EDA Playground'da Dene

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() ve get_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_Generator ile 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: env referansı, drv_vif/mon_vif virtual interface'leri ve test_name etiketini tutar.
  • ALU_Base_Test.run(): Test başlığını basar, env = new(...) ile environment'ı get_num_transactions() kadar işlemle oluşturur, ardından build(), reset(), run(), report() sırasını çağırır.
  • get_num_transactions(): Taban sınıfta 100 döndürür; türetilmiş sınıflar bunu ezerek farklı yük belirleyebilir.
  • ALU_Random_Test: super.new(..., "Random_Test") ile isimlendirilir ve get_num_transactions()'ı 200 döndürecek şekilde ezer. Taban run()'ı olduğu gibi kullanır.
  • ALU_Corner_Test.run(): Burada akış özelleştirilir; environment kurulduktan sonra alu_c_gen = new(env.gen2drv_mbx, ...) ile bir köşe generator'ı oluşturulur ve env.gen = alu_c_gen ile environment'ın generator'ı değiştirilir. Sonra reset(), run(), report() çağrılır.

Önemli Noktalar

  • run() ve get_num_transactions()'ın virtual olması, 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_mbx mailbox'ı build aşamasında oluşur.
  • env.gen'in çalışma zamanında yeniden atanması, gen.run()'ın virtual olması 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ıfa virtual function void configure_env(); adında bir kanca (hook) eklemektir: taban run() bunu build()'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_Generator sabit 240 kombinasyon üretir ve num argümanını yok sayar. Environment raporunun generated_count kullanması 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_mbx henüz null'dır; köşe generator null mailbox ile kurulur ve ilk put çöker.
  • run()'ı virtual yapmamak: 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'taki case string eşleştirmesi yapar; "Corner_Case_Test" ile "Corner_Test" farklı isimlerdir (Ders 46'daki case etiketleriyle karşılaştırın).

Kendinizi Deneyin

  1. ALU_Base_Test'e virtual function void configure_env(); endfunction kancası ekleyin, taban run() içinde env.build(); sonrasında çağırın. ALU_Corner_Test'i yalnızca bu kancayı override edecek şekilde sadeleştirin (artık run()'ı ezmesine gerek yok).
  2. ALU_Stress_Test extends ALU_Random_Test yazı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?
  3. ALU_Shift_Test yazın: Ders 38'deki ek görevde yazdığınız ALU_Shift_Generator'ı kullansın. Ders 46'daki case bloğuna eklemeyi unutmayın.
  4. Testlere string description; alanı ekleyin ve run() 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().