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
Simulation Log 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
. So, whatever digital design that you intend to create, it'll go inside a module
block. It may or may not have ports defined - 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
In the early days of integrated circuits, engineers had to sit down and physically draw transistors and their connections on paper to design them such that it can be fabricated on silicon. Bigger and complex circuits demanded more engineers, time and other resources and soon enough there was a need to have a better way of designing integrated circuits.
VHDL was soon developed to enhance the design process by allowing engineers to describe functionality of the desired hardware and let automation tools convert that behavior into actual hardware elements like combinational gates and sequential logic. Verilog was developed to simplify the process and make the Hardware Description Language (HDL) more robust and flexible. Today, Verilog is the most popular HDL used and practiced throughout the semiconductor industry.
How is Verilog useful ?
Verilog creates a level of abstraction that helps hide away the details of its implementation and technology.
For example, the design of a D flip-flop would require the knowledge of how the transistors need to be arranged to achieve a positive-edge triggered FF and what the rise, fall and clk-Q times required to latch the value onto a flop among many other technology oriented details. Power dissipation, timing and the ability to drive nets and other flops would also require a more thorough understanding of the physical characteristics of a transistor.
Verilog helps us to focus on the behavior and leave the rest to be sorted out later.
Example
The following code illustrates how a Verilog code looks like. We will delve into more details of the code in the next article. For the time being, let us simply understand that the behavior of a counter is described. The code essentially makes the counter count up if the up_down signal is 1, and down if its value is 0. It also resets the counter if the signal rstn becomes 0 making this an active-low reset.
module ctr (input up_down,
clk,
rstn,
output reg [2:0] out);
always @ (posedge clk)
if (!rstn)
out <= 0;
else begin
if (up_down)
out <= out + 1;
else
out <= out - 1;
end
endmodule
The simple example shown above illustrates how all the physical implementation details have been hidden while still providing a clear idea of how the counter functions.
ctr is a module
that represents an up/down counter, and it is possible to choose the actual physical implementation of the design from a wide variety of different styles of flops optimized for area, power and performance. They are usually compiled into libraries and will be available for us to select within EDA tools at a later stage in the design process.
Now that you know what Verilog is, let's start learning Verilog !