constraint_mode
Gün 3: Rastgele Üretim ve Kısıtlamalar | Çalışma zamanında kısıtlamaları açma/kapatma
Bu derste, çalışma zamanında (runtime) bir constraint bloğunu açıp kapatmaya yarayan constraint_mode() metodunu öğreneceğiz. Bu yetenek, aynı sınıfı farklı test senaryolarına yeniden kullanmanın en güçlü yollarından biridir.
constraint_mode() Nedir?
Bir sınıfta birden çok constraint bloğu tanımlandığında, normalde hepsi aktiftir ve randomize() çağrısında hepsi birden sağlanmaya çalışılır. constraint_mode() metodu, bir constraint bloğunu çalışma zamanında etkin (1) veya devre dışı (0) hale getirmemizi sağlar.
İki kullanım biçimi vardır:
- Belirli bir bloğu kontrol etmek:
nesne.constraint_adi.constraint_mode(0)o bloğu kapatır,(1)açar. - Tüm blokları kontrol etmek:
nesne.constraint_mode(0)sınıftaki tüm kısıtlamaları kapatır (tam serbest rastgele üretim),(1)hepsini açar.
Ayrıca argümansız çağrılırsa (nesne.constraint_adi.constraint_mode()) o bloğun mevcut durumunu (0/1) döndürür, yani durumu sorgulamak için de kullanılır.
Neden Kullanılır?
- Aynı transaction sınıfını farklı senaryolarda (düşük adres / yüksek adres, okuma / yazma, normal / hata) tekrar kullanmak.
- Birbiriyle çakışan kısıtlama bloklarını bilinçli olarak tanımlayıp, her seferinde yalnızca uygun olanı açmak.
- Hata enjeksiyonu (error injection) gibi özel modları gerektiğinde devreye almak.
Çakışan Kısıtlamaları Yönetmek
Bu desende sıkça, mantıken birbiriyle çelişen blocklar (örneğin "düşük adres" ve "yüksek adres") aynı sınıfta tanımlanır. Çakışmayı önlemek için kurucuda (new()) bunların bir kısmı baştan kapatılır; ardından test akışında uygun olan açılıp diğeri kapatılır.
Kaynak Kod
// =============================================================================
// GUN 3 - Konu 6: Calisma Zamaninda Kisitlamalari Acma/Kapatma (constraint_mode)
// =============================================================================
class FlexPacket;
rand bit [15:0] address;
rand bit [31:0] data;
rand bit [2:0] burst_len;
rand bit write_en;
rand bit [1:0] resp;
// Adres araligi kisitlamasi
constraint c_addr_low {
address inside {[16'h0000:16'h00FF]};
}
constraint c_addr_high {
address inside {[16'hFF00:16'hFFFF]};
}
// Burst kisitlamasi
constraint c_burst_aligned {
(burst_len > 0) -> (address[1:0] == 2'b00); // Hizali
}
// Yazma/okuma kisitlamasi
constraint c_write_mode {
write_en == 1;
}
constraint c_read_mode {
write_en == 0;
data == 0;
}
// Hata yaniti kisitlamasi
constraint c_no_error {
resp == 0; // OKAY
}
constraint c_force_error {
resp inside {2'b10, 2'b11}; // SLVERR veya DECERR
}
function new();
// Baslangicta cakisan kisitlamalari kapat
c_addr_high.constraint_mode(0);
c_read_mode.constraint_mode(0);
c_force_error.constraint_mode(0);
endfunction
function void display();
$display(" Addr=0x%04h | Data=0x%08h | Burst=%0d | W/R=%s | Resp=%0b",
address, data, burst_len, write_en ? "WRITE" : "READ", resp);
endfunction
endclass
module constraint_mode_ornek;
initial begin
FlexPacket pkt;
$display("=== constraint_mode Kullanimi ===\n");
pkt = new();
// --- Senaryo 1: Dusuk adres, yazma ---
$display("--- Senaryo 1: Dusuk Adres Yazma ---");
repeat (3) begin
assert(pkt.randomize()) else $fatal(1, "Hata!");
pkt.display();
end
// --- Senaryo 2: Yuksek adres bolgesine gec ---
$display("\n--- Senaryo 2: Yuksek Adres Bolgesi ---");
pkt.c_addr_low.constraint_mode(0); // Dusuk adresi kapat
pkt.c_addr_high.constraint_mode(1); // Yuksek adresi ac
repeat (3) begin
assert(pkt.randomize()) else $fatal(1, "Hata!");
pkt.display();
end
// --- Senaryo 3: Okuma modu ---
$display("\n--- Senaryo 3: Okuma Modu ---");
pkt.c_write_mode.constraint_mode(0); // Yazma kapat
pkt.c_read_mode.constraint_mode(1); // Okuma ac
repeat (3) begin
assert(pkt.randomize()) else $fatal(1, "Hata!");
pkt.display();
end
// --- Senaryo 4: Hata enjeksiyonu ---
$display("\n--- Senaryo 4: Hata Yaniti ---");
pkt.c_read_mode.constraint_mode(0);
pkt.c_write_mode.constraint_mode(1);
pkt.c_no_error.constraint_mode(0); // Normal yaniti kapat
pkt.c_force_error.constraint_mode(1); // Hata yanitini ac
repeat (3) begin
assert(pkt.randomize()) else $fatal(1, "Hata!");
pkt.display();
end
// --- Senaryo 5: Tum kisitlamalari kapat (tam rastgele) ---
$display("\n--- Senaryo 5: Serbest (Tum constraint kapali) ---");
pkt.constraint_mode(0); // TUM kisitlamalari kapat
repeat (3) begin
assert(pkt.randomize()) else $fatal(1, "Hata!");
pkt.display();
end
// --- Durumu sorgula ---
$display("\n--- Constraint Durumlari ---");
pkt.constraint_mode(1); // Hepsini ac
pkt.c_addr_high.constraint_mode(0);
pkt.c_read_mode.constraint_mode(0);
pkt.c_force_error.constraint_mode(0);
$display(" c_addr_low = %0d", pkt.c_addr_low.constraint_mode());
$display(" c_addr_high = %0d", pkt.c_addr_high.constraint_mode());
$display(" c_write_mode = %0d", pkt.c_write_mode.constraint_mode());
$display(" c_read_mode = %0d", pkt.c_read_mode.constraint_mode());
$display("\n=== constraint_mode Sonu ===");
$finish;
end
endmodule
Kodun Açıklaması
FlexPacketsınıfı, bilinçli olarak çakışan çiftler içeren çok sayıda constraint bloğu tanımlar:c_addr_low/c_addr_high(düşük/yüksek adres),c_write_mode/c_read_mode(yazma/okuma),c_no_error/c_force_error(normal/hata yanıtı).new()kurucusunda çakışmayı önlemek içinc_addr_high,c_read_modevec_force_errorbaştanconstraint_mode(0)ile kapatılır. Böylece nesne, başlangıçta düşük adres + yazma + normal yanıt üretecek şekilde tutarlı bir başlangıç durumundadır.- Senaryo 1 varsayılan durumu kullanır: düşük adres aralığı ve yazma modu.
- Senaryo 2 adres bölgesini değiştirir:
pkt.c_addr_low.constraint_mode(0)ile düşük adres kapatılır,pkt.c_addr_high.constraint_mode(1)ile yüksek adres açılır. Artık adresler 0xFF00–0xFFFF aralığından gelir. - Senaryo 3 okuma moduna geçer:
c_write_modekapatılıpc_read_modeaçılır; bu blokwrite_en == 0vedata == 0zorlar. - Senaryo 4 hata enjeksiyonu yapar:
c_no_errorkapatılıpc_force_erroraçılır;respdeğeri SLVERR (2'b10) veya DECERR (2'b11) olur. - Senaryo 5
pkt.constraint_mode(0)ile tüm kısıtlamaları kapatır — bu, tamamen serbest rastgele üretim demektir; her alan kendi tam bit aralığında değer alabilir. - Son bölümde
pkt.constraint_mode(1)ile hepsi tekrar açılır, sonra çakışan bloklar yeniden kapatılır veconstraint_mode()argümansız çağrılarak her bloğun mevcut durumu (c_addr_low,c_addr_high,c_write_mode,c_read_mode) sorgulanıp yazdırılır.
Önemli Noktalar
- Çakışan kısıtlamaları aynı anda açık bırakmayın. Örneğin hem
c_addr_lowhemc_addr_highaçıkken çözüm bulunamaz verandomize()başarısız olur; her zaman yalnızca uygun olanı aktif tutun. - Kurucuda güvenli bir başlangıç durumu kurun. Burada olduğu gibi çakışan blocların bir kısmını
new()içinde kapatmak, nesnenin daima tutarlı bir durumda doğmasını sağlar. constraint_mode(0)tüm blokları kapatır. Belirli bir blok yerine nesnenin kendisi üzerinden çağrıldığında etki sınıf geneline yayılır; bunu tam serbest üretim için bilinçli kullanın.- Durum sorgulamak için argümansız çağırın.
constraint_adi.constraint_mode()mevcut etkinlik durumunu döndürür; karmaşık senaryolarda hangi blokların açık olduğunu doğrulamak için faydalıdır. - Senaryolar arası geçişlerde modu değiştirdikten sonra, eski senaryonun açtığı blokları tekrar kapatmayı unutmayın; aksi halde sonraki randomize'lar beklenmedik kısıtlamalarla karşılaşır.
constraint_mode()nesneye özgüdür:pkt.c_addr_low.constraint_mode(0)yalnızcapktnesnesini etkiler; aynı sınıftan yaratılan başka bir nesne varsayılan (hepsi açık) durumda doğar. Kurucuda yapılan kapatmalar bu yüzden her nesne için ayrı ayrı çalışır.- Senaryo 5'te tüm kısıtlar kapalıyken
burst_lenveaddressarasındaki hizalama kuralı da kalkar; çıktıda hizasız adresler görmeniz normaldir. "Serbest mod", geçersiz stimulus üretmenin de bir yoludur ve DUT'un hata yakalama mantığını test etmek için bilinçli kullanılır.
constraint_mode ve rand_mode: İki Kardeş Metot
constraint_mode() |
rand_mode() |
|
|---|---|---|
| Neyi kontrol eder? | Bir kısıt bloğunu | Bir rand değişkenini |
| Kapalıyken ne olur? | Blok çözücü tarafından yok sayılır | Değişken rastgele değişmez; sabit (state) gibi davranır |
| Nesne üzerinden toplu çağrı | obj.constraint_mode(0) → tüm bloklar |
obj.rand_mode(0) → tüm rand alanlar |
| Durum sorgusu | obj.c_x.constraint_mode() → 0/1 |
obj.alan.rand_mode() → 0/1 |
| Tipik kullanım | Senaryo değiştirmek, çakışan kuralları yönetmek | Bir alanı elle sabitleyip diğerlerini rastgele bırakmak |
Örneğin "adres hep 0x0010 olsun, gerisi rastgele" için iki yol vardır: pkt.randomize() with { address == 16'h0010; } ya da pkt.address = 16'h0010; pkt.address.rand_mode(0); pkt.randomize();. İkincisi, değerin birçok çağrı boyunca sabit kalması istendiğinde daha temizdir.
Benzetme: Kısıt blokları bir ses masasındaki kanal düğmeleridir.
constraint_mode(0)kanalı mute eder; müzik (kısıt) oradadır ama duyulmaz.rand_mode(0)ise enstrümanın kendisini sabit bir notaya kilitler. Nesne üzerinden çağrı, tüm masayı tek seferde susturur ya da açar.
Sık Yapılan Hatalar
- Çakışan iki bloğu birden açık bırakmak:
c_addr_lowvec_addr_highbirlikte açıksa çözüm yoktur;randomize()0döner. Test akışında her geçişte "önce kapat, sonra aç" disiplinini uygulayın. - Kurucuda kapatmayı unutmak: Sınıfa yeni bir çakışan blok eklediğinizde
new()içindeki kapatma listesini güncellemezseniz nesne doğduğu anda çözümsüzdür. constraint_mode(0)'ı tek bloğa uyguladığınızı sanmak:pkt.constraint_mode(0)tüm blokları kapatır; belirli blok içinpkt.c_x.constraint_mode(0)yazılmalıdır.- Satır içi
withkısıtınınconstraint_mode'dan etkileneceğini düşünmek:with { }kısıtları her zaman aktiftir;constraint_modeyalnızca sınıf içindeki adlandırılmış blokları etkiler. - Kapanmayan geçiş: Senaryo 3'te okumaya geçip Senaryo 4'te yazmaya dönerken
c_read_mode'u kapatmayı unutursanızwrite_en == 0vewrite_en == 1çelişir.
Kendinizi Deneyin
- Senaryo 2'de yalnızca
c_addr_high.constraint_mode(1)yapıpc_addr_low'u açık bırakın.randomize()ne döndürdü?$fataltetiklendi mi? FlexPacket'ten ikinci bir nesne (pkt2 = new()) yaratın vepkt'nin kısıt modlarını değiştirdikten sonrapkt2.c_addr_high.constraint_mode()sorgulayın. Sonuç ne? Neden?rand_modekullanarakdata'yı32'hDEAD_BEEF'e sabitleyip 3 paket üretin; ardındanpkt.data.rand_mode(1)ile serbest bırakın.- Her senaryoyu ayrı bir
taskiçine alın ve task sonunda kısıt modlarını varsayılan duruma döndürün (Lab 3'teki yaklaşım). Bu düzenleme hangi hataları önler?
Hızlı Kontrol: pkt.constraint_mode(0) ile pkt.c_addr_low.constraint_mode(0) arasındaki fark nedir?
İlki nesnenin tüm kısıt bloklarını kapatır (tam serbest üretim), ikincisi yalnızca c_addr_low bloğunu kapatır. Argümansız çağrı (pkt.c_addr_low.constraint_mode()) ise durumu değiştirmez, yalnızca mevcut durumu (0/1) döndürür.