EDA Playground'da Dene

pre/post_randomize()

Gün 3: Rastgele Üretim ve Kısıtlamalar | Randomize öncesi ve sonrası otomatik çağrılan fonksiyonlar

Bu derste randomize() çağrısının hemen öncesinde ve sonrasında otomatik olarak tetiklenen iki özel kanca (hook) fonksiyonunu öğreneceğiz: pre_randomize() ve post_randomize(). Bunlar, rastgeleleştirme sürecine kod enjekte etmenin standart yoludur.

pre_randomize() ve post_randomize()

SystemVerilog, her randomize() çağrısında otomatik olarak şu sırayı izler:

  1. pre_randomize() çağrılır — henüz değişkenlere rastgele değer atanmadan önce.
  2. Çözücü, kısıtlamaları çözer ve rastgele değişkenleri günceller.
  3. post_randomize() çağrılır — rastgele değerler artık hazırdır.

Bu fonksiyonları sınıfınızda tanımlamanız zorunlu değildir; tanımlamazsanız boş gövdeli varsayılanları kullanılır. Tanımladığınızda ise randomize() onları sizin için otomatik çağırır; elle çağırmanıza gerek yoktur.

pre_randomize() ne için kullanılır?

  • Randomize öncesi durum hazırlığı, sayaç güncelleme, bayrak ayarlama.
  • Bazı değişkenleri geçici olarak sabitlemek veya ön koşullar kurmak.

post_randomize() ne için kullanılır?

  • Türetilmiş alanları hesaplamak: CRC, checksum, toplam boyut gibi rastgele olmayan ama rastgele değerlere bağlı alanlar.
  • Rastgele üretilmiş değerlerde küçük düzeltmeler yapmak (örneğin belirli bitleri zorlamak).
  • Üretilen veriyi loglamak veya doğrulamak.

Kaynak Kod

// =============================================================================
// GUN 3 - Konu 5: pre_randomize() ve post_randomize() Fonksiyonlari
// =============================================================================

class EthernetFrame;
  rand bit [47:0] dst_mac;
  rand bit [47:0] src_mac;
  rand bit [15:0] ethertype;
  rand bit [7:0]  payload [];
  rand int        payload_len;
  
  // Hesaplanan alanlar (rastgele degil)
  bit [31:0] crc;
  int        frame_size;
  int        frame_id;
  static int frame_count = 0;

  constraint c_payload {
    payload_len inside {[46:1500]};  // Ethernet min/max
    payload.size() == payload_len;
  }

  constraint c_ethertype {
    ethertype inside {16'h0800,      // IPv4
                      16'h0806,      // ARP
                      16'h86DD};     // IPv6
  }

  // randomize() oncesinde otomatik cagrilir
  function void pre_randomize();
    frame_id = frame_count++;
    $display("  [PRE]  Frame #%0d randomize basliyor...", frame_id);
  endfunction

  // randomize() sonrasinda otomatik cagrilir
  function void post_randomize();
    // CRC hesapla (basitlestirilmis)
    crc = 0;
    crc ^= dst_mac[31:0] ^ dst_mac[47:32];
    crc ^= src_mac[31:0] ^ src_mac[47:32];
    crc ^= {16'h0, ethertype};
    foreach (payload[i])
      crc ^= {24'h0, payload[i]};

    frame_size = 6 + 6 + 2 + payload_len + 4;  // dst+src+type+payload+CRC

    // MAC adreslerini duzelt: unicast (LSB=0) ve locally administered
    dst_mac[0] = 0;  // Unicast
    src_mac[0] = 0;  // Unicast
    src_mac[1] = 1;  // Locally administered

    $display("  [POST] Frame #%0d: CRC=0x%08h, Size=%0d bytes",
             frame_id, crc, frame_size);
  endfunction

  function void display();
    $display("  Frame #%0d:", frame_id);
    $display("    DST MAC : %012h", dst_mac);
    $display("    SRC MAC : %012h", src_mac);
    $display("    Type    : 0x%04h (%s)", ethertype, get_type_name());
    $display("    Payload : %0d bytes", payload_len);
    $display("    CRC     : 0x%08h", crc);
    $display("    Toplam  : %0d bytes", frame_size);
  endfunction

  function string get_type_name();
    case (ethertype)
      16'h0800: return "IPv4";
      16'h0806: return "ARP";
      16'h86DD: return "IPv6";
      default:  return "Bilinmeyen";
    endcase
  endfunction
endclass

module pre_post_randomize;
  initial begin
    EthernetFrame frame;

    $display("=== pre_randomize() ve post_randomize() ===\n");

    frame = new();

    repeat (4) begin
      $display("--- Randomize cagrisi ---");
      assert(frame.randomize()) else $fatal(1, "Randomize hata!");
      frame.display();
      $display();
    end

    // Ozel kisitlama ile
    $display("--- Sadece IPv4 paketleri ---");
    repeat (2) begin
      assert(frame.randomize() with {
        ethertype == 16'h0800;
        payload_len < 100;
      }) else $fatal(1, "Randomize hata!");
      frame.display();
      $display();
    end

    $display("=== pre/post_randomize Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • EthernetFrame sınıfı rastgele alanları (dst_mac, src_mac, ethertype, payload, payload_len) ve hesaplanan/rastgele olmayan alanları (crc, frame_size, frame_id, statik frame_count) içerir.
  • c_payload constraint'i payload_len'i Ethernet'in geçerli sınırları olan 46–1500 arasına koyar ve payload.size() == payload_len ile dizi boyutunu eşitler. c_ethertype ise ethertype'ı IPv4 (0x0800), ARP (0x0806) ve IPv6 (0x86DD) değerlerinden biriyle sınırlar.
  • pre_randomize() her randomize'dan önce çalışır; frame_id = frame_count++ ile her çerçeveye benzersiz bir kimlik atar ve [PRE] etiketli bir mesaj basar. Bu, rastgeleleştirmenin ne zaman başladığını gösterir.
  • post_randomize() rastgele değerler hazır olduktan sonra çalışır ve şunları yapar:
    • dst_mac, src_mac, ethertype ve tüm payload baytları üzerinden XOR ile basitleştirilmiş bir crc hesaplar.
    • frame_size'ı başlık + payload + CRC toplamı olarak (6 + 6 + 2 + payload_len + 4) hesaplar.
    • MAC adreslerini düzeltir: dst_mac[0]=0 ve src_mac[0]=0 ile unicast, src_mac[1]=1 ile "locally administered" yapar. Bu, rastgele üretilen değerlerin post-randomize ile düzeltilmesine güzel bir örnektir.
  • get_type_name() yardımcı fonksiyonu ethertype değerini okunabilir bir isme (IPv4, ARP, IPv6) çevirir.
  • module içinde önce 4 çerçeve serbestçe üretilir; her çağrıda PRE → POST sırası ekrana yansır. Ardından randomize() with { ethertype == 16'h0800; payload_len < 100; } ile yalnızca küçük IPv4 paketleri üretilir.

Önemli Noktalar

  • pre_randomize() ve post_randomize()'ı elle çağırmayın. randomize() bunları otomatik tetikler; elle çağırmak çift çalışmaya ve hatalı duruma yol açar.
  • Hesaplanan alanları post_randomize() içinde üretin. crc ve frame_size gibi rastgele olmayan ama rastgele değerlere bağlı alanlar için doğru yer burasıdır; çünkü o aşamada tüm rastgele değerler kesinleşmiştir.
  • post_randomize() içinde değer değiştirmek kısıtlamaları atlatabilir. MAC adres bitlerinin elle ayarlanması gibi düzeltmeler çözücünün dışında gerçekleşir; bu yüzden bu değişikliklerin hiçbir constraint'i ihlal etmediğinden emin olun.
  • pre_randomize() çözücüden önce çalışır, dolayısıyla orada atadığınız rand değişkenleri çözücü tarafından üzerine yazılabilir; bu fonksiyon daha çok rastgele olmayan hazırlık için uygundur.
  • Bu kancalar, kısıtlamalarla ifade edilemeyen prosedürel mantığı (karmaşık CRC, koşullu düzeltmeler) eklemenin temiz yoludur; ancak mümkün olan her kuralı önce constraint olarak ifade etmeye çalışın.