EDA Playground'da Dene

Lab 2: Hiyerarşik Transaction

Gün 2: Nesne Yönelimli Programlama (OOP) | Transaction modeli, kalıtım ve hata enjeksiyonu

Bu lab, Gün 2 boyunca öğrendiğimiz tüm OOP kavramlarını (sınıf, kalıtım, polimorfizm, virtual metodlar, $cast) tek bir uygulamada bir araya getirir. Hiyerarşik bir Transaction modeli kurup polimorfik bir kuyruk üzerinde işlemleri yönetecek ve hata enjeksiyonu yapacağız.

Lab'da Neler Yapılıyor?

Gerçek doğrulama ortamlarında işlemler (transaction) tek tip değildir: okuma, yazma ve hatalı işlemler bir arada akar. Bu lab, ortak bir temel sınıftan türeyen özelleşmiş işlem türlerini tek bir polimorfik kuyrukta yönetmenin pratik bir örneğidir.

  • Hiyerarşi: Temel Transaction sınıfından ReadTransaction ve WriteTransaction türetilir; WriteTransaction'dan da hata enjeksiyonu yapan ErrorTransaction türetilir.
  • Polimorfik kuyruk: Farklı tipteki nesneler tek bir Transaction kuyruğunda (txn_queue) tutulur ve virtual metodlar sayesinde her biri kendi davranışını sergiler.
  • Hata enjeksiyonu: ErrorTransaction, bir enum ile seçilen moda göre adres ve/veya veriyi kasıtlı olarak bozar; bu, DUT'un hatalı senaryolara tepkisini test etmek için kullanılır.
  • $cast ile tür tespiti: Kuyruktaki her eleman çalışma zamanında $cast ile sınıflandırılıp ilgili işleme tabi tutulur.

Kaynak Kod

// =============================================================================
// GUN 2 - Lab 2: Hiyerarsik Transaction Modeli ve Hata Enjeksiyonu
// =============================================================================
// Bu lab'da:
// - Temel Transaction sinifi olusturulacak
// - Kalitim ile ReadTxn, WriteTxn alt siniflari turetilecek
// - Hata enjeksiyonu yapan ErrorTxn sinifi eklenecek
// - Polimorfizm ile heterojen kuyruk islenecek
// =============================================================================

// --- Temel Islem Sinifi ---
class Transaction;
  static int global_id = 0;
  
  int          id;
  logic [31:0] address;
  logic [31:0] data;
  bit          valid;
  int          timestamp;

  function new(logic [31:0] addr = 0, logic [31:0] data = 0);
    this.id      = global_id++;
    this.address = addr;
    this.data    = data;
    this.valid   = 1;
    this.timestamp = $time;
  endfunction

  virtual function string get_type();
    return "BASE";
  endfunction

  virtual function void display();
    $display("  [%5s] ID=%0d | Addr=0x%08h | Data=0x%08h | V=%0b | T=%0t",
             get_type(), id, address, data, valid, timestamp);
  endfunction

  virtual function Transaction copy();
    Transaction t = new(this.address, this.data);
    t.id = this.id;
    t.valid = this.valid;
    return t;
  endfunction

  virtual function bit compare(Transaction other);
    return (this.address == other.address && this.data == other.data);
  endfunction
endclass

// --- Okuma Islemi ---
class ReadTransaction extends Transaction;
  int burst_size;
  logic [31:0] read_data [$];

  function new(logic [31:0] addr, int burst = 1);
    super.new(addr, 0);
    this.burst_size = burst;
  endfunction

  virtual function string get_type();
    return "READ";
  endfunction

  task execute();
    $display("  >>> READ baslatildi: Addr=0x%08h, Burst=%0d", address, burst_size);
    for (int i = 0; i < burst_size; i++) begin
      #5;
      read_data.push_back($urandom);
    end
    $display("  <<< READ tamamlandi: %0d veri okundu", read_data.size());
  endtask

  virtual function void display();
    super.display();
    $display("         Burst=%0d, Okunan=%0d adet", burst_size, read_data.size());
  endfunction
endclass

// --- Yazma Islemi ---
class WriteTransaction extends Transaction;
  logic [3:0]  byte_enable;
  logic [31:0] write_data [$];

  function new(logic [31:0] addr, logic [31:0] data, logic [3:0] be = 4'hF);
    super.new(addr, data);
    this.byte_enable = be;
    write_data.push_back(data);
  endfunction

  virtual function string get_type();
    return "WRITE";
  endfunction

  function void add_data(logic [31:0] d);
    write_data.push_back(d);
  endfunction

  virtual function void display();
    super.display();
    $display("         BE=0x%01h, WData=%p", byte_enable, write_data);
  endfunction
endclass

// --- Hata Enjeksiyonu Sinifi ---
class ErrorTransaction extends WriteTransaction;
  typedef enum {NO_ERROR, ADDR_CORRUPT, DATA_CORRUPT, BOTH_CORRUPT} error_type_e;
  error_type_e error_mode;

  function new(logic [31:0] addr, logic [31:0] data, error_type_e err = NO_ERROR);
    super.new(addr, data);
    this.error_mode = err;
  endfunction

  virtual function string get_type();
    return "ERROR";
  endfunction

  function void inject_error();
    case (error_mode)
      ADDR_CORRUPT: begin
        address ^= 32'hFFFF_0000;
        $display("  [!] HATA ENJEKSIYONU: Adres bozuldu -> 0x%08h", address);
      end
      DATA_CORRUPT: begin
        data ^= 32'h0000_FFFF;
        $display("  [!] HATA ENJEKSIYONU: Veri bozuldu -> 0x%08h", data);
      end
      BOTH_CORRUPT: begin
        address ^= 32'hFFFF_0000;
        data    ^= 32'h0000_FFFF;
        $display("  [!] HATA ENJEKSIYONU: Her ikisi de bozuldu");
      end
      default: $display("  [OK] Hata yok");
    endcase
    valid = (error_mode == NO_ERROR);
  endfunction

  virtual function void display();
    super.display();
    $display("         Hata Modu: %s", error_mode.name());
  endfunction
endclass

// =============================================================================
module lab2_hiyerarsik_transaction;
  initial begin
    Transaction txn_queue [$];  // Polimorfik kuyruk

    $display("============================================================");
    $display("  LAB 2: Hiyerarsik Transaction Modeli");
    $display("============================================================\n");

    // --- 1. Cesitli islemler olustur ---
    $display("--- 1. Islem Olusturma ---");
    begin
      ReadTransaction  r1, r2;
      WriteTransaction w1, w2;
      ErrorTransaction e1, e2, e3;

      r1 = new(32'hA000_0000, 4);
      r2 = new(32'hA000_0100, 1);
      w1 = new(32'hB000_0000, 32'hDEAD_BEEF);
      w2 = new(32'hB000_0004, 32'hCAFE_BABE, 4'h3);
      e1 = new(32'hC000_0000, 32'h1234_5678, ErrorTransaction::ADDR_CORRUPT);
      e2 = new(32'hC000_0004, 32'hABCD_EF01, ErrorTransaction::DATA_CORRUPT);
      e3 = new(32'hC000_0008, 32'h9999_9999, ErrorTransaction::BOTH_CORRUPT);

      // Polimorfik kuyruga ekle
      txn_queue = {r1, r2, w1, w2, e1, e2, e3};
    end

    // --- 2. Tum islemleri goster ---
    $display("\n--- 2. Tum Islemler (Polimorfik display) ---");
    foreach (txn_queue[i]) begin
      txn_queue[i].display();
      $display();
    end

    // --- 3. Hata enjeksiyonu ---
    $display("--- 3. Hata Enjeksiyonu ---");
    foreach (txn_queue[i]) begin
      ErrorTransaction et;
      if ($cast(et, txn_queue[i])) begin
        et.inject_error();
        et.display();
        $display();
      end
    end

    // --- 4. Istatistikler ---
    $display("--- 4. Istatistikler ---");
    begin
      int read_count = 0, write_count = 0, error_count = 0, valid_count = 0;
      foreach (txn_queue[i]) begin
        ReadTransaction rt;
        WriteTransaction wt;
        ErrorTransaction et;
        if ($cast(et, txn_queue[i])) error_count++;
        else if ($cast(rt, txn_queue[i])) read_count++;
        else if ($cast(wt, txn_queue[i])) write_count++;
        if (txn_queue[i].valid) valid_count++;
      end
      $display("  Toplam     = %0d", txn_queue.size());
      $display("  READ       = %0d", read_count);
      $display("  WRITE      = %0d", write_count);
      $display("  ERROR      = %0d", error_count);
      $display("  Gecerli    = %0d", valid_count);
      $display("  Gecersiz   = %0d", txn_queue.size() - valid_count);
    end

    $display("\n============================================================");
    $display("  LAB 2 TAMAMLANDI");
    $display("============================================================");
    $finish;
  end
endmodule

Kodun Açıklaması

  • Temel Transaction sınıfı: id, address, data, valid, timestamp alanlarını ve static int global_id sayacını içerir. Constructor'da this.id = global_id++ ile her işleme benzersiz bir kimlik verilir, this.timestamp = $time ile oluşturulma anı kaydedilir. get_type(), display(), copy() ve compare() metodları virtual'dır.
  • ReadTransaction: burst_size ve read_data kuyruğunu ekler. super.new(addr, 0) ile temeli kurar. execute() bir task'tır; #5 gecikmelerle burst_size kadar $urandom verisini read_data'ya doldurur (zaman tüketen davranış).
  • WriteTransaction: byte_enable ve write_data kuyruğunu ekler; constructor'da gelen veriyi write_data'ya koyar. add_data() ile ek veri eklenebilir. get_type() "WRITE" döndürür.
  • ErrorTransaction extends WriteTransaction: İki seviyeli kalıtım örneğidir. Sınıf içinde error_type_e adlı enum (NO_ERROR, ADDR_CORRUPT, DATA_CORRUPT, BOTH_CORRUPT) tanımlanır. inject_error() metodu, error_mode'a göre address ve/veya data'yı XOR ile bozar ve valid bayrağını günceller (valid = (error_mode == NO_ERROR)).
  • Polimorfik display(): Her sınıf display()'i virtual olarak override eder ve super.display() ile temel çıktıyı koruyup üzerine kendi bilgisini ekler. Kuyruktaki txn_queue[i].display() çağrısı her elemanın gerçek tipine göre çözülür.
  • Polimorfik kuyruk kurulumu: r1, r2, w1, w2, e1, e2, e3 nesneleri oluşturulup txn_queue = {r1, r2, w1, w2, e1, e2, e3}; ile tek bir Transaction kuyruğunda toplanır (upcasting).
  • $cast ile hata enjeksiyonu: 3. adımda her eleman için $cast(et, txn_queue[i]) denenir; yalnızca ErrorTransaction olanlar başarıyla dönüşür ve inject_error() çağrılır.
  • İstatistikler: 4. adımda $cast sıralı kontrollerle her elemanın tipi tespit edilir. Önemli sıralama: ErrorTransaction bir WriteTransaction olduğundan, et kontrolü wt kontrolünden önce yapılır; aksi halde hatalı işlemler yazma olarak sayılırdı. valid bayrağına göre geçerli/geçersiz sayımı da yapılır.

Önemli Noktalar

  • Polimorfik bir kuyruk (Transaction txn_queue[$]) farklı alt tipleri tek tip kodla işlemeyi sağlar; bu, ölçeklenebilir doğrulama ortamlarının temel kalıbıdır.
  • $cast ile tür kontrolü yaparken sıralama kritiktir: en özelleşmiş (en alttaki) tipten en genele doğru kontrol edin. ErrorTransaction, WriteTransaction'dan türediği için önce ona bakılmalıdır.
  • virtual metodlar olmadan polimorfik kuyruktaki display() çağrıları temel sınıf sürümünü çalıştırırdı; doğru davranış için metodlar virtual olmalıdır.
  • static global_id sayacı tüm işlemler arasında paylaşıldığından, hangi alt sınıftan üretilirse üretilsin her nesne benzersiz bir id alır.
  • Hata enjeksiyonu (inject_error) gerçek doğrulamada DUT'un hatalı/bozuk işlemlere dayanıklılığını sınamak için kullanılır; valid bayrağı sonrasında beklenen sonucu belirlemeye yardımcı olur.
  • execute() bir task olduğundan zaman tüketir; zamanlamaya duyarlı senaryolarda function yerine task kullanılması gerektiğini hatırlatır.