EDA Playground'da Dene

Driver Modülü

Gün 6: Bitirme Projesi - Bölüm 1 | Transaction'ları DUT pin seviyesine süren modül

Driver (sürücü), soyut transaction nesnelerini gerçek pin seviyesindeki sinyal hareketlerine çeviren bileşendir. ALU mimarimizde generator ile DUT arasında köprü kurar: mailbox'tan aldığı ALU_Transactionalu_if üzerinden DUT girişlerine uygular.

Driver ve Mimarideki Yeri

Driver'ın iki yüzü vardır:

  • Üst taraf (transaction seviyesi): gen2drv mailbox'ından transaction alır.
  • Alt taraf (sinyal seviyesi): virtual alu_if.driver vif aracılığıyla clocking block üzerinden DUT pinlerini sürer.

Ayrıca driver, sürdüğü her transaction'ın bir kopyasını (txn.copy()) drv2scb mailbox'ı ile scoreboard'a iletir. Böylece scoreboard "ne uyguladık" bilgisini doğrudan kaynağından öğrenir ve referans modeliyle karşılaştırır.

Clocking Block ile Senkron Sürüş

Sinyaller vif.drv_cb clocking block'u üzerinden ve <= (nonblocking) ile sürülür. @(vif.drv_cb) ifadesi bir sonraki saat kenarını bekler. Bu yaklaşım:

  • Yarış koşullarını önler (sinyaller doğru skew ile uygulanır).
  • DUT'un in_valid el sıkışmasıyla senkron çalışmasını sağlar.

Reset Görevi

reset() task'ı rst_n'i düşürür, tüm girişleri sıfırlar, birkaç saat çevrimi bekler ve rst_n'i tekrar yükseltir. Reset sırasında rst_n doğrudan (clocking block dışından) atanır, çünkü modport'ta output rst_n olarak tanımlıdır.

Kaynak Kod

// =============================================================================
// GUN 6 - Konu 4: Driver - Uyariciyi DUT'ye Suren Modul
// =============================================================================

class ALU_Driver;
  virtual alu_if.driver vif;
  mailbox #(ALU_Transaction) gen2drv;
  mailbox #(ALU_Transaction) drv2scb;
  int driven_count = 0;

  function new(virtual alu_if.driver vif,
               mailbox #(ALU_Transaction) in_mbx,
               mailbox #(ALU_Transaction) out_mbx);
    this.vif     = vif;
    this.gen2drv = in_mbx;
    this.drv2scb = out_mbx;
  endfunction

  task run();
    ALU_Transaction txn;
    $display("[Driver] Baslatildi");
    
    forever begin
      gen2drv.get(txn);
      drive_transaction(txn);
      drv2scb.put(txn.copy());  // Scoreboard'a kopya gonder
      driven_count++;
    end
  endtask

  task drive_transaction(ALU_Transaction txn);
    @(vif.drv_cb);
    vif.drv_cb.in_valid  <= 1;
    vif.drv_cb.operand_a <= txn.operand_a;
    vif.drv_cb.operand_b <= txn.operand_b;
    vif.drv_cb.opcode    <= txn.opcode;
    
    @(vif.drv_cb);
    vif.drv_cb.in_valid <= 0;
  endtask

  task reset();
    $display("[Driver] Reset baslatiliyor...");
    vif.rst_n = 0;
    vif.drv_cb.in_valid  <= 0;
    vif.drv_cb.operand_a <= 0;
    vif.drv_cb.operand_b <= 0;
    vif.drv_cb.opcode    <= 0;
    repeat(5) @(vif.drv_cb);
    vif.rst_n = 1;
    @(vif.drv_cb);
    $display("[Driver] Reset tamamlandi");
  endtask
endclass

Kodun Açıklaması

  • Üyeler: virtual alu_if.driver vif DUT sinyallerine erişimi sağlar; gen2drv generator'dan girişi, drv2scb scoreboard'a çıkışı taşır; driven_count sürülen işlem sayısını tutar.
  • new(...): Yapıcı, virtual interface'i ve iki mailbox'ı dışarıdan alır (dependency injection), bunları kendi üyelerine bağlar.
  • run(): Sonsuz döngüde gen2drv.get(txn) ile bir transaction bekler, drive_transaction(txn) ile pinlere uygular, ardından drv2scb.put(txn.copy()) ile kopyasını scoreboard'a yollar ve driven_count++ yapar.
  • drive_transaction(txn): @(vif.drv_cb) ile saat kenarını bekler, in_valid <= 1 yapıp operandları ve opcode'u sürer. Bir sonraki kenarda in_valid <= 0 ile geçerli darbeyi tek çevrimlik tutar.
  • reset(): vif.rst_n = 0 ile reset'i etkinleştirir, tüm girişleri sıfırlar, repeat(5) @(vif.drv_cb) ile beş çevrim bekler, sonra vif.rst_n = 1 ile reset'ten çıkar.

Önemli Noktalar

  • drv2scb.put(txn.copy()) kopyalama şarttır: Aynı txn nesnesi paylaşılırsa, döngünün sonraki turunda değişebilir ve scoreboard yanlış beklenen değerle karşılaştırma yapar.
  • Tüm sürüşler clocking block (vif.drv_cb) üzerinden nonblocking (<=) yapılır; bu, DUT ile aradaki yarış koşullarını ortadan kaldırır.
  • in_valid darbesi yalnızca bir çevrim yüksek tutulur; DUT'un pipeline mantığı bu el sıkışmaya göre çıkış üretir.
  • rst_n doğrudan atanır (=), clocking block üzerinden değil; çünkü reset, clocking semantiğinin dışında kontrol edilmek istenen bir sinyaldir ve modport'ta output olarak ayrılmıştır.
  • run() sonsuz döngüdür; simülasyonun durması, generator'ın bitişi ve environment'taki bekleme/join_any mantığıyla yönetilir, driver kendi başına durmaz.