Lab 3: Generator Sınıfı
Gün 3: Rastgele Üretim ve Kısıtlamalar | Senaryoya uygun rastgele veri üreten Generator tasarımı
Bu lab çalışmasında, Gün 3 boyunca öğrendiğimiz tüm constrained-random tekniklerini tek bir uygulamada birleştiriyoruz: kısıtlamalı bir transaction sınıfı ve onu farklı senaryolarda sürebilen bir Generator sınıfı tasarlayacağız. Bu yapı, gerçek doğrulama ortamlarının (testbench) çekirdeğini oluşturur.
Generator Deseni
Bir Generator (üretici), test sırasında tasarıma uygulanacak rastgele transaction'ları üreten bileşendir. Tipik olarak:
- İçinde bir veya daha fazla transaction nesnesi tutar.
- Belirli sayıda transaction'ı, belirli bir senaryoya göre rastgeleleştirir.
- Senaryolar arasında geçiş yaparken
constraint_mode()ve satır içiwithkısıtlamalarını kullanır.
Bu derste birleştirdiğimiz kavramlar:
rand/randcdeğişkenler ve enum tipleri.distile gerçekçi komut ve hata dağılımları.- implication (
->) ile komut tipine bağlı kurallar. constraint_mode()ile senaryoya özel kısıtlama açma/kapatma.post_randomize()ile türetilmiş alan hesaplama ve hata enjeksiyonu.
Senaryo Tabanlı Doğrulama
Aynı transaction sınıfını birden çok senaryoda yeniden kullanmak, doğrulama ortamlarında temel bir verimlilik kaynağıdır. Generator, her senaryo için ayrı bir task tanımlayarak (normal trafik, sadece okuma, yüksek adres, hata enjeksiyonu) tek bir veri modelinden çok çeşitli test durumları üretir.
Adım Adım Görev Listesi
Çözümü okumadan önce bu adımları kendiniz tamamlamayı deneyin:
- Transaction:
cmd_evestatus_eenum'larını,randalanları (command,address,data,burst_length,byte_strobe,id,status) vecrc,txn_id,static countalanlarını içerenBusTransactionsınıfını yazın. - Temel kısıtlar:
burst_lengthiçin implication kuralları (tekil komutlarda 1, burst komutlarında 2–16) vec_valid(hizalı adres,status == VALID,byte_strobe != 0). - Adres bölgeleri: Üç ayrık bölge kısıtı yazın; kurucuda ikisini kapatın.
- Dağılımlar: Komut ve hata durumu için
distkısıtları; hata dağılımı kurucuda kapalı olsun. post_randomize():crchesaplayın vestatus'a göre adres/veriyi bozun.- Generator: Dört senaryo
task'ı yazın; her biri değiştirdiği kısıt modunu sonunda geri alsın. - Çalıştırın: Her senaryoda 8 transaction üretip çıktıyı inceleyin.
Kontrol noktası: Hata enjeksiyonu senaryosunda
ADDR_ERRORsatırlarının adresi...3ile bitmeli (hizalama bozuldu),DATA_ERRORsatırlarının verisi ise tersine çevrilmiş olmalı.
Kaynak Kod
// =============================================================================
// GUN 3 - Lab 3: Generator Sinifi - Senaryoya Uygun Rastgele Veri Uretimi
// =============================================================================
class BusTransaction;
typedef enum logic [1:0] {READ=0, WRITE=1, BURST_READ=2, BURST_WRITE=3} cmd_e;
typedef enum {VALID, ADDR_ERROR, DATA_ERROR, PROTOCOL_ERROR} status_e;
rand cmd_e command;
rand logic [31:0] address;
rand logic [31:0] data;
rand int unsigned burst_length;
rand bit [3:0] byte_strobe;
rand bit [2:0] id;
rand status_e status;
bit [31:0] crc;
int txn_id;
static int count = 0;
// --- Temel kisitlamalar ---
constraint c_base {
burst_length inside {[1:16]};
(command inside {READ, WRITE}) -> burst_length == 1;
(command inside {BURST_READ, BURST_WRITE}) -> burst_length inside {[2:16]};
}
// --- Gecerli paket ---
constraint c_valid {
status == VALID;
address[1:0] == 2'b00; // 4-byte hizali
byte_strobe != 0;
}
// --- Adres araliklari ---
constraint c_addr_region_A {
address inside {[32'h0000_0000:32'h0000_FFFF]};
}
constraint c_addr_region_B {
address inside {[32'h1000_0000:32'h1000_FFFF]};
}
constraint c_addr_region_C {
address inside {[32'hFFFF_0000:32'hFFFF_FFFF]};
}
// --- Komut dagilimi ---
constraint c_cmd_dist {
command dist { READ := 30, WRITE := 30, BURST_READ := 20, BURST_WRITE := 20 };
}
// --- Hata enjeksiyonu ---
constraint c_inject_error {
status dist { VALID := 80, ADDR_ERROR := 10, DATA_ERROR := 5, PROTOCOL_ERROR := 5 };
}
function new();
txn_id = count++;
c_addr_region_B.constraint_mode(0);
c_addr_region_C.constraint_mode(0);
c_inject_error.constraint_mode(0);
endfunction
function void post_randomize();
crc = address ^ data ^ {29'h0, command};
if (status == ADDR_ERROR)
address[1:0] = 2'b11; // Hizalamayi boz
else if (status == DATA_ERROR)
data = ~data;
endfunction
function void display();
$display(" TXN#%03d | %-11s | Addr=0x%08h | Data=0x%08h | Burst=%2d | BE=%04b | ID=%0d | %s",
txn_id, command.name(), address, data, burst_length,
byte_strobe, id, status.name());
endfunction
endclass
class Generator;
BusTransaction txn;
int num_transactions;
string scenario_name;
function new(int num = 10);
this.num_transactions = num;
this.txn = new();
endfunction
// Senaryo 1: Normal trafik
task run_normal_traffic();
scenario_name = "Normal Trafik";
$display("\n=== Senaryo: %s (%0d txn) ===", scenario_name, num_transactions);
repeat (num_transactions) begin
assert(txn.randomize()) else $fatal(1, "Randomize hata!");
txn.display();
end
endtask
// Senaryo 2: Sadece okuma
task run_read_only();
scenario_name = "Sadece Okuma";
$display("\n=== Senaryo: %s ===", scenario_name);
repeat (num_transactions) begin
assert(txn.randomize() with { command inside {BusTransaction::READ, BusTransaction::BURST_READ}; })
else $fatal(1, "Randomize hata!");
txn.display();
end
endtask
// Senaryo 3: Yuksek adres bolgesi
task run_high_address();
scenario_name = "Yuksek Adres Bolgesi";
$display("\n=== Senaryo: %s ===", scenario_name);
txn.c_addr_region_A.constraint_mode(0);
txn.c_addr_region_C.constraint_mode(1);
repeat (num_transactions) begin
assert(txn.randomize()) else $fatal(1, "Randomize hata!");
txn.display();
end
txn.c_addr_region_A.constraint_mode(1);
txn.c_addr_region_C.constraint_mode(0);
endtask
// Senaryo 4: Hata enjeksiyonu
task run_error_injection();
scenario_name = "Hata Enjeksiyonu";
$display("\n=== Senaryo: %s ===", scenario_name);
txn.c_valid.constraint_mode(0);
txn.c_inject_error.constraint_mode(1);
repeat (num_transactions) begin
assert(txn.randomize()) else $fatal(1, "Randomize hata!");
txn.display();
end
txn.c_valid.constraint_mode(1);
txn.c_inject_error.constraint_mode(0);
endtask
endclass
module lab3_generator;
initial begin
Generator gen;
$display("============================================================");
$display(" LAB 3: Generator Sinifi - Senaryolu Rastgele Uretim");
$display("============================================================");
gen = new(8);
gen.run_normal_traffic();
gen.run_read_only();
gen.run_high_address();
gen.run_error_injection();
$display("\n============================================================");
$display(" LAB 3 TAMAMLANDI");
$display("============================================================");
$finish;
end
endmodule
Kodun Açıklaması
BusTransactionsınıfı iki enum kullanır: komut tipi içincmd_e(READ,WRITE,BURST_READ,BURST_WRITE) ve durum içinstatus_e(VALID,ADDR_ERROR,DATA_ERROR,PROTOCOL_ERROR). Rastgele alanlar arasındacommand,address,data,burst_length,byte_strobe,idvestatusyer alır.- Kısıtlamalar:
c_base,burst_length'i 1–16 arasına koyar ve implication ile tekil komutlarda (READ,WRITE) uzunluğu 1'e, burst komutlarında 2–16 arasına zorlar.c_valid, geçerli paketler içinstatus == VALID, 4-byte hizalı adres (address[1:0] == 2'b00) vebyte_strobe != 0kurar.c_addr_region_A/B/C, üç farklı adres bölgesi tanımlar; bunlarconstraint_mode()ile teker teker açılıp kapatılacak şekilde kurgulanmıştır.c_cmd_distvec_inject_error, sırasıyla komut ve hata durumu içindistile olasılık dağılımları tanımlar.
new()kurucusutxn_idatar ve çakışmayı önlemek içinc_addr_region_B,c_addr_region_Cilec_inject_error'ı baştan kapatır — yani varsayılan davranış: A bölgesi, geçerli paketler.post_randomize(), türetilmişcrcalanını hesaplar ve hata durumlarına göre değer bozar:ADDR_ERROR'da adres hizasını bozar (address[1:0] = 2'b11),DATA_ERROR'da veriyi tersler (data = ~data). Bu, hatanın rastgeleleştirme sonrası enjekte edilmesinin örneğidir.Generatorsınıfı içinde birBusTransactionnesnesi (txn) ve dört senaryotask'ı bulunur:run_normal_traffic()varsayılan kısıtlamalarla normal trafik üretir.run_read_only(), satır içiwith { command inside {BusTransaction::READ, BusTransaction::BURST_READ}; }ile yalnızca okuma komutları üretir.run_high_address(),c_addr_region_A'yı kapatıpc_addr_region_C'yi açar, senaryo bitince eski duruma döndürür.run_error_injection(),c_valid'i kapatıpc_inject_error'ı açarak dağılımlı hata durumları üretir, sonra geri alır.
moduleiçindeGenerator8 transaction ile oluşturulur ve dört senaryo sırayla çalıştırılır.
Önemli Noktalar
- Senaryo
task'ları durumu geri yükler.run_high_address()verun_error_injection(), değiştirdikleri constraint modlarını sonunda eski haline getirir; bu, senaryoların birbirini etkilememesi için kritik bir pratiktir. - Enum değerlerine sınıf dışından erişirken kapsam belirtin. Satır içi kısıtlamada
BusTransaction::READgibisinif::degerbiçimi kullanılır. - Hata enjeksiyonunu
post_randomize()içinde yapmak güçlüdür. Kısıtlamalarla geçerli bir paket üretip ardından bilinçli olarak bozmak, hem geçerli hem hatalı senaryoları tek modelden türetmeyi sağlar. - Çakışan adres bölgesi kısıtlamalarını aynı anda açmayın. A, B, C bölgeleri ayrık aralıklardır; aynı anda birden fazlası açıkken
randomize()çözümsüz kalır. distağırlıkları gerçekçi trafik modellemek içindir.c_cmd_distvec_inject_errorile üretilen dağılım, gerçek sistem davranışına yakın test senaryoları üretmenizi sağlar; istatistiksel anlamlılık için yeterli sayıda transaction üretin.- Bu Generator deseni, ileride UVM
sequence/sequenceryapılarına geçişin temelini oluşturur; mantık aynıdır, yalnızca altyapı standartlaşır. - İnce bir nokta —
c_validkapalıyken hizalama: Hata senaryosundac_validkapatıldığı için çözücüaddress[1:0]'ı zaten rastgele seçer;post_randomize()iseADDR_ERRORdurumunda bunu kesin2'b11yapar. Yani "hatalı adres" tanımı deterministiktir ve scoreboard bunu kolayca tanıyabilir.VALIDdurumundaki paketlerde hizalama artık garanti değildir; gerçek bir projedec_valid'i tamamen kapatmak yerine yalnızcastatus == VALIDsatırını ayrı bir bloğa taşımak daha doğru olur. Bunu ek görevlerde yapacaksınız. Generatortek birtxnnesnesini her senaryoda yenidenrandomize()eder. Bu, nesne yaratma maliyetini sıfırlar; ama transaction'ları bir kuyruğa koyup saklamanız gerekirse (Lab 4'te olacağı gibi) her seferinde yeni nesne ya dacopy()gerekir — aksi halde kuyruktaki tüm elemanlar aynı nesneyi gösterir.
Ek Görevler (Zorluk Artan)
- Kolay:
run_normal_traffic()içinde komut dağılımını saymak içinint hist[string]kullanın ve senaryo sonunda yüzdeleri basın. 8 yerine 400 transaction iledisthedeflerine (30/30/20/20) yaklaşıldığını gösterin. - Orta:
c_valid'i ikiye bölün:c_status_valid { status == VALID; }vec_align { address[1:0] == 0; byte_strobe != 0; }. Hata senaryosunda yalnızcac_status_valid'i kapatın;VALIDkalan paketlerin hâlâ hizalı olduğunu doğrulayın. - Orta:
run_region_b()adında yeni bir senaryo ekleyin (B bölgesi, yalnızcaWRITE/BURST_WRITE). İki farklı mekanizmayı birlikte kullanın:constraint_mode(bölge) +with(komut). - Zor: Generator'a
BusTransaction history[$]kuyruğu ekleyin ve her üretilen transaction'ın kopyasını saklayın (copy()metodu yazmanız gerekecek). Senaryolar bittiğinde kuyruktakistatus != VALIDolanlarıfind withile süzüp sayın. Kopya almayı unutursanız ne gözlemlersiniz?
Hızlı Kontrol: Hata enjeksiyonu senaryosunda c_valid neden kapatılmalıdır?
Çünkü c_valid içinde status == VALID kuralı vardır; bu açıkken c_inject_error'daki ADDR_ERROR := 10 gibi değerler hiç seçilemez (çelişki → randomize() başarısız). Hata üretebilmek için VALID'i zorlayan kuralın devre dışı kalması gerekir.