Part 8 · Checking & Coverage · Intermediate
Coverage Subscriber Setup
uvm_subscriber covergroup class, analysis_export wiring, and per-instance coverage.
uvm_subscriber pattern
A coverage subscriber extends uvm_subscriber#(txn_type). UVM provides the analysis_export and calls your write() function for every transaction the monitor emits. Inside write(), you sample a covergroup with the transaction fields as arguments.
Legend: [COVER] [UVM]
COVERGROUP ANATOMY
covergroup cg with function sample(bus_txn t);
│
├─ cp_dir : coverpoint t.write ← scalar field → bins
│ bins read = {0};
│ bins write = {1};
│
├─ cp_len : coverpoint t.len ← range field → named bins
│ bins single = {1};
│ bins small = {[2:4]};
│ bins large = {[5:16]};
│
└─ x_dir_len : cross cp_dir, cp_len ← combination bin
bins wr_4 = binsof(cp_dir.write) && binsof(cp_len.small);
option.per_instance = 1; ← separate coverage per subscriber instance
option.name = "bus_cov"; ← appears in coverage reportComplete bus_cov subscriber
class bus_cov extends uvm_subscriber #(bus_txn);
`uvm_component_utils(bus_cov)
bus_txn tr;
bit in_reset;
covergroup cg with function sample(bus_txn t);
option.per_instance = 1;
option.name = "bus_cov";
cp_dir : coverpoint t.write {
bins read = {0};
bins write = {1};
}
cp_len : coverpoint t.len {
bins single = {1};
bins small = {[2:4]};
bins large = {[5:16]};
}
x_dir_len : cross cp_dir, cp_len;
endgroup
function new(string name, uvm_component parent);
super.new(name, parent);
cg = new();
endfunction
function void write(bus_txn t);
if (in_reset) return;
if (t.kind != RESPONSE) return;
cg.sample(t);
endfunction
function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info("COV", $sformatf("%s coverage %0.1f%%",
get_full_name(), cg.get_inst_coverage()), UVM_LOW)
endfunction
endclassEnv wiring and per-instance coverage
connect_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);
dut_agt = bus_agent::type_id::create("dut_agt", this);
cov = bus_cov::type_id::create("cov", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// [COVER] monitor → subscriber (NOT driver)
dut_agt.mon.ap.connect(cov.analysis_export);
endfunctionCode walkthrough
extends uvm_subscriber#(bus_txn) — UVM provides analysis_export and write() dispatch.
covergroup with function sample(bus_txn t) — t is the argument passed to cg.sample(t).
option.per_instance = 1 — each subscriber instance gets its own coverage database entry.
write() gates on in_reset and t.kind — see Sampling Discipline sub-topic.
report_phase prints get_inst_coverage() — per-instance % for sign-off.
MULTI-AGENT PER-INSTANCE COVERAGE [COVER]
cpu_agt.mon.ap ──► cpu_cov.analysis_export → cpu_cov.cg (instance 1)
dma_agt.mon.ap ──► dma_cov.analysis_export → dma_cov.cg (instance 2)
option.per_instance = 1 → separate closure tracking per agent
Merge across seeds AND instances for full sign-offKey takeaways
Subscriber extends uvm_subscriber#(txn) — write() samples covergroup.
Connect monitor.ap to analysis_export — never driver.
option.per_instance = 1 for multi-agent environments.
Common pitfalls
Connecting driver seq_item_port — samples intent, not DUT behavior.
Forgetting cg = new() in constructor — null covergroup crash.
No in_reset gate — reset-phase transactions pollute closure.