EDA Playground'da Dene

Constraint Blokları

Gün 3: Rastgele Üretim ve Kısıtlamalar | inside, dist, if-else, implication (->) kısıtlamaları

Bu derste constraint bloklarının en sık kullanılan yapı taşlarını öğreneceğiz: değer kümesi sınırlayan inside, olasılık dağılımı belirleyen dist, koşullu kısıtlama kuran if-else ve bir koşuldan sonuç türeten implication (->) operatörü.

Constraint Bloğu Nedir?

Bir constraint bloğu, bir sınıfın rastgele değişkenlerinin hangi değerleri alabileceğini tanımlayan kurallar kümesidir. randomize() çağrıldığında çözücü (solver), tüm aktif constraint bloklarını aynı anda sağlayan bir değer kombinasyonu bulmaya çalışır. Kısıtlamalar bildirimseldir (declarative): "ne olması gerektiğini" söylersiniz, "nasıl çözüleceğini" değil.

inside

inside operatörü bir değişkeni belirli bir değer kümesi veya aralık(lar)ı ile sınırlar. Birden fazla aralık verilebilir ve ! ile olumsuzlanarak (!(x inside {...})) bir kümenin dışında kalması da istenebilir.

dist — Olasılık Dağılımı

dist, değerlerin hangi olasılıkla seçileceğini belirler. İki ağırlık operatörü vardır:

  • := → ağırlık her bir değere ayrı ayrı uygulanır.
  • :/ → ağırlık, belirtilen aralıktaki tüm değerlere eşit olarak bölüştürülür.

if-else ve implication (->)

  • if-else kısıtlamaları, bir alanın değerine göre başka alanlara koşullu kurallar uygulamayı sağlar.
  • Implication (->) ise "A ise B de olmalı" mantığını ifade eder. (koşul) -> (sonuç) biçiminde yazılır; koşul doğruysa sonuç da zorlanır, koşul yanlışsa sonuç üzerinde herhangi bir kısıtlama olmaz.

Kaynak Kod

// =============================================================================
// GUN 3 - Konu 3: Kisitlama Bloklari: inside, dist, if-else, implication
// =============================================================================

class ConfigPacket;
  typedef enum logic [1:0] {IDLE, READ, WRITE, BURST} cmd_e;

  rand cmd_e         command;
  rand logic [15:0]  address;
  rand logic [31:0]  data;
  rand int unsigned  length;
  rand bit [7:0]     prio;
  rand bit           valid;

  // --- inside: Deger kumesi kisitlamasi ---
  constraint c_addr_inside {
    address inside {[16'h0000:16'h0FFF],    // Bolge 1
                    [16'h2000:16'h2FFF],    // Bolge 2
                    [16'hF000:16'hFFFF]};   // Bolge 3
  }

  // --- dist: Olasilik dagilimi ---
  constraint c_cmd_dist {
    command dist {
      READ   := 40,   // %40 okuma
      WRITE  := 40,   // %40 yazma
      BURST  := 15,   // %15 burst
      IDLE   := 5     // %5  bosta
    };
  }

  constraint c_priority_dist {
    prio dist {
      [0:3]   :/ 60,   // Dusuk oncelik - %60 (/= esit dagilir: her biri %15)
      [4:6]   :/ 30,   // Orta oncelik - %30
      7       := 10    // Yuksek oncelik - %10
    };
  }

  // --- if-else: Kosullu kisitlama ---
  constraint c_data_ifelse {
    if (command == READ) {
      data == 32'h0;                        // Okumada veri sifir
      length inside {[1:4]};
    } else if (command == WRITE) {
      data != 32'h0;                        // Yazmada veri sifir olamaz
      length inside {[1:8]};
    } else if (command == BURST) {
      length inside {[4:16]};               // Burst uzun
    } else {
      length == 0;                          // IDLE
      data == 0;
    }
  }

  // --- implication (->): Sonuc kisitlamasi ---
  constraint c_valid_impl {
    (command != IDLE) -> (valid == 1);      // IDLE degilse gecerli olmali
    (command == IDLE) -> (valid == 0);
  }

  constraint c_burst_addr {
    (command == BURST) -> (address[3:0] == 4'h0);  // Burst hizali adres
  }

  // --- Sabit deger dislama ---
  constraint c_no_zero_addr {
    address != 16'h0000;
  }

  function void display();
    $display("  CMD=%-5s | Addr=0x%04h | Data=0x%08h | Len=%2d | Pri=%0d | V=%0b",
             command.name(), address, data, length, prio, valid);
  endfunction
endclass

module constraint_bloklari;
  initial begin
    ConfigPacket pkt;
    int cmd_counts [string];

    $display("=== Constraint Bloklari ===\n");
    pkt = new();

    // --- Kisitlamali rastgele uretim ---
    $display("--- 20 Rastgele Paket ---");
    cmd_counts["IDLE"]  = 0;
    cmd_counts["READ"]  = 0;
    cmd_counts["WRITE"] = 0;
    cmd_counts["BURST"] = 0;

    repeat (20) begin
      assert(pkt.randomize()) else $fatal(1, "Randomize hata!");
      pkt.display();
      cmd_counts[pkt.command.name()]++;
    end

    // --- Dagilim istatistikleri ---
    $display("\n--- Komut Dagilimi (20 deneme) ---");
    foreach (cmd_counts[key])
      $display("  %s: %0d kez (%%%0d)", key, cmd_counts[key], cmd_counts[key]*5);

    // --- inside dislama (~inside) ---
    $display("\n--- Adres Dislama (~inside) ---");
    repeat (5) begin
      assert(pkt.randomize() with {
        !(address inside {[16'h0000:16'h00FF]});
      });
      $display("  Addr = 0x%04h (0x0000-0x00FF disinda)", pkt.address);
    end

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

Kodun Açıklaması

  • ConfigPacket sınıfı, cmd_e adında bir enum (IDLE, READ, WRITE, BURST) ve command, address, data, length, prio, valid rastgele üyelerini içerir.
  • c_addr_inside constraint'i address'i üç ayrı bölgeyle sınırlar: 0x0000–0x0FFF, 0x2000–0x2FFF ve 0xF000–0xFFFF. Bu, çoklu aralıklı inside kullanımının tipik örneğidir.
  • c_cmd_dist, command enum'una bir dağılım atar: READ ve WRITE %40'ar, BURST %15, IDLE %5. Burada := operatörü kullanıldığı için her ağırlık o tek değere uygulanır.
  • c_priority_dist, :/ operatörünü gösterir: [0:3] :/ 60 ifadesinde %60'lık ağırlık dört değere eşit bölünür (her biri ~%15). 7 := 10 ise tek değere %10 verir.
  • c_data_ifelse, command değerine göre koşullu kurallar uygular. Örneğin READ durumunda data == 0 ve length inside {[1:4]}; WRITE durumunda data != 0 ve length inside {[1:8]} olur.
  • c_valid_impl implication kullanır: (command != IDLE) -> (valid == 1) ve (command == IDLE) -> (valid == 0). Böylece IDLE dışındaki komutlarda valid her zaman 1, IDLE'da 0 olur.
  • c_burst_addr, BURST komutunda address[3:0] == 4'h0 zorlayarak hizalı (aligned) adres garantisi verir.
  • c_no_zero_addr, address != 16'h0000 ile sıfır adresi tamamen dışlar.
  • module içinde 20 paket üretilir ve cmd_counts adlı string-indeksli ilişkisel dizide her komutun kaç kez çıktığı sayılır; ardından dağılım yüzdeleri (cmd_counts[key]*5) yazdırılır.
  • Son bölümde randomize() with { !(address inside {[16'h0000:16'h00FF]}); } ile satır içi olarak belirli bir adres aralığı dışlanır.

Önemli Noktalar

  • := ile :/ farkını karıştırmayın. := ağırlığı her değere ayrı uygularken, :/ ağırlığı aralıktaki değerlere eşit böler. Yanlış operatör beklenmedik dağılımlara yol açar.
  • Implication (->) tek yönlüdür. (A) -> (B), yalnızca A doğruyken B'yi zorlar; A yanlışsa B serbesttir. Çift yönlü ilişki için her iki yönü de yazmak gerekir (kodda IDLE için yapıldığı gibi).
  • Tüm aktif constraint blocları aynı anda sağlanır. Çözücü, bloklar arasında çelişki varsa randomize'ı başarısız kılar; bu yüzden if-else, inside ve implication kurallarının birbiriyle tutarlı olduğundan emin olun.
  • Dağılım istatistiklerini doğrulamak için yeterli örnek üretin. 20 deneme dağılımın genel eğilimini gösterir, ancak küçük örneklemde gerçek yüzdelerden sapma normaldir.
  • inside aralıklarının kümülatif olarak en az bir geçerli değer içermesine dikkat edin; aksi halde diğer kısıtlamalarla birleştiğinde çözümsüzlük oluşabilir.