Factory & Overrides
Master the UVM factory pattern — create objects/components via the factory and use type overrides for flexibility.
UVM Factory & Overrides
The UVM factory is one of the most powerful features of UVM. It lets you create objects and components indirectly — and swap implementations without changing any testbench code.
The Problem Without a Factory
systemverilog// Hard-coded type — can't change without editing source my_driver drv = new("drv", this);
The Solution With a Factory
systemverilog// Indirect creation — type can be overridden at runtime my_driver drv = my_driver::type_id::create("drv", this);
The factory sits between your code and the actual constructor, allowing you to substitute a derived class without touching existing code.
How to Use the Factory
Step 1: Register your class
systemverilogclass my_driver extends uvm_driver #(my_txn); `uvm_component_utils(my_driver) // ← Register! // ... endclass
Step 2: Always create via the factory
systemverilog// For components (in build_phase): my_driver drv = my_driver::type_id::create("drv", this); // For objects (transactions, configs): my_txn txn = my_txn::type_id::create("txn");
Type Overrides
Global Override — Replace All Instances
systemverilogclass error_driver extends my_driver; `uvm_component_utils(error_driver) // Override run_phase to inject errors task run_phase(uvm_phase phase); // ... error injection logic ... endtask endclass // In the test's build_phase: function void build_phase(uvm_phase phase); // Every my_driver::type_id::create() now returns error_driver my_driver::type_id::set_type_override( error_driver::get_type() ); super.build_phase(phase); endfunction
Instance Override — Replace One Specific Instance
systemverilogfunction void build_phase(uvm_phase phase); // Only "env.agent.driver" is overridden my_driver::type_id::set_inst_override( error_driver::get_type(), "env.agent.driver" ); super.build_phase(phase); endfunction
Practical Example: Error Injection
systemverilog// Base transaction class base_txn extends uvm_sequence_item; `uvm_object_utils(base_txn) rand bit [7:0] data; function new(string name = "base_txn"); super.new(name); endfunction endclass // Extended: adds error injection capability class error_txn extends base_txn; `uvm_object_utils(error_txn) rand bit inject_error; // 10% of transactions will have errors constraint err_c { inject_error dist { 0 := 90, 1 := 10 }; } function new(string name = "error_txn"); super.new(name); endfunction endclass // Error test: override base_txn → error_txn globally class error_test extends base_test; `uvm_component_utils(error_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); base_txn::type_id::set_type_override( error_txn::get_type() ); super.build_phase(phase); endfunction endclass
Every
base_txn::type_id::create()call anywhere in the testbench now produces anerror_txn— without editing a single line of existing code!
Debugging the Factory
Print all registered types and active overrides:
systemverilog// In any phase: factory.print();
Factory Rules Summary
| Rule | Why |
|---|---|
Always use type_id::create(), never new() | Enables overrides |
| Always register with utility macros | Factory needs to know about your class |
| Override type must extend the original | Liskov substitution principle |
Set overrides before super.build_phase() | Components are created in super |