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-elsekı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ı
ConfigPacketsınıfı,cmd_eadında bir enum (IDLE,READ,WRITE,BURST) vecommand,address,data,length,prio,validrastgele üyelerini içerir.c_addr_insideconstraint'iaddress'i üç ayrı bölgeyle sınırlar: 0x0000–0x0FFF, 0x2000–0x2FFF ve 0xF000–0xFFFF. Bu, çoklu aralıklıinsidekullanımının tipik örneğidir.c_cmd_dist,commandenum'una bir dağılım atar:READveWRITE%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] :/ 60ifadesinde %60'lık ağırlık dört değere eşit bölünür (her biri ~%15).7 := 10ise tek değere %10 verir.c_data_ifelse,commanddeğerine göre koşullu kurallar uygular. ÖrneğinREADdurumundadata == 0velength inside {[1:4]};WRITEdurumundadata != 0velength inside {[1:8]}olur.c_valid_implimplication kullanır:(command != IDLE) -> (valid == 1)ve(command == IDLE) -> (valid == 0). Böylece IDLE dışındaki komutlardavalidher zaman 1, IDLE'da 0 olur.c_burst_addr, BURST komutundaaddress[3:0] == 4'h0zorlayarak hizalı (aligned) adres garantisi verir.c_no_zero_addr,address != 16'h0000ile sıfır adresi tamamen dışlar.moduleiçinde 20 paket üretilir vecmd_countsadlı 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,insideve 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.
insidearalı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.distyalnızca bir tercihtir, garanti değil.command dist { READ := 40, ... }yazmanız, başka bir kısıtREAD'i dışladığında çözücünün yine de geçerli bir komut seçmesine engel olmaz; yüzdeler yalnızca kalan geçerli değerler arasında uygulanır.- Kısıtlar çift yönlüdür:
c_data_ifelseyalnızca "komut READ ise veri 0" demez; çözücüdata'yı önce 0 dışı bir değere karar verdiysecommand'ın READ olamayacağını da çıkarır. Bu yüzdenif-elseve implication'ı prosedürel kod gibi değil, cebirsel denklemler gibi okuyun.
Kısıt Operatörleri Özet Tablosu
| Yapı | Anlamı | Örnek |
|---|---|---|
inside { } |
Küme/aralık üyeliği | addr inside {[0:15], 32, 64}; |
!( inside ) |
Kümenin dışında | !(addr inside {[0:255]}); |
dist { := } |
Her değere ayrı ağırlık | op dist { RD := 3, WR := 1 }; |
dist { :/ } |
Ağırlık aralığa eşit bölünür | len dist { [1:4] :/ 80, [5:8] :/ 20 }; |
-> |
Tek yönlü koşul (implication) | (op == RD) -> (data == 0); |
if ... else |
Koşullu kısıt bloğu | if (op == BURST) { len > 1; } |
<-> |
Çift yönlü eşdeğerlik | (op == IDLE) <-> (valid == 0); |
soft |
Çelişirse sessizce vazgeçilir | soft len == 4; |
solve a before b |
Çözüm sırası (dağılımı etkiler) | solve op before data; |
unique { } |
Elemanlar birbirinden farklı | unique { a, b, c }; |
foreach |
Dizi elemanlarına kısıt | Sonraki ders |
İki pratik ekleme:
<->(eşdeğerlik): Koddakic_valid_implbloğundaki iki implication tek satırda yazılabilir:(command != IDLE) <-> (valid == 1);. Çift yönlü ilişki için bu daha okunaklıdır.softkısıtlar:soft length == 4;yazarsanız çözücü mümkün olduğunda uzunluğu 4 seçer, ama birwith { length == 8; }ile çelişirse hata vermedensoft'u göz ardı eder. Varsayılan davranış tanımlamanın en zarif yoludur; UVM sequence'lerinde çok kullanılır.
Benzetme: Kısıt bloğu, bir restoranda garsona verilen sipariş notu gibidir: "et yemeği olsun (inside), %40 ihtimalle balık (dist), vejetaryen seçildiyse et olmasın (->), mümkünse acılı (soft)". Garson (çözücü) tüm notlara aynı anda uyan bir tabak getirir; birbirine zıt iki not yazarsanız mutfaktan boş döner.
Sık Yapılan Hatalar
:=ile:/'yi karıştırmak:[0:3] := 60yazmak her bir değere 60 ağırlık verir (toplam 240);[0:3] :/ 60ise 4 değere toplam 60 (her biri 15). Dağılım tamamen değişir.- Implication'ı çift yönlü sanmak:
(a) -> (b)ifadesibdoğruykenahakkında hiçbir şey söylemez. Çift yön için<->kullanın. - Çelişen implication'lar:
(cmd == READ) -> (data == 0)iledata != 0sabit kısıtı birlikte varsa READ hiç üretilemez;distyüzdeniz sessizce bozulur. - Kısıt içinde prosedürel ifade kullanmak:
constraint c { if (x > 5) y = 3; }geçersizdir; kısıtlar atama değil eşitlik ifade eder (y == 3). - Yüzdeleri az örnekle doğrulamak: 20 denemede %40'ı tam göremezsiniz; dağılım testlerinde en az birkaç yüz örnek kullanın.
Kendinizi Deneyin
c_valid_implbloğunu(command != IDLE) <-> (valid == 1);tek satırına indirgeyin ve davranışın aynı kaldığını gösterin.c_priority_dist'teki:/'leri:=yapın; 200 paket üretippriodağılımını bir ilişkisel dizide sayın. Yüzdeler nasıl değişti?soft length == 4;kısıtı ekleyin; normal üretimdelength'in hep 4 çıktığını,with { length == 10; }ile çağrıldığında ise hata vermeden 10 olduğunu gösterin.repeat (20)'yirepeat (1000)yapın vecmd_countsyüzdelerinin hedef dağılıma (40/40/15/5) ne kadar yaklaştığını not edin.
Hızlı Kontrol: x dist { [1:4] :/ 80, 5 := 20 } ifadesinde 2 değerinin seçilme olasılığı yaklaşık yüzde kaçtır?
Yaklaşık %20. :/ ile 80 ağırlık dört değere eşit bölünür (her biri 20); 5 değeri ise := ile tek başına 20 alır. Toplam 100 ağırlık içinde 2'nin payı 20/100'dür.