Difficulty: Hard · Puzzle 3 of 6 · Topic: Factory and config_db
UVM 1.2. The test creates a my_cfg (extends base_cfg) and sets it with `uvm_config_db#(base_cfg)`. The agent reads it with `uvm_config_db#(my_cfg)`. get() returns 0 and the agent runs with a null cfg. The path and field name are right. Why, and what are two fixes? Also: would `uvm_config_db#(int)::set` be found by a `uvm_config_db#(bit [31:0])::get`?
// test build_phase
my_cfg c = my_cfg::type_id::create("c");
uvm_config_db#(base_cfg)::set(this, "env.agent", "cfg", c);
// agent build_phase
my_cfg cfg;
if (!uvm_config_db#(my_cfg)::get(this, "", "cfg", cfg))
`uvm_warning("CFG", "no cfg, using defaults")The parameter of uvm_config_db is part of the lookup key. The object's dynamic type is not.
1) A config_db entry is stored as a resource of the parameterised type given to set (here base_cfg). A get with a different parameter (my_cfg) looks in a different set of resources, so nothing is found. The dynamic type of the stored object is never consulted.
2) get() returns 0, `cfg` stays null, and the warning appears (or a later null dereference).
3) Fix A: set and get with the same parameter, e.g. both `uvm_config_db#(my_cfg)`. Fix B: get with `uvm_config_db#(base_cfg)` into a base_cfg handle and `$cast` it to my_cfg.
4) The same rule applies to scalars: a value set with `#(int)` is not found by `#(bit [31:0])` or `#(longint)`.Answer: The lookup key includes the parameter type: set with #(base_cfg) is invisible to get with #(my_cfg). Match the types, or get as base and $cast. An int set is not found by a bit [31:0] get either.
Checked by running the code in Verilator 5; the printed output supports the answer above. Hand-written model of the documented UVM 1.2 behaviour in plain SystemVerilog (Verilator), not the UVM library: a store keyed by the class parameter (per-specialization static storage) mimics the type-parameterised lookup.
Polymorphism does not help: assignment-compatible is not the same as the same config_db type, and a mismatch is silent (just a 0 return) unless you check it.
What if the test sets both `#(base_cfg)` and `#(my_cfg)` entries for the same path and field? Model answer: they are two independent resources, so each get sees only the one of its own type; there is no conflict and no precedence between them, which can hide a stale entry of the other type.