Uçtan Uca ALU UVM Testbench'i: Scoreboard, Coverage ve Test Varyantları
UVM Gün 7: Bitirme Projesi | Predictor (referans model) +
uvm_tlm_analysis_fifo'lu scoreboard,check_phase/report_phase,uvm_subscriberile coverage, sequence seçen test hiyerarşisi,+UVM_TESTNAMEile regresyon, bilinçli hata enjeksiyonu
Amaç
Gün 6'da DUT'a bağlanan iskeleti bu derste kendi kendini denetleyen (self-checking) bir ortama dönüştüreceğiz: monitor'ün gözlediği her işlem bir referans modelden geçecek, scoreboard beklenen ile gözleneni eşleştirecek, coverage "yeterince denedik mi?" sorusunu ölçecek ve testler yalnızca senaryo (sequence) seçerek çoğalacak. Dersin sonunda +UVM_TESTNAME=alu_corner_test ile koşan, *** TEST PASSED *** basan ve DUT'a enjekte edilen bir hatayı yakalayan tam bir UVM testbench'iniz olacak.
Bu, SystemVerilog Gün 7 bitirme projesinin (Dersler 41–46) UVM karşılığıdır; her bileşen için oradaki dersi referans vereceğiz.
Ön Koşullar
- UVM Gün 3 (analysis port/imp,
uvm_subscriber, TLM port türleri tablosu), Gün 5 (factory, test varyantları), Gün 6 (alu_transaction,alu_driver,alu_monitor,alu_agent,tb_top). - SystemVerilog Ders 42 (referans model ve bayrak sözleşmesi), Ders 44 (test hiyerarşisi), Ders 45 (covergroup, cross,
ignore_bins).
Mimari: Gözlemden Karara
agent.ap (alu_monitor her out_valid'de bir alu_transaction yayinlar)
│
┌────────────────┼──────────────────────┐
▼ ▼ ▼
alu_predictor alu_scoreboard alu_coverage
(referans model) act_export ─▶ act_fifo (uvm_subscriber, covergroup)
expected_ap ──────▶ exp_export ─▶ exp_fifo
run_phase: exp_fifo.get / act_fifo.get / compare
check_phase: kuyruklar bos mu? report_phase: ozet
SystemVerilog projesinde scoreboard "beklenen" veriyi driver'ın kopyasından alıyordu. Burada her şey monitor'den gelir: predictor, gözlenen girişlerden beklenen çıkışı hesaplar; scoreboard iki akışı sırayla eşleştirir. Driver'a hiç güvenilmez; doğrulama tamamen pinlerde gözlenen gerçeklere dayanır (Ders 42'deki "predictor" satırı).
Lab 7.1: Predictor — Referans Model Ayrı Bir Bileşen
Görev: Ders 42'deki compute_reference()'ı bir uvm_subscriber içine taşıyın; beklenen işlemi ayrı bir analysis port'tan yayınlasın.
class alu_predictor extends uvm_subscriber #(alu_transaction);
`uvm_component_utils(alu_predictor)
uvm_analysis_port #(alu_transaction) expected_ap;
function new(string name = "alu_predictor", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
expected_ap = new("expected_ap", this);
endfunction
// uvm_subscriber: analysis_export'a gelen her gozlem icin write() cagrilir
virtual function void write(alu_transaction t);
alu_transaction exp;
if (!$cast(exp, t.clone())) // monitor'un nesnesine dokunma: once kopyala
`uvm_fatal("PRED", "clone() basarisiz")
predict(exp); // result/flags'i SPESIFIKASYONA gore doldur
expected_ap.write(exp);
endfunction
// Ders 42: compute_reference() — DUT koduna bakmadan, sozlesmeden yazilir
// Bayraklar (Ders 36-1): [0]=Z [1]=N [2]=C (yalnizca ADD/SUB) [3]=P
function void predict(alu_transaction tr);
case (tr.opcode)
OP_ADD: tr.result = {8'h00, tr.a} + {8'h00, tr.b}; // bit 8 = carry
OP_SUB: tr.result = {8'h00, tr.a} - {8'h00, tr.b}; // a<b ise 16'hFFxx (borrow)
OP_AND: tr.result = {8'h00, tr.a & tr.b};
OP_OR: tr.result = {8'h00, tr.a | tr.b};
OP_XOR: tr.result = {8'h00, tr.a ^ tr.b};
OP_NOT: tr.result = {8'h00, ~tr.a};
OP_SHL: tr.result = {8'h00, tr.a} << tr.b[2:0];
OP_SHR: tr.result = {8'h00, tr.a} >> tr.b[2:0];
default: tr.result = 16'h0000;
endcase
tr.flags = 4'b0000;
tr.flags[0] = (tr.result == 16'h0000); // Z
tr.flags[1] = tr.result[15]; // N
tr.flags[2] = (tr.opcode inside {OP_ADD, OP_SUB}) ? tr.result[8] : 1'b0; // C
tr.flags[3] = ^tr.result; // P
endfunction
endclass
Kodun Açıklaması
- Neden ayrı bileşen? Scoreboard "iki akışı eşleştir ve karşılaştır" işini yapar; bu iş protokolden bağımsızdır. "Beklenen değer nedir?" sorusu ise tamamen ALU'ya özgüdür. İkisini ayırmak, scoreboard'u başka projede değiştirmeden kullanmanızı, predictor'ı ise tek başına test etmenizi sağlar. Gerçek bir pipeline'da predictor giriş monitor'ünden, scoreboard'un
acttarafı çıkış monitor'ünden beslenir; bu ALU'da tek monitor hem girişi hem çıkışı aynı anda yakaladığı için ikisi aynı porttan gelir. t.clone()+$cast:write()'a gelent, monitor'ün yayınladığı nesnedir ve aynı anda scoreboard ile coverage'a da gider. Üzerineresultyazarsanız scoreboard'un "gözlenen" değerini de değiştirmiş olursunuz.clone()factory üzerinden yeni nesne yaratıpcopy()yapar (field makroları sayesinde elledo_copyyazmadan); dönüş tipiuvm_objectolduğu için$castgerekir.predict()birfunction: Zaman tüketmez;write()zinciri içinde çağrılabilir. Ders 42'deki uyarı aynen geçerli: model, DUT'unalways_combbloğunu kopyalayarak değil, spesifikasyondan yazılır.
Lab 7.2: Scoreboard — İki Akış, Bir Karar
Görev: Gün 3'teki "Kendinizi Deneyin #4" fikrini uygulayın: write() içinde hemen karşılaştırmak yerine iki uvm_tlm_analysis_fifo kullanın ve eşleştirmeyi run_phase'de yapın.
class alu_scoreboard extends uvm_scoreboard;
`uvm_component_utils(alu_scoreboard)
// Dis yuz: env yalnizca bu iki export'u bilir
uvm_analysis_export #(alu_transaction) exp_export;
uvm_analysis_export #(alu_transaction) act_export;
// Ic yuz: write() cagrilarini zaman tuketmeyen bir kuyruga cevirir
uvm_tlm_analysis_fifo #(alu_transaction) exp_fifo;
uvm_tlm_analysis_fifo #(alu_transaction) act_fifo;
int unsigned n_match = 0, n_mismatch = 0;
function new(string name = "alu_scoreboard", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
exp_export = new("exp_export", this);
act_export = new("act_export", this);
exp_fifo = new("exp_fifo", this);
act_fifo = new("act_fifo", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
exp_export.connect(exp_fifo.analysis_export); // export -> export: asagi dogru
act_export.connect(act_fifo.analysis_export);
endfunction
virtual task run_phase(uvm_phase phase);
alu_transaction exp, act;
forever begin
exp_fifo.get(exp); // beklenen gelene kadar bloklar
act_fifo.get(act); // gozlenen gelene kadar bloklar
compare(exp, act);
end
endtask
function void compare(alu_transaction exp, alu_transaction act);
if (act.result === exp.result && act.flags === exp.flags) begin
n_match++;
`uvm_info("SB", {"PASS ", act.convert2string()}, UVM_HIGH)
end else begin
n_mismatch++;
`uvm_error("SB", $sformatf("MISMATCH #%0d\n beklenen: %s\n gozlenen: %s",
n_mismatch, exp.convert2string(), act.convert2string()))
end
endfunction
// run_phase bitince kuyruklarda eslesmemis islem kalmamali
virtual function void check_phase(uvm_phase phase);
super.check_phase(phase);
if (exp_fifo.used() != 0 || act_fifo.used() != 0)
`uvm_error("SB", $sformatf("Eslesmemis islem kaldi: exp=%0d act=%0d",
exp_fifo.used(), act_fifo.used()))
if (n_match + n_mismatch == 0)
`uvm_error("SB", "Hic islem karsilastirilmadi: monitor veya baglantilari kontrol edin")
endfunction
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("SB", $sformatf("\n ===== SCOREBOARD =====\n Toplam : %0d\n Basarili : %0d\n Basarisiz : %0d\n ======================",
n_match + n_mismatch, n_match, n_mismatch), UVM_NONE)
endfunction
endclass
Kodun Açıklaması
- Export → FIFO:
uvm_tlm_analysis_fifoiçinde hazır biranalysis_exporttaşır; gelenwrite()çağrısı veriyi kuyruğa koyar ve hemen döner. Scoreboard dışarıya kendiexp_export/act_export'unu sunar ve bunları FIFO'ların export'larına bağlar. Env, scoreboard'un içinde FIFO olduğunu bilmek zorunda değildir; yarın kuyruk tabanlı (out-of-order) bir eşleştirmeye geçerseniz env değişmez. run_phase'deget:write()birfunction'dır, bloklayamaz; iki akışın "ikisi de geldiğinde karşılaştır" mantığı ancak bloklayangetile doğal yazılır. SystemVerilog projesindekidrv2scb.get(expected); mon2scb.get(actual);kalıbının (Ders 42) birebir UVM karşılığı budur.===ile karşılaştırma:result/flagslogicolduğu içinXyakalanır.exp.compare(act)(field makrolarının ürettiği karşılaştırma) burada kullanılmadı; çünkü girişleri de karşılaştırır ve girişler her iki nesnede zaten aynıdır; anlamlı olan yalnızca çıkışlardır. Alternatifler FAQ'da.check_phase: Objection erken düşerse (boşaltma süresi yoksa)exp_fifo'da bir öğe kalır; bu kontrol "son işlem hiç karşılaştırılmadı" durumunu görünür kılar. İkinci kontrol, bağlantı unutulduğunda testin sessizce geçmesini engeller: hiç karşılaştırma yapılmamış bir test geçmiş sayılmaz.report_phase'de özet,UVM_NONEile: Verbosity ne olursa olsun basılır; regresyon betikleri bu bloğu okuyabilir.
Lab 7.3: Coverage — uvm_subscriber ile Kapsam
Görev: Ders 45'teki covergroup'u sadeleştirerek bir subscriber'a taşıyın.
class alu_coverage extends uvm_subscriber #(alu_transaction);
`uvm_component_utils(alu_coverage)
covergroup cg with function sample(opcode_e op, bit [7:0] a, bit [7:0] b, logic [3:0] flags);
option.per_instance = 1;
cp_op: coverpoint op; // enum: 8 otomatik bin
cp_a: coverpoint a {
bins zero = {8'h00}; bins one = {8'h01}; bins mid = {8'h7F, 8'h80};
bins max = {8'hFF}; bins other = default;
}
cp_b: coverpoint b {
bins zero = {8'h00}; bins one = {8'h01}; bins mid = {8'h7F, 8'h80};
bins max = {8'hFF}; bins other = default;
}
cp_flags: coverpoint flags {
bins none = {4'b0000}; bins z = {4'b0001}; bins n = {4'b0010};
bins c = {4'b0100}; bins p = {4'b1000}; bins multi = default;
}
cx_op_a: cross cp_op, cp_a;
cx_op_b: cross cp_op, cp_b {
// Kaydirmada b, c_shift_range ile 0-7'ye kisitli: mid/max hic gelemez -> kapsam disi
ignore_bins shift_big = binsof(cp_op) intersect {OP_SHL, OP_SHR} &&
binsof(cp_b) intersect {[8:255]};
}
endgroup
function new(string name = "alu_coverage", uvm_component parent = null);
super.new(name, parent);
cg = new(); // covergroup elle yaratilir (Gun 3)
endfunction
virtual function void write(alu_transaction t);
cg.sample(t.opcode, t.a, t.b, t.flags);
endfunction
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("COV", $sformatf("Fonksiyonel kapsam (alu_coverage): %0.2f%%", cg.get_inst_coverage()), UVM_NONE)
endfunction
endclass
Kodun Açıklaması
with function sample(...): Gün 3'teki handle tabanlı toplayıcının tekniği, ama tetikleyici TLM (write()). İki yaklaşım birleşti: port üzerinden bağımsızlık, argümanlısampleile sınıf üyesi değişkenlere bağımlılıktan kurtulma.cp_op: coverpoint op;Enum tipli coverpoint'te her enum değeri otomatik bin olur; 8 satır yazmaya gerek yok.ignore_binsgerekçeli:OP_SHL/OP_SHRileb ≥ 8kısıt gereği hiç üretilemez; bu çaprazları saymak kapsamı asla %100'e ulaşmayacak biçimde şişirirdi. Ders 34-1'deki kural: her exclusion'ın bir gerekçesi olmalı; burada gerekçec_shift_range'dir.defaultbin'ler çaprazlara girmez:other/multiyalnızca coverpoint'in kendi yüzdesini anlamlı tutar;crossyalnızca adlandırılmış bin'leri çaprazlar.
Lab 7.4: Environment — Dört Alıcı, Tek Yayın
class alu_env extends uvm_env;
`uvm_component_utils(alu_env)
alu_agent agent;
alu_predictor pred;
alu_scoreboard sb;
alu_coverage cov;
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);
pred = alu_predictor::type_id::create("pred", this);
sb = alu_scoreboard::type_id::create("sb", this);
cov = alu_coverage::type_id::create("cov", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent.ap.connect(pred.analysis_export); // gozlem -> referans model
agent.ap.connect(sb.act_export); // gozlem -> scoreboard (gercek)
agent.ap.connect(cov.analysis_export); // gozlem -> kapsam
pred.expected_ap.connect(sb.exp_export); // beklenen -> scoreboard
endfunction
endclass
Kodun Açıklaması
agent.apüç kezconnectedilir: Gün 3'teki broadcast davranışı;mon.ap.write(tr)tek çağrıyla predictor, scoreboard ve coverage'ınwrite()'larını sırayla tetikler. Env, monitor'ü hiç görmez; agent'ınap'si yeterlidir (Gün 6).- Çağrı sırası ve FIFO düzeni: Aynı
writezinciri içinde önce predictor çalışır (beklenenexp_fifo'ya girer), sonraact_fifodolar. Scoreboardget'leri sırayla okuduğu için her beklenen doğru gözlenenle eşleşir. Bağlantı sırası değişse bile her iki FIFO aynı gözlem için birer öğe alır; eşleşme bozulmaz. - Predictor ve coverage
uvm_subscriber: İkisinin de tek girişi var;analysis_exporthazır geldiği için ek port tanımlanmadı. Scoreboard'un iki girişi olduğu için export'ları kendisi tanımladı.
Lab 7.5: Sequence'ler ve Test Hiyerarşisi
Görev: Gün 6'daki alu_random_seq'e Ders 38'deki köşe tarayıcıyı ekleyin; testler yalnızca hangi sequence'in koşacağını seçsin.
// Ders 38: ALU_Corner_Generator — her kose degeri her islemle caprazlar (240 islem)
class alu_corner_seq extends uvm_sequence #(alu_transaction);
`uvm_object_utils(alu_corner_seq)
bit [7:0] corners[] = '{8'h00, 8'h01, 8'h7F, 8'h80, 8'hFE, 8'hFF};
function new(string name = "alu_corner_seq");
super.new(name);
endfunction
virtual task body();
foreach (corners[i]) begin
foreach (corners[j]) begin
for (int op = 0; op < 8; op++) begin
opcode_e o = opcode_e'(op);
if (o inside {OP_SHL, OP_SHR} && corners[j] > 7) continue; // c_shift_range ile celisir
req = alu_transaction::type_id::create("req");
start_item(req);
if (!req.randomize() with { opcode == o; a == corners[i]; b == corners[j]; })
`uvm_error("SEQ", "randomize basarisiz")
finish_item(req);
end
end
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
// KANCA: turetilmis testler yalnizca bunu ezer (Ders 44'teki configure_env fikri)
virtual function uvm_sequence #(alu_transaction) make_sequence();
alu_random_seq seq = alu_random_seq::type_id::create("seq");
return seq;
endfunction
virtual task run_phase(uvm_phase phase);
uvm_sequence #(alu_transaction) seq = make_sequence();
phase.raise_objection(this, get_type_name());
phase.phase_done.set_drain_time(this, 50); // son islemin sonucu pinlere ciksin (Gun 1 ek bolumu)
seq.start(env.agent.sqr);
phase.drop_objection(this, get_type_name());
endtask
// Gecti/kaldi karari: hata sayacina bakilir, scoreboard'a degil
virtual function void report_phase(uvm_phase phase);
uvm_report_server svr = uvm_report_server::get_server();
super.report_phase(phase);
if (svr.get_severity_count(UVM_ERROR) == 0 && svr.get_severity_count(UVM_FATAL) == 0)
`uvm_info("TEST", "*** TEST PASSED ***", UVM_NONE)
else
`uvm_info("TEST", $sformatf("*** TEST FAILED *** (%0d UVM_ERROR)", svr.get_severity_count(UVM_ERROR)), UVM_NONE)
endfunction
endclass
class alu_random_test extends alu_base_test;
`uvm_component_utils(alu_random_test)
function new(string name = "alu_random_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function uvm_sequence #(alu_transaction) make_sequence();
alu_random_seq seq = alu_random_seq::type_id::create("seq");
seq.n_items = 200;
return seq;
endfunction
endclass
class alu_corner_test extends alu_base_test;
`uvm_component_utils(alu_corner_test)
function new(string name = "alu_corner_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function uvm_sequence #(alu_transaction) make_sequence();
return alu_corner_seq::type_id::create("seq");
endfunction
endclass
tb_top Gün 6'dakiyle aynıdır; yalnızca run_test("alu_base_test") yerine argümansız run_test() yazın ve testi komut satırından seçin:
+UVM_TESTNAME=alu_random_test # 200 rastgele islem
+UVM_TESTNAME=alu_corner_test # 240 yonlendirilmis kose islemi
Kodun Açıklaması
make_sequence()kancası: Ders 44'teALU_Corner_Test::run()taban akışı kopyalıyordu ve bunu "kod kokusu" olarak işaretlemiştik. Burada tabanrun_phasesabittir; türetilmiş test yalnızca sequence'i seçer. Yeni bir senaryo = yeni sequence + 10 satırlık test sınıfı.- Dönüş tipi
uvm_sequence #(alu_transaction): Her iki sequence de bundan türediği için kanca tek tipte dönebilir;seq.start(sqr)polimorfik çalışır (SystemVerilog Ders 14). set_drain_time(this, 50): Objection sıfıra düştükten sonra UVM 50 birim daha bekler; sonfinish_itemdöndüğünde DUT sonucu henüz pinlerde değildir, monitor bu süre içinde görür. Gün 6'daki#50'nin standart karşılığıdır.report_phase'de hata sayacı:*** TEST PASSED ***kararı scoreboard'un sayaçlarına değil UVM hata sayacına bakar:check_phase'deki "kuyrukta öğe kaldı", driver'ınNOVIF'i, sequence'inrandomizehatası, hepsiUVM_ERROR/UVM_FATALüretir ve testi düşürür. Regresyon betiği tek bir satırı arar.alu_corner_seq240 işlem üretir: 6 × 6 × 8 = 288 kombinasyondan kaydırma +b > 7olan 48'i atlanır (Ders 38'deki hesap).randomize() withile değerler sabitlenir ama sınıf kısıtları yine denetlenir;continueolmasaydıc_shift_rangeile çelişen kombinasyonlarrandomize'ı düşürürdü.
Çalıştırma ve Beklenen Çıktı
+UVM_TESTNAME=alu_corner_test ile (zamanlar yaklaşıktır):
UVM_INFO @ 0: reporter [RNTST] Running test alu_corner_test...
UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology: (portlar kisaltildi)
uvm_test_top alu_corner_test
env alu_env
agent alu_agent
ap uvm_analysis_port
drv alu_driver
mon alu_monitor
sqr uvm_sequencer #(REQ,RSP)
cov alu_coverage
pred alu_predictor
sb alu_scoreboard
act_export uvm_analysis_export
act_fifo uvm_tlm_analysis_fifo #(T)
exp_export uvm_analysis_export
exp_fifo uvm_tlm_analysis_fifo #(T)
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=0x00 b=0x00 -> result=0x0000 flags=0001 (P C N Z)
...
UVM_INFO @ 4915: uvm_test_top.env.sb [SB]
===== SCOREBOARD =====
Toplam : 240
Basarili : 240
Basarisiz : 0
======================
UVM_INFO @ 4915: uvm_test_top.env.cov [COV] Fonksiyonel kapsam (alu_coverage): 100.00%
UVM_INFO @ 4915: uvm_test_top [TEST] *** TEST PASSED ***
--- UVM Report Summary ---
UVM_ERROR : 0
UVM_FATAL : 0
Rastgele test 200 işlemle genellikle %100'e ulaşmaz (köşe çaprazlarının bir kısmı eksik kalır); bu, Ders 34-1'deki "tek test nadiren her şeyi kapatır, veritabanları birleştirilir" dersinin canlı örneğidir.
Hata Enjeksiyonu: Scoreboard Gerçekten Çalışıyor mu?
Hiç hata bulmamış bir scoreboard'a güvenmeyin. Ders 36-1, Kendinizi Deneyin #2'deki hatayı DUT'a enjekte edin: OP_SUB: next_result = operand_b - operand_a;. alu_corner_test artık şunu basar:
UVM_ERROR @ 2545: uvm_test_top.env.sb [SB] MISMATCH #1
beklenen: OP_SUB a=0x00 b=0x01 -> result=0xffff flags=0110 (P C N Z)
gozlenen: OP_SUB a=0x00 b=0x01 -> result=0x0001 flags=1000 (P C N Z)
...
Toplam : 240
Basarili : 210
Basarisiz : 30
UVM_INFO @ 4915: uvm_test_top [TEST] *** TEST FAILED *** (30 UVM_ERROR)
Neden 30? SUB için 36 köşe çifti vardır; a == b olan 6'sında b - a == a - b olduğundan hata görünmez. Bir hatanın bazı girdilerde gizlenebildiğini görmek, "testler geçti" cümlesine neden şüpheyle bakmanız gerektiğini anlatır. Hatayı geri alın ve testin tekrar geçtiğini doğrulayın.
Küçük Bir Regresyon
for t in alu_random_test alu_corner_test; do
<simulator> ... +UVM_TESTNAME=$t > $t.log 2>&1
grep -q "TEST PASSED" $t.log && echo "$t: PASS" || echo "$t: FAIL"
done
Ders 46'daki +TEST= betiğinin UVM karşılığı: test seçimi case bloğuyla değil factory'yle yapılır (Gün 5), karar *** TEST PASSED *** satırından okunur.
Dosya Yapısı
alu_pkg.sv package alu_pkg; import uvm_pkg::*; `include "uvm_macros.svh"
├─ alu_transaction.sv Gun 6
├─ alu_driver.sv Gun 6
├─ alu_monitor.sv Gun 6
├─ alu_agent.sv Gun 6
├─ alu_predictor.sv Gun 7
├─ alu_scoreboard.sv Gun 7
├─ alu_coverage.sv Gun 7
├─ alu_env.sv Gun 7 (guncellendi)
├─ alu_sequences.sv Gun 6 + Gun 7
└─ alu_tests.sv Gun 7
alu_if.sv, alu.sv SystemVerilog projesi (Ders 36, 36-1), degismedi
tb_top.sv Gun 6; run_test() argumansiz
`include sırası bağımlılık sırasıdır (transaction herkesten önce, env scoreboard'dan sonra, testler en sonda); paket tek derleme birimi olduğu için Ders 46'daki "önce tanımla, sonra kullan" kuralı aynen geçerlidir.
SystemVerilog Projesi ile Son Karşılaştırma
| SV projesi (Dersler 41–46) | UVM (Gün 6–7) | Kazanım |
|---|---|---|
ALU_Generator::run() + -> done |
alu_random_seq::body(); bitiş start() dönüşünde |
Senaryolar birleştirilebilir, yeniden kullanılabilir |
gen2drv / drv2scb / mon2scb mailbox'ları |
seq_item_port + analysis port/FIFO |
1→N yayın, el sıkışma, zaman tüketmeyen write() |
| Scoreboard driver kopyasından beslenir | Predictor monitor'den beslenir | Doğrulama tamamen gözleme dayanır |
env.run(): fork … join_any + wait zinciri |
run_phase paralel, objection + drain time |
Bitiş mantığı standart, bileşen sayısından bağımsız |
env.report() → scb.report(), cov.report() |
report_phase her bileşende otomatik |
Rapor sırası ve "unutulan rapor" sorunu yok |
+TEST= + case |
+UVM_TESTNAME + factory |
Test eklemek için tb_top değişmez |
env.gen = corner_gen (Ders 44) |
make_sequence() kancası veya factory override |
Env'in içine dokunulmaz |
$display("[Scoreboard] HATA") |
`uvm_error → hata sayacı → TEST FAILED |
Geçti/kaldı kararı makine tarafından okunur |
Önemli Noktalar
- Scoreboard monitor'den beslenir, driver'dan değil: Driver'ın "ne gönderdiğini sandığı" ile pinlerde olan arasındaki fark, tam da yakalamak istediğiniz hatadır.
- Predictor ayrı, scoreboard genel: "Beklenen nedir?" protokole özgü; "eşleştir ve karşılaştır" değildir. Ayrım, her iki parçanın yeniden kullanımını sağlar.
write()içinde kopyala, zaman tüketme: Nesneyi değiştireceksenizclone(); bloklamanız gerekiyorsa FIFO +run_phase.check_phasesessiz başarıyı engeller: "Kuyrukta öğe kaldı" ve "hiç karşılaştırma yapılmadı" kontrolleri olmadan, bağlantısı unutulmuş bir scoreboard testi geçirir.- Geçti/kaldı = hata sayacı:
uvm_report_servertüm bileşenlerinUVM_ERROR/UVM_FATAL'larını toplar; test kararı tek yerden, tek satırla verilir. ignore_binsgerekçe ister: Kısıt gereği üretilemeyen kombinasyonları çaprazdan düşürün; "ulaşamadım" diye düşürmeyin (Ders 34-1).- Hata enjeksiyonu rutin olmalı: Scoreboard'un bir hatayı gerçekten yakaladığını görmeden ortam "bitti" sayılmaz.
- Test = senaryo seçimi: Sequence kancası ya da factory override; env ve agent'a dokunmadan her yeni senaryo bir test sınıfıdır.
Sık Yapılan Hatalar
- Predictor'da
clone()yapmamak: Monitor'ün nesnesineresultyazılır; scoreboard "gözlenen"i beklenenle aynı nesneden okur, her şey geçer. Hata enjeksiyonu testi bunu anında ortaya çıkarır. - FIFO'ları
type_id::createile yaratmaya çalışmak: FIFO ve port'larnew("ad", this)ile yaratılır (Gün 3);createkullanılırsa parametreli tip için factory kaydı gerekir ve gereksizdir. exp_export'u FIFO'ya bağlamayı unutmak:connect_phasesonunda UVM "export bağlı değil" uyarısı verir veexp_fifohiç dolmaz; scoreboard ilkget'te sonsuza kadar bekler.check_phase'deki "hiç karşılaştırılmadı" hatası bunu görünür kılar.- Objection'ı boşaltmadan düşürmek: Son
expgelir,actgelmez;check_phaseexp=1 act=0basar.set_drain_timeekleyin. - Field makrolarının
compare()'ını körü körüne kullanmak: Girişleri de karşılaştırır; bu projede zararsız ama gürültülü, başka projede yanlış pozitif kaynağıdır. Neyi karşılaştırdığınızı bilin (FAQ #3). cp_op: coverpoint opiçinbinsyazıp enum değerlerini unutmak: Enum coverpoint'inde otomatik bin'ler yeterlidir; elle bin yazarken bir değeri atlamak kapsamı sessizce düşürür.run_test()'i argümansız bırakıp+UVM_TESTNAMEvermemek: Hiçbir test yaratılmaz; UVM "no components instantiated" fatal'ıyla durur. Regresyon betiği her zaman test adını geçmeli.
Kendinizi Deneyin
- Predictor'daki
$cast(exp, t.clone())satırınıexp = t;yapın vealu_corner_test'i koşun. Geçti mi? ŞimdiSUBhatasını enjekte edip tekrar koşun. Scoreboard neden hiçbir şey yakalamıyor? set_drain_timesatırını silin.check_phasene basıyor?*** TEST FAILED ***görünüyor mu? Neden bu, "kaçırılmış bir hata"dan daha iyidir?alu_env::connect_phase'deagent.ap.connect(sb.act_export)satırını silin. Hangi faz, hangi mesajla sizi uyarıyor? Testin sonucu ne?- Gün 5 tekniğiyle bir
alu_narrow_transactionyazın (constraint c_narrow { a inside {[0:15]}; b inside {[0:15]}; }) ve yalnızca override yapan biralu_narrow_testekleyin. Kapsam yüzdesi nasıl değişti? Hangi bin'ler boş kaldı? - Scoreboard'u sırasız (out-of-order) hâle getirin: beklenenleri
alu_transaction exp_q[$]kuyruğunda tutun, gözlenen geldiğindea,b,opcodeüçlüsü eşleşen ilk öğeyifind_first_indexile bulup çıkarın. Hangi durumda bu yapı zorunludur (Ders 42 tablosu)? alu_coverage::report_phase'ecg.cp_op.get_inst_coverage()gibi coverpoint bazlı yüzdeler ekleyin; rastgele testte en düşük hangi coverpoint?alu_random_test'i 50 kez farklı seed ile koşup (+ntb_random_seed=/-sv_seed/+UVM_...aracınıza göre) kapsam veritabanlarını birleştirin. Köşe testi olmadan %100'e kaç koşuda ulaşıyorsunuz?
Hızlı Kontrol: Scoreboard'un check_phase'i neden "hiç karşılaştırma yapılmadı" diye hata verir; sıfır hata zaten iyi değil mi?
Sıfır hata ile sıfır karşılaştırma farklı şeylerdir. Bağlantısı unutulmuş, monitor'ü hiç out_valid görmemiş ya da objection'ı anında düşmüş bir ortamda scoreboard hiç çalışmaz ve hata sayacı sıfır kalır; test geçer. check_phase bu "sessiz başarı"yı açık bir hataya çevirir: bir doğrulama ortamı, hiçbir şeyi denetlemediğini de raporlamalıdır.
UVM Gün 7: Sıkça Sorulan Sorular (FAQ)
Soru 1: Predictor'ı scoreboard'un içine bir predict() fonksiyonu olarak koysam olmaz mıydı?
Cevap: Olurdu ve küçük projelerde yaygındır (Ders 42'de böyle yaptık). Ayırmanın iki getirisi vardır: scoreboard protokolden bağımsız kalır (iki FIFO + karşılaştır, her projede aynı) ve gerçek tasarımlarda giriş/çıkış monitor'leri ayrı olduğunda predictor doğal olarak giriş tarafına bağlanır. Tek monitor'lü bu ALU'da ayrım biraz "fazla" görünebilir; alışkanlık, büyük projede karşılığını verir.
Soru 2: Neden write() içinde hemen karşılaştırmak yerine FIFO + run_phase kullanıldı?
Cevap: Burada iki akış var: beklenen (predictor'dan) ve gözlenen (monitor'den). write() tek bir akışın tek bir öğesini görür; "ikisi de geldi mi?" sorusunu cevaplayamaz, üstelik bloklayamaz. FIFO, write()'ı kuyruğa koymaya indirger; run_phase'deki bloklayan get çifti ise eşleştirmeyi doğal biçimde yazdırır. Gün 3'te tek akışta write() içinde karşılaştırmak yeterliydi; iki akış olunca FIFO standarttır.
Soru 3: exp.compare(act) kullansam ne olurdu?
Cevap: Field makroları compare()'ı tüm UVM_ALL_ON alanlar için üretir: opcode, a, b, result, flags. Girişler iki nesnede zaten aynı olduğu için sonuç yalnızca çıkışlara bağlı olurdu; yani çalışırdı. Üç alternatif: (1) girişleri UVM_ALL_ON | UVM_NOCOMPARE ile işaretlemek; (2) do_compare(uvm_object rhs, uvm_comparer comparer) yazıp yalnızca çıkışları karşılaştırmak (Gün 2 ek bölümündeki do_* kalıbı); (3) bu derste yapıldığı gibi alanları elle === ile karşılaştırmak. Üçüncüsü en şeffaf, ikincisi büyük transaction'larda en ölçeklenebilir olanıdır. compare()'ın == kullandığını, X'i yakalamak için uvm_comparer'ın ayarlanması gerektiğini de unutmayın.
Soru 4: clone() ile copy() arasındaki fark nedir?
Cevap: copy(rhs) mevcut bir nesnenin alanlarını rhs'den doldurur; clone() ise factory üzerinden yeni bir nesne yaratıp ona copy() yapar ve uvm_object döndürür. Predictor elinde boş bir nesne olmadığı için clone() kullandı; $cast gerekliliği de bu yüzdendir. İkisi de field makrolarından (ya da elle yazılmış do_copy'den) beslenir; uvm_field_* yazmadıysanız copy() hiçbir alanı kopyalamaz.
Soru 5: Coverage'ı neden Gün 3'teki gibi handle ile değil subscriber ile bağladık?
Cevap: İkisi de geçerlidir. Burada tüm alıcılar (predictor, scoreboard, coverage) aynı agent.ap'den beslendiği için bir tane daha connect satırı en tutarlı yoldu; monitor'ün coverage'ı tanıması gerekmedi. Handle tabanlı yaklaşım, örneklemenin bileşen içinde birden çok noktadan ve farklı argümanlarla yapılması gerektiğinde (örneğin driver'ın iç durumunu örneklemek) tercih edilir.
Soru 6: +UVM_TESTNAME ile Gün 5'teki factory override aynı mekanizma mı?
Cevap: Aynı factory, iki farklı kapı. +UVM_TESTNAME=alu_corner_test, run_test()'in factory'den adıyla hangi test sınıfını yaratacağını söyler (create_component_by_name). Override ise "X istenince Y ver" kuralıdır. İkisini birleştirebilirsiniz: +UVM_TESTNAME=alu_random_test +uvm_set_type_override=alu_transaction,alu_narrow_transaction tek derlemeyle yepyeni bir senaryo üretir; regresyonların "tek derleme, yüzlerce koşu" ilkesi buna dayanır.