Every t = new() freezes the concrete type at compile time. The factory replaces that call with a registry lookup, so the test picks the type without touching the generator.
The generator-injection sketch in the polymorphism lesson motivates this pattern. The registry and override below are taught only here.
Requested name | Default build | After the override |
|---|---|---|
| A fresh | A fresh |
| A fresh | A fresh |
Overrides redirect future creates only. Objects built earlier keep their old type, which is why the override lands before anything is built.
Three pieces cooperate. A parameterized creator builds one instance per type, a static registry maps names to creators, and an override map substitutes names before lookup.
Registration is an explicit call before the first create. Static-initializer self-registration looks convenient, but cross-class static init order is undefined, so entries can silently vanish.
// snippet
txn_factory::register("bus_txn", bus_creator);
txn_factory::register("err_txn", err_creator);
txn_factory::set_override("bus_txn", "err_txn");
bus_txn t = txn_factory::create("bus_txn"); // actually an err_txnThe error test installs one override before building anything. Every later request for the base name receives the error subclass, with virtual methods dispatching to it.
Direct new() calls escape this machinery. Any construction outside the factory keeps the old type under every override.
Hand-built piece | UVM equivalent |
|---|---|
|
|
Parameterized creator class |
|
|
|
|
|
UVM's preferred form is by type, through the type_id proxy: type_id::create() and set_type_override_by_type(). The type handle gives compile-time checking that a string name cannot.
The spine matches. UVM adds instance-path overrides, reporting and automation around it.
Nothing changes for existing objects. Overrides redirect future create() calls only, so the bench still runs the old type.
new() directly. What escapes?That construction bypasses the registry. Overrides never affect it, so the error test silently runs clean transactions there.
Virtual dispatch. kind() and convert2string() on the created handle run the derived methods, which only the substituted type provides.
t1 as a bus_txn, then installs an override from bus_txn to err_txn, then creates t2. What do t1.kind() and t2.kind() return?"bus_txn" and "err_txn". An override changes what later create calls build; t1 already exists and keeps its type.
Explicit registration, one override, and a testbench that proves substitution through virtual dispatch.
class bus_txn;
bit [15:0] addr;
bit [31:0] data;
virtual function string kind();
return "bus_txn";
endfunction
virtual function string convert2string();
return $sformatf("bus_txn addr=%04h data=%08h", addr, data);
endfunction
endclass
class err_txn extends bus_txn;
bit corrupt;
virtual function string kind();
return "err_txn";
endfunction
virtual function string convert2string();
if (corrupt != 0) return {super.convert2string(), " [CORRUPT]"};
return super.convert2string();
endfunction
endclass
virtual class creator_base;
pure virtual function bus_txn build();
endclass
class creator #(type T = bus_txn) extends creator_base;
virtual function bus_txn build();
T obj;
obj = new();
return obj;
endfunction
endclass
class txn_factory;
static creator_base registry[string];
static string override_map[string];
static function void register(string name, creator_base c);
registry[name] = c;
endfunction
static function void set_override(string from, string to);
override_map[from] = to;
endfunction
static function bus_txn create(string name);
string actual;
actual = name;
if (override_map.exists(name) != 0) actual = override_map[name];
if (registry.exists(actual) == 0) begin
$fatal(1, "factory: no creator registered under that name");
return null;
end
return registry[actual].build();
endfunction
endclass
PASS: registry creates by name and the override substitutes err_txnRun with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. These class benches were run on Verilator only.