UVM Prepared Document
UVM Prepared Document
UVM Prepared Document
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.
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
Test
This is the top most class. test is responsible for,
Environment or Env
The environment is a container component for grouping higher level components like agent's and
scoreboard.
Agent
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)
`uvm_field_*(FIELD,FLAG)
`uvm_object_utils_end
FLAG Description
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
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:
//Control Information
//Payload Information
//Analysis Information
`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
super.new(name);
endfunction
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
//Control Information
//Payload Information
//Analysis Information
`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
super.new(name);
endfunction
//constaint, to generate any one among write and read
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
module seq_item_tb;
//instance
mem_seq_item seq_item;
initial begin
//create method
seq_item = mem_seq_item::type_id::create();
seq_item.randomize();
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
//Control Information
//Payload Information
//Analysis Information
`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
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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");
//copy 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
-------------------------------------
-------------------------------------
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
`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
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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");
//clone method
seq_item_1.addr = 8;
seq_item_1.wdata = 'h56;
//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
`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
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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------------------------------
//compare method
if(seq_item_0.compare(seq_item_1))
else
//---------------Matching Case------------------------------
//compare method
if(seq_item_0.compare(seq_item_1))
else
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
`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
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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");
foreach(bit_packed_data[i])
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
-------------------------------------
`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
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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");
//---------------------- PACK ------------------------------
foreach(byte_packed_data[i])
end
endmodule
Simulator Output:
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
-------------------------------------
`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
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
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");
foreach(int_packed_data[i])
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
example:
....
endclass
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);
Below block diagram shows the order in which the methods will get called on calling the start of
sequence.
uvm sequence phases
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
1.create_item() /createreq.
2.wait_for_grant().
3.randomize the req.
4.send the req.
5.wait for item done.
6.get response.
req = **_seq_item::type_id::create(“req”);
wait_for_grant() This method call is blocking, Execution will be blocked until the method
returns.
* 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;
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.
`uvm_object_utils(mem_sequence)
//Constructor
super.new(name);
endfunction
endtask
endclass
Macro Description
`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_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_object_utils(mem_sequence)
//Constructor
super.new(name);
endfunction
`uvm_do(req)
endtask
endclass
`uvm_object_utils(mem_sequence)
//Constructor
super.new(name);
endfunction
`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
endfunction
`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
super.new(name);
endfunction
`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
super.new(name);
endfunction
`uvm_create(req)
`uvm_rand_send_with(req,{req.rd_en == 1;})
endtask
endclass
calling sequence's inside the sequence
write_sequence wr_seq;
read_sequence rd_seq;
`uvm_object_utils(wr_rd_seq)
//Constructor
super.new(name);
endfunction
`uvm_do(wr_seq)
`uvm_do(rd_seq)
endtask
endclass
uvm sequencer
`uvm_sequencer_utils(mem_sequencer)
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::set and uvm_config_db::get methods are used to store and retrieve the information from
database respectively.
Where,
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.
example:
top.env.agent.monitor
The above diagrams illustrates how a resource whose name is mem_intf and type is mem_if is stored in
the pool.
• value is the variable to which the value is to be retreived from the database.
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.
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.
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.
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:
run Used for stimulus generation, driving, monitoring and checking. parallel
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.
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.
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 :
`uvm_component_utils(mem_driver)
// Constructor
super.new(name, parent);
endfunction : new
endclass : mem_driver
2. Declare the virtual interface,
// Virtual Interface
super.build_phase(phase);
endfunction: build_phase
5. Add driving logic. get the seq_item and drive to DUT signals,
// run 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.
// Virtual Interface
`uvm_component_utils(mem_driver)
// Constructor
super.new(name, parent);
endfunction : new
super.build_phase(phase);
endfunction: build_phase
// run 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
req.print();
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
if(req.wr_en) begin
@(posedge vif.DRIVER.clk);
end
if(req.rd_en) begin
@(posedge vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge vif.DRIVER.clk);
req.rdata = `DRIV_IF.rdata;
end
$display("-----------------------------------------");
endtask : drive
endclass : mem_driver
UVM Monitor
UVM Monitor
A monitor is a passive entity that samples the DUT signals through virtual interface and converts the signal
level activity to transaction level.
Monitor should have analysis port (TLM port) and virtual interface handle that points to DUT signals.
Writing Monitor :
`uvm_component_utils(mem_monitor)
// new - constructor
super.new(name, parent);
trans_collected = new();
endfunction : new
endclass : mem_monitor
// Virtual Interface
virtual mem_if vif;
endfunction: build_phase
5. Declare seq_item handle, Used as place holder for sampled signal activity,
mem_seq_item trans_collected;
7. After sampling, by using write method send the sampled transaction packet to the scoreboard,
item_collected_port.write(trans_collected);
// Virtual Interface
mem_seq_item trans_collected;
`uvm_component_utils(mem_monitor)
// new - constructor
super.new(name, parent);
trans_collected = new();
endfunction : new
super.build_phase(phase);
endfunction: build_phase
// run phase
item_collected_port.write(trans_collected);
endtask : run_phase
endclass : mem_monitor
UVM Agent
UVM Agent
Active agent should contain all three components driver,sequencer, and monitor.
Passive agent:
Passive agents samples DUT signals but does not drive them.
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 :
`uvm_component_utils(mem_agent)
// constructor
super.new(name, parent);
endfunction : new
endclass : mem_agent
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);
end
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);
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase
mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;
// UVM automation macros for general components
`uvm_component_utils(mem_agent)
// constructor
super.new(name, parent);
endfunction : new
// build_phase
super.build_phase(phase);
end
endfunction : build_phase
// connect_phase
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
`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.
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);
write method of scoreboard will receive the transaction packet from monitor, on calling write method
from monitor.
uvm scoreboard write function
`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
// 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:
`uvm_component_utils(mem_model_env)
// new - constructor
super.new(name, parent);
endfunction : new
endclass : mem_model_env
mem_agent mem_agnt;
3. Create agent,
mem_agent mem_agnt;
`uvm_component_utils(mem_model_env)
// new - constructor
super.new(name, parent);
endfunction : new
// build_phase
super.build_phase(phase);
endfunction : build_phase
endclass : mem_model_env
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.
A master agent may need to complete all its write and read operations before the run phase should be
allowed to stop.
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.
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();
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
-----
-----
wr_rd_seq.start(mem_agent.sequencer);
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
test class contains the environment, configuration properties, class overrides etc.
initial begin
run_test();
end
Among multiple test cases, particular test case can be selected and execute on two methods,
example: run_test("mem_model_test");
Writing Test :
`uvm_component_utils(mem_model_test)
super.new(name,parent);
endfunction : new
endclass : mem_model_test
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");
seq.start(env.mem_agnt.sequencer);
`uvm_component_utils(mem_model_test)
mem_model_env env;
mem_sequence seq;
super.new(name,parent);
endfunction : new
super.build_phase(phase);
seq = mem_sequence::type_id::create("seq");
endfunction : build_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.
uvm tb top
module tbench_top;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
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
//Utility macro
`uvm_object_utils(mem_seq_item)
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
//Utility macro
`uvm_object_utils(mem_seq_item)
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
//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.
//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.
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
Sequence
`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.
`uvm_sequence_utils(mem_sequence,mem_sequencer)
//Constructor
function new(string name = "mem_sequence");
super.new(name);
endfunction
req = mem_seq_item::type_id::create("req");
wait_for_grant();
req.randomize();
send_request(req);
wait_for_item_done();
endtask
endclass
write sequence,
`uvm_object_utils(mem_wr_seq)
//Constructor
function new(string name = "mem_wr_seq");
super.new(name);
endfunction
endclass
read sequence,
`uvm_object_utils(mem_rd_seq)
//Constructor
function new(string name = "mem_rd_seq");
super.new(name);
endfunction
endclass
Sequencer
`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.
`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;
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
// Virtual Interface
virtual mem_if vif;
`uvm_component_utils(mem_driver)
// 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.
`uvm_component_utils(mem_monitor)
// new - constructor
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : mem_monitor
// Virtual Interface
virtual mem_if vif;
5. Declare seq_item handle, Used as place holder for sampled signal activity,
mem_seq_item trans_collected;
// 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);
// Virtual Interface
virtual mem_if vif;
`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
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,
// build_phase
function void build_phase(uvm_phase phase);
super.build_phase(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
// 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);
// 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.
`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);
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.
// run phase
virtual task run_phase(uvm_phase phase);
--- comparision logic ---
endtask : run_phase
`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
// 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.
`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
mem_agent mem_agnt;
mem_scoreboard mem_scb;
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);
//---------------------------------------
// 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);
//---------------------------------------
// 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
`uvm_component_utils(mem_model_test)
endclass : mem_model_test
mem_model_env env;
mem_sequence seq;
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);
`uvm_component_utils(mem_model_test)
mem_model_env env;
mem_sequence seq;
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
module tbench_top;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
initial begin
run_test();
end
endmodule