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.

diagram
[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
systemverilog
// 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

diagram
[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
diagram
[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

systemverilog
// 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")
endfunction
  • Publish 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.