Virtual Interface ile DUT'a Bağlanma: Gerçek Driver ve Monitor
UVM Gün 6: DUT Entegrasyonu |
uvm_config_db#(virtual alu_if), clocking block üzerinden süren driver,out_valid'de örnekleyen monitor, agent'ın analysis port'u,tb_top'ta interface + DUT +run_test()
Amaç
Gün 1–5 boyunca ortamı DUT'suz kurduk: driver yalnızca log basıp #20 bekledi, monitor sahte transaction üretti. Bu derste SystemVerilog bitirme projesinin 8-bit ALU'sunu (Ders 36-1) ve alu_if arayüzünü (Ders 36) UVM ortamına bağlayacağız. Dersin sonunda elinizde gerçek pinleri süren bir alu_driver, DUT çıkışlarını transaction'a çeviren bir alu_monitor, bunları paketleyen bir agent ve DUT'u gerçekten çalıştıran bir tb_top olacak. Scoreboard ve coverage Gün 7'de bu iskeletin üzerine gelecek.
Ön Koşullar
- SystemVerilog Gün 5: interface, modport, clocking block, virtual interface (Dersler 30–32).
- SystemVerilog bitirme projesi:
alumodülü (Ders 36-1),alu_if(Ders 36),ALU_Driver(Ders 39),ALU_Monitor(Ders 41). Bu derste aynı interface ve DUT değişmeden kullanılır; driver ve monitor UVM'e taşınır. - UVM Gün 2–4:
uvm_sequence_item, sequencer–driver el sıkışması,uvm_analysis_port,uvm_config_db.
İki Dünya, Bir Köprü
UVM bileşenleri sınıftır: build_phase'de yaratılır, hiyerarşik modül yolu yoktur. DUT ve interface ise modüldür: elaboration'da var olur, sabit bir yolu vardır (tb_top.aif). Sınıfın interface'e ulaşmasının tek taşınabilir yolu virtual interface handle'ıdır; handle'ın sınıfa ulaşmasının UVM'deki standart yolu ise uvm_config_db'dir. SystemVerilog projesinde bu işi new(aif, aif) ile yapıcıya vererek çözmüştünüz; UVM'de yapıcı imzası sabit olduğu için (new(name, parent)) veri tabanı devreye girer.
tb_top (modül, statik) UVM hiyerarşisi (sınıflar, dinamik)
────────────────────────────── ─────────────────────────────────────────
clk ──▶ alu_if aif(clk) ──▶ alu dut(...) uvm_test_top : alu_base_test
│ └─ env : alu_env
│ uvm_config_db#(virtual alu_if) └─ agent : alu_agent
└────── ::set(null,"uvm_test_top.*", ├─ sqr : alu_sequencer ◀── alu_random_seq
"vif", aif) ├─ drv : alu_driver ── vif.drv_cb ──▶ DUT girişleri
└─ mon : alu_monitor ◀─ vif.mon_cb ─── DUT çıkışları
└─ ap ─▶ agent.ap ─▶ (Gün 7: scoreboard, coverage)
set çağrısı tb_top'ta, run_test()'ten önce yapılır; get çağrıları driver ve monitor'ün build_phase'inde, yani simülasyonun 0. anında çalışır. Sıralama bu yüzden kritiktir.
Gün 2–4'teki İskeletten Gerçek Bileşenlere
| Gün 2–4 (DUT'suz) | Bu ders (ALU) | Değişen |
|---|---|---|
my_transaction (addr/data/we) |
alu_transaction (opcode/a/b + result/flags) |
Alanlar DUT sözleşmesine göre |
my_driver: log + #20 |
alu_driver: vif.drv_cb üzerinden pin sürer, reset yapar |
drive_item gövdesi |
my_monitor: sahte tr |
alu_monitor: out_valid anında pinleri okur |
run_phase gövdesi |
my_agent |
alu_agent + kendi ap'si |
Monitor portunu dışarı açar |
tb_top: yalnızca run_test() |
tb_top: saat + interface + DUT + config_db::set + run_test() |
Statik dünya eklendi |
Fazlar, factory, sequencer–driver el sıkışması, objection: hiçbiri değişmedi. UVM'in vaadi tam olarak budur: altyapı sabit kalır, yalnızca protokole özgü parçalar yazılır.
Lab 6.1: Interface ve DUT (SystemVerilog Projesinden Aynen)
alu_if ve alu modülü Ders 36 ve 36-1'dekiyle birebir aynıdır; burada yalnızca hatırlatma için interface'in iskeleti verilmiştir.
interface alu_if(input logic clk);
logic rst_n;
logic in_valid;
logic [7:0] operand_a, operand_b;
logic [2:0] opcode;
logic out_valid;
logic [15:0] result;
logic [3:0] flags;
clocking drv_cb @(posedge clk); // driver: surer + okur
default input #1 output #1;
output in_valid, operand_a, operand_b, opcode;
input out_valid, result, flags;
endclocking
clocking mon_cb @(posedge clk); // monitor: yalnizca okur
default input #1;
input in_valid, operand_a, operand_b, opcode, out_valid, result, flags;
endclocking
modport driver (clocking drv_cb, output rst_n, input clk);
modport monitor (clocking mon_cb, input clk, rst_n);
endinterface
Kodun Açıklaması
- İki clocking block, iki bakış açısı:
drv_cbgiriş sinyallerinioutputolarak sürer;mon_cbaynı sinyalleriinputolarak okur. Bir clocking block'unoutput'u okunamadığı için monitor'ün kendi penceresi olmak zorundadır (Ders 36'daki tartışma). rst_nclocking dışında: Reset asenkron bir sinyaldir; driver onu doğrudan (vif.rst_n = 0) sürer.- Modport'lar isteğe bağlı: Bu derste
uvm_config_db#(virtual alu_if)ile modport'suz handle taşınır ve driver/monitorvif.drv_cb/vif.mon_cbüzerinden çalışır. Modport'lu handle (virtual alu_if.driver) da taşınabilir; amasetveget'teki tip parametresi birebir aynı olmak zorunda olduğu için (virtual alu_if≠virtual alu_if.driver) ekipler genellikle tek bir tipte karar kılar. Sektörde yaygın olan, modport'suz tam interface'i taşımaktır.
Lab 6.2: ALU Transaction
Görev: alu_transaction sınıfını DUT sözleşmesine (Ders 36-1) göre yazın. Opcode kodlaması DUT ile birebir aynı olmalıdır.
package alu_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
// DUT'taki localparam'larla BIREBIR ayni kodlama (Ders 36-1)
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;
class alu_transaction extends uvm_sequence_item;
// Girisler: sequence rastgele uretir, driver surer
rand opcode_e opcode;
rand bit [7:0] a;
rand bit [7:0] b;
// Cikislar: monitor pinlerden doldurur (logic: X/Z yakalanabilsin)
logic [15:0] result;
logic [3:0] flags; // [0]=Z [1]=N [2]=C [3]=P
constraint c_shift_range { (opcode inside {OP_SHL, OP_SHR}) -> b inside {[0:7]}; }
constraint c_corners {
a dist { 8'h00 := 10, [8'h01:8'hFE] :/ 80, 8'hFF := 10 };
b dist { 8'h00 := 10, [8'h01:8'hFE] :/ 80, 8'hFF := 10 };
}
`uvm_object_utils_begin(alu_transaction)
`uvm_field_enum(opcode_e, opcode, UVM_ALL_ON)
`uvm_field_int(a, UVM_ALL_ON)
`uvm_field_int(b, UVM_ALL_ON)
`uvm_field_int(result, UVM_ALL_ON)
`uvm_field_int(flags, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "alu_transaction");
super.new(name);
endfunction
// Loglar icin tek satirlik ozet (Gun 2: field makrolarinin print()'ine hafif alternatif)
virtual function string convert2string();
return $sformatf("%-6s a=0x%02h b=0x%02h -> result=0x%04h flags=%04b (P C N Z)",
opcode.name(), a, b, result, flags);
endfunction
endclass
// Diger siniflar asagidaki laboratuvarlarda eklenir:
// `include "alu_driver.sv" ... "alu_test.sv"
endpackage
Kodun Açıklaması
opcode_epaket seviyesinde: Enum'u sınıfın içine değil paketin içine koyduk; böylece driver (vif.drv_cb.opcode <= tr.opcode) ve monitor (opcode_e'(vif.mon_cb.opcode)) aynı tipi uzun yol yazmadan kullanır. SystemVerilog projesindeALU_Transaction::opcode_ebiçiminde sınıf içindeydi; ikisi de geçerlidir.- Girişler
rand bit, çıkışlarlogic: Girişleri sequence üretir,bityeterlidir. Çıkışları monitor pinlerden okur; reset öncesiXdeğerlerini kaybetmemek içinlogicseçilir.uvm_field_inther iki tiple çalışır. - Kısıtlar SystemVerilog projesinden (Ders 37) sadeleştirildi:
c_shift_rangeDUT'un yalnızcab[2:0]'ı kullandığı gerçeğini yansıtır;c_cornersköşe değerleri öne çıkarır. Sequence'lerrandomize() with {...}ile bunların üstüne kısıt ekleyebilir (Gün 4). uvm_field_enum(opcode_e, opcode, UVM_ALL_ON): Enum alanları için ayrı makro vardır;print()çıktısında ham sayı yerineOP_ADDgörünür.convert2string():`uvm_infoiçindetr.convert2string()çağırmak,tr.print()'in çok satırlı tablosundan çok daha okunaklı bir log verir. Driver ve monitor bunu kullanacak.
Lab 6.3: Gerçek Driver — Clocking Block Üzerinden Sürmek
Görev: Gün 5'teki kancalı driver kalıbını koruyarak (drive_item), gövdeyi Ders 39'daki ALU_Driver'dan taşıyın.
class alu_driver extends uvm_driver #(alu_transaction);
`uvm_component_utils(alu_driver)
virtual alu_if vif; // tb_top'tan uvm_config_db ile gelir
function new(string name = "alu_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// vif olmadan driver'in calismasi imkansiz: uyari degil, oldurucu hata
if (!uvm_config_db#(virtual alu_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", {"Virtual interface bulunamadi: ", get_full_name(), ".vif"})
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
reset_dut(); // once DUT'u bilinen duruma getir
forever begin
seq_item_port.get_next_item(req); // sequence'ten paket iste (bloklar)
drive_item(req); // pinlere sur (2 saat cevrimi)
seq_item_port.item_done(); // sequence'e "suruldu" de
end
endtask
// Ders 39: ALU_Driver::reset() ile ayni
virtual task reset_dut();
`uvm_info("DRV", "Reset basliyor", UVM_MEDIUM)
vif.rst_n = 1'b0; // asenkron reset: clocking disi, dogrudan
vif.drv_cb.in_valid <= 1'b0;
vif.drv_cb.operand_a <= '0;
vif.drv_cb.operand_b <= '0;
vif.drv_cb.opcode <= '0;
repeat (5) @(vif.drv_cb);
vif.rst_n = 1'b1;
@(vif.drv_cb);
`uvm_info("DRV", "Reset tamamlandi", UVM_MEDIUM)
endtask
// Ders 39: ALU_Driver::drive_transaction() ile ayni
virtual task drive_item(alu_transaction tr);
@(vif.drv_cb); // saat kenarini bekle
vif.drv_cb.in_valid <= 1'b1;
vif.drv_cb.operand_a <= tr.a;
vif.drv_cb.operand_b <= tr.b;
vif.drv_cb.opcode <= tr.opcode;
@(vif.drv_cb); // bir cevrim gecerli tut
vif.drv_cb.in_valid <= 1'b0;
`uvm_info("DRV", {"Suruldu: ", tr.convert2string()}, UVM_HIGH)
endtask
endclass
Kodun Açıklaması
- **
getbaşarısızlığında`uvm_fatal:** Gün 4'tedriver_delayiçinuvm_warning+ varsayılan kullanmıştık; çünkü gecikmesiz de çalışabilirdik.vifolmadan ise tek bir satır bile sürülemez; sessizce devam etmek yalnızca daha geç, daha anlaşılmaz bir null-handle hatası üretir. Kural: isteğe bağlı konfigürasyon → warning, zorunlu kaynak → fatal. - Reset driver'ın
run_phase'inin başında: SystemVerilog projesinde environmentdrv.reset()çağırıyordu. Burada driver, sequence'ten ilk paketi istemeden önce DUT'u resetler. Sequence bu sıradastart_item'da bekler (Gün 4'teki el sıkışma); yani reset bitmeden hiçbir paket sürülmez; ek bir senkronizasyon gerekmez. Büyük projelerde reset ayrı bir agent ya dareset_phaseile yönetilir; ilke aynıdır. vif.drv_cb.x <= ...ve@(vif.drv_cb): Ders 39'daki kurallar aynen geçerli: veri sinyalleri clocking block üzerinden nonblocking atanır,output #1skew DUT ile yarışı önler. Her transaction tam iki saat çevrimi sürer;in_validbir çevrim yüksek kalır.get_next_item/item_doneçifti değişmedi: Gün 2'deki iskelet ile tek farkdrive_item'ın artık gerçekten zaman harcaması ve pin sürmesidir.item_done()çağrısı ikinci@(vif.drv_cb)'den sonra geldiği için sequence'infinish_item'ı, paket gerçekten pinlere uygulandıktan sonra döner.
Lab 6.4: Gerçek Monitor — out_valid Anında Örnekleme
Görev: Ders 41'deki ALU_Monitor mantığını UVM'e taşıyın; mailbox yerine analysis port kullanın.
class alu_monitor extends uvm_monitor;
`uvm_component_utils(alu_monitor)
virtual alu_if vif;
uvm_analysis_port #(alu_transaction) ap; // Gun 3: 1 -> N yayin
int unsigned n_observed = 0;
function new(string name = "alu_monitor", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new("ap", this);
if (!uvm_config_db#(virtual alu_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", {"Virtual interface bulunamadi: ", get_full_name(), ".vif"})
endfunction
virtual task run_phase(uvm_phase phase);
alu_transaction tr;
super.run_phase(phase);
forever begin
@(vif.mon_cb); // her saat kenarinda ornekle
if (vif.mon_cb.out_valid === 1'b1) begin // === : reset oncesi X'e aldanma
tr = alu_transaction::type_id::create("tr"); // her gozlem icin YENI nesne
tr.opcode = opcode_e'(vif.mon_cb.opcode);
tr.a = vif.mon_cb.operand_a;
tr.b = vif.mon_cb.operand_b;
tr.result = vif.mon_cb.result;
tr.flags = vif.mon_cb.flags;
n_observed++;
`uvm_info("MON", {"Gozlendi: ", tr.convert2string()}, UVM_MEDIUM)
ap.write(tr); // scoreboard/coverage'a yayinla
end
end
endtask
endclass
Kodun Açıklaması
- Monitor sürmez, yalnızca okur:
vif.mon_cbyalnızcainputiçerir; monitor'ün bir sinyali yanlışlıkla sürmesi derleme hatasıdır. Bu, modport kullanmadan da pasifliği garanti eder. out_valid === 1'b1: Reset öncesiout_validX'tir;==ile karşılaştırmakXüretir veifbunu yanlış sayar (zararsız), ama!= 0yazsaydınız yanlış pozitif alırdınız. Ders 41'deki alışkanlık:===.- Zamanlama sözleşmesi: Driver operandları
in_validdüştükten sonra da bir çevrim pinlerde tuttuğu içinout_valid=1olan kenardaoperand_a/bveopcodehâlâ aynı işleme aittir. Bu, Ders 41'de açıklanan driver–monitor sözleşmesidir; driver back-to-back sürmeye başlarsa monitor girişleriin_validanında yakalayıp çıkışla eşleştirmek zorunda kalır (Kendinizi Deneyin #3). - Her gözlemde
create: Gün 3'teki tuzak:tr'yi döngü dışında bir kez yaratırsanız scoreboard ve coverage aynı nesneyi paylaşır, bir sonraki örnekleme "beklenen" değeri değiştirir.ap.write(tr)handle yayınlar, kopya değil. n_observed: Gün 7'decheck_phasesürülen ve gözlenen sayıları karşılaştıracak; ilk sağlık kontrolü budur.
Lab 6.5: Agent, Environment, Sequence, Test ve tb_top
Görev: Parçaları birleştirin. Agent, monitor'ün portunu kendi ap'si üzerinden dışarı açsın.
// Bos alt sinif yerine typedef (Gun 2'deki my_sequencer'in kisa hali)
typedef uvm_sequencer #(alu_transaction) alu_sequencer;
class alu_agent extends uvm_agent;
`uvm_component_utils(alu_agent)
alu_sequencer sqr;
alu_driver drv;
alu_monitor mon;
uvm_analysis_port #(alu_transaction) ap; // monitor'un portunu disari acar
function new(string name = "alu_agent", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new("ap", this);
mon = alu_monitor::type_id::create("mon", this); // her zaman
if (get_is_active() == UVM_ACTIVE) begin // yalnizca aktifse
sqr = alu_sequencer::type_id::create("sqr", this);
drv = alu_driver::type_id::create("drv", this);
end
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
mon.ap.connect(ap); // port -> port (yukari)
if (get_is_active() == UVM_ACTIVE)
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
class alu_env extends uvm_env;
`uvm_component_utils(alu_env)
alu_agent agent; // Gun 7: + scoreboard, coverage
function new(string name = "alu_env", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = alu_agent::type_id::create("agent", this);
endfunction
endclass
// Rastgele N islem: Gun 4'teki start_item/finish_item kalibi
class alu_random_seq extends uvm_sequence #(alu_transaction);
`uvm_object_utils(alu_random_seq)
int n_items = 20;
function new(string name = "alu_random_seq");
super.new(name);
endfunction
virtual task body();
repeat (n_items) begin
req = alu_transaction::type_id::create("req");
start_item(req);
if (!req.randomize())
`uvm_error("SEQ", "randomize basarisiz")
finish_item(req);
end
endtask
endclass
class alu_base_test extends uvm_test;
`uvm_component_utils(alu_base_test)
alu_env env;
function new(string name = "alu_base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = alu_env::type_id::create("env", this);
endfunction
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
virtual task run_phase(uvm_phase phase);
alu_random_seq seq;
phase.raise_objection(this, "alu_random_seq");
seq = alu_random_seq::type_id::create("seq");
seq.start(env.agent.sqr);
#50; // son islemin sonucu bir cevrim sonra cikar; monitor gorsun (bosaltma)
phase.drop_objection(this, "alu_random_seq");
endtask
endclass
// tb_top.sv — statik dunya
`timescale 1ns/1ps
module tb_top;
import uvm_pkg::*;
import alu_pkg::*;
logic clk = 0;
always #5 clk = ~clk; // 100 MHz
alu_if aif(clk); // Ders 36
alu dut ( // Ders 36-1
.clk(aif.clk), .rst_n(aif.rst_n), .in_valid(aif.in_valid),
.operand_a(aif.operand_a), .operand_b(aif.operand_b), .opcode(aif.opcode),
.out_valid(aif.out_valid), .result(aif.result), .flags(aif.flags)
);
initial begin
// vif, run_test()'ten ONCE veritabanina konur: build_phase onu zaman 0'da okur
uvm_config_db#(virtual alu_if)::set(null, "uvm_test_top.*", "vif", aif);
run_test("alu_base_test");
end
initial begin
$dumpfile("alu_uvm.vcd");
$dumpvars(0, tb_top);
end
endmodule
Beklenen çıktı (zamanlar yaklaşıktır):
UVM_INFO @ 0: uvm_test_top.env.agent.drv [DRV] Reset basliyor
UVM_INFO @ 60: uvm_test_top.env.agent.drv [DRV] Reset tamamlandi
UVM_INFO @ 85: uvm_test_top.env.agent.mon [MON] Gozlendi: OP_ADD a=0x12 b=0x34 -> result=0x0046 flags=1000 (P C N Z)
UVM_INFO @ 105: uvm_test_top.env.agent.mon [MON] Gozlendi: OP_SUB a=0x00 b=0x01 -> result=0xffff flags=0110 (P C N Z)
...
UVM_INFO @ 485: uvm_test_top.env.agent.mon [MON] Gozlendi: OP_SHL a=0xff b=0x07 -> result=0x7f80 flags=0000 (P C N Z)
--- UVM Report Summary ---
UVM_ERROR : 0
Kodun Açıklaması
- Agent'ın kendi
ap'si:mon.ap.connect(ap)bir port'u bir port'a bağlar; UVM'de bu, alt bileşenin portunu üst bileşene "yükseltmek" için standart yoldur. Env artıkagent.ap.connect(...)der ve agent'ın içinde monitor var mı, adı ne, bilmek zorunda kalmaz. Agent'ın dış yüzü üç şeyden oluşur:sqr(stimulus girişi),ap(gözlem çıkışı),is_active. typedef uvm_sequencer #(alu_transaction) alu_sequencer: Gün 2'deki boşmy_sequencersınıfının kısa hâli; davranış eklemeyecekseniztypedefyeterlidir ve factory'ye kayıt da otomatiktir (uvm_sequencerkendi*_param_utilsmakrosunu taşır).- Sequence
start_item'da reset'i bekler: Testseq.start(sqr)der demez sequencestart_item'a girer ve driverget_next_itemçağırana kadar bloklanır; driver isereset_dut()bitmedenget_next_itemdemez. Böylece "reset sırasında stimulus sürüldü" hatası yapısal olarak imkânsızdır. #50boşaltma: Sonfinish_itemdöndüğünde paket sürülmüştür ama sonucu bir çevrim sonra çıkar; objection hemen düşerse monitor son işlemi göremez. Gün 1'dekiset_drain_timebunun daha temiz yoludur (Kendinizi Deneyin #5).set(null, "uvm_test_top.*", "vif", aif):nullbağlam "en tepeden" demektir;"uvm_test_top.*"test altındaki her bileşenin bu anahtarı görmesini sağlar. Driver ve monitorget(this, "", "vif", vif)ile kendi yollarına bakar ve eşleşir. Yol"env.*"olsaydıuvm_test_top.env.agent.drvile eşleşmezdi (desen köke göre çözülür) veNOVIFalırdınız.- Sıralama garantisi:
setilerun_test()aynıinitialbloğunda, art arda yazılmıştır. Ayrıinitialbloklarına bölerseniz hangisinin önce çalışacağı standart tarafından belirlenmez;run_testönce koşarsabuild_phaseveritabanını boş bulur. - Opcode dönüşümleri: Driver
vif.drv_cb.opcode <= tr.opcodeyazar (enum → 3-bit, örtük); monitoropcode_e'(vif.mon_cb.opcode)ile geri çevirir (3-bit → enum, açık cast). Cast olmadanopcode.name()çalışmaz.
Önemli Noktalar
uvm_config_db#(virtual alu_if)tipi her iki uçta birebir aynı olmalı:virtual alu_ifilevirtual alu_if.driverfarklı tiplerdir;setvegetfarklı yazılırsa eşleşme olmaz vegetsessizce0döner.`uvm_fatalkontrolü bu yüzden şarttır.setrun_test()'ten önce,getbuild_phase'de:run_phase'degetyapmak çalışır ama geç kalmış olur; vif gibi yapısal kaynaklarbuild_phase'de alınır.- Clocking block disiplini UVM'de de aynı: Veri sinyalleri
vif.drv_cb.x <=, bekleme@(vif.drv_cb), okumavif.mon_cb.x. Ham sinyale (vif.in_valid) yalnızca reset gibi bilinçli istisnalar için dokunun. - Monitor bağımsızdır: Driver'ın ne gönderdiğini bilmez, yalnızca pinlerde gördüğünü raporlar. Gün 7'deki scoreboard tamamen monitor'den beslenecek; bu, SystemVerilog projesindeki "driver kopyası" yaklaşımından daha güvenilirdir.
- Her gözlemde yeni nesne,
ap.write(tr)handle yayınlar: Alıcılar nesneyi saklayacaksa (scoreboard kuyruğu) kopya almak alıcının sorumluluğudur; ama monitor her seferinde taze nesne yarattığı için paylaşım sorunu çıkmaz. - Agent dış yüzü:
sqr,ap,is_active: Env ve test yalnızca bu üçüyle konuşur; agent'ın içi (driver, monitor) değişse bile üst katmanlar etkilenmez. - Pasif agent = sürücüsüz gözlemci:
is_active = UVM_PASSIVEyapıldığında yalnızca monitor kalır; aynı agent, başka bir bloğun sürdüğü arayüzü izlemek için yeniden kullanılır (Gün 2).
Sürüşün Çevrim Çevrim Akışı (Ders 39'dan Hatırlatma)
| Kenar | Driver (drv_cb, output #1) |
DUT (always_ff) |
Monitor (mon_cb, input #1) |
|---|---|---|---|
| E0 | in_valid<=1, A, B, OP |
in_valid henüz 0 |
— |
| E1 | in_valid<=0 |
in_valid=1 → result<=, out_valid<=1 |
out_valid henüz 0 |
| E2 | Sonraki paket: in_valid<=1 |
in_valid=0 → out_valid<=0, result korunur |
E2'den 1 ns önce out_valid=1, result ve operandlar okunur → ap.write |
Monitor E2 kenarında okuma yaparken driver aynı kenarda yeni operandları sürer; input #1 / output #1 skew'leri sayesinde monitor eski (kararlı) değerleri görür. Bu tablo, clocking block'ların "neden" sorusunun pratik cevabıdır.
Sık Yapılan Hatalar
setyolunu yanlış yazmak:"env.*","*.drv"gibi desenlernullbağlamla köke göre çözülür;uvm_test_top.env.agent.drvile eşleşmezseNOVIF.+UVM_CONFIG_DB_TRACEile hangiset'in hangigetile eşleştiğini izleyin.set'irun_test()'ten sonra ya da ayrıinitial'da yapmak:build_phasezaman 0'da veritabanını boş bulur. Aynıinitialbloğunda,run_test'ten önce.- Tip parametresinde modport farkı:
setvirtual alu_ifile,getvirtual alu_if.driverile → eşleşmez. Tek tipte karar kılın. - Driver'da
@(vif.drv_cb)olmadan sürmek: İlk atama saat kenarı beklemeden aynı zaman diliminde yapılır; ardışık paketler üst üste biner. Herdrive_itemsaat kenarıyla başlar. - Monitor'de ham
vif.out_validokumak: Kenar anındaki NBA güncellemeleriyle yarışır;mon_cb.out_validPreponed bölgede örneklenmiş kararlı değerdir. - Objection'ı son
finish_item'dan hemen sonra düşürmek: Monitor son sonucu göremez, Gün 7'de "sürülen ≠ gözlenen" hatası alırsınız. Boşaltma süresi (#50veyaset_drain_time) bırakın. tr'yi döngü dışında yaratmak: Gün 3'teki paylaşılan nesne tuzağı; scoreboard'un beklenen değeri monitor'ün bir sonraki örneklemesiyle değişir.- Enum cast'ini unutmak:
tr.opcode = vif.mon_cb.opcode;bazı araçlarda hata, bazılarında sessiz uyarıdır;opcode_e'(...)yazın.
Kendinizi Deneyin
tb_top'takisetyolunu"env.*"yapın. Hangi bileşen, hangi fazdaNOVIFverdi?+UVM_CONFIG_DB_TRACEile eşleşme denemelerini izleyin.setsatırını#1;gecikmeli ayrı birinitialbloğuna taşıyın. Sonuç değişti mi? Peki#0gecikmeyle? Neden bu davranışa güvenmemek gerekir?- Driver'ı back-to-back sürecek şekilde değiştirin (ikinci
@(vif.drv_cb); in_valid <= 0;adımını kaldırın). Monitor hangi operandları hangi sonuçla eşleştiriyor? Ders 41, Kendinizi Deneyin #3'teki küçük pipeline düzeltmesini UVM monitor'üne uygulayın. alu_random_seq.n_items'ı testtenuvm_config_db#(int)::set(this, "*", "n_items", 50)ile ayarlayın; sequencebody()içindeuvm_config_db#(int)::get(m_sequencer, "", "n_items", n_items)ile okusun. Neden bağlam olarakthisdeğilm_sequencerverildi?#50'yi kaldırıpphase.phase_done.set_drain_time(this, 50);kullanın (raise_objection'dan hemen sonra). Log'daki sonMONmesajı hâlâ görünüyor mu?- Agent'ı
UVM_PASSIVEyapın (uvm_config_db#(uvm_active_passive_enum)::set(this, "env.agent", "is_active", UVM_PASSIVE)testinbuild_phase'ine). Testseq.start(env.agent.sqr)satırında ne hata veriyor? Pasif agent'lı bir testinrun_phase'i nasıl olmalı?
Hızlı Kontrol: uvm_config_db#(virtual alu_if)::set(...) neden run_test()'ten önce çağrılmalıdır?
Çünkü run_test() tüm fazları başlatır ve build_phase simülasyonun 0. anında, run_test çağrısının içinde çalışır. Driver ve monitor vif'i build_phase'de get eder; o anda veritabanında yoksa `uvm_fatal("NOVIF") ile simülasyon durur. set ile run_test aynı initial bloğunda art arda yazıldığında sıralama garanti altındadır.
UVM Gün 6: Sıkça Sorulan Sorular (FAQ)
Soru 1: Driver neden doğrudan tb_top.aif.drv_cb.in_valid <= 1 yazmıyor? Hiyerarşik yol daha basit değil mi?
Cevap: Çalışır ama iki büyük bedeli vardır. Birincisi, alu_pkg içindeki sınıf tb_top'ı tanımak zorunda kalır; aynı driver başka bir testbench'te (soc_tb.alu_inst.aif) derlenemez. İkincisi, paket modülden bağımsız derlenmek istenir; paketten modül hiyerarşisine referans vermek derleme sırasını ve yeniden kullanımı bozar. Virtual interface, "hangi interface" kararını tb_top'a bırakır; sınıf yalnızca "bir alu_if" bilir.
Soru 2: Modport'lu virtual interface (virtual alu_if.driver) kullanmalı mıyım?
Cevap: Güvenlik açısından caziptir: monitor'e virtual alu_if.monitor verirseniz sürmeye çalışan satır derlenmez. Bedeli, uvm_config_db tip parametresinin her bileşen için farklı olmasıdır (set iki kez, iki tiple). Bu derste tek tip (virtual alu_if) taşındı ve pasiflik mon_cb'nin tümü-input olmasıyla sağlandı. Ekip standardınız hangisiyse ona uyun; karıştırmayın.
Soru 3: Reset'i neden driver yapıyor? Test ya da bir "reset agent" yapsa daha doğru olmaz mı?
Cevap: Küçük bir ortamda driver'ın run_phase başında reset yapması en az kodla en güvenli yoldur: stimulus reset bitmeden sürülemez. Birden fazla agent'ın aynı reset'e bağlı olduğu sistemlerde reset ayrı bir bileşene (reset agent) alınır ve diğer driver'lar wait (vif.rst_n === 1'b1) ile bekler; ya da run_phase'in alt fazları (reset_phase, main_phase) kullanılır. İlke değişmez: stimulus üreten herkes reset'in bittiğini bilmeli.
Soru 4: set çağrısında null bağlam ne demek; this kullanabilir miyim?
Cevap: tb_top bir modüldür, uvm_component değildir; this burada tanımsızdır. null (veya eşdeğeri uvm_root::get()) "hiyerarşinin kökünden itibaren" demektir ve inst_name deseni köke göre yazılır: "uvm_test_top.*". Test sınıfının içinden aynı işi yapsaydınız this ile "env.*" yazardınız; iki yazım aynı bileşenleri hedefler.
Soru 5: Birden fazla interface (örneğin iki ALU, iki agent) olsaydı ne değişirdi?
Cevap: Ya alan adını ayırırsınız ("vif_a", "vif_b") ya da yolu daraltırsınız: set(null, "uvm_test_top.env.agent_a.*", "vif", aif_a) ve set(null, "uvm_test_top.env.agent_b.*", "vif", aif_b). İkinci yol daha iyidir; agent kodu "vif" anahtarını değiştirmeden iki kez kullanılır. Bu, Gün 5'teki instance override fikrinin config_db karşılığıdır: aynı sınıf, yola göre farklı kaynak.
Soru 6: Monitor'ün ap.write(tr) çağrısı zaman tüketmiyor; scoreboard uzun bir karşılaştırma yaparsa monitor yavaşlar mı?
Cevap: write() bir function'dır, alıcının write()'ı da function olmak zorundadır; zaman tüketemez ama hesaplama yapar ve bu hesap monitor'ün çağrı zincirinde koşar. Pratikte scoreboard karşılaştırmaları birkaç mikrosaniyelik yazılım işidir, simülasyon zamanı ilerlemez; monitor "yavaşlamaz". Alıcı gerçekten zaman harcamak istiyorsa (örneğin bir referans modelin task'ı) veriyi uvm_tlm_analysis_fifo'ya atıp kendi run_phase'inde işler (Gün 3, TLM port türleri tablosu; Gün 7'de uygulanacak).