EDA Playground'da Dene

Döngüsel Kısıtlamalar

Gün 3: Rastgele Üretim ve Kısıtlamalar | foreach ile dizi kısıtlamaları, unique, matris kısıtlamaları

Bu derste diziler (array) üzerindeki kısıtlamaları öğreneceğiz: foreach ile her elemana kural uygulama, dinamik dizi boyutunu kısıtlama, unique ile benzersizlik garantisi ve çok boyutlu (matris) dizilerde toplam (sum()) tabanlı kısıtlamalar.

Döngüsel (Iterative) Kısıtlamalar

Bir diziyi rastgeleleştirirken, çoğu zaman her elemana ya da elemanlar arası ilişkilere kural koymak isteriz. Bunu foreach ile yaparız. foreach bir constraint bloğu içinde kullanıldığında, döngü değişkeni (örneğin i) tüm indeksler üzerinde gezer ve her indeks için ayrı bir kısıtlama üretilmiş gibi davranır.

Bu yapı şunları mümkün kılar:

  • Eleman bazlı kurallar: her payload[i] belirli bir aralıkta olsun.
  • Komşu eleman ilişkileri: payload[i] != payload[i-1] gibi ardışık farklılık ya da header[i] > header[i-1] gibi sıralama.
  • Boyut kısıtlamaları: dinamik dizinin size() değeri başka bir rastgele alana bağlanabilir.

unique

unique { ... } kısıtlaması, küme içindeki tüm elemanların birbirinden farklı olmasını garanti eder. Bu, çakışmasız adresler, benzersiz ID'ler veya tekrar etmeyen veri kümeleri üretmek için idealdir.

sum() ve Genişlik (Width) Tuzağı

Bir dizinin elemanlarının toplamını kısıtlamak için dizi.sum() kullanılır. Ancak sum(), varsayılan olarak sonucu eleman tipinin bit genişliğiyle hesaplar; bu, dar bitli elemanlarda taşmaya (overflow) yol açabilir. Çözüm, sum() with (int'(item)) yazarak her elemanı toplamaya katılmadan önce int'e dönüştürmektir.

Kaynak Kod

// =============================================================================
// GUN 3 - Konu 4: Dongusel Kisitlamalar (Iterative Constraints - foreach)
// =============================================================================

class ArrayPacket;
  rand bit [7:0] payload [];      // Dinamik dizi
  rand int       length;
  rand bit [3:0] header [4];      // Sabit boyutlu dizi

  // Dizi boyutu kisitlamasi
  constraint c_length {
    length inside {[4:16]};
    payload.size() == length;
  }

  // foreach ile eleman kisitlamalari
  constraint c_payload {
    foreach (payload[i]) {
      payload[i] inside {[8'h01:8'hFE]};  // 0x00 ve 0xFF haric
      if (i > 0)
        payload[i] != payload[i-1];        // Ardisik elemanlar farkli
    }
  }

  // Header: artan sira
  constraint c_header_ordered {
    foreach (header[i]) {
      if (i > 0)
        header[i] > header[i-1];           // Kesin artan
    }
    header[0] >= 1;
  }

  function void display();
    $display("  Length=%0d", length);
    $write("  Header: ");
    foreach (header[i]) $write("%0d ", header[i]);
    $display();
    $write("  Payload: ");
    foreach (payload[i]) $write("%02h ", payload[i]);
    $display();
  endfunction
endclass

class UniqueArray;
  rand int data [8];

  // Tum elemanlar benzersiz olmali
  constraint c_unique {
    unique {data};
    foreach (data[i])
      data[i] inside {[1:100]};
  }

  function void display();
    $write("  Unique data: ");
    foreach (data[i]) $write("%0d ", data[i]);
    $display();
  endfunction
endclass

class Matrix;
  rand bit [3:0] grid [4][4];

  // Satir toplami 20'den kucuk olsun.
  // DIKKAT: sum() sonucu normalde eleman genisliginde (4 bit) hesaplanir ve
  // tasar (overflow). with (int'(item)) ile her eleman toplama int olarak katilir.
  constraint c_row_sum {
    foreach (grid[i]) {
      grid[i].sum() with (int'(item)) < 20;
    }
  }

  // Kosegen elemanlari 0 olmasin
  constraint c_diagonal {
    foreach (grid[i]) {
      grid[i][i] > 0;
    }
  }

  function void display();
    $display("  4x4 Matrix:");
    foreach (grid[i]) begin
      $write("    [");
      foreach (grid[i][j])
        $write("%2d ", grid[i][j]);
      // Ayni genisleme burada da gerekli; yoksa yazdirilan toplam da tasar
      $display("] sum=%0d", grid[i].sum() with (int'(item)));
    end
  endfunction
endclass

module iterative_constraints;
  initial begin
    ArrayPacket apkt;
    UniqueArray uarr;
    Matrix      mat;

    $display("=== Dongusel Kisitlamalar (foreach) ===\n");

    // --- Dizi kisitlamalari ---
    $display("--- ArrayPacket ---");
    apkt = new();
    repeat (3) begin
      assert(apkt.randomize()) else $fatal(1, "Randomize hata!");
      apkt.display();
      $display();
    end

    // --- Benzersiz elemanlar ---
    $display("--- UniqueArray ---");
    uarr = new();
    repeat (3) begin
      assert(uarr.randomize()) else $fatal(1, "Randomize hata!");
      uarr.display();
    end

    // --- Matris kisitlamalari ---
    $display("\n--- Matrix ---");
    mat = new();
    repeat (2) begin
      assert(mat.randomize()) else $fatal(1, "Randomize hata!");
      mat.display();
      $display();
    end

    $display("=== Dongusel Kisitlamalar Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • ArrayPacket sınıfında payload dinamik dizisi, length ve dört elemanlı sabit boyutlu header dizisi tanımlıdır.
    • c_length constraint'i length'i 4–16 arasına sınırlar ve payload.size() == length ile dizinin uzunluğunu bu rastgele değere eşitler.
    • c_payload içindeki foreach (payload[i]) her elemanı [8'h01:8'hFE] aralığına koyar (0x00 ve 0xFF hariç) ve if (i > 0) koşuluyla payload[i] != payload[i-1] diyerek ardışık elemanların farklı olmasını sağlar.
    • c_header_ordered, header dizisini kesin artan yapar (header[i] > header[i-1]) ve header[0] >= 1 ile başlangıcı sabitler.
  • UniqueArray sınıfında data sekiz elemanlı bir dizidir. c_unique constraint'i unique {data} ile tüm elemanları benzersiz kılar ve foreach ile her elemanı [1:100] aralığına yerleştirir.
  • Matrix sınıfında grid, 4×4'lük iki boyutlu bir dizidir.
    • c_row_sum, her satır için grid[i].sum() with (int'(item)) < 20 der; int'(item) cast'i toplama sırasında taşmayı önler.
    • c_diagonal, grid[i][i] > 0 ile köşegen elemanlarının sıfır olmamasını garanti eder.
  • module içinde ArrayPacket, UniqueArray ve Matrix nesneleri ayrı ayrı randomize() edilip assert(...) else $fatal(...) ile doğrulanır ve display() ile yazdırılır.

Önemli Noktalar

  • foreach constraint'i her indeks için ayrı bir kural üretir. İndeks değişkeni (i) yalnızca constraint bağlamında geçerlidir ve komşu elemanlara (i-1) erişerek ilişkiler kurmanızı sağlar.
  • Dinamik dizilerde önce boyutu kısıtlayın. payload.size() == length gibi bir kural olmadan çözücü dizi boyutunu serbestçe seçer; eleman kısıtlamalarının anlamlı olması için boyutun belirlenmesi önemlidir.
  • sum() taşmasına dikkat edin. Dar bitli dizilerde sum() with (int'(item)) kullanmazsanız toplam, eleman genişliğinde hesaplanır ve beklenmedik şekilde taşar — kod yorumlarında da bu özellikle vurgulanmıştır.
  • unique, çözücü için maliyetli olabilir. Büyük dizilerde veya dar değer aralıklarında benzersizlik sağlamak çözüm uzayını daraltır; aralığın eleman sayısından yeterince geniş olduğundan emin olun (örneğin 8 eleman için [1:100]).
  • Kesin artan (>) gibi sıkı sıralama kısıtlamalarında, değer aralığının eleman sayısını karşılayacak kadar geniş olması gerekir; aksi halde randomize başarısız olur. header için 4-bit (1..15) aralığında 4 artan değer rahatça bulunur; header[8] yapsaydınız da çözüm olurdu, ama bit [1:0] header[4] (yalnızca 0–3) ile header[0] >= 1 asla çözülemez.
  • foreach (payload[i]) içindeki i indeksi bir sabit gibi davranır; payload[i-1] ifadesinde i = 0 iken indeks -1 olur. if (i > 0) koruması tam da bu yüzden vardır; onu silerseniz çözücü tanımsız bir eleman referansıyla karşılaşır.
  • Dinamik dizi boyutu kısıtlanırken çözücü boyutu önce belirler, sonra elemanları çözer. Bu yüzden payload.size() ile elemanlar arası kısıtların aynı blokta olması sorun yaratmaz; ancak payload.size()'ı kısıtlamayı unutursanız boyut 0 bile olabilir ve foreach hiç çalışmaz.

Dizi Kısıtlarının Anatomisi

İhtiyaç Kısıt kalıbı
Boyutu sabitle arr.size() == 8; veya arr.size() inside {[4:16]};
Her eleman bir aralıkta foreach (arr[i]) arr[i] inside {[1:254]};
Komşular farklı foreach (arr[i]) if (i > 0) arr[i] != arr[i-1];
Artan sıra foreach (arr[i]) if (i > 0) arr[i] > arr[i-1];
Hepsi benzersiz unique { arr };
Toplam sınırı arr.sum() with (int'(item)) <= 100;
Belirli değerden kaç tane? arr.sum() with (int'(item == 0)) == 2; (tam iki sıfır)
İlk eleman özel arr[0] == 8'hA5;
İki boyutlu foreach (grid[i, j]) grid[i][j] < 10;

Tablodaki "kaç tane" kalıbı özellikle güçlüdür: item == 0 ifadesi 1-bit sonuç verir, int'() ile tam sayıya çevrilip toplandığında koşulu sağlayan eleman sayısını verir. "Pakette tam 3 hata baytı olsun" gibi kurallar bu tek satırla yazılır.

Benzetme: foreach kısıtı, bir sınıftaki her öğrenciye aynı kuralı okumak gibidir: "herkesin notu 50'nin üstünde olsun ve kimse yanındakiyle aynı notu almasın". Kuralı bir kez yazarsınız, çözücü onu her sıraya ayrı ayrı uygular.

Sık Yapılan Hatalar

  • sum() taşması: bit [3:0] elemanların toplamı 4-bit'te hesaplanırsa 15 + 1 = 0 olur; < 20 kısıtı anlamsızlaşır ve matris rastgele görünür. Daima with (int'(item)).
  • Boyutu kısıtlamadan eleman kısıtlamak: Çözücü size()'ı 0 seçebilir; tüm foreach kısıtları boş kümede "sağlanmış" sayılır ve dizi boş çıkar.
  • unique ile dar aralık: 8 benzersiz eleman için [1:5] aralığı matematiksel olarak imkânsızdır; randomize() başarısız olur.
  • İç içe dizilerde foreach (grid[i]) ile foreach (grid[i, j]) farkı: İlki yalnızca satırları, ikincisi her hücreyi gezer. grid[i][i] > 0 için satır düzeyi yeterlidir ama hücre kısıtı için [i, j] gerekir.
  • foreach değişkenini kısıt dışında kullanmak: i yalnızca foreach bloğunun içinde tanımlıdır.

Kendinizi Deneyin

  1. c_row_sum kısıtından with (int'(item)) kısmını silin ve çıktıdaki satır toplamlarına bakın: 20'nin üstünde satırlar görüyor musunuz? Neden?
  2. ArrayPacket'e payload.sum() with (int'(item == 8'h55)) == 2; kısıtı ekleyin: payload'da tam iki adet 0x55 olmalı. Çalıştırıp doğrulayın.
  3. UniqueArray aralığını [1:7] yapın (8 eleman için 7 değer). randomize() ne döndürdü?
  4. Matrix'e "her sütunun toplamı da 20'den küçük olsun" kısıtı ekleyin (ipucu: sütunlar için foreach (grid[i]) yeterli değil; iç döngüyü elle grid[0][j] + grid[1][j] + ... ya da ikinci bir foreach ile yazın).
Hızlı Kontrol: bit [7:0] arr[4] dizisinde arr.sum() < 300 kısıtı neden beklendiği gibi çalışmaz?

sum() eleman genişliğinde (8 bit) hesaplanır; sonuç en fazla 255 olabilir ve 300'ü asla geçmez; kısıt her zaman sağlanır, yani etkisizdir. Toplamın 8 bitte taşması da gerçek değeri gizler. Doğrusu arr.sum() with (int'(item)) < 300 yazmaktır.