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ı
Transactionsınıfındaaddress,data,sizeveread_writerastgele üyeleri tanımlanır.addr_rangeadlı constraint,addressdeğeriniinside {[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 veif (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ılarakaddress0x0100–0x01FF aralığına,data[31:16]sıfıra veread_write1'e (WRITE) sabitlenir. Bu kısıtlamalaraddr_rangeile ç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'h0ABCile adres elle ayarlanır, ardındantxn.randomize(data, size, read_write)çağrılır. Argüman listesindeaddressolmadığı için adres0x0ABColarak 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ğundanrandomize()başarısız olur (0dö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ındakiassert(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()0dö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.
withiç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ızcarand/randcalanları değiştirir;randolmayan alanlar durum değişkeni (state variable) olarak kabul edilir ve kısıtlarda sabit gibi kullanılır. Örneğinrandolmayan birmodealanınız varsaconstraint 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şkenlerinrandolması gerekmez; normal bir alanı bile geçici olarak rastgeleleştirebilirsiniz. Tersine,randolup 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 (0dö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. withiç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çinconstraint_mode(0)gerekir.withiçinde yerel değişkenle sınıf alanının aynı adı taşıması:with { address == address; }gibi ifadelerde her ikiaddressde sınıf alanını gösterir. Dış değişkene erişmek içinlocal::öneki kullanın:with { address == local::address; }.assertileif'i aynı sanmak:assert(obj.randomize())immediate assertion'dır; başarısızlıkta simülatör bir hata mesajı basar veelsedalı çalışır.ifise hiçbir şey basmaz. Bu yüzdenassert ... else $fatalendüstride tercih edilir.- Seçici randomize'ı kısıt kontrolü sanmak:
randomize(data)çağrısı yalnızcadata'yı değiştirir; ama tüm aktif kısıtlar yine geçerlidir.addresssabitken kısıtla çelişiyorsa çağrı başarısız olur.
Kendinizi Deneyin
txn.address = 16'h1234; if (txn.randomize(null)) ... else ...yazın. Hangi dal çalıştı?0x1234nedenaddr_rangekısıtını ihlal ediyor?txn.address = 16'h0ABC;sonrasıtxn.randomize(data)ile yalnızca veriyi değiştirin; ardındantxn.randomize(address)ile yalnızca adresi değiştiripdata'nın sabit kaldığını gösterin.- Modülde
int addr_hi = 16'h0200;tanımlayıpwith { address < local::addr_hi; }kullanın.local::'ü kaldırırsanız derleyici ne der? assert(txn.randomize() with { address == 16'hFFFF; }) else $display("yakalandi");yazın veassert'in ürettiği ek mesajıifsü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.