Transaction Sınıfı
Gün 6: Bitirme Projesi - Bölüm 1 | ALU transaction paketi: constraint'ler ve metodlar
Transaction (işlem paketi), testbench içinde dolaşan tek bir ALU işlemini soyutlayan veri sınıfıdır. Generator onu üretir, driver pinlere sürer, monitor DUT'tan toplar ve scoreboard karşılaştırır; yani ALU_Transaction, tüm bileşenler arasında ortak konuşulan "dil"dir.
Transaction Nedir ve Neden Gereklidir
Sinyal seviyesinde düşünmek yerine, doğrulama dünyasında işlemleri nesne olarak ele alırız. Bir ALU_Transaction nesnesi tek bir ALU işleminin hem girişlerini hem de beklenen/gözlenen çıkışlarını taşır:
- Girişler (
randile rastgeleleştirilir):opcode,operand_a,operand_b - Çıkışlar (DUT'tan okunur):
result,flags,out_valid - Meta veri: Her işleme benzersiz bir
idve toplam sayacıcount
Bu sayede bir transaction, mailbox'lar üzerinden bileşenden bileşene tek parça halinde aktarılabilir.
Constraint'ler (Kısıtlamalar) ile Akıllı Rastgelelik
Saf rastgelelik genellikle ilginç durumları yeterince üretmez. Bu sınıf üç kısıtlama kullanır:
c_opcode_dist: İşlem kodlarının dağılımını ağırlıklandırır; toplama/çıkarma daha sık (:= 20), mantıksal işlemler daha seyrek seçilir.c_corner_cases: Operandların köşe değerlerini (0,255,8'h80) bilinçli olarak öne çıkarır; bunlar taşma/işaret hatalarının en sık görüldüğü değerlerdir.c_shift_range: Kaydırma işlemlerindeoperand_b'yi0-7aralığına sınırlar; çünkü 8-bit veride 8'den büyük kaydırma anlamsızdır.
Yardımcı Metotlar
display(): İşlemi okunabilir formatta basar; hata ayıklamada vazgeçilmezdir.copy(): Nesnenin derin bir kopyasını üretir. Driver, scoreboard'a transaction'ın kopyasını gönderir; böylece aynı nesnenin paylaşılmasından doğan referans hataları engellenir.
Kaynak Kod
// =============================================================================
// GUN 6 - Konu 2: Transaction (Islem) Paketi Sinifi
// =============================================================================
class ALU_Transaction;
// Islem kodlari (DUT ile uyumlu)
typedef enum logic [2:0] {
OP_ADD = 3'b000,
OP_SUB = 3'b001,
OP_AND = 3'b010,
OP_OR = 3'b011,
OP_XOR = 3'b100,
OP_NOT = 3'b101,
OP_SHL = 3'b110,
OP_SHR = 3'b111
} opcode_e;
// Rastgele giris degiskenleri
rand opcode_e opcode;
rand logic [7:0] operand_a;
rand logic [7:0] operand_b;
// Cikis degiskenleri (DUT'tan okunacak)
logic [15:0] result;
logic [3:0] flags;
logic out_valid;
// Meta veriler
int id;
static int count = 0;
// --- Kisitlamalar ---
constraint c_opcode_dist {
opcode dist {
OP_ADD := 20,
OP_SUB := 20,
OP_AND := 10,
OP_OR := 10,
OP_XOR := 10,
OP_NOT := 10,
OP_SHL := 10,
OP_SHR := 10
};
}
// Kose degerler (0, 0x80, 0xFF) one cikarilir; araliklar birbiriyle
// CAKISMAZ: 0x80 degeri [1:127] ve [129:254] araliklarinin disinda tutulur.
constraint c_corner_cases {
operand_a dist {
0 := 10,
[1:127] :/ 35,
8'h80 := 10,
[129:254] :/ 35,
255 := 10
};
operand_b dist {
0 := 10,
[1:127] :/ 35,
8'h80 := 10,
[129:254] :/ 35,
255 := 10
};
}
// Shift isleminde B sadece 0-7
constraint c_shift_range {
(opcode inside {OP_SHL, OP_SHR}) -> operand_b inside {[0:7]};
}
function new();
this.id = count++;
endfunction
function void display(string prefix = "");
$display(" %sTXN#%03d | %s | A=0x%02h | B=0x%02h | Res=0x%04h | Flags=%04b | V=%0b",
prefix, id, opcode.name(), operand_a, operand_b, result, flags, out_valid);
endfunction
function ALU_Transaction copy();
ALU_Transaction t = new();
t.opcode = this.opcode;
t.operand_a = this.operand_a;
t.operand_b = this.operand_b;
t.result = this.result;
t.flags = this.flags;
t.out_valid = this.out_valid;
t.id = this.id;
return t;
endfunction
endclass
Kodun Açıklaması
opcode_eenum'u:OP_ADD'denOP_SHR'a kadar 8 işlemilogic [2:0]olarak kodlar. Değerler DUT'takilocalparamopcode'larıyla birebir aynıdır, böylece doğrudan pinlere sürülebilir.randdeğişkenler:opcode,operand_a,operand_brastgeleleştirilebilir alanlardır.randomize()çağrıldığında kısıtlamalara uygun değerler üretilir.- Çıkış alanları:
result,flags,out_validrastgele değildir; DUT'tan veya referans modelden gelen değerlerle doldurulur. static int count: Tüm nesneler arasında paylaşılan sayaç.new()içindethis.id = count++ile her transaction'a artan benzersiz bir kimlik verilir.c_opcode_dist:distoperatörüyle opcode olasılık ağırlıklarını belirler (OP_ADD := 20gibi).c_corner_cases:operand_aveoperand_biçin:=(her değere ayrı ağırlık) ve:/(aralığa toplam ağırlık) operatörlerini birlikte kullanarak köşe değerleri öne çıkarır. Toplam ağırlık 100'dür: üç köşe değer (0,0x80,0xFF) %10'ar, iki ara bölge %35'er. Aralıkların çakışmaması önemlidir:0x80değeri[1:127]ve[129:254]dışında bırakılmıştır; aynı değer hem bir aralıkta hem tek başına listelenseydi standart bunu hata olarak kabul eder ve ağırlığı belirsizleşirdi.c_shift_range:(opcode inside {OP_SHL, OP_SHR}) -> operand_b inside {[0:7]}ile koşullu (implication) kısıtlama uygular.copy(): Yeni bir nesne oluşturup tüm alanları tek tek kopyalar ve döndürür; sığ kopyalamanın yan etkilerinden kaçınır.
Önemli Noktalar
- Transaction enum'u DUT opcode'larıyla senkron olmalıdır; uyumsuzluk tüm karşılaştırmaları sessizce bozar.
assert(txn.randomize())her zaman kontrol edilmelidir; constraint'ler çözülemezse randomize başarısız olur ve sessizce yanlış veriyle devam edilmemelidir.copy()kullanımı kritiktir: Aynı nesne referansı birden çok bileşene gönderilirse, sonradan yapılan değişiklikler beklenen değeri bozabilir. Derin kopya bu tuzağı önler.distağırlıkları kapsamı (coverage) doğrudan etkiler; nadir senaryolara yeterli ağırlık vermek fonksiyonel kapsamı hızlandırır.c_shift_rangegibi koşullu kısıtlamalar, generator'daki yönlendirilmiş üretimle uyumlu olmalıdır; aksi halde solver çakışması (conflict) yaşanır.distlistesinde aynı değer iki kez görünmemelidir. Bu yüzden ara bölgeler0x80etrafında ikiye bölünmüştür. Kısıt yazarken "bu değer kaç yerde geçiyor?" sorusunu sormak, çözücü hatalarını ve belirsiz ağırlıkları önler.- Çıkış alanları
randdeğildir amacopy()tarafından kopyalanır. Driver sürdüğü andaresult/flagshenüz boştur; scoreboard bunları referans modelden hesaplar. Monitor ise DUT'tan okuduğu değerlerle yeni bir transaction üretir. Aynı sınıfın hem "beklenen" hem "gözlenen" rolünde kullanılması, karşılaştırmayı kolaylaştırır.
Transaction'ın Yaşam Döngüsü
Generator Driver DUT Monitor Scoreboard
────────── ────────── ─── ────────── ──────────
new() + randomize ─▶ get(txn)
drive pins ─────────▶ hesapla
put(txn.copy()) ────────────────────────────────────────▶ expected
out_valid ─▶ new() + pinlerden ─▶ actual
doldur, put(txn)
compare(expected, actual)
Aynı ALU_Transaction sınıfı üç farklı yerde yaratılır: generator'da (girişler rastgele), driver'da copy() ile (beklenen kayıt), monitor'de (gözlenen çıkış). Sınıfın hem giriş hem çıkış alanlarını taşıması bu yüzden gereklidir.
Sık Yapılan Hatalar
- Enum değerlerini DUT ile farklı sıralamak:
OP_AND = 3'b011yazılırsa DUTORyapar, scoreboardANDbekler; her transaction'da hata alırsınız ama kaynağı bulmak saatler alabilir. copy()'ye yeni alan eklemeyi unutmak: Sınıfa bir alan ekleyipcopy()'yi güncellemezseniz scoreboard'a giden kopyada o alan varsayılan değerde kalır.static count'ucopy()içinde artırmak:new()çağrısıcount++yapar; kopyalar da kimlik tüketir. Bu projedeidzatencopy()içinde eski değere eşitlenir; amacountyine atlar. Kritik değildir, ancak "üretilen transaction sayısı =count" varsayımı yapmayın.distiçinde örtüşen aralıklar: Yukarıda açıklanan hata.
Kendinizi Deneyin
- 1000 transaction
randomize()edipoperand_aiçin0,0x80,0xFFve "diğer" sayılarını bir ilişkisel dizide toplayın. Dağılım 10/10/10/70'e yaklaştı mı? c_shift_rangekısıtını geçici olarak kapatın (txn.c_shift_range.constraint_mode(0)) veOP_SHLileoperand_b = 8'h09üretin. DUT ne yapar (ipucu:operand_b[2:0])? Bu, bir "tasarım hatası" mı, "sözleşme" mi?display()çıktısına%sileopcode.name()yerine%0dile ham değeri bastırın; hangisi hata ayıklamada daha kullanışlı?- Sınıfa
function bit compare(ALU_Transaction other);ekleyin:resultveflagseşitliğini===ile karşılaştırsın. Gün 7'de scoreboard'u bu metodu kullanacak şekilde sadeleştirmeyi deneyin.
Hızlı Kontrol: dist içinde aynı değer neden iki ayrı terimde yer almamalıdır?
Standart, bir dist listesinde her değerin en fazla bir terimde görünmesini ister; aksi durum hata ya da belirsiz ağırlık üretir. Bu projede 0x80 köşe değeri bu yüzden [1:127] ve [129:254] aralıklarının dışında bırakılarak tek başına listelenir.