Test Sınıfları
Gün 7: Bitirme Projesi - Bölüm 2 | Farklı test senaryoları: Random, Corner, Stress
Test sınıfları, doğrulama senaryolarını tanımlayan en üst seviye kontrol katmanıdır. ALU mimarimizde test, environment'ı oluşturur, yapılandırır ve çalıştırır; "hangi senaryoyu, kaç transaction ile koşacağız?" kararını verir.
Test Katmanı ve Mimarideki Yeri
Test, testbench top ile environment arasında durur. Top seviyesi yalnızca hangi testin koşulacağını seçer; testin içeriğini test sınıfları belirler. Bu katman sayesinde aynı environment, farklı senaryolarla tekrar tekrar kullanılabilir.
Bu derste kalıtım tabanlı bir test hiyerarşisi vardır:
ALU_Base_Test: Ortak akışı tanımlar (build→reset→run→report).run()veget_num_transactions()metotlarıvirtual'dır.ALU_Random_Test: Taban testi miras alır; yalnızca transaction sayısını (200) değiştirir, varsayılan rastgele generator'ı kullanır.ALU_Corner_Test:run()metodunu ezerek environment'ın generator'ınıALU_Corner_Generatorile değiştirir; böylece köşe durum testi koşar.
Polymorphism ile Generator Değiştirme
ALU_Corner_Test'in en öğretici yanı, environment kurulduktan sonra env.gen referansını türetilmiş bir generator'la değiştirmesidir. run() metodu virtual olduğundan, environment fork ettiğinde doğru (köşe) generator çalışır. Bu, çalışma zamanı çok biçimliliğinin (polymorphism) pratik bir örneğidir.
Kaynak Kod
// =============================================================================
// GUN 7 - Konu 4: Test Siniflari - Farkli Test Senaryolari
// =============================================================================
class ALU_Base_Test;
ALU_Environment env;
virtual alu_if.driver drv_vif;
virtual alu_if.monitor mon_vif;
string test_name;
function new(virtual alu_if.driver drv_vif, virtual alu_if.monitor mon_vif,
string name = "Base_Test");
this.drv_vif = drv_vif;
this.mon_vif = mon_vif;
this.test_name = name;
endfunction
virtual task run();
$display("\n##########################################################");
$display(" TEST: %s", test_name);
$display("##########################################################\n");
// Environment olustur
env = new(drv_vif, mon_vif, get_num_transactions());
env.build();
env.reset();
env.run();
env.report();
endtask
virtual function int get_num_transactions();
return 100;
endfunction
endclass
// Rastgele test
class ALU_Random_Test extends ALU_Base_Test;
function new(virtual alu_if.driver drv_vif, virtual alu_if.monitor mon_vif);
super.new(drv_vif, mon_vif, "Random_Test");
endfunction
virtual function int get_num_transactions();
return 200;
endfunction
endclass
// Kose durumu testi
class ALU_Corner_Test extends ALU_Base_Test;
function new(virtual alu_if.driver drv_vif, virtual alu_if.monitor mon_vif);
super.new(drv_vif, mon_vif, "Corner_Case_Test");
endfunction
virtual task run();
ALU_Corner_Generator alu_c_gen;
env = new(drv_vif, mon_vif, get_num_transactions());
env.build();
alu_c_gen = new(env.gen2drv_mbx, get_num_transactions());
env.gen = alu_c_gen;
// 3. Run simulation
env.reset();
env.run();
env.report();
endtask
endclass
Kodun Açıklaması
ALU_Base_Testüyeleri:envreferansı,drv_vif/mon_vifvirtual interface'leri vetest_nameetiketini tutar.ALU_Base_Test.run(): Test başlığını basar,env = new(...)ile environment'ıget_num_transactions()kadar işlemle oluşturur, ardındanbuild(),reset(),run(),report()sırasını çağırır.get_num_transactions(): Taban sınıfta100döndürür; türetilmiş sınıflar bunu ezerek farklı yük belirleyebilir.ALU_Random_Test:super.new(..., "Random_Test")ile isimlendirilir veget_num_transactions()'ı200döndürecek şekilde ezer. Tabanrun()'ı olduğu gibi kullanır.ALU_Corner_Test.run(): Burada akış özelleştirilir; environment kurulduktan sonraalu_c_gen = new(env.gen2drv_mbx, ...)ile bir köşe generator'ı oluşturulur veenv.gen = alu_c_genile environment'ın generator'ı değiştirilir. Sonrareset(),run(),report()çağrılır.
Önemli Noktalar
run()veget_num_transactions()'ınvirtualolması, test hiyerarşisinin temelidir; aynı taban akış farklı senaryolara uyarlanır.ALU_Corner_Test, generator'ıbuild()'den sonra değiştirir; çünkü değiştirilecek generator'ın bağlanacağıenv.gen2drv_mbxmailbox'ı build aşamasında oluşur.env.gen'in çalışma zamanında yeniden atanması,gen.run()'ınvirtualolması sayesinde doğru türetilmiş davranışı tetikler.- Yeni bir test senaryosu eklemek kolaydır:
ALU_Base_Test'ten türeyip yalnızca farklı kısımları ezmek yeterlidir; mevcut kodu değiştirmeye gerek kalmaz (açık/kapalı prensibi). - Test ismi (
test_name) anlamlı tutulmalıdır; raporlarda ve top seviyesindeki+TEST=seçiminde bu isim referans alınır. ALU_Corner_Test.run()taban akışı kopyalar — bu bir kod kokusudur.env = new(...),build(),reset(),run(),report()satırlarıALU_Base_Test.run()ile aynıdır; yalnızca araya "generator'ı değiştir" adımı girer. Daha temiz çözüm, taban sınıfavirtual function void configure_env();adında bir kanca (hook) eklemektir: tabanrun()bunubuild()'den sonra çağırır, köşe test yalnızca bu kancayı override eder. Ek görevlerde bunu yapacaksınız; UVM'in faz mekanizması tam olarak bu kanca fikrinin genelleştirilmiş hâlidir.- Köşe testinde
get_num_transactions()anlamsızdır:ALU_Corner_Generatorsabit 240 kombinasyon üretir venumargümanını yok sayar. Environment raporunungenerated_countkullanması bu yüzden önemlidir.
Test Hiyerarşisi
ALU_Base_Test run(): env kur → build → reset → run → report ; get_num_transactions() = 100
├── ALU_Random_Test get_num_transactions() = 200 (run() taban)
└── ALU_Corner_Test run(): ... build → env.gen = Corner_Generator → reset → run → report
Yeni bir test eklemek, bu ağaca bir dal eklemektir; mevcut dallara dokunulmaz (açık/kapalı prensibi: genişlemeye açık, değişikliğe kapalı).
Test Katmanı Ne Yapar, Ne Yapmaz?
| Test katmanının işi | Test katmanının işi değil |
|---|---|
| Hangi generator/senaryo? | Pinleri sürmek (driver) |
| Kaç transaction? | Sonucu hesaplamak (scoreboard) |
| Environment'ı kurmak ve başlatmak | Sinyal gözlemek (monitor) |
| Konfigürasyon parametreleri (gecikme, hata oranı...) | Mailbox bağlantıları (environment) |
| Raporu tetiklemek | Kapsam tanımlamak (coverage) |
Test ne kadar "ince" kalırsa, aynı environment o kadar çok senaryoda yeniden kullanılır. UVM'de test sınıfları genellikle 30–50 satırdır; tüm iş environment'ta ve altındadır.
Benzetme: Environment bir orkestra, test ise konser programıdır. Program "bu akşam Beethoven, 40 dakika" der; enstrümanları çalmaz, notaları yazmaz. Aynı orkestra her akşam farklı programla sahneye çıkar.
Sık Yapılan Hatalar
- Generator'ı
build()'den önce değiştirmek:env.gen2drv_mbxhenüznull'dır; köşe generatornullmailbox ile kurulur ve ilkputçöker. run()'ıvirtualyapmamak:ALU_Base_Test test = corner_test; test.run();taban sürümü çalıştırır; köşe testi hiç koşmaz.- Test içinde pin sürmek / sonuç kontrol etmek: Katman sınırını aşar; test environment'tan bağımsız hâle gelir ve yeniden kullanılamaz.
- Her test için ayrı environment sınıfı yazmak: Environment ortak kalmalı; farklılık test katmanında (hangi generator, kaç transaction) ifade edilir.
- Test adını
+TEST=ile uyumsuz seçmek: Top'takicasestring eşleştirmesi yapar;"Corner_Case_Test"ile"Corner_Test"farklı isimlerdir (Ders 46'dakicaseetiketleriyle karşılaştırın).
Kendinizi Deneyin
ALU_Base_Test'evirtual function void configure_env(); endfunctionkancası ekleyin, tabanrun()içindeenv.build();sonrasında çağırın.ALU_Corner_Test'i yalnızca bu kancayı override edecek şekilde sadeleştirin (artıkrun()'ı ezmesine gerek yok).ALU_Stress_Test extends ALU_Random_Testyazın:get_num_transactions()2000 döndürsün. Simülasyon süresi ve kapsam yüzdeleri 200'e göre nasıl değişti?ALU_Shift_Testyazın: Ders 38'deki ek görevde yazdığınızALU_Shift_Generator'ı kullansın. Ders 46'dakicasebloğuna eklemeyi unutmayın.- Testlere
string description;alanı ekleyin verun()başında bassın. Regresyon raporlarında test açıklamalarının neden değerli olduğunu düşünün.
Hızlı Kontrol: ALU_Corner_Test generator'ı neden env.build()'den sonra değiştirir?
Köşe generator'ın bağlanacağı env.gen2drv_mbx mailbox'ı build() içinde yaratılır. build() öncesinde bu handle null'dır; generator null mailbox ile kurulursa ilk put çağrısında çalışma zamanı hatası alınır. Bu yüzden sıra: build() → generator değiştir → reset() → run().