This conditional statement is used to make a decision on whether the statements within the if
block should be executed or not.
- If the expression evaluates to true (i.e. any non-zero value), all statements within that particular
if
block will be executed - If it evaluates to false (zero or 'x' or 'z'), the statements inside
if
block will not be executed - If there is an else statement and expression is false then statements within the
else
block will be executed.
Syntax
If multiple statements need to be placed inside the if or else part, it needs to be enclosed within begin
and end
.
if ([expression])
Single statement
// Use "begin" and "end" blocks for more than 1 statements
if ([expression]) begin
Multiple statements
end
// Use else to execute statements for which expression is false
if ([expression]) begin
Multiple statements
end else begin
Multiple statements
end
// if-else-if style to check for more expressions if the previous one doesn't match
if ([expression 1])
Single statement
else if ([expression 2]) begin
Multiple Statements
end else
Single statement
Hardware Implementation
if without else
if
without an else
part implies that the value remain unchanged for any condition that does not satisfy the expression inside if
.
module des ( input en,
input d,
output reg q);
always @ (en or d)
if (en)
q = d;
endmodule
Value of output q is updated whenever d or en changes in value.

if with else
Output q will get the value of input d at the positive edge of clock if rstn is high and describes the behavior of a D flop.
module dff ( input clk,
input rstn,
input d,
output reg q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else
q <= d;
end
endmodule
Note that the synthesized output indicates a flop with an output q .

if else if
In the following example, the design module has a 4-bit output q that is incremented when mode is 1 and decrements when mode is 2 with if else
construct. Note that the description does not specify what has to be done if mode is 0 or 3 which are valid values for a 2-bit variable. It is assumed that the circuit does nothing when mode is 1 and 3, but maintain exiting value of q . It is not recommended to leave such ambiguity in real design code, but is shown here to highlight the possibility.
module des ( input [1:0] mode,
input clk,
input rstn,
output reg [3:0] q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else begin
if (mode == 1)
q <= q + 1;
else if (mode == 2)
q <= q - 1;
end
end
endmodule
The synthesized output may differ with availability of cells for a given technology library
Shown below is the synthesized output and it is worth to note that q got implemented as a 4-bit flop which has a pin CE to enable the flop. Note that this flop is enabled only when mode is 1 or 2 and not for other values. Output q is fed back through an adder and subtractor block into the input of the same flop through a mux which is again controlled by mode .

Consider the same design from above with a 1-bit mode .
module des ( input mode,
input clk,
input rstn,
output reg [3:0] q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else begin
if (mode)
q <= q + 1;
else
q <= q - 1;
end
end
endmodule
In this case, a regular flop without a CE pin is used along with a few multiplexers to choose the correct signal based on value of mode .

Examples
if without else for single statement
module tb;
int a = 10;
initial begin
if (a == 10) // if block can have only one statement in it
$display ("a is found to be 10");
$display ("Always executed regardless of value of a"); // This statement is outside if block because
end
endmodule
ncsim> run a is found to be 10 Always executed regardless of value of a ncsim: *W,RNQUIE: Simulation is complete.
if without else for multiple statements
module tb;
int a = 10;
initial begin
if (a == 10) begin // if block has begin end keywords, and can support multiple statements
$display ("a is found to be 10");
$display ("Its good to get 10");
// Anything else can be done here until the "end" keyword
end
$display ("Always executed regardless of value of a"); // Statement is outside the if block, because of the closing "end" for the "begin" in if
end
endmodule
ncsim> run a is found to be 10 Its good to get 10 Always executed regardless of value of a ncsim: *W,RNQUIE: Simulation is complete.
if-else for single statement
module tb;
int a = 9;
initial begin
if (a == 10)
$display ("a is found to be 10"); // Is executed when "if" expression is True
else
$display ("a is NOT 10 :("); // Is executed when "if" expression is false
end
endmodule
ncsim> run a is NOT 10 :( ncsim: *W,RNQUIE: Simulation is complete.
if-else for multiple statements
module tb;
int a = 9;
initial begin
if (a == 10) begin
$display ("a is found to be 10"); // Is executed when "if" expression is True
// Can have more additional statements here
end else begin
$display ("a is NOT 10 :("); // Is executed when "if" expression is false
$display ("Why is a not 10 ?");
// Can have more additional statements here
end
end
endmodule
ncsim> run a is NOT 10 :( Why is a not 10 ? ncsim: *W,RNQUIE: Simulation is complete.
uvm_object
is the one of the base classes from where almost all UVM classes are derived. Typically configuration classes and data objects are derived from this class and are passed to different testbench components during the course of a simulation. There is often a need to copy, compare and print values in these classes.
UVM has included user-definable functions like do_copy() to copy, do_compare() to compare and do_print to print values in the class. But it can at times be an overhead to define all these methods for every class object created in the testbench. UVM comes with automation of core methods like copy, compare, pack, and print using `uvm_field_*
macros. This avoids the need for a user implementation of the do_* methods for each function.
These macros expand into complicated code that may not be run-time efficient and are not generally recommended !
The `uvm_field_*
macros are called inside `uvm_*_utils_begin
and `uvm_*_utils_end
macro blocks during factory registration.
ARG | Variable name compatible with the macro type |
FLAG | Default value is UVM_ALL_ON, ARG variable will be included in all data methods |
FLAG types
All possible values for FLAG are shown in the table below. Multiple flags can be OR'ed together with the |
operator to apply the required operations.
Value | Operation |
---|---|
UVM_ALL_ON | Set all operations on |
UVM_DEFAULT | [Recommended] Enables all operations. Additional flags may be turned off by default in the future versions |
UVM_NOCOPY | Do not copy this field |
UVM_NOCOMPARE | Do not compare this field |
UVM_NOPRINT | Do not print this field |
UVM_NOPACK | Do not pack/unpack this field |
UVM_REFERENCE | For object types, operate on handles only (like no deep copy) |
Print Options
The following flags can be OR'ed together to print in the required format.
Value | Operation |
---|---|
UVM_BIN | Print/record the field in binary |
UVM_DEC | Print/record the field in decimal |
UVM_UNSIGNED | Print/record the field in unsigned decimal |
UVM_OCT | Print/record the field in octal |
UVM_HEX | Print/record the field in hexadecimal |
UVM_STRING | Print/record the field in string format |
UVM_TIME | Print/record the field in time format |
typedef enum {FALSE, TRUE} e_bool;
class Child extends uvm_object;
string m_name;
logic[3:0] m_age;
`uvm_object_utils_begin(Child)
`uvm_field_string (m_name, UVM_ALL_ON)
`uvm_field_int (m_age, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="Child");
super.new(name);
endfunction
endclass
class Parent extends uvm_object;
string m_name;
bit[15:0] m_age;
int m_numbers[$];
e_bool m_employed;
Child m_child;
`uvm_object_utils_begin(Parent)
`uvm_field_enum (e_bool, m_employed, UVM_ALL_ON)
`uvm_field_int (m_age, UVM_ALL_ON)
`uvm_field_queue_int (m_numbers, UVM_ALL_ON)
`uvm_field_string (m_name, UVM_ALL_ON)
`uvm_field_object (m_child, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name="Parent");
super.new(name);
endfunction
endclass
module tb;
initial begin
Parent p = Parent::type_id::create("Parent");
p.m_name = "Joey";
p.m_employed = TRUE;
p.m_age = 29;
p.m_numbers = '{1234, 5678, 9011};
p.m_child = new();
p.m_child.m_name = "Joey Jr";
p.m_child.m_age = 1;
p.print();
end
endmodule
ncsim> run UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_root.svh(392) @ 0: reporter [UVM/RELNOTES] ----------------------------------------- Name Type Size Value ----------------------------------------- Parent Parent - @1829 m_employed e_bool 32 TRUE m_age integral 16 'h1d m_numbers da(integral) 3 - [0] integral 32 'h4d2 [1] integral 32 'h162e [2] integral 32 'h2333 m_name string 4 Joey m_child Child - @1830 m_name string 7 Joey Jr m_age integral 4 'h1 ----------------------------------------- ncsim: *W,RNQUIE: Simulation is complete.
A resource is a parameterized container that holds arbitrary data. Resources can be used to configure components, supply data to sequences, or enable sharing of information across disparate parts of the testbench. They are stored using scoping information so their visibility can be constrained to certain parts of the testbench. You can put any data type into the resource database, and have another component retrieve it later at some point in simulation, which is a very convenient feature to have.

We have already seen how to use `uvm_do
set of macros. They automatically create a new object via calls to `uvm_create
, randomize the item and send it to a sequencer. If we already have a data object that we simply want to send to a sequencer, we can use `uvm_send
. There are different variations to this macro, just like `uvm_do_*
.

All behavioral code is written inside module
and endmodule
. It may or may not have ports defined to allow signals to enter the block as input
or escape the block as output
.
Module
The empty module in the example below is called testbench. You can name it whatever you like, except that it should be alphanumeric, and can contain '_'.

module testbench;
endmodule