UART · Module 16
UART UVM Environment Architecture
How agent, scoreboard, reference model, coverage and configuration fit together for a serial asynchronous interface, what each UVM class replaces from the directed environment, and what the methodology does not change.
Modules 14 and 15 built a complete verification environment: a driver, a monitor, a reference model, a scoreboard, property checkers and a coverage model. Every one of them works, every one was mutation-tested, and none of them is a UVM class.
So the first question this module has to answer honestly is what UVM adds — and the answer is not "checking". The checking does not change at all. What changes is that the roles become reusable, composable and configurable, and that matters at a scale this UART has not yet reached.
1. The Shape
Every box already exists in this curriculum, built as a module:
| UVM component | What it replaces | Built in |
|---|---|---|
uvm_sequence_item | a set of task arguments | 14.2 §1 |
uvm_sequence | a for loop in a test | 14.2 §5 |
uvm_sequencer | nothing — there was only one stimulus source | new |
uvm_driver | uart_line_driver's send_frame task | 14.2 |
uvm_monitor | uart_line_monitor | 14.4 |
uvm_analysis_port | a hierarchical reference into the monitor's buffer | new |
| reference model | uart_predictor | 14.5 |
uvm_scoreboard | a comparison loop | 14.5 |
| coverage collector | uart_cov | 15.3 |
uvm_config_db | module parameters and task arguments | new |
Three rows say "new", and those three are the whole of what UVM adds. A sequencer, so more than one stimulus source can share a driver. An analysis port, so a monitor can broadcast without knowing who is listening. A configuration database, so the test can reach into a component it did not build.
Everything else is a rename.
2. What the Three New Mechanisms Are For
The sequencer: more than one source of stimulus
The directed environment had exactly one. A test called send_frame and that was the only thing driving the wire, so no arbitration was needed and none existed.
The moment two things want to drive — a traffic sequence and an error-injection sequence, or a UART sequence and the register sequence from Module 13 — something must decide who goes next, and must hold the winner's item in front of the driver until the driver is finished with it. That is a sequencer, and Chapter 16.3 builds one that runs.
The analysis port: broadcast without coupling
In Module 14 the scoreboard read the monitor's buffer through a hierarchical reference — mon.q_data[i]. That works and it welds the two together: the scoreboard knows the monitor's internal array, and adding a second consumer means a second piece of code reaching into the same place.
An analysis port inverts it. The monitor calls write() and does not know or care who receives it. Chapter 16.4 builds one that runs, and measures the property that makes it work — and the one that makes it dangerous.
The configuration database: reaching past a boundary
A module's parameters are fixed at elaboration and its task arguments are supplied by whoever calls it. Neither lets a test configure a component three levels down that it did not instantiate.
// In the test: set it before the environment is built.
uvm_config_db#(uart_cfg)::set(this, "env.m_agent.*", "cfg", m_cfg);
// In the driver's build_phase: get it, without knowing who set it.
if (!uvm_config_db#(uart_cfg)::get(this, "", "cfg", m_cfg))
`uvm_fatal("NOCFG", "no uart_cfg for this agent")3. The Configuration Object
One object, passed by handle, holding everything the environment needs to know. It is worth being deliberate about what goes in it.
class uart_cfg extends uvm_object;
`uvm_object_utils(uart_cfg)
// ---- what the DUT is configured to do -----------------------------
rand int unsigned data_bits; // 5..9
rand int unsigned parity_mode; // none / even / odd / mark
rand int unsigned stop_halves; // 2 = 1 bit, 3 = 1.5, 4 = 2
// ---- what the ENVIRONMENT should do about it ----------------------
real nominal_tbit_ns = 8680.5556;
bit is_active = 1; // drive, or only observe
bit checks_enable = 1;
bit coverage_enable = 1;
constraint c_legal { data_bits inside {[5:9]};
parity_mode inside {[0:3]};
stop_halves inside {[2:4]}; }
function new(string name = "uart_cfg");
super.new(name);
endfunction
endclassThe two halves of that class are different kinds of thing and it is worth knowing which is which. The first three fields describe the device: they must match how the DUT is programmed, and a mismatch is a testbench bug that will present as a protocol error. The last four describe the environment: they change what the testbench does and cannot make the DUT wrong.
checks_enable and coverage_enable exist because the same agent is reused in contexts where its checks are somebody else's responsibility — a full-chip test where the UART is incidental traffic should not be re-verifying the UART.
4. What UVM Does Not Change
Worth stating plainly, because the methodology is often sold as though it did.
It does not improve the checking. The scoreboard still compares against a reference model written from the specification; the monitor still has to bring its own bit period; the properties of Module 15 are the same properties. A UVM environment with a monitor that reads the DUT's oversample tick is exactly as blind as a directed one that does.
It does not tell you what to test. Chapter 14.1's requirement list is methodology-independent. uvm_do_with makes a corner cheap to express; it has no opinion about which corners exist.
It does not find the bugs Module 15 found. Four of that module's six property bugs were about when a signal is sampled, and no amount of class structure prevents an off-by-one in a temporal claim.
And it does not reduce the amount of code. For a single-agent UART testbench it increases it substantially. The return comes from reuse — a second agent, a second test, a second project — and on a testbench that will only ever be used once, it is a cost with no payback.
5. The Toolchain Reality
Every other module in this curriculum publishes code that was compiled and executed. This module cannot do that for the UVM classes, and the reason is worth setting out precisely rather than hand-waving.
There is no uvm_pkg on this machine. And a hand-rolled substitute is not possible either — Icarus Verilog 13.0 lacks the mechanisms UVM is built from. Each was probed individually rather than assumed:
=== UVM MECHANISM AVAILABILITY — Icarus Verilog 13.0, probed individually ===
mechanism result
-------------------------------- ------
classes, methods, new() WORKS
int / logic queues WORKS
virtual method dispatch BROKEN - base handle calls BASE
queue of class handles REFUSED - 'not yet supported'
mailbox #(T) REFUSED - syntax error
$cast REFUSED - not defined
parameterised class #(type T) REFUSED - syntax error
uvm_pkg on this machine ABSENTThe third line is the one that ends it. A base handle pointing at a derived object calls the base method:
class b; virtual function string s(); return "BASE"; endfunction endclass
class d extends b; virtual function string s(); return "DERIVED"; endfunction endclass
// ...
o = new(); h = o; // base handle, derived object
$display("%s", h.s()); // prints BASEEvery one of those is load-bearing:
| UVM mechanism | Depends on |
|---|---|
| the factory and every override | $cast, parameterised classes, virtual dispatch |
| TLM and analysis ports | parameterised classes |
| the sequencer | mailboxes or class-handle queues |
| phasing | virtual dispatch |
VHDL has no UVM at all; its equivalents are OSVVM and UVVM, neither installed here.
6. Understanding Check
7. Summary
Seven of the ten components in a UVM UART environment are renames of things Modules 14 and 15 already built and verified.
Three are new, and they are what UVM adds: a sequencer so more than one source can share a driver, an analysis port so a monitor can broadcast without coupling, and a configuration database so a test can reach past a boundary it did not build.
is_active is the flag that makes an agent worth building — and it only works because the monitor depends on nothing but the wire.
A failed uvm_config_db::get must be fatal, because the database cannot distinguish a typo from a subtree that has not been built yet.
UVM does not improve the checking, choose the corners, or reduce the code. On a testbench used once it is a cost with no payback; the return is reuse.
And on this toolchain the UVM classes cannot run — no uvm_pkg, and Icarus lacks $cast, parameterised classes, mailboxes, class-handle queues and working virtual dispatch, each probed individually. The mechanisms UVM is built from are published as running code instead.
8. What Comes Next
Chapter 16.2 writes the transaction: the fields, the constraints that keep a random frame legal, and the ones that let a sequence ask for an illegal frame on purpose.
Browse the full path on the UART tutorials index. For the environment this one reorganises, read back to Chapter 14.5.
Continue learning
Related tutorials
- Related topic
Monitor, Agent and Active/Passive Reuse
Recovering frames independently of the driver, the analysis port that broadcasts without coupling — built as running code with its dangerous property measured — and the flag that makes one agent serve two testbenches.
- 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
Active and Passive Agents
Three agents on one bus. A structurally passive agent reports every transaction and is high impedance on every cycle measured; a flag-gated one is indistinguishable from it until a single gate is missing, at which point it corrupts the bus while still declared passive.
- Related topic
A Reusable UVM SPI Agent
Three arbitration policies deliver identical items and identical functional coverage, and completely different traffic: round robin switches between sequences 23 times and strict priority once. A coverage report cannot tell you which one ran, so it cannot tell you whether two sequences ever overlapped.
Where this fits
Part of the UART curriculum.
