EDA Playground'da Dene

Transaction Sınıfı

Gün 6: Bitirme Projesi - Bölüm 1 | ALU transaction paketi: constraint'ler ve metodlar

Transaction (işlem paketi), testbench içinde dolaşan tek bir ALU işlemini soyutlayan veri sınıfıdır. Generator onu üretir, driver pinlere sürer, monitor DUT'tan toplar ve scoreboard karşılaştırır; yani ALU_Transaction, tüm bileşenler arasında ortak konuşulan "dil"dir.

Transaction Nedir ve Neden Gereklidir

Sinyal seviyesinde düşünmek yerine, doğrulama dünyasında işlemleri nesne olarak ele alırız. Bir ALU_Transaction nesnesi tek bir ALU işleminin hem girişlerini hem de beklenen/gözlenen çıkışlarını taşır:

  • Girişler (rand ile rastgeleleştirilir): opcode, operand_a, operand_b
  • Çıkışlar (DUT'tan okunur): result, flags, out_valid
  • Meta veri: Her işleme benzersiz bir id ve toplam sayacı count

Bu sayede bir transaction, mailbox'lar üzerinden bileşenden bileşene tek parça halinde aktarılabilir.

Constraint'ler (Kısıtlamalar) ile Akıllı Rastgelelik

Saf rastgelelik genellikle ilginç durumları yeterince üretmez. Bu sınıf üç kısıtlama kullanır:

  • c_opcode_dist: İşlem kodlarının dağılımını ağırlıklandırır; toplama/çıkarma daha sık (:= 20), mantıksal işlemler daha seyrek seçilir.
  • c_corner_cases: Operandların köşe değerlerini (0, 255, 8'h80) bilinçli olarak öne çıkarır; bunlar taşma/işaret hatalarının en sık görüldüğü değerlerdir.
  • c_shift_range: Kaydırma işlemlerinde operand_b'yi 0-7 aralığına sınırlar; çünkü 8-bit veride 8'den büyük kaydırma anlamsızdır.

Yardımcı Metotlar

  • display(): İşlemi okunabilir formatta basar; hata ayıklamada vazgeçilmezdir.
  • copy(): Nesnenin derin bir kopyasını üretir. Driver, scoreboard'a transaction'ın kopyasını gönderir; böylece aynı nesnenin paylaşılmasından doğan referans hataları engellenir.

Kaynak Kod

// =============================================================================
// GUN 6 - Konu 2: Transaction (Islem) Paketi Sinifi
// =============================================================================

class ALU_Transaction;
  // Islem kodlari (DUT ile uyumlu)
  typedef enum logic [2:0] {
    OP_ADD = 3'b000,
    OP_SUB = 3'b001,
    OP_AND = 3'b010,
    OP_OR  = 3'b011,
    OP_XOR = 3'b100,
    OP_NOT = 3'b101,
    OP_SHL = 3'b110,
    OP_SHR = 3'b111
  } opcode_e;

  // Rastgele giris degiskenleri
  rand opcode_e    opcode;
  rand logic [7:0] operand_a;
  rand logic [7:0] operand_b;

  // Cikis degiskenleri (DUT'tan okunacak)
  logic [15:0]     result;
  logic [3:0]      flags;
  logic            out_valid;

  // Meta veriler
  int              id;
  static int       count = 0;

  // --- Kisitlamalar ---
  constraint c_opcode_dist {
    opcode dist {
      OP_ADD := 20,
      OP_SUB := 20,
      OP_AND := 10,
      OP_OR  := 10,
      OP_XOR := 10,
      OP_NOT := 10,
      OP_SHL := 10,
      OP_SHR := 10
    };
  }

  constraint c_corner_cases {
    operand_a dist {
      0        := 10,
      [1:254]  :/ 70,
      255      := 10,
      8'h80    := 10
    };
    operand_b dist {
      0        := 10,
      [1:254]  :/ 70,
      255      := 10,
      8'h80    := 10
    };
  }

  // Shift isleminde B sadece 0-7
  constraint c_shift_range {
    (opcode inside {OP_SHL, OP_SHR}) -> operand_b inside {[0:7]};
  }

  function new();
    this.id = count++;
  endfunction

  function void display(string prefix = "");
    $display("  %sTXN#%03d | %s | A=0x%02h | B=0x%02h | Res=0x%04h | Flags=%04b | V=%0b",
             prefix, id, opcode.name(), operand_a, operand_b, result, flags, out_valid);
  endfunction

  function ALU_Transaction copy();
    ALU_Transaction t = new();
    t.opcode    = this.opcode;
    t.operand_a = this.operand_a;
    t.operand_b = this.operand_b;
    t.result    = this.result;
    t.flags     = this.flags;
    t.out_valid = this.out_valid;
    t.id        = this.id;
    return t;
  endfunction
endclass

Kodun Açıklaması

  • opcode_e enum'u: OP_ADD'den OP_SHR'a kadar 8 işlemi logic [2:0] olarak kodlar. Değerler DUT'taki localparam opcode'larıyla birebir aynıdır, böylece doğrudan pinlere sürülebilir.
  • rand değişkenler: opcode, operand_a, operand_b rastgeleleştirilebilir alanlardır. randomize() çağrıldığında kısıtlamalara uygun değerler üretilir.
  • Çıkış alanları: result, flags, out_valid rastgele değildir; DUT'tan veya referans modelden gelen değerlerle doldurulur.
  • static int count: Tüm nesneler arasında paylaşılan sayaç. new() içinde this.id = count++ ile her transaction'a artan benzersiz bir kimlik verilir.
  • c_opcode_dist: dist operatörüyle opcode olasılık ağırlıklarını belirler (OP_ADD := 20 gibi).
  • c_corner_cases: operand_a ve operand_b için := (her değere ayrı ağırlık) ve :/ (aralığa toplam ağırlık) operatörlerini birlikte kullanarak köşe değerleri öne çıkarır.
  • c_shift_range: (opcode inside {OP_SHL, OP_SHR}) -> operand_b inside {[0:7]} ile koşullu (implication) kısıtlama uygular.
  • copy(): Yeni bir nesne oluşturup tüm alanları tek tek kopyalar ve döndürür; sığ kopyalamanın yan etkilerinden kaçınır.

Önemli Noktalar

  • Transaction enum'u DUT opcode'larıyla senkron olmalıdır; uyumsuzluk tüm karşılaştırmaları sessizce bozar.
  • assert(txn.randomize()) her zaman kontrol edilmelidir; constraint'ler çözülemezse randomize başarısız olur ve sessizce yanlış veriyle devam edilmemelidir.
  • copy() kullanımı kritiktir: Aynı nesne referansı birden çok bileşene gönderilirse, sonradan yapılan değişiklikler beklenen değeri bozabilir. Derin kopya bu tuzağı önler.
  • dist ağırlıkları kapsamı (coverage) doğrudan etkiler; nadir senaryolara yeterli ağırlık vermek fonksiyonel kapsamı hızlandırır.
  • c_shift_range gibi koşullu kısıtlamalar, generator'daki yönlendirilmiş üretimle uyumlu olmalıdır; aksi halde solver çakışması (conflict) yaşanır.