EDA Playground'da Dene

randomize() Metodu

Gün 3: Rastgele Üretim ve Kısıtlamalar | Rastgele üretim, inline constraint, hata kontrolü

Bu derste her rastgele sınıfın kalbi olan randomize() metodunu derinlemesine inceleyeceğiz: temel kullanımını, satır içi (inline) kısıtlama ekleyen with sözdizimini, yalnızca seçili değişkenleri rastgeleleştirmeyi ve dönüş değeri ile hata kontrolünü öğreneceğiz.

randomize() Metodu

randomize(), rastgele üyeleri olan her sınıfın otomatik olarak sahip olduğu, yerleşik (built-in) bir sınıf metodudur. Çağrıldığında sınıftaki tüm rand/randc değişkenlerine, tanımlı constraint blokları ve verilen kısıtlamaları sağlayacak şekilde rastgele değerler atar.

Metot bir bit döndürür:

  • 1 → çözüm bulundu, değişkenler güncellendi.
  • 0 → kısıtlamalar çakıştı (çözüm yok), değişkenler değişmeden kalır.

Bu yüzden dönüş değeri asla göz ardı edilmemelidir.

Satır İçi Kısıtlama: with { ... }

randomize() çağrısına with { ... } ekleyerek, sınıfta tanımlı kalıcı kısıtlamalara ek olarak yalnızca o çağrıya özel geçici kısıtlamalar verebilirsiniz. Bu kısıtlamalar sınıf içindeki blocklarla AND ilişkisiyle birleşir.

Seçici Randomize

randomize() metoduna argüman olarak değişken adları verilirse (randomize(data, size) gibi), yalnızca o değişkenler rastgeleleştirilir; geri kalanı o anki değerlerinde sabit tutulur. Bu, belirli bir alanı sabit tutup diğerlerini değiştirmek istediğinizde çok kullanışlıdır.

Hata Kontrolü ve assert

Endüstride yaygın kalıp, randomize çağrısını bir assert(...) else $fatal(...) içine almaktır. Böylece çözülemeyen bir kısıtlama anında ve net bir mesajla yakalanır.

Kaynak Kod

// =============================================================================
// GUN 3 - Konu 2: randomize() Metodu ve Hata Kontrolu
// =============================================================================

class Transaction;
  rand logic [15:0] address;
  rand logic [31:0] data;
  rand bit [2:0]    size;
  rand bit          read_write;  // 0=read, 1=write

  constraint addr_range {
    address inside {[16'h0000 : 16'h0FFF]};
  }

  function void display(string prefix = "");
    $display("  %sAddr=0x%04h, Data=0x%08h, Size=%0d, R/W=%s",
             prefix, address, data, size,
             read_write ? "WRITE" : "READ");
  endfunction
endclass

module randomize_metodu;
  initial begin
    Transaction txn;
    int success;

    $display("=== randomize() Metodu ===\n");
    txn = new();

    // --- Temel randomize ---
    $display("--- Temel randomize() ---");
    repeat (5) begin
      success = txn.randomize();
      if (success)
        txn.display();
      else
        $display("  HATA: Randomize basarisiz!");
    end

    // --- randomize() with (inline constraint) ---
    $display("\n--- randomize() with (Satir ici kisitlama) ---");
    repeat (5) begin
      success = txn.randomize() with {
        address >= 16'h0100 && address <= 16'h01FF;
        data[31:16] == 16'h0000;
        read_write == 1;
      };
      if (success)
        txn.display("inline: ");
      else
        $display("  HATA: Inline randomize basarisiz!");
    end

    // --- Belirli degiskenleri sabit tutma ---
    $display("\n--- Secici Randomize ---");
    txn.address = 16'h0ABC;
    repeat (3) begin
      // address sabit, sadece digerleri rastgele
      success = txn.randomize(data, size, read_write);
      txn.display("addr sabit: ");
    end

    // --- randomize() donus degeri kontrolu ---
    $display("\n--- Hata Kontrolu ---");
    success = txn.randomize() with { address == 16'hFFFF; }; // Constraint ile cakisabilir
    if (!success)
      $display("  Constraint cakismasi: 0xFFFF aralik disi (0x0000-0x0FFF)");

    // Dogru kullanim: assert ile
    $display("\n--- assert ile Kontrol ---");
    // assert(txn.randomize()) else $fatal(1, "Randomize HATA!");
    if (txn.randomize())
      txn.display("assert OK: ");
    else
      $fatal(1, "Randomize basarisiz!");

    $display("\n=== randomize() Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • Transaction sınıfında address, data, size ve read_write rastgele üyeleri tanımlanır. addr_range adlı constraint, address değerini inside {[16'h0000 : 16'h0FFF]} aralığına sınırlar — yani adres her zaman 0x0000–0x0FFF arasında kalır.
  • Temel randomize bölümünde success = txn.randomize() çağrısının dönüş değeri bir değişkene atanır ve if (success) ile kontrol edilir; başarısızsa hata basılır.
  • Satır içi kısıtlama bölümünde randomize() with { ... } kullanılarak address 0x0100–0x01FF aralığına, data[31:16] sıfıra ve read_write 1'e (WRITE) sabitlenir. Bu kısıtlamalar addr_range ile çakışmaz çünkü 0x0100–0x01FF, izin verilen 0x0000–0x0FFF aralığının içindedir.
  • Seçici randomize bölümünde önce txn.address = 16'h0ABC ile adres elle ayarlanır, ardından txn.randomize(data, size, read_write) çağrılır. Argüman listesinde address olmadığı için adres 0x0ABC olarak sabit kalır, yalnızca diğer üç alan değişir.
  • Hata kontrolü bölümünde bilinçli olarak çakışan bir kısıtlama (address == 16'hFFFF) verilir. 0xFFFF, addr_range'in izin verdiği aralığın dışında olduğundan randomize() başarısız olur (0 döner) ve değişkenler değişmez.
  • Son bölümde doğru kalıp gösterilir: if (txn.randomize()) ... else $fatal(...). Yorum satırındaki assert(txn.randomize()) else $fatal(...) ise endüstride en yaygın yazım biçimidir.

Önemli Noktalar

  • Çakışan kısıtlamalarda eski değerler kalır. randomize() 0 döndüğünde değişkenler güncellenmez; bu yüzden başarısız bir çağrının ardından "eski" değerlerle çalışma riski vardır.
  • Satır içi kısıtlamalar sınıf kısıtlamalarını ezmez, onlarla birleşir. with içindeki ifadeler mevcut constraint blocklarıyla AND'lenir; bu nedenle sınıf kısıtlamasıyla çelişen bir inline kısıtlama çözümsüzlüğe yol açar.
  • Seçici randomize ile sabit tutulan alanların değerini önceden ayarlayın. randomize(...) argüman listesi dışında kalan rastgele alanlar, mevcut değerlerinde dondurulur.
  • Üretimde assert(...) else $fatal(...) kalıbını tercih edin. Bu, çözülemeyen kısıtlamaları sessizce geçiştirmek yerine hemen, açık bir mesajla raporlar.
  • Hangi değerlerin köşe durum oluşturduğunu test ederken (örneğin aralık sınırları) kasıtlı çakışmalarla constraint mantığınızı doğrulamak iyi bir pratiktir.
  • randomize() çağrısı yalnızca rand/randc alanları değiştirir; rand olmayan alanlar durum değişkeni (state variable) olarak kabul edilir ve kısıtlarda sabit gibi kullanılır. Örneğin rand olmayan bir mode alanınız varsa constraint c { mode == FAST -> size < 4; } yazabilirsiniz; çözücü mode'un o anki değerini okur.
  • Seçici randomize'da (randomize(data, size)) argüman olarak verilen değişkenlerin rand olması gerekmez; normal bir alanı bile geçici olarak rastgeleleştirebilirsiniz. Tersine, rand olup listede olmayanlar o çağrıda durum değişkeni gibi davranır.

randomize() Ailesi

Çağrı Ne yapar?
obj.randomize() Tüm rand/randc alanları, tüm aktif kısıtlarla çözer
obj.randomize() with { ... } Sınıf kısıtlarına ek satır içi kısıt (AND)
obj.randomize(a, b) Yalnızca a ve b rastgele, kalanlar sabit
obj.randomize(null) Hiçbir şeyi değiştirmez; yalnızca mevcut değerlerin kısıtları sağladığını kontrol eder (1/0). Elle doldurulan bir paketi doğrulamak için idealdir
obj.alan.rand_mode(0) O alanı rastgelelikten geçici çıkarır (Gün 3'ün sonunda constraint_mode ile birlikte)
std::randomize(x) with {...} Sınıf dışı değişken için

Benzetme: randomize() bir bulmaca çözücüdür: sınıf kısıtları bulmacanın basılı kuralları, with { } ise o tur için elle eklediğiniz ek notlardır. Çözücü tüm kurallara aynı anda uyan bir cevap bulamazsa kâğıdı boş bırakır (0 döner) ve önceki cevap kâğıtta kalır.

Sık Yapılan Hatalar

  • Başarısız randomize()'ı fark etmemek: En pahalı hata. Testiniz "geçti" der ama aslında hep aynı eski değerleri sürmüştür. Dönüşü her zaman kontrol edin.
  • with içinde sınıf kısıtını "ezmek" istemek: with { address == 16'hFFFF; } sınıf kısıtını iptal etmez, onunla çelişir ve çözümsüz kalır. Kısıtı gerçekten kaldırmak için constraint_mode(0) gerekir.
  • with içinde yerel değişkenle sınıf alanının aynı adı taşıması: with { address == address; } gibi ifadelerde her iki address de sınıf alanını gösterir. Dış değişkene erişmek için local:: öneki kullanın: with { address == local::address; }.
  • assert ile if'i aynı sanmak: assert(obj.randomize()) immediate assertion'dır; başarısızlıkta simülatör bir hata mesajı basar ve else dalı çalışır. if ise hiçbir şey basmaz. Bu yüzden assert ... else $fatal endüstride tercih edilir.
  • Seçici randomize'ı kısıt kontrolü sanmak: randomize(data) çağrısı yalnızca data'yı değiştirir; ama tüm aktif kısıtlar yine geçerlidir. address sabitken kısıtla çelişiyorsa çağrı başarısız olur.

Kendinizi Deneyin

  1. txn.address = 16'h1234; if (txn.randomize(null)) ... else ... yazın. Hangi dal çalıştı? 0x1234 neden addr_range kısıtını ihlal ediyor?
  2. txn.address = 16'h0ABC; sonrası txn.randomize(data) ile yalnızca veriyi değiştirin; ardından txn.randomize(address) ile yalnızca adresi değiştirip data'nın sabit kaldığını gösterin.
  3. Modülde int addr_hi = 16'h0200; tanımlayıp with { address < local::addr_hi; } kullanın. local::'ü kaldırırsanız derleyici ne der?
  4. assert(txn.randomize() with { address == 16'hFFFF; }) else $display("yakalandi"); yazın ve assert'in ürettiği ek mesajı if sürümüyle karşılaştırın.
Hızlı Kontrol: randomize() 0 döndürdüğünde rand alanların değeri ne olur?

Değişmez. Çözücü çözüm bulamazsa hiçbir alanı güncellemez; nesne çağrı öncesindeki değerleriyle kalır. Bu yüzden başarısızlığı yakalamadan devam eden bir test, eski verileri yeni sanarak sürer.