Day 12 / 25
← Day 11 Day 13 →
HomeVerificationDay 12 — UVM Factory
Track 2 — UVM Core

UVM Factory

By EcrioniX · Updated June 2026

The UVM factory is the mechanism that makes testbench reuse possible without editing source code. It intercepts every object and component creation request, checks for registered overrides, and returns the right type — all transparently. Tests can swap entire drivers, sequences, or scoreboards without touching the original agent code.

⏱ 26 min read📖 Day 12 of 25🎯 Factory · Overrides · Registration
Contents
  1. Why the Factory
  2. Registration Macros
  3. type_id::create() — How it Works
  4. Type Override
  5. Instance Override
  6. Factory Debug
  7. Override Use Cases
  8. Component vs Object
  9. Complete Override Example
  10. FAQ

1. Why the Factory

Without the factory, swapping a component requires editing the environment source code — changing new my_driver() to new err_driver() everywhere the original appears. This is fragile and means you cannot run both a clean test and an error-injection test from the same environment without forking the code.

With the factory, the environment always creates my_driver via my_driver::type_id::create(). A test registers an override before build_phase and the factory silently substitutes err_driver at creation time. The environment never needs to know:

// Without factory — must edit environment source
driver = new err_driver("driver", this);   // ← fragile, need to edit agent

// With factory — test registers override, environment unchanged
// In the environment (never changes):
driver = my_driver::type_id::create("driver", this);

// In error-injection test (override registered before build):
my_driver::type_id::set_type_override(err_driver::get_type());
// Now every create("driver",...) of my_driver returns an err_driver

2. Registration Macros

Every class that participates in the factory must register itself using one of the following macros. These macros generate the boilerplate type_id class and static registration hooks:

MacroUse forNotes
`uvm_component_utils(T)Components (driver, monitor, agent, env, scoreboard)Includes parent arg in create()
`uvm_object_utils(T)Objects (seq_item, sequence, transaction)No parent arg needed
`uvm_component_utils_begin(T) ... `uvm_component_utils_endComponent with field automationAdd `uvm_field_* macros inside
`uvm_object_utils_begin(T) ... `uvm_object_utils_endObject with field automationEnables copy/compare/print/pack
`uvm_component_param_utils(T#(P))Parameterised componentsOne registration per parameter set
// Component registration
class my_driver extends uvm_driver #(my_trans);
  `uvm_component_utils(my_driver)   // registers with factory
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  // ... tasks and functions
endclass

// Object registration with field automation
class my_trans extends uvm_sequence_item;
  rand logic [31:0] addr;
  rand logic [31:0] data;
  rand logic        write;

  `uvm_object_utils_begin(my_trans)
    `uvm_field_int(addr,  UVM_ALL_ON)
    `uvm_field_int(data,  UVM_ALL_ON)
    `uvm_field_int(write, UVM_ALL_ON)
  `uvm_object_utils_end

  function new(string name = "my_trans");
    super.new(name);
  endfunction
endclass

3. type_id::create() — How It Works Internally

When you call my_driver::type_id::create("driver", this), the factory:

  1. Looks up my_driver in its type registry by string name and/or type handle
  2. Checks if any type or instance override is registered for my_driver at the requested hierarchical path
  3. If an override exists, instantiates the override class instead
  4. Returns the created object (cast is implicit for components; requires $cast for objects)
// CORRECT — always use type_id::create()
my_driver drv = my_driver::type_id::create("drv", this);

// WRONG — bypasses factory, overrides never apply
my_driver drv = new("drv", this);   // ← never do this in UVM

// For objects (seq items, sequences) — no parent arg
my_trans t = my_trans::type_id::create("t");

// For sequences started inside a task
my_seq seq = my_seq::type_id::create("seq");
seq.start(sequencer);
Never use new() in UVM: The only place new() is acceptable is inside the constructor itself (super.new(name, parent)). All other instantiation must go through type_id::create(). Even in simple throw-away code, the habit of using new() directly will eventually cause a hard-to-debug override that silently fails.

4. Type Override

A type override replaces every instance of a base class anywhere in the testbench hierarchy with the override class. The override must be registered before build_phase runs — typically in the test's build_phase before calling super.build_phase(phase):

// Method 1: via the base class type_id (recommended)
my_driver::type_id::set_type_override(err_driver::get_type());

// Method 2: via factory singleton (equivalent)
uvm_factory::get().set_type_override_by_type(
  my_driver::get_type(),
  err_driver::get_type()
);

// Method 3: by string name (avoid — error-prone typos)
factory.set_type_override_by_name("my_driver", "err_driver");

// In an error-injection test
class err_test extends base_test;
  `uvm_component_utils(err_test)

  function void build_phase(uvm_phase phase);
    // Register override BEFORE super.build_phase() triggers environment build
    my_driver::type_id::set_type_override(err_driver::get_type());
    super.build_phase(phase);   // now builds err_driver, not my_driver
  endfunction
endclass

5. Instance Override

An instance override applies only to a component at a specific hierarchical path. This is more targeted than a type override and takes precedence over it:

// Replace only the driver at this specific path
my_driver::type_id::set_inst_override(
  err_driver::get_type(),
  "uvm_test_top.env.agent.driver"   // exact hierarchical path
);

// Wildcards are supported — replace all drivers under any agent
my_driver::type_id::set_inst_override(
  err_driver::get_type(),
  "uvm_test_top.env.*.driver"
);

// Via factory singleton
uvm_factory::get().set_inst_override_by_type(
  my_driver::get_type(),
  err_driver::get_type(),
  "uvm_test_top.env.agent.driver"
);
Override typeScopePrecedenceUse when
Type overrideEntire testbenchLowerGlobally replacing all instances of a class
Instance overrideSpecific path onlyHigher — wins over type overrideReplacing one specific component in a multi-agent env

6. Factory Debug

When overrides don't seem to take effect, use the factory's built-in debug tools:

// Print all registered factory types and active overrides
uvm_factory::get().print(1);   // 1 = print all; 0 = overrides only

// Print the full component topology after build_phase
uvm_top.print_topology();

// Plusarg: shows each override application at creation time
// Add to simulator command line:
// +UVM_FACTORY_PRINT_OVERRIDE_INFO

// Check what type the factory will create for a given context
begin
  uvm_object_wrapper w;
  w = uvm_factory::get().find_override_by_type(
        my_driver::get_type(),
        "uvm_test_top.env.agent.driver");
  `uvm_info("FAC",
    $sformatf("Factory will create: %s", w.get_type_name()),
    UVM_NONE)
end
Most common mistake: Registering the override after super.build_phase(phase) — by that time all components are already built. Always call overrides as the very first statements in the test's build_phase, before the super call.

7. Override Use Cases

Factory overrides enable a library of reuse patterns without touching source code:

8. Component vs Object

The factory handles both UVM components and UVM objects, but they differ in how they are created and where they live:

Featureuvm_componentuvm_object
Base classuvm_componentuvm_object / uvm_sequence_item
LifecycleExists for entire simulationCreated and garbage-collected per transaction
ParentHas hierarchical parentNo parent (standalone)
Registration`uvm_component_utils`uvm_object_utils
create() signatureT::type_id::create("name", parent)T::type_id::create("name")
Phase executionParticipates in UVM phasesNo phases
Examplesdriver, monitor, agent, env, testseq_item, sequence, config_object

9. Complete Override Example: Error-Injection Driver

A full example showing base driver, error-injection derived driver, and a test that swaps them via factory:

// --- Base driver (in the agent package) ---
class apb_driver extends uvm_driver #(apb_trans);
  `uvm_component_utils(apb_driver)
  virtual apb_if vif;

  function new(string name, uvm_component parent); super.new(name,parent); endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if(!uvm_config_db#(virtual apb_if)::get(this,"","vif",vif))
      `uvm_fatal("VIF","No APB vif")
  endfunction

  task run_phase(uvm_phase phase);
    apb_trans req;
    forever begin
      seq_item_port.get_next_item(req);
      drive_normal(req);             // normal APB transfer
      seq_item_port.item_done();
    end
  endtask

  virtual task drive_normal(apb_trans req);
    @(vif.master_cb);
    vif.master_cb.paddr  <= req.addr;
    vif.master_cb.pwdata <= req.data;
    vif.master_cb.pwrite <= req.write;
    vif.master_cb.psel   <= 1;
    @(vif.master_cb);
    vif.master_cb.penable <= 1;
    @(vif.master_cb iff vif.master_cb.pready);
    vif.master_cb.psel    <= 0;
    vif.master_cb.penable <= 0;
  endtask
endclass

// --- Error-injection driver (in test package) ---
class apb_err_driver extends apb_driver;
  `uvm_component_utils(apb_err_driver)

  function new(string name, uvm_component parent); super.new(name,parent); endfunction

  // Override drive_normal to inject a SLVERR on every other transaction
  int txn_count = 0;
  virtual task drive_normal(apb_trans req);
    txn_count++;
    if (txn_count % 2 == 0) begin
      `uvm_info("ERR_DRV", "Injecting SLVERR", UVM_MEDIUM)
      @(vif.master_cb);
      vif.master_cb.psel    <= 1;
      vif.master_cb.paddr   <= req.addr ^ 32'hDEAD; // corrupted addr
      vif.master_cb.penable <= 1;
      @(vif.master_cb);
      vif.master_cb.psel    <= 0;
      vif.master_cb.penable <= 0;
    end else
      super.drive_normal(req);   // normal path for odd transactions
  endtask
endclass

// --- Error-injection test ---
class err_inject_test extends base_test;
  `uvm_component_utils(err_inject_test)

  function new(string name, uvm_component parent); super.new(name,parent); endfunction

  function void build_phase(uvm_phase phase);
    // Override BEFORE super.build_phase — factory checks at creation time
    apb_driver::type_id::set_type_override(apb_err_driver::get_type());
    `uvm_info("TEST", "Factory override: apb_driver → apb_err_driver", UVM_NONE)
    super.build_phase(phase);   // environment builds — err_driver is created
  endfunction
endclass

Key Takeaways — Day 12

Frequently Asked Questions

Why should I never use new() directly in UVM?
Using new() directly bypasses the UVM factory, which means factory overrides have no effect on that object. If a test tries to replace your driver with an error-injection driver using a factory override, but the driver was created with new() instead of type_id::create(), the override is silently ignored. Always use MyClass::type_id::create(name, parent) so the factory can intercept the creation and return the overriding type if one has been registered.
What is the difference between type override and instance override?
A type override (set_type_override_by_type) replaces every instance of a class throughout the entire testbench hierarchy with the specified override class. An instance override (set_inst_override_by_type) replaces only the instance at a specific hierarchical path. Instance overrides take precedence over type overrides. Use type override to globally swap all agents to error-injection mode; use instance override when you only want one specific driver or agent to behave differently.
What does uvm_component_utils do?
The `uvm_component_utils macro registers the class with the UVM factory under its own type name, generates a type_id nested class that supports create() and override, and adds common utilities like get_type_name() and create(). Without this macro, the class cannot be created via type_id::create() and cannot be overridden by the factory.
How do I debug UVM factory overrides?
Use uvm_factory::get().print() to print all registered types and active overrides to the log. Also add +UVM_FACTORY_PRINT_OVERRIDE_INFO to the simulator command line to get a message every time an override is applied during object creation. Use uvm_top.print_topology() after the build_phase to see the full component hierarchy and verify which types were actually instantiated.
Next → Day 13
UVM Config DB
set() and get() API, virtual interface passing, configuration objects, path wildcards, precedence rules, and +UVM_CONFIG_DB_TRACE debug.