Skip to content

UVM Factory Debug

Reading factory print() output, override chains, simulator debug switches, 5-step workflow.

UVM Fundamentals · Module 7

Why Factory Debug Is Its Own Topic

Compilation errors and runtime crashes are straightforward to diagnose — the simulator points you to the problem. Factory failures are different. The simulation compiles cleanly, elaborates cleanly, and runs to completion. The wrong component type is created and the test either produces incorrect results or the override simply has no observable effect.

There are exactly three ways a factory override can fail silently:

  • Override Not Registered — The override was set but the factory has no record of it. Root causes: missing `uvm_*_utils macro on source or override class, or the package containing the class was never compiled or imported.
  • Override Registered Too Late — The factory shows the override in its table, but the component was already created before the override was registered. No runtime error. The original type runs with the wrong implementation.
  • Override Path Mismatch — Instance override registered but path pattern does not match the component's actual get_full_name(). The factory falls through to the type override or the original type without any warning message.

Each failure produces a simulation that runs normally. The only way to distinguish them is with deliberate factory introspection. That is what this module covers.

factory::print() — Anatomy of the Output

uvm_factory::get().print() is the single most important factory debug tool. It dumps the complete factory state: all registered types, all active type overrides, and all active instance overrides. Call it in start_of_simulation_phase so all components have been created and overrides have been applied.

SystemVerilog — calling factory::print() in start_of_simulation_phase
// Add to your base_test or any top-level component — remove before tapeout
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// print(0) — show overrides only (recommended for routine debug)
uvm_factory::get().print(0);
 
// print(1) — show all registered types + overrides (verbose)
uvm_factory::get().print(1);
endfunction

Reading the Factory Print Output

The output has three sections. Each tells you something specific about the factory state. Simulator Console — start_of_simulation_phase#### UVM Factory Configuration (52 types registered) ####--- Instance Overrides --- No instance overrides are registered.--- Type Overrides --- Requested Type Override Type -------------------------- --------------------------- apb_driver error_inject_driver--- All Registered Types (sorted by name) --- apb_driver [component] apb_monitor [component] apb_seq_item [object] error_inject_driver [component] ← override class my_env [component] my_scoreboard [component] ... (46 more types)#### End of UVM Factory Configuration ####Total registered types52 classes have uvm_*_utils — all discoverable by the factoryInstance overrides sectionEmpty = no set_inst_override() was called. Expected here.Active type override — KEY LINEapb_driver → error_inject_driverIf your override appears here → it IS registeredAll registered type namesVerify both original AND override class appear here.Missing class = uvm_*_utils was not included.Override class must be registered tooIf error_inject_driver is missing here → it has no macro Figure 1 — Annotated factory::print() output. Five key reading points: total type count, instance override section, active type override table, registered type list, and override class registration confirmation.

Filtering the Factory Print Output

A large project can have 200+ registered types. Printing all of them clutters the log and makes it hard to find what you need. Use the argument to print() and type-specific queries to filter.

CallOutputUse When
factory.print(0)Only the override tables (type + instance). No type list.Routine debug — confirms which overrides are active without the type noise.
factory.print(1)Override tables + complete list of all registered types.Diagnosing "class not registered" — verify a class appears in the type list.
factory.debug_pass_one(0)Simulator-specific — Questa only. Shows factory resolution per create() call in real time.Tracing exactly which type was resolved for a specific create() call.
SystemVerilog — conditional factory print driven by plusarg
// Control factory print from the command line without editing source code
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
if ($test$plusargs("FACTORY_DEBUG")) begin
`uvm_info("FACTORY", "Factory state at start_of_simulation:", UVM_LOW)
uvm_factory::get().print(1);   // verbose — all types + overrides
end else if ($test$plusargs("FACTORY_OVR")) begin
uvm_factory::get().print(0);   // concise — overrides only
end
endfunction
 
// Simulate with: vsim +FACTORY_DEBUG work.tb_top
// Or:            vsim +FACTORY_OVR  work.tb_top

Type Verification at Runtime — get_type_name()

Even after confirming the override is registered in the factory, you need to verify that the actual object created is the override type. get_type_name() returns the class name of the object at runtime — not the variable's declared type.

SystemVerilog — verifying actual type with get_type_name()
// In apb_agent.sv — after build_phase creates the driver
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// Check what type was actually created
`uvm_info("AGENT",
$sformatf("Driver type: %s  Path: %s",
drv.get_type_name(),
drv.get_full_name()),
UVM_LOW)
 
// Console output if override worked:
//   UVM_INFO: Driver type: error_inject_driver  Path: ...apb_agent.drv
// Console output if override FAILED:
//   UVM_INFO: Driver type: apb_driver            Path: ...apb_agent.drv
endfunction
 
// ── Programmatic type check — fail simulation if wrong type ──────────
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
if (drv.get_type_name() != "error_inject_driver")
`uvm_fatal("TYPE_CHK",
$sformatf("Expected error_inject_driver, got %s. Override failed.",
drv.get_type_name()))
endfunction

$cast() as a Type Check

When a base-type handle holds an override object, use $cast() to check whether the actual type is what you expect without triggering a fatal error if it is not:

SystemVerilog — using $cast() to verify override type
// drv is declared as apb_driver but may hold error_inject_driver
error_inject_driver eid_drv;
 
if ($cast(eid_drv, drv)) begin
// $cast returns 1 → drv IS error_inject_driver → override worked
`uvm_info("CAST", "Override confirmed via $cast", UVM_LOW)
eid_drv.set_error_rate(10);   // access override-specific method safely
end else begin
// $cast returns 0 → drv is apb_driver → override did NOT work
`uvm_warn("CAST", "Override failed — drv is base apb_driver type")
end
 
// Note: $cast() is non-destructive — it doesn't change drv or eid_drv
// if cast fails. This makes it safe for runtime type checking.

Tracking Override Chains at Runtime

When multiple layers of overrides are registered (A→B, B→C), tracing the chain manually through factory::print() output is straightforward — read the Type Overrides table from top to bottom and follow the chain. For programmatic chain inspection, use find_override_by_type().

SystemVerilog — programmatic override chain inspection
// ── find_override_by_type() traces one hop in the chain ──────────────
uvm_object_wrapper resolved;
uvm_factory        factory = uvm_factory::get();
 
// What does the factory give when apb_driver is requested at path "*.drv"?
resolved = factory.find_override_by_type(
apb_driver::get_type(),    // requested type
"uvm_test_top.env.apb_agent.drv"   // path context
);
 
`uvm_info("CHAIN",
$sformatf("apb_driver resolves to: %s", resolved.get_type_name()),
UVM_LOW)
 
// If override A→B→C is registered:
// find_override_by_type(A) returns C directly — factory follows the full chain
 
// ── Walking the chain manually (step by step) ────────────────────────
uvm_object_wrapper current = apb_driver::get_type();
uvm_object_wrapper next;
 
forever begin
next = factory.find_override_by_type(current, "*");
if (next == current) break;   // no further override — chain ends
`uvm_info("CHAIN",
$sformatf("  %s → %s", current.get_type_name(), next.get_type_name()),
UVM_LOW)
current = next;
end
`uvm_info("CHAIN", $sformatf("Final resolved type: %s", current.get_type_name()), UVM_LOW)
Console — reading the override chain from factory::print(0) output
## factory::print(0) output when chain A→B→C is registered:
 
#### UVM Factory Configuration ####
--- Type Overrides ---
Requested Type       Override Type
-------------------- -------------------------
apb_driver           protocol_driver          ←── Step 1: A → B
protocol_driver      error_inject_driver      ←── Step 2: B → C
 
## Reading the chain: start from apb_driver
## apb_driver → protocol_driver → error_inject_driver (no further override)
## Factory creates: error_inject_driver
## Returned as:     apb_driver (base type)

Simulator-Specific Factory Debug Switches

Beyond calling factory::print() in your code, each major simulator provides command-line mechanisms to enable real-time factory tracing without modifying source.

SimulatorSwitch / PlusargEffect
Questa+UVM_FACTORY_OVERRIDE_TRACEPrints a message every time the factory resolves a type, showing what was requested and what was returned. Real-time trace per create() call.
VCS+UVM_FACTORY_OVERRIDE_TRACESame as Questa. Supported in UVM 1.2 and later.
Xcelium-uvmfactoryoverridedbgXcelium-specific debug flag. Produces similar per-create() trace output.
All simulators+UVM_CONFIG_DB_TRACENot factory-specific, but useful alongside factory debug — shows all config_db get/set operations which often reveal whether virtual interfaces reached their targets.
Console — UVM_FACTORY_OVERRIDE_TRACE output example
## Simulation command:
## vsim +UVM_FACTORY_OVERRIDE_TRACE work.tb_top
 
## Output (one line per create() call):
 
UVM_INFO @ 0: Factory: create_component_by_type
Requested: apb_driver
Override:  error_inject_driver  (type override)
Created:   error_inject_driver  path=uvm_test_top.env.apb_agent.drv
 
UVM_INFO @ 0: Factory: create_component_by_type
Requested: apb_monitor
Override:  (none)
Created:   apb_monitor  path=uvm_test_top.env.apb_agent.mon
 
## Key reading: "Override: (none)" means original type used — no override matched
## "Override: error_inject_driver (type override)" means type override matched
## "Override: ... (instance override)" means instance override matched by path

Systematic Five-Step Debug Workflow

Follow these steps in order. Each step either resolves the problem or narrows it to the next step. Skipping steps wastes time — each step takes less than two minutes. Override has no visible effectSTEP 1Add factory.print(0) to start_of_simulationDoes the override appear in the Type Overrides table?NOFix: Add `uvm_utils toboth classes. Check imports.YESSTEP 2Check component creation codeIs it type_id::create() or direct new()?new()Fix: Replace new() withtype_id::create(name, this)STEP 3Check get_type_name() after build_phaseDoes it return the override class name?NOFix: Override registeredafter super.build_phase()STEP 4Check build_phase call orderIs set_override() before super.build_phase()?YESSTEP 5For instance override: verify path pattern vs get_full_name()Override WORKINGtype_id::create() is usingfactory — check logic instead Figure 2 — Five-step factory debug workflow. Each step eliminates one failure category. Steps 1–4 handle most failures; Step 5 is specific to instance override path mismatches.

  • Call factory.print(0) in start_of_simulation_phase Look at the "Type Overrides" and "Instance Overrides" sections. Expected: your override appears with correct source → target class names. If missing: add `uvm_*_utils to both classes. Verify the package is compiled and imported in the current compilation unit.
  • Verify the component is created with type_id::create() Search the VIP or environment code for the create() call. Confirm it is apb_driver::type_id::create(...), not new(...). Expected: type_id::create(name, this) — two arguments for components. If using new(): replace with type_id::create(). Factory override has zero effect on objects created with direct new().
  • Check get_type_name() after build_phase completes In start_of_simulation_phase, print drv.get_type_name(). Compare with what you expected. Expected: prints the override class name (e.g., "error_inject_driver"). If prints original class name: the override was registered too late. Move set_type_override() before super.build_phase().
  • Verify override registration order in build_phase Inspect the test's build_phase. The set_type_override() or set_inst_override() call must appear before super.build_phase() on the same line or earlier. Expected: override call is the first statement in build_phase. If after super.build_phase(): move it before. If in a base test, ensure the base test's build_phase also places overrides before its own super call.
  • For instance override: match path pattern to get_full_name() Print the component's actual full path: drv.get_full_name(). Compare character-by-character with the pattern in set_inst_override(). Expected: the pattern matches the full path (exact or via wildcard). If mismatch: correct the pattern. Use +UVM_FACTORY_OVERRIDE_TRACE from the command line to see real-time path matching during create() calls.

Quick Reference — All Factory Debug Tools

ToolCall / UsageWhat It Tells You
factory.print(0)uvm_factory::get().print(0)All active overrides (type + instance). No type list. Best for routine debug.
factory.print(1)uvm_factory::get().print(1)Overrides + all registered types. Use to confirm a class was registered.
get_type_name()handle.get_type_name()Actual runtime class name of the object. Override worked if name ≠ declared type.
get_full_name()comp.get_full_name()Full hierarchy path. Used to verify instance override path patterns.
$cast()if ($cast(override_h, base_h))Non-fatal type check. Returns 1 if actual type is assignment-compatible with override type.
find_override_by_type()factory.find_override_by_type(T::get_type(), path)Returns the final resolved type for a given requested type and path context. Follows full chain.
FACTORY_OVERRIDE_TRACEPlusarg: +UVM_FACTORY_OVERRIDE_TRACEReal-time trace of every factory create() call — requested type, resolved type, path.
CONFIG_DB_TRACEPlusarg: +UVM_CONFIG_DB_TRACENot factory-specific but useful alongside — shows all config_db get/set to find missing virtual interfaces.
SystemVerilog — reusable factory debug snippet for any component
// Drop this into any component's start_of_simulation_phase while debugging
// Remove before commit. Does not affect simulation behaviour.
 
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
if ($test$plusargs("FACTORY_DEBUG")) begin
automatic string divider = {60{"="}};
`uvm_info("DBG", divider, UVM_LOW)
`uvm_info("DBG", $sformatf("Component: %s  Type: %s",
get_full_name(), get_type_name()), UVM_LOW)
`uvm_info("DBG", "Factory state:", UVM_LOW)
uvm_factory::get().print(0);   // overrides only
`uvm_info("DBG", divider, UVM_LOW)
end
endfunction
 
// Run with: vsim +FACTORY_DEBUG +UVM_FACTORY_OVERRIDE_TRACE work.tb_top
// The combination gives you both the registry state and real-time resolution trace.

Code Examples — Factory Debug From Simple to Systematic

The best way to learn factory debugging is to create a broken scenario, observe the output, and fix it. These four examples build progressively from basic factory.print() inspection to the kind of multi-level override chain tracing that production teams use when a VIP behaves incorrectly in regression.

Example 1 — Beginner: Reading factory.print() Output

SystemVerilog — Beginner: Basic factory.print() Inspection
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// Three related classes ─────────────────────────────────────────────
class base_txn extends uvm_sequence_item;
`uvm_object_utils(base_txn)
function new(string n="base_txn"); super.new(n); endfunction
endclass
 
class err_txn extends base_txn;
`uvm_object_utils(err_txn)
function new(string n="err_txn"); super.new(n); endfunction
endclass
 
class base_drv extends uvm_driver#(base_txn);
`uvm_component_utils(base_drv)
function new(string n, uvm_component p); super.new(n,p); endfunction
endclass
 
class print_test extends uvm_test;
`uvm_component_utils(print_test)
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Register one type override
base_txn::type_id::set_type_override(err_txn::get_type());
super.build_phase(phase);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
$display("\n=== factory.print() output ===");
uvm_factory::get().print(1);
phase.drop_objection(this);
endtask
endclass
 
module print_demo_top;
initial run_test("print_test");
endmodule
 
// Annotated Expected Output:
//
// === factory.print() output ===
// #### Factory Configuration (classes=4, overrides=1) ####
//
// Object Types:                        ← uvm_object subclasses registered
//   base_txn
//   err_txn
//
// Component Types:                     ← uvm_component subclasses registered
//   base_drv
//   print_test
//
// Type Overrides:                      ← active type-level substitutions
//   base_txn → err_txn
//       [when base_txn requested, err_txn is given]
//
// Instance Overrides:                  ← path-specific substitutions
//   (none)
//
// Reading this: "base_txn → err_txn" means ANY call to
// base_txn::type_id::create() will return an err_txn object.
// If this override is unexpected, check your test's build_phase.

Example 2 — Intermediate: Confirming Actual Type at Runtime

SystemVerilog — Intermediate: Runtime Type Verification
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// Three driver types ────────────────────────────────────────────────
class base_drv extends uvm_component;
`uvm_component_utils(base_drv)
function new(string n, uvm_component p); super.new(n,p); endfunction
endclass
 
class slow_drv extends base_drv;
`uvm_component_utils(slow_drv)
function new(string n, uvm_component p); super.new(n,p); endfunction
endclass
 
class type_check_test extends uvm_test;
`uvm_component_utils(type_check_test)
base_drv drv;
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
base_drv::type_id::set_type_override(slow_drv::get_type());
super.build_phase(phase);
drv = base_drv::type_id::create("drv", this);
endfunction
 
task run_phase(uvm_phase phase);
slow_drv sd;
phase.raise_objection(this);
 
// Method 1: get_type_name() — what type is it REALLY?
$display("[TYPE] get_type_name(): %s", drv.get_type_name());
 
// Method 2: $cast — does the actual type match our expectation?
if($cast(sd, drv)) begin
$display("[CAST] $cast to slow_drv: SUCCESS");
end else begin
$display("[CAST] $cast to slow_drv: FAIL — wrong type");
end
 
// Method 3: Factory lookup — what would factory return for this type?
$display("[FACT] Factory override for base_drv:");
uvm_factory::get().debug_pass_one(base_drv::get_type(),
"debug", "uvm_test_top.drv");
 
phase.drop_objection(this);
endtask
endclass
 
module type_check_top;
initial run_test("type_check_test");
endmodule
 
// Expected Output:
// [TYPE] get_type_name(): slow_drv        ← confirmed actual type
// [CAST] $cast to slow_drv: SUCCESS       ← cast confirms type compatibility
// [FACT] Factory override for base_drv: slow_drv (type override)

Example 3 — Verification: Tracing a Three-Level Override Chain

SystemVerilog — Verification: Multi-Level Override Chain Debug
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// Override chain: base → extended → specialized ────────────────────
class drv_v1 extends uvm_component;
`uvm_component_utils(drv_v1)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string version(); return "v1"; endfunction
endclass
 
class drv_v2 extends drv_v1;
`uvm_component_utils(drv_v2)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string version(); return "v2"; endfunction
endclass
 
class drv_v3 extends drv_v2;
`uvm_component_utils(drv_v3)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string version(); return "v3"; endfunction
endclass
 
class chain_test extends uvm_test;
`uvm_component_utils(chain_test)
drv_v1 drv;
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Layer 1: VIP base package registers: drv_v1 → drv_v2
drv_v1::type_id::set_type_override(drv_v2::get_type());
// Layer 2: Your team adds: drv_v2 → drv_v3
drv_v2::type_id::set_type_override(drv_v3::get_type());
super.build_phase(phase);
drv = drv_v1::type_id::create("drv", this);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
$display("\nOverride chain for drv_v1:");
$display("  drv_v1 requested → factory checks override → drv_v2");
$display("  drv_v2 override?  → factory checks override → drv_v3");
$display("  drv_v3 override?  → none → FINAL RESULT: drv_v3");
$display("\nActual drv type: %s version=%s",
drv.get_type_name(), drv.version());
 
$display("\nfactory.print(1) shows the chain:");
uvm_factory::get().print(1);
phase.drop_objection(this);
endtask
endclass
 
module chain_top;
initial run_test("chain_test");
endmodule
 
// Expected Output:
// Override chain for drv_v1:
//   drv_v1 requested → factory checks override → drv_v2
//   drv_v2 override?  → factory checks override → drv_v3
//   drv_v3 override?  → none → FINAL RESULT: drv_v3
//
// Actual drv type: drv_v3  version=v3   ← chain fully resolved!
//
// factory.print(1):
// Type Overrides:
//   drv_v1 → drv_v2
//   drv_v2 → drv_v3
// ↑ Reading this: drv_v1 requests eventually resolves to drv_v3

Example 4 — Tricky: Debug a "Silent Override Failure" (new() bypass)

SystemVerilog — Tricky: Diagnosing Silent Override Failure
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// VIP agent that uses new() instead of type_id::create() ──────────
class bad_agent extends uvm_agent;
`uvm_component_utils(bad_agent)
drv_v1 drv;
function new(string n, uvm_component p); super.new(n,p); endfunction
function void build_phase(uvm_phase p);
super.build_phase(p);
drv = new("drv", this);   // ← VIP bug: uses new() not factory
endfunction
endclass
 
class bypass_debug_test extends uvm_test;
`uvm_component_utils(bypass_debug_test)
bad_agent agent;
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Override registered — but will it work?
drv_v1::type_id::set_type_override(drv_v2::get_type());
super.build_phase(phase);
agent = bad_agent::type_id::create("agent", this);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
 
$display("\n=== Diagnostic 1: factory.print() ===");
uvm_factory::get().print(1);
// Shows: drv_v1 → drv_v2 override IS registered
 
$display("\n=== Diagnostic 2: actual driver type ===");
$display("agent.drv type: %s", agent.drv.get_type_name());
// Shows: drv_v1 ← override DIDN'T apply because new() was used
 
$display("\n=== Root Cause: bad_agent uses new() instead of factory ===");
$display("Fix: in bad_agent.build_phase, change:");
$display("  drv = new('drv', this);                     // bypass");
$display("To:");
$display("  drv = drv_v1::type_id::create('drv', this); // factory");
 
phase.drop_objection(this);
endtask
endclass
 
module bypass_top;
initial run_test("bypass_debug_test");
endmodule
 
// Expected Output:
// === Diagnostic 1: factory.print() ===
// Type Overrides:
//   drv_v1 → drv_v2      ← override IS registered
//
// === Diagnostic 2: actual driver type ===
// agent.drv type: drv_v1  ← STILL drv_v1! Override didn't apply
//
// === Root Cause: bad_agent uses new() instead of factory ===
// Fix: change drv = new(...) to drv = drv_v1::type_id::create(...)
//
// KEY INSIGHT: factory.print() shows override as registered,
// but get_type_name() shows it didn't apply.
// This signature means: new() bypass.

Common Bugs — Factory Debug Scenarios From Real Projects

Factory debug bugs fall into two categories: bugs where the override doesn't apply (silent failure), and bugs where the override applies but to the wrong thing (unintended global effect). Both are hard to find because UVM doesn't raise errors for either. You have to proactively diagnose them.

⚠️ Bug 1 — factory.print() Shows Override, But Type Is Still Wrong

Symptom: factory.print() confirms the override is registered (base_drv → err_drv), but get_type_name() returns base_drv. The override is in the factory but not taking effect.

Diagnostic: This specific combination — override registered in factory but not applied at runtime — has exactly three root causes. Work through them in order.

SystemVerilog — Bug 1: Three Root Causes, Three Fixes
// ROOT CAUSE A: VIP uses new() instead of factory ─────────────────
// factory.print() shows: base_drv → err_drv
// get_type_name() shows: base_drv  ← wrong
// ❌ VIP code:
drv = new("drv", this);                              // bypass factory
// ✓ Fix:
drv = base_drv::type_id::create("drv", this);      // use factory
 
// ROOT CAUSE B: Override registered AFTER create() ─────────────────
// ❌ Wrong order in test.build_phase:
function void build_phase(uvm_phase phase);
super.build_phase(phase);                            // ← creates drv here
drv = base_drv::type_id::create("drv", this);    // ← using base type
base_drv::type_id::set_type_override(err_drv::get_type()); // ← too late
endfunction
 
// ✓ Fix: override BEFORE super.build_phase()
function void build_phase(uvm_phase phase);
base_drv::type_id::set_type_override(err_drv::get_type()); // ← FIRST
super.build_phase(phase);                            // ← cascade uses override
drv = base_drv::type_id::create("drv", this);    // ← gets err_drv ✓
endfunction
 
// ROOT CAUSE C: Instance override path is wrong ────────────────────
// ❌ Wrong path (agent is deeper than expected):
base_drv::type_id::set_inst_override(err_drv::get_type(),
"uvm_test_top.drv");   // ← wrong: actually "uvm_test_top.agent.drv"
 
// ✓ Fix: use print_topology() to find correct path:
uvm_root::get().print_topology();  // run first to see actual paths
base_drv::type_id::set_inst_override(err_drv::get_type(),
"uvm_test_top.agent.drv");  // ← correct path

⚠️ Bug 2 — Type Override Affects Agents You Didn't Intend to Change

Symptom: You add a type override for one specific agent's driver, but two other agents (also using the same driver class) suddenly behave differently. Your targeted change became global.

Root cause: Type override is always global. You used set_type_override() when you needed set_inst_override(). Every instance of base_drv — across all three agents — got err_drv.

SystemVerilog — Bug 2: Unintended Global vs Intended Instance
// ❌ WRONG: type override changes ALL three agents' drivers
function void build_phase(uvm_phase phase);
base_drv::type_id::set_type_override(err_drv::get_type());  // ← GLOBAL
super.build_phase(phase);
// agent0.drv → err_drv  ← intended
// agent1.drv → err_drv  ← unintended side effect
// agent2.drv → err_drv  ← unintended side effect
endfunction
 
// Debug: factory.print() clearly shows global scope
// Type Overrides: base_drv → err_drv  ← no path constraint = global
 
// ✓ CORRECT: instance override is path-specific
function void build_phase(uvm_phase phase);
base_drv::type_id::set_inst_override(err_drv::get_type(),
"uvm_test_top.agent0.drv");  // ← ONLY agent0
super.build_phase(phase);
// agent0.drv → err_drv  ← intended ✓
// agent1.drv → base_drv ← untouched ✓
// agent2.drv → base_drv ← untouched ✓
endfunction
 
// Verify with: foreach agent, print get_type_name() of each driver
// Confirm factory.print() now shows:
// Instance Overrides: uvm_test_top.agent0.drv: base_drv → err_drv

🔍 Debug Decision Tree — Which Debug Tool to Use First

Problem: Override not working? │ ├─ Is override in factory at all? │ → uvm_factory::get().print(1) │ └─ NO: Check macro, check pre-super registration order │ └─ YES: Continue below ↓ │ ├─ Does actual type match expected? │ → comp.get_type_name() == expected? │ └─ NO (still base type): Check for new() bypass in VIP │ └─ YES (correct type): Override working, check behavior bug │ ├─ For instance override: is path correct? │ → uvm_root::get().print_topology() │ └─ Compare actual path to your set_inst_override() string │ └─ For chain: is final resolution correct? → factory.debug_pass_one(type, context, path) └─ Shows what factory WOULD return for that create() call

Ready-to-Run: Complete Factory Debug Demo

This single-file testbench demonstrates every factory debug technique in one place: print(), get_type_name(), $cast, print_topology(), and the chain resolution pattern. Save as factory_debug_demo.sv and run it — you'll see the complete diagnostic workflow in one simulation.

SystemVerilog — factory_debug_demo.sv (Complete, Ready-to-Run)
// ================================================================
// factory_debug_demo.sv — Complete Factory Debug Demonstration
// Shows all debug techniques: print(), get_type_name(), $cast,
// print_topology(), and override chain tracing
//
// Compile (Questa):
//   vlog -sv -L uvm_1_2 factory_debug_demo.sv
//   vsim -c -L uvm_1_2 factory_debug_top -do "run -all; quit"
//
// Compile (VCS):
//   vcs -sverilog -ntb_opts uvm-1.2 factory_debug_demo.sv && ./simv
//
// Compile (Xcelium):
//   xrun -sv -uvm factory_debug_demo.sv
// ================================================================
 
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Three-level hierarchy for all demos ───────────────────────────
class comp_a extends uvm_component;
`uvm_component_utils(comp_a)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string label(); return "A"; endfunction
endclass
 
class comp_b extends comp_a;
`uvm_component_utils(comp_b)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string label(); return "B"; endfunction
endclass
 
class comp_c extends comp_b;
`uvm_component_utils(comp_c)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string label(); return "C"; endfunction
endclass
 
class obj_x extends uvm_sequence_item;
`uvm_object_utils(obj_x)
function new(string n="obj_x"); super.new(n); endfunction
endclass
 
class obj_y extends obj_x;
`uvm_object_utils(obj_y)
function new(string n="obj_y"); super.new(n); endfunction
endclass
 
// ── Complete debug test ────────────────────────────────────────────
class factory_debug_test extends uvm_test;
`uvm_component_utils(factory_debug_test)
comp_a c1, c2, c3;
 
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Setup: chain comp_a → comp_b → comp_c + instance exception
comp_a::type_id::set_type_override(comp_b::get_type());   // chain step 1
comp_b::type_id::set_type_override(comp_c::get_type());   // chain step 2
comp_a::type_id::set_inst_override(                       // c2 stays at comp_b
comp_b::get_type(), "uvm_test_top.c2");
obj_x::type_id::set_type_override(obj_y::get_type());     // object override
 
super.build_phase(phase);
c1 = comp_a::type_id::create("c1", this);
c2 = comp_a::type_id::create("c2", this);
c3 = comp_a::type_id::create("c3", this);
endfunction
 
task run_phase(uvm_phase phase);
obj_x obj;
comp_b cb;
comp_c cc;
phase.raise_objection(this);
 
// ── Debug Tool 1: factory.print() ─────────────────────────
`uvm_info("DBG","\n=== TOOL 1: factory.print() ===",UVM_NONE)
uvm_factory::get().print(1);
 
// ── Debug Tool 2: get_type_name() ─────────────────────────
`uvm_info("DBG","\n=== TOOL 2: get_type_name() ===",UVM_NONE)
$display("  c1: %s (label=%s)", c1.get_type_name(), c1.label());
$display("  c2: %s (label=%s)", c2.get_type_name(), c2.label());
$display("  c3: %s (label=%s)", c3.get_type_name(), c3.label());
 
// ── Debug Tool 3: $cast ────────────────────────────────────
`uvm_info("DBG","\n=== TOOL 3: $cast verification ===",UVM_NONE)
$display("  c1 castable to comp_c? %0b", $cast(cc, c1));
$display("  c2 castable to comp_c? %0b", $cast(cc, c2));
$display("  c2 castable to comp_b? %0b", $cast(cb, c2));
 
// ── Debug Tool 4: print_topology() ────────────────────────
`uvm_info("DBG","\n=== TOOL 4: print_topology() ===",UVM_NONE)
uvm_root::get().print_topology();
 
// ── Debug Tool 5: Object override check ───────────────────
`uvm_info("DBG","\n=== TOOL 5: Object type check ===",UVM_NONE)
obj = obj_x::type_id::create("obj");
$display("  obj type: %s", obj.get_type_name());
 
phase.drop_objection(this);
endtask
endclass
 
module factory_debug_top;
initial run_test("factory_debug_test");
endmodule
 
// ================================================================
// Expected Output:
//
// === TOOL 1: factory.print() ===
// Type Overrides:
//   comp_a → comp_b
//   comp_b → comp_c
//   obj_x  → obj_y
// Instance Overrides:
//   uvm_test_top.c2: comp_a → comp_b  ← stops chain for c2
//
// === TOOL 2: get_type_name() ===
//   c1: comp_c (label=C)   ← chain: a→b→c, resolved to comp_c
//   c2: comp_b (label=B)   ← inst override stops at b (doesn't follow type chain)
//   c3: comp_c (label=C)   ← chain: a→b→c, resolved to comp_c
//
// === TOOL 3: $cast verification ===
//   c1 castable to comp_c? 1   ← comp_c IS-A comp_c
//   c2 castable to comp_c? 0   ← comp_b is NOT comp_c
//   c2 castable to comp_b? 1   ← comp_b IS-A comp_b
//
// === TOOL 4: print_topology() ===
// uvm_test_top [factory_debug_test]
//   c1 [comp_c]
//   c2 [comp_b]    ← instance override stopped the chain here
//   c3 [comp_c]
//
// === TOOL 5: Object type check ===
//   obj type: obj_y   ← object override also works
// ================================================================

Interview Questions — Factory Debug Questions From Real Interviews

Beginner Level

  • Q1 What does uvm_factory::get().print(1) show and when do you use it? Answer: factory.print(1) prints a table of all classes registered with the factory (argument 1 means "verbose" — show everything; 0 means "show only overrides"). The table shows: (1) all registered object types, (2) all registered component types, (3) active type overrides (global substitutions), and (4) active instance overrides (path-specific substitutions). You use it when an override isn't behaving as expected — to verify whether the override is actually registered in the factory. If the override appears in this output but still doesn't apply, the root cause is usually a new() bypass or wrong timing (registered after creation).
  • Q2 What is the difference between get_type_name() and get_full_name() for debugging? Answer: get_type_name() returns the class type (e.g., "err_drv") — it tells you what the object actually IS at runtime, regardless of what type the handle is declared as. Use it to confirm factory overrides applied correctly. get_full_name() returns the component's position in the hierarchy (e.g., "uvm_test_top.agent0.drv") — it tells you WHERE the component lives. Use it to verify paths for instance overrides. For debugging factory issues, always check get_type_name() first — if it shows the base type when you expected an override type, the factory isn't doing what you think.

Intermediate Level

  • Q3 factory.print() shows "drv_a → drv_b", but get_type_name() still returns "drv_a". List the possible causes in order of likelihood. Answer: (1) Most likely: VIP code uses new() instead of type_id::create() — check with grep for "= new(" in VIP source. (2) Second most likely: override registered AFTER the create() call (wrong timing in build_phase — check whether override comes before super.build_phase()). (3) Less likely: the class that factory.print() shows the override for is a different class from what the VIP actually creates — similar name, different registration. (4) Least likely: factory singleton issue (multiple simulation processes sharing memory — rare in standard simulators).

Senior / Architect Level

  • Q4 An instance override doesn't apply even though the path string exactly matches print_topology() output. What's the most common overlooked cause? Answer: The timing of set_inst_override() relative to create(). Even when the path is correct, if the override is registered after the specific create() call (not just after super.build_phase()), the factory doesn't retroactively update already-created objects. In deep hierarchies, a parent component creates children in its own build_phase, which runs during the super.build_phase() cascade. If you register the instance override in your test AFTER super.build_phase() completes, the child was already created without it. Solution: always register all overrides — both type and instance — before calling super.build_phase() in the test's build_phase.

Best Practices — Factory Debug Habits That Save Hours

PracticeWhen to ApplyEngineering Benefit
Add factory.print(1) in start_of_simulationAlways in development; gate with plusarg in regressionConfirms override state before any transactions run — catches misconfiguration early
Verify get_type_name() for every overridden componentAfter any override registration, in start_of_simulation_phaseConfirms factory actually applied the override — catches timing and bypass bugs
Use print_topology() before setting instance override pathsDevelopment phase, when adding new instance overridesGets the correct path string — eliminates "silent no-match" bugs
$cast to confirm type compatibilityAfter getting a component with potential override, before using subtype methodsProvides a runtime type safety check that prevents null-dereference in wrong-type scenarios
Grep for "= new(" in VIP code before debugging overridesWhen factory.print() shows override but get_type_name() shows base typeImmediately identifies new() bypass — the most common override failure cause
Gate debug output with plusargProduction regression environmentsAvoids log pollution; enables on-demand debug: +UVM_FACTORY_DEBUG=1

💡 Pro Tip — Plusarg-Controlled Factory Debug

In production environments, you never want factory debug output in every run. Gate it behind a plusarg check so it's on-demand: function void start_of_simulation_phase(uvm_phase phase); super.start_of_simulation_phase(phase); if($test$plusargs("FACTORY_DEBUG")) begin $display("\n=== Factory State ==="); uvm_factory::get().print(1); $display("\n=== Topology ==="); uvm_root::get().print_topology(); end endfunction // Run clean: vsim ... (no debug output) // Run debug: vsim ... +FACTORY_DEBUG (full diagnostic)

Summary — Factory Debug Is a Skill, Not a Guess

Engineers who don't know factory debug spend days on problems that take five minutes with the right tools. The difference isn't expertise in UVM theory — it's knowing which diagnostic to apply and reading what the output tells you. Factory debug is a mechanical process: print state, verify type, check path, confirm timing.

ToolWhat It AnswersWhen to Use First
factory.print(1)Is the override registered in the factory?Step 1 of any override debug session
comp.get_type_name()Did the override actually apply?After factory.print() confirms override is registered
$cast(sub, comp)Is the actual type compatible with expected subtype?When using subtype-specific methods after override
print_topology()What is the exact component path? What type is shown?Before setting instance overrides, after build cascade
grep "= new("Is the VIP bypassing the factory?When print() shows override but type is still base
  • 1 factory.print() + get_type_name() together. These two tools answer the full diagnostic question: "Is the override registered?" + "Did it apply?" If print() shows it but get_type_name() shows the base type, you have a bypass or timing problem.
  • 2 Override chains resolve recursively. If A → B and B → C, requesting A gives C. Instance overrides stop chain resolution at that path — c2 with inst_override to B won't follow the B → C type override.
  • 3 Gate debug output with plusargs. Factory debug should be on-demand, not always-on. One plusarg check in start_of_simulation_phase gives you full diagnostic power without cluttering regression logs.