Hierarchical Testbench Configuration Using Uvm
Hierarchical Testbench Configuration Using Uvm
Hierarchical Testbench Configuration Using Uvm
Authors
Abstract
Hannes Nurminen
Professional
Services, Synopsys
SoC designs have become extremely complex as more and more IP blocks are integrated into them. This
increases the verification challenge manifold in terms of configuration and data handling, as well as architecting
and maintaining a large verification environment. Hence it has become very important to create a robust and
reusable testbench using a proven methodology that does not just facilitate but also improves the efficiency in
verifying different configurations of the device under test (DUT).
Accellera Systems Initiatives Universal Verification Methodology (UVM), a stable and widely used methodology
for architecting testbenches for verification of complex designs helps mitigate these verification challenges
to create a scalable, robust and reusable test environment. UVM provides a vast set of macro, policy and
base classes that help facilitate the creation of these testbenches, including an easy way to pass objects and
variables across testbench hierarchy.
For engineers who are new to verification methodologies or are in the process of adopting UVM, this paper
focuses on the UVM configuration mechanism uvm _ config _ db, which helps in passing different class
properties across hierarchy testbench components. Through the use of examples, the usage, techniques, and
limitations of uvm _ config _ db are explained.
Introduction
To address the needs of todays verification architecture, a hierarchical setup of components is necessary to
easily move or share configurations and other parameters across different testbench components. To enable
this, UVM provides the infrastructure to maintain a database of objects and variables that can be populated
and accessed using strings. This is achieved using the UVM syntax uvm _ config _ db.
Using uvm _ config _ db, objects can share the handle to their data members with other objects. Other
testbench components can get access to the object without knowing where it exists in the hierarchy. Its
almost like making some class variables global or public. Any testbench component can place handles and
get handles to objects. In the database, handles are identified by assigned type and name.
Primarily there are two uvm _ config _ db functions, set() and get(). Any verification component using
set() gives others access to the object it has created and controls which components have visibility to the
object it has shared. The object can be shared globally or made available to one or more specific testbench
components. Verification components using get() check if there is a shared handle matching the used
parameters. The get() function defines the object type, the name and hierarchical path to the object
searched for.
cntxt,
inst _ name,
field _ name,
value)
cntxt,
inst _ name,
field _ name,
value)
cntxt and inst _ name are used to specify the storage location or address of the object handle. When used
properly these parameters define the hierarchical path to the object data.
field _ name is the name for the object. It does not have to match the objects actual name in the source code.
Objects using set() and get() must use exactly the same name, otherwise the receiving party (get()) will fail to
find the object from uvm _ config _ db.
value is the actual object handle shared through the uvm _ config _ db. Multiple recipients accessing an
object via get(), will access the same object.
<type> is used as a parameter for the uvm _ config _ db class to identify the object from the uvm _
config _ db. <type> which may be either an integral or string, is the class name of the value. The exception
is with enumerated type variables which must use int otherwise the set() wont work as expected.
13 typedef enum {single,incr,wrap4,incr4,wrap8,incr8,wrap16,incr16}hburst _ t
14 hburst _ t
hburst;
15 uvm _ config _ db#(int)::set(this,a,hburst,incr);
Figure 2: config_db for enum type
The set() specifies the address (cntxt & inst _ name) where the object handle is stored to control the
recipient(s) of the object. The get() has the same flexibility, and can freely select from where the information is to
be fetched. In practice get() can be used to fetch an object destined to any component in the hierarchy. Typically
for set() and get(), this is used in the cntxt field to specify the current instance/scope. set() uses
inst _ name to address the object to the appropriate sub-block in the hierarchy. get() often uses empty ()
inst _ name, since it typically is getting the objects destined for itself.
5 uvm _ config _ db#(int)::set(this,my _ subblock _ a,max _ cycles,max _ cycles)
6 uvm _ config _ db#(int)::get(this,, max _ cycles,max _ cycles)
Figure 3: set() and get() typical use
uvm _ config _ db has two additional functions exists() and wait _ modified(). exists() verifies that the
defined variable is found in the uvm _ config _ db. The wait _ modified() function blocks execution until the
defined variable is accessed with the set() call.
2 uvm _ config _ db#(int)::exists(this,my _ subblock _ a,max _ cycles)
3 uvm _ config _ db#(int)::wait _ modified(this,my _ subblock _ a,max _ cycles)
Figure 4: exists() and wait_modified() typical use
Automatic Configuration
UVM also offers build-time configuration of uvm _ component (and extended) classes utilizing uvm _ config _
db. In automatic configuration, it is sufficient to call set() from an upper layer in the hierarchy and the get() will
automatically execute at build time without requiring an explicit call. Automatic configuration utilizes the uvm _
config _ db feature under the hood to pass the configuration values from higher level testbench components in
the hierarchy to its lower level components.
For automatic configuration to work there are two important requirements:
``
The variable or object must have the appropriate FLAG in uvm _ field _ * macros
``
super() must be called in build _ phase()
12 endfunction
13
14 endclass
Once the component properties have the uvm _ field _ * declaration(s) in place with the appropriate FLAG(s),
the macro provides the set _ * _ local functionality and super.build _ phase() calls the apply _
config _ settings() method under the hood. The apply _ config _ settings() method searches for all
appropriate config settings matching this components instance and for each match, the appropriate set _ * _
local method is called using the matching uvm _ config _ db settings field _ name and value.
The super.build _ phase() method may be replaced with the apply _ config _ settings() method
however it is recommended to use the super.build _ phase() method.
15 class agent extends uvm _ agent;
16 int i4;
17 uvm _ component _ utils _ begin(agent)
18
`uvm _ field _ int (i4, UVM _ ALL _ ON)
19 uvm _ component _ utiles _ end
20
virtual function void build _ phase(uvm _ phase phase);
21
apply _ config _ settings(1); //No super.build _ phase(phase)
22
23
endfunction
24
25 endclass
The implicit get() method call will not work in the following instances:
``
Missing uvm _ field _ * macro
``
FLAG is set to UVM _ READONLY
``
Missing super.build _ phase() or apply _ config _ settings() in build _ phase()
Below are log messages generated during the simulation phase because of an explicit apply _ config _
settings() function call:
UVM _ INFO @ 0: env.name _ agent _ 1 [CFGAPL] applying configuration settings
UVM _ INFO @ 0: env.name _ agent _ 1 [CFGAPL] applying configuration to field i4
To set the value for i4 of the above agent, env would have the build _ phase() below:
3 function void build _ phase (uvm _ phase phase);
4
agent _ 1 = agent::type _ id::create(name _ agent _ 1, this);
5
uvm _ component _ db#(int)::set(this, name _ agent _ 1, i4, 1111);
6 endfunction
Hierarchal Testbench Configuration Using uvm_config_db
During the build phase of the simulation the agent objects i4 variable would get value 1111. It is important to note
that automatic configuration happens only at build phase.
Command Line
Compilation and simulation time are the major contributors to verification overhead. The ability to change the
configuration or parameters without being forced to recompile is critical. The UVM class uvm _ cmdline _
processor provides a mechanism to capture the command line argument and pass to verification components
the testcase name, verbosity, configuration and other attributes.
Configuration overriding can only be done from the command line for integer and string using the following:
+uvm _ set _ config _ int=<comp>,<filed>,<value>
+uvm _ set _ config _ string=<comp>,<field>,<value>
There is no way to override the object from the command line, because uvm _ object cannot be passed to
the simulation.
When using the command line argument to set the configuration, make sure that the <type> used in
uvm _ config _ db set() and get() functions is uvm _ bitstream _ t for integer and the <type> for
string is as shown below:
2 class env extends uvm _ env;
3
int a;
4
string color;
5
7
function new(string name, uvm _ component parent);
8
super.new(name, parent);
9
endfunction
10
11 virtual function void build _ phase(uvm _ phase parent);
12
super.build _ phase (phase);
13
if(!uvm _ config _ db #(uvm _ bitstream _ t)::get(this, , a, a))
14
`uvm _ fatal(GET _ NOTSUCC, Get is not successful for a .);
15
if(!uvm _ config _ db #(string)::get(this, , color, color))
16
`uvm _ fatal(GET _ NOTSUCC, Get is not successful for color.);
17
`uvm _ info(GET _ VALUE, $psprintf (The value of a = %d and color =
%s,a,color),UVM _ LOW);
18 endfunction
19
20
21 endclass
22
23 class test extends uvm _ test;
24 int a = 2;
25 string color =blue;
26 env env _ i;
27
28
29 virtual function void build _ phase(uvm _ phase phase);
30 super.build _ phase (phase);
31 env _ i = env:type _ id::create(env _ i, this);
32 uvm _ config _ db#(uvm _ bitstream _ t)::set(this, env _ i, a, a);
33 uvm _ config _ db#(string)::set(this, env _ i, color, color);
34 endfunction
35
36 endclass
The command line argument for the example above is:
<simulation command> +UVM _ TESTNAME=test +uvm _ set _ config _ int=uvm _ test _
top.env _ i, a, 6 +uvm _ set _ config _ string=uvm _ test _ top.env _ i, color, red
Cross-Hierarchical Access
The set() and get() parameters cntxt, inst _ name and field _ name make it possible to use a
number of different paths to the same object. cntxt uses actual object hierarchy whereas inst _ name and
field _ name uses the hierarchy path with names given to the objects in create()/new() method. It is good
practice to create the objects with the same name as the object name.
When referencing down in hierarchy, it should be enough to use this in cntxt and then provide the path and/
or names in inst _ name. Field _ name should be used just for the name of the object. When referencing
upwards in hierarchy, utilize the uvm _ root::get() function to get access to the hierarchy root, and then
reference down from there using inst _ name parameter.
Figure 5 below clarifies and provides examples how objects can be referenced in uvm _ config _ db.
To one object
To multiple objects
To everyone
test a
test a
test a
env
env
env
test a
test a
env
env
agent_1
agent_1
agent_1
agent_1
agent_1
agent_2
agent_2
agent_2
agent_2
agent_2
Figure 5: Options for using cntxt and inst_name parameters in set() and get()
uvm _ config _ db does not actually limit how path field name is shared between cntxt, inst _ name and
field _ name. UVM combines all three of these parameters into one key that is used to access the database.
This feature makes it possible to reference the same object in multiple different ways using the 3 metacharacters
*,+,?. The table below determines the significance of each metacharacter:
Character
Meaning
0 or more characters
1 or more characters
Exactly 1 character
The illustration below shows using these metacharacters for the same object in uvm _ config _ db.
uvm _ config _ db#(<type>)::set( <cntx> , <inst _ name> , <field _ name> , <object>);
uvm _ config _ db#(<type>)::set( <cntx> , <inst _ name> , <field _ name> , <object>);
i _ of _ env
i _ of _ env
i _ of _ env
test_a
cfg
env
cfg
env
agent_1
agent_1
set
set
sequncr
sequncr
set
cfg
cfg
set
set
cfg
cfg
set
set
monitor
driver
monitor
driver
cfg
cfg
cfg
cfg
set
2 module tb _ top();
3 svt _ axi _ if vif();
4
5
6 initial begin
7
uvm _ config _ db #(virtual svt _ axi _ if)::set(null, uvm _ test _ top.env.m _
agent _ 0, vif, vif);
8 end
9 endmodule
10
11 class axi _ agent extends uvm _ agent;
12
virtual svt _ axi _ if vif();
13
14
15
virtual function _ void build _ phase(uvm _ phase phase);
16
super.build _ phase(phase);
17
18
if(!uvm _ config _ db#(virtual svt _ axi _ if)::get(this,,vif, vif))
19
`uvm _ fatal(AXI _ AGENT:NOVIF, The virtual interface get is not successful);
20
uvm _ config _ db#(virtual svt _ axi _ if)::set(this, driver,vif,vif);
21
uvm _ config _ db#(virtual svt _ axi _ if)::set(this,monitor,vif,vif);
22
endfunction
23 endclass
Event Synchronization
uvm _ config _ db is used to make the object available for others, it does not create new copies of the object.
Figure 8 below shows how event-object created by Object A is also made available to Objects X, Y and Z through
the uvm _ config _ db. When Object A chooses to use trigger() for the event object, others can detect
it because they have access to exactly the same object. This demonstrates how the same object event is
referenced from four different objects with three different instance names.
Object X
uvm _ event my _ event;
uvm _ config _ db#(uvm _ event)::get(this, , my _ event, my _ event);
Object A
uvm
_ event my _ event;
my _ event = new(my _ event);
uvm _ config _ db#(uvm _ event)::set(this, agent*, my _ event, my _ event);
&
Object Y
uvm _ event my _ event _ also;
uvm _ config _ db#(uvm _ event)::get(this, , my _ event, my _ event _ also);
Object Z
event
Some attention needs to be paid that set() is called before get()for a specific item, otherwise get() will
fail. Values passed through uvm _ config _ db before run _ phase() need to take into account that
build _ phase()constructs objects from top to bottom. This is often the desired order, since settings and
configurations are usually set from higher levels to lower levels via agents. During the simulation, use of set()
and get() need to be synchronized/timed by the normal testbench operation or by using events to create a
synchronization mechanism.
Limitations
uvm _ config _ db can be used anywhere in the hierarchy. The first parameter of set() and get() functions,
cntxt, however needs to be of type class uvm _ component (or extended from that). cntxt parameter
is often given value utilizing class member this. So if set() or get() functions are used outside uvm _
component extended object, cntxt parameter can be given value using uvm _ root::get(), or just
value null.
uvm_sequence
uvm_sequencer
uvm_driver
uvm_scoreboard
uvm_monitor
uvm_agent
uvm_env
uvm_test
uvm_sequence_base
uvm_component
uvm_sequence_item
uvm_report_object
uvm_transaction
uvm_object
uvm_void
Figure 9: set() and get() functions need cntxt parameter of type uvm_component or null
One common usage of uvm _ config _ db outside uvm _ components, is delivering values from hdl _ top to
the testbench, including access to interfaces instantiated on hdl _ top. Though hdl _ top is not extended from
any UVM class, uvm _ config _ db can still be utilized and communication with the UVM part of the testbench
is possible.
If set() or get() function is used with cntxt parameter not pointing to object of uvm _ component extended
classes, there will be a compile error as shown below.
25 Error-[SV-IUOT] Illegal us of this
26
test.sv, 9
27
this can only be used in a class method.
28
29 Error-[ICTTFC] Incompatible complex type usage
30
test.svh, 281
31
Incompatible complex type usage in task or function call.
32
The following expression is incompatible with the formal parameter of the
33
function. The type of the actual is class
34
my _ pkg::bus _ slave, while the type of the formal is class
35
uvm _ pkg::uvm _ component. Expression: this
36
Source info: uvm _ pkg _ uvm _ config _ db _ 2116934237::get(this, \000,
37
this.get _ full _ name(), my _ agent)
Figure 10: Example error messages when trying to use this for non-uvm_component in set/get
``
Simulation time errors
get() does not find what was set using set() due to misspelling of inst _ name or field value
null object access attributed to get() used before set()
Synopsys VCS Discovery Visualization Environment (DVE) has built-in support for UVM debug. Using the GUI, it is
possible to get list of Set calls without Get and Get calls without Set. These lists help to find and detect errors
in the testbench. Figure 11 below shows the DVE UVM debug dialog window.
The UVM command line option +UVM _ CONFIG _ DB _ TRACE makes all set() and get() calls visible in the
simulation log. However doing this makes the log file too verbose and difficult to interpret. For this reason tracing
is typically turned on only when finding a specific uvm _ config _ db problem. Below is an example of log
messages printed out when set() and get() functions are executed.
39 UVM _ INFO /global/apps4/vcs _ 2012.09-3/etc/uvm-1.1/base/uvm _ resource db.svh(129) @ 0:
40 reporter [CFGDB/SET]
41 Configuration uvm _ test _ top.env.name _ agent _ 1.i _ of _ env (type int)
42 set by uvm _ test _ top.env.name _ agent _ 1 = (int) 1
43
44 UVM _ INFO /global/apps4/vcs _ 2012.09-3/etc/uvm-1.1/base/uvm _ resource _ db.svh(129) @ 0;
45 reporter [CFGDB/GET]
46 Configuration uvm _ test _ top.env.name _ agent _ 1.i _ of _ env (type int)
47 read by uvm _ test _ top.env.name _ agent _ 1 = (int) 1
Sometimes it may help just to print out the name of the object. UVM object has functions get _ name() and
get _ full _ name(). By using these, it can be verified manually that names used in the source code and
named objects at runtime match. Below is an example of how to print the objects name.
49 $display(this.get _ name= %0s, this _ get _ full _ name= %0s, this.get _ name(),
this.get _ full _ name());
Conclusion
When using UVM you cant avoid uvm _ config _ db. So its better to get a solid understanding about what the
set() and get() functions of the uvm _ config _ db do and how you can use them more efficiently in building
your testbench. Below are some dos and donts found to be useful when using UVM.
Dos:
``
For simplicity and to avoid confusion, use the field name as the variable name
``
Investigate an upshot warning/error on an unsuccessful get() method call
``
Set the configuration variable needed across the verification component in the agents test environment
to enable the agent to later set its sub-components
Donts:
``
Avoid using the uvm _ config _ db mechanism excessively as it may cause
performance issues
``
Avoid using the automatic configuration or implicit get() method call
Apart from the above recommendations, it is recommended to use a UVM-aware GUI-based debugging tool such
as Synopsys VCS Discovery Visualization Environment (DVE). As part of Synopsys Professional Services we have
used these concepts across multiple customer engagements to successfully deploy UVM. For more information
on these services, see www.synopsys.com/services.
Appendix
Below is a sample UVM environment showcasing the examples presented earlier. set() and get() functions
utilize only integers (int) though classes and interfaces that would normally be used.
1 // Usage
2 // vcs R sverilog ntb _ opts uvm-1.1 debug _ all +vcs+vcdpluson
-1 sim.log
3 // q example.sv +UVM _ TESTNAME=test _ a
4 import uvm _ pkg::*;
5
6 module dut;
7
int dut _ int = 10;
8
initial uvm _ config _ db#(int)::set(uvm _ root::get(),
9
*,
10
from _ dut,
11
dut _ int
12
);
13 endmodule
14
15 module top;
16 initial run _ test();
17 dut i _ dut();
18 endmodule
19
20 class agent extends uvm _ component;
21 int i1 _ agent, i2 _ agent, i3 _ agent; // to receive values from uvm _ config _ db
22 int i4; // to use automatic configuration
23 uvm _ event new _ values; // to signal new step in test (env->agent)
24 `uvm _ component _ utils _ begin (agent)
25 `uvm _ field _ int(i4, UVM _ ALL _ ON)
26 `uvm _ component _ utils _ end
27 function new (string name, uvm _ component parent);
28
super.new(name, parent);
29 endfunction
30 function void build _ phase (uvm _ phase phase);
31
super.build _ phase(phase);
32
uvm _ config _ db#(uvm _ event)::get(this, , new _ values, new _ values);
33
if (new _ values == null)
34
`uvm _ fatal(get _ name(),
35
new _ values must be set in uvm _ config _ db
36 );
37
$display( Agent \%0s\ got Automatic configuration: i4=%0d,
38
get _ name(),
39
i4
40
);
41 endfunction
42 task run _ phase (uvm _ phase phase);
43
while (1)
44
begin
45
uvm _ config _ db#(int)::get(this,
46 ,
47
i _ of _ env,
48 i1 _ agent
49 );
Hierarchal Testbench Configuration Using uvm_config_db
10
11
116
new _ values.trigger(); #1;
117 $display( --- 4 to every agent, regexp name _ agent _ ? --- );
118
uvm _ config _ db#(int)::set(this,
119 name _ agent _ ?,
120 i _ of _ env,
121 i4 _ env
122 );
123
new _ values.trigger(); #1;
124
$display( --- 5 to every agent, regexp name _ agent* --- );
125
uvm _ config _ db#(int)::set(this,
126 name _ agent _ *,
127 i _ of _ env,
128 i5 _ env
129 );
130
new _ values.trigger(); #1;
131
$display( --- 6 to every agent, regexp *agent* --- );
132
uvm _ config _ db#(int)::set(uvm _ root::get(),
133 *agent*,
134 i _ of _ env,
135 i6 _ env
136 );
137
// uvm _ config _ db: share data with everyone
138
new _ values.trigger(); #1;
139
$display( --- 7 to everyone, regexp *--- );
140
uvm _ config _ db#(int)::set(uvm _ root::get(),
141 *,
142 i _ of _ env,
143 i7 _ env
144 );
145
new _ values.trigger(); #1;
146
$display( --- 8 to everyone, regexp *--- );
147
uvm _ config _ db#(int)::set(null,
148 *,
149 i _ of _ env,
150 i8 _ env
151 );
152
new _ values.trigger();
153
phase.drop _ objection(this);
154 endtask
155 endclass
156
157 class test _ a extends uvm _ test;
158 `uvm _ component _ utils (test _ a)
159 env env;
160 function new (string name=test _ a, uvm _ component parent=null);
161
super.new (name, parent);
162
env = new(env, this);
163 endfunction
164 function void end _ of _ elaboration();
165
print();
166 endfunction
167 task run _ phase(uvm _ phase phase);
168
#1000; global _ stop _ request();
169 endtask
170 endclass
References
1
2
Accellera Systems Initiative Universal Verification Methodology (UVM) 1.1 Users Guide, May 18, 2011
Accellera Systems Initiative Universal Verification Methodology (UVM) 1.1 Class Reference Manual, June 2011
Synopsys, Inc. 700 East Middlefield Road Mountain View, CA 94043 www.synopsys.com
2014 Synopsys, Inc. All rights reserved. Synopsys is a trademark of Synopsys, Inc. in the United States and other countries. A list of Synopsys trademarks is
available at http://www.synopsys.com/copyright.html. All other names mentioned herein are trademarks or registered trademarks of their respective owners.
06/14.AP.CS3989.