UVM Foundations

What UVM is, the factory, phasing, config DB, and your first sequence item and sequence.

01 · What UVM is

The Universal Verification Methodology

UVM is a SystemVerilog class library and set of conventions for building reusable verification components. Before UVM, every team re-invented its own testbench architecture. UVM standardized that architecture so an agent written for one project can drop into another.

UVM is not a language feature. It's a library: you import it, extend its base classes, and rely on its factory and phasing machinery.

UVM is plumbing
UVM gives you the plumbing — factory, phasing, config DB, TLM ports. You still have to write the actual verification logic (drivers, monitors, checkers, coverage).

The three base classes

Almost everything in UVM inherits from one of three base classes:

  • uvm_object — data containers (transactions, configs, sequences).
  • uvm_component — long-lived actors in the hierarchy (drivers, monitors, scoreboards, agents, env, tests).
  • uvm_transaction — legacy; in modern UVM use uvm_sequence_item.
02 · The factory

Registration and type substitution

UVM's factory is a type registry. You register every class, and the factory constructs instances by type name — which means you can replace any type with a subtype from outside the class, without editing the class.

factory.svREGISTRATION
class axi_driver extends uvm_driver#(axi_txn);
  `uvm_component_utils(axi_driver)   // register with factory

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

class axi_txn extends uvm_sequence_item;
  `uvm_object_utils(axi_txn)         // register data type
endclass

Use uvm_component_utils for components (anything with a parent in the test hierarchy). Use uvm_object_utils for data objects (transactions, sequences, configs).

Type overrides

From a test, you can tell the factory to create a subclass wherever the base class was requested:

override.svTYPE OVERRIDE
class error_test extends base_test;
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    axi_txn::type_id::set_type_override(bad_axi_txn::get_type());
    // now every axi_txn created is actually a bad_axi_txn
  endfunction
endclass
03 · Phasing

The build/run schedule UVM runs for you

UVM runs every component through a sequence of phases. You override the phase methods you care about; UVM calls them in order.

PhaseUse for
build_phaseCreate child components. Get config from uvm_config_db.
connect_phaseConnect TLM ports and exports.
end_of_elaborationFinal checks before simulation starts.
run_phaseThe simulation itself. Tasks here consume time.
extract / check / reportPost-sim analysis and pass/fail reporting.
Top-down build, bottom-up connect
UVM calls build_phase top-down — parents build their children first. It calls connect_phase bottom-up — leaves connect first, then parents wire them together.
04 · Config DB

Passing config from top to deep children

The uvm_config_db is a keyed registry for runtime configuration. A test sets a value under a scope path; any component in that scope can get it during build.

config_db.svVIRTUAL INTERFACE
// In top-level tb module:
initial begin
  uvm_config_db#(virtual axi_lite_if)::set(
    null, "uvm_test_top.env.agent*", "vif", dut_if);
  run_test("base_test");
end

// In the agent's build_phase:
if (!uvm_config_db#(virtual axi_lite_if)::get(
      this, "", "vif", vif))
  `uvm_fatal("AGENT", "no virtual interface supplied")

The most common use is handing a virtual interface from the top-level module (where the actual interface is instanced) down to the driver and monitor deep inside the env.

05 · Sequence items

The unit of transaction-level stimulus

A uvm_sequence_item is a data class carrying everything the driver needs to perform one transaction. It's a bag of rand fields plus the methods UVM needs (convert2string, do_copy, do_compare).

axi_txn.svSEQUENCE ITEM
class axi_txn extends uvm_sequence_item;
  rand bit[31:0] addr;
  rand bit[31:0] data;
  rand bit          is_write;
  axi_resp_e      resp;

  `uvm_object_utils_begin(axi_txn)
    `uvm_field_int(addr,     UVM_ALL_ON)
    `uvm_field_int(data,     UVM_ALL_ON)
    `uvm_field_int(is_write, UVM_ALL_ON)
    `uvm_field_enum(axi_resp_e, resp, UVM_ALL_ON)
  `uvm_object_utils_end

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

The uvm_field_* macros generate copy, compare, pack, print for free. Heavy macro magic but effective.

06 · Your first sequence

A sequence drives items at a sequencer

A sequence is a stream of sequence items. It runs on a sequencer, which hands items to the driver.

basic_seq.svSEQUENCE
class axi_basic_seq extends uvm_sequence#(axi_txn);
  `uvm_object_utils(axi_basic_seq)

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

  task body();
    repeat (10) begin
      `uvm_do_with(req, { req.is_write == 1; req.addr[1:0] == 0; })
    end
  endtask
endclass

The `uvm_do_with macro creates, randomizes with the given inline constraint, sends to the driver, and waits for completion — all in one line.