HomeProblemsTheoryUVM
Intermediate3 min readChapter 7

Drivers & Monitors

Implement UVM drivers that convert transactions to pin-level signals, and monitors that observe the interface.

Drivers & Monitors

Drivers and monitors are the bridge between the transaction-level UVM world and the pin-level RTL world.


The Driver

The driver takes sequence items from the sequencer and drives them onto the DUT's interface.

systemverilog
class apb_driver extends uvm_driver #(apb_txn); `uvm_component_utils(apb_driver) virtual apb_if vif; // Handle to the DUT interface function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) `uvm_fatal("NOVIF", "Virtual interface not found") endfunction task run_phase(uvm_phase phase); apb_txn req; // Initialize all signals to idle vif.PSEL <= 0; vif.PENABLE <= 0; vif.PWRITE <= 0; forever begin seq_item_port.get_next_item(req); drive_transfer(req); seq_item_port.item_done(); end endtask task drive_transfer(apb_txn txn); // ── SETUP Phase ── @(posedge vif.PCLK); vif.PSEL <= 1; vif.PADDR <= txn.addr; vif.PWRITE <= txn.write; if (txn.write) vif.PWDATA <= txn.data; // ── ACCESS Phase ── @(posedge vif.PCLK); vif.PENABLE <= 1; // Wait for slave ready do @(posedge vif.PCLK); while (!vif.PREADY); // Capture read data if (!txn.write) txn.data = vif.PRDATA; // Return to idle vif.PSEL <= 0; vif.PENABLE <= 0; endtask endclass

The Monitor

The monitor passively observes the interface. It never drives signals. It collects transactions and broadcasts them via an analysis port.

systemverilog
class apb_monitor extends uvm_monitor; `uvm_component_utils(apb_monitor) virtual apb_if vif; uvm_analysis_port #(apb_txn) ap; // Broadcast port function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); ap = new("ap", this); if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) `uvm_fatal("NOVIF", "Virtual interface not found") endfunction task run_phase(uvm_phase phase); apb_txn txn; forever begin txn = apb_txn::type_id::create("txn"); // Wait for a transaction to start @(posedge vif.PCLK); wait(vif.PSEL); // Capture address and direction txn.addr = vif.PADDR; txn.write = vif.PWRITE; if (vif.PWRITE) txn.data = vif.PWDATA; // Wait for completion @(posedge vif.PCLK); wait(vif.PENABLE && vif.PREADY); if (!vif.PWRITE) txn.data = vif.PRDATA; // Broadcast to all subscribers ap.write(txn); `uvm_info("MON", txn.convert2string(), UVM_HIGH) end endtask endclass

Driver vs Monitor — Comparison

AspectDriverMonitor
DirectionDrives signals to DUTReads signals from DUT
Active/PassiveOnly in active agentsIn all agents
Base classuvm_driveruvm_monitor
CommunicationGets items from sequencerBroadcasts via analysis port
Signal handlingAssigns (drives)Samples (reads only)

The Virtual Interface

Both driver and monitor access DUT signals through a virtual interface:

systemverilog
interface apb_if(input logic PCLK); logic PSEL; logic PENABLE; logic PWRITE; logic [31:0] PADDR; logic [31:0] PWDATA; logic [31:0] PRDATA; logic PREADY; endinterface

It's set in the top module and retrieved via uvm_config_db:

systemverilog
// Top module: module tb_top; logic clk; apb_if vif(clk); initial begin uvm_config_db#(virtual apb_if)::set( null, "uvm_test_top.env.agent*", "vif", vif ); run_test(); end endmodule

Best Practices

  1. Deassert signals between transactions in the driver — prevents carry-over.
  2. Monitors never drive signals — they are passive observers only.
  3. Use analysis ports in monitors — enables multiple subscribers.
  4. Always check for the virtual interface\uvm_fatal if not found.
  5. Handle reset gracefully — both driver and monitor should reset cleanly.