Wishbone · Module 26
UVM Concepts
You have already built a monitor, a reference model, a scoreboard and a coverage collector. UVM is a vocabulary for those roles — it supplies no protocol knowledge and no bins.
This chapter does not start UVM from the beginning. It starts from something you already have.
Chapters 26.1 through 26.5 built a complete verification environment: a protocol checker with activation counts, a passive monitor, an independent reference model, a scoreboard and a functional coverage model, all executing against a real Wishbone subsystem. UVM is a set of names and conventions for exactly those roles.
UVM does not make verification rigorous. The architecture does. What UVM supplies is a standard vocabulary, a standard component hierarchy, and reusable plumbing — so that the environment you built by hand can be assembled from parts other people recognise.
1. The Mapping
| what you built | UVM name | file |
|---|---|---|
| the transaction record | uvm_sequence_item | wb_monitor.sv fields |
the stimulus in tb_*.sv | uvm_sequence | the op() tasks |
| the request-driving logic | uvm_driver | wb_master_fsm + op() |
wb_monitor | uvm_monitor | unchanged in role |
| driver + sequencer + monitor, bundled | uvm_agent | — |
wb_refmodel + wb_scoreboard | uvm_scoreboard (+ predictor) | unchanged in role |
wb_coverage | uvm_subscriber | unchanged in role |
wb_ver_env | uvm_env | unchanged in role |
tb_env.sv | uvm_test | — |
Nine rows, and eight of them are a rename. That is the honest summary of what this chapter has to teach.
2. The Transaction Becomes A Class
Your monitor's fields, as a uvm_sequence_item:
// ── REPRESENTATIVE UVM CODE — NOT COMPILED IN THIS MODULE ───────────
// No UVM library or UVM-capable simulator is present in this
// environment. This is published as a mapping, not as evidence.
class wishbone_item extends uvm_sequence_item;
rand bit [11:0] adr;
rand bit [31:0] wdat;
rand bit [3:0] sel;
rand bit we;
bit [31:0] rdat;
term_e term; // ACK / ERR / RTY
int waits;
`uvm_object_utils_begin(wishbone_item)
`uvm_field_int(adr, UVM_ALL_ON)
`uvm_field_int(wdat, UVM_ALL_ON)
`uvm_field_int(sel, UVM_ALL_ON)
`uvm_field_int(we, UVM_ALL_ON)
`uvm_object_utils_end
endclassCompare that field list against the one you already wrote:
// adr which location sel which byte lanes
// we direction wdat what was written
// rdat what was read term ACK / ERR / RTY
// waits clocks presented unanswered - the endpoint's cost
// seq monotonic attempt number - makes ordering checkableThe same decisions, in a class instead of a port list. The hard part — deciding that term and waits must be carried — is done before UVM enters the picture.
3. The Monitor Keeps Its Rules
// ── REPRESENTATIVE UVM CODE — NOT COMPILED IN THIS MODULE ───────────
class wishbone_monitor extends uvm_monitor;
uvm_analysis_port #(wishbone_item) ap;
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
// THE SAME RULE AS wb_monitor.sv: emit on the completion event,
// never merely because STB is high.
if (vif.cyc && vif.stb && (vif.ack || vif.err || vif.rty)) begin
wishbone_item t = wishbone_item::type_id::create("t");
t.adr = vif.adr;
t.rdat = vif.dat_i; // qualified NOW - RULE 3.65
t.term = vif.rty ? RTY : (vif.err ? ERR : ACK);
ap.write(t);
end
end
endtask
endclassUVM does not know about RULE 3.60 or RULE 3.65. The emit condition and the sampling clock are exactly the decisions Chapter 26.3 made, and a UVM monitor written with
if (vif.stb)duplicates transactions in precisely the same way — for precisely the same reason.
The analysis port is the one genuinely new idea in this chapter. Your environment wired the monitor's outputs to the scoreboard and the coverage collector by hand:
wb_scoreboard #(.AW(AW),.DW(DW),.SW(SW)) u_sb (
.obs_valid_i(t_valid),.obs_adr_i(t_adr),.obs_we_i(t_we),An analysis port makes that one-to-many broadcast rather than point-to-point wiring, so a third subscriber costs nothing in the monitor.
4. Active And Passive Agents
| agent | contains | when |
|---|---|---|
| active master | sequencer + driver + monitor | you are generating the traffic |
| passive | monitor only | something else is generating it |
| active slave | sequencer + driver + monitor | you are emulating a responder |
Your environment has one of each in spirit. wb_master_fsm driven by op() is an active master agent; wb_vplan_checker, wb_monitor and wb_coverage are passive observers; and the register slave and memory are real RTL rather than emulated responders.
The passive distinction matters for a reason that is not about UVM: a passive agent can be attached to a design you did not write, in a system you do not control, without changing its behaviour. That is the same property Chapter 26.3 insisted on when it gave the monitor no outputs.
5. The Scoreboard Keeps Its Independence
// ── REPRESENTATIVE UVM CODE — NOT COMPILED IN THIS MODULE ───────────
class wishbone_scoreboard extends uvm_scoreboard;
`uvm_analysis_imp_decl(_obs)
uvm_analysis_imp_obs #(wishbone_item, wishbone_scoreboard) obs_imp;
function void write_obs(wishbone_item t);
wishbone_item exp = predictor.predict(t); // INDEPENDENT model
if (t.term != exp.term)
`uvm_error("SB", "termination class mismatch")
else if (!t.we && (t.rdat !== exp.rdat))
`uvm_error("SB", $sformatf("data: exp %0h got %0h", exp.rdat, t.rdat))
endfunction
endclassThe independence rule survives the translation unchanged:
// A scoreboard whose expected value came from the DUT is asking the
// suspect to write its own alibi. Module 25 made that point about a DMA;
// here it is architecture.UVM will happily let you write a scoreboard that reads DUT internals through a virtual interface. It has no opinion. The discipline is yours, and Chapter 26.4 showed what it buys: a design with zero protocol violations returning wrong data on four of nine transactions.
6. Coverage Becomes A Subscriber
// ── REPRESENTATIVE UVM CODE — NOT COMPILED IN THIS MODULE ───────────
class wishbone_coverage extends uvm_subscriber #(wishbone_item);
wishbone_item t;
covergroup cg;
op: coverpoint t.we { bins rd = {0}; bins wr = {1}; }
term: coverpoint t.term { bins ack = {ACK}; bins err = {ERR};
bins rty = {RTY}; }
waitc: coverpoint t.waits { bins zero = {0}; bins short = {[1:3]};
bins lng = {[4:$]}; }
selc: coverpoint t.sel { bins full = {4'hF};
bins partial = default; }
op_x_wait: cross op, waitc;
wr_x_sel: cross op, selc;
endgroup
function void write(wishbone_item t_in);
t = t_in;
cg.sample();
endfunction
endclassThat is the same bin model Chapter 26.5 built as counters, expressed in the syntax a commercial tool understands. The dimensions, the crosses and the reasons for them are identical — and so is the hard part:
covergroupwill compute a percentage for a bin model that excludes every interesting case. Choosing the bins, justifying the exclusions, and knowing that hitting a bin is not the same as being sensitive to its bug — none of that is supplied by the language.
7. What UVM Does Not Do
Correcting the misconceptions directly, against the evidence in this module:
| claim | what this module measured |
|---|---|
| "UVM is what makes verification rigorous" | the rigour is in the architecture; UVM renames it |
| "UVM replaces assertions" | it has no protocol knowledge at all — RULE 3.60 is still yours to write |
| "UVM replaces scoreboards" | uvm_scoreboard is a base class, not a comparison |
| "UVM automatically provides functional coverage" | covergroup is a language feature; the bins are yours |
| "a UVM monitor can emit whenever STB is high" | it duplicates exactly as DUPLICATE_MONITOR did — 15 transactions for 5 completions |
| "UVM code is verified because it is UVM" | verification code is software; Chapter 26.4's REF_IGNORES_SEL was a testbench bug with a DUT-bug symptom |
8. Sequence To Pins, And Back
The one structural thing UVM adds that your hand-built environment did not have is a separation between deciding what to send and knowing how to send it.
sequence "write 0x1122_3344 to offset 1, lane 1 only"
↓ sequence_item
sequencer arbitrates between sequences
↓
driver drives CYC, STB, ADR, SEL, WE and holds them
↓ still until a termination arrives (RULE 3.60)
Wishbone pins
↓
DUT
↓
monitor reconstructs on the completion event
↓ analysis port
scoreboard + coverageIn tb_env.sv those first three roles are one op() task plus wb_master_fsm:
task automatic op(input logic we, input [AW-1:0] a,
input [DW-1:0] d, input [SW-1:0] s);Splitting them buys reuse: the same driver serves any sequence, and the same sequence runs against any driver implementing the same item. It costs indirection, which is why a teaching environment is clearer without it and a production one is not.
The driver inherits an obligation the sequence knows nothing about. RULE 3.60 requires the request to be held still until answered — a sequence says what to transfer, and the driver is where "and do not move it while waiting" lives. A driver that re-drove its outputs from a fresh item each clock would reproduce Chapter 26.2's MOVING_REQUEST exactly.
9. Why The Analysis Port Is Not Just Wiring
Your environment connected the monitor to the scoreboard with a bundle of signals:
.obs_valid_i(t_valid),.obs_adr_i(t_adr),.obs_we_i(t_we),
.obs_rdat_i(t_rdat),.obs_term_i(t_term),Adding the coverage collector meant adding another bundle. An analysis port makes that a broadcast — one publisher, any number of subscribers, none of which the publisher knows about. Adding a third consumer changes no existing code.
It also changes what is communicated: a transaction object rather than a set of concurrent signals, so the subscriber has no timing relationship with the monitor at all. That is the "transaction-level" in TLM, and it is the reason a scoreboard can be written without knowing how many clocks anything took.
10. Honest Scope
// ── REPRESENTATIVE UVM CODE — NOT COMPILED IN THIS MODULE ───────────
// No UVM library or UVM-capable simulator is present in this
// environment. This is published as a mapping, not as evidence.None of the UVM code in this chapter was compiled or executed. The executable proof of this module is Chapters 26.1–26.5, which ran twelve simulations against real RTL in a real simulator. This chapter maps that proof onto UVM's vocabulary and stops there.
Deliberately out of scope, and owned by the dedicated UVM curriculum:
- the factory and type overrides
uvm_config_db- the phase mechanism beyond
run_phase - objections and end-of-test
- virtual sequences and sequence arbitration
- the register abstraction layer (RAL)
Six pages do not make anyone a UVM expert, and this chapter does not pretend otherwise. What it should leave you with is the opposite of intimidation: the components have names you now recognise, because you built every one of them before you learned what UVM calls it.
11. Where Module 26 Ends
Five verification components, twelve simulations, eight defects and a discrimination matrix in which every defect was caught by the layer the plan nominated and the other layers stayed quiet:
intended detections 8 of 8
missed intended detections 0
unexpected protocol detections 0
false positives (correct rig) 0The single most transferable result is the one this module opened with and closed with: four of those eight defects — two in the design, one in the monitor, one in the reference model — kept the bus perfectly legal. A conformance suite passes all four.
Module 27 takes up debugging: what to do when one of these layers fires and you do not yet know which model is wrong.
Continue learning
Related tutorials
- Related topic
UVM AXI Agent Overview
Assemble the driver, sequencer, and monitor into a reusable UVM AXI agent — the standard unit that bundles one interface's verification, switchable between active (drive + observe) and passive (observe-only), with an analysis port to scoreboard and coverage. The reuse boundary that makes AXI verification composable across environments.
- Related topic
A Reusable UVM Ethernet Agent
Module 20's four components share no key, so the agent is where one lives; and every modulus in the coverage model is the beat width, so the cross moves 128x between 10 and 100 Gb/s.
- Related topic
CPU to Peripheral Communication
A CPU reaches hardware outside itself by reading and writing addressed locations, and a peripheral is hardware it cannot execute. Everything a driver does has to be expressed as a read or a write of a location the peripheral answers for — and once more than a couple of peripherals exist, wiring each one to the core separately stops scaling. That is the problem an on-chip bus is the answer to.
- Related topic
Memory-Mapped IO
Memory-mapped I/O does not turn a peripheral into memory. It gives the peripheral's registers addresses in the processor's address space, so an ordinary load or store selects them. The address then does two jobs — name the target, name the register inside it — and the map that assigns them is a contract between software and RTL.
Standards & specifications
- Governing standard
- Wishbone SoC Interconnection Architecture (OpenCores)(opens OpenCores in a new tab)
Defines the Wishbone signal set, the bus cycles built from it and the interface rules a portable IP core must follow. It deliberately leaves interconnect topology, address map and arbitration policy to the integrator, so those are system decisions rather than requirements of the specification.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the Wishbone curriculum.
