The Factory¶
The problem it solves¶
Say a test needs to inject an error: one specific driver, in one specific test, should occasionally corrupt a field before driving it. The naive fix — editing the driver class to add error-injection logic, gated by some flag — pollutes every test (including the 99% that don't want error injection) with logic that only matters to one test. The UVM factory lets you substitute a derived class in place of the original, from the test level, without touching the original class at all.
Registration¶
Every class that should be substitutable registers itself:
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver) // components
// ...
endclass
class my_seq_item extends uvm_sequence_item;
`uvm_object_utils(my_seq_item) // objects (sequence items, sequences)
// ...
endclass
These macros register the class with a global factory registry under its type name (as a string) — this string-keyed registry is exactly what makes override-by-name possible later.
create() instead of new()¶
Anywhere a component or object might need to be substituted, it must be constructed via the factory, not new() directly:
// in build_phase:
drv = my_driver::type_id::create("drv", this); // factory-aware — CAN be overridden
// NOT:
drv = new("drv", this); // bypasses the factory entirely — can never be overridden
This is the single most common reason a factory override "doesn't work": somewhere in the hierarchy, a component was constructed with plain new() instead of ::type_id::create(), so the factory never got a chance to substitute anything for it.
Type overrides¶
class error_driver extends my_driver;
`uvm_component_utils(error_driver)
// ... overridden behavior, e.g. occasional field corruption ...
endclass
// in the test's build_phase, BEFORE the env (and its driver) is built:
my_driver::type_id::set_type_override(error_driver::get_type());
super.build_phase(phase); // env/agent/driver get built here — now gets error_driver instead
Every my_driver::type_id::create(...) call anywhere in the hierarchy now instantiates error_driver instead — with zero changes to the env, agent, or driver class itself. The substitution is entirely test-side.
Instance overrides: substituting just one specific instance¶
A type override is global — every instance of my_driver anywhere gets replaced. If only one driver (say, in a multi-agent env, only agent #2's driver) needs the override, use an instance override with a hierarchical path:
Config-driven overrides without recompiling¶
The same effect can be triggered from the command line, without editing any test code:
This is what makes the factory genuinely powerful in a regression environment: a nightly regression can inject error scenarios into arbitrary tests via command-line plusargs, without a single line of test code being written for that specific run.
Why this depends on virtual methods¶
Everything here works because of ordinary SystemVerilog polymorphism (see OOP & Classes) — a factory override just means "when asked to create a my_driver, actually construct an error_driver object and return a handle typed as my_driver." Every existing call site that only ever knew about the my_driver type keeps compiling and running unchanged, because error_driver extends my_driver and virtual dispatch handles the rest. The factory isn't magic — it's a registry plus a disciplined use of the same OOP substitution principle covered earlier.
Overrides must be set before the target is built
set_type_override/set_inst_override only affects create() calls that happen after the override is registered. Setting an override in build_phase works because UVM's function-phases run top-down (test's build_phase runs before env's, which runs before agent's, which runs before driver's) — but setting an override any later than that (e.g. in connect_phase) is too late; the components are already built.