Advanced UVM

Virtual sequences, the register model, objections, callbacks, and running on three simulators.

01 · Virtual sequences

Coordinating multiple agents

A regular sequence runs on one sequencer, driving one agent. A virtual sequence runs on a virtual sequencer and coordinates multiple real sequencers — essential when your test needs synchronized stimulus across interfaces (e.g., configure via APB, then traffic via AXI).

virtual_seq.svVIRTUAL SEQUENCE
class v_sequencer extends uvm_sequencer;
  axi_sequencer axi_seqr;
  apb_sequencer apb_seqr;
  `uvm_component_utils(v_sequencer)
endclass

class config_then_traffic_seq extends uvm_sequence;
  `uvm_object_utils(config_then_traffic_seq)
  `uvm_declare_p_sequencer(v_sequencer)

  task body();
    apb_config_seq cfg = apb_config_seq::type_id::create("cfg");
    axi_traffic_seq tr = axi_traffic_seq::type_id::create("tr");
    cfg.start(p_sequencer.apb_seqr);     // configure first
    tr.start(p_sequencer.axi_seqr);      // then traffic
  endtask
endclass
Use virtual sequences from day one
Even if you only have one agent today, start with a virtual sequence pattern. Adding a second agent later becomes trivial — you don't rewrite every test.
02 · Register model

uvm_reg, adapter, predictor

The UVM register model mirrors the DUT's register map in SystemVerilog. You get typed field access (regmodel.status.error.read()) instead of manual bus transactions, plus shadow registers, mirror/desired value tracking, and built-in access tests.

  • uvm_reg_field — a bit range inside a register.
  • uvm_reg — a single addressable register.
  • uvm_reg_block — a collection of registers and sub-blocks.
  • uvm_reg_map — maps registers to an address space.
  • uvm_reg_adapter — translates register operations to bus transactions.
  • uvm_reg_predictor — updates mirrored values from observed bus traffic.
reg_connect.svENV.CONNECT_PHASE
function void connect_phase(uvm_phase phase);
  adapter   = axi_reg_adapter::type_id::create("adapter");
  predictor = uvm_reg_predictor#(axi_txn)::type_id::create("pred", this);

  regmodel.default_map.set_sequencer(agent.seqr, adapter);
  predictor.map     = regmodel.default_map;
  predictor.adapter = adapter;
  agent.mon.ap.connect(predictor.bus_in);
endfunction
03 · Objections and end-of-test

When does run_phase finish?

UVM's run_phase ends when no component has an active objection. A test raises an objection when it has work pending and drops it when that work is done. The simulator doesn't exit until the objection count is zero.

objections.svTEST
class smoke_test extends base_test;
  `uvm_component_utils(smoke_test)

  task run_phase(uvm_phase phase);
    axi_basic_seq seq;
    phase.raise_objection(this);     // hold the phase open
    seq = axi_basic_seq::type_id::create("seq");
    seq.start(env.agent.seqr);
    phase.phase_done.set_drain_time(this, 100);  // let residuals flush
    phase.drop_objection(this);
  endtask
endclass
Drain time matters
Bus transactions in flight when objection drops can leave orphaned callbacks. set_drain_time asks UVM to wait a bit longer before actually ending the phase — long enough for the last expected response to arrive.
04 · Callbacks

Injecting behavior without subclassing

UVM callbacks let you hook into a component's behavior from outside. Register a callback class, and the target calls it at well-defined points — without the target having to know about the callback's existence.

Common use: error injection. A test registers a callback that corrupts 5% of outgoing transactions, without rewriting the driver.

callback.svCALLBACK
class axi_driver_cb extends uvm_callback;
  virtual function void pre_drive(axi_txn t); endfunction
endclass

class corrupt_cb extends axi_driver_cb;
  function void pre_drive(axi_txn t);
    if ($urandom_range(0,99) < 5) t.data ^= 32'h1;
  endfunction
endclass

// In driver.run_phase:
`uvm_do_callbacks(axi_driver, axi_driver_cb, pre_drive(req))
05 · Running the simulation

VCS, Xcelium, Questa

UVM code is simulator-agnostic, but each tool has its own command-line conventions.

MakefileTHREE VENDORS
# Synopsys VCS
vcs -sverilog -ntb_opts uvm-1.2 -full64 \
    -timescale=1ns/1ps +define+UVM_NO_DPI \
    -l compile.log tb_top.sv
./simv +UVM_TESTNAME=smoke_test -l run.log

# Cadence Xcelium
xrun -sv -uvm -timescale 1ns/1ps \
     tb_top.sv +UVM_TESTNAME=smoke_test -l xrun.log

# Siemens Questa
vlog -sv +incdir+$UVM_HOME/src $UVM_HOME/src/uvm_pkg.sv tb_top.sv
vsim -c -do "run -all; quit" tb_top \
     +UVM_TESTNAME=smoke_test -l questa.log

Control verbosity with +UVM_VERBOSITY=UVM_MEDIUM. Dial individual components up/down with +uvm_set_verbosity=env.agent.drv,_ALL_,UVM_HIGH,time.