Gün 6 Ara Testi: Generator + Driver
Gün 6: Bitirme Projesi - Bölüm 1 | Monitor ve scoreboard olmadan altyapının çalıştığını doğrulayan ara testbench
Bu ders, Gün 6'nın sonunda elinizdeki parçaları (interface, DUT, transaction, generator, driver) ilk kez bir arada çalıştırır. Henüz monitor, scoreboard ve coverage yoktur; amaç "doğru mu çalışıyor?" sorusunu değil, "çalışıyor mu?" sorusunu yanıtlamaktır. Gerçek projelerde de testbench böyle kurulur: önce stimulus DUT'a ulaşsın, dalga formunda görünsün; kontrol mekanizmaları sonra eklenir.
Neden Bir Ara Test?
Tüm bileşenleri yazıp en sonda tek seferde çalıştırmak, hatayı bulmayı zorlaştırır: "Scoreboard uyuşmazlık veriyor" dediğinizde sorun generator'da mı, driver'da mı, interface bağlantısında mı, DUT'ta mı bilemezsiniz. Ara test bu belirsizliği parçalar:
- Bağlantı doğrulaması: Interface, DUT portları ve clocking block gerçekten birbirine bağlı mı?
$dumpvarsile dalga formundain_valid,operand_adeğişiyor mu? - Mailbox zinciri: Generator'ın ürettiği her transaction driver'a ulaşıyor mu?
Uretilen == Suruleneşitliği bunu gösterir. - Zamanlama: Driver'ın
output #1skew ile sürdüğü değerleri DUT doğru kenarda örnekliyor mu?out_valid'inin_valid'den bir çevrim sonra yükseldiğini dalga formunda görürsünüz. - Reset dizisi:
rst_ndüşerken çıkışlar sıfırlanıyor mu?
Bu adımda drv2scb_mbx yaratılır ama hiç okunmaz; driver kopyaları içine koyar, mailbox sınırsız olduğu için put bloklanmaz. Gün 7'de scoreboard bu mailbox'ın diğer ucuna bağlanacak.
Kaynak Kod
// =============================================================================
// GUN 6 - Konu 5: Ara Testbench (Generator + Driver, henuz kontrol yok)
// =============================================================================
// Bu dosya Gun 6'nin sonunda elde edilen mimaridir.
// Gun 7'de Monitor, Scoreboard, Coverage ve Environment eklenecektir.
// =============================================================================
`include "alu_if.sv" // Interface (Ders 36)
`include "alu.sv" // DUT (Ders 36-1)
`include "6_2_transaction.sv" // ALU_Transaction (Ders 37)
`include "6_3_generator.sv" // ALU_Generator (Ders 38)
`include "6_4_driver.sv" // ALU_Driver (Ders 39)
module tb_top_day6;
// Saat uretimi: 10 ns periyot (100 MHz)
logic clk = 0;
always #5 clk = ~clk;
// Interface
alu_if aif(clk);
// DUT baglantisi: pinler interface sinyallerine baglanir
alu dut (
.clk (aif.clk),
.rst_n (aif.rst_n),
.in_valid (aif.in_valid),
.operand_a (aif.operand_a),
.operand_b (aif.operand_b),
.opcode (aif.opcode),
.out_valid (aif.out_valid),
.result (aif.result),
.flags (aif.flags)
);
// Mailbox'lar
mailbox #(ALU_Transaction) gen2drv_mbx;
mailbox #(ALU_Transaction) drv2scb_mbx; // Gun 7'de scoreboard okuyacak
// Bilesenler
ALU_Generator gen;
ALU_Driver drv;
initial begin
$display("============================================================");
$display(" GUN 6: ALU Testbench - Altyapi Testi");
$display("============================================================\n");
// Mailbox olustur
gen2drv_mbx = new();
drv2scb_mbx = new();
// Bilesenleri olustur ve bagla (aif -> virtual alu_if.driver donusumu otomatik)
gen = new(gen2drv_mbx, 20);
drv = new(aif, gen2drv_mbx, drv2scb_mbx);
// Reset
drv.reset();
// Calistir: generator biter, driver sonsuz donguden hic cikmaz
fork
gen.run();
drv.run();
join_any
// Generator bitene ve son transaction surulene kadar bekle
wait(gen.done.triggered);
wait(drv.driven_count == gen.generated_count);
repeat (5) @(aif.drv_cb); // Son out_valid'in dalga formunda gorunmesi icin
$display("\n --- Gun 6 Istatistikler ---");
$display(" Uretilen : %0d", gen.generated_count);
$display(" Surulen : %0d", drv.driven_count);
$display(" Bekleyen : %0d (drv2scb icinde, Gun 7'de okunacak)", drv2scb_mbx.num());
$display("\n============================================================");
$display(" GUN 6 TAMAMLANDI");
$display(" (Gun 7'de Monitor, Scoreboard ve Coverage eklenecek)");
$display("============================================================");
$finish;
end
// Dalga formu kaydi
initial begin
$dumpfile("alu_tb.vcd");
$dumpvars(0, tb_top_day6);
end
endmodule
Kodun Açıklaması
- **
`includesırası**: Önce interface ve DUT (statik tasarım öğeleri), sonra sınıflar. Sınıflarvirtual alu_if.drivertipine atıfta bulunduğu için interface'in önce derlenmesi en güvenli sıradır. Generator,ALU_Transaction'ı kullandığı için transaction dosyası ondan önce gelir. - Saat ve interface:
always #5 clk = ~clkile 10 ns periyot üretilir;alu_if aif(clk)interface'i bu saati alır ve clocking block'lar bu saatle tetiklenir. - DUT bağlantısı: Her port
aif.<sinyal>ile eşlenir. İsimlerin aynı olması bağlamayı mekanik ve hatasız kılar. - Mailbox'lar ve bileşenler:
gen2drv_mbxgenerator→driver kanalıdır.drv2scb_mbxşimdilik yalnızca doldurulur.gen = new(gen2drv_mbx, 20)20 transaction üretecek şekilde,drv = new(aif, ...)ise interface'indrivermodport görünümüyle kurulur. - Reset ve çalışma:
drv.reset()5 çevrimrst_n'i düşük tutar.fork ... join_anyile generator ve driver paralel başlar; driverforeverdöngüsünde olduğu içinjoin_anygenerator bitince ana süreci serbest bırakır. - Bitiş senkronizasyonu:
wait(gen.done.triggered)üretimin bittiğini,wait(drv.driven_count == gen.generated_count)ise son transaction'ın da sürüldüğünü garanti eder. Sabit bir#200yerine sayaç eşitliği beklemek, transaction sayısı değişse bile kodun doğru kalmasını sağlar.repeat (5) @(aif.drv_cb)yalnızca dalga formunda sonout_validdarbesinin görünmesi içindir. - İstatistikler: Üretilen ve sürülen adetlerin eşit olması altyapının çalıştığının kanıtıdır.
drv2scb_mbx.num()ise driver'ın scoreboard için biriktirdiği kopyaları sayar; Gün 7'de bu sayı sıfıra inecek (scoreboard tüketecek).
Çıktıyı Okuma Rehberi
| Gözlem | Beklenen | Değilse ilk bakılacak yer |
|---|---|---|
[Driver] Reset tamamlandi |
Simülasyonun ~60 ns'sinde | drv.reset() içindeki repeat(5) ve saat üretimi |
Uretilen : 20, Surulen : 20 |
Eşit | Mailbox handle'ları aynı mı? drv.run() fork'ta mı? |
Bekleyen : 20 |
Sürülen kadar | drv2scb.put(txn.copy()) çağrısı var mı? |
Dalga formunda out_valid |
Her in_valid darbesinden bir çevrim sonra 1 çevrimlik darbe |
DUT always_ff, clocking skew |
Dalga formunda result |
out_valid=1 iken değişir, sonra sabit kalır |
if (in_valid) koşulu |
Önemli Noktalar
- Küçük adımlarla kurun, her adımda çalıştırın: Gerçek projelerde bir testbench'in ilk sürümü yalnızca "reset at, bir transaction sür, dalga formuna bak" yapar. Bu ders o alışkanlığı modeller.
- Kontrol olmayan test "geçer" ama bir şey kanıtlamaz: Bu testbench DUT yanlış sonuç üretse de "GUN 6 TAMAMLANDI" basar. Doğruluk kararı Gün 7'deki scoreboard'un işidir; burada yalnızca veri akışı doğrulanır.
- Tüketilmeyen mailbox şişer:
drv2scb_mbxsınırsız olduğu için 20 transaction sorun değildir; ama milyonlarca transaction'lı bir regresyonda okunmayan bir mailbox bellek sızıntısı gibi davranır. Herput'un birget'i olmalıdır. - Sayaç tabanlı bitiş, zaman tabanlı bitişten iyidir:
wait(drv.driven_count == gen.generated_count)ifadesi, Lab 4'teki#100sihirli sayısının olgunlaşmış hâlidir. - Dalga formu bu aşamada en iyi dostunuzdur: Scoreboard yokken doğruluğu gözle kontrol edersiniz.
$dumpvars(0, tb_top_day6)tüm hiyerarşiyi kaydeder; EDA Playground'da "Open EPWave after run" seçeneğini açın.
Sık Yapılan Hatalar
alu_if aif();(saatsiz): Clocking block hiç tetiklenmez;drv.reset()içindeki ilk@(vif.drv_cb)sonsuza kadar bekler ve hiçbir mesaj basılmaz.joinkullanmak: Driver asla bitmediği için simülasyon sonsuza kadar sürer.join_anyşarttır.- Interface'i sınıflardan sonra
includeetmek: Çoğu araç tolere eder, ama bazılarıvirtual alu_if.driveriçin "bilinmeyen tip" hatası verir. Statik öğeleri önce derleyin. gen.generated_countyerinegen.num_transactionsbeklemek: Bu dosyada ikisi eşittir (20); ancak Gün 7'de köşe üreticiye geçildiğindenum_transactionsanlamsızlaşır. Baştan doğru sayacı kullanın.$finisholmadan bırakmak:always #5 clkbloğu simülasyonu sonsuza kadar sürdürür.
Kendinizi Deneyin
gen = new(gen2drv_mbx, 20)değerini 200 yapın. Sabit bir#200beklemesi olsaydı istatistikler ne gösterirdi? Sayaç tabanlı bekleme ile hâlâ200/200çıktığını doğrulayın.- Dalga formunu açın ve tek bir transaction için
in_valid,operand_a,out_valid,resultsinyallerini aynı pencerede inceleyin.in_validileout_validarasındaki gecikmeyi veoutput #1skew'ini ölçün. - Driver'ın
drive_transactionçağrısından sonra$displayileaif.resultdeğerini basın. Hangi transaction'ın sonucunu görüyorsunuz: şu anki mi, bir önceki mi? Neden? - DUT'ta bilinçli hata yapın (
OP_ADD→operand_a - operand_b). Bu testbench hatayı fark etti mi? Bu gözlemi Gün 7'nin motivasyonu olarak not edin.
Hızlı Kontrol: Bu testbench DUT'un doğru çalıştığını kanıtlar mı?
Hayır. Yalnızca stimulus'un üretilip DUT'a kadar ulaştığını (veri akışını) kanıtlar. DUT yanlış sonuç üretse bile "Uretilen == Surulen" eşitliği bozulmaz. Doğruluk kararı için monitor + referans model + scoreboard gerekir; bunlar Gün 7'nin konusudur.