Part 3 · Factory & Configuration · Intermediate
uvm_resource_db vs uvm_config_db: Layer Choice and Lookup Semantics
How config_db wraps resource_db, when hierarchical lookup wins, and when flat scope+name resource pools are the right tool.
Two layers, one storage model
Under the hood, uvm_config_db delegates to uvm_resource_db — a global typed pool keyed by scope string and field name. config_db adds hierarchical path matching on top of that flat storage.
[UVM][CONFIG] layer stack
uvm_config_db#set/get <- use this 99% of the time
|
v hierarchical lookup + wildcard path matching
uvm_resource_db <- flat scope + name keys
|
v
uvm_resource_base <- typed storage per T// config_db: test pushes cfg to env subtree
uvm_config_db#(env_cfg)::set(this, "env*", "cfg", cfg);
// resource_db: package init sets global knob
uvm_resource_db#(int)::set("global", "max_outstanding", 8, null);
int n;
if (!uvm_resource_db#(int)::read_by_name("global", "max_outstanding", n))
`uvm_warning("RES", "max_outstanding not found")Key takeaways
config_db = hierarchical component configuration.
resource_db = flat scope + name for globals and package defaults.
Never store the same knob through both without documenting precedence.
Common pitfalls
Assuming config_db and resource_db entries are independent when they alias.
Using read_by_type when multiple entries exist — first match is ambiguous.
Setting resource_db entries from random components instead of central init.
Decision matrix and lookup flow
Choose the API by whether the consumer needs hierarchical context or a scope-independent global value.
When to use each
[CONFIG] use config_db when:
- passing vif to driver/monitor under an agent path
- test sets agent cfg for a specific env child
- scoped overrides per agent instance
- wildcard paths like "env.agt*" are required
[UVM] use resource_db when:
- global simulation knob with no hierarchy
- package-level default at compile/elaboration time
- debugging: dump all resources of a type
- VIP publishes package-wide protocol constants[CONFIG] config_db lookup flow
consumer.get(inst, field, name, val)
-> walk instance parent chain
-> match set paths + wildcards
-> return first matching typed entry
[UVM] resource_db lookup flow
read_by_name(scope, name, val)
-> exact scope + name match in typed pool
read_by_type(val)
-> first resource of type T (order not guaranteed)Coexistence pattern
// Package init: global default via resource_db
function void init_uvm_resources();
uvm_resource_db#(int)::set("global", "verbosity_cap", UVM_MEDIUM, null);
endfunction
// Test build: scoped override via config_db
function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(env_cfg)::set(this, "env*", "cfg", cfg);
if (!uvm_config_db#(virtual apb_if)::get(this, "env.apb.drv", "vif", vif))
`uvm_fatal("NOVIF", "vif missing for apb driver")
endfunctionPublish globals once in package init or top before run_test.
Keep component-specific values in config_db with explicit paths.
Dump both pools when triaging missing-config failures.
Common pitfalls
Mixing both APIs for the same logical knob without a naming convention.
Relying on resource_db read_by_type in multi-tenant environments.
Setting config_db from deep env children when test should own policy.