Introduction
Throughout my verification career, I’ve consistently used uvm_config_db
as my go-to mechanism for sharing data between components in UVM testbenches. It’s ubiquitous in training materials, example code, and verification environments. However, after reading Cliff Cummings’ illuminating paper “The Untapped Power of UVM Resources and Why Engineers Should Use the uvm_resource_db API,” I’ve come to realize I’ve been limiting myself unnecessarily.
In this post, I’ll share what I’ve learned about the differences between uvm_resource_db
and uvm_config_db
, and why I’m planning to make the switch in my future projects.
The Historical Context
First, let’s understand why we have these two APIs in the first place. According to Cummings, the resource database was designed with several goals:
- Enable virtual interfaces to be treated like other configuration items
- Remove restrictions on what types can be stored
- Detach configuration from the component hierarchy
- Provide a general-purpose mechanism for sharing data between entities
The uvm_resource_db
API was created to meet these goals. However, to ease the transition from OVM to UVM, a “convenience” layer was added called uvm_config_db
that more closely mimicked the semantics of the older OVM set_config_*
facility.
Crucially, uvm_config_db
was intended as a transitional API rather than the primary interface. Yet today, Cummings estimates that over 90% of verification engineers primarily use uvm_config_db
—myself included! Why? “Because early UVM books and examples gave this flawed recommendation”. I can’t agree more.
Key Differences: What I’ve Been Missing
1. Simpler Syntax
The uvm_config_db
API requires a component context and a string-based path, which can be confusing:
uvm_config_db#(virtual dut_if)::set(null, "*agnt*", "vif", dif);
(<font size=0.01>My idea was to always use cntxt=null
for modules/interfaces and this
for components</font>)
But uvm_resource_db
uses a straightforward string for scoping:
uvm_resource_db#(virtual dut_if)::set("*agnt*", "vif", dif);
2. Access from Anywhere
One of the most limiting aspects of uvm_config_db
is that it’s designed to work primarily with components. This creates complications when trying to access configuration from:
- Sequences (requiring p_sequencer patterns)
- Modules (requiring workarounds)
- Non-component classes
The uvm_resource_db
API eliminates these limitations, allowing any entity to store and retrieve data from the resource database.
3. Multiple Access Methods
uvm_config_db
only provides a get()
method that accesses resources by name. In contrast, uvm_resource_db
offers:
read_by_name()
– similar touvm_config_db::get()
read_by_type()
– retrieve resources based on type, which can simplify access patterns
4. Pseudo-Scopes
Perhaps the most powerful feature I’ve been missing is the ability to use “pseudo-scopes” – non-component-based scopes that allow for flexible data sharing patterns:
// Setting a resource with a pseudo-scope
uvm_resource_db#(int)::set("LCNT::*", "loop_count", 10);
// Retrieving it from anywhere
int loop_count;
uvm_resource_db#(int)::read_by_name("LCNT::*", "loop_count", loop_count);
This eliminates many of the awkward patterns I’ve had to use to share data between components and sequences.

Practical Implications: No More p_sequencer!
One of the most common patterns in UVM testbenches is the use of a “p_sequencer” to access configuration data from sequences (right? RIGHT?):
class my_sequence extends uvm_sequence;
`uvm_declare_p_sequencer(my_sequencer)
task body();
// Access config through p_sequencer
if (!uvm_config_db#(int)::get(p_sequencer, "", "param", param))
`uvm_error(...)
endtask
endclass
With uvm_resource_db
, this pattern becomes unnecessary:
class my_sequence extends uvm_sequence;
task body();
// Direct access from the sequence
if (!uvm_resource_db#(int)::read_by_name("SEQ::*", "param", param))
`uvm_error(...)
endtask
endclass
No more p_sequencer, no more virtual sequencers just for configuration passing!
Making the Transition
So, Chen, What do you say? I need to rewrite my whole testbench and use uvm_resource_db
?
The good news is that we don’t have to completely rewrite our existing testbenches. Since both APIs access the same underlying resource database, we can:
- Continue using existing
uvm_config_db
code - Start using
uvm_resource_db
for new components - Gradually replace
uvm_config_db
calls as we refactor
Any resource stored with uvm_config_db
can be accessed with uvm_resource_db
, though the reverse isn't always true (particularly for pseudo-scopes).
Conclusion
After learning about the capabilities of uvm_resource_db
, I’m convinced it’s time to make the switch. The simpler syntax, flexible access patterns, and elimination of workarounds like p_sequencer make it a superior choice for most use cases.
As Cummings puts it, the mistake wasn’t adding uvm_config_db
to UVM—it was needed for backward compatibility. The mistake was “using and promoting the uvm_config_db API as the primary interface to the resource database.”
I’m excited to start using uvm_resource_db
in my next project and to gradually refactor existing code to take advantage of its capabilities. Have you used uvm_resource_db
in your projects? I’d love to hear about your experiences in the comments!