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 daheader[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ı
ArrayPacketsınıfındapayloaddinamik dizisi,lengthve dört elemanlı sabit boyutluheaderdizisi tanımlıdır.c_lengthconstraint'ilength'i 4–16 arasına sınırlar vepayload.size() == lengthile dizinin uzunluğunu bu rastgele değere eşitler.c_payloadiçindekiforeach (payload[i])her elemanı[8'h01:8'hFE]aralığına koyar (0x00 ve 0xFF hariç) veif (i > 0)koşuluylapayload[i] != payload[i-1]diyerek ardışık elemanların farklı olmasını sağlar.c_header_ordered,headerdizisini kesin artan yapar (header[i] > header[i-1]) veheader[0] >= 1ile başlangıcı sabitler.
UniqueArraysınıfındadatasekiz elemanlı bir dizidir.c_uniqueconstraint'iunique {data}ile tüm elemanları benzersiz kılar veforeachile her elemanı[1:100]aralığına yerleştirir.Matrixsınıfındagrid, 4×4'lük iki boyutlu bir dizidir.c_row_sum, her satır içingrid[i].sum() with (int'(item)) < 20der;int'(item)cast'i toplama sırasında taşmayı önler.c_diagonal,grid[i][i] > 0ile köşegen elemanlarının sıfır olmamasını garanti eder.
moduleiçindeArrayPacket,UniqueArrayveMatrixnesneleri ayrı ayrırandomize()edilipassert(...) else $fatal(...)ile doğrulanır vedisplay()ile yazdırılır.
Önemli Noktalar
foreachconstraint'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() == lengthgibi 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 dizilerdesum() 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.headeriçin 4-bit (1..15) aralığında 4 artan değer rahatça bulunur;header[8]yapsaydınız da çözüm olurdu, amabit [1:0] header[4](yalnızca 0–3) ileheader[0] >= 1asla çözülemez. foreach (payload[i])içindekiiindeksi bir sabit gibi davranır;payload[i-1]ifadesindei = 0iken indeks-1olur.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; ancakpayload.size()'ı kısıtlamayı unutursanız boyut 0 bile olabilir veforeachhiç ç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:
foreachkı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ırsa15 + 1 = 0olur;< 20kısıtı anlamsızlaşır ve matris rastgele görünür. Daimawith (int'(item)).- Boyutu kısıtlamadan eleman kısıtlamak: Çözücü
size()'ı 0 seçebilir; tümforeachkısıtları boş kümede "sağlanmış" sayılır ve dizi boş çıkar. uniqueile 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])ileforeach (grid[i, j])farkı: İlki yalnızca satırları, ikincisi her hücreyi gezer.grid[i][i] > 0için satır düzeyi yeterlidir ama hücre kısıtı için[i, j]gerekir. foreachdeğişkenini kısıt dışında kullanmak:iyalnızcaforeachbloğunun içinde tanımlıdır.
Kendinizi Deneyin
c_row_sumkısıtındanwith (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?ArrayPacket'epayload.sum() with (int'(item == 8'h55)) == 2;kısıtı ekleyin: payload'da tam iki adet0x55olmalı. Çalıştırıp doğrulayın.UniqueArrayaralığını[1:7]yapın (8 eleman için 7 değer).randomize()ne döndürdü?Matrix'e "her sütunun toplamı da 20'den küçük olsun" kısıtı ekleyin (ipucu: sütunlar içinforeach (grid[i])yeterli değil; iç döngüyü ellegrid[0][j] + grid[1][j] + ...ya da ikinci birforeachile 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.