UVM Tips and Tricks
UVM Tips and Tricks
UVM Tips and Tricks
by Sandeep Nasa and Shankar Arora, Logic Fruit Technologies Pvt. Ltd.
UVM is the most widely used Verification UVM base class library and UVM verification
methodology for functional verification of environment architecture.
digital hardware (described using Verilog,
SystemVerilog or VHDL at appropriate This article will furnish several examples
abstraction level). It is based on OVM and is to improve the performance of the UVM
developed by Accellera. It consists of base testbench by applying different optimizing
libraries written in SystemVerilog which techniques to random access generation,
enables the end user to create testbench configuration database, objection mechanism,
components faster using these base libraries. sequence generation, loop usage.
Due to its benefits such as reusability,
efficiency and automation macros it is a widely
accepted verification methodology. INTRODUCTION
The rise in level of complexity of the chips
UVM has a lot of features so it’s difficult due to the addition of more features has
for a new user to use it efficiently. A better direct impact on the level of abstraction at
efficiency can be obtained by customizing the which the chips are designed and moreover
UVM base library and applying certain tips and on the verification of these chips which
tricks while building UVM testbenches, which consumes almost 70 percent of the time to
is mainly the purpose of this article. verify these chips.
VerificationHorizonsBlog.com 31
1. COMMON UVM MISTAKES Therefore, to prevent this error we need
AND THEIR RESOLUTION WITH to write file adder_design.sv as shown below:
TIPS AND TRICKS:
`ifndef ADDER_DESIGN
1.1 Use of Macros to Overcome Errors `define ADDER_DESIGN
Faced in Package: ......... adder_design logic .........
In case of UVM project, source files are added `endif
32 mentor.com
threads in parallel and takes loop parameter iteration, a new value is allocated to variable l
as an input, the generated result holds only and passed to the respective task.
the last value of the variable. For example if
the code along with its simulated output is The modified code along with output is
as written below: as shown below:
VerificationHorizonsBlog.com 33
// Transaction class In order to overcome the above problem
class transaction extends uvm_sequence_item; we can prefix `local::` before the address of
rand [31:0] addr;
sequence class seq. Thus, we could modify
rand [31:0] data;
endclass the code as:
// Sequence class
class seq extends uvm_sequence#(seq_item);
// Transaction class
bit [31:0] addr;
class transaction extends uvm_sequence_item;
task body();
rand [31:0] addr;
transaction trans;
rand [31:0] data;
bit [31:0] addr = 32’h11001100;
endclass
assert(trans.randomize() with { trans.addr == addr; });
// Sequence class
endtask
class seq extends uvm_sequence#(seq_item);
endclass
bit [31:0] addr;
task body();
transaction trans;
bit [31:0] addr = 32’h11001100;
Here the “with” feature is used to override assert(trans.randomize()
the address but instead the code generates a with { trans.addr == local::addr; });
endtask
transaction with addr as hbfdf5196. So why is endclass
the intended value of trans.addr not applied
with the inline trans.addr == addr constraint.
The problem arises when you try to make the Now with these modifications above code
transaction item address equal to the address generates the following address:
in the calling sequence class using the above
in-line constraint. The result is undesirable since # Name Type Size Value
# trans transaction - @636
the constraint will actually cause the seq_item # addr integral 32 ‘h11001100
address (trans.addr) to be equal to itself. This
gotcha in SystemVerilog arises because we
have addr as a variable defined in both the This use of “local::” makes sure that the
transaction class as well as the sequence class. constraint solver looks for the address following
SystemVerilog scoping rules pick the variable the local:: only in the local scope (i.e. the
which is part of the object being randomized. address in the sequence class seq). So, now
the constraint will be the desired one which
The SystemVerilog P1800-2012 LRM states that: states that while randomizing the address
of the transaction class, the constraint solver
“Unqualified names in an unrestricted in-lined
should make sure that the address of the
constraint block are then resolved by searching
transaction should be equal to the address
first in the scope of the randomize() with
in the sequence seq.
object class followed by a search of the scope
containing the method call —the local scope.” class Ethernet;
rand bit [3:0]payload[];
task display();
$display(“Randomize Values”);
$display(“-------------------------------”);
$display(“payload : %p”,payload);
$display(“-------------------------------”);
endtask
34 mentor.com
Results:
endclass
module test();
Randomized Values
Ethernet Eth;
--------------------------------------------------------------
initial begin
payload : ‘ {}
repeat(2)
--------------------------------------------------------------
begin
Randomized Values
Eth=new();
--------------------------------------------------------------
assert (Eth.randomize());
payload : ‘ {}
Eth.display();
--------------------------------------------------------------
end
end
endmodule
The solution to this issue is that the size
of the dynamic array or queue declared
b. Dynamic arrays declared as rand can as rand should also be constrained.
be a source of error for some new users.
With the addition of constraint the result
It won’t randomize the size of dynamic array
of the compilation is as below:
when we try to randomize it based on how
the code is written. Consider the example Randomized Values
of an Ethernet packet. --------------------------------------------------------------
payload : ‘ {‘he, ‘h4, ‘h4, ‘h8}
--------------------------------------------------------------
class Ethernet; Randomized Values
rand bit [3:0]payload[]; --------------------------------------------------------------
constraint c { payload.size() ==4;} payload : ‘ {‘h6, ‘he, ‘h5, ‘h3}
task display(); --------------------------------------------------------------
$display(“Randomize Values”);
$display(“-------------------------------”);
$display(“payload : %p”,payload);
$display(“-------------------------------”);
endtask c. It’s very important to check the return value
endclass of the randomize() function while applying
module test();
Ethernet Eth; this function on the object of the transaction
initial begin item type. The randomize() function returns
repeat(2)
1 if all the rand variables attain a valid value
begin
Eth=new(); otherwise it returns zero. It is very important
assert (Eth.randomize()); to check whether the randomization is
Eth.display();
end
successful or failed. In case randomization
end is not successful (due to invalid constraints
endmodule or any other reason) then its rand variables
will hold the previous values. But it’s always
recommended that we should check the
Per the expectation, the call to randomize() randomization using assert statement instead
must generate random values for the payload of using if statement because the use of assert
array. But unfortunately, this doesn’t happen. statement makes sure that the simulation gets
Instead, the randomize call will exit with no terminated when randomization fails.
error, warning. The payload array has no value.
VerificationHorizonsBlog.com 35
For example: The solution with if statement is as below:
In this case, we can see that the source address The solution with using assert for checking
holds the value of 5 and does not follow the the successful randomization is as shown here:
constraint. Therefore, it is necessary to detect
whether the randomized value matches the class Ethernet;
rand bit [47:0] src_addr = 4’h5;
constraint or not but here no message is
rand bit [47:0] dest_addr;
printed. So it’s important to check whether the
constraint c{src_addr > 48’h4;}
randomized Ethernet packet satisfies the source constraint c1{src_addr == 48’h4;}
task display();
address constraint for the Ethernet packet $display(“Randomize Values”);
or not. This can be done either by using an $display(“-------------------------------”);
if statement or by using an assertion.
36 mentor.com
For example: Consider the following code:
$display(“src_addr : %p”,src_addr);
$display(“-------------------------------”);
endtask class Ethernet;
endclass randc bit [1:0] a1;
randc bit [6:0] b1;
module test(); constraint c{(a1 != 2’b01) -> (b1<7’h10);
Ethernet Eth; (a1 == 2’b01) -> (b1>=7’h10);}
initial begin task display();
repeat(2) $display(“Randomize Values”);
begin $display(“-------------------------------”);
Eth=new(); $display(“a1 : %p”,a1);
assert(Eth.randomize()); $display(“b1 : %p”,b1);
Eth.display(); $display(“-------------------------------”);
end endtask
end endclass
endmodule module test();
Ethernet Eth;
initial begin
repeat(2)
begin
So, in this case we get an error message that Eth=new();
randomization failed and simulation stopped. assert(Eth.randomize());
Eth.display();
End
//Results generated by the previous code end
Error-[CNST-CIF] Constraints inconsistency endmodule
failure testbench.sv, 22
Constraints are inconsistent and
cannot be solved. Please check
the inconsistent constraints being
printed above and rewrite them.
The result of compilation of this code
“testbench.sv”, 22: test.unnamedSS_3.unnamedSS_2 will give an error as mentioned below:
started at 0ns failed at 0ns
Offending ‘Eth.randomize()’
a1 = 0, b1 = 0
Randomize Values
………………………………………………………………
src_addr: 5 In this particular scenario, the problem
……………………………………………………………… occurs due to the conflict between constraint
Time: 0ns solving and cyclic randomization. This is also
tool dependent. So, if the tool wants to solve
one of the variables first, it has to compromise
with the cyclic nature of the randc type of
d. Some common issues related to random the variable.
variables are with variables defined as randc, So, in this case there are two options:
but the generated random results are not
perfectly cyclic because of the constraint • Either to compromise with the intended
applied on them. cyclic behavior (as previous results)
• The solution to the above problem is to
make sure that there is no conflict between
the generated randomized values (which
can be attained by removing constraint)
VerificationHorizonsBlog.com 37
The code with removed constraint is as below:
Consumer component
int id_value = 0;
class Ethernet; forever begin
randc bit [1:0] a1; `uvm_config_db#(int)::wait_modified(this,”*”,”id_value”);
randc bit [6:0] b1; if(!uvm_config_db#(int)::get(this,””,”id_value”, id_value) begin
task display(); `uvm_error(.......)
$display(“Randomize Values”); end
$display(“-------------------------------”); end
$display(“a1 : %p”,a1);
$display(“b1 : %p”,b1);
$display(“-------------------------------”);
endtask
endclass The more efficient way is as mentioned below:
38 mentor.com
2.2 Minimize Factory Overrides 2.3 Avoid the Use of uvm_printer Class
for Stimulus Objects Initially, the uvm_printer class was designed
Using UVM factory provides an override to be used with uvm_field_macro in order to
feature where an object of one type can print the component hierarchy or transaction
be substituted with an object of derived fields in several formats. This class comes
type without changing the structure of the with performance overhead.
testbench. This feature could be applied
to change the behavior of the generated //Low performance code
transaction without modifying the testbench seq_item req = seq_item::type_id::create(“req”);
task body;
This performance overhead can be
seq_item item;
repeat(200) begin avoided by using convert2string() method
item = seq_item::type_id::create(“item”); for objects. The method returns a string
start_item(item);
assert(item.randomize()); that can be displayed or printed using
finish_item(item); the UVM messaging macros.
endtask
repeat(20) begin
start_item(req);
assert(req.randomize());
Therefore, to minimize the costly impact of
finish_item(req);
this factory overriding, first create an object `uvm_info(“BUS_SEQ”, req.convert2string(), UVM_DEBUG)
and then clone it each time it is used to avoid end
the use of factory.
VerificationHorizonsBlog.com 39
//Low performance code 2.5 Use of UVM Objections
uvm_reg reg_i[$]; UVM provides an objection mechanism to
randc i;
allow synchronization communication among
int regs_no;
different components which helps in deciding
repeat(200) begin when to close the test. UVM has built-in
reg_i = decode.get_registers();
regs_no = regs.size();
objection for each phase, which provides
repeat(regs_no) begin a way for the components and objects to
assert(this.randomize()); synchronize their activity.
assert(reg_i.randomize());
reg_i[i].update();
end Objections should only be used by the
end controlling threads, and it is also very necessary
to place the objections in the run-time method
of the top level test class, or in the body method
In the code above get_registers is called inside of a virtual sequence. Using them in any other
the loop which is less efficient. place is likely to be unnecessary and also cause
a degradation in performance.
task body;
sequence seq = sequence::type_id::create(“seq”);
seq.start(seqr);
In efficient code, call to get_registers is kept endtask
outside the repeat loop, so that only one call
is made to get_registers() and avoids the
overhead associated with the repeated call.
The code above is less efficient since the
objection is raised per sequence_item.
40 mentor.com
The high performance code is given below. For example: consider the dynamic array
VerificationHorizonsBlog.com 41
string is set to “*”, this means that the entire module top;
component hierarchy will be searched for import uvm_pkg::*;
import test_pkg::*;
uvm_config_db settings before returning
ahb_if AHB();
the result: apb_if APB();
initial begin
//Low Performance Code `uvm_config_db#(virtual ahb_if)::set(“uvm_test_top”, “ “, “AHB”,
sb_cfg = sb_config::type_id::create(“sb_cfg”); AHB);
uvm_config_db#(sb_config)::set(this, “*”, “*_config”, sb_cfg); `uvm_config_db#(virtual apb_if)::set(“uvm_test_top”, “ “, “APB”,
APB);
//In the env.sb component run_test();
sb_config cfg; end
if(!uvm_config_db#(sb_config)::get(this,” “, “_config”, cfg)) begin class test extends uvm_component;
`uvm_error(....)
end ahb_agent_config ahb_cfg;
apb_agent_config apb_cfg;
42 mentor.com
The second example shows how a shared to refer to the size of these fields we can just
package passes the virtual interface handles refer them by their names.
from the top level testbench module to the
For example: `define Pream_size 64
UVM test class. The uvm_config_db::set()
`define Dest_addr_size 48
and get() calls get eliminated and also the `define Sour_addr_size 48
entry from uvm_config_db for each virtual `define type_field_size 16
`define data_size 800
interface handle got eliminated. When the `define crc_size 32
virtual interface handles are used more, a
significant improvement in the performance
is observed.
Now suppose we need to generate packets
of data with size 200 bytes. So, instead of
3. CONVENTIONS FOR USING making changes in all the files referring to
UVM METHODOLOGY FEATURES the size of the data, we can just change it in
a. It is a good practice to set the variables the file where we defined the data size.
used in different files to be declared in a
`define data_size 1600
single file by using `define macro so that they
can be referred by that name and moreover
any update in the value will be changed only
Now the Ethernet packets will have data
in that file in which the variable is defined and
of size 200 bytes (1600 bits) and all the files
the change will be reflected in all the files.
referring to the data size will be automatically
For example: consider an Ethernet packet updated with the data size 200 bytes.
which has several fields of different size but
b. All the enum fields should be placed
the size of some of the fields are fixed (except
in a separate file.
payload field). Suppose initially we set the size
of the data field to some fixed size: For example: consider the examples of an
open source libtins. Since we can put different
Like: Preamble is of 8 bytes. types of packets in the Ethernet packet,
Destination Address is of 6 bytes
we can define them in a single file and
Source Address is of 6 bytes
Type field is of 2 bytes then refer them as required in other files.
Data field is of 100 bytes
CRC is of 4 bytes Tins::PDU* pdu_from_flag(PDU::PDUType type, const uint8_t*
buffer, uint32_t size) {
switch(type) {
case Tins::PDU::ETHERNET_II:
And when we go deep into the hierarchy of return new Tins::EthernetII(buffer, size);
the Ethernet packet, we can explore further case Tins::PDU::IP:
fields deep in the hierarchy of the Ethernet return new Tins::IP(buffer, size);
case Tins::PDU::IPv6:
packet some of them with the same size. return new Tins::IPv6(buffer, size);
case Tins::PDU::ARP:
So we can define the size of all the fields in return new Tins::ARP(buffer, size);
case Tins::PDU::IEEE802_3:
one file. So, in the other files which needs
return new Tins::IEEE802_3(buffer, size);
case Tins::PDU::PPPOE:
VerificationHorizonsBlog.com 43
return new Tins::PPPOE(buffer, size); if (Internals::pdu_type_registered<EthernetII>(flag)) {
#ifdef TINS_HAVE_DOT11 return static_cast<Constants::Ethernet::e>(
case Tins::PDU::RADIOTAP: Internals::pdu_type_to_id<EthernetII>(flag)
return new Tins::RADIOTAP(buffer, size); );
case Tins::PDU::DOT11: }
case Tins::PDU::DOT11_ACK: return Constants::Ethernet::UNKNOWN;
case Tins::PDU::DOT11_ASSOC_REQ: }
case Tins::PDU::DOT11_ASSOC_RESP: }
case Tins::PDU::DOT11_AUTH:
case Tins::PDU::DOT11_BEACON:
case Tins::PDU::DOT11_BLOCK_ACK:
case Tins::PDU::DOT11_BLOCK_ACK_REQ:
case Tins::PDU::DOT11_CF_END: These are defined in the internal.cpp files in
case Tins::PDU::DOT11_DATA: the libtins project and are referred by the files
case Tins::PDU::DOT11_CONTROL: which have a header for different packets.
case Tins::PDU::DOT11_DEAUTH:
case Tins::PDU::DOT11_DIASSOC:
case Tins::PDU::DOT11_END_CF_ACK:
case Tins::PDU::DOT11_MANAGEMENT: 4. COMMON HIERARCHY GIVING
case Tins::PDU::DOT11_PROBE_REQ:
case Tins::PDU::DOT11_PROBE_RESP: WELL DEFINED ARCHITECTURE
case Tins::PDU::DOT11_PS_POLL: WHICH IS EASY TO UNDERSTAND
case Tins::PDU::DOT11_REASSOC_REQ:
case Tins::PDU::DOT11_REASSOC_RESP: AND MANAGE
case Tins::PDU::DOT11_RTS: It is better to create a proper project hierarchy
case Tins::PDU::DOT11_QOS_DATA:
return Tins::Dot11::from_bytes(buffer, size);
to keep and manage and handle the project
#endif // TINS_HAVE_DOT11 easily. For example the screenshot shows that
default: how the different files are arranged in different
return 0;
}; project directories (test cases, sequences,
} architecture and design).
Constants::Ethernet::e pdu_flag_to_ether_type
(PDU::PDUType flag) {
switch (flag) {
case PDU::IP:
return Constants::Ethernet::IP;
case PDU::IPv6:
return Constants::Ethernet::IPV6;
case PDU::ARP:
return Constants::Ethernet::ARP;
case PDU::DOT1Q:
return Constants::Ethernet::VLAN;
case PDU::PPPOE:
return Constants::Ethernet::PPPOED;
case PDU::MPLS:
return Constants::Ethernet::MPLS;
case PDU::RSNEAPOL:
case PDU::RC4EAPOL:
return Constants::Ethernet::EAPOL;
default:
44 mentor.com
CONCLUSION
In summary, the article focuses on the
common mistakes made by the novice in
verification and provides the solution to
these problems through various tips and
programming examples. Moreover, the
article also suggests various tricks which
can be applied to enhance the performance
of UVM Testbenches. It also covers various
conventions to be followed for making the
code simpler and how to maintain the
project hierarchy.
REFERENCES
1. “UVM and Emulation: How to Get Your
Ultimate Testbench Acceleration Speed-
up” Hans van der Schoot & Ahmed Yehia,
DVCon 2015.
2. “Easier UVM for Functional Verification by
Mainstream Users”, John Aynsley, Duolos.
3. “The Top Most Common SystemVerilog
Constrained Random Gotchas”, Ahmed
Yehia, DVCon 2014.
4. “Making the most of SystemVerilog and
UVM: Hints and Tips for new users”, Dr.
David Long, Doulus.
5. https://verificationacademy.com/
cookbook/uvm/performance_guidelines
6. www.libtins.github.io/download/
VerificationHorizonsBlog.com 45
VERIFICATION
ACADEMY
The Most Comprehensive Resource for Verification Training
www.verificationacademy.com
Editor:
Tom Fitzpatrick
Program Manager:
Rebecca Granquist
Phone: 503-685-7000
To subscribe visit:
www.mentor.com/horizons