UVM-10: Factory Overrides in Practice — VLSI Trainers
VLSI Trainers UVM Series · UVM-10
UVM Series · UVM-10

Factory Overrides in Practice

Practical patterns for using the UVM factory — type overrides vs instance overrides, when to apply each, the abstract/concrete component pattern for protocol-agnostic environments, sequence overrides, and command-line override plusargs.

📋 Quick Review — How the Factory Works

The factory maps registered class types to class wrappers. When type_id::create() is called, the factory checks its override table before constructing. If an override is registered, the substitute type is constructed instead. The caller receives the result via a base-class handle — polymorphism handles the rest.

Two prerequisites must be satisfied for any override to work:

  1. Both classes must be registered with the factory using `uvm_component_utils or `uvm_object_utils.
  2. The substitute class must extend the original class (directly or transitively). A factory override is not a general type substitution — it requires a valid is-a relationship.
Override must be set before create() is called. Factory overrides apply at the moment create() executes. If you register an override after the component tree has been built, it has no effect on already-created objects. Always set overrides at the very start of the test’s build_phase — before creating the env.

📋 Type Override in Practice

A type override replaces every instance of the original type with the substitute type across the entire testbench hierarchy. Use it when you want to globally change the behaviour of a component for a specific test without touching any environment source.

Type Override — All Instances of base_driver Become fast_driver my_env apb_agent_0 fast_driver ★ overridden apb_agent_1 fast_driver ★ overridden apb_agent_2 fast_driver ★ overridden base_driver::type_id::set_type_override(fast_driver::get_type()) — all three agents affected vlsitrainers.com
Figure 1 — Type override. One set_type_override() call replaces base_driver with fast_driver in every agent. All three agents are affected. The environment and agent source code is unchanged.
// ── fast_driver extends base_driver — required for override
class fast_driver extends base_driver;
  `uvm_component_utils(fast_driver)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  task run_phase(uvm_phase phase);
    // faster drive implementation
  endtask
endclass

// ── In test build_phase — BEFORE creating env ─────────────
function void build_phase(uvm_phase phase);
  super.build_phase(phase);

  // ① Register override — must be before create()
  base_driver::type_id::set_type_override(
    fast_driver::get_type());

  // Optional: replace=1 forces override even if one already exists
  base_driver::type_id::set_type_override(
    fast_driver::get_type(), 1);

  // ② Now create env — fast_driver replaces base_driver everywhere
  m_env = my_env::type_id::create("m_env", this);
endfunction

📋 Instance Override in Practice

An instance override targets a specific component by its hierarchy path. All other instances of the same type are unaffected. Use it when only one agent in a multi-agent environment needs a specialised component.

// ── Target only the APB agent's driver, leave others alone
function void build_phase(uvm_phase phase);
  super.build_phase(phase);

  // Replace ONLY the driver at this specific path
  base_driver::type_id::set_inst_override(
    error_driver::get_type(),
    "uvm_test_top.m_env.m_apb_agent.m_drv");

  // Wildcard: replace all drivers inside apb_agent subtree
  base_driver::type_id::set_inst_override(
    error_driver::get_type(),
    "uvm_test_top.m_env.m_apb_agent.*");

  m_env = my_env::type_id::create("m_env", this);
endfunction
Override typeScopeMethodPath required?
Type overrideAll instances of type TT::type_id::set_type_override(Sub::get_type())No
Instance overrideOnly the component at the specified pathT::type_id::set_inst_override(Sub::get_type(), "path")Yes — full dot-path
Instance priorityInstance overrides take priority over type overrides for matching paths

📋 Abstract / Concrete Pattern

The most powerful factory pattern: the environment is written against an abstract (do-nothing) base class. Tests activate concrete implementations via type override. This completely separates environment architecture from protocol-specific implementation.

// ── Abstract base — protocol-independent, does nothing ───
class base_driver extends uvm_driver#(bus_seq_item);
  `uvm_component_utils(base_driver)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  task run_phase(uvm_phase phase);
    // Default: raise/drop immediately — no stimulus
    phase.raise_objection(this);
    `uvm_info("DRV", "Abstract driver — no stimulus generated", UVM_LOW)
    phase.drop_objection(this);
  endtask
endclass

// ── Concrete APB implementation ───────────────────────────
class apb_driver extends base_driver;
  `uvm_component_utils(apb_driver)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  task run_phase(uvm_phase phase);
    // Full APB protocol drive implementation
    forever begin
      bus_seq_item item;
      seq_item_port.get_next_item(item);
      // ... drive APB signals ...
      seq_item_port.item_done();
    end
  endtask
endclass

// ── APB test — wires the concrete driver ──────────────────
class apb_test extends uvm_test;
  `uvm_component_utils(apb_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  function void build_phase(uvm_phase phase);
    // One line activates the concrete APB driver everywhere
    base_driver::type_id::set_type_override(apb_driver::get_type());
    m_env = my_env::type_id::create("m_env", this);
  endfunction
endclass
The abstract/concrete pattern enables parallel development. The environment team writes against abstract base classes immediately — no protocol implementation needed. Protocol teams develop concrete classes independently. Tests wire them together with a single override. Teams never block each other, and the environment stays permanently protocol-agnostic.

📋 Overriding Sequences

Sequences are objects — the same override mechanism applies. A derived test can substitute a specialised sequence for the standard one without changing the test’s run_phase structure.

// ── Base sequence for normal data transfers ───────────────
class data_seq extends uvm_sequence#(bus_seq_item);
  `uvm_object_utils(data_seq)
  function new(string name="data_seq"); super.new(name); endfunction
  task body();
    // normal write/read transfers
  endtask
endclass

// ── Derived sequence injects errors ───────────────────────
class error_seq extends data_seq;
  `uvm_object_utils(error_seq)
  function new(string name="error_seq"); super.new(name); endfunction
  task body();
    super.body();            // run normal transfers first
    inject_errors();          // then add error cases
  endtask
endclass

// ── Error test — overrides sequence, keeps run_phase ──────
class error_test extends base_test;
  `uvm_component_utils(error_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  function void build_phase(uvm_phase phase);
    // Substitute error_seq for data_seq — run_phase unchanged
    data_seq::type_id::set_type_override(error_seq::get_type());
    super.build_phase(phase);   // builds env normally
  endfunction
  // run_phase inherited from base_test — creates data_seq which is now error_seq
endclass

📋 Command-Line Overrides

The factory supports overrides directly from the simulator command line — no source code changes or recompilation required. This is the most powerful use of the factory for regression and debug.

// ── Type override from command line ──────────────────────
// +uvm_set_type_override=OriginalType,SubstituteType
// Example:
// vsim tb_top +uvm_set_type_override=base_driver,fast_driver

// ── Instance override from command line ───────────────────
// +uvm_set_inst_override=OriginalType,SubstituteType,PathString
// Example:
// vsim tb_top +uvm_set_inst_override=base_driver,error_driver,
//             uvm_test_top.m_env.m_apb_agent.m_drv

// ── Select test class from command line ───────────────────
// +UVM_TESTNAME=test_class_name
// Overrides the string argument to run_test()
// vsim tb_top +UVM_TESTNAME=apb_test

Command-line overrides apply after any in-code overrides. They allow regression scripts to sweep through component variants without modifying testbench source — the same compiled testbench can run with different drivers, sequences, or monitors.

📋 When to Use Each Override Type

ScenarioOverride typeWhy
Replace standard driver with faster version for performance regressionType overrideAll agents should use the faster driver — no path needed
Inject errors on one specific interface without affecting othersInstance overrideOnly that agent’s driver should be replaced
Activate a concrete protocol driver from an abstract baseType override in testThe whole point of abstract/concrete — one override activates everywhere
Substitute a different sequence library for error coverageType override on sequenceAll sequences of that type get the error variant
Run with a passive monitor-only agent at chip levelConfig object (is_active)Active/passive is a config flag, not a factory override — use the agent config
Switch test variant without recompilingCommand-line type overrideRegression scripts can sweep variants with no source change

📋 Factory Debug

// ── Print full factory state — call from end_of_elaboration
function void end_of_elaboration_phase(uvm_phase phase);
  uvm_factory::get().print(1);     // 1 = show overrides
  uvm_top.print_topology();         // show component tree
endfunction

// ── Debug what type would be created at a specific path ───
uvm_factory::get().debug_create_by_type(
  base_driver::get_type(),
  "uvm_test_top.m_env.m_apb_agent",
  "m_drv");

// ── Common factory debug checklist ───────────────────────
// 1. Is the substitute class registered? (`uvm_component_utils present?)
// 2. Does substitute extend original? ($cast fails if not)
// 3. Was override set before create() was called?
// 4. If instance override, is the path string exactly right?
// 5. Use +UVM_FACTORY_TRACE command-line flag for per-create logging

📋 Quick Reference

ItemKey fact
Override prerequisite 1Both classes registered with factory (`uvm_*_utils macro)
Override prerequisite 2Substitute must extend original — directly or through inheritance chain
Override must be set whenIn test’s build_phase, before creating the component that calls create()
Type override syntaxOrig::type_id::set_type_override(Sub::get_type())
Instance override syntaxOrig::type_id::set_inst_override(Sub::get_type(), "full.path")
replace argumentOptional second arg to set_type_override — 1 forces replacement of existing override
Instance override priorityInstance overrides take priority over type overrides for matching paths
Abstract/concrete purposeEnv built against abstract base; test activates concrete impl via type override
Sequence override worksYes — same set_type_override / set_inst_override API, sequences are objects
Command-line type override+uvm_set_type_override=OrigType,SubType
Command-line inst override+uvm_set_inst_override=OrigType,SubType,path
Test class from command line+UVM_TESTNAME=my_test_class
Debug: print factoryuvm_factory::get().print(1) from end_of_elaboration_phase
Debug: trace per create+UVM_FACTORY_TRACE command-line flag
Active/passive is NOT a factory overrideUse agent config’s is_active field — factory override changes the component type, not its mode
Scroll to Top