uvm_config_db in UVM

In UVM-based verification, components like agents, drivers, and monitors are often instantiated hierarchically. Sharing configuration information, such as register settings, queues, list, time delays, protocol-specific parameters or class handles between these components is a common requirement.

The uvm_config_db is a centralized database used to store and retrieve this information. It enables decoupling between components, allowing parameters to be set at a higher level (e.g., the test) and retrieved by lower-level components without direct connections. This database is globally accessible. uvm_config_db is internally built on top of the uvm_resource_db, but it is not a direct extension (inheritance). Instead, it is a utility class that uses uvm_resource_db under the hood to manage the configuration settings.

Key Methods of uvm_config_db

1. set Method

The set method is used to store a key-value pair in the configuration database. The key acts as an identifier, while the value holds the data being shared. The set method is typically called at a higher hierarchy level.

Syntax:

uvm_config_db#(type)::set(scope, inst_name, field_name, value);
  • type: The data type of the value being stored.
  • scope: The hierarchical scope where the configuration is set.
  • inst_name: The name of the instance (or wildcard "*" for all).
  • field_name: The key used to identify the configuration value.
  • value: The actual data being stored.

2. get Method

The get method retrieves the value associated with a specific key. Components lower in the hierarchy typically use this method to fetch their configuration.

Syntax:

uvm_config_db#(type)::get(scope, inst_name, field_name, value);
  • type: The data type of the value being retrieved.
  • scope: The hierarchical scope from which to retrieve the value.
  • inst_name: The name of the instance (or wildcard "*" for all).
  • field_name: The key used to identify the configuration value.
  • value: The variable where the retrieved data will be stored.

Example: Using uvm_config_db

Let’s explore a simple example where a configuration value is set in the test and retrieved in an agent.

1. Test Class

The test sets a delay configuration value for the driver in the agent.

import uvm_pkg::*;
`include "uvm_macros.svh"
`include "driver.sv"
`include "agent.sv"

class my_test extends uvm_test;
  `uvm_component_utils(my_test)
  
  my_agent agent;

  function new(string name = "my_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    agent = my_agent::type_id::create("agent",this);
    // Setting a delay of 10 in the configuration database
    uvm_config_db#(int)::set(this, "*", "delay", 10);
  endfunction
  
  function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    uvm_top.print_topology();
  endfunction
  
endclass

module tb;
  initial begin
    run_test("my_test");
  end 
endmodule

2. Agent Class

The agent retrieves the configuration and passes it to the driver.

class my_agent extends uvm_agent;
  `uvm_component_utils(my_agent)

  my_driver driver;
  int delay;

  function new(string name = "my_agent", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // Create and retrieve configuration for the driver
    driver = my_driver::type_id::create("driver", this);

    if (uvm_config_db#(int)::get(this, "", "delay", delay)) begin
      driver.set_delay(delay); // Pass the retrieved delay to the driver
    end else begin
      `uvm_error("CONFIG", "Delay not found in configuration database")
    end
  endfunction
endclass

3. Driver Class

The driver uses the retrieved configuration during its operation.

class my_driver extends uvm_driver;
  `uvm_component_utils(my_driver)

  int delay;

  function new(string name = "my_driver", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  function void set_delay(int d);
    delay = d;
  endfunction

  virtual task run_phase(uvm_phase phase);
   forever begin
     `uvm_info("DRIVER", $sformatf("Processing transaction with delay: %0d", delay), UVM_LOW)
     #50 $finish;
    end
  endtask
endclass

Output:

UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /apps/vcsmx/vcs/U-2023.03-SP2//etc/uvm-1.2/src/base/uvm_root.svh(589) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------------------------
Name                 Type                    Size  Value
--------------------------------------------------------
uvm_test_top         my_test                 -     @336 
  agent              my_agent                -     @349 
    driver           my_driver               -     @363 
      rsp_port       uvm_analysis_port       -     @382 
      seq_item_port  uvm_seq_item_pull_port  -     @372 
--------------------------------------------------------

UVM_INFO driver.sv(17) @ 0: uvm_test_top.agent.driver [DRIVER] Processing transaction with delay: 10
UVM_INFO /apps/vcsmx/vcs/U-2023.03-SP2//etc/uvm-1.2/src/base/uvm_report_server.svh(904) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :    4
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[DRIVER]     1
[RNTST]     1
[UVM/RELNOTES]     1
[UVMTOP]     1

$finish called from file "/apps/vcsmx/vcs/U-2023.03-SP2//etc/uvm-1.2/src/base/uvm_root.svh", line 527.

Precedence Rule

The uvm_config_db resolves configuration values based on the path specificity provided in the inst_name parameter during the set method.

More specific paths override less specific paths.

  • For example, if a configuration is set at the env.agent level and another at the env.agent.sequencer level, the value set for env.agent.sequencer will take precedence when accessed by the sequencer.

Example of Precedence

Setting Configuration at Different Levels

// Set configuration at the environment level (less specific)
uvm_config_db#(int)::set(null, "env.agent", "timeout", 100);

// Set configuration at the sequencer level (more specific)
uvm_config_db#(int)::set(null, "env.agent.sequencer", "timeout", 200);

Retrieving Configuration

int timeout_value;

// In the sequencer, retrieve the "timeout" value
if (uvm_config_db#(int)::get(this, "", "timeout", timeout_value)) begin
    $display("Timeout value: %0d", timeout_value);
end else begin
    $error("Timeout configuration not found!");
end

Output:

Timeout value: 200

Explanation: When get is called in the sequencer, the uvm_config_db picks the value set specifically for env.agent.sequencer (200), as it has higher specificity compared to the value set at env.agent.

If no specific path is found i.e. inst_name is (“*”) then it will pick set() method in higher component hierarchy over the set() method in lower component hierarchy.

Detailed Example

Consider the following setup:

uvm_config_db#(int)::set(null, "*", "timeout", 50);           // set method in test
uvm_config_db#(int)::set(null, "*", "timeout", 100);    // set method in env

Retrieving Configuration

uvm_config_db#(int)::get(this, "", "timeout", timeout_value); //consider get method in driver

Value Retrieved: 50.

Key Differences Between uvm_config_db and uvm_resource_db

Featureuvm_config_dbuvm_resource_db
HierarchyUses hierarchical path matching (inst_name).Uses scope and name for resource access.
Ease of UseProvides a simplified interface for set and get.Requires more detailed control and setup.
Path MatchingHierarchical and context-aware.Based on exact resource scope and name.
Error MessagesUser-friendly error handling for missing entries.Minimal error handling (user must manage).

uvm_config_db enhances the basic uvm_resource_db with hierarchical matching, making it more intuitive for configuration in UVM testbenches.