EDA Playground'da Dene

constraint_mode

Gün 3: Rastgele Üretim ve Kısıtlamalar | Çalışma zamanında kısıtlamaları açma/kapatma

Bu derste, çalışma zamanında (runtime) bir constraint bloğunu açıp kapatmaya yarayan constraint_mode() metodunu öğreneceğiz. Bu yetenek, aynı sınıfı farklı test senaryolarına yeniden kullanmanın en güçlü yollarından biridir.

constraint_mode() Nedir?

Bir sınıfta birden çok constraint bloğu tanımlandığında, normalde hepsi aktiftir ve randomize() çağrısında hepsi birden sağlanmaya çalışılır. constraint_mode() metodu, bir constraint bloğunu çalışma zamanında etkin (1) veya devre dışı (0) hale getirmemizi sağlar.

İki kullanım biçimi vardır:

  • Belirli bir bloğu kontrol etmek: nesne.constraint_adi.constraint_mode(0) o bloğu kapatır, (1) açar.
  • Tüm blokları kontrol etmek: nesne.constraint_mode(0) sınıftaki tüm kısıtlamaları kapatır (tam serbest rastgele üretim), (1) hepsini açar.

Ayrıca argümansız çağrılırsa (nesne.constraint_adi.constraint_mode()) o bloğun mevcut durumunu (0/1) döndürür, yani durumu sorgulamak için de kullanılır.

Neden Kullanılır?

  • Aynı transaction sınıfını farklı senaryolarda (düşük adres / yüksek adres, okuma / yazma, normal / hata) tekrar kullanmak.
  • Birbiriyle çakışan kısıtlama bloklarını bilinçli olarak tanımlayıp, her seferinde yalnızca uygun olanı açmak.
  • Hata enjeksiyonu (error injection) gibi özel modları gerektiğinde devreye almak.

Çakışan Kısıtlamaları Yönetmek

Bu desende sıkça, mantıken birbiriyle çelişen blocklar (örneğin "düşük adres" ve "yüksek adres") aynı sınıfta tanımlanır. Çakışmayı önlemek için kurucuda (new()) bunların bir kısmı baştan kapatılır; ardından test akışında uygun olan açılıp diğeri kapatılır.

Kaynak Kod

// =============================================================================
// GUN 3 - Konu 6: Calisma Zamaninda Kisitlamalari Acma/Kapatma (constraint_mode)
// =============================================================================

class FlexPacket;
  rand bit [15:0] address;
  rand bit [31:0] data;
  rand bit [2:0]  burst_len;
  rand bit        write_en;
  rand bit [1:0]  resp;

  // Adres araligi kisitlamasi
  constraint c_addr_low {
    address inside {[16'h0000:16'h00FF]};
  }

  constraint c_addr_high {
    address inside {[16'hFF00:16'hFFFF]};
  }


  // Burst kisitlamasi
  constraint c_burst_aligned {
    (burst_len > 0) -> (address[1:0] == 2'b00);  // Hizali
  }

  // Yazma/okuma kisitlamasi
  constraint c_write_mode {
    write_en == 1;
  }

  constraint c_read_mode {
    write_en == 0;
    data == 0;
  }

  // Hata yaniti kisitlamasi
  constraint c_no_error {
    resp == 0;  // OKAY
  }

  constraint c_force_error {
    resp inside {2'b10, 2'b11};  // SLVERR veya DECERR
  }

  function new();
    // Baslangicta cakisan kisitlamalari kapat
    c_addr_high.constraint_mode(0);
    c_read_mode.constraint_mode(0);
    c_force_error.constraint_mode(0);
  endfunction

  function void display();
    $display("  Addr=0x%04h | Data=0x%08h | Burst=%0d | W/R=%s | Resp=%0b",
             address, data, burst_len, write_en ? "WRITE" : "READ", resp);
  endfunction
endclass

module constraint_mode_ornek;
  initial begin
    FlexPacket pkt;
    $display("=== constraint_mode Kullanimi ===\n");

    pkt = new();

    // --- Senaryo 1: Dusuk adres, yazma ---
    $display("--- Senaryo 1: Dusuk Adres Yazma ---");
    repeat (3) begin
      assert(pkt.randomize()) else $fatal(1, "Hata!");
      pkt.display();
    end

    // --- Senaryo 2: Yuksek adres bolgesine gec ---
    $display("\n--- Senaryo 2: Yuksek Adres Bolgesi ---");
    pkt.c_addr_low.constraint_mode(0);   // Dusuk adresi kapat
    pkt.c_addr_high.constraint_mode(1);  // Yuksek adresi ac
    repeat (3) begin
      assert(pkt.randomize()) else $fatal(1, "Hata!");
      pkt.display();
    end

    // --- Senaryo 3: Okuma modu ---
    $display("\n--- Senaryo 3: Okuma Modu ---");
    pkt.c_write_mode.constraint_mode(0); // Yazma kapat
    pkt.c_read_mode.constraint_mode(1);  // Okuma ac
    repeat (3) begin
      assert(pkt.randomize()) else $fatal(1, "Hata!");
      pkt.display();
    end

    // --- Senaryo 4: Hata enjeksiyonu ---
    $display("\n--- Senaryo 4: Hata Yaniti ---");
    pkt.c_read_mode.constraint_mode(0);
    pkt.c_write_mode.constraint_mode(1);
    pkt.c_no_error.constraint_mode(0);    // Normal yaniti kapat
    pkt.c_force_error.constraint_mode(1); // Hata yanitini ac
    repeat (3) begin
      assert(pkt.randomize()) else $fatal(1, "Hata!");
      pkt.display();
    end

    // --- Senaryo 5: Tum kisitlamalari kapat (tam rastgele) ---
    $display("\n--- Senaryo 5: Serbest (Tum constraint kapali) ---");
    pkt.constraint_mode(0);  // TUM kisitlamalari kapat
    repeat (3) begin
      assert(pkt.randomize()) else $fatal(1, "Hata!");
      pkt.display();
    end

    // --- Durumu sorgula ---
    $display("\n--- Constraint Durumlari ---");
    pkt.constraint_mode(1);  // Hepsini ac
    pkt.c_addr_high.constraint_mode(0);
    pkt.c_read_mode.constraint_mode(0);
    pkt.c_force_error.constraint_mode(0);
    $display("  c_addr_low    = %0d", pkt.c_addr_low.constraint_mode());
    $display("  c_addr_high   = %0d", pkt.c_addr_high.constraint_mode());
    $display("  c_write_mode  = %0d", pkt.c_write_mode.constraint_mode());
    $display("  c_read_mode   = %0d", pkt.c_read_mode.constraint_mode());

    $display("\n=== constraint_mode Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • FlexPacket sınıfı, bilinçli olarak çakışan çiftler içeren çok sayıda constraint bloğu tanımlar: c_addr_low / c_addr_high (düşük/yüksek adres), c_write_mode / c_read_mode (yazma/okuma), c_no_error / c_force_error (normal/hata yanıtı).
  • new() kurucusunda çakışmayı önlemek için c_addr_high, c_read_mode ve c_force_error baştan constraint_mode(0) ile kapatılır. Böylece nesne, başlangıçta düşük adres + yazma + normal yanıt üretecek şekilde tutarlı bir başlangıç durumundadır.
  • Senaryo 1 varsayılan durumu kullanır: düşük adres aralığı ve yazma modu.
  • Senaryo 2 adres bölgesini değiştirir: pkt.c_addr_low.constraint_mode(0) ile düşük adres kapatılır, pkt.c_addr_high.constraint_mode(1) ile yüksek adres açılır. Artık adresler 0xFF00–0xFFFF aralığından gelir.
  • Senaryo 3 okuma moduna geçer: c_write_mode kapatılıp c_read_mode açılır; bu blok write_en == 0 ve data == 0 zorlar.
  • Senaryo 4 hata enjeksiyonu yapar: c_no_error kapatılıp c_force_error açılır; resp değeri SLVERR (2'b10) veya DECERR (2'b11) olur.
  • Senaryo 5 pkt.constraint_mode(0) ile tüm kısıtlamaları kapatır — bu, tamamen serbest rastgele üretim demektir; her alan kendi tam bit aralığında değer alabilir.
  • Son bölümde pkt.constraint_mode(1) ile hepsi tekrar açılır, sonra çakışan bloklar yeniden kapatılır ve constraint_mode() argümansız çağrılarak her bloğun mevcut durumu (c_addr_low, c_addr_high, c_write_mode, c_read_mode) sorgulanıp yazdırılır.

Önemli Noktalar

  • Çakışan kısıtlamaları aynı anda açık bırakmayın. Örneğin hem c_addr_low hem c_addr_high açıkken çözüm bulunamaz ve randomize() başarısız olur; her zaman yalnızca uygun olanı aktif tutun.
  • Kurucuda güvenli bir başlangıç durumu kurun. Burada olduğu gibi çakışan blocların bir kısmını new() içinde kapatmak, nesnenin daima tutarlı bir durumda doğmasını sağlar.
  • constraint_mode(0) tüm blokları kapatır. Belirli bir blok yerine nesnenin kendisi üzerinden çağrıldığında etki sınıf geneline yayılır; bunu tam serbest üretim için bilinçli kullanın.
  • Durum sorgulamak için argümansız çağırın. constraint_adi.constraint_mode() mevcut etkinlik durumunu döndürür; karmaşık senaryolarda hangi blokların açık olduğunu doğrulamak için faydalıdır.
  • Senaryolar arası geçişlerde modu değiştirdikten sonra, eski senaryonun açtığı blokları tekrar kapatmayı unutmayın; aksi halde sonraki randomize'lar beklenmedik kısıtlamalarla karşılaşır.