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.agentlevel and another at theenv.agent.sequencerlevel, the value set forenv.agent.sequencerwill 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
| Feature | uvm_config_db | uvm_resource_db |
|---|---|---|
| Hierarchy | Uses hierarchical path matching (inst_name). | Uses scope and name for resource access. |
| Ease of Use | Provides a simplified interface for set and get. | Requires more detailed control and setup. |
| Path Matching | Hierarchical and context-aware. | Based on exact resource scope and name. |
| Error Messages | User-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.