Skip to content

Type vs Instance Override

When to choose type override vs instance, chain overrides, five dangerous pitfalls.

UVM Fundamentals · Module 6

The Decision Framework — Type vs. Instance

The choice between type override and instance override is not stylistic. It is determined by scope: how many instances do you want to replace? Need to substitute a UVM class?Should ALL instances be replaced?YESTYPE OVERRIDEset_type_override()global — all instances affectedNOSpecific instance by path?(known hierarchy path)YESINSTANCE OVERRIDEset_inst_override()surgical — one path onlyNOOnly configuration differs?(values, not behaviour)YESUSE uvm_config_dboverride is the wrong tool hereNORECONSIDER DESIGNmay be architecture issue Figure 1 — Decision tree for choosing between type override, instance override, and config_db. Follow from top. The answer is almost always one of the first two branches. Use TYPE OVERRIDE when…

  • You want to replace every instance of the class across the whole simulation
  • You are writing a dedicated test scenario — e.g., an error-injection test that runs the entire testbench with an error-injecting driver
  • The replacement is contextual to the test, not the environment structure
  • You want the VIP code and environment code completely untouched
  • Example: all apb_driver instances → slow_apb_driver (adds wait states to every transaction) Use INSTANCE OVERRIDE when…
  • You have a specific component at a known hierarchy path that needs the replacement
  • Other instances of the same class must continue with the original behaviour
  • You are in a multi-interface environment and only one interface needs the variant
  • You need to inject a debug monitor into one agent without affecting the others
  • Example: only uvm_test_top.env.ahb_agent.drverror_drv, while all other agents keep their original driver

Type Override Deep Dive — Scope and Subtleties

What "Global" Actually Means

Type override is global with respect to the factory registry — not with respect to time. It replaces every instance created after the override is registered. Instances created before the override was set keep the original type. In practice, because all components are created in build_phase and the override is set at the start of build_phase, this distinction rarely matters — but it matters when you try to override inside a sequence during run_phase.

Multiple Type Overrides for the Same Class

Only one type override can be active for any given source class at a time. Calling set_type_override() a second time for the same source class replaces the previous override — it does not accumulate. The last call before component creation wins.

SystemVerilog — last set_type_override wins for same source type
function void build_phase(uvm_phase phase);
// Override 1 registered first
apb_driver::type_id::set_type_override(slow_apb_driver::get_type());
 
// Override 2 registered second — REPLACES Override 1
apb_driver::type_id::set_type_override(error_inject_driver::get_type());
 
super.build_phase(phase);
// Result: apb_driver instances become error_inject_driver, NOT slow_apb_driver
// slow_apb_driver override was silently discarded
endfunction
 
// If you want BOTH behaviours, create a class that extends both:
class slow_error_driver extends apb_driver;   // combines both variants
`uvm_component_utils(slow_error_driver)
// implement both slow and error behaviours here
endclass

Instance Override Deep Dive — Path Patterns and Priority

How Path Matching Works

Instance override matches the full path returned by get_full_name() of the component at creation time. The factory evaluates the pattern against the concatenation of the parent's full name and the new component's name argument.

PatternMatchesDoes Not Match
"uvm_test_top.env.agent.drv"Exactly that one componentAny other path
"*.agent.drv"Any drv under any agent at any depthenv.drv (no agent in the path)
"*.agent_0.*"Every component directly under agent_0Components two or more levels under agent_0
"uvm_test_top.*"Every direct child of the test rootGrandchildren and deeper
"*drv*"Any component whose name contains "drv" anywhereComponents with names that don't contain "drv"

Instance Override vs. Type Override Priority

When both a type override and an instance override are registered for the same source type, the instance override always wins for any path it matches. The type override acts as the fallback for all unmatched paths.

SystemVerilog — type and instance overrides coexisting
function void build_phase(uvm_phase phase);
// Type override: ALL apb_driver instances → slow_driver (default)
apb_driver::type_id::set_type_override(slow_apb_driver::get_type());
 
// Instance override: ONLY agent_0's driver → error_inject_driver
apb_driver::type_id::set_inst_override(
error_inject_driver::get_type(),
"*.apb_agent_0.drv"
);
 
super.build_phase(phase);
// apb_agent_0.drv  → error_inject_driver  (instance override wins)
// apb_agent_1.drv  → slow_apb_driver      (type override as fallback)
// apb_agent_2.drv  → slow_apb_driver      (type override as fallback)
endfunction

Chain Overrides — Overriding an Override

The factory follows a chain: if a resolved type itself has an override, the factory continues resolving until it reaches a type with no override. This enables layered customisation — a team adds one override, a test adds another on top of it. OVERRIDE REGISTRATIONSFACTORY RESOLUTION (at create time)RESULTVIP base register:base_driver (A)Agent team override:A → Bbase_driver → protocol_driver (B)Test override:B → Cprotocol_driver → error_driver (C)create(base_driver)Factory: requested = base_driverOverride: A → B ∴ resolved = BOverride: B → C ∴ resolved = CC has no override → chain endsObject created:error_driver(type C)Handle returned as:base_driver(base type)Polymorphism routes methodcalls to error_driver Figure 2 — Chain override resolution. Each registered override is followed until no more overrides exist. The base type is used as the return handle; polymorphism does the rest.

Overriding Objects — Transactions and Sequences

Module 5 focused on component overrides. The factory works identically for uvm_object-derived classes — transactions, sequences, configuration objects. The API is the same. The use cases are different.

SystemVerilog — overriding transactions and sequences
// ── Use case 1: Override the transaction to add timing fields ────────
class apb_debug_seq_item extends apb_seq_item;
`uvm_object_utils(apb_debug_seq_item)   // `uvm_object_utils, not component
 
bit [63:0] issue_time;     // simulation time when transaction was issued
bit [63:0] complete_time;  // simulation time when DUT responded
string     debug_tag;      // user-defined label for the waveform
 
function new(string name = "apb_debug_seq_item");
super.new(name);
endfunction
endclass
 
// Override in your debug test — driver gets apb_debug_seq_item from sequencer
class debug_test extends base_test;
function void build_phase(uvm_phase phase);
// Transactions become debug transactions — driver still uses apb_seq_item handle
apb_seq_item::type_id::set_type_override(apb_debug_seq_item::get_type());
super.build_phase(phase);
endfunction
endclass
 
// ── Use case 2: Override the sequence to inject directed stimulus ─────
class corner_case_seq extends apb_base_seq;
`uvm_object_utils(corner_case_seq)
function new(string name = "corner_case_seq"); super.new(name); endfunction
task body();
// Directed corner-case stimulus — overrides random stimulus
endtask
endclass
 
// In the test: swap the default random sequence with the corner-case one
apb_base_seq::type_id::set_type_override(corner_case_seq::get_type());

Five Dangerous Pitfalls

These are the five scenarios where overrides fail silently or cause unexpected results. Every one of them has been encountered in production environments.

  • P1 Component created with new() — override has no effect If the agent uses drv = new("drv", this) instead of apb_driver::type_id::create("drv", this), the factory is bypassed entirely. The override is registered but never consulted. Fix: Replace every new() call in VIP and environment code with type_id::create(). Search your codebase for = new( in component build phases.
  • P2 Override class missing uvm_*_utils — factory cannot find the type The factory resolves types by their registered wrapper (type_id). If the override class has no ``uvm_component_utils or ``uvm_object_utils, it has no type_id. Calling error_inject_driver::get_type()` returns null. The override silently fails. Fix: Add the macro to the override class. Both the original and the override must be registered.
  • P3 Override registered after super.build_phase() — the component already exists The driver is created deep inside the build_phase cascade triggered by super.build_phase(). Registering the override after that call means the target was created with the original type and the override will never be used in this simulation. Fix: Always register overrides before super.build_phase(). If using base test patterns, register at the very start of build_phase.
  • P4 Override class does not extend the original — factory fatal The factory enforces a type-safety check: the override type must be a subtype of the requested type. If error_inject_driver does not extend apb_driver, the factory throws uvm_fatal at runtime: "Override type is not a subtype of requested type." Fix: The override class must extend the original: class error_inject_driver extends apb_driver;. This is mandatory, not optional.
  • P5 Instance override path doesn't match get_full_name() — no match, original used The path pattern in set_inst_override() is matched against the component's actual full path at creation time. If the path differs by even one character (wrong agent name, wrong depth, extra prefix), the pattern doesn't match and the original type is created without any warning. Fix: Use uvm_factory::get().print() in start_of_simulation_phase to see registered overrides and which ones matched (covered in Module 7).

Override Doesn't Work — Debug Checklist

Run through this checklist in order when an override has no visible effect. Each check eliminates one category of failure.

  • Original class has uvm_component_utils` or uvm_object_utils`.
  • Override class has uvm_component_utils` or uvm_object_utils`.
  • Override class extends the original class (not just a sibling or unrelated class).
  • Both classes are in packages that are compiled and imported.
  • set_type_override() or set_inst_override() is called before super.build_phase().
  • The component is created with type_id::create(), not direct new().
  • For instance override: verify the path matches get_full_name() of the target component exactly.
  • No second set_type_override() call later is overwriting the first registration.
  • Run uvm_factory::get().print() in start_of_simulation_phase and confirm the override appears in the output.
  • Add +UVM_FACTORY_OVERRIDE_TRACE plusarg (simulator-specific) to see real-time override resolution messages.
SystemVerilog — adding factory debug print to start_of_simulation
// Add this temporarily to any component to see what the factory knows
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// Print the complete factory state: registry + override table
uvm_factory::get().print(1);   // 1 = verbose, shows all registered types and overrides
 
// You will see output like:
// #### UVM Factory Configuration (#types registered: 47) ####
// No type overrides are registered.
//   -- or --
// Type Overrides:
//   apb_driver  →  error_inject_driver
//
// If your override does NOT appear here, one of P1–P4 applies.
// If it DOES appear but the wrong type is created, P5 applies.
endfunction
 
// ── Confirm what type was actually created ────────────────────────
// In the agent's build_phase after create():
`uvm_info("FACTORY_CHK",
$sformatf("drv is a: %s", drv.get_type_name()),
UVM_LOW)
// If override worked: prints "error_inject_driver"
// If override failed: prints "apb_driver"

Quick Reference

ScenarioUseCode Pattern
Replace all instances of a classType overrideA::type_id::set_type_override(B::get_type())
Replace one instance at a known pathInstance overrideA::type_id::set_inst_override(B::get_type(), "path")
Replace most instances, one exceptionType + InstanceType override for all, then instance override for the exception
Layered override (team + test)Chain overrideRegister A→B at team level, B→C at test level; factory resolves A→C
Override a transaction typeType override on objectapb_seq_item::type_id::set_type_override(debug_item::get_type())
Override a sequence typeType override on objectbase_seq::type_id::set_type_override(corner_seq::get_type())
Debug: check what was createdget_type_name()drv.get_type_name() — should print override class name
Debug: print factory statefactory.print()uvm_factory::get().print(1) in start_of_simulation

Code Examples — Override Patterns From Basic to Production

Abstract rules about "type vs instance" click into place the moment you run these patterns and see the output. Each example isolates one concept so the behavior is unambiguous. Work through them in order.

Example 1 — Beginner: Type Override Changes Every Instance

SystemVerilog — Beginner: Type Override Affects All Instances
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// Base driver and extended version ─────────────────────────────────
class base_drv extends uvm_driver#(uvm_sequence_item);
`uvm_component_utils(base_drv)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string mode(); return "NORMAL"; endfunction
function void build_phase(uvm_phase p);
super.build_phase(p);
$display("  [%s] type=%-18s mode=%s", get_full_name(), get_type_name(), mode());
endfunction
endclass
 
class err_drv extends base_drv;
`uvm_component_utils(err_drv)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string mode(); return "ERROR_INJECT"; endfunction
endclass
 
class type_ovr_test extends uvm_test;
`uvm_component_utils(type_ovr_test)
base_drv d0, d1, d2;
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Register override BEFORE super.build_phase()
base_drv::type_id::set_type_override(err_drv::get_type());
super.build_phase(phase);
// ALL three drivers get err_drv — type override is global
d0 = base_drv::type_id::create("d0", this);
d1 = base_drv::type_id::create("d1", this);
d2 = base_drv::type_id::create("d2", this);
endfunction
 
task run_phase(uvm_phase p);
p.raise_objection(this); p.drop_objection(this);
endtask
endclass
 
module type_ovr_top;
initial run_test("type_ovr_test");
endmodule
 
// Expected Output:
//   [uvm_test_top.d0] type=err_drv          mode=ERROR_INJECT
//   [uvm_test_top.d1] type=err_drv          mode=ERROR_INJECT
//   [uvm_test_top.d2] type=err_drv          mode=ERROR_INJECT
// ↑ All three changed — that is what TYPE override does

Example 2 — Intermediate: Instance Override — One Path, Surgical Precision

SystemVerilog — Intermediate: Instance Override on Exactly One Driver
`include "uvm_macros.svh"
import uvm_pkg::*;
 
class inst_ovr_test extends uvm_test;
`uvm_component_utils(inst_ovr_test)
base_drv d0, d1, d2;   // same three drivers
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Override ONLY the instance at path "uvm_test_top.d1"
base_drv::type_id::set_inst_override(
err_drv::get_type(),
"uvm_test_top.d1");   // ← exact path
super.build_phase(phase);
d0 = base_drv::type_id::create("d0", this);   // → base_drv
d1 = base_drv::type_id::create("d1", this);   // → err_drv (override)
d2 = base_drv::type_id::create("d2", this);   // → base_drv
endfunction
 
task run_phase(uvm_phase p);
p.raise_objection(this); p.drop_objection(this);
endtask
endclass
 
module inst_ovr_top;
initial run_test("inst_ovr_test");
endmodule
 
// Expected Output:
//   [uvm_test_top.d0] type=base_drv         mode=NORMAL        ← unchanged
//   [uvm_test_top.d1] type=err_drv          mode=ERROR_INJECT  ← ONLY d1 changed
//   [uvm_test_top.d2] type=base_drv         mode=NORMAL        ← unchanged

Example 3 — Verification: Combining Both — Global Override + One Exception

SystemVerilog — Verification: Type Override + Instance Exception
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// Three driver types ────────────────────────────────────────────────
// base_drv and err_drv defined above, plus:
class slow_drv extends base_drv;
`uvm_component_utils(slow_drv)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string mode(); return "SLOW_TIMING"; endfunction
endclass
 
class combined_test extends uvm_test;
`uvm_component_utils(combined_test)
base_drv d0, d1, d2, d3;
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
// Step 1: Global type override — ALL drivers become err_drv
base_drv::type_id::set_type_override(err_drv::get_type());
 
// Step 2: Instance override — d2 specifically gets slow_drv
// Instance override takes precedence over type override
base_drv::type_id::set_inst_override(
slow_drv::get_type(),
"uvm_test_top.d2");
 
super.build_phase(phase);
d0 = base_drv::type_id::create("d0", this);   // → err_drv  (type ovr)
d1 = base_drv::type_id::create("d1", this);   // → err_drv  (type ovr)
d2 = base_drv::type_id::create("d2", this);   // → slow_drv (inst ovr wins)
d3 = base_drv::type_id::create("d3", this);   // → err_drv  (type ovr)
endfunction
task run_phase(uvm_phase p);
p.raise_objection(this); p.drop_objection(this);
endtask
endclass
 
module combined_top;
initial run_test("combined_test");
endmodule
 
// Expected Output:
//   d0: type=err_drv   mode=ERROR_INJECT   ← type override
//   d1: type=err_drv   mode=ERROR_INJECT   ← type override
//   d2: type=slow_drv  mode=SLOW_TIMING    ← instance override WINS
//   d3: type=err_drv   mode=ERROR_INJECT   ← type override
//
// Instance override takes precedence over type override for that specific path.

Example 4 — Tricky: Override Object Types in the Sequence Layer

SystemVerilog — Tricky: Overriding Transaction Types (Objects)
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// Base transaction and extended version ────────────────────────────
class base_pkt extends uvm_sequence_item;
`uvm_object_utils(base_pkt)
rand bit[7:0] data;
function new(string n="base_pkt"); super.new(n); endfunction
virtual function string kind(); return "BASE"; endfunction
endclass
 
class crc_err_pkt extends base_pkt;
`uvm_object_utils(crc_err_pkt)
bit[7:0] bad_crc = 8'hFF;
function new(string n="crc_err_pkt"); super.new(n); endfunction
virtual function string kind(); return "CRC_ERROR"; endfunction
endclass
 
class pkt_ovr_test extends uvm_test;
`uvm_component_utils(pkt_ovr_test)
function new(string n, uvm_component p); super.new(n,p); endfunction
 
task run_phase(uvm_phase phase);
base_pkt p1, p2;
phase.raise_objection(this);
 
// Override ALL base_pkt objects → crc_err_pkt
base_pkt::type_id::set_type_override(crc_err_pkt::get_type());
 
// Create using base type — factory gives crc_err_pkt
p1 = base_pkt::type_id::create("p1");
p2 = base_pkt::type_id::create("p2");
 
$display("p1: type=%s kind=%s", p1.get_type_name(), p1.kind());
$display("p2: type=%s kind=%s", p2.get_type_name(), p2.kind());
 
// $cast succeeds because crc_err_pkt IS-A base_pkt
begin
crc_err_pkt crc_p;
$cast(crc_p, p1);
$display("bad_crc = 0x%0h", crc_p.bad_crc);
end
phase.drop_objection(this);
endtask
endclass
 
module pkt_ovr_top;
initial run_test("pkt_ovr_test");
endmodule
 
// Expected Output:
// p1: type=crc_err_pkt  kind=CRC_ERROR   ← factory substituted the type
// p2: type=crc_err_pkt  kind=CRC_ERROR
// bad_crc = 0xff                          ← extended fields accessible via $cast

Common Bugs — Override Failures That Are Hard to Diagnose

Override bugs share a common characteristic: they fail silently. No UVM_ERROR. No simulation crash. The wrong type quietly runs, produces wrong behavior, and the first symptom is often a miscompare in the scoreboard three hours into a 500-transaction regression test.

⚠️ Bug 1 — Type Override Applied After the Build Cascade

Symptom: get_type_name() returns "base_drv" even though the test registers an override. The override IS in the factory — factory.print() shows it — but it has no effect.

Root cause: The override was registered AFTER super.build_phase(), which already completed the create() calls. The factory had the base type during those calls and the override arrived too late.

SystemVerilog — Bug 1: Late Override vs Correct Ordering
// ❌ WRONG — override after super (too late)
function void build_phase(uvm_phase phase);
super.build_phase(phase);   // ← cascade already runs here
d0 = base_drv::type_id::create("d0", this);  // gets base_drv
base_drv::type_id::set_type_override(err_drv::get_type()); // ← TOO LATE
endfunction
// factory.print() shows the override IS registered
// but d0 is already created — it's base_drv
 
// ✓ CORRECT — override before super (in time)
function void build_phase(uvm_phase phase);
base_drv::type_id::set_type_override(err_drv::get_type()); // ← FIRST
super.build_phase(phase);   // ← cascade sees the override
d0 = base_drv::type_id::create("d0", this);  // gets err_drv ✓
endfunction

⚠️ Bug 2 — Instance Path Wrong: Override Silently Doesn't Match

Symptom: Instance override registered, but the target component has the base type. No error from UVM. The path string you provided doesn't match get_full_name() at creation time.

Root cause: Path strings in instance overrides must exactly match the component's get_full_name(). If the component is inside an agent (e.g., uvm_test_top.agent0.drv) but you wrote uvm_test_top.drv, no match is found and the factory silently uses the type-level lookup (which returns base type).

SystemVerilog — Bug 2: Wrong Instance Path
// ❌ WRONG — path doesn't include the agent hierarchy
base_drv::type_id::set_inst_override(
err_drv::get_type(),
"uvm_test_top.drv");   // ← wrong! driver is inside agent0
// Actual path: "uvm_test_top.agent0.drv"
// Factory: no match found → falls back to type lookup → base_drv
 
// ── Debug: find the correct path ─────────────────────────────────
// In start_of_simulation_phase:
uvm_root::get().print_topology();
// Output shows: uvm_test_top.agent0.drv (agent0)
// ↑ Use this exact string in set_inst_override()
 
// ✓ CORRECT — use the exact path from print_topology()
base_drv::type_id::set_inst_override(
err_drv::get_type(),
"uvm_test_top.agent0.drv");   // ← exact match
 
// Alternative: wildcards (works in most UVM implementations)
// set_inst_override(err_drv::get_type(), "uvm_test_top.*.drv");

🔍 Debug Insight — Override Not Working? Check These In Order

  1. Is the override registered before super.build_phase()? Move it to the first line of build_phase.
  2. Does the VIP use new() instead of type_id::create()? grep -rn "= new(" src/vip/ — any hit bypasses the factory.
  3. Does the override class extend the base class? UVM rejects cross-type overrides.
  4. Run factory.print(): uvm_factory::get().print(1) in start_of_simulation_phase — shows all registered overrides.
  5. Run print_topology(): Get the exact path of the target component, then verify it matches your inst_override string.
  6. Check get_type_name() at runtime: $display("actual type: %s", comp.get_type_name()) — if it's the base type, override didn't apply.

Ready-to-Run: Complete Override Comparison Demo

This single-file testbench demonstrates type override, instance override, combined operation, and factory diagnostics side-by-side. Save as override_demo.sv and run it directly.

SystemVerilog — override_demo.sv (Complete, Ready-to-Run)
// ================================================================
// override_demo.sv — Type vs Instance Override Comparison
//
// Compile (Questa):
//   vlog -sv -L uvm_1_2 override_demo.sv
//   vsim -c -L uvm_1_2 override_demo_top -do "run -all; quit"
//
// Compile (VCS):
//   vcs -sverilog -ntb_opts uvm-1.2 override_demo.sv && ./simv
//
// Compile (Xcelium):
//   xrun -sv -uvm override_demo.sv
// ================================================================
 
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Three driver flavors ───────────────────────────────────────────
class drv_a extends uvm_component;
`uvm_component_utils(drv_a)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string flavor(); return "A-BASE"; endfunction
function void build_phase(uvm_phase p);
super.build_phase(p);
$display("  %-30s %-12s %s", get_full_name(), get_type_name(), flavor());
endfunction
endclass
 
class drv_b extends drv_a;
`uvm_component_utils(drv_b)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string flavor(); return "B-ERROR"; endfunction
endclass
 
class drv_c extends drv_a;
`uvm_component_utils(drv_c)
function new(string n, uvm_component p); super.new(n,p); endfunction
virtual function string flavor(); return "C-PERF"; endfunction
endclass
 
// ── Test: shows all three scenarios in sequence ────────────────────
class override_demo_test extends uvm_test;
`uvm_component_utils(override_demo_test)
drv_a d[4];
function new(string n, uvm_component p); super.new(n,p); endfunction
 
function void build_phase(uvm_phase phase);
$display("\n%-30s %-12s %s", "PATH", "TYPE", "FLAVOR");
$display({60{"-"}});
 
// Scenario: type override (all) + instance override (one exception)
drv_a::type_id::set_type_override(drv_b::get_type());   // global: A→B
drv_a::type_id::set_inst_override(                       // d[2] exception: →C
drv_c::get_type(), "uvm_test_top.d2");
 
super.build_phase(phase);
foreach(d[i])
d[i] = drv_a::type_id::create($sformatf("d%0d",i), this);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
$display("\n=== Factory diagnostic ===");
uvm_factory::get().print(1);
phase.drop_objection(this);
endtask
endclass
 
module override_demo_top;
initial run_test("override_demo_test");
endmodule
 
// ================================================================
// Expected Output:
//
// PATH                           TYPE         FLAVOR
// ------------------------------------------------------------
//   uvm_test_top.d0              drv_b        B-ERROR   ← type override
//   uvm_test_top.d1              drv_b        B-ERROR   ← type override
//   uvm_test_top.d2              drv_c        C-PERF    ← instance override wins
//   uvm_test_top.d3              drv_b        B-ERROR   ← type override
//
// === Factory diagnostic ===
// (shows registered types, active type overrides, active instance overrides)
// ================================================================

Interview Questions — Override-Specific Questions From Real Interviews

Beginner Level

  • Q1 When would you use a type override vs an instance override? Answer: Type override when you want the change to apply globally — every instance of that class gets the new type, regardless of where it is in the hierarchy. Instance override when you need surgical precision — only the component at a specific path gets the new type, everything else remains unchanged. Real-world rule of thumb: type override for "change all drivers to error mode in this test"; instance override for "change only the PCIe agent's driver while leaving all USB agents normal."
  • Q2 If both a type override and instance override exist for the same component, which wins? Answer: Instance override wins for the specific path, type override applies to all other instances. The factory checks instance overrides first (path-specific match), then falls back to type override if no instance match is found. This allows you to set a global type override and then selectively revert specific instances back to base or to a third type using instance overrides.

Intermediate Level

  • Q3 You register an instance override but the component still gets the base type. List three possible causes. Answer: (1) Wrong path string — the path passed to set_inst_override() doesn't exactly match the component's get_full_name(). Use print_topology() to verify. (2) Late registration — the override was registered after super.build_phase() when create() calls had already executed. Register before super.build_phase(). (3) VIP uses new() directly — the component is created with direct new() rather than type_id::create(), bypassing the factory entirely. Search for = new( in VIP code.

Senior / Architect Level

  • Q4 Your project has 12 test variants that each need different combinations of driver types. How do you architect this without creating 12 test classes with 12 sets of override registrations? Answer: Use a base test class with a virtual configure() method. The base test calls configure() in build_phase before the super call. Each derived test overrides configure() to register exactly the overrides it needs. Since configure() runs before super.build_phase(), all overrides are in place before any create() calls. Alternatively, use plusarg-driven override selection: the base test reads +DRV_TYPE from the command line and registers the corresponding override — one test class, 12 command-line configurations. This is the production-scale approach used on large SoC verification programs.

Best Practices — Override Rules From Production Code Review

RuleCorrect PatternWhy It Matters
Override before super.build_phase()First two lines of build_phase: overrides, then superSuper triggers the cascade — overrides registered after it miss the create() calls
Verify path with print_topology()Add print_topology() to start_of_simulation_phase during debugInstance override paths must exactly match get_full_name() — wrong paths fail silently
Use get_type_name() to confirm$display("actual: %s", comp.get_type_name())Confirms whether factory gave you what you asked for — most reliable diagnostic
Override class must extend base classclass err_drv extends base_drvFactory enforces subtype relationship; unrelated types produce UVM_FATAL
Prefer type override for uniform changesset_type_override for "all X become Y"Simpler, fewer paths to maintain — one line changes all instances
Use instance override for exceptionsset_inst_override for "X at path P becomes Y"Surgical precision without disturbing other instances
Document every override in test comments// Override: base_drv → err_drv (all instances)Override effects are invisible from VIP code — tests must be self-documenting

💡 Pro Tip — Plusarg-Driven Override Selection

For large projects with many test variants, drive override selection from the command line rather than creating separate test classes: function void build_phase(uvm_phase phase); string drv_type; if($value$plusargs("DRV_TYPE=%s", drv_type)) begin case(drv_type) "error": base_drv::type_id::set_type_override(err_drv::get_type()); "slow": base_drv::type_id::set_type_override(slow_drv::get_type()); endcase end super.build_phase(phase); endfunction // Run: vsim ... +DRV_TYPE=error → all drivers become err_drv // Run: vsim ... +DRV_TYPE=slow → all drivers become slow_drv // Run: vsim ... → all drivers are base_drv (no override)

Summary — Choosing the Right Override for the Job

Type override and instance override solve the same fundamental problem — component substitution without touching VIP source — but at different scopes. Picking the wrong one doesn't cause a crash; it causes incorrect behavior that's hard to trace because the factory runs silently.

DimensionType OverrideInstance Override
ScopeGlobal — ALL instances of the classPath-specific — ONE component at a given path
APIT::type_id::set_type_override(U::get_type())T::type_id::set_inst_override(U::get_type(), "path")
PriorityLower — instance override wins for matched pathsHigher — beats type override at its specific path
Use whenAll instances need the same behavior changeOne specific component needs different behavior
Common failureRegistered after super.build_phase()Wrong path string — silent no-match
  • 1 Both fail silently. No UVM_ERROR. No crash. The wrong type quietly runs. Always verify with get_type_name() and factory.print() when override behavior seems off.
  • 2 Timing is everything. The override must be registered before super.build_phase() triggers the creation cascade. This is the single most common override bug across all UVM projects.
  • 3 Instance override requires an exact path. Use print_topology() to get the correct path string. A near-miss doesn't warn you — the factory silently falls through to type-level lookup.