Lab 2: Hiyerarşik Transaction
Gün 2: Nesne Yönelimli Programlama (OOP) | Transaction modeli, kalıtım ve hata enjeksiyonu
Bu lab, Gün 2 boyunca öğrendiğimiz tüm OOP kavramlarını (sınıf, kalıtım, polimorfizm, virtual metodlar, $cast) tek bir uygulamada bir araya getirir. Hiyerarşik bir Transaction modeli kurup polimorfik bir kuyruk üzerinde işlemleri yönetecek ve hata enjeksiyonu yapacağız.
Lab'da Neler Yapılıyor?
Gerçek doğrulama ortamlarında işlemler (transaction) tek tip değildir: okuma, yazma ve hatalı işlemler bir arada akar. Bu lab, ortak bir temel sınıftan türeyen özelleşmiş işlem türlerini tek bir polimorfik kuyrukta yönetmenin pratik bir örneğidir.
- Hiyerarşi: Temel
TransactionsınıfındanReadTransactionveWriteTransactiontüretilir;WriteTransaction'dan da hata enjeksiyonu yapanErrorTransactiontüretilir. - Polimorfik kuyruk: Farklı tipteki nesneler tek bir
Transactionkuyruğunda (txn_queue) tutulur vevirtualmetodlar sayesinde her biri kendi davranışını sergiler. - Hata enjeksiyonu:
ErrorTransaction, birenumile seçilen moda göre adres ve/veya veriyi kasıtlı olarak bozar; bu, DUT'un hatalı senaryolara tepkisini test etmek için kullanılır. $castile tür tespiti: Kuyruktaki her eleman çalışma zamanında$castile sınıflandırılıp ilgili işleme tabi tutulur.
Adım Adım Görev Listesi
Çözüme bakmadan önce kendiniz denemek için yol haritası:
- Temel sınıf:
static int global_id,id,address,data,valid,timestampalanlarıylaTransactionyazın.get_type(),display(),copy(),compare()metotlarınıvirtualtanımlayın. - Okuma / yazma:
ReadTransaction(burst + okunan veri kuyruğu) veWriteTransaction(byte enable + yazılan veri kuyruğu) türetin;super.new(...)zincirini doğru kurun veget_type()/display()'i override edin. - Hata enjeksiyonu:
WriteTransaction'danErrorTransactiontüretin. Birenumile hata modunu seçin,inject_error()metodu adres/veriyi XOR ile bozsun vevalidbayrağını güncellesin. - Polimorfik kuyruk: Farklı tipteki nesneleri tek bir
Transaction txn_queue[$]içinde toplayın veforeachiledisplay()çağırın. $castile tür tespiti: KuyruktakiErrorTransaction'ları bulup yalnızca onlardainject_error()çağırın.- İstatistik:
$castile READ/WRITE/ERROR sayılarını çıkarın; en özelleşmiş tipten en genele doğru kontrol ettiğinizden emin olun.
Kontrol noktası: 4. adımdan sonra çıktıda her transaction'ın kendi tipine özgü satırlar görünüyorsa polimorfizm çalışıyor demektir. Hepsi
[ BASE]basıyorsavirtualeksiktir.
Kaynak Kod
// =============================================================================
// GUN 2 - Lab 2: Hiyerarsik Transaction Modeli ve Hata Enjeksiyonu
// =============================================================================
// Bu lab'da:
// - Temel Transaction sinifi olusturulacak
// - Kalitim ile ReadTxn, WriteTxn alt siniflari turetilecek
// - Hata enjeksiyonu yapan ErrorTxn sinifi eklenecek
// - Polimorfizm ile heterojen kuyruk islenecek
// =============================================================================
// --- Temel Islem Sinifi ---
class Transaction;
static int global_id = 0;
int id;
logic [31:0] address;
logic [31:0] data;
bit valid;
int timestamp;
function new(logic [31:0] addr = 0, logic [31:0] data = 0);
this.id = global_id++;
this.address = addr;
this.data = data;
this.valid = 1;
this.timestamp = $time;
endfunction
virtual function string get_type();
return "BASE";
endfunction
virtual function void display();
$display(" [%5s] ID=%0d | Addr=0x%08h | Data=0x%08h | V=%0b | T=%0t",
get_type(), id, address, data, valid, timestamp);
endfunction
virtual function Transaction copy();
Transaction t = new(this.address, this.data);
t.id = this.id;
t.valid = this.valid;
return t;
endfunction
virtual function bit compare(Transaction other);
return (this.address == other.address && this.data == other.data);
endfunction
endclass
// --- Okuma Islemi ---
class ReadTransaction extends Transaction;
int burst_size;
logic [31:0] read_data [$];
function new(logic [31:0] addr, int burst = 1);
super.new(addr, 0);
this.burst_size = burst;
endfunction
virtual function string get_type();
return "READ";
endfunction
task execute();
$display(" >>> READ baslatildi: Addr=0x%08h, Burst=%0d", address, burst_size);
for (int i = 0; i < burst_size; i++) begin
#5;
read_data.push_back($urandom);
end
$display(" <<< READ tamamlandi: %0d veri okundu", read_data.size());
endtask
virtual function void display();
super.display();
$display(" Burst=%0d, Okunan=%0d adet", burst_size, read_data.size());
endfunction
endclass
// --- Yazma Islemi ---
class WriteTransaction extends Transaction;
logic [3:0] byte_enable;
logic [31:0] write_data [$];
function new(logic [31:0] addr, logic [31:0] data, logic [3:0] be = 4'hF);
super.new(addr, data);
this.byte_enable = be;
write_data.push_back(data);
endfunction
virtual function string get_type();
return "WRITE";
endfunction
function void add_data(logic [31:0] d);
write_data.push_back(d);
endfunction
virtual function void display();
super.display();
$display(" BE=0x%01h, WData=%p", byte_enable, write_data);
endfunction
endclass
// --- Hata Enjeksiyonu Sinifi ---
class ErrorTransaction extends WriteTransaction;
typedef enum {NO_ERROR, ADDR_CORRUPT, DATA_CORRUPT, BOTH_CORRUPT} error_type_e;
error_type_e error_mode;
function new(logic [31:0] addr, logic [31:0] data, error_type_e err = NO_ERROR);
super.new(addr, data);
this.error_mode = err;
endfunction
virtual function string get_type();
return "ERROR";
endfunction
function void inject_error();
case (error_mode)
ADDR_CORRUPT: begin
address ^= 32'hFFFF_0000;
$display(" [!] HATA ENJEKSIYONU: Adres bozuldu -> 0x%08h", address);
end
DATA_CORRUPT: begin
data ^= 32'h0000_FFFF;
$display(" [!] HATA ENJEKSIYONU: Veri bozuldu -> 0x%08h", data);
end
BOTH_CORRUPT: begin
address ^= 32'hFFFF_0000;
data ^= 32'h0000_FFFF;
$display(" [!] HATA ENJEKSIYONU: Her ikisi de bozuldu");
end
default: $display(" [OK] Hata yok");
endcase
valid = (error_mode == NO_ERROR);
endfunction
virtual function void display();
super.display();
$display(" Hata Modu: %s", error_mode.name());
endfunction
endclass
// =============================================================================
module lab2_hiyerarsik_transaction;
initial begin
Transaction txn_queue [$]; // Polimorfik kuyruk
$display("============================================================");
$display(" LAB 2: Hiyerarsik Transaction Modeli");
$display("============================================================\n");
// --- 1. Cesitli islemler olustur ---
$display("--- 1. Islem Olusturma ---");
begin
ReadTransaction r1, r2;
WriteTransaction w1, w2;
ErrorTransaction e1, e2, e3;
r1 = new(32'hA000_0000, 4);
r2 = new(32'hA000_0100, 1);
w1 = new(32'hB000_0000, 32'hDEAD_BEEF);
w2 = new(32'hB000_0004, 32'hCAFE_BABE, 4'h3);
e1 = new(32'hC000_0000, 32'h1234_5678, ErrorTransaction::ADDR_CORRUPT);
e2 = new(32'hC000_0004, 32'hABCD_EF01, ErrorTransaction::DATA_CORRUPT);
e3 = new(32'hC000_0008, 32'h9999_9999, ErrorTransaction::BOTH_CORRUPT);
// Polimorfik kuyruga ekle
txn_queue = {r1, r2, w1, w2, e1, e2, e3};
end
// --- 2. Tum islemleri goster ---
$display("\n--- 2. Tum Islemler (Polimorfik display) ---");
foreach (txn_queue[i]) begin
txn_queue[i].display();
$display();
end
// --- 3. Hata enjeksiyonu ---
$display("--- 3. Hata Enjeksiyonu ---");
foreach (txn_queue[i]) begin
ErrorTransaction et;
if ($cast(et, txn_queue[i])) begin
et.inject_error();
et.display();
$display();
end
end
// --- 4. Istatistikler ---
$display("--- 4. Istatistikler ---");
begin
int read_count = 0, write_count = 0, error_count = 0, valid_count = 0;
foreach (txn_queue[i]) begin
ReadTransaction rt;
WriteTransaction wt;
ErrorTransaction et;
if ($cast(et, txn_queue[i])) error_count++;
else if ($cast(rt, txn_queue[i])) read_count++;
else if ($cast(wt, txn_queue[i])) write_count++;
if (txn_queue[i].valid) valid_count++;
end
$display(" Toplam = %0d", txn_queue.size());
$display(" READ = %0d", read_count);
$display(" WRITE = %0d", write_count);
$display(" ERROR = %0d", error_count);
$display(" Gecerli = %0d", valid_count);
$display(" Gecersiz = %0d", txn_queue.size() - valid_count);
end
$display("\n============================================================");
$display(" LAB 2 TAMAMLANDI");
$display("============================================================");
$finish;
end
endmodule
Kodun Açıklaması
- Temel
Transactionsınıfı:id,address,data,valid,timestampalanlarını vestatic int global_idsayacını içerir. Constructor'dathis.id = global_id++ile her işleme benzersiz bir kimlik verilir,this.timestamp = $timeile oluşturulma anı kaydedilir.get_type(),display(),copy()vecompare()metodlarıvirtual'dır. ReadTransaction:burst_sizeveread_datakuyruğunu ekler.super.new(addr, 0)ile temeli kurar.execute()birtask'tır;#5gecikmelerleburst_sizekadar$urandomverisiniread_data'ya doldurur (zaman tüketen davranış).WriteTransaction:byte_enablevewrite_datakuyruğunu ekler; constructor'da gelen veriyiwrite_data'ya koyar.add_data()ile ek veri eklenebilir.get_type()"WRITE"döndürür.ErrorTransaction extends WriteTransaction: İki seviyeli kalıtım örneğidir. Sınıf içindeerror_type_eadlıenum(NO_ERROR,ADDR_CORRUPT,DATA_CORRUPT,BOTH_CORRUPT) tanımlanır.inject_error()metodu,error_mode'a göreaddressve/veyadata'yı XOR ile bozar vevalidbayrağını günceller (valid = (error_mode == NO_ERROR)).- Polimorfik
display(): Her sınıfdisplay()'ivirtualolarak override eder vesuper.display()ile temel çıktıyı koruyup üzerine kendi bilgisini ekler. Kuyruktakitxn_queue[i].display()çağrısı her elemanın gerçek tipine göre çözülür. - Polimorfik kuyruk kurulumu:
r1, r2, w1, w2, e1, e2, e3nesneleri oluşturuluptxn_queue = {r1, r2, w1, w2, e1, e2, e3};ile tek birTransactionkuyruğunda toplanır (upcasting). $castile hata enjeksiyonu: 3. adımda her eleman için$cast(et, txn_queue[i])denenir; yalnızcaErrorTransactionolanlar başarıyla dönüşür veinject_error()çağrılır.- İstatistikler: 4. adımda
$castsıralı kontrollerle her elemanın tipi tespit edilir. Önemli sıralama:ErrorTransactionbirWriteTransactionolduğundan,etkontrolüwtkontrolünden önce yapılır; aksi halde hatalı işlemler yazma olarak sayılırdı.validbayrağına göre geçerli/geçersiz sayımı da yapılır.
Önemli Noktalar
- Polimorfik bir kuyruk (
Transaction txn_queue[$]) farklı alt tipleri tek tip kodla işlemeyi sağlar; bu, ölçeklenebilir doğrulama ortamlarının temel kalıbıdır. $castile tür kontrolü yaparken sıralama kritiktir: en özelleşmiş (en alttaki) tipten en genele doğru kontrol edin.ErrorTransaction,WriteTransaction'dan türediği için önce ona bakılmalıdır.virtualmetodlar olmadan polimorfik kuyruktakidisplay()çağrıları temel sınıf sürümünü çalıştırırdı; doğru davranış için metodlarvirtualolmalıdır.static global_idsayacı tüm işlemler arasında paylaşıldığından, hangi alt sınıftan üretilirse üretilsin her nesne benzersiz biridalır.- Hata enjeksiyonu (
inject_error) gerçek doğrulamada DUT'un hatalı/bozuk işlemlere dayanıklılığını sınamak için kullanılır;validbayrağı sonrasında beklenen sonucu belirlemeye yardımcı olur. execute()birtaskolduğundan zaman tüketir; zamanlamaya duyarlı senaryolardafunctionyerinetaskkullanılması gerektiğini hatırlatır. Lab'ın ana akışındaexecute()hiç çağrılmaz; ek görevlerde kullanacaksınız.txn_queue = {r1, r2, w1, w2, e1, e2, e3};satırı yedi handle'ı kuyruğa kopyalar, nesneleri değil. Bu yüzden 3. adımdaet.inject_error()ile yapılan bozma, aynı nesneyi gösterene1/e2/e3handle'larından da görülür. Önceki dersin "alias" kavramı burada işinize yarar.- Temel sınıftaki
copy()metodu yalnızca temel alanları kopyalar;ReadTransactioniçin çağrıldığındaburst_sizeveread_datakaybolur. Bu, ek görevlerde düzeltmeniz için bilinçli bırakılmış bir eksikliktir.
Çıktıyı Okuma Rehberi
| Bölüm | Beklenen | Nasıl doğrularsınız? |
|---|---|---|
| 2. Tüm işlemler | 7 blok; her biri [ READ], [WRITE] veya [ERROR] etiketli ve kendi ek satırıyla |
Hepsi [ BASE] ise virtual eksik |
| 3. Hata enjeksiyonu | Yalnızca 3 ERROR işlemi bozulur; e1 adres, e2 veri, e3 her ikisi |
READ/WRITE'lar bu bölümde görünmemeli |
| 4. İstatistik | READ=2, WRITE=2, ERROR=3, Geçerli=4, Geçersiz=3 | ERROR=0 ve WRITE=5 ise $cast sırası ters |
Ek Görevler (Zorluk Artan)
- Kolay:
ErrorTransaction'aNO_ERRORmodunda bire4ekleyin;inject_error()sonrasındavaliddeğerinin1kaldığını gösterin. - Orta:
ReadTransactionveWriteTransactioniçincopy()'yi override edin;super.copy()sonucunu$castile alıp kendi alanlarınızı ekleyin. Kopyanın kuyruk alanlarının bağımsız olduğunuread_data.push_backile kanıtlayın. - Orta: Kuyruktaki tüm
ReadTransaction'lar içinexecute()task'ınıfork ... joiniçinde paralel çalıştırın (automaticdeğişken tuzağına dikkat — Gün 4'te ayrıntılı göreceksiniz). - Zor:
compare()'i alt sınıflarda override edin:WriteTransactioniçinbyte_enablevewrite_datakuyruğu da karşılaştırılsın. Üst sınıf handle'ı ile çağrıldığında (txn_queue[i].compare(txn_queue[j])) doğru sürümün çalıştığını gösterin.
Hızlı Kontrol: İstatistik bölümünde $cast(wt, ...) kontrolü $cast(et, ...)'dan önce yapılsaydı ne olurdu?
ErrorTransaction bir WriteTransaction olduğu için $cast(wt, ...) başarılı olur ve tüm hata işlemleri WRITE olarak sayılırdı (WRITE=5, ERROR=0). Tür tespitinde her zaman en özelleşmiş sınıftan en genele doğru kontrol edin.