EDA Playground'da Dene

Final Testbench

Gün 7: Bitirme Projesi - Bölüm 2 | Tüm bileşenleri bir araya getiren son testbench

Testbench Top (en üst modül), tüm projeyi bir araya getiren statik modüldür. ALU mimarimizin "ana sahnesidir": saat üretir, interface ve DUT'u somutlaştırır, bunları bağlar ve komut satırından seçilen testi başlatır.

Testbench Top ve Mimarideki Yeri

Top, hiyerarşinin en tepesinde yer alır ve sınıf tabanlı doğrulama dünyası ile statik donanım dünyasını birleştirir. Görevleri:

  • Dosyaları dahil etmek: Tüm bileşen sınıflarını `include ile derlemeye dahil eder.
  • Saat üretmek: always #5 clk = ~clk ile 100 MHz'lik saat sağlar.
  • Bağlamak: alu_if aif(clk) interface'ini ve alu dut(...) modülünü oluşturup pinleri arayüze bağlar.
  • Test seçmek: $value$plusargs ile komut satırından (+TEST=...) hangi senaryonun koşacağını belirler.

Komut Satırından Test Seçimi

+TEST=Random_Test veya +TEST=Corner_Test argümanı, simülatöre hangi test sınıfının örnekleneceğini söyler. Bu, polymorphism'in son halkasıdır: ALU_Base_Test test handle'ı türetilmiş bir nesneye (ALU_Random_Test veya ALU_Corner_Test) işaret eder ve test.run() doğru senaryoyu çalıştırır.

Dalga Formu (Waveform) Kaydı

$dumpfile/$dumpvars ile tüm hiyerarşi bir VCD dosyasına kaydedilir; hata ayıklarken sinyalleri görsel olarak incelemeyi sağlar.

Kaynak Kod

// =============================================================================
// GUN 7 KONU 6: Final Testbench Top-Level
// =============================================================================
// =============================================================================

// Statik tasarim ogeleri once: interface ve DUT
`include "alu_if.sv"            // Ders 36
`include "alu.sv"               // Ders 36-1

// Siniflar, bagimlilik sirasiyla
`include "6_2_transaction.sv"   // Ders 37  - herkes bunu kullanir
`include "7_5_coverage.sv"      // Ders 45  - scoreboard ve environment kullanir
`include "6_3_generator.sv"     // Ders 38
`include "6_4_driver.sv"        // Ders 39
`include "7_1_monitor.sv"       // Ders 41
`include "7_2_scoreboard.sv"    // Ders 42  - ALU_Coverage'i bilmeli
`include "7_3_environment.sv"   // Ders 43  - tum bilesenleri bilmeli
`include "7_4_test.sv"          // Ders 44  - environment'i bilmeli

module tb_top_final;

  // Clock generation
  logic clk = 0;
  always #5 clk = ~clk;  // 100MHz (period = 10ns)

  // Interface
  alu_if aif(clk);

  // DUT (Design Under Test)
  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)
  );

  // Test selection
  initial begin
    ALU_Base_Test test;
    
    // Declare handles for derived classes
    ALU_Random_Test rand_test;
    ALU_Corner_Test corner_test;
    
    string test_name;

    // Test selection from command line: +TEST=Random_Test
    if (!$value$plusargs("TEST=%s", test_name))
      test_name = "Random_Test";  // Default

    $display("============================================================");
    $display("  ALU VERIFICATION PROJECT - FINAL");
    $display("  Selected Test: %s", test_name);
    $display("============================================================");

    case (test_name)
      "Random_Test": begin
        rand_test = new(aif, aif);
        test = rand_test;
      end
      "Corner_Test": begin
        corner_test = new(aif, aif);
        test = corner_test;
      end
      default: begin
        $display("  Unknown test: %s, Random_Test is being used", test_name);
        rand_test = new(aif, aif);
        test = rand_test;
      end
    endcase

    test.run();

    $display("\n============================================================");
    $display("  SIMULATION COMPLETED");
    $display("============================================================");
    $finish;
  end

  // VCD waveform
  initial begin
    $dumpfile("alu_tb_final.vcd");
    $dumpvars(0, tb_top_final);
  end

endmodule

Kodun Açıklaması

  • **`include direktifleri**: Önce statik tasarım öğeleri (interface, DUT), sonra sınıflar bağımlılık sırasıyla derlemeye dahil edilir. Bir sınıf, kullandığı diğer sınıflardan **önce** derlenmiş olmalıdır: coverage scoreboard'dan, scoreboard/monitor/driver/generator environment'tan, environment test'ten önce gelir. Interface'in en başta olması, virtual alu_if.driver gibi tiplerin sınıflar derlenirken bilinmesini sağlar (bazı araçlar geç tanımı tolere eder, ama önce tanımlamak her araçta güvenlidir).
  • Saat üreteci: logic clk = 0; ile başlatılır, always #5 clk = ~clk; her 5 ns'de tersine döner, böylece 10 ns periyot (100 MHz) oluşur.
  • alu_if aif(clk): Interface, üretilen saatle somutlaştırılır.
  • alu dut (...): DUT, tüm pinleri aif arayüzünün ilgili sinyallerine bağlanarak örneklenir.
  • Test seçimi: $value$plusargs("TEST=%s", test_name) komut satırı argümanını okur; yoksa varsayılan "Random_Test" kullanılır. case bloğunda Random_Test → ALU_Random_Test, Corner_Test → ALU_Corner_Test örneklenir ve ALU_Base_Test test handle'ına atanır. Bilinmeyen isimde varsayılana düşülür.
  • test.run(): Seçilen testin sanal run() metodu çağrılır; tüm doğrulama akışı buradan başlar. Bitince $finish ile simülasyon sonlandırılır.
  • VCD: $dumpfile("alu_tb_final.vcd") ve $dumpvars(0, tb_top_final) ile tüm hiyerarşi dalga formuna kaydedilir.

Önemli Noktalar

  • `include sırası derleme bağımlılıklarını yansıtmalıdır; yanlış sıra "tanımlanmamış tip" hatalarına yol açar.

  • Test handle'ının taban tip (ALU_Base_Test) olması ve türetilmiş nesneye atanması, çalışma zamanı çok biçimliliğinin (polymorphism) tüm sistemi sürdüğü noktadır.

  • +TEST= argümanı, tek bir derlemeyle farklı senaryoları koşmayı sağlar; regresyon (regression) otomasyonu için idealdir.

  • Saat üreteci interface dışında, top'ta tutulur ve interface'e parametre olarak geçirilir; bu, tek bir saat kaynağının tüm tasarımı beslemesini garanti eder.

  • $dumpvars(0, tb_top_final) sıfır seviyesiyle tüm alt hiyerarşiyi kaydeder; büyük tasarımlarda dosya boyutu için kapsamı daraltmak gerekebilir.

  • new(aif, aif) çağrısında aynı interface iki farklı modport tipine dönüşür: İlk aif, virtual alu_if.driver, ikincisi virtual alu_if.monitor parametresine gider. SystemVerilog interface örneğini uygun modport görünümüne otomatik çevirir; bu, Ders 32'de öğrendiğiniz dönüşümün aynısıdır.

  • **`include ile package farkı:** Bu proje `include kullanır çünkü EDA Playground'da tek derleme birimi vardır ve öğrenmesi kolaydır. Gerçek projelerde sınıflar bir package alu_tb_pkg; ... endpackage içine konur, top modülde import alu_tb_pkg::*; yapılır. Paket; isim çakışmalarını önler, çift tanımı imkânsız kılar ve derleme sırasını tek dosyada toplar. UVM derslerinde bu yapıya geçeceksiniz.

  • +TEST= ile seçilebilen bir regresyon için küçük bir betik yeterlidir:

    for t in Random_Test Corner_Test; do
      xrun -f filelist.f +TEST=$t -l $t.log     # arac komutu simulatore gore degisir
    done
    grep -l "HATA TESPIT" *.log || echo "Tum testler gecti"
    

    Tek derleme, farklı +TEST= argümanları; işte regresyon otomasyonunun temeli.

Tüm Proje Bir Bakışta

tb_top_final
 ├── clk (always #5)
 ├── alu_if aif(clk) ───────────────────────┐
 ├── alu dut(.clk(aif.clk), ...)  ◀─────────┤ pinler
 └── initial: test = new(aif, aif); test.run()
        │
        └── ALU_<X>_Test.run()
              └── ALU_Environment
                    ├── gen : ALU_Generator / ALU_Corner_Generator ──gen2drv──▶ drv
                    ├── drv : ALU_Driver (vif = aif.driver) ──drv_cb──▶ DUT ──drv2scb──▶ scb
                    ├── mon : ALU_Monitor (vif = aif.monitor) ◀──mon_cb── DUT ──mon2scb──▶ scb
                    ├── scb : ALU_Scoreboard (referans model + karsilastirma) ──sample──▶ cov
                    └── cov : ALU_Coverage (tek ornek, env raporlar)

Bu şemayı Gün 1'deki "Doğrulama Mühendisinin Günlük Hayatında" tablosuyla yan yana koyun: her satır bu şemada bir kutuya dönüştü.

Sık Yapılan Hatalar

  • **`include sırasını bozmak:** "Unknown type ALU_Coverage" gibi hatalar neredeyse her zaman sıralama hatasıdır.
  • Aynı dosyayı iki kez include etmek: Sınıf çift tanımlanır. Include guard (`ifndef ALU_TRANSACTION_SV) ekleyin (Makro dersi).
  • +TEST adını yanlış yazmak: case eşleşmez, default dalı sessizce Random_Test koşturur ve siz köşe testini koştuğunuzu sanırsınız. default dalındaki uyarı mesajını mutlaka okuyun.
  • $finish'i test.run() dönmeden çağırmak: Scoreboard raporu basılamaz. run() tüm boşaltma zincirini beklediği için $finish ondan sonra gelmelidir.
  • Saati interface içinde üretmek: Birden fazla interface örneğinde saatler kayar; saat tek kaynaktan (top) gelsin.

Kendinizi Deneyin

  1. EDA Playground'da projeyi +TEST=Corner_Test ile çalıştırın ("Run options" alanına yazın). Scoreboard raporunda Toplam : 240 ve kapsam raporunda cp_op_a %100 görmelisiniz.
  2. DUT'a bilinçli hata enjekte edin (Ders 36-1, ek görev 2: OP_SUB operandlarını ters çevirin). Random_Test kaç hata yakaladı? Corner_Test? Hangi girişlerde hata görünmedi (a == b)?
  3. Tüm sınıf dosyalarını package alu_tb_pkg; ... endpackage içine alın ve top'ta import alu_tb_pkg::*; kullanın. virtual alu_if referansları için interface'in paketten önce derlenmesi gerektiğini gözlemleyin.
  4. Yukarıdaki regresyon betiğini kendi simülatörünüze uyarlayın; her iki test için log dosyalarını üretin ve grep ile özet çıkarın.
Hızlı Kontrol: ALU_Base_Test test handle'ının türetilmiş bir nesneye atanması neden tüm sistemi "sürer"?

test.run() çağrısı, run() virtual olduğu için handle'ın tipine değil nesnenin gerçek tipine göre çözülür: ALU_Corner_Test nesnesiyse onun run()'ı çalışır ve köşe generator kurulur. Bu tek satır, Gün 2'deki polimorfizm dersinin en üst katmandaki uygulamasıdır; +TEST= argümanı yalnızca hangi nesnenin yaratılacağını seçer.