EDA Playground'da Dene

Generator Modülü

Gün 6: Bitirme Projesi - Bölüm 1 | Rastgele ve yönlendirilmiş test üretici

Generator (üretici), testbench'in uyaranlarını (stimulus) üreten bileşendir. ALU mimarimizde zincirin başında durur: ALU_Transaction nesneleri oluşturur, kısıtlamalara göre rastgeleleştirir ve bir mailbox üzerinden driver'a iletir.

Generator ve Mimarideki Yeri

Generator, "DUT'a hangi senaryoları uygulayalım?" sorusunun cevabını üretir. Driver ile arasındaki iletişim, gen2drv adlı bir mailbox ile sağlanır:

  • Generator transaction'ı gen2drv.put(txn) ile kutuya bırakır.
  • Driver aynı kutudan get() ile alır.

Bu yapı producer-consumer (üretici-tüketici) modelidir ve mailbox sayesinde iki bileşen birbirinden bağımsız hızlarda çalışabilir.

Kalıtım ile İki Üretim Stratejisi

Bu derste iki üretici sınıfı vardır:

  • ALU_Generator (taban sınıf): Belirtilen sayıda tamamen rastgele transaction üretir. run() metodu virtual tanımlanmıştır; bu sayede türetilmiş sınıflar davranışı ezebilir (override).
  • ALU_Corner_Generator (türetilmiş sınıf): extends ALU_Generator ile yönlendirilmiş (directed) test üretir. Önceden tanımlı köşe değer kümesini (corners) tüm işlemlerle çaprazlayarak sistematik biçimde tarar.

Her iki sınıf da işi bitince -> done ile bir event tetikler; böylece environment, üretimin tamamlandığını anlayabilir.

Kaynak Kod

// =============================================================================
// GUN 6 - Konu 3: Generator - Rastgele Uyarici Uretici
// =============================================================================

class ALU_Generator;
  mailbox #(ALU_Transaction) gen2drv;
  int num_transactions;
  event done;

  function new(mailbox #(ALU_Transaction) mbx, int num = 100);
    this.gen2drv = mbx;
    this.num_transactions = num;
  endfunction

  // Base run task must be virtual to be overridden
  virtual task run();
    ALU_Transaction txn;
    $display("[Generator] Random tests starting...");
    for (int i = 0; i < num_transactions; i++) begin
      txn = new();
      assert(txn.randomize()) else $fatal(1, "Randomize Error!");
      gen2drv.put(txn);
    end
    -> done;
  endtask
endclass

// Derived generator for corner cases
class ALU_Corner_Generator extends ALU_Generator;
  
  function new(mailbox #(ALU_Transaction) mbx, int num = 100);
    super.new(mbx, num);
  endfunction

  virtual task run();
    ALU_Transaction txn;
    logic [7:0] corners [] = '{0, 1, 8'h7F, 8'h80, 8'hFE, 8'hFF};
    
    $display("[Corner_Generator] Corner cases starting...");
    foreach (corners[i]) begin
      foreach (corners[j]) begin
        for (int op = 0; op < 8; op++) begin
          
          // Skip invalid shift operations where operand_b is greater than 7
          // to prevent constraint solver conflicts with c_shift_range
          if ((op == ALU_Transaction::OP_SHL || op == ALU_Transaction::OP_SHR) && corners[j] > 7) begin
            continue;
          end

          txn = new();
          assert(txn.randomize() with {
            operand_a == corners[i];
            operand_b == corners[j];
            opcode    == ALU_Transaction::opcode_e'(op);
          }) else $fatal(1, "Randomize Error!");
          
          gen2drv.put(txn);
        end
      end
    end
    -> done;
  endtask
endclass

Kodun Açıklaması

  • mailbox #(ALU_Transaction) gen2drv: Üretilen transaction'ların driver'a aktarıldığı tipli kuyruk. Yapıcıda (new) dışarıdan alınır, böylece environment hangi mailbox'ın kullanılacağını kontrol eder.
  • int num_transactions ve event done: Üretilecek işlem sayısı ve bitiş sinyali. done event'i, üretim tamamlandığında -> done ile tetiklenir.
  • virtual task run() (taban): Bir döngüde num_transactions kadar txn = new() yapar, assert(txn.randomize()) ile kısıtlamalara uygun rastgele değerler üretir ve gen2drv.put(txn) ile kuyruğa koyar. randomize başarısız olursa $fatal ile durur.
  • ALU_Corner_Generator: Taban sınıftan türetilir; super.new(mbx, num) ile mailbox'ı miras alır.
  • Köşe değer dizisi: logic [7:0] corners [] = '{0, 1, 8'h7F, 8'h80, 8'hFE, 8'hFF} ile kritik operand değerleri tanımlanır.
  • İç içe foreach + for: Tüm operand-a × operand-b × opcode kombinasyonları taranır. Kaydırma işlemlerinde corners[j] > 7 olan durumlar continue ile atlanır; bu, c_shift_range kısıtlamasıyla çakışmayı önler.
  • randomize() with {...}: Operand ve opcode değerleri satır içi kısıtlamayla sabitlenerek tam istenen kombinasyon üretilir.

Önemli Noktalar

  • run() metodunun virtual olması kalıtımın anahtarıdır; environment aynı gen referansı üzerinden hem rastgele hem yönlendirilmiş üreticiyi çalıştırabilir (polymorphism).
  • -> done event'i ile senkronizasyon: Environment, üretimin bittiğini bu event'i bekleyerek anlar; aksi halde simülasyon erken sonlanabilir veya gereksiz beklenir.
  • Yönlendirilmiş testte solver çakışmasından kaçınma: Geçersiz shift kombinasyonlarını continue ile atlamak, randomize() with çağrısının başarısız olmasını engeller.
  • Mailbox'ın dışarıdan enjekte edilmesi (dependency injection), generator'ı test ortamından bağımsız ve yeniden kullanılabilir kılar.
  • Köşe değer taraması, saf rastgeleliğin kaçırabileceği sınır durumlarını garanti altına alır; rastgele ve yönlendirilmiş yaklaşımlar birbirini tamamlar.