UVM Config DB

A mechanism to share data/objects vertically in the testbench hierarchy without direct connections.

The Set and Get Mechanics

Why Config DB Matters

The Problem: Without Config DB, you'd have to hardcode settings in every component. Want to change the baud rate? Edit 10 files.

The Solution: Config DB lets you set values once (in the test) and they automatically reach the right components. Change one line, update everything.

The uvm_config_db is a type-safe, string-based database used to pass configuration parameters vertically (Top-to-Bottom) through the UVM hierarchy. It is the primary way to decouple components—allowing a Test to change an Agent's behavior without modifying the Agent's source code.

1. The API Breakdown

Both set() and get() require four parameters:

  • Context (uvm_component): The starting point of the search. Usually this.
  • Inst Name (string): The hierarchical path to the target component (supports wildcards like *).
  • Field Name (string): The key used to find the data.
  • Value (TYPE): The actual data being passed.
Example 1: Passing a Virtual Interface

// Inside the top-level module (harness)
initial begin
    uvm_config_db#(virtual my_if)::set(null, "uvm_test_top*", "vif", vif);
end
// Inside the Driver
virtual my_if vif;
function void build_phase(uvm_phase phase);
    if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
        `uvm_fatal("NOVIF", "Could not get virtual interface handle!")
endfunction
                            

Why use `null` as the context in the harness?

In a SystemVerilog module (top-level), there is no UVM component context. Passing null tells UVM to start the search from uvm_root. The star "uvm_test_top*" ensures the configuration is visible to the test and everything inside it.

Best Practice: Configuration Objects

Setting individual variables (int, bool, string) one by one is inefficient for large environments. Instead, professional UVM environments package all settings into a single Config Object.


// 1. Define the object
class my_agent_config extends uvm_object;
    `uvm_object_utils(my_agent_config)
    uvm_active_passive_enum is_active = UVM_ACTIVE;
    int baud_rate = 9600;
endclass
// 2. Set the object in the Test
my_agent_config cfg = my_agent_config::type_id::create("cfg");
uvm_config_db#(my_agent_config)::set(this, "env.agent*", "agent_cfg", cfg);
// 3. Get the object in the Agent
my_agent_config m_cfg;
uvm_config_db#(my_agent_config)::get(this, "", "agent_cfg", m_cfg);
                            

Accessing DUT Signals in UVM

Interview Essential: "How can I access DUT signals from a UVM component?"

Three Methods (Best to Worst):

Method Pros Cons
Virtual Interface Type-safe, reusable, professional standard Requires interface definition
Hierarchical Path Quick for backdoor access Hardcoded paths, fragile to DUT changes
uvm_hdl_* API Dynamic path resolution String-based, slow, error-prone
Method 1: Virtual Interface (Recommended)

// In Driver - Access signals through virtual interface
task run_phase(uvm_phase phase);
    forever begin
        seq_item_port.get_next_item(req);
        @(posedge vif.clk);
        vif.data  <= req.data;   // Drive signal
        vif.valid <= 1'b1;
        @(posedge vif.clk);
        vif.valid <= 1'b0;
        seq_item_port.item_done();
    end
endtask
                            
Method 2: Hierarchical Backdoor (For Debugging Only)

// Direct path access - AVOID in production code
function void force_reset();
    // Hardcoded path to DUT signal
    force tb_top.dut.internal_reset = 1'b1;
    #10ns;
    release tb_top.dut.internal_reset;
endfunction
                            
Method 3: uvm_hdl_* API (Dynamic Paths)

// Dynamic path - useful for RAL backdoor access
logic [31:0] reg_value;
string path = "tb_top.dut.regfile.config_reg";
// Read from DUT
uvm_hdl_read(path, reg_value);
// Write to DUT  
uvm_hdl_deposit(path, 32'hDEADBEEF);
                            

Best Practice

Always use virtual interfaces for signal access. Hierarchical paths and uvm_hdl_* should only be used for backdoor register access in RAL or temporary debugging. They make your testbench fragile to DUT hierarchy changes.

Architecture: Config DB vs Resource DB

Under the hood, uvm_config_db is just a convenience wrapper around uvm_resource_db. The resource database is the low-level engine that stores raw data, while the configuration database adds a layer of Path Precedence.

Feature uvm_config_db uvm_resource_db
Precedence Hierarchy wins. A set() from a higher-level component (e.g., Test) always overrides a set() from a lower-level one. Timing wins. Generally, the last write command wins.
Lookup Path-based AND String-based. Search-based (Regex on names/types).
Performance Light overhead due to path resolution string parsing. Faster for high-frequency, non-hierarchical lookups.

Debug Story: The "Silent Failure"

The Scenario: You set a timeout value of 1000ns in the top-level test, but the driver still times out at 0ns.

// Top Level (Test)
uvm_config_db#(int)::set(this, "*", "timeout", 1000);
// Low Level (Driver)
bit [31:0] timeout_val;
// FAILS silently!
uvm_config_db#(bit [31:0])::get(this, "", "timeout", timeout_val);

The Root Cause: int is a signed 32-bit type. bit [31:0] is unsigned. In UVM's string-based lookup, the type parameter #(type) is part of the key. int != bit [31:0]. The lookup fails, returns 0, and prints no error (unless you check the return bit).

The Magic Fix: Tracing

Add +UVM_CONFIG_DB_TRACE to your simulation command.

It prints every set/get. You will see:

UVM_INFO @ 0: reporter [CFGDB/SET] ... value=(int) 1000
UVM_INFO @ 0: reporter [CFGDB/GET] ... value=(bit[31:0]) ? (failed lookup)