Part 3 · Factory & Configuration · Intermediate
Factory vs Direct new: The Non-Negotiable Rule
Why new() bypasses override resolution, where direct construction still appears, and migration patterns for legacy code.
Why new() breaks reuse
Overrides apply only when the factory mediates construction. Direct new() hard-wires the concrete type and silently ignores all override policy.
[FACTORY][UVM] create vs new
test policy:
base_driver -> err_driver (type override)
env path A (factory):
drv = base_driver::type_id::create("drv", this)
result: err_driver built
env path B (direct new):
drv = new("drv", this); // if legal in custom code
result: base_driver built, override ignored// GOOD
drv = base_driver::type_id::create("drv", this);
// BAD — never for replaceable components
// drv = new base_driver("drv", this); // illegal for uvm_component
// Some teams wrap illegally:
// drv = base_driver::type_id::create("drv", this); // then replace handle manuallyKey takeaways
If a type should be replaceable, it must be factory-created.
new() is for non-UVM helper classes outside override policy.
A single new() in env can waste hours debugging 'ignored' overrides.
Common pitfalls
Refactoring macros but leaving old new() calls in agents.
Constructing sequences with new() when override policy is needed.
Library code that returns new() objects while env expects factory path.
Allowed and discouraged construction cases
Not every object participates in override policy. Separate replaceable UVM types from plain SV helpers.
Construction matrix
[FACTORY] construction policy
factory create required:
uvm_component children in env/agents
override-able sequences/items/cfg objects
direct new acceptable:
plain structs, interfaces wrappers, non-UVM utilities
static helpers with no factory registration
gray zone:
uvm_object created once in package init — document as fixed typeCode review grep for `= new(` under uvm_component build paths.
Lint/custom script: flag new() on classes with utils macros.
VIP docs must state which types are override targets.
Migration snippet
// legacy
function void build_phase(uvm_phase phase);
super.build_phase(phase);
mon = new("mon", this);
endfunction
// factory-correct
function void build_phase(uvm_phase phase);
super.build_phase(phase);
mon = base_monitor::type_id::create("mon", this);
endfunction[UVM] migration validation
1) replace new with type_id::create
2) add/verify utils macro
3) run test with intentional override
4) confirm get_type_name() shows derivativeCommon pitfalls
Partial migration — one agent still uses new().
Thinking compile-time type check equals factory compliance.
Using create() only at top level while sub-hierarchy uses new().