SV-05

UVM Basics —
Universal Verification Methodology

EcrioniX · SystemVerilog Series· ~20 min read· 6 Code Examples
UVM Component Hierarchy
uvm_test uvm_env uvm_agent (ACTIVE) driver + sequencer + monitor uvm_agent (PASSIVE) monitor only uvm_driver uvm_seqr uvm_monitor uvm_scoreboard DUT (RTL)

What is UVM?

UVM (Universal Verification Methodology) is a standardized SystemVerilog class library (IEEE 1800.2) adopted across all major EDA tools and semiconductor companies. It provides a reusable framework so that verification components (drivers, monitors, scoreboards) written for one project can be plugged into another with minimal change.

UVM is built on three pillars:

1. UVM Phases

PhasePurposeNotes
build_phaseCreate all sub-components using factoryTop-down: parent builds before children
connect_phaseWire TLM ports between componentsBottom-up: children connect before parents
start_of_simulation_phasePrint topology, configureRarely overridden
run_phaseDrive stimulus, collect responses (time-consuming)The only phase that advances simulation time
extract_phaseCollect final resultsPost-run
check_phaseVerify no pending expected transactionsAfter extract
report_phasePrint coverage, pass/fail summaryLast phase

2. The `uvm_component_utils Macro

Every UVM component must register itself with the factory using `uvm_component_utils (for components) or `uvm_object_utils (for transaction objects like sequence items):

SystemVerilog — UVM Transaction (Sequence Item)
class apb_txn extends uvm_sequence_item;
  // Factory registration — required for create() and factory overrides
  `uvm_object_utils_begin(apb_txn)
    `uvm_field_int(addr,  UVM_ALL_ON)
    `uvm_field_int(data,  UVM_ALL_ON)
    `uvm_field_int(write, UVM_ALL_ON)
  `uvm_object_utils_end

  rand bit [11:0] addr;
  rand bit [31:0] data;
  rand bit         write;

  function new(string name = "apb_txn");
    super.new(name);
  endfunction

  constraint c_addr { addr[1:0] == 0; }   // word-aligned
endclass

3. Driver — Driving Transactions on the Interface

SystemVerilog — UVM Driver
class apb_driver extends uvm_driver #(apb_txn);
  `uvm_component_utils(apb_driver)

  virtual apb_if vif;   // virtual interface to DUT

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // Get virtual interface from config_db
    if (!uvm_config_db #(virtual apb_if)::get(this, "", "vif", vif))
      `uvm_fatal("NO_VIF", "Virtual interface not set")
  endfunction

  task run_phase(uvm_phase phase);
    apb_txn txn;
    forever begin
      seq_item_port.get_next_item(txn);   // block until sequence sends
      drive_apb(txn);                     // drive DUT interface
      seq_item_port.item_done();          // signal: done with this item
    end
  endtask

  task drive_apb(apb_txn txn);
    @(posedge vif.clk);
    vif.PSEL  = 1; vif.PENABLE = 0;
    vif.PADDR = txn.addr; vif.PWRITE = txn.write;
    vif.PWDATA = txn.data;
    @(posedge vif.clk);
    vif.PENABLE = 1;
    @(posedge vif.clk iff vif.PREADY);
    vif.PSEL = 0; vif.PENABLE = 0;
  endtask
endclass

4. Sequence and Sequencer

SystemVerilog — UVM Sequence
class apb_write_seq extends uvm_sequence #(apb_txn);
  `uvm_object_utils(apb_write_seq)

  int num_txns = 10;

  function new(string name = "apb_write_seq");
    super.new(name);
  endfunction

  task body();
    apb_txn txn;
    repeat (num_txns) begin
      // create() uses factory — allows override
      txn = apb_txn::type_id::create("txn");
      start_item(txn);             // request access to sequencer
      assert (txn.randomize() with { write == 1; });
      finish_item(txn);            // send to driver, wait for item_done
    end
  endtask
endclass

// In run_phase of uvm_test:
apb_write_seq seq = apb_write_seq::type_id::create("seq");
seq.num_txns = 50;
seq.start(env.agent.sequencer);   // run on agent's sequencer

5. Monitor — Observing the Bus

SystemVerilog — UVM Monitor
class apb_monitor extends uvm_monitor;
  `uvm_component_utils(apb_monitor)

  virtual apb_if vif;
  // TLM port — broadcasts transactions to scoreboard/coverage
  uvm_analysis_port #(apb_txn) ap;

  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("NO_VIF", "Virtual interface not found")
  endfunction

  task run_phase(uvm_phase phase);
    forever begin
      apb_txn txn = apb_txn::type_id::create("mon_txn");
      // Sample DUT signals on PENABLE + PREADY handshake
      @(posedge vif.clk iff (vif.PENABLE && vif.PREADY));
      txn.addr  = vif.PADDR;
      txn.data  = vif.PWRITE ? vif.PWDATA : vif.PRDATA;
      txn.write = vif.PWRITE;
      ap.write(txn);    // broadcast to all connected subscribers
    end
  endtask
endclass

6. Complete Testbench Structure — Top Module

SystemVerilog — UVM top module
module tb_top;
  import uvm_pkg::*;
  `include "uvm_macros.svh"
  `include "apb_txn.sv"
  `include "apb_driver.sv"
  `include "apb_monitor.sv"
  `include "apb_agent.sv"
  `include "apb_env.sv"
  `include "apb_test.sv"

  // Clock and interface
  logic clk, rst_n;
  apb_if dut_if(.clk(clk), .rst_n(rst_n));
  apb_slave dut(.apb(dut_if));

  initial clk = 0;
  always #5 clk = ~clk;
  initial begin
    rst_n = 0; #20; rst_n = 1;
  end

  initial begin
    // Pass virtual interface through config_db to all UVM components
    uvm_config_db #(virtual apb_if)::set(
      null, "uvm_test_top.*", "vif", dut_if
    );
    // Start the test — replace "apb_test" via +UVM_TESTNAME=xxx at runtime
    run_test("apb_test");
  end
endmodule

UVM Key Classes — Quick Reference

ClassExtendsRole
uvm_sequence_itemuvm_objectTransaction — the data passed between components
uvm_sequenceuvm_sequence_baseGenerate a stream of transactions (body task)
uvm_sequenceruvm_sequencer_baseArbitrate sequences and feed driver via TLM FIFO
uvm_driveruvm_componentPull items from sequencer, drive interface
uvm_monitoruvm_componentObserve interface, broadcast via uvm_analysis_port
uvm_scoreboarduvm_componentCompare expected vs actual via uvm_analysis_imp
uvm_agentuvm_componentContainer: driver + sequencer + monitor
uvm_envuvm_componentTop-level container: agents + scoreboard + coverage
uvm_testuvm_componentConfigure env, start sequences, override factory
Series complete! You've covered the full SystemVerilog verification stack — SVA, OOP, constrained-random, functional coverage, and UVM. Go to the SystemVerilog Hub to review all five topics, or explore the Interview Prep section for UVM/SV interview questions.