In short: the UVM driver is the only active, pin-wiggling component that translates abstract sequence items into real DUT signal activity, while the monitor is a passive observer that reconstructs those same transactions from the pins and broadcasts them out through a TLM analysis port to scoreboards and coverage.
Every UVM testbench divides its work along one sharp line: who is allowed to drive the DUT, and who is only allowed to watch it. Get that division wrong and you build a testbench that reports passing coverage while quietly steering the design away from the very corners it was supposed to find. The driver and the monitor sit on opposite sides of that line, and understanding the asymmetry between them is one of the most common things a verification lead will probe in an interview.
Two roles, one interface, opposite directions
A UVM agent typically bundles three components around a single virtual interface handle: a sequencer that arbitrates stimulus, a driver that consumes it, and a monitor that reports on it. The driver and monitor both point at the same virtual interface, but they touch it in mirror-image ways. The driver assigns to interface signals (it wiggles pins); the monitor only samples them (it observes). This is why a well-written monitor never contains a single procedural assignment to a DUT input — if it did, you would have two drivers fighting for the same wire and an X on the bus.
Inside the driver: the get–drive–done handshake
The driver's run_phase is a forever loop around three calls: seq_item_port.get_next_item(req) blocks until the sequencer hands it a transaction, the driver spends however many clocks it needs to realize that transaction on the pins, then seq_item_port.item_done() unblocks the sequence. That handshake is the backbone of UVM flow control — the sequence cannot race ahead of the pins because it is held at finish_item until item_done fires.
Timing lives entirely in the driver. A good AXI or APB driver aligns every assignment to a clocking block (@(vif.cb)) so that setup and hold are respected against the DUT's sampling edge. A classic bug is driving on the active edge instead of through a clocking block, which passes in simulation but hides real hold violations that only Synopsys VCS or Cadence Xcelium gate-level runs later expose.
The driver is the only component that should assign to DUT inputs. If you ever need a second source of stimulus, add it to the sequence layer — never a second driver on the same interface.
Inside the monitor: reconstruct, don't drive
The monitor runs its own independent run_phase loop, continuously sampling the interface, detecting protocol events (a valid+ready handshake, a chip-select falling edge, a completed burst), and packing what it saw back into a fresh transaction object. Crucially it must work even when no driver is present — in a passive agent (UVM_PASSIVE), or when the DUT talks to another real block, the monitor is all you have. That is why the monitor reconstructs transactions from pins rather than copying the driver's req: copying would make the checker blind to exactly the bugs it exists to catch.
Once a transaction is assembled, the monitor publishes it. It owns a uvm_analysis_port and calls ap.write(txn). This is a non-blocking, fire-and-forget broadcast — the monitor does not know or care how many subscribers are listening. For a deeper look at how stimulus is generated upstream, see our companion piece on the UVM sequence and sequencer.
TLM ports vs analysis ports — the distinction interviewers love
UVM's transaction-level modeling (TLM) connections come in two flavors, and conflating them is a giveaway that someone has only read tutorials.
| Aspect | Peer-to-peer TLM (put/get) | Analysis port |
|---|---|---|
| Class | uvm_blocking_put_port, uvm_seq_item_pull_port | uvm_analysis_port / uvm_analysis_imp |
| Fan-out | Exactly one connection | Zero to many subscribers |
| Blocking? | Can block (handshake/flow control) | Never blocks — write() returns immediately |
| Used by | Sequencer ↔ driver | Monitor → scoreboard, coverage |
The driver-to-sequencer link uses blocking pull ports precisely because it needs back-pressure. The monitor-to-world link uses analysis ports precisely because it must not. If a scoreboard stalls, you never want the monitor (and therefore observation of the DUT) to stall with it.
One monitor can feed a scoreboard and a functional-coverage collector and a logger simultaneously — all from a single ap.write(). Add a subscriber by connecting its analysis_export; no monitor change needed.
Analysis export and the TLM FIFO pattern
A subscriber receives analysis transactions in one of two ways. A coverage collector usually extends uvm_subscriber and implements write(T t) directly — it reacts instantly. A scoreboard, which often needs to hold expected and actual items and compare them later, instead drops an uvm_tlm_analysis_fifo between the port and its logic, so the monitor's non-blocking writes land in a buffer the scoreboard drains with a blocking get(). This decouples sampling rate from checking rate.
When you model a predictor-plus-comparator scoreboard, you typically instantiate two analysis FIFOs — one fed by an input-side monitor, one by an output-side monitor — and the compare loop pops one from each. This exact structure appears in nearly every protocol VIP from Siemens Questa's UVM framework to open-source AXI VIPs, and it maps cleanly onto the design-services flows that teams at Avecas use on production silicon projects.
Common pitfalls
- Monitor writing after item_done: sampling on the wrong edge can make the monitor capture a value one cycle stale; always sample through the same clocking block the driver respects.
- Connecting the analysis port in the wrong phase: all TLM
connect()calls belong inconnect_phase, neverbuild_phase, because child components must exist first. - Passive agent still driving: forgetting to guard driver/sequencer creation behind
if(is_active == UVM_ACTIVE)creates a rogue driver in a monitor-only agent.
To build these components hands-on with graded labs on real VCS and Xcelium licences, explore our verification courses, and if you are coming from an RTL background first, start with the UVM agent architecture primer.
Ready to write your first self-checking testbench? The CourseTron UVM & SystemVerilog track walks you from pin-wiggling drivers to full scoreboard closure on live EDA tools.
Frequently asked questions
Why can't the monitor just read the driver's transaction instead of the pins?
Because then the monitor would report what you intended to drive, not what the DUT actually saw. Any bug in the driver's timing or the interface would be invisible. Reconstructing from pins keeps the checker independent of the stimulus path.
Is the analysis port blocking?
No. uvm_analysis_port::write() is non-blocking and returns immediately regardless of how many subscribers are connected or what they do. Back-pressure is handled by placing a uvm_tlm_analysis_fifo in front of any subscriber that needs to buffer.
How many subscribers can one analysis port have?
Any number, including zero. That is the whole point of the analysis port versus a one-to-one TLM put/get port — you can attach scoreboards, coverage collectors and loggers to a single monitor output without ever modifying the monitor.
Where do TLM connections get wired up?
In connect_phase(). The build_phase() constructs components top-down, so ports and exports only exist and can be linked once building is complete.