Skip to content

The UVM Factory

How macros register classes, type_id::create() resolution, overrides, and the critical ordering rule.

UVM Fundamentals · Module 5

The Problem the Factory Solves

Imagine your team maintains a shared APB VIP used by ten different projects. One project needs a driver that injects errors. Another needs a driver that adds protocol-level delays. A third needs a driver that records detailed timing logs for debug. Each project needs a different apb_driver.

Without a factory, you have two choices — both bad:

  • Option A — Edit the VIP — Edit apb_agent.sv to instantiate the variant driver. Now the shared VIP is different for every project. Version control becomes a nightmare. Other projects break.
  • Option B — Fork the VIP — Copy the entire VIP into each project. Now you maintain ten copies. A bug fix in the original must be applied to all ten — manually. Teams drift out of sync.
  • Option C — Use the Factory — The shared VIP creates drivers through the factory. Each project's test registers an override before the VIP builds. The VIP code is never touched. All ten projects use the same VIP source.

The factory makes Option C possible. It decouples "what type to create" from "the code that does the creating." The VIP asks the factory for an apb_driver. The test tells the factory to give an error_inject_driver instead. The VIP never knows the difference.

What the Factory Actually Is

The UVM factory is a singleton — one instance, accessible everywhere, that holds two tables:

  • The Type Registry — Maps each registered class name ("apb_driver") to a creator wrapper (type_id). Every call to uvm_component_utils or uvm_object_utils adds an entry to this table.
  • The Override Table — Maps requested types to override types. When create() is called, the factory checks this table first. If an override is registered, it creates the override type. If not, it creates the original. REGISTRATION (static init — once per package import)uvm_component_utils(apb_driver)uvm_component_utils(apb_monitor)`uvm_object_utils(apb_seq_item)FACTORY (uvm_default_factory)TYPE REGISTRY"apb_driver" → apb_driver"apb_monitor" → apb_monitor"apb_seq_item" → apb_seq_itemOVERRIDE TABLE (empty until set)apb_driver → error_inject_driver ↑ set by test before build_phase()CREATION (runtime)apb_driver::type_id::create ("drv", this)lookupNo overrideapb_driverdefault creationOverride seterror_inject_drvoverride created Figure 1 — Factory mechanism. Registration happens once at static init time. Every create() call queries both the type registry and the override table. The caller gets whichever type the factory resolves to.

Registration — What the Macros Do Inside

The uvm_component_utils` and uvm_object_utils` macros do far more than a comment. They generate several declarations inside your class and register it with the factory at simulation startup. If you skip the macro, the factory cannot create or override your class.

SystemVerilog — what uvm_component_utils expands to (simplified)
// When you write this:
class apb_driver extends uvm_driver #(apb_seq_item);
`uvm_component_utils(apb_driver)
// ...
endclass
 
// The macro expands to approximately:
class apb_driver extends uvm_driver #(apb_seq_item);
 
// 1. Defines type_id — the factory wrapper for this class
typedef uvm_component_registry #(apb_driver, "apb_driver") type_id;
 
// 2. Defines get_type() — used by override calls
static function type_id get_type();
return type_id::get();
endfunction
 
// 3. Defines get_type_name() — used by reporting and debug
virtual function string get_type_name();
return "apb_driver";
endfunction
 
// 4. Registration happens automatically at package import time via
//    uvm_component_registry's static constructor — no code needed from you
 
// ...
endclass
 
// This is why type_id::create() and type_id::set_type_override() work:
// type_id is a concrete class that knows how to create apb_driver.
// The factory holds a reference to type_id — not to apb_driver directly.

Factory Creation — What type_id::create() Does

When you call apb_driver::type_id::create("drv", this), the following sequence executes inside the factory. Understanding this sequence explains every override behaviour you will ever encounter.

  • Call is routed to the factory singleton type_id::create() calls uvm_factory::get().create_component_by_type(type_id::get(), context_path, name, parent). The factory singleton is the same object throughout the entire simulation.
  • Factory checks the override table — instance overrides first The factory looks up the requested type (apb_driver) and the full path of the parent component. If an instance override matches the path pattern, that override type is used. Instance override always wins over type override.
  • Factory checks for a type override If no instance override matched, the factory checks whether a type override was registered for apb_driver. If yes, the override type is used. If no, the original registered type is used.
  • The resolved type's creator is called The factory calls the creator function for the resolved type. This is essentially new(name, parent) on the resolved class — which may be apb_driver or error_inject_driver depending on the override table.
  • The handle is returned as the requested base type The factory returns the created object as an apb_driver handle, even if an error_inject_driver was created. This works because the override class must extend the base class — so the assignment is type-safe. Polymorphism does the rest.

Type Override — Global Substitution

A type override tells the factory: "Every time anyone asks for apb_driver, give them error_inject_driver instead." It applies globally — every instance of that type in the entire simulation hierarchy is substituted.

SystemVerilog — complete type override example
// ── Step 1: Create the override class — it MUST extend the original ──
class error_inject_driver extends apb_driver;
`uvm_component_utils(error_inject_driver)   // must also be registered
 
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
 
// Override only the method that differs
task run_phase(uvm_phase phase);
`uvm_info("ERR_DRV", "Error injection enabled", UVM_LOW)
// inject errors on select transactions
super.run_phase(phase);
endtask
endclass
 
// ── Step 2: Register the override in your test ────────────────────────
class error_inject_test extends base_test;
`uvm_component_utils(error_inject_test)
 
function void build_phase(uvm_phase phase);
// ⚠️ CRITICAL: register override BEFORE super.build_phase()
// super.build_phase() triggers env.build_phase() → agent.build_phase()
// which calls apb_driver::type_id::create() — override must be set by then
apb_driver::type_id::set_type_override(error_inject_driver::get_type());
 
super.build_phase(phase);   // env creates drivers — now gets error_inject_driver
endfunction
endclass
 
// Result: every apb_driver instance in the entire sim is now error_inject_driver
// The agent, env, VIP — none of them were edited.

Three Rules for Type Override

#RuleConsequence if Violated
1The override class must extend the original classFactory throws a fatal error: "Override type is not a subtype of the requested type."
2The override class must have uvm_component_utils` or uvm_object_utils`The factory cannot find the override type. The override silently has no effect.
3The override must be registered before the component is created (before super.build_phase())The component was already created with the original type. The override is registered but never used.

Instance Override — Path-Specific Substitution

An instance override substitutes one specific component identified by its full hierarchy path. It is the surgical alternative to the blunt-force type override. TYPE OVERRIDE — affects ALL instanceserror_inject_drvapb_agent_0.drverror_inject_drvapb_agent_1.drverror_inject_drvapb_agent_2.drvALL 3 instances overridden — globallyapb_driver::type_id:: set_type_override( error_inject_driver:: get_type());INSTANCE OVERRIDE — one specific pathapb_driverapb_agent_0.drverror_inject_drvapb_agent_1.drv ✓apb_driverapb_agent_2.drvONLY apb_agent_1.drv overriddenapb_driver::type_id:: set_inst_override( error_inject_driver::get_type(), ".apb_agent_1.drv"); Figure 2 — Type override replaces every instance globally (left). Instance override uses a path pattern to replace exactly one specific instance (right). Wildcards () are supported in the path pattern.

SystemVerilog — instance override with path patterns and wildcards
// ── Instance override — exact path ────────────────────────────────
apb_driver::type_id::set_inst_override(
error_inject_driver::get_type(),
"uvm_test_top.env.apb_agent_1.drv"   // exact full path
);
 
// ── Instance override — wildcard path ─────────────────────────────
apb_driver::type_id::set_inst_override(
error_inject_driver::get_type(),
"*.apb_agent_1.drv"   // * matches any number of path segments
);
 
// ── Overriding all drivers in one specific agent (wildcard suffix) ─
apb_driver::type_id::set_inst_override(
error_inject_driver::get_type(),
"*.apb_agent_1.*"   // all components under apb_agent_1
);
 
// ── Alternative: use the factory singleton directly ──────────────
uvm_factory factory = uvm_factory::get();
factory.set_inst_override_by_type(
apb_driver::get_type(),
error_inject_driver::get_type(),
"uvm_test_top.env.apb_agent_1.drv"
);

The Critical Ordering Rule — Override Before Creation

This is the single most common factory mistake in production UVM code. The override must be registered before the component is created. In practice, this means: register overrides at the start of your test's build_phase(), before calling super.build_phase().

SystemVerilog — correct and wrong ordering of factory overrides
// ── Why the order matters — the build_phase cascade ─────────────────
//
//  error_inject_test.build_phase()
//    → base_test.build_phase()
//       → env.build_phase()                ← creates components HERE
//          → apb_agent.build_phase()
//             → apb_driver::type_id::create("drv", this)  ← point of creation
//
//  The override must be registered BEFORE the cascade reaches the create() call.
 
// ❌ WRONG — override registered after super.build_phase() ────────────
class error_inject_test extends base_test;
`uvm_component_utils(error_inject_test)
function void build_phase(uvm_phase phase);
super.build_phase(phase);                         // driver already created!
apb_driver::type_id::set_type_override(           // too late — ignored
error_inject_driver::get_type());
endfunction
endclass
 
// ✓ CORRECT — override registered before super.build_phase() ──────────
class error_inject_test extends base_test;
`uvm_component_utils(error_inject_test)
function void build_phase(uvm_phase phase);
apb_driver::type_id::set_type_override(           // registered first
error_inject_driver::get_type());
super.build_phase(phase);                         // cascade creates driver
// apb_driver::type_id::create() now returns error_inject_driver ✓
endfunction
endclass
 
// ── Alternative: use new() callback in new() — also correct ──────────
// Some teams register overrides in the test constructor (new()) which
// runs before build_phase(). Both approaches work — pick one and be consistent.

The Symptom of a Too-Late Override

The simulation runs without errors. The override class is registered. But ``uvm_infomessages show the original type name, not the override type name. The DUT behaves as if no override was applied. Root cause:set_type_override()was called after the component was already created. Fix: move the override call to beforesuper.build_phase()`.

Quick Reference — All Factory Methods

MethodWhere CalledWhat It Does
type_id::create("name", parent)build_phaseCreates a component through the factory. Respects all registered overrides.
type_id::create("name")AnywhereCreates an object (no parent) through the factory.
T::type_id::set_type_override(U::get_type())Before super.build_phase()Globally replaces every instance of T with U. U must extend T.
T::type_id::set_inst_override(U::get_type(), "path")Before super.build_phase()Replaces T with U only at the given hierarchy path. Wildcards allowed.
T::get_type()In override callsReturns the type handle for T. Used as the argument to override methods.
uvm_factory::get().print()start_of_simulation_phasePrints the full factory registry and override table. Essential for debugging. Covered in Module 7.
SystemVerilog — factory pattern cheat sheet
// ── Registration (in every class — never skip) ────────────────────
class my_component extends uvm_component;
`uvm_component_utils(my_component)   // structural elements
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
 
class my_object extends uvm_sequence_item;
`uvm_object_utils(my_object)        // data / transactions
function new(string name = "my_object");
super.new(name);
endfunction
endclass
 
// ── Creation (always through the factory) ─────────────────────────
my_component comp = my_component::type_id::create("comp", this);
my_object    obj  = my_object::type_id::create("obj");
 
// ── Type override (BEFORE super.build_phase) ──────────────────────
my_component::type_id::set_type_override(my_override_component::get_type());
 
// ── Instance override (BEFORE super.build_phase) ──────────────────
my_component::type_id::set_inst_override(
my_override_component::get_type(),
"uvm_test_top.env.agent.comp"   // or use wildcards: "*.agent.comp"
);
 
// ── Override priority (highest to lowest) ─────────────────────────
//   1. Instance override (most specific path wins)
//   2. Type override
//   3. Original registered type (default)

Code Examples — Factory in Action From Simple to Production

The factory is one of those mechanisms that sounds complicated until you see it working. These four examples build progressively — from the minimal registration pattern to the kind of multi-level override chain that production VIP teams use to swap components between regression tests.

Example 1 — Beginner: Registration and Basic Creation

SystemVerilog — Beginner: Factory Registration and Creation
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Step 1: Register classes with the factory via macros ──────────
class base_txn extends uvm_sequence_item;
`uvm_object_utils(base_txn)   // ← registers "base_txn" in factory
rand bit[7:0] data;
function new(string n="base_txn"); super.new(n); endfunction
virtual function string describe();
return $sformatf("base_txn: data=%0h", data);
endfunction
endclass
 
class reg_test extends uvm_test;
`uvm_component_utils(reg_test)   // ← registers "reg_test" in factory
function new(string n, uvm_component p); super.new(n,p); endfunction
 
task run_phase(uvm_phase phase);
base_txn txn;
phase.raise_objection(this);
 
// ── Step 2: Create via factory — ALWAYS this way ──────────
txn = base_txn::type_id::create("txn");
void'(txn.randomize());
 
$display("[FACTORY] Created: %s", txn.describe());
$display("[FACTORY] Actual type: %s", txn.get_type_name());
 
// ── Step 3: Verify factory lookup ─────────────────────────
$display("[FACTORY] Factory knows base_txn: %0b",
(base_txn::type_id::get() != null));
 
phase.drop_objection(this);
endtask
endclass
 
module factory_basic_top;
initial run_test("reg_test");
endmodule
 
// Expected Output:
// [FACTORY] Created: base_txn: data=a3
// [FACTORY] Actual type: base_txn
// [FACTORY] Factory knows base_txn: 1

Example 2 — Intermediate: Type Override Swaps Driver Transparently

SystemVerilog — Intermediate: Type Override for Error Injection
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Production driver — normal operation ──────────────────────────
class apb_driver extends uvm_driver#(base_txn);
`uvm_component_utils(apb_driver)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string get_mode(); return "NORMAL"; endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
$display("[DRV] Built: %s mode=%s", get_type_name(), get_mode());
endfunction
endclass
 
// ── Error injection driver — extends production driver ─────────────
class apb_err_driver extends apb_driver;
`uvm_component_utils(apb_err_driver)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string get_mode(); return "ERROR_INJECT"; endfunction
endclass
 
// ── Test: overrides driver type before build cascade ───────────────
class error_test extends uvm_test;
`uvm_component_utils(error_test)
apb_driver drv;   // declared as base type
 
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Register override BEFORE calling super (which creates the driver)
apb_driver::type_id::set_type_override(apb_err_driver::get_type());
super.build_phase(phase);
// Factory intercepts create() and returns apb_err_driver instead
drv = apb_driver::type_id::create("drv", this);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
$display("[TEST] drv actual type: %s",  drv.get_type_name());
$display("[TEST] drv mode:        %s",  drv.get_mode());
// Polymorphism: apb_err_driver.get_mode() called even though handle is apb_driver
phase.drop_objection(this);
endtask
endclass
 
module factory_override_top;
initial run_test("error_test");
endmodule
 
// Expected Output:
// [DRV] Built: apb_err_driver mode=ERROR_INJECT  ← factory gave us the subtype!
// [TEST] drv actual type: apb_err_driver
// [TEST] drv mode:        ERROR_INJECT
//
// Key: the VIP code (apb_driver::type_id::create) was NOT modified.
// The test registered the override → factory did the substitution.

Example 3 — Verification: Instance Override for One Specific Agent

SystemVerilog — Verification: Instance Override on One Path
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Two agents in the testbench — only agent_0 gets overridden ────
class multi_agent_test extends uvm_test;
`uvm_component_utils(multi_agent_test)
apb_driver drv_0, drv_1;  // two drivers
 
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
super.build_phase(phase);
 
// Override ONLY the driver at path "...agent_0.drv"
apb_driver::type_id::set_inst_override(
apb_err_driver::get_type(),
"uvm_test_top.drv_0");   // ← path must match get_full_name()
 
// Both created the same way — factory decides what each gets
drv_0 = apb_driver::type_id::create("drv_0", this);
drv_1 = apb_driver::type_id::create("drv_1", this);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
$display("drv_0 type: %s mode: %s", drv_0.get_type_name(), drv_0.get_mode());
$display("drv_1 type: %s mode: %s", drv_1.get_type_name(), drv_1.get_mode());
phase.drop_objection(this);
endtask
endclass
 
module instance_override_top;
initial run_test("multi_agent_test");
endmodule
 
// Expected Output:
// drv_0 type: apb_err_driver  mode: ERROR_INJECT  ← overridden
// drv_1 type: apb_driver      mode: NORMAL         ← unchanged
//
// Instance override is path-specific — surgical precision
// Type override would have changed BOTH drivers

Example 4 — Tricky: Override Registered After build_phase Cascade

SystemVerilog — Tricky: Late Override (Common Bug)
`include "uvm_macros.svh"
import uvm_pkg::*;
 
class late_override_test extends uvm_test;
`uvm_component_utils(late_override_test)
apb_driver drv;
 
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
super.build_phase(phase);   // ← build cascade starts here
drv = apb_driver::type_id::create("drv", this);
// ❌ WRONG: override registered AFTER create() already happened
apb_driver::type_id::set_type_override(apb_err_driver::get_type());
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
$display("[LATE] drv type: %s", drv.get_type_name());
// Output: "apb_driver" — override arrived too late
// Override IS registered in factory, but the object was already created
phase.drop_objection(this);
endtask
endclass
 
// Expected Output (bug version):
// [LATE] drv type: apb_driver  ← NOT apb_err_driver — too late!
//
// ✓ CORRECT — register override BEFORE super.build_phase():
//   function void build_phase(uvm_phase phase);
//     apb_driver::type_id::set_type_override(apb_err_driver::get_type()); // ← FIRST
//     super.build_phase(phase);  // ← cascade creates using the override
//     drv = apb_driver::type_id::create("drv", this);
//   endfunction

Common Bugs — Factory Failures That Are Hard to Diagnose

Factory bugs are uniquely frustrating because they usually don't crash the simulation. The wrong type silently runs. Overrides silently don't apply. You end up debugging behavior differences without realizing the root cause is that the factory gave you the wrong component two build_phases ago.

⚠️ Bug 1 — Using new() Instead of type_id::create() — Override Never Works

Symptom: You register a type override in the test, but the driver still runs with the base behavior. get_type_name() returns the base class name. No errors. The override is simply ignored.

Root cause: new() bypasses the factory entirely. Factory overrides only apply when objects are created via type_id::create(). If VIP code uses direct new(), factory is never consulted and the override is invisible.

SystemVerilog — Bug 1: Direct new() vs Factory Create
// ❌ WRONG — direct new() bypasses factory completely
class apb_agent extends uvm_agent;
`uvm_component_utils(apb_agent)
apb_driver drv;
 
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = new("drv", this);   // ← bypasses factory — WRONG
// Type override in test will NEVER take effect here
endfunction
endclass
 
// ✓ CORRECT — always use factory create()
class apb_agent extends uvm_agent;
`uvm_component_utils(apb_agent)
apb_driver drv;
 
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = apb_driver::type_id::create("drv", this);  // ← factory consulted
// Now type overrides registered in any test WILL take effect
endfunction
endclass
 
// Debug command to find all new() usage in VIP code:
// grep -rn "= new(" src/vip/ | grep -v "\.md"
// Any hit = potential factory bypass

⚠️ Bug 2 — Instance Override Path Doesn't Match get_full_name()

Symptom: Instance override registered, but get_type_name() still returns the base class. No error from the factory. Simulation runs normally with the wrong type.

Root cause: The path string in set_inst_override() must exactly match the component's get_full_name() at the time of creation. If the hierarchy changed (agent renamed, nesting level different), the path doesn't match and the factory silently falls back to the base type.

SystemVerilog — Bug 2: Wrong Path in Instance Override
// ❌ WRONG — path doesn't match actual component hierarchy
apb_driver::type_id::set_inst_override(
apb_err_driver::get_type(),
"uvm_test_top.agent.drv");   // ← assumes "agent" layer exists
// But actual hierarchy is: uvm_test_top.drv (no agent wrapper)
// Result: no match → falls back to base type silently
 
// ── Debug: print actual path BEFORE registering override ──────────
// Add this in start_of_simulation_phase (BEFORE build_phase completes)
// Actually, print_topology() shows all paths:
uvm_root::get().print_topology();
// Then use the EXACT path shown in the topology output
 
// ✓ CORRECT — verify path with get_full_name() first
// Option 1: Use wildcards (supported in most UVM implementations)
apb_driver::type_id::set_inst_override(
apb_err_driver::get_type(),
"uvm_test_top*.drv");   // ← wildcard matches any middle path
 
// Option 2: Hard-code the exact path from topology output
apb_driver::type_id::set_inst_override(
apb_err_driver::get_type(),
"uvm_test_top.drv");   // ← exactly what print_topology shows

🔍 Debug Insight — Factory Diagnostic Toolkit

  • Print factory state: factory.print() or uvm_default_factory.print() — shows all registered types and active overrides
  • Verify actual type at runtime: $display("type: %s", comp.get_type_name()) — if it's the base class name, override didn't apply
  • Find hierarchy paths: uvm_root::get().print_topology() in start_of_simulation_phase — shows all component full paths
  • Find new() bypasses: grep -rn "= new(" src/vip/ — any direct construction bypasses factory
  • Override too late: If override is registered inside or after super.build_phase(), the components are already created

Ready-to-Run: Complete Factory Demo

This single-file testbench demonstrates type override, instance override, factory diagnostics, and the ordering rule — everything from the article in one compilable file. Save as factory_demo.sv and run it.

SystemVerilog — factory_demo.sv (Complete, Ready-to-Run)
// ================================================================
// factory_demo.sv — UVM Factory Complete Demonstration
// Shows: registration, type override, instance override, diagnostics
//
// Compile (Questa):
//   vlog -sv -L uvm_1_2 factory_demo.sv
//   vsim -c -L uvm_1_2 factory_demo_top -do "run -all; quit"
//
// Compile (VCS):
//   vcs -sverilog -ntb_opts uvm-1.2 factory_demo.sv && ./simv
//
// Compile (Xcelium):
//   xrun -sv -uvm factory_demo.sv
// ================================================================
 
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Base transaction ───────────────────────────────────────────────
class pkt extends uvm_sequence_item;
`uvm_object_utils(pkt)
rand bit[7:0] data;
function new(string n="pkt"); super.new(n); endfunction
virtual function string kind(); return "NORMAL"; endfunction
endclass
 
// ── Error packet — extends base ────────────────────────────────────
class err_pkt extends pkt;
`uvm_object_utils(err_pkt)
function new(string n="err_pkt"); super.new(n); endfunction
virtual function string kind(); return "ERROR"; endfunction
endclass
 
// ── Base component ─────────────────────────────────────────────────
class base_comp extends uvm_component;
`uvm_component_utils(base_comp)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string mode(); return "BASE"; endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
$display("  Built: %-20s type=%-15s mode=%s",
get_full_name(), get_type_name(), mode());
endfunction
endclass
 
// ── Extended component ─────────────────────────────────────────────
class adv_comp extends base_comp;
`uvm_component_utils(adv_comp)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string mode(); return "ADVANCED"; endfunction
endclass
 
// ── Full-featured test ─────────────────────────────────────────────
class factory_test extends uvm_test;
`uvm_component_utils(factory_test)
base_comp comp_a, comp_b, comp_c;
 
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
uvm_factory factory = uvm_factory::get();
 
`uvm_info("TEST", "\n=== 1. Type Override (all instances) ===", UVM_NONE)
base_comp::type_id::set_type_override(adv_comp::get_type());
 
`uvm_info("TEST", "\n=== 2. Instance Override (comp_b only) ===", UVM_NONE)
base_comp::type_id::set_inst_override(
base_comp::get_type(),   // revert comp_b back to base
"uvm_test_top.comp_b");
 
super.build_phase(phase);
comp_a = base_comp::type_id::create("comp_a", this);  // → adv_comp
comp_b = base_comp::type_id::create("comp_b", this);  // → base_comp (inst override)
comp_c = base_comp::type_id::create("comp_c", this);  // → adv_comp
endfunction
 
task run_phase(uvm_phase phase);
pkt p1, p2;
phase.raise_objection(this);
 
`uvm_info("TEST", "\n=== 3. Object Type Override ===", UVM_NONE)
pkt::type_id::set_type_override(err_pkt::get_type());
p1 = pkt::type_id::create("p1");
$display("  p1 type=%s kind=%s", p1.get_type_name(), p1.kind());
 
`uvm_info("TEST", "\n=== 4. Factory Print ===", UVM_NONE)
uvm_factory::get().print(1);
 
`uvm_info("TEST", "\n=== 5. Component Summary ===", UVM_NONE)
$display("  comp_a: %s", comp_a.get_type_name());
$display("  comp_b: %s", comp_b.get_type_name());
$display("  comp_c: %s", comp_c.get_type_name());
 
phase.drop_objection(this);
endtask
endclass
 
module factory_demo_top;
initial run_test("factory_test");
endmodule
 
// ================================================================
// Expected Output:
//
// === 1. Type Override (all instances) ===
// === 2. Instance Override (comp_b only) ===
//   Built: uvm_test_top.comp_a  type=adv_comp   mode=ADVANCED
//   Built: uvm_test_top.comp_b  type=base_comp  mode=BASE      ← inst override
//   Built: uvm_test_top.comp_c  type=adv_comp   mode=ADVANCED
//
// === 3. Object Type Override ===
//   p1 type=err_pkt kind=ERROR    ← pkt factory gave err_pkt
//
// === 4. Factory Print ===
//   (shows registered types and active overrides)
//
// === 5. Component Summary ===
//   comp_a: adv_comp    ← type override applied
//   comp_b: base_comp   ← instance override reverted it
//   comp_c: adv_comp    ← type override applied
// ================================================================

Interview Questions — Factory Questions From Real Interviews

Beginner Level

  • Q1 **What does the uvm_component_utils macro do internally?** **Answer:** It registers the class with the UVM factory so it can be created by name, overridden, and looked up at runtime. Specifically, it defines type_id(the factory registration wrapper), providesget_type()(returns the registered type object), and definescreate()(the factory creation method). Without this macro, the class exists in SystemVerilog but is invisible to UVM's factory — you can't override it, can't create it by name string, andget_type_name()` returns a generic base class name.
  • Q2 What is the difference between type override and instance override? Answer: Type override replaces every instance of a class across the entire testbench — when any code calls base_type::type_id::create() anywhere in the hierarchy, the factory returns the override type. Instance override is path-specific: only the component at the exact path string (matching its get_full_name()) gets the override. Other instances of the same type are unaffected. Use type override for "change all drivers to error-inject mode." Use instance override for "change only agent_0's driver, leave agent_1 alone."

Intermediate Level

  • Q3 Why must factory overrides be registered before super.build_phase() in the test's build_phase? Answer: super.build_phase() triggers the build_phase cascade — it calls build_phase on all child components, which in turn call it on their children. Any type_id::create() calls in those child build_phases consult the factory at that moment. If the override hasn't been registered yet (because it's in code after the super call), those create() calls get the base type. The objects are already created before the override arrives. Registering overrides before super.build_phase() ensures the factory has the override in place before any creation happens.
  • Q4 Does the override class need to extend the overridden class? What happens if it doesn't? Answer: Yes — the override class must be a subtype (direct or indirect) of the overridden class. The factory creates the override type but returns it through the base type's handle (via SystemVerilog polymorphism). If the override class doesn't extend the base, UVM will issue a UVM_WARNING or UVM_FATAL from the factory saying "override type is not a subtype of the requested type," and the creation may fail. This is enforced because the calling code holds a handle of the base type — $cast between unrelated types would fail at runtime.

Senior / Architect Level

  • Q5 A test registers both a type override and an instance override for the same component. Which takes precedence? Answer: Instance override takes precedence over type override for the specific instance path. The factory's priority order is: (1) Instance override for the exact path — highest priority; (2) Type override — applies to all instances not covered by an instance override; (3) Default registered type — if no override exists. This allows you to set a global type override ("all drivers → error driver") and then selectively revert specific instances back to base ("agent_2's driver → base driver") using an instance override that points back to the original type.

Best Practices — Factory Rules That Production Teams Enforce

RuleCorrect PatternWhy It Matters
Always use type_id::create()T::type_id::create("name", parent)Direct new() bypasses factory — overrides never work on that component
Register override BEFORE super.build_phase()Override first line of build_phase, then superSuper triggers the cascade — components created before override arrives get base type
Override class must extend base classclass override_drv extends base_drvFactory enforces subtype relationship; unrelated types cause fatal errors
Verify path before instance overrideUse print_topology() to get exact path stringsWrong path → override silently doesn't apply → wrong type runs
Use factory.print() for diagnosticsuvm_factory::get().print(1) in start_of_simulationShows registered types and active overrides — confirms override took effect
Never override without a macroBoth base and override class need ``uvm_*_utils`Un-registered classes can't be overridden or looked up by the factory
Don't override in connect_phase or run_phaseOverride only in build_phase (before super)Overrides in later phases are too late — all components already created

💡 Pro Tip — Factory-Based Test Architecture

In production environments, the factory enables a "configuration by substitution" pattern. A base test class builds a standard testbench. Derived tests override specific components before super.build_phase() to inject error behavior, logging, or alternate implementations — all without touching VIP source files. This is how teams run the same VIP against 50 different test scenarios: one set of VIP source files, 50 different test classes that swap components via factory overrides. No code duplication, no ifdef forests.

Summary — The Mechanism That Makes UVM Reuse Possible

The UVM factory is the reason VIPs are worth buying. Without it, every test that needs different behavior requires editing the VIP source — which means version control chaos, merge conflicts, and VIPs that drift between projects. With the factory, you change test behavior by changing the test, not the VIP.

ConceptWhat It DoesWhen to Use It
RegistrationMakes a class known to the factory via `uvm_*_utils macroEvery class that needs to be created, overridden, or looked up by name
type_id::create()Creates objects through the factory (not new())Every component and object creation in VIP and testbench code
Type OverrideReplaces all instances of a class globallySwap all drivers to error mode, all sequences to stress mode
Instance OverrideReplaces one specific component by pathError injection on one specific agent while leaving others normal
factory.print()Shows registered types and active overridesDebugging when override doesn't seem to take effect
  • 1 Registration is invisible but mandatory. Without ``uvm_*_utils`, the class is opaque to the factory. You can still create it with new() — but you can never override it and type_id::create() returns the wrong type.
  • 2 Override before super.build_phase() — non-negotiable. The cascade creates all components. If the override isn't registered before the cascade starts, it arrives after all objects are already created. The override is stored but never used.
  • 3 The factory is what makes VIPs reusable. The whole point of UVM's factory isn't convenience — it's that test configurations can vary independently of VIP source code. One set of files, infinite configurations. That's the engineering value.