The complete config_db API — set() and get() signatures, the four-part key, path wildcards and precedence, what types can be stored, the exists() and dump() debug methods, and every common pitfall explained with the exact fix.
uvm_config_db is a global, type-parameterised key-value store built into the UVM infrastructure. It allows any component to place a value into the database and any other component to retrieve it by key — without either component needing a direct handle to the other.
It solves a fundamental problem in deep hierarchies: how does the test (at the top) pass configuration to a driver (at the bottom) without every intermediate component explicitly forwarding the value? The answer is config_db — the test sets the value with a path that matches the driver’s location, and the driver retrieves it without knowing who set it.
The config_db has two primary methods. The type parameter T must match exactly between set() and get() — mismatched types silently return 0 from get().
// ── set() — place a value into the database ────────────── // static function bit set( // uvm_component cntxt, // context: this component, or null for top // string inst_name, // path suffix — often a child name or wildcard // string field_name,// the key string — must match get() // T value // the value to store // ); uvm_config_db #(T)::set(cntxt, inst_name, field_name, value); // ── get() — retrieve a value from the database ─────────── // static function bit get( // uvm_component cntxt, // context: this component // string inst_name, // additional path suffix (usually "") // string field_name,// the key string — must match set() // ref T value // output: receives the stored value // ); bit found = uvm_config_db #(T)::get(cntxt, inst_name, field_name, value);
Both functions are static — they are called on the class type, not on an instance. The type parameter is the only way the database knows which “slot” to look in. A config_db #(int) and a config_db #(my_cfg) are completely separate databases even if they use the same key string.
`uvm_fatal on a failed get() for mandatory configuration, and `uvm_warning or a default value for optional configuration. Ignoring the return value and proceeding with an uninitialised variable is the most common source of mysterious simulation failures.
Every entry in config_db is identified by a four-part key: the type T, the setting scope (derived from cntxt + inst_name), and the field_name. Understanding how the scope is built is essential for writing correct set/get pairs.
// ── Concrete examples of set/get pairs ─────────────────── // Set from test — targeting env's child "m_agent" // this = uvm_test_top, inst_name = "m_env.m_agent" // scope stored = "uvm_test_top.m_env.m_agent" uvm_config_db #(apb_agent_cfg)::set( this, "m_env.m_agent", "cfg", m_agent_cfg); // Get in agent — this = uvm_test_top.m_env.m_agent, inst = "" // lookup = "uvm_test_top.m_env.m_agent" → MATCH ✓ if (!uvm_config_db #(apb_agent_cfg)::get( this, "", "cfg", m_cfg)) `uvm_fatal("CFG", "Agent config not found!") // Wildcard: set for all agents in env uvm_config_db #(apb_agent_cfg)::set( this, "m_env.*", "cfg", m_agent_cfg); // * matches any child name // Null context: set from tb_top static initial block uvm_config_db #(virtual apb_if)::set( null, "uvm_test_top", "apb_vif", apb_bus); // null context → scope = "" + inst_name = "uvm_test_top" // Accessible by test (path = "uvm_test_top") and any descendant
The inst_name parameter in set() supports SystemVerilog glob-style wildcards, allowing one set() call to reach multiple components:
| Pattern | Matches | Use case |
|---|---|---|
"" (empty) | Only the context component itself | set() to self; get() in any component using just this |
"m_agent" | The child named exactly “m_agent” | Target a specific child component |
"m_env.m_agent" | Grandchild — env’s child named m_agent | Target a deep component from a high-level parent |
"*" | Any single component at any depth | Set for all components in the hierarchy |
"m_env.*" | Any direct child of m_env | Set for all agents inside env |
"*.m_drv" | Any component named m_drv at any level | Set for all drivers regardless of their parent |
"*" alone as the path. A wildcard that matches everything makes the configuration global — every component that calls get() with the same field_name will find it, including components you did not intend to configure. Use specific paths or at least "m_env.*" to limit scope. The tighter the path, the easier the testbench is to debug.
When multiple set() calls match the same get() request, config_db returns the most specific match — the one with the longest matching path. If two entries have the same specificity, the most recently set one wins.
| Priority | Specificity | Example scope |
|---|---|---|
| Highest | Exact full path match | "uvm_test_top.env.agent.drv" |
| ↓ | Partial wildcard — more specific | "uvm_test_top.env.*.drv" |
| ↓ | Single wildcard | "*.drv" |
| Lowest | Pure wildcard match | "*" |
This precedence rule is what makes test-time overrides work cleanly. A base test can set a default configuration with a broad path. A derived test can override it for one specific component with a more precise path — and the more specific entry wins.
config_db is parameterised on any SystemVerilog type. The most common types used in practice:
| Type | Typical use | Example |
|---|---|---|
| Config object class | Agent config, env config — the primary use case | uvm_config_db #(apb_agent_cfg)::set(...) |
virtual <if> | Passing virtual interface from tb_top to test | uvm_config_db #(virtual apb_if)::set(...) |
int | Verbosity, iteration counts, enable flags | uvm_config_db #(int)::set(this, "*", "verbosity", 5) |
bit | Feature enable/disable flags | uvm_config_db #(bit)::set(this, "m_env", "has_cov", 1) |
string | Test name, log file paths | uvm_config_db #(string)::set(...) |
uvm_object | Base class — if type not known at set() time | Avoid — use specific type for type safety |
uvm_object requires $cast at get() time, adds runtime overhead, and loses type checking. Store as the actual config class type — the compiler will catch type mismatches at compile time rather than at simulation runtime.
The recommended pattern is to group all configuration for a component into a single config object class that extends uvm_object. One config object per component level — one for the env, one for each agent, one for any complex sub-component.
// ── Config object for an APB agent ─────────────────────── class apb_agent_cfg extends uvm_object; `uvm_object_utils(apb_agent_cfg) // Mandatory: virtual interface and active/passive mode virtual apb_if vif; uvm_active_passive_enum is_active = UVM_ACTIVE; // Optional sub-component enables (with safe defaults) bit has_coverage = 1; bit has_err_injection = 0; // Protocol-specific parameters int max_wait_states = 4; bit check_pslverr = 1; function new(string name = "apb_agent_cfg"); super.new(name); endfunction endclass // ── Test configures it, sets it, agent retrieves it ────── // In test build_phase: m_cfg = apb_agent_cfg::type_id::create("m_cfg"); m_cfg.is_active = UVM_ACTIVE; m_cfg.has_err_injection = 1; // override for error test uvm_config_db #(apb_agent_cfg)::set( this, "m_env.m_agent", "cfg", m_cfg); // In agent build_phase: if (!uvm_config_db #(apb_agent_cfg)::get( this, "", "cfg", m_cfg)) `uvm_fatal("CFG", "No agent config!")
For multi-agent environments the recommended pattern is to nest config objects inside each other, mirroring the component hierarchy. The test creates all config objects, populates them, nests the agent configs inside the env config, and sets only the env config into config_db. The env then re-distributes the nested agent configs to each agent.
// ── Env config holds agent configs ─────────────────────── class gpio_env_cfg extends uvm_object; `uvm_object_utils(gpio_env_cfg) apb_agent_cfg m_apb_cfg; // nested agent config bit has_sb = 1; function new(string name="gpio_env_cfg"); super.new(name); endfunction endclass // ── Test: create all, nest, set once ───────────────────── function void build_phase(uvm_phase phase); super.build_phase(phase); m_env_cfg = gpio_env_cfg::type_id::create("m_env_cfg"); m_env_cfg.m_apb_cfg = apb_agent_cfg::type_id::create("m_apb_cfg"); // Assign virtual interface into nested config if (!uvm_config_db #(virtual apb_if)::get( this, "", "apb_vif", m_env_cfg.m_apb_cfg.vif)) `uvm_fatal("TEST", "No vif!") // Set only the top-level env config — one set() call uvm_config_db #(gpio_env_cfg)::set( this, "m_env", "cfg", m_env_cfg); m_env = gpio_env::type_id::create("m_env", this); endfunction // ── Env: get top-level config, re-distribute nested configs function void build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db #(gpio_env_cfg)::get( this, "", "cfg", m_cfg)) `uvm_fatal("ENV", "No env config!") // Push nested agent config down to agent uvm_config_db #(apb_agent_cfg)::set( this, "m_agent", "cfg", m_cfg.m_apb_cfg); m_agent = gpio_agent::type_id::create("m_agent", this); endfunction
When get() unexpectedly returns 0, use the built-in debug facilities to inspect what is actually stored in config_db.
// ── Check if an entry exists without retrieving it ─────── if (uvm_config_db #(apb_agent_cfg)::exists( this, "", "cfg")) `uvm_info("CFG", "cfg exists in config_db", UVM_LOW) // ── Dump ALL config_db entries to transcript ────────────── // Very useful: shows every stored (type, scope, field) → value // Call from end_of_elaboration_phase for a clean snapshot function void end_of_elaboration_phase(uvm_phase phase); uvm_config_db #(apb_agent_cfg)::dump(); // type-specific dump endfunction // ── Wait — set() before or after get()? ────────────────── // set() in tb_top initial block → must be before run_test() // set() in test build_phase → available to all children // set() must always precede the component's build_phase // because get() is called in build_phase // ── UVM verbosity flag to enable config_db tracing ─────── // +UVM_CONFIG_DB_TRACE on command line prints every set/get
| Pitfall | Symptom | Fix |
|---|---|---|
| Type mismatch between set() and get() | get() returns 0 silently — even though the field_name and path are correct. Very hard to spot. | The T in config_db #(T) must be identical in set() and get(). A derived class type is not the same as its base class type. |
| get() return value ignored | Component silently uses uninitialised value (null handle, random int, etc). Crashes later in run_phase. | Always use `uvm_fatal or at least `uvm_warning when get() returns 0. |
| set() called after build_phase starts | Component’s build_phase runs and calls get() before the value is set. get() returns 0. | set() must be called before the target component’s build_phase. Set from the parent’s build_phase (or tb_top initial block for global values). |
| Wrong inst_name path in set() | get() returns 0. The scope stored does not match the component calling get(). | Use +UVM_CONFIG_DB_TRACE to see the exact scopes. Verify the component path with get_full_name() in the get() component. |
Using null context in get() | Lookup fails — null context means an empty base path, which rarely matches stored scopes. | Always use this as the context in get(). Only use null in set() from tb_top’s static initial block. |
| set() after child’s build_phase (top-down overlap) | Config not available to child — build is top-down, so child builds before parent finishes if parent sets after creating the child. | Always set config_db entries before calling create() on the child that needs to get() them. |
| Overusing wildcards | Unexpected components receive configuration. Debug becomes very difficult. | Use the most specific path that works. Reserve "*" for truly global settings like verbosity. |
| Item | Key fact |
|---|---|
| config_db purpose | Global type-parameterised key-value store. Decouples configuration source from consumer. |
| set() signature | config_db #(T)::set(cntxt, inst_name, field_name, value) |
| get() signature | config_db #(T)::get(cntxt, inst_name, field_name, ref value) → bit |
| T must match | Identical type in set() and get() — derived class ≠ base class |
| Scope formula | scope = cntxt.get_full_name() + "." + inst_name |
| Null context scope | null context → scope starts from "", combine with inst_name e.g. "uvm_test_top" |
| get() context | Always use this — null context in get() almost never works |
| Empty inst_name in get() | "" → lookup path = just this.get_full_name() |
Wildcard "*" | Matches any single component at any depth below cntxt |
| Precedence | Most specific path wins. Tie = most recent set() wins. |
| Always check return | if (!config_db::get(...)) `uvm_fatal(...) for mandatory config |
| set() timing | Must happen before the component that calls get() runs its build_phase |
| Recommended pattern | Store config objects (not primitives). One config class per component level. Nest configs. |
| Debug: exists() | config_db #(T)::exists(this, "", "key") — checks without retrieving |
| Debug: dump() | config_db #(T)::dump() — prints all entries of type T |
| Debug: command line | +UVM_CONFIG_DB_TRACE — prints every set() and get() with path and result |