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.
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:
`uvm_component_utils or `uvm_object_utils.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.
// ── 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
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 type | Scope | Method | Path required? |
|---|---|---|---|
| Type override | All instances of type T | T::type_id::set_type_override(Sub::get_type()) | No |
| Instance override | Only the component at the specified path | T::type_id::set_inst_override(Sub::get_type(), "path") | Yes — full dot-path |
| Instance priority | Instance overrides take priority over type overrides for matching paths | — | — |
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
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
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.
| Scenario | Override type | Why |
|---|---|---|
| Replace standard driver with faster version for performance regression | Type override | All agents should use the faster driver — no path needed |
| Inject errors on one specific interface without affecting others | Instance override | Only that agent’s driver should be replaced |
| Activate a concrete protocol driver from an abstract base | Type override in test | The whole point of abstract/concrete — one override activates everywhere |
| Substitute a different sequence library for error coverage | Type override on sequence | All sequences of that type get the error variant |
| Run with a passive monitor-only agent at chip level | Config object (is_active) | Active/passive is a config flag, not a factory override — use the agent config |
| Switch test variant without recompiling | Command-line type override | Regression scripts can sweep variants with no source change |
// ── 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
| Item | Key fact |
|---|---|
| Override prerequisite 1 | Both classes registered with factory (`uvm_*_utils macro) |
| Override prerequisite 2 | Substitute must extend original — directly or through inheritance chain |
| Override must be set when | In test’s build_phase, before creating the component that calls create() |
| Type override syntax | Orig::type_id::set_type_override(Sub::get_type()) |
| Instance override syntax | Orig::type_id::set_inst_override(Sub::get_type(), "full.path") |
| replace argument | Optional second arg to set_type_override — 1 forces replacement of existing override |
| Instance override priority | Instance overrides take priority over type overrides for matching paths |
| Abstract/concrete purpose | Env built against abstract base; test activates concrete impl via type override |
| Sequence override works | Yes — 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 factory | uvm_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 override | Use agent config’s is_active field — factory override changes the component type, not its mode |