Skip to content
VLSI Mentor

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

A hierarchy diagram of a UVM testbench. At the top is the test, which builds and configures everything below it. Beneath the test is the environment, which contains the agent together with the scoreboard and the coverage collector. Inside the agent are three components: the sequencer, which arbitrates between sequences and hands transactions to the driver; the driver, which converts each transaction into activity on the interface; and the monitor, which observes the interface and reports what it saw. The scoreboard and coverage collector both receive transactions from the monitor through analysis ports, and neither drives anything.sqruvm_sequencerdrvuvm_drivermonuvm_monitoragentuvm_agentsbuvm_scoreboardcovuvm_subscriberenvuvm_envtest_baseuvm_test
Figure 1 — the canonical UVM hierarchy, which is what the environment of Modules 14 and 15 already was. The test configures; the environment holds the agent and the checkers; the agent holds the sequencer, driver and monitor. Nothing here is specific to UART — that is the point of drawing it before anything is written.

Every box already exists in this curriculum, built as a module:

UVM componentWhat it replacesBuilt in
uvm_sequence_itema set of task arguments14.2 §1
uvm_sequencea for loop in a test14.2 §5
uvm_sequencernothing — there was only one stimulus sourcenew
uvm_driveruart_line_driver's send_frame task14.2
uvm_monitoruart_line_monitor14.4
uvm_analysis_porta hierarchical reference into the monitor's buffernew
reference modeluart_predictor14.5
uvm_scoreboarda comparison loop14.5
coverage collectoruart_cov15.3
uvm_config_dbmodule parameters and task argumentsnew

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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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
endclass

The 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== 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          ABSENT

The third line is the one that ends it. A base handle pointing at a derived object calls the base method:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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 BASE

Every one of those is load-bearing:

UVM mechanismDepends on
the factory and every override$cast, parameterised classes, virtual dispatch
TLM and analysis portsparameterised classes
the sequencermailboxes or class-handle queues
phasingvirtual 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

Where this fits

Part of the UART curriculum.