Monitor Modülü
Gün 7: Bitirme Projesi - Bölüm 2 | DUT çıkışlarını izleyen ve paketleyen modül
Monitor (izleyici), DUT'un arayüzündeki sinyal hareketlerini gözleyip tekrar transaction nesnelerine paketleyen pasif bileşendir. ALU mimarimizde driver'ın tersi yönde çalışır: pin seviyesinden transaction seviyesine geçiş yapar ve sonucu scoreboard'a iletir.
Monitor ve Mimarideki Yeri
Monitor doğrulamanın "gözüdür". Görevi yalnızca izlemektir; DUT'a hiçbir sinyal sürmez. Bu yüzden virtual alu_if.monitor modport'unu kullanır ki bu modport'ta tüm sinyaller input'tur.
İletişim akışı şöyledir:
- Monitor,
vif.mon_cbclocking block'unu her saat kenarında örnekler. out_validyüksek olduğunda DUT geçerli bir sonuç üretmiş demektir.- Bu anda gözlenen giriş ve çıkış değerleri yeni bir
ALU_Transaction'a paketlenir vemon2scbmailbox'ı ile scoreboard'a gönderilir.
Neden Ayrı Bir Monitor?
Driver "ne uyguladığımızı" bilirken, monitor "DUT'un gerçekte ne yaptığını" bağımsızca gözler. Scoreboard bu iki bağımsız kaynağı (driver kopyası vs. monitor gözlemi) karşılaştırarak DUT'un doğruluğunu test eder. İzlemenin sürüşten ayrılması, doğrulamanın güvenilirliğini artırır.
Kaynak Kod
// =============================================================================
// GUN 7 - Konu 1: Monitor - Arayuzdeki Hareketleri Izleyen Modul
// =============================================================================
class ALU_Monitor;
virtual alu_if.monitor vif;
mailbox #(ALU_Transaction) mon2scb;
int monitored_count = 0;
function new(virtual alu_if.monitor vif, mailbox #(ALU_Transaction) mbx);
this.vif = vif;
this.mon2scb = mbx;
endfunction
task run();
ALU_Transaction txn;
$display("[Monitor] Baslatildi - DUT cikislari izleniyor");
forever begin
@(vif.mon_cb);
if (vif.mon_cb.out_valid) begin
txn = new();
txn.operand_a = vif.mon_cb.operand_a;
txn.operand_b = vif.mon_cb.operand_b;
txn.opcode = ALU_Transaction::opcode_e'(vif.mon_cb.opcode);
txn.result = vif.mon_cb.result;
txn.flags = vif.mon_cb.flags;
txn.out_valid = 1;
mon2scb.put(txn);
monitored_count++;
end
end
endtask
endclass
Kodun Açıklaması
- Üyeler:
virtual alu_if.monitor vifsalt-okuma erişim sağlar;mon2scbgözlenen transaction'ları scoreboard'a taşır;monitored_countizlenen işlem sayısını tutar. new(...): Virtual interface ve mailbox'ı dışarıdan alır.run(): Sonsuz döngüde her@(vif.mon_cb)saat kenarında örnek alır.if (vif.mon_cb.out_valid): Yalnızca DUT geçerli bir çıkış işaretlediğinde paketleme yapılır; geçersiz çevrimler atlanır.- Transaction paketleme: Gözlenen
operand_a,operand_b,opcode,result,flagsdeğerleri yeni birtxn'e kopyalanır.opcode,ALU_Transaction::opcode_e'(...)ile enum tipine dönüştürülür.out_validalanı1olarak işaretlenir. mon2scb.put(txn)vemonitored_count++: Paketlenen transaction scoreboard'a gönderilir ve sayaç artırılır.
Önemli Noktalar
- Monitor tamamen pasiftir;
mon_cbmodport'u sayesinde hiçbir sinyale yazamaz, bu da yanlışlıkla DUT'u uyarmayı imkânsız kılar. out_validfiltresi kritiktir: Yalnızca geçerli çevrimleri toplamak, scoreboard'da driver ile monitor akışlarının hizalı kalmasını sağlar.opcodeenum dönüşümü (opcode_e'(...)) yapılmadan hamlogicdeğer scoreboard'daname()gibi enum metotlarıyla doğru yorumlanamaz.- DUT'un bir çevrimlik pipeline gecikmesi monitorün doğru zamanda örnekleme yapmasını gerektirir; clocking block bu hizalamayı otomatik sağlar.
run()sonsuz döngüdür; durması environment'ın yaşam döngüsü yönetimine bırakılmıştır.- Monitor operandları da
out_validanında okur. Driverin_valid'i düşürdükten sonra operandları değiştirmediği için (sonraki transaction bir çevrim sonra gelir),out_valid=1olan kenardaoperand_a/bveopcodehâlâ o işleme aittir. Bu "şans" değil, driver ile monitor arasındaki zamanlama sözleşmesidir; driver back-to-back sürerse bu sözleşme bozulur ve monitor operandları bir sonraki işlemden okur. Ek görevlerde bunu kıracaksınız. - Monitor neden
copy()göndermez? Çünkü herout_valid'denew()ile yeni bir nesne yaratır; kimseyle paylaşmaz. Driver'dakicopy()ihtiyacı, nesnenin generator'dan gelmesi ve başka yerlerde de tutulmasıydı. monitored_countiledriven_counteşit olmalıdır. Fark varsa ya monitor birout_validkaçırmıştır (skew/zamanlama) ya da DUT beklenmedik ekstraout_validüretmiştir; bu eşitlik environment raporundaki ilk sağlık kontrolüdür.
Driver ve Monitor: Simetrik İkili
| Driver | Monitor | |
|---|---|---|
| Yön | Transaction → pinler | Pinler → transaction |
| Modport | alu_if.driver (sürer) |
alu_if.monitor (yalnızca okur) |
| Clocking block | drv_cb (output + input) |
mon_cb (yalnızca input) |
| Zaman kaynağı | Mailbox'tan gelen transaction | Her saat kenarı (@(vif.mon_cb)) |
| Tetikleyici | gen2drv.get() |
out_valid == 1 |
| Çıkış | drv2scb.put(copy) (beklenen) |
mon2scb.put(new) (gözlenen) |
| Bilmesi gereken | Giriş protokolü (in_valid el sıkışması) |
Çıkış protokolü (out_valid anlamı) |
| Bilmemesi gereken | Doğru sonucun ne olduğu | Driver'ın ne gönderdiği |
Son iki satır kritiktir: monitor driver'dan bağımsız olmalıdır. Monitor "driver şunu gönderdi, demek ki bu çıkış ona ait" diye düşünmez; yalnızca pinlerde gördüğünü raporlar. Bağımsızlık, aynı hatanın iki yerde birden yapılıp gizlenmesini önler.
Benzetme: Driver bir restoranda garsondur: siparişi mutfağa (DUT) iletir. Monitor ise mutfaktan çıkan tabakları fotoğraflayan bağımsız bir denetçidir; garsonun ne söylediğiyle ilgilenmez, yalnızca tabakta ne olduğunu kaydeder. Scoreboard (hakem) sipariş fişi ile fotoğrafı karşılaştırır.
Sık Yapılan Hatalar
out_validfiltresini kaldırmak: Monitor her çevrim transaction üretir; scoreboard'dakimon2scb.get()driver'dan gelen beklenenle hizasını kaybeder ve her şey "uyuşmazlık" olur.if (vif.mon_cb.out_valid == 1'b1)yerine===kullanmamak: Reset öncesiout_validXolabilir;==ileXkarşılaştırmasıXüretir veifbunu yanlış sayar — burada zararsız, ama!= 0yazsaydınızXyanlış pozitif verirdi. Gün 5'teki alışkanlığı sürdürün:=== 1'b1.- Ham sinyali okumak (
vif.out_valid): Clocking block dışından okunan değer kenar anındaki NBA güncellemelerinden etkilenir;mon_cb.out_validise Preponed bölgede örneklenmiş, kararlı değerdir. - Enum dönüşümünü unutmak:
txn.opcode = vif.mon_cb.opcode;bazı araçlarda uyarı, bazılarında hatadır;opcode_e'(...)cast'i hem taşınabilirliği hem dename()'in doğru çalışmasını sağlar. - Monitor'ü
join_noneile başlatıpdisable forkile öldürmek: Gün 4'teki kapsam tuzağı; environment'ta timeout kalıbı kullanıyorsanız izolefork...joinsarmalı gerekir.
Kendinizi Deneyin
if (vif.mon_cb.out_valid)satırınıif (1)yapın ve Gün 7'de final testbench'i çalıştırın: scoreboard kaç hata verdi, neden?- Monitor'e
int idle_cycles = 0;sayacı ekleyin;out_valid=0olan her çevrimi sayın ve raporda basın. Driver'ın 2 çevrimlik ritmindeidle_cycles ≈ monitored_countçıkması gerekir; açıklayın. - Driver'ı back-to-back sürecek şekilde değiştirin (Ders 39, ek görev 1). Monitor'ün operandları hangi işlemden okuduğunu scoreboard hatalarından çıkarın. Sonra monitor'ü, operandları
in_valid=1olan kenarda yakalayıp bir çevrim sonraresultile birleştirecek şekilde (küçük bir pipeline) düzeltin. - Monitor'e
virtual alu_if.driver vifvermeyi deneyin. Hangi satır derlenmiyor? Bu hata neden "iyi bir hata"dır?
Hızlı Kontrol: Monitor, operandları neden out_valid anında okuyabiliyor; bu her DUT için geçerli midir?
Bu projede driver operandları in_valid düştükten sonra da bir çevrim daha pinlerde tuttuğu için out_valid kenarında hâlâ o işleme ait değerler okunur. Bu, driver ile monitor arasındaki zamanlama sözleşmesidir, genel bir kural değildir. Pipeline'ı daha derin bir DUT'ta ya da back-to-back sürüşte monitor, girişleri in_valid anında yakalayıp çıkışla eşleştirmek (kendi içinde kuyruklamak) zorundadır.