Generator Modülü
Gün 6: Bitirme Projesi - Bölüm 1 | Rastgele ve yönlendirilmiş test üretici
Generator (üretici), testbench'in uyaranlarını (stimulus) üreten bileşendir. ALU mimarimizde zincirin başında durur: ALU_Transaction nesneleri oluşturur, kısıtlamalara göre rastgeleleştirir ve bir mailbox üzerinden driver'a iletir.
Generator ve Mimarideki Yeri
Generator, "DUT'a hangi senaryoları uygulayalım?" sorusunun cevabını üretir. Driver ile arasındaki iletişim, gen2drv adlı bir mailbox ile sağlanır:
- Generator transaction'ı
gen2drv.put(txn)ile kutuya bırakır. - Driver aynı kutudan
get()ile alır.
Bu yapı producer-consumer (üretici-tüketici) modelidir ve mailbox sayesinde iki bileşen birbirinden bağımsız hızlarda çalışabilir.
Kalıtım ile İki Üretim Stratejisi
Bu derste iki üretici sınıfı vardır:
ALU_Generator(taban sınıf): Belirtilen sayıda tamamen rastgele transaction üretir.run()metoduvirtualtanımlanmıştır; bu sayede türetilmiş sınıflar davranışı ezebilir (override).ALU_Corner_Generator(türetilmiş sınıf):extends ALU_Generatorile yönlendirilmiş (directed) test üretir. Önceden tanımlı köşe değer kümesini (corners) tüm işlemlerle çaprazlayarak sistematik biçimde tarar.
Her iki sınıf da işi bitince -> done ile bir event tetikler; böylece environment, üretimin tamamlandığını anlayabilir.
Kaynak Kod
// =============================================================================
// GUN 6 - Konu 3: Generator - Rastgele Uyarici Uretici
// =============================================================================
class ALU_Generator;
mailbox #(ALU_Transaction) gen2drv;
int num_transactions; // Hedeflenen adet (rastgele uretici icin)
int generated_count = 0; // Gercekten uretilen adet (rapor icin)
event done;
function new(mailbox #(ALU_Transaction) mbx, int num = 100);
this.gen2drv = mbx;
this.num_transactions = num;
endfunction
// run() virtual olmali: turetilmis uretici bu davranisi ezebilsin
virtual task run();
ALU_Transaction txn;
$display("[Generator] Rastgele testler basliyor (%0d adet)...", num_transactions);
for (int i = 0; i < num_transactions; i++) begin
txn = new();
assert(txn.randomize()) else $fatal(1, "Randomize hatasi!");
gen2drv.put(txn);
generated_count++;
end
-> done;
endtask
endclass
// Kose durumlari icin turetilmis uretici (yonlendirilmis test)
class ALU_Corner_Generator extends ALU_Generator;
function new(mailbox #(ALU_Transaction) mbx, int num = 100);
super.new(mbx, num);
endfunction
virtual task run();
ALU_Transaction txn;
logic [7:0] corners [] = '{0, 1, 8'h7F, 8'h80, 8'hFE, 8'hFF};
$display("[Corner_Generator] Kose durumlari basliyor...");
foreach (corners[i]) begin
foreach (corners[j]) begin
for (int op = 0; op < 8; op++) begin
// Kaydirma islemlerinde operand_b > 7 olan kombinasyonlari atla;
// aksi halde c_shift_range kisiti ile celisir ve randomize basarisiz olur
if ((op == ALU_Transaction::OP_SHL || op == ALU_Transaction::OP_SHR) && corners[j] > 7) begin
continue;
end
txn = new();
assert(txn.randomize() with {
operand_a == corners[i];
operand_b == corners[j];
opcode == ALU_Transaction::opcode_e'(op);
}) else $fatal(1, "Randomize hatasi!");
gen2drv.put(txn);
generated_count++;
end
end
end
-> done;
endtask
endclass
Kodun Açıklaması
mailbox #(ALU_Transaction) gen2drv: Üretilen transaction'ların driver'a aktarıldığı tipli kuyruk. Yapıcıda (new) dışarıdan alınır, böylece environment hangi mailbox'ın kullanılacağını kontrol eder.int num_transactions,int generated_countveevent done:num_transactionshedeflenen adettir ve rastgele üretici tarafından kullanılır;generated_countise gerçekten üretilen adedi sayar. Köşe üreticisi sabit bir kombinasyon tablosu taradığı içinnum_transactions'ı yok sayar; bu yüzden environment raporugenerated_count'a güvenir.doneevent'i, üretim tamamlandığında-> doneile tetiklenir.virtual task run()(taban): Bir döngüdenum_transactionskadartxn = new()yapar,assert(txn.randomize())ile kısıtlamalara uygun rastgele değerler üretir vegen2drv.put(txn)ile kuyruğa koyar.randomizebaşarısız olursa$fatalile durur.ALU_Corner_Generator: Taban sınıftan türetilir;super.new(mbx, num)ile mailbox'ı miras alır.- Köşe değer dizisi:
logic [7:0] corners [] = '{0, 1, 8'h7F, 8'h80, 8'hFE, 8'hFF}ile kritik operand değerleri tanımlanır. - İç içe
foreach+for: Tüm operand-a × operand-b × opcode kombinasyonları taranır. Kaydırma işlemlerindecorners[j] > 7olan durumlarcontinueile atlanır; bu,c_shift_rangekısıtlamasıyla çakışmayı önler. randomize() with {...}: Operand ve opcode değerleri satır içi kısıtlamayla sabitlenerek tam istenen kombinasyon üretilir.
Önemli Noktalar
run()metodununvirtualolması kalıtımın anahtarıdır; environment aynıgenreferansı üzerinden hem rastgele hem yönlendirilmiş üreticiyi çalıştırabilir (polymorphism).-> doneevent'i ile senkronizasyon: Environment, üretimin bittiğini bu event'i bekleyerek anlar; aksi halde simülasyon erken sonlanabilir veya gereksiz beklenir.- Yönlendirilmiş testte solver çakışmasından kaçınma: Geçersiz shift kombinasyonlarını
continueile atlamak,randomize() withçağrısının başarısız olmasını engeller. - Mailbox'ın dışarıdan enjekte edilmesi (dependency injection), generator'ı test ortamından bağımsız ve yeniden kullanılabilir kılar.
- Köşe değer taraması, saf rastgeleliğin kaçırabileceği sınır durumlarını garanti altına alır; rastgele ve yönlendirilmiş yaklaşımlar birbirini tamamlar.
- Köşe üreticisi kaç transaction üretir? 6 × 6 × 8 = 288 kombinasyondan, kaydırma işlemlerinde
operand_b ∈ {0x7F, 0x80, 0xFE, 0xFF}olan 6 × 4 × 2 = 48 tanesi atlanır → 240 transaction.generated_counttam bu sayıyı verir;num_transactions(varsayılan 100) ise yalnızca rastgele üreticide anlamlıdır. randomize() withile değer sabitlemek "rastgeleliği öldürmek" değildir: Operandlar ve opcode sabitlenir ama sınıf kısıtları yine denetlenir.c_shift_rangeile çelişen bir kombinasyon istenirse çözücü başarısız olur;continueile atlama bu yüzden gereklidir. Alternatif olarak sınıftasoftkısıt kullanılsaydıwithotomatik olarak kazanırdı.- Üretici DUT'u bilmez: Generator yalnızca mailbox'a yazar; pin, saat, interface görmez. Bu, aynı generator'ın farklı bir driver/DUT ile hiç değiştirilmeden kullanılabilmesi demektir ve katmanlı testbench mimarisinin özüdür.
Rastgele vs Yönlendirilmiş Üretim
ALU_Generator (rastgele) |
ALU_Corner_Generator (yönlendirilmiş) |
|
|---|---|---|
| Ne üretir? | Kısıtlara uyan rastgele kombinasyonlar | Önceden belirlenmiş köşe değer tablosu |
| Adet | num_transactions |
Tablo boyutuyla sabit (240) |
| Güçlü yanı | Aklınıza gelmeyen kombinasyonlar | Sınır değerlerin garanti denenmesi |
| Zayıf yanı | 0xFF gibi tek değerlere nadiren isabet |
Yalnızca sizin düşündüğünüz senaryolar |
| Kapsama etkisi | Geniş alanı hızla tarar | Boş kalan köşe bin'lerini doldurur |
| Gerçek projede | Regresyonun omurgası | "Kapsam deliklerini kapatma" testleri |
İkisi birlikte kullanılır: önce rastgele test geniş alanı tarar, kapsam raporunda boş kalan bin'ler için yönlendirilmiş testler yazılır.
Benzetme: Rastgele üretici, bir binanın dayanıklılığını ölçmek için rastgele yerlere vuran bir çekiçtir; yönlendirilmiş üretici ise mühendisin "şu kolon, şu kiriş" diyerek kritik noktalara bilinçli vurmasıdır. Sadece rastgele vurursanız bazı kolonlara hiç dokunmazsınız; sadece bilinçli vurursanız aklınıza gelmeyen zayıf noktayı kaçırırsınız.
Sık Yapılan Hatalar
run()'ıvirtualyapmamak:env.gen = corner_gen;ataması yapılsa bilegen.run()taban sınıfın rastgele sürümünü çalıştırır; köşe testi hiç koşmaz ve siz fark etmezsiniz.- Aynı
txnnesnesini yeniden kullanmak: Döngü dışında teknew()yapılırsa mailbox'taki tüm transaction'lar aynı nesneyi gösterir; driver hep son değeri sürer. -> done'ı unutmak: Environmentwait(gen.done.triggered)ile beklediği için simülasyon hiç bitmez.- Köşe üreticide kısıtla çelişen kombinasyon:
continuekaldırılırsaOP_SHL×0xFFiçinrandomize()başarısız olur ve$fataltetiklenir. generated_count'u artırmayı unutmak: Alt sınıfrun()'ı override ettiği için taban sınıftaki artış çalışmaz; her override kendi sayacını güncellemelidir (ya daputişlemini ortak birsend(txn)metoduna taşıyın — ek görev).
Kendinizi Deneyin
ALU_Generator'avirtual task send(ALU_Transaction txn);metodu ekleyin:put+generated_count++burada olsun; her ikirun()bunu çağırsın. Tekrar eden kodu böylece kaldırın.ALU_Corner_Generator'dakicornersdizisine8'h01yerine8'h08koyun; kaydırma işlemlerinde kaç kombinasyon atlanır,generated_countkaç olur?- Yeni bir
ALU_Shift_Generator extends ALU_Generatoryazın: yalnızcaOP_SHL/OP_SHRüretsin veoperand_b0–7 arasındaki her değerirandcile tarasın. - Rastgele üreticinin
run()'ındatxn.randomize() with { opcode != ALU_Transaction::OP_NOT; }kullanın. Bu değişikliğincp_opcodekapsamına etkisini Gün 7'de gözlemleyin.
Hızlı Kontrol: ALU_Corner_Generator kaç transaction üretir ve bu sayı neden num_transactions'tan bağımsızdır?
240. 6 operand × 6 operand × 8 işlem = 288 kombinasyondan, kaydırmalarda operand_b > 7 olan 48 tanesi atlanır. Köşe üretici sabit bir tabloyu taradığı için num_transactions değerini hiç okumaz; gerçek adedi generated_count taşır.