Environment Sınıfı
Gün 7: Bitirme Projesi - Bölüm 2 | Tüm bileşenleri kapsayan ortam sınıfı
Environment (ortam), tüm doğrulama bileşenlerini tek bir çatı altında toplayan, onları oluşturan ve birbirine bağlayan üst düzey kapsayıcıdır. ALU mimarimizde generator, driver, monitor, scoreboard ve coverage'ı barındırır; mailbox'ları üretip aralarındaki tüm bağlantıyı kurar.
Environment ve Mimarideki Yeri
Environment, testbench'in "montaj fabrikası"dır. Tek tek bileşenleri tanımak yerine, test sınıfı yalnızca environment ile konuşur. Sorumlulukları:
- Mailbox'ları oluşturmak:
gen2drv_mbx,drv2scb_mbx,mon2scb_mbxkanallarını üretir. - Bileşenleri bağlamak: Her bileşene doğru mailbox ve virtual interface referanslarını verir.
- Yaşam döngüsünü yönetmek:
build→reset→run→reportaşamalarını sıralı yürütür.
Mailbox Bağlantı Topolojisi
Bileşenler arası veri akışı şu mailbox'larla kurulur:
gen2drv_mbx: Generator → Driverdrv2scb_mbx: Driver → Scoreboard (beklenen)mon2scb_mbx: Monitor → Scoreboard (gerçek)
Bu topoloji sayesinde bileşenler birbirine doğrudan değil, yalnızca mailbox'lar üzerinden bağlıdır (gevşek bağlılık / loose coupling).
Paralel Çalışma ve Senkronizasyon
run() task'ı dört bileşeni (gen, drv, mon, scb) fork...join_any ile paralel başlatır. Generator işini bitirip done event'ini tetikleyince, pipeline'ın her katmanı kendi sayacıyla boşalmayı onaylar: driver tüm işlemleri sürdü mü, scoreboard hepsini karşılaştırdı mı? Bu "boşaltma" (drain) kontrolü sabit bir bekleme süresine değil, sayaç eşitliklerine dayanır.
Tek Coverage, İki Kullanıcı
Coverage nesnesi environment'ta bir kez yaratılır ve scoreboard'a yapıcı argümanıyla verilir. Scoreboard onu sample() ile besler, environment report() ile okur. İki ayrı nesne olsaydı environment'ın raporu hep %0 gösterirdi; bu, handle paylaşımının (Gün 2) doğrulama ortamındaki en somut uygulamasıdır.
Kaynak Kod
// =============================================================================
// GUN 7 - Konu 3: Environment - Tum Bilesenleri Kapsayan Sinif
// =============================================================================
class ALU_Environment;
// Bilesenler
ALU_Generator gen;
ALU_Driver drv;
ALU_Monitor mon;
ALU_Scoreboard scb;
ALU_Coverage cov; // 7_5_coverage.sv'de tanimli
// Mailbox'lar
mailbox #(ALU_Transaction) gen2drv_mbx;
mailbox #(ALU_Transaction) drv2scb_mbx;
mailbox #(ALU_Transaction) mon2scb_mbx;
// Virtual interface
virtual alu_if.driver drv_vif;
virtual alu_if.monitor mon_vif;
// Konfigurasyon
int num_transactions;
function new(virtual alu_if.driver drv_vif, virtual alu_if.monitor mon_vif, int num_txn = 100);
this.drv_vif = drv_vif;
this.mon_vif = mon_vif;
this.num_transactions = num_txn;
endfunction
// Tum bilesenleri olustur ve bagla
function void build();
$display("[Environment] Build asamasi");
// Mailbox'lari olustur
gen2drv_mbx = new();
drv2scb_mbx = new();
mon2scb_mbx = new();
// Bilesenleri olustur
// Coverage once yaratilir ve scoreboard'a verilir: tek ornek, iki kullanici
cov = new();
gen = new(gen2drv_mbx, num_transactions);
drv = new(drv_vif, gen2drv_mbx, drv2scb_mbx);
mon = new(mon_vif, mon2scb_mbx);
scb = new(drv2scb_mbx, mon2scb_mbx, cov);
$display("[Environment] Tum bilesenler olusturuldu");
endfunction
// Reset
task reset();
$display("[Environment] Reset");
drv.reset();
endtask
// Tum bilesenleri calistir
task run();
$display("[Environment] Calistiriliyor...\n");
fork
gen.run();
drv.run();
mon.run();
scb.run();
join_any
// 1) Generator bitene kadar bekle
wait(gen.done.triggered);
// 2) Driver tum transaction'lari surmus olsun
wait(drv.driven_count == gen.generated_count);
// 3) Scoreboard hepsini karsilastirmis olsun (pipeline bosaldi)
wait(scb.total_count == gen.generated_count);
// 4) Dalga formunda son islemin gorunmesi icin birkac cevrim
repeat (5) @(drv_vif.drv_cb);
endtask
// Rapor
function void report();
$display("\n[Environment] Final Rapor");
$display(" Generator : %0d uretildi", gen.generated_count);
$display(" Driver : %0d suruldu", drv.driven_count);
$display(" Monitor : %0d izlendi", mon.monitored_count);
if (drv.driven_count != mon.monitored_count)
$display(" [UYARI] Surulen ve izlenen sayilari farkli! Monitor/driver hizalamasini kontrol edin.");
scb.report();
cov.report();
endfunction
endclass
Kodun Açıklaması
- Bileşen üyeleri:
gen,drv,mon,scb,covreferansları tüm doğrulama bileşenlerini tutar. - Mailbox üyeleri:
gen2drv_mbx,drv2scb_mbx,mon2scb_mbxbileşenler arası veri kanallarıdır. - Virtual interface'ler:
drv_vifvemon_vif, driver ve monitor'ün DUT'a bağlanmasını sağlar; yapıcıda dışarıdan alınır. build(): Önce üç mailboxnew()ile oluşturulur. Ardındancovyaratılır ve bileşenler doğru bağlantılarla kurulur:drvhemgen2drv_mbx'ten okur hemdrv2scb_mbx'e yazar,monmon2scb_mbx'e yazar,scbher iki giriş mailbox'ını vecovhandle'ını alır.reset(): Driver'ınreset()task'ını çağırarak DUT'u başlangıç durumuna getirir.run():fork ... join_anyile dört bileşeni paralel başlatır. Sonra üç aşamalı boşaltma yapar:wait(gen.done.triggered)üretim bitti,wait(drv.driven_count == gen.generated_count)hepsi sürüldü,wait(scb.total_count == gen.generated_count)hepsi karşılaştırıldı. Sonrepeat (5) @(drv_vif.drv_cb)yalnızca dalga formu içindir.report(): Generator/driver/monitor sayaçlarını basar; sürülen ile izlenen sayıları farklıysa bir uyarı ekler, ardındanscb.report()vecov.report()ile detaylı raporları tetikler.gen.generated_countkullanılması, köşe üreticide (num_transactions'ı yok sayar) doğru sayının görünmesini sağlar.
Önemli Noktalar
- Bağlantı (connection) tek noktadan yapılır: Mailbox eşleştirmelerinin ve handle enjeksiyonlarının tamamı
build()içindedir; bu, hata ayıklamayı ve bakımı kolaylaştırır. join_anykullanımı bilinçlidir: Bileşenlerin çoğu sonsuz döngüdür; tümünün bitmesini beklemek (join) yerine, generator bitince kontrol akışı devam eder.- Boşaltma sayaçla yapılır, zamanla değil:
#100gibi sabit bir bekleme, transaction sayısı ya da driver hızı değişince ya yetersiz kalır ya gereksiz uzar. Her katmanın sayacını beklemek bu kırılganlığı ortadan kaldırır. Bu dersin önceki sürümüwait(gen2drv_mbx.num() == 0); #100;kullanıyordu; mailbox'ın boşalması yalnızca driver'ın almaya başladığını gösterir, sürmeyi bitirdiğini değil. - Tek
covnesnesi: Environment yaratır, scoreboard besler, environment raporlar. Dersin önceki sürümünde scoreboard kendicov'unu yaratıyordu ve environment'ın raporu hiç örneklenmemiş ikinci bir nesneyi okuyordu (her zaman %0). Handle paylaşımı bu hatayı yapısal olarak imkânsız kılar. - Virtual interface'lerin dışarıdan enjekte edilmesi, aynı environment'ın farklı top/test yapılarında yeniden kullanılabilmesini sağlar.
drv_vif.drv_cbenvironment'tan erişilebilir: Environment driver modport'una sahip olduğu için clocking olayını bekleyebilir; ama sinyal sürmemelidir — sürüş yalnızca driver'ın işidir.
Bu Environment'ın UVM Karşılığı
| Bu ders | UVM |
|---|---|
ALU_Environment sınıfı |
uvm_env |
build() fonksiyonu |
build_phase() (top-down, factory ile create) |
| Mailbox eşleştirmeleri | connect_phase() (TLM port/export connect) |
reset() + run() task'ları |
run_phase() ve reset_phase/main_phase alt fazları |
wait(...) boşaltma zinciri |
phase.raise_objection / drop_objection |
report() |
report_phase() |
Yapıcıya verilen vif |
uvm_config_db#(virtual alu_if)::get |
Yapıcıya verilen cov |
Analysis port'tan uvm_subscriber ya da handle enjeksiyonu (UVM dersi 3) |
UVM, bu derste elle yaptığınız her adımı bir faz olarak standartlaştırır. "Hangi sırayla kur, bağla, çalıştır, raporla?" sorusunun cevabı artık metodolojinin kendisindedir.
Benzetme: Environment bir film setinin yapım amiridir: oyuncuları (bileşenleri) işe alır (
build), aralarındaki iletişimi kurar (mailbox'lar), "motor" der (run), çekim bitince herkesin işini bitirmesini bekler (boşaltma) ve günün raporunu yazar (report). Yönetmen (test) yalnızca "bugün hangi sahne?" kararını verir.
Sık Yapılan Hatalar
- Scoreboard'da ikinci bir
covyaratmak: Rapor %0 gösterir; kapsam toplanıyor sanırsınız ama okuduğunuz nesne boştur. - Boşaltmayı mailbox'ın boşalmasıyla ölçmek:
gen2drv_mbx.num() == 0yalnızca driver'ın son transaction'ı aldığını söyler; sürme, gözlem ve karşılaştırma hâlâ devam ediyor olabilir. joinkullanmak:drv,mon,scbsonsuz döngülüdür;joinasla dönmez.build()'itask,run()'ıfunctionyapmak:build()zaman tüketmez (functiondoğru);run()zaman tüketir (taskzorunlu). UVM'de debuild_phasefunction,run_phasetask'tır.- Bileşenleri
build()dışında yaratmak: Test sınıfıenv.gen'i değiştirmek içinbuild()'in bitmiş olmasına güvenir (Ders 44); yaratma yerleri dağılırsa bu sözleşme bozulur.
Kendinizi Deneyin
scb = new(drv2scb_mbx, mon2scb_mbx, cov);satırını eski haline (covolmadan, scoreboard içindenew()) döndürün vecov.report()çıktısını gözlemleyin. Hatayı kendi gözünüzle görün.wait(scb.total_count == gen.generated_count)satırını silip yerine#100koyun; sonranum_transactions'ı 2000 yapın. Scoreboard raporundakiToplamileGeneratorsayısı tutuyor mu?- Environment'a
int timeout_cycles = 10000;ekleyin verun()'daki boşaltma zincirini Gün 4'teki izole timeout kalıbı ile sarmalayın: süre aşılırsa$fatal("Simulation hang!"). report()'a toplam simülasyon süresini ($time) ve "transaction/çevrim" verimliliğini (generated_count/ çevrim sayısı) ekleyin.
Hızlı Kontrol: Environment neden cov nesnesini yaratıp scoreboard'a veriyor; scoreboard kendisi yaratsa ne olurdu?
Scoreboard kendi cov'unu yaratsaydı environment'ın cov handle'ı farklı bir nesneyi gösterirdi; scoreboard örnekler, environment ise hiç beslenmemiş nesneyi raporlardı (%0). Tek nesneyi yaratıp paylaştırmak (dependency injection), "besleyen" ile "raporlayan"ın aynı veriye bakmasını yapısal olarak garanti eder.