Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

UVM Prepared Document

Download as pdf or txt
Download as pdf or txt
You are on page 1of 85

Introduction to UVM

The Universal Verification Methodology (UVM) consists of class libraries needed for the development of
well constructed, reusable SystemVerilog based Verification environment.

In simple words, UVM consists of set of base classes with methods defined in it, SystemVerilog verification
environment can be developed by extending these base classes.

Now on will refer the UVM base classes as UVM Classes.

UVM Classes
UVM consists of three main types of UVM classes,
 uvm_object
 uvm_transaction
 uvm_component

uvm_object
 Core class based operational methods (create, copy, clone, compare, print, record, etc..),
instance identification fields (name, type name, unique id, etc.) and random seeding were defined in it.
 All uvm_transaction and uvm_component were derived from uvm_object.

uvm_transaction
 Used in stimulus generation and analysis.

uvm_component
 Components are quasi-static objects that exist throughout simulation.
 Every uvm_component is uniquely addressable via a hierarchical path name, e.g.
“env.agent.driver”.
 The uvm_component also defines a phased test flow, that components follow during the course
of simulation. Each phase(build, connect, run, etc.) is defined by a callback that is executed in precise
order.
 The uvm_component also defines configuration, reporting, transaction recording, and factory
interfaces.
uvm class hierarchy
UVM Testbench

UVM testbenchs are constructed by extending uvm classes.

below is the typical UVM testbench hierarchy diagram.

Typical UVM TestBench Hierarchy

Role of each testbench element is,

Test
This is the top most class. test is responsible for,

 configuring the testbench.


 initiate the testbench components construction process by building the next level down in the
hierarchy ex: env.
 initiate the stimulus by starting the sequence.

Environment or Env
The environment is a container component for grouping higher level components like agent's and
scoreboard.

Agent

UVM agent groups the uvm_components specific to an interface or protocol.

example: groups the components associated with BFM(Bus Functional Model).

The components of agent are,


 Sequence item:
Sequence item defines the pin level activity generated by agent (to drive to DUT through the driver) or
the activity has to be observed by agent (Placeholder for the activity monitored by monitor on DUT
signals).
 Driver:
Responsible for driving the packet level data inside sequence_item into pin level (to DUT).
 Sequence:
Defines the sequence in which the data items need to be generated and sent/received to/from driver.
 Sequencer:
Responsible for routing the data packet's(sequence_item) generated in sequence to the driver or vice
verse.
 Monitor:
Observes pin level activity on interface signals and converts into packet level which are sent to
components such as scoreboards.
Scoreboard
Receives data item's from monitor's and compares with expected values.expected values can be either
golden reference values or generated from reference model.
UVM TestBench Block Diagram

 UVM TestBench Block Diagram with single agent.


 UVM TestBench Block Diagram with multiple agent's and multiple instance of each.

UVM_Sequence_item
The sequence item is written by extending the uvm_sequence_item, uvm_sequence_item inherits from
the uvm_object via the uvm_transaction class. therefore uvm_sequence_item is of object type.
Before moving to uvm_sequence_item will look into uvm_object concepts required
to write uvm_sequence_item,
The uvm_object has a number of virtual methods which are used to implement
common data object functions (copy, clone, compare, print, transaction and
recording) and these should be implemented to make the sequence_item more
general purpose.
also uvm_Object has macros defined in it, mainly Utility Macros and Field Macros.
UVM Utility Macros
The utility macros provide implementations of the create method (needed for
cloning) and the get_type_name method (needed for debugging), etc.
objects with no field macros,
`uvm_object_utils(TYPE)

objects with field macros,


`uvm_object_utils_begin(TYPE)

`uvm_field_*(FIELD,FLAG)

`uvm_object_utils_end

UVM Field Macros


The `uvm_field_* macros are invoked inside of the `uvm_*_utils_begin and
`uvm_*_utils_end, for the implementations of the methods: copy, compare, pack,
unpack, record, print, and etc.
Each `uvm_field_* macro is named to correspond to a particular data type: integrals,
strings, objects, queues, etc., and each has at least two arguments: FIELD and FLAG.
`uvm_field_*(FIELD,FLAG);

FLAG Description

UVM_ALL_ON Set all operations on (default).

UVM_DEFAULT Use the default flag settings.


UVM_NOCOPY Do not copy this field.

UVM_NOCOMPARE Do not compare this field.

UVM_NOPRINT Do not print this field.

UVM_NODEFPRINT Do not print the field if it is the same as its

UVM_NOPACK Do not pack or unpack this field.

UVM_PHYSICAL Treat as a physical field. Use physical setting in policy class for this field.

UVM_ABSTRACT Treat as an abstract field. Use the abstract setting in the policy class for this field.

UVM_READONLY Do not allow setting of this field from the set_*_local methods.

A radix for printing and recording can be specified by OR’ing one of the following

constants in the FLAG argument

UVM_BIN Print / record the field in binary (base-2).

UVM_DEC Print / record the field in decimal (base-10).

UVM_UNSIGNED Print / record the field in unsigned decimal (base-10).

UVM_OCT Print / record the field in octal (base-8).

UVM_HEX Print / record the field in hexadecimal (base-16).

UVM_STRING Print / record the field in string format.

UVM_TIME Print / record the field in time format.

Sequence item:
Sequence item consist of data fields required for generating the stimulus.
In order to generate the stimulus, the sequence items are randomized in sequences.
Therefore data properties in sequence item should generally be declared as rand and
can have constraints defined.
Data fields represents the following types of information,
 Control Information - type of transfer, transfer size etc
 Payload Information - data content of the transfer
 Configuration Information - mode of operation, error behavior etc
 Analysis Information - fields used to capture information from DUT, ex: read
data, response etc
as analysis information fields will be used for capturing response, except these fields
the other fields can be declared as rand and can have constraints associated with it.
Sequence item example:

class mem_seq_item extends uvm_sequence_item;

//Control Information

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

//Payload Information

rand bit [7:0] wdata;

//Analysis Information

bit [7:0] rdata;

//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass
UVM Sequence item Methods

create():
The create method allocates a new object of the same type as this object and returns
it via a base uvm_object handle.
print():
The print method deep-prints this object’s properties in a format and manner
governed by the given printer argument;
Create() and Print() Method

class mem_seq_item extends uvm_sequence_item;

//Control Information

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

//Payload Information

rand bit [7:0] wdata;

//Analysis Information

bit [7:0] rdata;

//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction
//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass

//-------------------------------------------------------------------------

//Simple TestBench to create and randomize sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item;

initial begin

//create method

seq_item = mem_seq_item::type_id::create();

//randomizing the seq_item

seq_item.randomize();

//printing the seq_item

seq_item.print();

end

endmodule

Simulator Output:

---------------------------------------
Name Type Size Value
---------------------------------------
mem_seq_item mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
---------------------------------------
copy:
The copy makes this object a copy of the specified object.
Copy() Method

class mem_seq_item extends uvm_sequence_item;

//Control Information

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

//Payload Information

rand bit [7:0] wdata;

//Analysis Information

bit [7:0] rdata;

//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass
//-------------------------------------------------------------------------

//Simple TestBench to access sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item_0;

mem_seq_item seq_item_1;

initial begin

//create method

seq_item_0 = mem_seq_item::type_id::create("seq_item_0");

seq_item_1 = mem_seq_item::type_id::create("seq_item_1");

seq_item_0.randomize(); //randomizing the seq_item

seq_item_0.print(); //printing the seq_item_0

//copy method

seq_item_1.copy(seq_item_0); //copy seq_item_0 to seq_item_1

seq_item_1.print(); //printing the seq_item_1

end

endmodule

Simulator Output:
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_1 mem_seq_item - @338
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
clone
The clone method creates and returns an exact copy of this object. clone = create() +
copy();
Clone() Method

class mem_seq_item extends uvm_sequence_item;

//data and control fields

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

rand bit [7:0] wdata;

bit [7:0] rdata;

//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass
//-------------------------------------------------------------------------

//Simple TestBench to access sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item_0;

mem_seq_item seq_item_1;

initial begin

//create method

seq_item_0 = mem_seq_item::type_id::create("seq_item_0");

seq_item_0.randomize(); //randomizing the seq_item

seq_item_0.print(); //printing the seq_item_0

//clone method

$cast(seq_item_1,seq_item_0.clone()); //create seq_item_1 and copy seq_item_0 to seq_item_1

//changing the seq_item_1 values will not reflect on seq_item_0 values.

seq_item_1.addr = 8;

seq_item_1.wdata = 'h56;

`uvm_info("","Printing seq_item_0", UVM_LOW)

seq_item_0.print(); //printing the seq_item_0

`uvm_info("","Printing seq_item_1", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

//Note:: name of seq_item_1 will be printed as seq_item_0, because there is no option to pass argument to
create method while calling the clone method.

end

endmodule
Simulator Output:
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
UVM_INFO testbench.sv(52) @ 0: reporter [] Printing seq_item_0
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
UVM_INFO testbench.sv(54) @ 0: reporter [] Printing seq_item_1
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @338
addr integral 4 'h8
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h56
-------------------------------------

compare
Deep compares members of this data object with those of the object provided in the
rhs (right-hand side) argument, returning 1 on a match, 0 otherwise.

Compare Method

class mem_seq_item extends uvm_sequence_item;

//data and control fields

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

rand bit [7:0] wdata;


bit [7:0] rdata;

//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass

//-------------------------------------------------------------------------

//Simple TestBench to access sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item_0;

mem_seq_item seq_item_1;
initial begin

//create method

seq_item_0 = mem_seq_item::type_id::create("seq_item_0");

seq_item_1 = mem_seq_item::type_id::create("seq_item_1");

//---------------Mismatch Case------------------------------

seq_item_0.randomize(); //randomizing the seq_item_0

seq_item_1.randomize(); //randomizing the seq_item_1

seq_item_0.print(); //printing the seq_item_0

seq_item_1.print(); //printing the seq_item_1

//compare method

if(seq_item_0.compare(seq_item_1))

`uvm_info("","seq_item_0 matching with seq_item_1", UVM_LOW)

else

`uvm_error("","seq_item_0 is not matching with seq_item_1")

//---------------Matching Case------------------------------

seq_item_1.copy(seq_item_0); //copy seq_item_0 to seq_item_1

//compare method

if(seq_item_0.compare(seq_item_1))

`uvm_info("","seq_item_0 matching with seq_item_1", UVM_LOW)

else

`uvm_error("","seq_item_0 is not matching with seq_item_1")

end

endmodule
Simulator Output:
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_1 mem_seq_item - @338
addr integral 4 'h5
wr_en integral 1 'h0
rd_en integral 1 'h1
wdata integral 8 'h33
-------------------------------------
UVM_INFO @ 0: reporter [MISCMP] Miscompare for seq_item_0.addr: lhs = 'h4 : rhs = 'h5
UVM_INFO @ 0: reporter [MISCMP] 1 Miscompare(s) for object seq_item_1@338 vs. seq_item_0@334
UVM_ERROR testbench.sv(54) @ 0: reporter [] seq_item_0 is not matching with seq_item_1
UVM_INFO testbench.sv(59) @ 0: reporter [] seq_item_0 matching with seq_item_1

pack, pack_bytes, pack_ints


The pack methods bitwise-concatenate this object’s properties into an array of bits,
bytes, or ints.
unpack,unpack_bytes,unpack_ints
The unpack methods extract property values from an array of bits, bytes, or ints.
pack/unpack to/from Array of bit type

class mem_seq_item extends uvm_sequence_item;

//data and control fields

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

rand bit [7:0] wdata;

bit [7:0] rdata;

//Utility and Field macros,


`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass

//-------------------------------------------------------------------------

//Simple TestBench to access sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item_0;

mem_seq_item seq_item_1;

bit bit_packed_data[];

initial begin

//create method
seq_item_0 = mem_seq_item::type_id::create("seq_item_0");

seq_item_1 = mem_seq_item::type_id::create("seq_item_1");

//---------------------- PACK ------------------------------

seq_item_0.randomize(); //randomizing the seq_item_0

seq_item_0.print(); //printing the seq_item_0

seq_item_0.pack(bit_packed_data); //pack method

foreach(bit_packed_data[i])

`uvm_info("PACK",$sformatf("bit_packed_data[%0d] = %b",i,bit_packed_data[i]), UVM_LOW)

//---------------------- UNPACK ------------------------------

`uvm_info("UNPACK","Before UnPack", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

seq_item_1.unpack(bit_packed_data); //unpack method

`uvm_info("UNPACK","After UnPack", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

end

endmodule

Simulator Output:
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[0] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[1] = 1
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[2] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[3] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[4] = 1
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[5] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[6] = 1
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[7] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[8] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[9] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[10] = 1
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[11] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[12] = 0
UVM_INFO testbench.sv(51) @ 0: reporter [PACK] pack_data[13] = 0
UVM_INFO testbench.sv(54) @ 0: reporter [UNPACK] Before UnPack
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_1 mem_seq_item - @338
addr integral 4 'h0
wr_en integral 1 'h0
rd_en integral 1 'h0
wdata integral 8 'h0
-------------------------------------
UVM_INFO testbench.sv(57) @ 0: reporter [UNPACK] After UnPack
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_1 mem_seq_item - @338
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------

pack/unpack to/from Array of byte type

class mem_seq_item extends uvm_sequence_item;

//data and control fields

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

rand bit [7:0] wdata;

bit [7:0] rdata;

//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass

//-------------------------------------------------------------------------

//Simple TestBench to access sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item_0;

mem_seq_item seq_item_1;

byte unsigned byte_packed_data[];

initial begin

//create method

seq_item_0 = mem_seq_item::type_id::create("seq_item_0");

seq_item_1 = mem_seq_item::type_id::create("seq_item_1");
//---------------------- PACK ------------------------------

seq_item_0.randomize(); //randomizing the seq_item_0

seq_item_0.print(); //printing the seq_item_0

seq_item_0.pack_bytes(byte_packed_data); //pack method

foreach(byte_packed_data[i])

`uvm_info("PACK",$sformatf("byte_packed_data[%0d] = %b",i,byte_packed_data[i]), UVM_LOW)

//---------------------- UNPACK ------------------------------

`uvm_info("UNPACK","Before UnPack", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

seq_item_1.unpack_bytes(byte_packed_data); //unpack method

`uvm_info("UNPACK","After UnPack", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

end

endmodule

Simulator Output:
-------------------------------------

Name Type Size Value

-------------------------------------

seq_item_0 mem_seq_item - @334

addr integral 4 'h4

wr_en integral 1 'h1

rd_en integral 1 'h0


wdata integral 8 'h88

-------------------------------------

UVM_INFO testbench.sv(51) @ 0: reporter [PACK] byte_packed_data[0] = 01001010

UVM_INFO testbench.sv(51) @ 0: reporter [PACK] byte_packed_data[1] = 00100000

UVM_INFO testbench.sv(54) @ 0: reporter [UNPACK] Before UnPack

-------------------------------------

Name Type Size Value

-------------------------------------

seq_item_1 mem_seq_item - @338

addr integral 4 'h0

wr_en integral 1 'h0

rd_en integral 1 'h0

wdata integral 8 'h0

-------------------------------------

UVM_INFO testbench.sv(57) @ 0: reporter [UNPACK] After UnPack

-------------------------------------

Name Type Size Value

-------------------------------------

seq_item_1 mem_seq_item - @338

addr integral 4 'h4

wr_en integral 1 'h1

rd_en integral 1 'h0

wdata integral 8 'h88

-------------------------------------

pack/unpack to/from Array of int type


class mem_seq_item extends uvm_sequence_item;

//data and control fields

rand bit [3:0] addr;

rand bit wr_en;

rand bit rd_en;

rand bit [7:0] wdata;

bit [7:0] rdata;


//Utility and Field macros,

`uvm_object_utils_begin(mem_seq_item)

`uvm_field_int(addr,UVM_ALL_ON)

`uvm_field_int(wr_en,UVM_ALL_ON)

`uvm_field_int(rd_en,UVM_ALL_ON)

`uvm_field_int(wdata,UVM_ALL_ON)

`uvm_object_utils_end

//Constructor

function new(string name = "mem_seq_item");

super.new(name);

endfunction

//constaint, to generate any one among write and read

constraint wr_rd_c { wr_en != rd_en; };

endclass

//-------------------------------------------------------------------------

//Simple TestBench to access sequence item

//-------------------------------------------------------------------------

module seq_item_tb;

//instance

mem_seq_item seq_item_0;

mem_seq_item seq_item_1;

int unsigned int_packed_data[];

initial begin

//create method

seq_item_0 = mem_seq_item::type_id::create("seq_item_0");

seq_item_1 = mem_seq_item::type_id::create("seq_item_1");

//---------------------- PACK ------------------------------

seq_item_0.randomize(); //randomizing the seq_item_0


seq_item_0.print(); //printing the seq_item_0

seq_item_0.pack_ints(int_packed_data); //pack method

foreach(int_packed_data[i])

`uvm_info("PACK",$sformatf("int_packed_data[%0d] = %b",i,int_packed_data[i]), UVM_LOW)

//---------------------- UNPACK ------------------------------

`uvm_info("UNPACK","Before UnPack", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

seq_item_1.unpack_ints(int_packed_data); //unpack method

`uvm_info("UNPACK","After UnPack", UVM_LOW)

seq_item_1.print(); //printing the seq_item_1

end

endmodule

Simulator Output:
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_0 mem_seq_item - @334
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
-------------------------------------
UVM_INFO testbench.sv(52) @ 0: reporter [PACK] int_packed_data[0] = 01001010001000000000000000000000
UVM_INFO testbench.sv(55) @ 0: reporter [UNPACK] Before UnPack
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_1 mem_seq_item - @338
addr integral 4 'h0
wr_en integral 1 'h0
rd_en integral 1 'h0
wdata integral 8 'h0
-------------------------------------
UVM_INFO testbench.sv(60) @ 0: reporter [UNPACK] After UnPack
-------------------------------------
Name Type Size Value
-------------------------------------
seq_item_1 mem_seq_item - @338
addr integral 4 'h4
wr_en integral 1 'h1
rd_en integral 1 'h0
wdata integral 8 'h88
UVM Sequence
How to write uvm sequence
Sequence generates series of sequence_item's and send to the driver via sequencer, Sequence is written
by extending the uvm_sequence.

UVM Sequence

 A uvm_sequence is derived from an uvm_sequence_item


 sequence is parameterized with the type of sequence_item, this defines the type of the item
sequence will send/recieve to/from the driver.

sequence base class


virtual class uvm_sequence #( type REQ = uvm_sequence_item,

type RSP = REQ ) extends uvm_sequence_base

example:

class write_sequence extends uvm_sequence #(mem_seq_item);


....

....

endclass

sequence has handle req and rsp of mem_seq_item.


request/req:
A transaction that provides information to initiate the processing of a particular operation.

response/rsp:
A transaction that provides information about the completion or status of a particular operation.

Sequence Execution
Most important properties of a sequence are,
 body method
 m_sequencer handle
body Method:
body method defines, what the sequence does.

m_sequencer Handle:
The m_sequencer handle contains the reference to the sequencer on which the sequence is running.
Sequence will get executed upon calling the start of sequence from test.
sequence_name.start(sequencer_name);

sequencer_name specifies on which sequencer sequence has to run.


 There are Methods, macros and pre-defined callbacks associated with uvm_sequence.
 User can define the methods(task or function) to pre-defined callbacks. these methods will get
executed automatically upon calling start of the sequence.
 These methods should not be called directly by the user.

Below block diagram shows the order in which the methods will get called on calling the start of
sequence.
uvm sequence phases

* mid_do and post_do are functions, All other are tasks

Starting The Sequence:

Logic to generate and sending the sequence_item will be written inside the body() method of the
sequence.
The handshake between the sequence, sequencer and driver to send the sequence_item is given below.
sequence driver communication

Communication between the Sequence and driver involves below steps,

1.create_item() /createreq.
2.wait_for_grant().
3.randomize the req.
4.send the req.
5.wait for item done.
6.get response.

*steps 5 and 6 are optional.


seq driver communication without response stage

Method Call Description

create_item() Create and initialize* a sequence_item or sequence.

*initialize - initialized to communicate with the specified sequencer.

req = **_seq_item::type_id::create(“req”);

wait_for_grant() This method call is blocking, Execution will be blocked until the method
returns.

1. This method issues a request to the current sequencer.

2. The sequencer grants on getting get_next_item() request from driver.

req.randomize() This method is to randomize the sequence_item


send_request(req,re-randomize) Send the request item to the sequencer, which will forward it to the driver.

* re-randomize = 0 or If the re-randomize bit is set, the item will be randomized before being sent
to the driver.
re-randomize = 1;

wait_for_item_done() This call is optional.

This task will block until the driver calls item_done or put.

get_current_item() Returns the request item currently being executed by the sequencer.

If the sequencer is not currently executing an item, this method will return
null.

get_response(rsp) receives the response from driver.

Writing UVM Sequence

class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_sequence)

//Constructor

function new(string name = "mem_sequence");

super.new(name);

endfunction

virtual task body();

req = mem_seq_item::type_id::create("req"); //create the req (seq item)

wait_for_grant(); //wait for grant

assert(req.randomize()); //randomize the req

send_request(req); //send req to driver

wait_for_item_done(); //wait for item done from driver

get_response(rsp); //get response from driver

endtask

endclass

// assert(req.randomize());, will return the assertion error on randomization failure.


UVM Sequence macros
These macros are used to start sequences and sequence items on default sequencer, m_sequencer.

Macro Description

`uvm_do(Item/Seq) This macro takes seq_item or sequence as argument.

On calling `uvm_do() the above defined 6 steps will be executed.

`uvm_create(Item/Seq) This macro creates the item or sequence.

`uvm_send(Item/Seq) create() and randomize() are skipped, rest all other steps are
executed.

`uvm_rand_send(Item/Seq) Only create() is skipped, rest all other steps are executed.

`uvm_do_with(Item/Seq,Constraints) This macro performs above 6 steps along with constraints defined
in second argument.

`uvm_rand_send_with(Item/Seq,Constraints) create() is skipped, rest all other steps are executed along with
constraints defined in second argument.

`uvm_do_pri(Item/Seq,Priority ) Performs `uvm_do() with priority mentioned.

`uvm_do_pri_with(Item/Seq,Constraints,Priority) Performs `uvm_do() along with constraints defined and priority


mentioned.

`uvm_send_pri(Item/Seq,Priority) create() and randomize() are skipped, rest all other steps are
executed with priority mentioned.

`uvm_rand_send_pri(Item/Seq,Priority) Only create() is skipped, rest all other steps are executed with
priority mentioned.

`uvm_rand_send_pri_with(Item/Seq,Priority, create() is skipped, rest all other steps are executed along with
Constraints) constraints defined with priority mentioned.

`uvm_declare_p_sequencer(SEQUENCER) This macro is used to declare a variable p_sequencer whose type


is specified by SEQUENCER.

by using p_sequencer handle, properties of sequencer can be


accessed.
Writing the sequence using Macro's
`uvm_do()
class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_sequence)

//Constructor

function new(string name = "mem_sequence");

super.new(name);

endfunction

virtual task body();

`uvm_do(req)

endtask

endclass

`uvm_create() and `uvm_send()

class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_sequence)

//Constructor

function new(string name = "mem_sequence");

super.new(name);

endfunction

virtual task body();

`uvm_create(req)

assert(req.randomize());

`uvm_send(req);

endtask

endclass

`uvm_rand_send()
class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_sequence)

//Constructor

function new(string name = "mem_sequence");


super.new(name);

endfunction

virtual task body();

`uvm_create(req)

`uvm_rand_send(req)

endtask

endclass

`uvm_do_with()
class write_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(write_sequence)

//Constructor

function new(string name = "write_sequence");

super.new(name);

endfunction

virtual task body();

`uvm_do_with(req,{req.wr_en == 1;})

endtask

endclass

`uvm_rand_send_with()
class read_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(read_sequence)

//Constructor

function new(string name = "read_sequence");

super.new(name);

endfunction

virtual task body();

`uvm_create(req)

`uvm_rand_send_with(req,{req.rd_en == 1;})

endtask

endclass
calling sequence's inside the sequence

class wr_rd_seq extends uvm_sequence#(mem_seq_item);

write_sequence wr_seq;

read_sequence rd_seq;

`uvm_object_utils(wr_rd_seq)

//Constructor

function new(string name = "wr_rd_seq");

super.new(name);

endfunction

virtual task body();

`uvm_do(wr_seq)

`uvm_do(rd_seq)

endtask

endclass

difference between m_sequencer and p_sequencer:


m_sequencer,
The m_sequencer handle contains the reference to the sequencer(default sequencer) on which the
sequence is running.
This is determined by,

 the sequencer handle provided in the start method


 the sequencer used by the parent sequence
 the sequencer that was set using the set_sequencer method
p_sequencer,
The p_sequencer is a variable, used as handle to access the sequencer properties.
p_sequencer is defined using the macro `uvm_declare_p_sequencer(SEQUENCER_NAME)
UVM Sequencer
The sequencer control the flow of request and response sequence items between sequences and the
driver.
Sequencer and driver uses TLM Interface to communicate transactions.
uvm_sequencer and uvm_driver base classes has seq_item_export and seq_item_port defined
respectively. User need to connect them using TLM connect method.
Example:
driver.seq_item_port.connect(sequencer.seq_item_export);
Sequencer can be written by extending the uvm_sequencer parameterized with seq_item type.

uvm sequencer

UVM Sequencer Code

class mem_sequencer extends uvm_sequencer#(mem_seq_item);

`uvm_sequencer_utils(mem_sequencer)

function new (string name, uvm_component parent);

super.new(name, parent);

endfunction : new

endclass : mem_sequencer
UVM Configuration Database
UVM Config db
The configuration database provide access to a centralized database, where type specific information can
be stored and received. config_db can contain scalar objects, class handles, queues, lists, or even virtual
interfaces.

The database has both a name table and a type table and each resource is entered into both. Resources
are stored in a database so that each resource can be retrieved by name or by type, and database is
globally accessible.

uvm config db get and set

uvm_config_db::set and uvm_config_db::get methods are used to store and retrieve the information from
database respectively.

uvm config db set method

void uvm_config_db#(type T = int)::set(uvm_component cntxt, string inst_name, stringfield_name, T value);

Where,

• T is the type of the element being configured.

Type can be scalar objects, class handles, queues, lists, or even virtual interfaces).

• cntxt is the hierarchical starting point of where the database entry is accessible.

• inst_name is hierarchical path that limits accessibility of the database entry.

example:

top.env.agent.monitor

top.* - all of the scopes whose top-level component is top.

top.env.*.monitor - all of the scopes in env that end in monitor;

• field_name is the label used as a lookup for the database entry.


• value is the value to be stored in the database.

uvm_config_db set example


Below example shows, setting the interface handle intf, type mem_if, label mem_intf with global scope.

mem_if intf(clk,reset); //interface instance

uvm_config_db#(virtual mem_if)::set(null,"*","mem_intf",intf); //set method

uvm config db get and set

The above diagrams illustrates how a resource whose name is mem_intf and type is mem_if is stored in
the pool.

uvm config db get method

bit uvm_config_db#(type T=int)::get(uvm_component cntxt, string inst_name, stringfield_name, ref T value);

• value is the variable to which the value is to be retreived from the database.

* The other fields are same as in set method.


The method returns 1 if it is successful and 0 if there is no such resource of this type in database.

uvm_config_db get example

Below example shows. Using the get method to get virtual interface handle from database and assigns it
to mem_vif. If get method fails, the fatal message will be displayed.

virtual interface mem_if mem_vif; //virtual interface declaration

if( !uvm_config_db#(virtual mem_if)::get(this,"*", "mem_intf", mem_vif))

`uvm_fatal(get_full_name(),{"virtual interface must be set for:",".mem_vif"} ); //get method

VM Phases
UVM Phases top down
UVM Phases are a synchronizing mechanism for the environment.

Phases are represented by callback methods, A set of predefined phases and corresponding callbacks are
provided in uvm_component. The Method can be either function or task.

Any class deriving from uvm_component may implement any or all of these callbacks, which are executed
in a particular order.

The UVM Phases are,


 build
 connect
 end of elaboration
 start of simulation
 run
 extract
 check
 report

run phase is implemented as task and remaining all are function.


UVM Phases

Phases can be grouped into 3 categories,

1. Build Phases
build phase, connect phase and end_of_elobaration phase's belong to this category.
Phases in this categorize are executed at the start of the UVM testbench simulation, where the testbench
components are constructed, configured and testbench components are connected.

All the build phase methods are functions and therefore execute in zero simulation time.

uvm run phase


2. Run-time Phases
start of simulation and run phase belongs to run-time phases, run phase will get executed from start of
simulation to till the end of simulation.
run phase is time consuming, where the testcase is running.

3. Clean up Phases
extract, check, report and final belong to this category.
where the results of the testcase are collected and reported. example: number of error's during the
simulation are reported.

Phases Description:

Phase Description Execution


Order
build Used to construct the testbench components. top-down
connect Used to connect TLM ports of components. bottom-up
end_of_elaboration Used to make any final adjustments to the structure, configuration or bottom-up
connectivity of the testbench before simulation starts.

start_of_simulation used for printing testbench topology or configuration information. bottom-up

run Used for stimulus generation, driving, monitoring and checking. parallel

extract Used to retrieve and process information from scoreboards and


functional coverage monitors.

check Used to check that the DUT behaved correctly and to identify any errors
that may have occurred during the execution of the test bench.

report Used to display the results of the simulation or to write the results to file.

final Used to complete any other outstanding actions that the test bench has
not already completed.

UVM run phase phases

run phase has different phases, these are,


Phase Description

pre_reset pre_reset phase starts at the same time as the run phase. Its purpose is to take care of any
activity that should occur before reset, such as waiting for a power-good signal to go active.

reset The reset phase is reserved for DUT or interface specific reset behavior. For example, this
phase would be used to generate a reset and to put an interface into its default state.

post_reset Intended for any activity required immediately following reset.

pre_configure pre_configure phase is intended for anything that is required to prepare for the DUT's
configuration process after reset is completed

configure configure phase is used to program the DUT and any memories in the testbench so that it is
run ready for the start of the test case.
post_configure post_configure phase is used to wait for the effects of configuration to propagate through
the DUT, or for it to reach a state where it is ready to start the main test stimulus.

pre_main pre_main phase is used to ensure that all required components are ready to start generating
stimulus.

main This is where the stimulus specified by the test case is generated and applied to the DUT. It
completes when either all stimulus is exhausted or a timeout occurs.

post_main This phase is used to take care of any finalization of the main phase.

pre_shutdown This phase is a buffer for any DUT stimulus that needs to take place before the shutdown
phase.

shutdown The shutdown phase is used to ensure that the effects of the stimulus generated during the
main phase have propagated through the DUT and that any resultant data has drained away.

post_shutdown Perform any final activities before exiting the active simulation phases. At the end of the
post_shutdown phase, the UVM testbench execution process starts the clean up phases.
UVM Driver
Driver is written by extending uvm_driver.

uvm_driver is inherited from uvm_component, Methods and TLM port (seq_item_port) are defined for
communication between sequencer and driver.

The uvm_driver is a parameterized class and it is parameterized with the type of the request
sequence_item and the type of the response sequence_item.

UVM_Driver Methods

get_next_item
This method blocks until a REQ sequence_item is available in the sequencer.

try_next_item
This is a non-blocking variant of the get_next_item() method. It will return a null pointer if there is no REQ
sequence_item available in the sequencer.

item_done
The non-blocking item_done() method completes the driver-sequencer handshake and it should be called
after a get_next_item() or a successful try_next_item() call.

put
The put() method is non-blocking and is used to place a RSP sequence_item in the sequencer.

Writing Driver :

1. Driver is written by extending the UVM_DRIVER

class mem_driver extends uvm_driver #(mem_seq_item);

`uvm_component_utils(mem_driver)

// Constructor

function new (string name, uvm_component parent);

super.new(name, parent);

endfunction : new

endclass : mem_driver
2. Declare the virtual interface,

// Virtual Interface

virtual mem_if vif;

if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))

`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});

4. Will place the get config_db in the build_phase,

function void build_phase(uvm_phase phase);

super.build_phase(phase);

if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))

`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});

endfunction: build_phase

5. Add driving logic. get the seq_item and drive to DUT signals,
// run phase

virtual task run_phase(uvm_phase phase);

forever begin

seq_item_port.get_next_item(req);

......

.. driving logic ..

......

seq_item_port.item_done();

end

endtask : run_phase
uvm driver example
below is the complete drive code.

class mem_driver extends uvm_driver #(mem_seq_item);

// Virtual Interface

virtual mem_if vif;

`uvm_component_utils(mem_driver)

//uvm_analysis_port #(mem_seq_item) Drvr2Sb_port;

// Constructor

function new (string name, uvm_component parent);

super.new(name, parent);

endfunction : new

function void build_phase(uvm_phase phase);

super.build_phase(phase);

if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))

`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});

endfunction: build_phase

// run phase

virtual task run_phase(uvm_phase phase);

forever begin

seq_item_port.get_next_item(req);

//respond_to_transfer(req);

drive();
seq_item_port.item_done();

end

endtask : run_phase

// drive

virtual task drive();

req.print();

`DRIV_IF.wr_en <= 0;

`DRIV_IF.rd_en <= 0;

@(posedge vif.DRIVER.clk);

`DRIV_IF.addr <= req.addr;

if(req.wr_en) begin

`DRIV_IF.wr_en <= req.wr_en;

`DRIV_IF.wdata <= req.wdata;

//$display("\tADDR = %0h \tWDATA = %0h",req.addr,trans.wdata);

@(posedge vif.DRIVER.clk);

end

if(req.rd_en) begin

`DRIV_IF.rd_en <= req.rd_en;

@(posedge vif.DRIVER.clk);

`DRIV_IF.rd_en <= 0;

@(posedge vif.DRIVER.clk);

req.rdata = `DRIV_IF.rdata;

// $display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);

end

$display("-----------------------------------------");

endtask : drive

endclass : mem_driver
UVM Monitor
UVM Monitor

User defined monitor is extended from uvm_monitor, uvm_monitor is inherited by uvm_component.

A monitor is a passive entity that samples the DUT signals through virtual interface and converts the signal
level activity to transaction level.

Monitor samples DUT signals but does not drive them.

Monitor should have analysis port (TLM port) and virtual interface handle that points to DUT signals.

Writing Monitor :

1. Monitor is written by extending the UVM_MONITOR,

class mem_monitor extends uvm_monitor;

`uvm_component_utils(mem_monitor)

// new - constructor

function new (string name, uvm_component parent);

super.new(name, parent);

trans_collected = new();

item_collected_port = new("item_collected_port", this);

endfunction : new

endclass : mem_monitor

2. Declare virtual interface,

// Virtual Interface
virtual mem_if vif;

3. Connect interface to Virtual interface by using get method,

function void build_phase(uvm_phase phase);


super.build_phase(phase);

if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))

`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});

endfunction: build_phase

4. Declare analysis port,

uvm_analysis_port #(mem_seq_item) item_collected_port;

5. Declare seq_item handle, Used as place holder for sampled signal activity,

mem_seq_item trans_collected;

6. Add Sampling logic in run_phase,

7. After sampling, by using write method send the sampled transaction packet to the scoreboard,

item_collected_port.write(trans_collected);

Complete monitor code,

class mem_monitor extends uvm_monitor;

// Virtual Interface

virtual mem_if vif;

uvm_analysis_port #(mem_seq_item) item_collected_port;

// Placeholder to capture transaction information.

mem_seq_item trans_collected;

`uvm_component_utils(mem_monitor)
// new - constructor

function new (string name, uvm_component parent);

super.new(name, parent);

trans_collected = new();

item_collected_port = new("item_collected_port", this);

endfunction : new

function void build_phase(uvm_phase phase);

super.build_phase(phase);

if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))

`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});

endfunction: build_phase

// run phase

virtual task run_phase(uvm_phase phase);

item_collected_port.write(trans_collected);

endtask : run_phase

endclass : mem_monitor

UVM Agent
UVM Agent

user-defined agent is extended from uvm_agent, uvm_agent is inherited by uvm_component.

An agent typically contains: a driver,sequencer, and monitor.

Agents can be configured either active or passive.


Active agent:

Active agents generate stimulus and drive to DUT.

Active agent should contain all three components driver,sequencer, and monitor.

Passive agent:

Passive agents samples DUT signals but does not drive them.

Passive agent should contain only the monitor.


Agent can be configured as ACTIVE/PASSIVE by using set config method, default agent will be ACTIVE. set
config can be done in env or test.

set_config_int("path_to_agent", "is_active", UVM_ACTIVE);

set_config_int("path_to_agent", "is_active", UVM_PASSIVE);

get_is_active() Method:

get_is_active() Returns UVM_ACTIVE if the agent is acting as an active agent and UVM_PASSIVE if the
agent acting as a passive agent.

Writing Agent :

1. Agent is written by extending UVM_agent,

class mem_agent extends uvm_agent;

// UVM automation macros for general components

`uvm_component_utils(mem_agent)

// constructor

function new (string name, uvm_component parent);

super.new(name, parent);

endfunction : new

endclass : mem_agent

2. Declare driver, sequencer and monitor instance,

//declaring agent components


mem_driver driver;

mem_sequencer sequencer;

mem_monitor monitor;
3. Depending on Agent type, create agent components in build phase,
driver and sequencer will be crated only for active agent.

// build_phase
function void build_phase(uvm_phase phase);

super.build_phase(phase);

if(get_is_active() == UVM_ACTIVE) begin

driver = mem_driver::type_id::create("driver", this);

sequencer = mem_sequencer::type_id::create("sequencer", this);

end

monitor = mem_monitor::type_id::create("monitor", this);

endfunction : build_phase

4. Connect the driver seq_item_port to sequencer seq_item_export for communication between driver
and sequencer in connect phase.

// connect_phase
function void connect_phase(uvm_phase phase);

if(get_is_active() == UVM_ACTIVE) begin

driver.seq_item_port.connect(sequencer.seq_item_export);

end

endfunction : connect_phase

Complete Agent code,

class mem_agent extends uvm_agent;


//declaring agent components

mem_driver driver;

mem_sequencer sequencer;

mem_monitor monitor;
// UVM automation macros for general components

`uvm_component_utils(mem_agent)

// constructor

function new (string name, uvm_component parent);

super.new(name, parent);

endfunction : new

// build_phase

function void build_phase(uvm_phase phase);

super.build_phase(phase);

if(get_is_active() == UVM_ACTIVE) begin

driver = mem_driver::type_id::create("driver", this);

sequencer = mem_sequencer::type_id::create("sequencer", this);

end

monitor = mem_monitor::type_id::create("monitor", this);

endfunction : build_phase

// connect_phase

function void connect_phase(uvm_phase phase);

if(get_is_active() == UVM_ACTIVE) begin

driver.seq_item_port.connect(sequencer.seq_item_export);

end

endfunction : connect_phase

endclass : mem_agent
UVM Scoreboard Example
UVM Scoreboard
User-defined scoreboard is extended from uvm_scoreboard, uvm_scoreboard is inherited by
uvm_component.

Writing Scoreboard
Scoreboard is written by extending the UVM_SCOREBOARD

class mem_scoreboard extends uvm_scoreboard;

`uvm_component_utils(mem_scoreboard)

// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

endclass : mem_scoreboard

 Scoreboard will check the correctness of the DUT by comparing the DUT output with the
expected values.
 scoreboard will receive the transactions from the Monitors implemented inside agents.
 Monitor and scoreboard will communicate via TLM ports and exports.

Scoreboard shall compare the DUT output values with,


1. The golden reference values.
2. The values Generated from reference model.
UVM Scoreboard

*click on image for better view

Declare and Create TLM Analysis port, ( to receive transaction pkt from Monitor).
//Declaring port
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;

//creating port
item_collected_export = new("item_collected_export", this);

analysis export of Scoreboard is connected to Monitor port


monitor.item_collected_port.connect(scoreboard.item_collected_export);

uvm scoreboard write function

write method of scoreboard will receive the transaction packet from monitor, on calling write method
from monitor.
uvm scoreboard write function

//calling write method from monitor


item_collected_port.write(pkt);

//scoreboard write function


virtual function void write(mem_seq_item pkt);
pkt.print();
endfunction : write

UVM scoreboard code


Below is the complete scoreboard code.

class mem_scoreboard extends uvm_scoreboard;

`uvm_component_utils(mem_scoreboard)
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;

// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);


super.build_phase(phase);
item_collected_export = new("item_collected_export", this);
endfunction: build_phase

// write
virtual function void write(mem_seq_item pkt);
$display("SCB:: Pkt recived");
pkt.print();
endfunction : write

endclass : mem_scoreboard

UVM Env
User-defined environment is derived from uvm_env, uvm_env is inherited from uvm_component.

Environment is the container class, It contains one or more agents, as well as other components such as
scoreboard, top level monitor, and checker.

Writing Environment:

1. Environment is written by extending UVM_ENV.

class mem_model_env extends uvm_env;

`uvm_component_utils(mem_model_env)

// new - constructor

function new(string name, uvm_component parent);

super.new(name, parent);

endfunction : new

endclass : mem_model_env

2. Declare the agent,

mem_agent mem_agnt;

3. Create agent,

mem_agnt = mem_agent::type_id::create("mem_agnt", this);

Complete environment code,


class mem_model_env extends uvm_env;

mem_agent mem_agnt;

`uvm_component_utils(mem_model_env)

// new - constructor

function new(string name, uvm_component parent);

super.new(name, parent);

endfunction : new

// build_phase

function void build_phase(uvm_phase phase);

super.build_phase(phase);

mem_agnt = mem_agent::type_id::create("mem_agnt", this);

endfunction : build_phase

endclass : mem_model_env

UVM Objection / Managing End of Test

UVM Objection

UVM provides an objection mechanism to allow hierarchical status communication among components
which is helpful in deciding the end of test.

There is a built-in objection for each phase, which provides a way for components and objects to
synchronize their testing activity and indicate when it is safe to end the phase and, ultimately, the test
end.
The component or sequence will raise a phase objection at the beginning of an activity that must be
completed before the phase stops, so the objection will be dropped at the end of that activity. Once all
of the raised objections are dropped, the phase terminates.

Lets consider an example,

A master agent may need to complete all its write and read operations before the run phase should be
allowed to stop.

In this example, typically objections can either be added in sequence or test.

Objection in sequence,
objection is raised when it started as a root sequence (a sequence which has no parent sequence), and
to drop the objection when it is finished as a root sequence.

class wr_rd_seq extends uvm_sequence#(mem_seq_item);


task pre_body();

// raise objection if started as a root sequence

if(starting_phase != null)

starting_phase.raise_objection(this);

endtask

task body();

`uvm_do_with(req,wr_en==1);

`uvm_do_with(req,rd_en==1);

endtask

task post_body();

// drop objection if started as a root sequence

if(starting_phase != null)

starting_phase.drop_objection(this);

endtask

endclass
The starting_phase member is only set automatically if the sequence is started as the default sequence

for a particular phase. so objections were used in the sequence if it is default sequence for a particular
phase.

Objection in test,
If the sequence need to be started explicitly i.e in the test, then the objections were added in the test
instead of sequence

class wr_rd_test extends uvm_test;

-----

-----

task main_phase(uvm_phase phase);

phase.raise_objection(); //rasing objection

wr_rd_seq.start(mem_agent.sequencer);

phase.drop_objection(); //droping objection

endtask

endclass

Note:
When the sequence is started explicitly, the starting_phase member is null, so the sequence will not
raise or drop the phase objection.

UVM Test
UVM Test

User-defined test is derived from uvm_test, uvm_test is inherited from uvm_component.

Test defines the test scenario for the testbench.

test class contains the environment, configuration properties, class overrides etc.

A sequence/sequences are created and started in the test.


The UVM testbench is activated when the run_test() method is called, the global run_test() task should
be specified inside an initial block.

initial begin

run_test();

end

There can be many user defined testcases.

Among multiple test cases, particular test case can be selected and execute on two methods,

1. by specifying the testname as argument to run_test();

example: run_test("mem_model_test");

2. by providing the UVM_TESTNAME command line argument

example: <SIMULATION_COMMANDS> +UVM_TESTNAME=mem_model_test

Writing Test :

1. Test is written by extending the UVM_TEST,

class mem_model_test extends uvm_test;

`uvm_component_utils(mem_model_test)

function new(string name = "mem_model_test",uvm_component parent=null);

super.new(name,parent);

endfunction : new

endclass : mem_model_test

2. Declare env and sequence,

mem_model_env env;
mem_sequence seq;
3. Create env and sequence,

env = mem_model_env::type_id::create("env",this);
seq = mem_sequence::type_id::create("seq");

4. Start the sequence,

seq.start(env.mem_agnt.sequencer);

Complete Test code,

class mem_model_test extends uvm_test;

`uvm_component_utils(mem_model_test)

mem_model_env env;

mem_sequence seq;

function new(string name = "mem_model_test",uvm_component parent=null);

super.new(name,parent);

endfunction : new

virtual function void build_phase(uvm_phase phase);

super.build_phase(phase);

env = mem_model_env::type_id::create("env", this);

seq = mem_sequence::type_id::create("seq");

endfunction : build_phase

task run_phase(uvm_phase phase);

seq.start(env.mem_agnt.sequencer);

endtask : run_phase
endclass : mem_model_test

UVM tb top
UVM tb architecture
TestBench top is the module, it connects the DUT and Verification environment components.

Typical Testbench_top contains,


 DUT instance
 interface instance
 run_test() method
 virtual interface set config_db
 clock and reset generation logic
 wave dump logic

uvm tb top

*click on image for better view

module tbench_top;

//clock and reset signal declaration


bit clk;
bit reset;

//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end

//creatinng instance of interface, inorder to connect DUT and testcase


mem_if intf(clk,reset);

//DUT instance, interface signals are connected to the DUT ports


memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);

//enabling the wave dump


initial begin
uvm_config_db#(virtual mem_if)::set(uvm_root::get(),"*","mem_intf",intf);
$dumpfile("dump.vcd"); $dumpvars;
end

initial begin
run_test();
end
endmodule
UVM TestBench architecture
*click on image for better view

To maintain uniformity in naming the components/objects, all the component/object name's are starts
with mem_*.

TestBench Components/Objects
Sequence item

 fields required to generate the stimulus are declared in the sequence_item.


 sequence_item can be used as placeholder for the activity monitored by monitor on DUT signals.

1. sequence_item is written by extending uvm_seq_item;

class mem_seq_item extends uvm_sequence_item;

//Utility macro
`uvm_object_utils(mem_seq_item)

//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass

2. Declaring the fields in mem_seq_item,

class mem_seq_item extends uvm_sequence_item;

//data and control fields


bit [3:0] addr;
bit wr_en;
bit rd_en;
bit [7:0] wdata;
bit [7:0] rdata;

//Utility macro
`uvm_object_utils(mem_seq_item)

//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass

3. To generate the random stimulus, declare the fields as rand.

class mem_seq_item extends uvm_sequence_item;

//data and control fields


rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;

//Utility macro
`uvm_object_utils(mem_seq_item)

//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass

4. In order to use the uvm_object methods ( copy, compare, pack, unpack, record, print, and etc ),
all the fields are registered to uvm_field_* macros.

class mem_seq_item extends uvm_sequence_item;


//data and control fields
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;

//Utility and Field macros,


`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr_en,UVM_ALL_ON)
`uvm_field_int(rd_en,UVM_ALL_ON)
`uvm_field_int(wdata,UVM_ALL_ON)
`uvm_object_utils_end

//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction

endclass

5. Either write or read operation will be performed at once, so constraint is added to generate wr_en and
rd_en.

class mem_seq_item extends uvm_sequence_item;


//data and control fields
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;

//Utility and Field macros,


`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr_en,UVM_ALL_ON)
`uvm_field_int(rd_en,UVM_ALL_ON)
`uvm_field_int(wdata,UVM_ALL_ON)
`uvm_object_utils_end

//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction

//constaint, to generate any one among write and read


constraint wr_rd_c { wr_en != rd_en; };
endclass

Complete mem_seq_item code,

class mem_seq_item extends uvm_sequence_item;


//data and control fields
rand bit [3:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;

//Utility and Field macros,


`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(addr,UVM_ALL_ON)
`uvm_field_int(wr_en,UVM_ALL_ON)
`uvm_field_int(rd_en,UVM_ALL_ON)
`uvm_field_int(wdata,UVM_ALL_ON)
`uvm_object_utils_end

//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction

//constaint, to generate any one among write and read


constraint wr_rd_c { wr_en != rd_en; };

endclass

Sequence

 Sequence generates the stimulus and sends to driver via sequencer.


 An agent can have any number of sequences.

1. Sequecne is written by extending the uvm_sequence.

class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_sequence_utils(mem_sequence,mem_sequencer)

//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
endclass

2. Logic to generate and sending the sequence_item is added inside the body() method.

class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_sequence_utils(mem_sequence,mem_sequencer)

//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction

virtual task body();

req = mem_seq_item::type_id::create("req");
wait_for_grant();
req.randomize();
send_request(req);
wait_for_item_done();

endtask

endclass

write sequence,

class mem_wr_seq extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_wr_seq)

//Constructor
function new(string name = "mem_wr_seq");
super.new(name);
endfunction

virtual task body();


`uvm_do_with(req,{req.wr_en == 1;})
endtask

endclass
read sequence,

class mem_rd_seq extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_rd_seq)
//Constructor
function new(string name = "mem_rd_seq");
super.new(name);
endfunction

virtual task body();


`uvm_do_with(req,{req.rd_en == 1;})
endtask

endclass

Sequencer

Sequencer is written by extending uvm_sequencer, there is no extra logic required to be added in


sequencer.

class mem_sequencer extends uvm_sequencer#(mem_seq_item);

`uvm_component_utils(mem_sequencer)

//constructor
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction

endclass

Driver

driver receives the stimulus from sequence via sequencer and drives on interface signals.

1. driver is written by extending the uvm_driver

class mem_driver extends uvm_driver #(mem_seq_item);

`uvm_component_utils(mem_driver)

// Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

endclass : mem_driver
2. Declare the virtual interface,

// Virtual Interface
virtual mem_if vif;

3. Get the interface handle using get config_db,

if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))


`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});

4. adding the get config_db in the build_phase,

function void build_phase(uvm_phase phase);


super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase

5. Add driving logic. get the seq_item and drive to DUT signals,

// run phase
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
......
.. driving logic ..
......
seq_item_port.item_done();
end
endtask : run_phase

Complete driver code,

class mem_driver extends uvm_driver #(mem_seq_item);

// Virtual Interface
virtual mem_if vif;

`uvm_component_utils(mem_driver)

//uvm_analysis_port #(mem_seq_item) Drvr2Sb_port;

// Constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NO_VIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase

// run phase
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
//respond_to_transfer(req);
drive();
seq_item_port.item_done();
end
endtask : run_phase

// drive
virtual task drive();
req.print();
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
`DRIV_IF.addr <= req.addr;
if(req.wr_en) begin
`DRIV_IF.wr_en <= req.wr_en;
`DRIV_IF.wdata <= req.wdata;
//$display("\tADDR = %0h \tWDATA = %0h",req.addr,trans.wdata);
@(posedge vif.DRIVER.clk);
end
if(req.rd_en) begin
`DRIV_IF.rd_en <= req.rd_en;
@(posedge vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
req.rdata = `DRIV_IF.rdata;
// $display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
endtask : drive

endclass : mem_driver

Monitor
 Monitor samples the DUT signals through virtual interface and converts the signal level activity to
transaction level.
1. Monitor is written by extending the uvm_monitor.

class mem_monitor extends uvm_monitor;

`uvm_component_utils(mem_monitor)

// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

endclass : mem_monitor

2. Declare virtual interface,

// Virtual Interface
virtual mem_if vif;

3. Connect interface to Virtual interface by using get method,

function void build_phase(uvm_phase phase);


super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase

4. Declare analysis port,

uvm_analysis_port #(mem_seq_item) item_collected_port;

5. Declare seq_item handle, Used as place holder for sampled signal activity,

mem_seq_item trans_collected;

6. Add Sampling logic in run_phase,

 sample the interface signal and assign to trans_collected handle


 sampling logic is placed in forever loop

// run phase
virtual task run_phase(uvm_phase phase);
forever begin
//sampling logic
@(posedge vif.MONITOR.clk);
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en);
trans_collected.addr = vif.monitor_cb.addr;
if(vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en;
trans_collected.wdata = vif.monitor_cb.wdata;
trans_collected.rd_en = 0;
@(posedge vif.MONITOR.clk);
end
if(vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en;
trans_collected.wr_en = 0;
@(posedge vif.MONITOR.clk);
@(posedge vif.MONITOR.clk);
trans_collected.rdata = vif.monitor_cb.rdata;
end
end
endtask : run_phase

7. After sampling, by using write method send the sampled transaction packet to the scoreboard,

item_collected_port.write(trans_collected);

Complete monitor code,

class mem_monitor extends uvm_monitor;

// Virtual Interface
virtual mem_if vif;

uvm_analysis_port #(mem_seq_item) item_collected_port;

// Placeholder to capture transaction information.


mem_seq_item trans_collected;

`uvm_component_utils(mem_monitor)

// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
trans_collected = new();
item_collected_port = new("item_collected_port", this);
endfunction : new

function void build_phase(uvm_phase phase);


super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
// run phase
virtual task run_phase(uvm_phase phase);
forever begin
@(posedge vif.MONITOR.clk);
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en);
trans_collected.addr = vif.monitor_cb.addr;
if(vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en;
trans_collected.wdata = vif.monitor_cb.wdata;
trans_collected.rd_en = 0;
@(posedge vif.MONITOR.clk);
end
if(vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en;
trans_collected.wr_en = 0;
@(posedge vif.MONITOR.clk);
@(posedge vif.MONITOR.clk);
trans_collected.rdata = vif.monitor_cb.rdata;
end
item_collected_port.write(trans_collected);
end
endtask : run_phase

endclass : mem_monitor

Agent

An agent is a container class contains a driver,sequencer, and monitor.

1. agent is written by extending the uvm_agent,

class mem_agent extends uvm_agent;

// UVM automation macros for general components


`uvm_component_utils(mem_agent)

// constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

endclass : mem_agent
2. Declare driver, sequencer and monitor instance,

//declaring agent components


mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;

3. Depending on Agent type, create agent components in build phase,


driver and sequencer will be crated only for active agent.

// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);

if(get_is_active() == UVM_ACTIVE) begin


driver = mem_driver::type_id::create("driver", this);
sequencer = mem_sequencer::type_id::create("sequencer", this);
end

monitor = mem_monitor::type_id::create("monitor", this);


endfunction : build_phase

4. Connect the driver seq_item_port to sequencer seq_item_export for communication between driver
and sequencer in connect phase.

// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase

Complete Agent code,

class mem_agent extends uvm_agent;


//declaring agent components
mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;

// UVM automation macros for general components


`uvm_component_utils(mem_agent)

// constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(phase);

if(get_is_active() == UVM_ACTIVE) begin


driver = mem_driver::type_id::create("driver", this);
sequencer = mem_sequencer::type_id::create("sequencer", this);
end

monitor = mem_monitor::type_id::create("monitor", this);


endfunction : build_phase

// connect_phase
function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase

endclass : mem_agent

Scoreboard

scoreboard receives the transaction from monitor and compares with the reference values.

1. Scoreboard is written by extending uvm_scoreboard,

class mem_scoreboard extends uvm_scoreboard;

`uvm_component_utils(mem_scoreboard)

// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

endclass : mem_scoreboard

2. Declare and Create TLM Analysis port, ( to receive transaction pkt from Monitor).

//Declaring port
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;
//creating port
item_collected_export = new("item_collected_export", this);

3. analysis export of Scoreboard is connected to Monitor port. (Connection is done in environment


connect phase)

monitor.item_collected_port.connect(scoreboard.item_collected_export);

4. write method of scoreboard will receive the transaction packet from monitor, on calling write method
from monitor.

uvm scoreboard connection

//calling write method from monitor


item_collected_port.write(pkt);

//scoreboard write function


virtual function void write(mem_seq_item pkt);
pkt.print();
endfunction : write

6. Add Sampling logic in run_phase,

// run phase
virtual task run_phase(uvm_phase phase);
--- comparision logic ---
endtask : run_phase

Complete scoreboard code


class mem_scoreboard extends uvm_scoreboard;

`uvm_component_utils(mem_scoreboard)
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;

// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);


super.build_phase(phase);
item_collected_export = new("item_collected_export", this);
endfunction: build_phase

// write
virtual function void write(mem_seq_item pkt);
$display("SCB:: Pkt recived");
pkt.print();
endfunction : write

// run phase
virtual task run_phase(uvm_phase phase);
--- comparision logic ---
endtask : run_phase
endclass : mem_scoreboard

Environment/env

Environment is the container class, It contains one or more agents, as well as other components such as
scoreboard, top level monitor, and checker.

1. Environment is written by extending the uvm_env,

class mem_model_env extends uvm_env;

`uvm_component_utils(mem_model_env)

// new - constructor
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_model_env

2. Declare the agent and scoreboard,

mem_agent mem_agnt;
mem_scoreboard mem_scb;

3. Create agent and scoreboard,

mem_agnt = mem_agent::type_id::create("mem_agnt", this);


mem_scb = mem_scoreboard::type_id::create("mem_scb", this);

3. Connecting monitor port to scoreboard port,

mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);

Complete environment code,

class mem_model_env extends uvm_env;

//---------------------------------------
// agent and scoreboard instance
//---------------------------------------
mem_agent mem_agnt;
mem_scoreboard mem_scb;

`uvm_component_utils(mem_model_env)

//---------------------------------------
// constructor
//---------------------------------------
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

//---------------------------------------
// build_phase - crate the components
//---------------------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase);

mem_agnt = mem_agent::type_id::create("mem_agnt", this);


mem_scb = mem_scoreboard::type_id::create("mem_scb", this);
endfunction : build_phase

//---------------------------------------
// connect_phase - connecting monitor and scoreboard port
//---------------------------------------
function void connect_phase(uvm_phase phase);
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);
endfunction : connect_phase

endclass : mem_model_env

Test

Test defines the test scenario for the testbench.

1. test is written by extending the uvm_test,

class mem_model_test extends uvm_test;

`uvm_component_utils(mem_model_test)

function new(string name = "mem_model_test",uvm_component parent=null);


super.new(name,parent);
endfunction : new

endclass : mem_model_test

2. Declare env and sequence,

mem_model_env env;
mem_sequence seq;

3. Create env and sequence,

env = mem_model_env::type_id::create("env",this);
seq = mem_sequence::type_id::create("seq");

4. Start sequence,

seq.start(env.mem_agnt.sequencer);

Complete Test code,

class mem_model_test extends uvm_test;

`uvm_component_utils(mem_model_test)

mem_model_env env;
mem_sequence seq;

function new(string name = "mem_model_test",uvm_component parent=null);


super.new(name,parent);
endfunction : new

virtual function void build_phase(uvm_phase phase);


super.build_phase(phase);

env = mem_model_env::type_id::create("env", this);


seq = mem_sequence::type_id::create("seq");
endfunction : build_phase

task run_phase(uvm_phase phase);


phase.raise_objection(this);
seq.start(env.mem_agnt.sequencer);
phase.drop_objection(this);
endtask : run_phase

endclass : mem_model_test

TestBench_Top

TestBench top is the module, it connects the DUT and Verification environment components.

Testbench_top contains,
 DUT instance
 interface instance
 run_test() method
 virtual interface set config_db
 clock and reset generation logic
 wave dump logic
run test

*click on image for better view

module tbench_top;

//clock and reset signal declaration


bit clk;
bit reset;

//clock generation
always #5 clk = ~clk;

//reset Generation
initial begin
reset = 1;
#5 reset =0;
end

//creatinng instance of interface, inorder to connect DUT and testcase


mem_if intf(clk,reset);

//DUT instance, interface signals are connected to the DUT ports


memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);
//enabling the wave dump
initial begin
uvm_config_db#(virtual mem_if)::set(uvm_root::get(),"*","vif",intf);
$dumpfile("dump.vcd"); $dumpvars;
end

initial begin
run_test();
end
endmodule

You might also like