Wednesday, 8 November 2017

Timeunit and Timeprecision in SystemVerilog

Within a design element, such as a module, program or interface, the time precision specifies how delay values are rounded before being used in simulation.

The time precision is relative to the time units. If the precision is the same as the time units, then delay values are rounded off to whole numbers (integers). If the precision is one order of magnitude smaller than the time units, then delay values are rounded off to one decimal place.

For example, if the time unit specified is 1ns and the precision is 100ps, then delay values are rounded off to one decimal place (100ps is equivalent to 0.1ns). Thus, a delay of 2.75ns would be rounded off to 2.8ns.

The time unit and time precision can be specified in the following two ways:
— Using the compiler directive `timescale
— Using the keywords timeunit and timeprecision

Here in this post, we will go in details of second way (timeunit and timeprecision).

The time precision may also be declared using an optional second argument to the timeunit keyword using the slash separator. For example:

------------------------------------------------------------------------------
module D (...);
timeunit 100ps;
timeprecision 10fs;
...
endmodule
module E (...);
timeunit 100ps / 10fs; // timeunit with optional second argument
...
endmodule
------------------------------------------------------------------------------

Defining the timeunit and timeprecision constructs within the design element removes the file order dependency problems with compiler directives.

There shall be at most one time-unit and one time-precision for any module, program, package, or interface definition or in any compilation-unit scope. This shall define a time scope.

The timeunit and timeprecision declarations shall precede any other items in the current time scope. i.e. timeunit and timeprecision shall be declared either inside module, program, package, interface or after any item in any compilation-unit scope.

The timeunit and timeprecision declarations can be repeated as later items, but must match the previous declaration within the current time scope.

If a timeunit is not specified within a module, program, package, or interface definition, then the time unit shall be determined using the following rules of precedence:
  1. If the module or interface definition is nested, then the time unit shall be inherited from the enclosing module or interface (programs and packages cannot be nested).
  2. Else, if a `timescale directive has been previously specified (within the compilation unit), then the time unit shall be set to the units of the last `timescale directive.
  3. Else, if the compilation-unit scope specifies a time unit (outside all other declarations), then the time unit shall be set to the time units of the compilation unit.
  4. Else, the default time unit shall be used.


The global time precision, also called the simulation time unit, is the minimum of,
  • All the timeprecision statements,
  • All the time precision arguments to timeunit declarations, and
  • The smallest time precision argument of all the `timescale compiler directives in the design.

Let's go through one example,
------------------------------------------------------------------------------
`timescale 1ps/1ps
module mod1 (output logic [1:0] a);
timeunit 1ns;
timeprecision 1ps;
task print();
a = 0;
#5; // 5 ns = 5000 ps
a ++;
#1.1234; // 5 ns + 1.123 ns = 6.123 ns = 6123 ps
a ++;
endtask : print
endmodule : mod1
module mod2 (output logic [1:0] b);
//timeunit 10ns/10ps;
timeunit 10ns;
timeprecision 10ps;
task print();
b = 0;
#5; // 50 ns = 50000 ps
b ++;
#1.123; // 50 ns + 10.123 ns = 60.123 ns = 60123 ps
// Not 60120 ps, because 'global time precision' is the minimum of
// all the timeprecision statements,
// all the time precision arguments to timeunit declarations, and
// the smallest time precision argument of all the `timescale compiler directives in the design.
// Here smallest precision is 1 ps
b ++;
endtask : print
endmodule : mod2
interface intf();
logic [1:0] a;
logic [1:0] b;
logic [1:0] c = 0;
endinterface : intf
module top();
intf if1 ();
mod1 M1 (if1.a);
mod2 M2 (if1.b);
initial begin
M1.print();
end
initial begin
M2.print();
end
initial begin
#5; // 5 ps
if1.c ++;
#1.123; // 5 + 1 ps = 6 ps
if1.c ++;
end
initial begin
$monitor("%t, a=%b, b=%B, c=%b", $realtime, if1.a, if1.b, if1.c);
end
endmodule : top
//Output:
// 0, a=00, b=00, c=00
// 5, a=00, b=00, c=01
// 6, a=00, b=00, c=10
// 5000, a=01, b=00, c=10
// 6123, a=10, b=00, c=10
// 50000, a=10, b=01, c=10
// 61230, a=10, b=10, c=10
------------------------------------------------------------------------------

Reference:
1) SystemVerilog LRM (1800-2012)

Tuesday, 26 September 2017

Array Manipulation Methods in SystemVerilog with example

SV provides build in methods to facilitate searching from array, array ordering and reduction.

Array locator methods:

Array locator methods operate on any unpacked array, including queues, but their return type is a queue.

Element locator methods (with clause is mandatory):
find() returns all the elements satisfying the given expression.
find_first() returns the first element satisfying the given expression.
find_last() returns the last element satisfying the given expression.

Index locator methods (with clause is mandatory):
find_index() returns the indices of all the elements satisfying the given expression.
find_first_index() returns the index of the first element satisfying the given expression.
find_last_index() returns the index of the last element satisfying the given expression.

Index locator methods return a queue of int for all arrays except associative arrays, which return a queue of the same type as the associative index type.
arrays that specify a wildcard index type shall not be allowed.

If no elements satisfy the given expression or the array is empty (in the case of a queue or dynamic array), then an empty queue is returned.

Index locator methods return a queue with the indices of all items that satisfy the expression.

The optional expression specified by the with clause shall evaluate to a Boolean value.

Let's go through below example,
---------------------------------------------------------------------
module top();
int q[$];
int result[$];
initial begin
q = {9, 1, 8, 3, 4, 4};
$display("1. queue 'q' in decimal : %p", q);
// Find all the elements less than 5
$display("2. find with (item < 5) : %p", q.find with (item < 5) ); // These
$display("3. find() with (item < 5) : %p", q.find() with (item < 5) ); // All
$display("4. find(item) with (item < 5) : %p", q.find(item) with (item < 5) ); // Are
$display("5. find(x) with (x < 5) : %p", q.find(x) with (x < 5) ); // Equivalent
// Find all the elements greater than 3
result = q.find with (item > 3);
$display("6. find with (item > 3) : %p", result);
// Find indices of all items greater than 7
result = q.find_index with (item > 7);
$display("7. find_index with (item > 7) : %p", result);
// Find indices of all items equals to 4
$display("8. find_index with (item == 4) : %p", q.find_index with (item == 4) );
// Find first item grater than 8
result = q.find_first with (item > 8);
$display("9. find_first with (item > 8) : %p", result);
// Find first item grater than 10
$display("10. find_first with (item > 10) : %p", q.find_first with (item > 10) );
// Find index of first item equals to 4
result = q.find_first_index with (item == 4);
$display("11. find_first_index with (item == 4) : %p", result);
// Find last item less than 4
$display("12. find_last with (item < 4) : %p", q.find_last with (item < 4) );
// Find index of last item equals to 4
$display("13. find_last_index with (item == 4) : %p", q.find_last_index with (item == 4));
// find all items equal to their position (index)
//result = q.find with ( item == item.index );
//$display("14. find with ( item == item.index ) : %p", result);
end
endmodule : top
//Output:
// 1. queue 'q' in decimal : '{9, 1, 8, 3, 4, 4}
// 2. find with (item < 5) : '{1, 3, 4, 4}
// 3. find() with (item < 5) : '{1, 3, 4, 4}
// 4. find(item) with (item < 5) : '{1, 3, 4, 4}
// 5. find(x) with (x < 5) : '{1, 3, 4, 4}
// 6. find with (item > 3) : '{9, 8, 4, 4}
// 7. find_index with (item > 7) : '{0, 2}
// 8. find_index with (item == 4) : '{4, 5}
// 9. find_first with (item > 8) : '{9}
// 10. find_first with (item > 10) : '{}
// 11. find_first_index with (item == 4) : '{4}
// 12. find_last with (item < 4) : '{3}
// 13. find_last_index with (item == 4) : '{5}
---------------------------------------------------------------------

Element locator methods (with clause is optional):
min() returns the element with the minimum value or whose expression evaluates to a minimum.
max() returns the element with the maximum value or whose expression evaluates to a maximum.
unique() returns all elements with unique values or whose expression evaluates to a unique value.
unique_index() returns the indices of all elements with unique values or whose expression evaluates to a unique value.

Let's go through below example,
---------------------------------------------------------------------
module top();
int q[$];
int result[$];
initial begin
q = {2, 6, 7, 3, 2, 7, 10, 3, 16};
$display("1. queue 'q' in decimal : %p", q);
result = q.min();
$display("2. Minimum element of queue 'q' : %p", result);
//result.delete();
//result = q.min with (item > 5);
//$display("3. Minimum element of queue 'q' : %p", result);
result = q.max();
$display("4. Maximum element of queue 'q' : %p", result);
//result.delete();
//result = q.max(x) with (x <15);
//$display("5. Maximum element of queue 'q' : %p", result);
result = q.unique();
$display("6. Unique elements of queue 'q' : %p", result);
result = q.unique_index();
$display("7. Index of Unique elements of queue 'q' : %p", result);
end
endmodule : top
//Output:
// 1. queue 'q' in decimal : '{2, 6, 7, 3, 2, 7, 10, 3, 16}
// 2. Minimum element of queue 'q' : '{2}
// 4. Maximum element of queue 'q' : '{16}
// 6. Unique elements of queue 'q' : '{2, 6, 7, 3, 10, 16}
// 7. Index of Unique elements of queue 'q' : '{0, 1, 2, 3, 6, 8}
---------------------------------------------------------------------

Array reduction methods:


Array reduction methods may be applied to any unpacked array of integral values to reduce the array to a single value

sum() returns the sum of all the array elements or, if a with clause is specified, returns the sum of the values yielded by evaluating the expression for each array element.

product() returns the product of all the array elements or, if a with clause is specified, returns the product of the values yielded by evaluating the expression for each array element.

and() returns the bitwise AND ( & ) of all the array elements or, if a with clause is specified, returns the bitwise AND of the values yielded by evaluating the expression for each array element.

or() returns the bitwise OR ( | ) of all the array elements or, if a with clause is specified, returns the bitwise OR of the values yielded by evaluating the expression for each array element.

xor() returns the bitwise XOR ( ^ ) of all the array elements or, if a with clause is specified, returns the bitwise XOR of the values yielded by evaluating the expression for each array element.

Let's go through below example,
---------------------------------------------------------------------
module top();
int unsigned q[$];
int unsigned result;
initial begin
q = {1, 2, 3, 4};
$display("1. queue 'q' in decimal : %p", q);
result = q.sum(); // 1 + 2 + 3 + 4 = 10
$display("2. sum of all elements of queue 'q' : %0d", result);
result = q.product(); // 1 * 2 * 3 * 4 = 24
$display("3. product of all elements of queue 'q' : %0d", result);
$display("");
q = {4, 5, 'hC, 'hF};
$display("4. queue 'q' in decimal : %p", q);
result = q.and(); // 0b0100 & 0b0101 & 0b1100 & 0b1111 = 0b0100 = 4
$display("5. AND of all elements of queue 'q' : %0d", result);
$display("");
q = {4, 5, 8, 0};
$display("6. queue 'q' in decimal : %p", q);
result = q.or(); // 0b0100 | 0b0101 | 0b1000 | 0b0000 = 0b1101 = 13
$display("7. OR of all elements of queue 'q' : %0d", result);
$display("");
q = {1, 2, 3, 4};
$display("8. queue 'q' in decimal : %p", q);
result = q.xor(); // 0b0001 ^ 0b0010 ^ 0b0011 ^ 0b0100 = 0b0100 = 4
$display("9. XOR of all elements of queue 'q' : %0d", result);
result = q.xor() with (item + 4); // 0b0101 ^ 0b0100 ^ 0b0111 ^ 0b1000 = 0b1100 = 12
$display("10. XOR (with clause) of all elements of queue 'q' : %0d", result);
end
endmodule : top
//Output:
// 1. queue 'q' in decimal : '{1, 2, 3, 4}
// 2. sum of all elements of queue 'q' : 10
// 3. product of all elements of queue 'q' : 24
//
// 4. queue 'q' in decimal : '{4, 5, 12, 15}
// 5. AND of all elements of queue 'q' : 4
//
// 6. queue 'q' in decimal : '{4, 5, 8, 0}
// 7. OR of all elements of queue 'q' : 13
//
// 8. queue 'q' in decimal : '{1, 2, 3, 4}
// 9. XOR of all elements of queue 'q' : 4
// 10. XOR (with clause) of all elements of queue 'q' : 12
---------------------------------------------------------------------

Array ordering methods:

Array ordering methods reorder the elements of any unpacked array (fixed or dynamically sized) except for associative arrays.

reverse() reverses the order of the elements in the array. Specifying a with clause shall be a compiler error.

sort() sorts the array in ascending order, optionally using the expression in the with clause.

rsort() sorts the array in descending order, optionally using the expression in the with clause.

shuffle() randomizes the order of the elements in the array. Specifying a with clause shall be a compiler error.

Let's go through below example,
---------------------------------------------------------------------
module top();
int unsigned q[$];
initial begin
q = {9, 1, 8, 3, 4, 4};
$display("1. queue 'q' in decimal : %p", q);
q.reverse();
$display("2. After calling reverse() function, queue 'q' in decimal : %p", q);
q.sort();
$display("3. After calling sort() function, queue 'q' in decimal : %p", q);
// First ODD and then EVEN in assending order
q.sort with (item % 2 == 0);
$display("4. After calling sort() function, queue 'q' in decimal : %p", q);
// First EVEN and then ODD in assending order
q.sort with (item % 2 != 0);
$display("5. After calling sort() function, queue 'q' in decimal : %p", q);
q.rsort();
$display("6. After calling rsort() function, queue 'q' in decimal : %p", q);
q.shuffle();
$display("7. After calling shuffle() function, queue 'q' in decimal : %p", q);
end
endmodule : top
//Output:
// 1. queue 'q' in decimal : '{9, 1, 8, 3, 4, 4}
// 2. After calling reverse() function, queue 'q' in decimal : '{4, 4, 3, 8, 1, 9}
// 3. After calling sort() function, queue 'q' in decimal : '{1, 3, 4, 4, 8, 9}
// 4. After calling sort() function, queue 'q' in decimal : '{1, 3, 9, 4, 4, 8}
// 5. After calling sort() function, queue 'q' in decimal : '{4, 4, 8, 1, 3, 9}
// 6. After calling rsort() function, queue 'q' in decimal : '{9, 8, 4, 4, 3, 1}
// 7. After calling shuffle() function, queue 'q' in decimal : '{4, 4, 8, 1, 9, 3}
---------------------------------------------------------------------


Now let's see couple of practical examples, let's say we want to find number of 1s from bit array or bit queue or we want to find out number of non-zero elements in any array or queue.
---------------------------------------------------------------------
module top();
int unsigned q[$];
int unsigned r[$];
bit bitq[$];
int unsigned result;
initial begin
q = {1, 0, 2, 3, 0, 4};
bitq = {0 ,1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1};
$display("1. queue 'q' in decimal : %p", q);
$display("2. queue 'bitq' : %p", bitq);
result = bitq.sum(item) with (int'(item));
$display("3. Number of 1's in 'bitq' : %0d", result);
r = q.find(x) with (x != 0);
$display("4. Number of non-zero elements in queue 'q' :%0d", r.size());
end
endmodule : top
//Output:
// 1. queue 'q' in decimal : '{1, 0, 2, 3, 0, 4}
// 2. queue 'bitq' : '{'h0, 'h1, 'h1, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h1}
// 3. Number of 1's in 'bitq' : 7
// 4. Number of non-zero elements in queue 'q' :4
---------------------------------------------------------------------

Sunday, 23 July 2017

Advantage of uvm_event over SV event and how to disable uvm_event using callback

The uvm_event#(type T = uvm_object) class is an extension of the abstract uvm_event_base class. The optional parameter T allows the user to define a data type which can be passed during an event trigger. uvm_event class is an abstract wrapper class around SystemVerilog event construct.

It provides additional services such as over traditional SystemVerilog event like,
1) pass data when event is triggered,
A traditional Systemverilog event does not have functionality to pass data when event is triggered. While uvm_event adds this functionality. So, you can pass the transaction class handle when some event is triggered.
By calling trigger task we can trigger the event and optionally give the data which we want to pass using uvm_event.
By calling wait_trigger()/wait_ptrigger() task of uvm_event we can wait for event to be trigger and then by calling get_trigger_data() function we can get data.
or we can directly use only one task wait_trigger_data()/wait_ptrigger_data() of uvm_event to wait for event to be triggered and to get the data.

2) setting callbacks,
We can also add callbacks whenever an event is triggered. This is done by registering a callback class with particular event.

3) maintaining the number of waiters,
We can get the number of processes waiting on the event (using get_num_waiters() function).

4) maintaining the time when event was triggered,
We can get the time that this event was last triggered (using get_trigger_time() function)

Like SystemVerilog event has trigger (@event) and persistent trigger (wait(event.triggered)) mode, uvm_event also has trigger (wait_trigger task) and persistent trigger (wait_ptrigger task).


Let's go through below example and see how we can transfer data using uvm_event and how we can disable uvm_event from triggering using callbacks of uvm_event.

-------------------------------------------------------------------
`include "uvm_macros.svh"
import uvm_pkg::*;
//-----------------------------------------------------------------------------
// Interface
//-----------------------------------------------------------------------------
interface my_interface(input logic clk);
bit valid;
logic [7 : 0] addr;
reg data_reg;
wire data;
assign data = data_reg;
endinterface : my_interface
typedef virtual my_interface my_vif;
//-----------------------------------------------------------------------------
// Sequence Item Class
//-----------------------------------------------------------------------------
class my_seq_item extends uvm_sequence_item;
rand logic [7:0] addr;
rand logic [7:0] data;
int unsigned pkt_id;
constraint addr_range_cn {
addr inside {[10:20]};
}
constraint data_range_cn {
data inside {[100:200]};
}
`uvm_object_utils_begin(my_seq_item)
`uvm_field_int(pkt_id, UVM_ALL_ON| UVM_DEC)
`uvm_field_int(addr, UVM_ALL_ON| UVM_HEX)
`uvm_field_int(data, UVM_ALL_ON| UVM_HEX)
`uvm_object_utils_end
function new(string name="my_seq_item");
super.new(name);
endfunction : new
virtual function string convert2string();
convert2string =
$sformatf("pkt_id:%0d, addr=0x%0h, data=0x%0h", pkt_id, addr, data);
endfunction : convert2string
endclass : my_seq_item
//-----------------------------------------------------------------------
// Sequencer Class
//-----------------------------------------------------------------------
typedef class my_agent;
class my_sequencer extends uvm_sequencer #(my_seq_item);
my_agent parent;
`uvm_component_utils (my_sequencer)
function new (string name="my_sequencer", uvm_component parent=null);
super.new(name, parent);
if(!$cast(this.parent, parent)) begin
`uvm_fatal(get_name(), $sformatf("Casting failed from"))
end
endfunction : new
endclass : my_sequencer
view raw uvm_event_1.sv hosted with ❤ by GitHub
-------------------------------------------------------------------

-------------------------------------------------------------------
//-----------------------------------------------------------------------
// Driver Class
//-----------------------------------------------------------------------
class my_driver extends uvm_driver #(my_seq_item);
my_agent parent;
uvm_event PKT_TX_CMPLT_EV;
uvm_event_pool my_event_pool;
`uvm_component_utils (my_driver)
function new (string name="my_driver", uvm_component parent=null);
super.new(name, parent);
if(!$cast(this.parent, parent)) begin
`uvm_fatal(get_name(), $sformatf("Casting failed from"))
end
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
my_event_pool = uvm_event_pool::get_global_pool();
// Returns the item with the given key.
// If no item exists by that key, a new item is created with that key and returned.
if (!$cast(PKT_TX_CMPLT_EV, my_event_pool.get($sformatf("DRV_EVENT")))) begin
// OR, you can directly use add method
// PKT_TX_CMPLT_EV = my_event_pool.add($sformatf("DRV_EVENT"));
`uvm_fatal(get_name(), $sformatf("casting failed from uvm_object to uvm_event"))
end
endfunction : build_phase
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_name(),
$sformatf("After get_next_item, my_seq_item : %s", req.convert2string()), UVM_LOW)
// Item done is non-blocking
seq_item_port.item_done();
for (int unsigned i=0; i<8; i ++) begin
@(posedge parent.vif.clk);
if (i == 0) begin
parent.vif.valid <= 1'b1;
parent.vif.addr[7:0] <= req.addr[7:0];
end
parent.vif.data_reg <= req.data[i];
end
@(posedge parent.vif.clk);
parent.vif.valid <= 1'b0;
PKT_TX_CMPLT_EV.trigger(req);
end
endtask : run_phase
endclass : my_driver
//-----------------------------------------------------------------------
// Agent Class
//-----------------------------------------------------------------------
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
my_vif vif;
`uvm_component_utils_begin (my_agent)
`uvm_field_object(sqr, UVM_DEFAULT)
`uvm_field_object(drv, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name="my_agent", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(my_vif) :: get(this, "", $sformatf("vif"), this.vif)) begin
`uvm_fatal(get_name(), $sformatf("Failed to get virtual interface"))
end
sqr = my_sequencer :: type_id :: create("sqr", this);
drv = my_driver :: type_id :: create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
//-----------------------------------------------------------------------
// UVM event callback class
//-----------------------------------------------------------------------
class my_event_callback extends uvm_event_callback;
`uvm_object_utils(my_event_callback)
function new(string name="my_event_callback");
super.new(name);
endfunction : new
// Called just before triggering the associated event.
// If this function returns 1, then the event will not trigger
// and the post-trigger callback is not called.
// This provides a way for a callback to prevent the event from triggering.
// e - is the uvm_event#(T=uvm_object) that is being triggered.
// data - is the optional data associated with the event trigger.
virtual function bit pre_trigger (uvm_event e, uvm_object data);
my_seq_item obj;
if(!$cast(obj, data)) begin
`uvm_fatal(get_name(), $sformatf("Casting failed"))
end
else begin
if(obj.pkt_id == 3) begin
`uvm_info(get_name(),
$sformatf("pre_trigger: discarding packet from event:%s, pkt:%s",
e.get_name(), obj.convert2string()), UVM_LOW)
return 1;
end
else begin
`uvm_info(get_name(),
$sformatf("pre_trigger: pkt:%s", obj.convert2string()), UVM_LOW)
return 0;
end
end
endfunction : pre_trigger
// Called after triggering the associated event.
// e - is the uvm_event#(T=uvm_object) that is being triggered.
// data - is the optional data associated with the event trigger.
virtual function void post_trigger (uvm_event e, uvm_object data);
my_seq_item obj;
if(!$cast(obj, data)) begin
`uvm_fatal(get_name(), $sformatf("Casting failed"))
end
else begin
`uvm_info(get_name(),
$sformatf("post_trigger: pkt:%s", obj.convert2string()), UVM_LOW)
end
endfunction : post_trigger
endclass : my_event_callback
view raw uvm_event_2.sv hosted with ❤ by GitHub
-------------------------------------------------------------------
As shown in above code, one uvm_event named PKT_TX_CMPLT_EV is taken in driver.
In build phase of driver we get global handle of event pool using static method get_event_pool of uvm_event_pool class.
Then PKT_TX_CMPLT_EV is added into associative array of uvm_event_pool using get/add method of uvm_event_pool. Note that here PKT_TX_CMPLT_EV event is added in associative array of uvm_event_pool using key (in string format) DRV_EVENT.
In run phase of driver when stimulus is driven, trigger method of uvm_event is called and transaction class is passed in argument of trigger method.

uvm_event also provides facility of callback when event is triggered.
In code my_event_callback (callback for uvm_event) class which extended from uvm_event_callback.
uvm_event_callback provides two hookups, 1) pre_trigger, 2) post_trigger.

pre_trigger:
Called just before triggering the associated event. If this function returns 1, then the event will not trigger and the post-trigger callback is not called.

post_trigger:
Called after triggering the associated event.

-------------------------------------------------------------------
//-----------------------------------------------------------------------
// Sequence Class
//-----------------------------------------------------------------------
class my_seq extends uvm_sequence #(my_seq_item);
uvm_event my_event;
int unsigned pkt_id;
my_event_callback my_event_cb;
`uvm_object_utils (my_seq)
`uvm_declare_p_sequencer(my_sequencer)
function new(string name="my_seq");
super.new(name);
endfunction : new
task get_event(input string event_key,
output uvm_event e);
uvm_event_pool my_event_pool;
my_event_pool = uvm_event_pool::get_global_pool();
e = my_event_pool.get(event_key);
endtask : get_event
task get_event_data(ref uvm_event e, ref my_seq_item my_item);
uvm_object item;
// This method calls wait_ptrigger followed by get_trigger_data (Recommanded)
`uvm_info(get_name(), $sformatf("before wait_ptrigger_data"), UVM_LOW)
e.wait_ptrigger_data(item);
`uvm_info(get_name(), $sformatf("after wait_ptrigger_data"), UVM_LOW)
if (item == null) begin
`uvm_error(get_name(),
$sformatf("Got NULL item from event"))
end
else begin
if ($cast(my_item, item)) begin
`uvm_info(get_name(),
$sformatf("Got the item from event %s", my_item.convert2string()), UVM_LOW)
end
else begin
`uvm_error(get_name(),
$sformatf("Casting failed from %s to %s", item.get_type_name(), my_item.get_type_name()))
end
end
endtask : get_event_data
task send_tr(input int unsigned pkt_id = 0);
my_seq_item req;
`uvm_create_on(req, p_sequencer)
if(!req.randomize()) begin
`uvm_fatal(get_name(), $sformatf("Randomization failed"))
end
req.pkt_id = pkt_id;
`uvm_info (get_name(),
$sformatf("After randomizating, my_seq_item : %s",
req.convert2string()), UVM_LOW)
// Sequence will comout from `uvm_send as item_done in driver is non blocking
`uvm_send(req)
endtask : send_tr
task body ();
my_seq_item my_data;
pkt_id ++;
send_tr(pkt_id);
get_event("DRV_EVENT", my_event);
get_event_data(my_event, my_data);
// do manipulation on my_data
my_data = null;
my_event_cb = my_event_callback :: type_id :: create ("my_event_cb");
my_event.add_callback(my_event_cb);
`uvm_info(get_name(),
$sformatf("%s event_callback is added for %s event",
my_event_cb.get_name(), my_event.get_name()), UVM_LOW)
#200;
pkt_id ++;
send_tr(pkt_id);
get_event("DRV_EVENT", my_event);
get_event_data(my_event, my_data);
// do manipulation on my_data
my_data = null;
#200;
pkt_id ++;
send_tr(pkt_id);
begin
fork
begin
get_event("DRV_EVENT", my_event);
get_event_data(my_event, my_data);
my_data = null;
end
begin
repeat (50) begin
@(posedge p_sequencer.parent.vif.clk);
end
`uvm_info(get_name(), $sformatf("event is not triggered"), UVM_LOW)
end
join_any
disable fork;
end
my_event.delete_callback(my_event_cb);
`uvm_info(get_name(),
$sformatf("%s event_callback is deleted for %s event",
my_event_cb.get_name(), my_event.get_name()), UVM_LOW)
#200;
pkt_id ++;
send_tr(pkt_id);
get_event("DRV_EVENT", my_event);
get_event_data(my_event, my_data);
endtask : body
endclass : my_seq
//-----------------------------------------------------------------------
// Test Class
//-----------------------------------------------------------------------
class my_test extends uvm_test;
my_agent agent;
my_vif vif;
`uvm_component_utils_begin (my_test)
`uvm_field_object(agent, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name="my_test", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(my_vif) :: get(this, "", $sformatf("vif"), this.vif)) begin
`uvm_fatal(get_name(), $sformatf("Failed to get virtual interface"))
end
agent = my_agent::type_id::create("agent", this);
uvm_config_db#(my_vif) :: set(this, "agent", $sformatf("vif"), this.vif);
endfunction : build_phase
task run_phase(uvm_phase phase);
my_seq seq;
phase.raise_objection(this);
seq = my_seq::type_id::create ("seq");
//seq.set_starting_phase(phase);
#150;
seq.start(agent.sqr);
#150;
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
// Top module
module top();
bit clk;
my_interface intf(clk);
initial begin
uvm_config_db#(my_vif) :: set(uvm_root::get(), "uvm_test_top", "vif", intf);
forever begin
#5 clk = ~clk;
end
end
initial begin
run_test("my_test");
end
endmodule : top
view raw uvm_event_3.sv hosted with ❤ by GitHub
-------------------------------------------------------------------
As shown in above code, in sequence, event callback is registered with associated event using add_callback method of uvm_event and also deleted using delete_callback method of uvm_event

-------------------------------------------------------------------
Output:
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO uvm_event_3.sv(55) @ 150: uvm_test_top.agent.sqr@@seq [seq] After randomizating, my_seq_item : pkt_id:1, addr=0x13, data=0x8c
UVM_INFO uvm_event_2.sv(33) @ 150: uvm_test_top.agent.drv [drv] After get_next_item, my_seq_item : pkt_id:1, addr=0x13, data=0x8c
UVM_INFO uvm_event_3.sv(26) @ 150: uvm_test_top.agent.sqr@@seq [seq] before wait_ptrigger_data
UVM_INFO uvm_event_3.sv(28) @ 235: uvm_test_top.agent.sqr@@seq [seq] after wait_ptrigger_data
UVM_INFO uvm_event_3.sv(37) @ 235: uvm_test_top.agent.sqr@@seq [seq] Got the item from event pkt_id:1, addr=0x13, data=0x8c
UVM_INFO uvm_event_3.sv(74) @ 235: uvm_test_top.agent.sqr@@seq [seq] my_event_cb event_callback is added for DRV_EVENT event
UVM_INFO uvm_event_3.sv(55) @ 435: uvm_test_top.agent.sqr@@seq [seq] After randomizating, my_seq_item : pkt_id:2, addr=0xd, data=0x6e
UVM_INFO uvm_event_2.sv(33) @ 435: uvm_test_top.agent.drv [drv] After get_next_item, my_seq_item : pkt_id:2, addr=0xd, data=0x6e
UVM_INFO uvm_event_3.sv(26) @ 435: uvm_test_top.agent.sqr@@seq [seq] before wait_ptrigger_data
UVM_INFO uvm_event_2.sv(111) @ 525: reporter [my_event_cb] pre_trigger: pkt:pkt_id:2, addr=0xd, data=0x6e
UVM_INFO uvm_event_2.sv(127) @ 525: reporter [my_event_cb] post_trigger: pkt:pkt_id:2, addr=0xd, data=0x6e
UVM_INFO uvm_event_3.sv(28) @ 525: uvm_test_top.agent.sqr@@seq [seq] after wait_ptrigger_data
UVM_INFO uvm_event_3.sv(37) @ 525: uvm_test_top.agent.sqr@@seq [seq] Got the item from event pkt_id:2, addr=0xd, data=0x6e
UVM_INFO uvm_event_3.sv(55) @ 725: uvm_test_top.agent.sqr@@seq [seq] After randomizating, my_seq_item : pkt_id:3, addr=0x12, data=0xa3
UVM_INFO uvm_event_2.sv(33) @ 725: uvm_test_top.agent.drv [drv] After get_next_item, my_seq_item : pkt_id:3, addr=0x12, data=0xa3
UVM_INFO uvm_event_3.sv(26) @ 725: uvm_test_top.agent.sqr@@seq [seq] before wait_ptrigger_data
UVM_INFO uvm_event_2.sv(106) @ 815: reporter [my_event_cb] pre_trigger: discarding packet from event:DRV_EVENT, pkt:pkt_id:3, addr=0x12, data=0xa3
UVM_INFO uvm_event_3.sv(98) @ 1225: uvm_test_top.agent.sqr@@seq [seq] event is not triggered
UVM_INFO uvm_event_3.sv(107) @ 1225: uvm_test_top.agent.sqr@@seq [seq] my_event_cb event_callback is deleted for DRV_EVENT event
UVM_INFO uvm_event_3.sv(55) @ 1425: uvm_test_top.agent.sqr@@seq [seq] After randomizating, my_seq_item : pkt_id:4, addr=0x14, data=0xa5
UVM_INFO uvm_event_2.sv(33) @ 1425: uvm_test_top.agent.drv [drv] After get_next_item, my_seq_item : pkt_id:4, addr=0x14, data=0xa5
UVM_INFO uvm_event_3.sv(26) @ 1425: uvm_test_top.agent.sqr@@seq [seq] before wait_ptrigger_data
UVM_INFO uvm_event_3.sv(28) @ 1515: uvm_test_top.agent.sqr@@seq [seq] after wait_ptrigger_data
UVM_INFO uvm_event_3.sv(37) @ 1515: uvm_test_top.agent.sqr@@seq [seq] Got the item from event pkt_id:4, addr=0x14, data=0xa5
UVM_INFO uvm_objection.svh(1271) @ 1665: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO uvm_report_server.svh(847) @ 1665: reporter [UVM/REPORT/SERVER]
-------------------------------------------------------------------


FAQ:
Through uvm_event we can pass data(transaction class) when event is triggered, then why do we need TLM/Analysis ports in UVM?
Ans:
If event is triggered again before receiver gets the data then data will be overwritten.

Reset Testing using Phase Jump in UVM

Reset testing is a crucial element of functional sign-off for any chip. The architectural components of the entire verification environment need to be correctly synchronized to be made aware of the reset condition. Scoreboards, drivers and monitors need to be tidied up, and the complex stimulus generation needs to be killed gracefully.

As we know, in UVM, there are twelve phases parallel to run_phase:
  1. pre_reset_phase(), reset_phase(), post_reset_phase(): Phases involved in reset activity.
  2. pre_configure_phase(), configure_phase(), post_configure_phase(): Phases involved in configuring DUT.
  3. pre_main_phase(), main_phase(), post_main_phase(): Phases involved in driving main stimulus to the DUT.
  4. pre_shutdown_phase(), shutdown_phase and post_shutdown_phase(): Phases involved in settling down the DUT after driving main stimulus.

Using these phases instead of using only run_phase, we can achieve synchronization between all components of verification environment also easily test reset functionality.

In reset testing, user drives random sequence to the DUT and in between data transmission, reset is applied followed by driving restart sequence. We will see how the reset functionality could be easily tested using phases parallel to run_phase and phase jump feature of UVM.

Let’s go through complete example to understand how it is achieved using UVM phases and Phase jump feature.
----------------------------------------------------------------------
`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg :: *;
`define ADDR_WIDTH_IN_BITS 8
`define DATA_WIDTH_IN_BITS 8
//-----------------------------------------------------------------------------
// Interface
//-----------------------------------------------------------------------------
interface my_interface(input logic clk,
input logic rstn /* Active Low Reset */);
bit valid;
logic [`ADDR_WIDTH_IN_BITS - 1 : 0] start_addr;
reg [`DATA_WIDTH_IN_BITS - 1 : 0] data_reg;
wire [`DATA_WIDTH_IN_BITS - 1 : 0] data;
int unsigned length_in_bytes;
assign data = data_reg;
endinterface : my_interface
typedef virtual my_interface my_vif;
typedef class my_agent;
//-----------------------------------------------------------------------------
// Agent Configuration Class
//-----------------------------------------------------------------------------
class my_agent_cfg extends uvm_object;
// virtual interface
my_vif vif;
// The length of time, in ps, that reset will stay active
int unsigned reset_time_ps = 10;
// Minimum length of payload data
int unsigned min_payload_length = 5;
// Maximum length of payload data
int unsigned max_payload_length = 100;
uvm_active_passive_enum is_active = UVM_ACTIVE;
`uvm_object_utils_begin(my_agent_cfg)
`uvm_field_enum(uvm_active_passive_enum, is_active, UVM_DEFAULT)
`uvm_field_int(reset_time_ps, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(min_payload_length, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(max_payload_length, UVM_DEFAULT | UVM_DEC)
`uvm_object_utils_end
function new(string name="my_agent_cfg");
super.new(name);
endfunction : new
function void is_valid();
if (max_payload_length < min_payload_length) begin
`uvm_error(get_name(),
$sformatf("Value of max_payload_length is shall be greater or equal to value of min_payload_length, configured values of max_payload_length:%0d, min_payload_length:%0d",
max_payload_length, min_payload_length))
end
if (reset_time_ps <= 0) begin
`uvm_error(get_name(), $sformatf("reset_time_ps shall be greater than 0"))
end
endfunction : is_valid
endclass : my_agent_cfg
//-----------------------------------------------------------------------------
// Sequence Item Class
//-----------------------------------------------------------------------------
class my_seq_item extends uvm_sequence_item;
// Random varialbes
rand logic [`ADDR_WIDTH_IN_BITS - 1 : 0] start_addr;
rand logic [`DATA_WIDTH_IN_BITS - 1 : 0] data[];
rand int unsigned payload_length;
// Non random variables
my_agent_cfg cfg;
`uvm_object_utils_begin(my_seq_item)
`uvm_field_int (start_addr, UVM_DEFAULT | UVM_HEX)
`uvm_field_int (payload_length, UVM_DEFAULT | UVM_DEC)
`uvm_field_array_int (data, UVM_DEFAULT | UVM_HEX)
`uvm_object_utils_end
constraint length_cn {
payload_length inside {[cfg.min_payload_length : cfg.max_payload_length]};
data.size == payload_length;
}
constraint order_cn {
solve payload_length before data;
}
function new(string name="my_seq_item");
super.new(name);
endfunction : new
function string convert2string();
convert2string = $sformatf("start_addr:%0h, payload_length:%0d, data[0]:%0h, data[%0d]:%0h",
start_addr, payload_length, data[0], payload_length-1, data[payload_length-1]);
endfunction : convert2string
endclass : my_seq_item
view raw phase_jump_1.sv hosted with ❤ by GitHub
----------------------------------------------------------------------
----------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sequencer Class
//-----------------------------------------------------------------------------
class my_sequencer extends uvm_sequencer#(my_seq_item);
my_agent parent;
`uvm_component_utils(my_sequencer)
function new(string name="my_sequencer", uvm_component parent=null);
super.new(name, parent);
if (parent == null) begin
`uvm_fatal(get_name(),
$sformatf("NULL handle is provided in parent argument of constructor"))
end
else begin
if (!$cast(this.parent, parent)) begin
`uvm_fatal(get_name(),
$sformatf("Casting Failed, provide parent of valid type"))
end
end
endfunction : new
endclass : my_sequencer
//-----------------------------------------------------------------------------
// Driver Class
//-----------------------------------------------------------------------------
class my_driver extends uvm_driver#(my_seq_item);
my_agent parent;
event reset_driver;
`uvm_component_utils_begin(my_driver)
`uvm_component_utils_end
function new(string name="my_driver", uvm_component parent=null);
super.new(name, parent);
if (parent == null) begin
`uvm_fatal(get_name(),
$sformatf("NULL handle is provided in parent argument of constructor"))
end
else begin
if (!$cast(this.parent, parent)) begin
`uvm_fatal(get_name(),
$sformatf("Casting Failed, provide parent of valid type"))
end
end
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
endfunction : build_phase
task pre_reset_phase(uvm_phase phase);
// De-assert valid signal after enterring into pre_reset_phase
parent.cfg.vif.valid <= 1'b0;
endtask : pre_reset_phase
// Wait for Reset to assert and
// Do something you want to do after reset is asserted
task reset_phase(uvm_phase phase);
phase.raise_objection(this);
wait (parent.cfg.vif.rstn == 1'b0);
`uvm_info(get_name(),
$sformatf("after waiting for rstn to be asserted"), UVM_LOW)
phase.drop_objection(this);
endtask : reset_phase
// Wait for Reset to de-assert
task post_reset_phase(uvm_phase phase);
phase.raise_objection(this);
wait (parent.cfg.vif.rstn == 1'b1);
`uvm_info(get_name(),
$sformatf("after waiting for rstn to be deasserted"), UVM_LOW)
phase.drop_objection(this);
endtask : post_reset_phase
// Drive stimulus on interface and in parallel
// wait for reset to be asserted
task main_phase(uvm_phase phase);
`uvm_info(get_name(), $sformatf("enter in main_phase"), UVM_LOW)
forever begin
fork
begin
drive();
end
begin
wait (reset_driver.triggered);
`uvm_info(get_name(),
$sformatf("after wait for reset_driver event to be triggered"), UVM_LOW)
end
join_any
disable fork;
end
endtask : main_phase
task drive();
my_seq_item tr;
`uvm_info(get_name(), $sformatf("Before get_next_item"), UVM_LOW)
seq_item_port.get_next_item(tr);
`uvm_info(get_name(),
$sformatf("After get_next_item tr:\n%s", tr.convert2string()), UVM_LOW)
@(posedge parent.cfg.vif.clk);
parent.cfg.vif.valid <= 1'b1;
parent.cfg.vif.length_in_bytes <= tr.payload_length;
parent.cfg.vif.start_addr <= tr.start_addr;
for (int unsigned i=0; i<tr.payload_length; i ++) begin
parent.cfg.vif.data_reg <= tr.data[i];
@(posedge parent.cfg.vif.clk);
if (i == tr.payload_length - 1) begin
parent.cfg.vif.valid <= 1'b0;
end
end
seq_item_port.item_done(tr);
`uvm_info(get_name(), $sformatf("item_done is called"), UVM_LOW)
endtask : drive
endclass : my_driver
//-----------------------------------------------------------------------------
// Monitor Class
//-----------------------------------------------------------------------------
class my_monitor extends uvm_monitor;
my_agent parent;
`uvm_component_utils_begin(my_monitor)
`uvm_component_utils_end
function new(string name="my_monitor", uvm_component parent=null);
super.new(name, parent);
if (parent == null) begin
`uvm_fatal(get_name(), $sformatf("NULL handle is provided in parent argument of constructor"))
end
else begin
if (!$cast(this.parent, parent)) begin
`uvm_fatal(get_name(), $sformatf("Casting Failed, provide parent of valid type"))
end
end
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
endfunction : build_phase
task pre_reset_phase(uvm_phase phase);
endtask : pre_reset_phase
// Wait for reset to de-assert
task reset_phase(uvm_phase phase);
@(posedge parent.cfg.vif.rstn);
`uvm_info(get_name(), $sformatf("rstn deassertion detected"), UVM_LOW)
endtask : reset_phase
// Sample interface signals and form packet and
// in parallel wait for reset to be asserted
task main_phase(uvm_phase phase);
my_seq_item tr;
forever begin
fork
begin
receive(tr);
end
begin
@(negedge parent.cfg.vif.rstn);
`uvm_info(get_name(), $sformatf("rstn is asserted during reception of data"), UVM_LOW)
`uvm_info(get_name(), $sformatf(" tr:\n%s", tr.convert2string()), UVM_LOW)
end
join_any
disable fork;
// put tr into analysis port
tr = null;
end
endtask : main_phase
task receive(ref my_seq_item tr);
@(negedge parent.cfg.vif.clk);
if (parent.cfg.vif.valid == 1'b1) begin
`uvm_info(get_name(), $sformatf("valid is detected"), UVM_LOW)
tr = new("tr");
tr.payload_length = parent.cfg.vif.length_in_bytes;
tr.start_addr = parent.cfg.vif.start_addr;
tr.data = new[tr.payload_length];
for (int unsigned i=0; i<tr.payload_length; i ++) begin
tr.data[i] = parent.cfg.vif.data;
if (i != tr.payload_length - 1) begin
@(negedge parent.cfg.vif.clk);
end
end
`uvm_info(get_name(), $sformatf("Complete tr:\n%s", tr.convert2string()), UVM_LOW)
end
endtask : receive
endclass : my_monitor
//-----------------------------------------------------------------------------
// Agent Class
//-----------------------------------------------------------------------------
class my_agent extends uvm_agent;
my_agent_cfg cfg;
my_sequencer sqr;
my_driver drv;
my_monitor mon;
`uvm_component_utils_begin(my_agent)
`uvm_field_object(cfg, UVM_DEFAULT)
`uvm_field_object(sqr, UVM_DEFAULT)
`uvm_field_object(drv, UVM_DEFAULT)
`uvm_field_object(mon, UVM_DEFAULT)
`uvm_component_utils_end
function new(string name="my_agent", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(my_agent_cfg) :: get(this, "", "cfg", this.cfg) &&
this.cfg != null) begin
`uvm_fatal(get_name(), $sformatf("configuration object of type:%s is not set for this instance", this.cfg.get_type()))
end
if (!uvm_config_db#(my_vif) :: get(this, "", "vif", this.cfg.vif)) begin
`uvm_fatal(get_name(), $sformatf("Interface is not passed to agent"))
end
cfg.is_valid();
if (cfg.is_active == UVM_ACTIVE) begin
sqr = my_sequencer :: type_id :: create ("sqr", this);
drv = my_driver :: type_id :: create ("drv", this);
end
mon = my_monitor :: type_id :: create ("mon", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
if (cfg.is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
end
endfunction : connect_phase
task pre_reset_phase(uvm_phase phase);
if(cfg.is_active == UVM_ACTIVE) begin
// Tells the sequencer to kill all sequences and child sequences currently
// operating on the sequencer, and remove all requests, locks and responses
// that are currently queued.
// This essentially resets the sequencer to an idle state.
sqr.stop_sequences();
// Indicates Driver that reset is asserted
->drv.reset_driver;
`uvm_info(get_name(), $sformatf("reset_driver event is triggered"), UVM_LOW)
end
endtask : pre_reset_phase
endclass : my_agent
view raw phase_jump_2.sv hosted with ❤ by GitHub
----------------------------------------------------------------------
As shown in above code,
Driver is waiting for Reset to be asserted (in reset_phase) by raising objection and then perform action which user want on assertion of Reset signal and at last drop the objection and move to post_reset_phase. In post_reset_phase driver is waiting for Reset to de-assert and then move to main_phase. In main_phase driver drives stimulus on interface and in parallel to that wait for indication from agent about assertion of reset.

Components such as monitors that attach to signaling interfaces should be designed to be phase independent because they are intended to mimic other real devices in the system. These components should watch the reset signal associated with their interface and reset themselves accordingly.

You may find that the driver, the sequencer, and their currently running sequences will squawk with errors if they are not synchronized properly. UVM requires that the sequencer first stop its sequences and then the driver must be certain to not call item_done on any outstanding sequences.  However, the order that a simulator executes threads in the various components is indeterminate. To synchronize these operations, the containing agent has a pre_reset_phase such as the above.

----------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Sequence Class
//-----------------------------------------------------------------------------
class my_base_sequence extends uvm_sequence#(my_seq_item);
int unsigned payload_length;
`uvm_object_utils(my_base_sequence)
`uvm_declare_p_sequencer(my_sequencer)
function new(string name="my_base_sequence");
super.new(name);
endfunction : new
task body();
endtask : body
endclass : my_base_sequence
class my_sequence extends my_base_sequence;
`uvm_object_utils(my_sequence)
function new(string name="my_sequence");
super.new(name);
endfunction : new
task body();
my_seq_item req;
my_seq_item rsp;
`uvm_create(req)
req.cfg = p_sequencer.parent.cfg;
if (!req.randomize() with {payload_length == local::payload_length;}) begin
`uvm_fatal(get_name(), $sformatf("Randomization failed"))
end
else begin
`uvm_info(get_name(),
$sformatf("After randomization tr in seq:\n%s", req.convert2string()), UVM_LOW)
end
`uvm_send(req)
get_response(rsp);
`uvm_info(get_name(),
$sformatf("Got response from driver"), UVM_LOW)
endtask : body
endclass : my_sequence
//-----------------------------------------------------------------------------
// Test Class
//-----------------------------------------------------------------------------
class my_test extends uvm_test;
my_vif act_vif;
my_vif psv_vif;
my_agent act_agent;
my_agent psv_agent;
my_agent_cfg act_agent_cfg;
my_agent_cfg psv_agent_cfg;
// The number of times the test has run so far
int unsigned run_count;
`uvm_component_utils_begin(my_test)
`uvm_field_int(run_count, UVM_DEFAULT | UVM_DEC)
`uvm_field_object(act_agent, UVM_DEFAULT)
`uvm_field_object(act_agent_cfg, UVM_DEFAULT)
`uvm_field_object(psv_agent, UVM_DEFAULT)
`uvm_field_object(psv_agent_cfg, UVM_DEFAULT)
`uvm_component_utils_end
function new(string name="my_test", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(my_vif) :: get(this, "", $sformatf("act_vif"), act_vif)) begin
`uvm_fatal(get_name(), $sformatf("act_vif is not set for this class from top module"))
end
if (!uvm_config_db#(my_vif) :: get(this, "", $sformatf("psv_vif"), psv_vif)) begin
`uvm_fatal(get_name(), $sformatf("psv_vif is not set for this class from top module"))
end
act_agent = my_agent :: type_id :: create ($sformatf("act_agent"), this);
psv_agent = my_agent :: type_id :: create ($sformatf("psv_agent"), this);
act_agent_cfg = my_agent_cfg :: type_id :: create ($sformatf("act_agent_cfg"), this);
act_agent_cfg.is_active = UVM_ACTIVE;
psv_agent_cfg = my_agent_cfg :: type_id :: create ($sformatf("psv_agent_cfg"), this);
psv_agent_cfg.is_active = UVM_PASSIVE;
uvm_config_db#(my_vif) :: set(this, $sformatf("act_agent"), "vif", act_vif);
uvm_config_db#(my_vif) :: set(this, $sformatf("psv_agent"), "vif", psv_vif);
uvm_config_db#(my_agent_cfg) :: set(this, $sformatf("act_agent"), "cfg", act_agent_cfg);
uvm_config_db#(my_agent_cfg) :: set(this, $sformatf("psv_agent"), "cfg", psv_agent_cfg);
endfunction : build_phase
task main_phase (uvm_phase phase);
phase.raise_objection(this);
if (run_count == 0) begin
fork
begin
// Run sequence
my_sequence seq;
seq = my_sequence :: type_id :: create ("seq");
seq.payload_length = 50;
seq.start(act_agent.sqr);
`uvm_info(get_name(), $sformatf("finished first seq"), UVM_LOW)
#1000;
`uvm_info(get_name(),
$sformatf("finished thread parallel to waiting for rstn"), UVM_LOW)
end
begin
@(negedge act_vif.rstn);
`uvm_info(get_name(), $sformatf("rstn assertion detected"), UVM_LOW)
end
join_any
disable fork;
end
else if (run_count == 1) begin
// Run sequence
my_sequence seq;
seq = my_sequence :: type_id :: create ("seq");
seq.payload_length = 40;
seq.start(act_agent.sqr);
#100;
end
if (run_count == 0) begin
phase.get_objection().set_report_severity_id_override(UVM_WARNING, "OBJTN_CLEAR", UVM_INFO);
phase.jump(uvm_pre_reset_phase::get());
end
else begin
phase.drop_objection(this);
end
run_count ++;
endtask : main_phase
endclass : my_test
//-----------------------------------------------------------------------------
// TB TOP module
//-----------------------------------------------------------------------------
module top();
bit clk;
logic rstn;
my_interface act_if (clk, rstn);
my_interface psv_if (clk, rstn);
initial begin
run_test("my_test");
end
assign psv_if.length_in_bytes = act_if.length_in_bytes;
assign psv_if.start_addr = act_if.start_addr;
assign psv_if.data_reg = act_if.data_reg;
assign psv_if.valid = act_if.valid;
initial begin
uvm_config_db#(virtual my_interface) :: set(uvm_root::get(), "uvm_test_top", $sformatf("act_vif"), act_if);
uvm_config_db#(my_vif) :: set(uvm_root::get(), "uvm_test_top", $sformatf("psv_vif"), psv_if);
end
initial begin
forever begin
#5 clk = !clk;
end
end
initial begin
#7 rstn = 1'b1;
# 30 rstn = 1'b0;
#100 rstn = 1'b1;
#270 rstn = 1'b0;
#70 rstn = 1'b1;
#1000 $finish;
end
endmodule : top
view raw phase_jump_3.sv hosted with ❤ by GitHub
----------------------------------------------------------------------
When test enters main_phase initially first time at that time run_count is 0, so on assertion of Reset test will do phase.jump method and move to pre_reset_phase from main_phase.
When test enters main_phase second time at that time run_count is 1, so at that time it will not do phase jumping.

Note: It is not good to use a phase jumping feature in case any of the components of testbench don’t use the sub-phases of UVM.

Reference:

Saturday, 22 July 2017

Extend run_phase using set_drain_time and all_dropped callback after all objection drops

It is more often that two or more components in verification environment are not in sync with each other. And there may be a case where driver/transmitter finish it's job first and call item_done. After item_done is called from driver and if there is no action pending in sequence then start method of sequence finishes and objection is also dropped.
But we want that simulation (more precisely we can say run_phase) should extend to some more time after all objection are dropped, so that other components which are late with respect to driver can finish it's job. We can achieve this by using set_drain_time in UVM.

Let's go through example and see how we can achieve this using set_drain_time in UVM.
--------------------------------------------------------------------
`timescale 1ns/1ns
`include "uvm.sv"
import uvm_pkg::*;
`include "uvm_macros.svh"
// In this example, a drain time of 10 is set on the component.
// The component then forks 4 processes which consume different
// amounts of time. When the last process is done (time 85),
// the drain time takes effect. The test is completed at time 95.
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
function new (string name="my_agent", uvm_component parent=null);
super.new(name, parent);
endfunction : new
virtual task run_phase(uvm_phase phase);
// Run a bunch of processes in parallel
fork
doit(phase, 85);
doit(phase, 55);
join
endtask : run_phase
// A simple task that consumes some time.
task doit (uvm_phase phase, time delay);
static int s_inst = 0;
int inst = s_inst++;
//Raise an objection before starting the activity
phase.raise_objection(this);
uvm_report_info(get_name(), $sformatf("Starting doit (%0d) with delay %0t", inst, delay), UVM_NONE);
#delay;
uvm_report_info(get_name(), $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
phase.drop_objection(this);
endtask : doit
endclass : my_agent
class my_env extends uvm_test;
my_agent agent;
`uvm_component_utils(my_env)
function new (string name="my_env", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent = my_agent :: type_id :: create ("agent", this);
endfunction : build_phase
virtual task run_phase(uvm_phase phase);
// Run a bunch of processes in parallel
fork
doit(phase, 35);
doit(phase, 60);
join
endtask : run_phase
// A simple task that consumes some time.
task doit (uvm_phase phase, time delay);
static int s_inst = 0;
int inst = s_inst++;
//Raise an objection before starting the activity
phase.raise_objection(this);
uvm_report_info(get_name(), $sformatf("Starting doit (%0d) with delay %0t", inst, delay), UVM_NONE);
#delay;
uvm_report_info(get_name(), $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
phase.drop_objection(this);
endtask : doit
endclass : my_env
class my_test extends uvm_test;
my_env env;
`uvm_component_utils(my_test)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
env = my_env :: type_id::create("env", this);
endfunction : build_phase
virtual task run_phase(uvm_phase phase);
// Set a drain time on the objection if needed
`uvm_info("drain", "Setting drain time of 10", UVM_NONE)
// uvm-1.1d or earlier (depricated in uvm-1.2
//uvm_test_done.set_drain_time(this,10);
// uvm-1.2
phase.phase_done.set_drain_time(this,10);
// Run a bunch of processes in parallel
fork
doit(phase, 25);
doit(phase, 50);
join
endtask : run_phase
// A simple task that consumes some time.
task doit (uvm_phase phase, time delay);
static int s_inst = 0;
int inst = s_inst++;
//Raise an objection before starting the activity
phase.raise_objection(
this, // uvm_object obj
"", // string description=""
1 // int count=1, default value = 1 which increment objection count by 1
);
uvm_report_info(get_name(), $sformatf("Starting doit (%0d) with delay %0t", inst, delay), UVM_NONE);
#delay;
uvm_report_info(get_name(), $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
phase.drop_objection(
this, // uvm_object obj
"", // string description=""
1 // int count=1, default value = 1 which decrement objection count by 1
);
endtask : doit
endclass : my_test
module top;
initial begin
run_test("my_test");
end
endmodule : top
// Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO top.sv(98) @ 0: uvm_test_top [drain] Setting drain time of 10
// UVM_INFO @ 0: uvm_test_top [uvm_test_top] Starting doit (0) with delay 25
// UVM_INFO @ 0: uvm_test_top [uvm_test_top] Starting doit (1) with delay 50
// UVM_INFO @ 0: uvm_test_top.env [env] Starting doit (0) with delay 35
// UVM_INFO @ 0: uvm_test_top.env [env] Starting doit (1) with delay 60
// UVM_INFO @ 0: uvm_test_top.env.agent [agent] Starting doit (0) with delay 85
// UVM_INFO @ 0: uvm_test_top.env.agent [agent] Starting doit (1) with delay 55
// UVM_INFO @ 25: uvm_test_top [uvm_test_top] Ending doit (0)
// UVM_INFO @ 35: uvm_test_top.env [env] Ending doit (0)
// UVM_INFO @ 50: uvm_test_top [uvm_test_top] Ending doit (1)
// UVM_INFO @ 55: uvm_test_top.env.agent [agent] Ending doit (1)
// UVM_INFO @ 60: uvm_test_top.env [env] Ending doit (1)
// UVM_INFO @ 85: uvm_test_top.env.agent [agent] Ending doit (0)
// UVM_INFO /home/shahs/gotcha/uvm/uvm-1.2/src/base/uvm_objection.svh(1271) @ 95: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
// UVM_INFO /home/shahs/gotcha/uvm/uvm-1.2/src/base/uvm_report_server.svh(847) @ 95: reporter [UVM/REPORT/SERVER]

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


UVM also provides callback hookups when every objection raises and drops and when all objection drops.
Let's go through example and see how it works,

--------------------------------------------------------------------
`timescale 1ns/1ns
`include "uvm.sv"
import uvm_pkg::*;
`include "uvm_macros.svh"
// In this example, a drain time of 10 is set on the component.
// The component then forks 4 processes which consume different
// amounts of time. When the last process is done (time 85),
// the drain time takes effect. The test is completed at time 95.
// After drain time finished, all_dopped callback takes effect
// which takes 50 timeunit and it is completed at time (at time 145)
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
function new (string name="my_agent", uvm_component parent=null);
super.new(name, parent);
endfunction : new
virtual task run_phase(uvm_phase phase);
// Run a bunch of processes in parallel
fork
doit(phase, 85);
doit(phase, 55);
join
endtask : run_phase
// A simple task that consumes some time.
task doit (uvm_phase phase, time delay);
static int s_inst = 0;
int inst = s_inst++;
//Raise an objection before starting the activity
phase.raise_objection(this);
uvm_report_info(get_name(), $sformatf("Starting doit (%0d) with delay %0t", inst, delay), UVM_NONE);
#delay;
uvm_report_info(get_name(), $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
phase.drop_objection(this);
endtask : doit
endclass : my_agent
class my_env extends uvm_test;
my_agent agent;
`uvm_component_utils(my_env)
function new (string name="my_env", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent = my_agent :: type_id :: create ("agent", this);
endfunction : build_phase
virtual task run_phase(uvm_phase phase);
// Run a bunch of processes in parallel
fork
doit(phase, 35);
doit(phase, 60);
join
endtask : run_phase
// A simple task that consumes some time.
task doit (uvm_phase phase, time delay);
static int s_inst = 0;
int inst = s_inst++;
//Raise an objection before starting the activity
phase.raise_objection(this);
uvm_report_info(get_name(), $sformatf("Starting doit (%0d) with delay %0t", inst, delay), UVM_NONE);
#delay;
uvm_report_info(get_name(), $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
phase.drop_objection(this);
endtask : doit
endclass : my_env
class my_test extends uvm_test;
my_env env;
`uvm_component_utils(my_test)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
env = my_env :: type_id::create("env", this);
endfunction : build_phase
virtual task run_phase(uvm_phase phase);
// Set a drain time on the objection if needed
`uvm_info("drain", "Setting drain time of 10", UVM_NONE)
// uvm-1.1d or earlier (depricated in uvm-1.2
//uvm_test_done.set_drain_time(this,10);
// uvm-1.2
phase.phase_done.set_drain_time(this,10);
// Run a bunch of processes in parallel
fork
doit(phase, 25);
doit(phase, 50);
join
endtask : run_phase
// A simple task that consumes some time.
task doit (uvm_phase phase, time delay);
static int s_inst = 0;
int inst = s_inst++;
//Raise an objection before starting the activity
phase.raise_objection(
this, // uvm_object obj
"", // string description=""
1 // int count=1, default value = 1 which increment objection count by 1
);
uvm_report_info(get_name(), $sformatf("Starting doit (%0d) with delay %0t", inst, delay), UVM_NONE);
#delay;
uvm_report_info(get_name(), $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
phase.drop_objection(
this, // uvm_object obj
"", // string description=""
1 // int count=1, default value = 1 which decrement objection count by 1
);
endtask : doit
// Called when this or a descendant of this component instance raises the specified objection.
// source_obj - the object that originally raised the objection.
// description - optionally provided by the source_obj to give a reason for raising the objection.
// count - indicates the number of objections raised by the source_obj.
virtual function void raised (uvm_objection objection,
uvm_object source_obj,
string description, // 2nd argument of phase.raise_objection method
int count);
`uvm_info("raised", $sformatf("%d objection(s) raised from %s, total count is now %0d",
count, source_obj.get_full_name, objection.get_objection_total(this)), UVM_NONE)
// description is empty
//`uvm_info("raised", $sformatf("description=%s", description), UVM_LOW)
endfunction : raised
// Called when this or a descendant of this component instance drops the specified objection.
// source_obj - the object that originally dropped the objection.
// description - optionally provided by the source_obj to give a reason for dropping the objection.
// count - indicates the number of objections dropped by the source_obj.
virtual function void dropped (uvm_objection objection,
uvm_object source_obj,
string description, // 2nd argument of phase.drop_objection method
int count);
`uvm_info("dropped", $sformatf("%d objection(s) dropped from %s, total count is now %0d",
count, source_obj.get_full_name, objection.get_objection_total(this)), UVM_NONE)
endfunction : dropped
// Called when all objections have been dropped by this component and all its descendants.
// source_obj - the object that dropped the last objection.
// description - optionally provided by the source_obj to give a reason for dropping the objection.
// count - indicates the number of objections dropped by the source_obj.
virtual task all_dropped (uvm_objection objection,
uvm_object source_obj,
string description, // 2nd argument of phase.drop_objection method
int count);
`uvm_info("all_dropped", $sformatf("Last %d objection(s) dropped from %s, total count is now %0d",
count, source_obj.get_full_name, objection.get_objection_total(this)), UVM_NONE)
#50;
endtask : all_dropped
endclass : my_test
module top;
initial begin
run_test("my_test");
end
endmodule : top
view raw all_dropped.sv hosted with ❤ by GitHub

--------------------------------------------------------------------
--------------------------------------------------------------------
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO top.sv(94) @ 0: uvm_test_top [drain] Setting drain time of 10
// UVM_INFO top.sv(140) @ 0: uvm_test_top [raised] 1 objection(s) raised from uvm_test_top, total count is now 1
// UVM_INFO @ 0: uvm_test_top [uvm_test_top] Starting doit (0) with delay 25
// UVM_INFO top.sv(140) @ 0: uvm_test_top [raised] 1 objection(s) raised from uvm_test_top, total count is now 2
// UVM_INFO @ 0: uvm_test_top [uvm_test_top] Starting doit (1) with delay 50
// UVM_INFO top.sv(140) @ 0: uvm_test_top [raised] 1 objection(s) raised from uvm_test_top.env, total count is now 3
// UVM_INFO @ 0: uvm_test_top.env [env] Starting doit (0) with delay 35
// UVM_INFO top.sv(140) @ 0: uvm_test_top [raised] 1 objection(s) raised from uvm_test_top.env, total count is now 4
// UVM_INFO @ 0: uvm_test_top.env [env] Starting doit (1) with delay 60
// UVM_INFO top.sv(140) @ 0: uvm_test_top [raised] 1 objection(s) raised from uvm_test_top.env.agent, total count is now 5
// UVM_INFO @ 0: uvm_test_top.env.agent [agent] Starting doit (0) with delay 85
// UVM_INFO top.sv(140) @ 0: uvm_test_top [raised] 1 objection(s) raised from uvm_test_top.env.agent, total count is now 6
// UVM_INFO @ 0: uvm_test_top.env.agent [agent] Starting doit (1) with delay 55
// UVM_INFO @ 25: uvm_test_top [uvm_test_top] Ending doit (0)
// UVM_INFO top.sv(154) @ 25: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top, total count is now 5
// UVM_INFO @ 35: uvm_test_top.env [env] Ending doit (0)
// UVM_INFO top.sv(154) @ 35: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top.env, total count is now 4
// UVM_INFO @ 50: uvm_test_top [uvm_test_top] Ending doit (1)
// UVM_INFO top.sv(154) @ 50: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top, total count is now 3
// UVM_INFO @ 55: uvm_test_top.env.agent [agent] Ending doit (1)
// UVM_INFO top.sv(154) @ 55: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top.env.agent, total count is now 2
// UVM_INFO @ 60: uvm_test_top.env [env] Ending doit (1)
// UVM_INFO top.sv(154) @ 60: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top.env, total count is now 1
// UVM_INFO @ 85: uvm_test_top.env.agent [agent] Ending doit (0)
// UVM_INFO top.sv(154) @ 85: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top.env.agent, total count is now 0
// UVM_INFO top.sv(166) @ 95: uvm_test_top [all_dropped] Last 1 objection(s) dropped from uvm_test_top.env.agent, total count is now 0
// UVM_INFO /home/shahs/gotcha/uvm/uvm-1.2/src/base/uvm_objection.svh(1271) @ 145: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
// UVM_INFO /home/shahs/gotcha/uvm/uvm-1.2/src/base/uvm_report_server.svh(847) @ 145: reporter [UVM/REPORT/SERVER]

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

Once all the objections are dropped, Drain time takes effect. After Drain time is finished, all_dropped callback takes effect.

Pros and Cons of Pointers in C

Advantages of Pointer:

1) Pointers allow us to perform dynamic memory allocation and deallocation
2) Using pointer we can return more than one value from the function
3) Using pointer can pass array or string more efficiency
4) Pointer helps us to build complex data structure like linked-list, stack, tree, graphs etc.
5) Pointers permit references to functions and thereby facilitating passing of functions as arguments to other functions.
6) Use function call by reference.
4) Increase the execution speed of program. how?


Drawback of Pointer:

1) Uninitialized pointers might cause segmentation fault.
2) Dynamically allocated block needs to be freed explicitly.  Otherwise, it would lead to memory leak.
3) If pointers are updated with incorrect values, it might lead to memory corruption.
4) Pointer bugs are difficult to debug.

Thursday, 23 March 2017

Automatic raise/drop objection with UVM-1.2

Variable uvm_sequence_base::starting_phase is deprecated and replaced by two new methods set_starting_phase and get_starting_phase, which prevent starting_phase from being modified in the middle of a phase. This change is not backward-compatible with UVM 1.1, though variable starting_phase, although deprecated, has not yet been removed from the base class library.

New method uvm_sequence_base::set_automatic_phase_objection causes raise_objection and drop_objection to be called automatically around a sequence, avoiding the need to call raise/drop_objection manually in one common situation.

Lets understand it trough below mentioned example.
------------------------------------------------
import uvm_pkg :: *;
class my_seq_item extends uvm_sequence_item;
rand logic [7:0] addr;
rand logic [7:0] data;
constraint addr_range_cn {
addr inside {[10:20]};
}
constraint data_range_cn {
data inside {[100:200]};
}
`uvm_object_utils_begin(my_seq_item)
`uvm_field_int(addr, UVM_ALL_ON| UVM_DEC)
`uvm_field_int(data, UVM_ALL_ON| UVM_DEC)
`uvm_object_utils_end
function new(string name="my_seq_item");
super.new(name);
endfunction : new
virtual function string convert2string();
convert2string = $sformatf("addr=%0d, data=%0d", addr, data);
endfunction : convert2string
endclass : my_seq_item
class my_sequencer extends uvm_sequencer #(my_seq_item);
`uvm_component_utils (my_sequencer)
function new (string name="my_sequencer", uvm_component parent=null);
super.new(name, parent);
endfunction : new
endclass : my_sequencer
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils (my_driver)
function new (string name="my_driver", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
endfunction : build_phase
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_name(),
$sformatf("in driver after get_next_item my_seq_item= %s", req.convert2string()), UVM_LOW);
#50;
`uvm_info(get_name(), $sformatf("item_done called"), UVM_LOW);
seq_item_port.item_done();
end
endtask : run_phase
endclass : my_driver
class my_agent extends uvm_agent;
`uvm_component_utils (my_agent)
my_sequencer sqr;
my_driver drv;
function new (string name="my_agent", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = my_sequencer :: type_id :: create("sqr", this);
drv = my_driver :: type_id :: create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
class my_seq extends uvm_sequence #(my_seq_item);
`uvm_object_utils (my_seq)
function new(string name="my_seq");
super.new(name);
// The most common interaction with the starting phase
// within a sequence is to simply ~raise~ the phase's objection
// prior to executing the sequence, and ~drop~ the objection
// after ending the sequence (either naturally, or
// via a call to <kill>). In order to
// simplify this interaction for the user, the UVM
// provides the ability to perform this functionality
// automatically.
// From a timeline point of view, the automatic phase objection
// looks like:
//| start() is executed
//| --! Objection is raised !--
//| pre_start() is executed
//| pre_body() is optionally executed
//| body() is executed
//| post_body() is optionally executed
//| post_start() is executed
//| --! Objection is dropped !--
//| start() unblocks
// NEVER set the automatic phase objection bit to 1 if your sequence
// runs with a forever loop inside of the body, as the objection will
// never get dropped!
set_automatic_phase_objection(1);
endfunction : new
task body ();
`uvm_create(req)
if(!req.randomize()) begin
`uvm_fatal(get_name(), $sformatf("Randomization failed"))
end
`uvm_info (get_name(),
$sformatf("After randomizating in my_seq my_seq_item= %s",
req.convert2string()), UVM_LOW)
`uvm_send(req)
endtask : body
endclass : my_seq
class my_test extends uvm_test;
`uvm_component_utils (my_test)
my_agent agent;
function new (string name="my_test", uvm_component parent=null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
endfunction : build_phase
task run_phase(uvm_phase phase);
my_seq seq;
uvm_phase starting_phase;
bit automatic_phase_objection_status;
seq = my_seq::type_id::create ("seq");
// Function: set_starting_phase
// Sets the 'starting phase'.
seq.set_starting_phase(phase);
// If set_automatic_phase_objection is not called in new (constructor)
// of sequence then can be calleb from test-case
// seq.set_automatic_phase_objection(1);
fork
begin
#30;
// Function: get_starting_phase
// Returns the 'starting phase'.
// If non-null, the starting phase specifies the phase in which this
// sequence was started.
starting_phase = seq.get_starting_phase();
`uvm_info(get_name(),
$sformatf("starting_phase:%s", starting_phase.get_full_name()), UVM_LOW)
// Function: get_automatic_phase_objection
// Returns (and locks) the value of the 'automatically object to
// starting phase' bit.
//
// If 1, then the sequence will automatically raise an objection
// to the starting phase (if the starting phase is not ~null~) immediately
// prior to <pre_start> being called. The objection will be dropped
// after <post_start> has executed, or <kill> has been called.
automatic_phase_objection_status = seq.get_automatic_phase_objection();
`uvm_info(get_name(),
$sformatf("during seq is running, get_automatic_phase_objection returns :%b", automatic_phase_objection_status), UVM_LOW)
end
join_none
seq.start(agent.sqr);
automatic_phase_objection_status = seq.get_automatic_phase_objection();
`uvm_info(get_name(),
$sformatf("After seq finished, get_automatic_phase_objection returns :%b", automatic_phase_objection_status), UVM_LOW)
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.agent.sqr.seq raised 1 objection(s) (automatic phase objection): count=1 total=1
// UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.agent.sqr added 1 objection(s) to its total (raised from source object , automatic phase objection): count=0 total=1
// UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top.agent added 1 objection(s) to its total (raised from source object , automatic phase objection): count=0 total=1
// UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_test_top added 1 objection(s) to its total (raised from source object uvm_test_top.agent.sqr.seq, automatic phase objection): count=0 total=1
// UVM_INFO @ 0: run [OBJTN_TRC] Object uvm_top added 1 objection(s) to its total (raised from source object uvm_test_top.agent.sqr.seq, automatic phase objection): count=0 total=1
// UVM_INFO testbench.sv(118) @ 0: uvm_test_top.agent.sqr@@seq [seq] After randomizating in my_seq my_seq_item= addr=19, data=140
// UVM_INFO testbench.sv(50) @ 0: uvm_test_top.agent.drv [drv] in driver after get_next_item my_seq_item= addr=19, data=140
// UVM_INFO testbench.sv(158) @ 30: uvm_test_top [uvm_test_top] starting_phase:common.run
// UVM_INFO testbench.sv(170) @ 30: uvm_test_top [uvm_test_top] during seq is running, get_automatic_phase_objection returns :1
// UVM_INFO testbench.sv(52) @ 50: uvm_test_top.agent.drv [drv] item_done called
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top.agent.sqr.seq dropped 1 objection(s) (automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top.agent.sqr.seq all_dropped 1 objection(s) (automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top.agent.sqr subtracted 1 objection(s) from its total (dropped from source object , automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top.agent.sqr subtracted 1 objection(s) from its total (all_dropped from source object , automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top.agent subtracted 1 objection(s) from its total (dropped from source object , automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top.agent subtracted 1 objection(s) from its total (all_dropped from source object , automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top subtracted 1 objection(s) from its total (dropped from source object uvm_test_top.agent.sqr.seq, automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_test_top subtracted 1 objection(s) from its total (all_dropped from source object uvm_test_top.agent.sqr.seq, automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_top subtracted 1 objection(s) from its total (dropped from source object uvm_test_top.agent.sqr.seq, automatic phase objection): count=0 total=0
// UVM_INFO @ 50: run [OBJTN_TRC] Object uvm_top subtracted 1 objection(s) from its total (all_dropped from source object uvm_test_top.agent.sqr.seq, automatic phase objection): count=0 total=0
// UVM_INFO uvm-1.2/src/base/uvm_objection.svh(1271) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
// UVM_INFO testbench.sv(176) @ 50: uvm_test_top [uvm_test_top] After seq finished, get_automatic_phase_objection returns :1
// UVM_INFO uvm-1.2/src/base/uvm_report_server.svh(847) @ 50: reporter [UVM/REPORT/SERVER]

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


Ref:
1) https://www.doulos.com/knowhow/sysverilog/uvm/uvm-1.2/

Simulation timeout in UVM using set_timeout and +UVM_TIMEOUT

Default timeout for simulation or you can say timeout for run_phase (as rest all phases are non-time consuming) is `UVM_DEFAULT_TIMEOUT, if not overridden by uvm_root::set_timeout or uvm_cmdline_processor::+UVM_TIMEOUT.

Default value of `UVM_DEFAULT_TIMEOUT is 9200 second.
-------------------------------------------------
uvm-1.2/src/base/uvm_objection.svh
Line 57: `define UVM_DEFAULT_TIMEOUT 9200s
-------------------------------------------------

Example 1 (Timeout at default time which is 9200 second):
-------------------------------------------------
`timescale 1ns/1ns
import uvm_pkg::*;
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
endfunction : start_of_simulation_phase
task pre_main_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : pre_main_phase
task main_phase(uvm_phase phase);
phase.raise_objection(this);
// Will cause a time-out
// because we forgot to drop the objection
endtask : main_phase
task shutdown_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : shutdown_phase
endclass : test
module top();
initial begin
run_test("test");
end
endmodule : top
//Output (with +UVM_OBJECTION_TRACE simulation switch):
// UVM_INFO @ 0: reporter [RNTST] Running test test...
// UVM_FATAL uvm-1.2/src/base/uvm_phase.svh(1491) @ 9200000000000: reporter [PH_TIMEOUT] Default timeout of 9200000000000 hit, indicating a probable testbench issue
// UVM_INFO uvm-1.2/src/base/uvm_report_server.svh(847) @ 9200000000000: reporter [UVM/REPORT/SERVER]
-------------------------------------------------

1) Overridden by uvm_root::set_timeout

-------------------------------------------------
uvm-1.2/src/base/uvm_root.svh
Line 213: // Variable- phase_timeout
// Specifies the timeout for the run phase. Default is `UVM_DEFAULT_TIMEOUT
time phase_timeout = `UVM_DEFAULT_TIMEOUT;
Line 139: // Function: set_timeout
// Specifies the timeout for the simulation. Default is <`UVM_DEFAULT_TIMEOUT>
// The timeout is simply the maximum absolute simulation time allowed before a
// ~FATAL~ occurs. If the timeout is set to 20ns, then the simulation must end
// before 20ns, or a ~FATAL~ timeout will occur.
// This is provided so that the user can prevent the simulation from potentially
// consuming too many resources (Disk, Memory, CPU, etc) when the testbench is
// essentially hung.
function void uvm_root::set_timeout(time timeout, bit overridable=1);
static bit m_uvm_timeout_overridable = 1;
if (m_uvm_timeout_overridable == 0) begin
uvm_report_info("NOTIMOUTOVR",
$sformatf("The global timeout setting of %0d is not overridable to %0d due to a previous setting.",
phase_timeout, timeout), UVM_NONE);
return;
end
m_uvm_timeout_overridable = overridable;
phase_timeout = timeout;
endfunction
-------------------------------------------------
-------------------------------------------------
uvm-1.2/src/base/uvm_phase.svh
Line 1297: task uvm_phase::execute_phase();
uvm_task_phase task_phase;
uvm_root top;
uvm_phase_state_change state_chg;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
...........
// TIMEOUT
begin
if (this.get_name() == "run") begin
if (top.phase_timeout == 0)
wait(top.phase_timeout != 0);
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TO_WAIT", $sformatf("STARTING PHASE TIMEOUT WATCHDOG (timeout == %t)", top.phase_timeout), this, UVM_HIGH)
`uvm_delay(top.phase_timeout)
if ($time == `UVM_DEFAULT_TIMEOUT) begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
foreach (m_executing_phases[p]) begin
if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
$sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
this,
UVM_LOW)
end
end
`uvm_fatal("PH_TIMEOUT",
$sformatf("Default timeout of %0t hit, indicating a probable testbench issue",
`UVM_DEFAULT_TIMEOUT))
end
else begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT", "PHASE TIMEOUT WATCHDOG EXPIRED", this, UVM_LOW)
foreach (m_executing_phases[p]) begin
if ((p.phase_done != null) && (p.phase_done.get_objection_total() > 0)) begin
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/TIMEOUT/OBJCTN",
$sformatf("Phase '%s' has outstanding objections:\n%s", p.get_full_name(), p.phase_done.convert2string()),
this,
UVM_LOW)
end
end
`uvm_fatal("PH_TIMEOUT",
$sformatf("Explicit timeout of %0t hit, indicating a probable testbench issue",
top.phase_timeout))
end
if (m_phase_trace)
`UVM_PH_TRACE("PH/TRC/EXE/3","PHASE EXIT TIMEOUT",this,UVM_DEBUG)
end // if (this.get_name() == "run")
else begin
wait (0); // never unblock for non-run phase
end
end // if (m_phase_trace)
-------------------------------------------------

Example 2 (Timeout at specified time which is specified using set_timeout):
-------------------------------------------------
`timescale 1ns/1ns
import uvm_pkg::*;
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
// uvm_top is a constant handle of uvm_root declared in uvm_root.svh file
uvm_top.set_timeout(100s, 1); // Override default timeout to 1oo second
// or you can use below syntax as well
// uvm_root::get().set_timeout(100s, 1);
endfunction : start_of_simulation_phase
task pre_main_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : pre_main_phase
task main_phase(uvm_phase phase);
phase.raise_objection(this);
// Will cause a time-out
// because we forgot to drop the objection
endtask : main_phase
task shutdown_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : shutdown_phase
endclass : test
module top();
initial begin
run_test("test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test test...
// UVM_FATAL uvm-1.2/src/base/uvm_phase.svh(1508) @ 100000000000: reporter [PH_TIMEOUT] Explicit timeout of 100000000000 hit, indicating a probable testbench issue
// UVM_INFO uvm-1.2/src/base/uvm_report_server.svh(847) @ 100000000000: reporter [UVM/REPORT/SERVER]
-------------------------------------------------


2) Overridden by uvm_cmdline_processor::+UVM_TIMEOUT

+UVM_TIMEOUT=, allows users to change the global timeout of the UVM framework.  The argument (‘YES’ or ‘NO’) specifies whether user code can subsequently change this value.  If set to ‘NO’ and the user code tries to change the global timeout value, a warning message will be generated.

Example 3 (Timeout at 100s using simulation switch +UVM_TIMEOUT=100000000000):
Note: Here you need to give simulation time in format of timescale defined in simulation
-------------------------------------------------
`timescale 1ns/1ns
import uvm_pkg::*;
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
endfunction : start_of_simulation_phase
task pre_main_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : pre_main_phase
task main_phase(uvm_phase phase);
phase.raise_objection(this);
// Will cause a time-out
// because we forgot to drop the objection
endtask : main_phase
task shutdown_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : shutdown_phase
endclass : test
module top();
initial begin
run_test("test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test test...
// UVM_INFO @ 0: reporter [TIMOUTSET] '+UVM_TIMEOUT=100000000000' provided on the command line is being applied.
// UVM_FATAL uvm-1.2/src/base/uvm_phase.svh(1508) @ 100000000000: reporter [PH_TIMEOUT] Explicit timeout of 100000000000 hit, indicating a probable testbench issue
// UVM_INFO uvm-1.2/src/base/uvm_report_server.svh(847) @ 100000000000: reporter [UVM/REPORT/SERVER]
-------------------------------------------------

Tuesday, 21 February 2017

Lock and Grab of sequencer in UVM

There are a number of modelling scenarios where one sequence needs to have exclusive access to a driver via a sequencer. One example of this type of scenario is a sequence which is responding to an interrupt.
In order to accommodate this requirement, the uvm_sequencer provides 2 types of mechanism called lock()-unlock() and grab()-ungrab().

If sequencer is doing some sequence and based on some external events if user wants sequencer to pause the current sequence, he/she can grab/lock sequencer and start another sequence. Once the started sequence finishes sequencer can resume the normal operation, upon ungrab/unlock.
This mechanism is generally used to mimic CPU behavior, where CPU is doing a normal bus operation and when interrupt comes, CPU needs to suspend the normal operation and start interrupt handling. Once interrupt handling is over, CPU should resume the normal operation from where it was suspended.

A lock might be used to model a prioritized interrupt and a grab might be used to model a non-mask able interrupt (NMI).
The lock() and grab() calls have antidote calls to release a lock and these are unlock() and ungrab().

Lock()
1) A lock() request is put in back of the arbitration queue . A lock request will be arbitrated the same as any other request.
2) A lock is granted after all earlier requests are completed and no other locks or grabs are blocking this sequence.
3) A lock() is blocking task and when access is granted, it will unblock.
4) If no argument is supplied, then current default sequencer is chosen.

Grab()
1) A grab() request is put in front of the arbitration queue. A grab() request will be arbitrated before any other requests.
2) A grab() is granted when no other grabs or locks are blocking this sequence. (The only thing that stops a sequence from grabbing a sequencer is a pre-existing lock() or grab() condition.)
3) A grab() is blocking task and when access is granted, it will unblock.
4) If no argument is supplied, then current default sequencer is chosen.

Unlock()
The unlock sequencer function is called from within a sequence to give up its lock or grab. A locking sequence must call unlock before completion; otherwise the sequencer will remain locked.

Ungrab()
An alias of function unlock().


What happens if 2 sequences try to grab or lock the same sequencer?
The most recent grab goes to the front of the queue; the most recent lock goes to the back of the queue.

Priority in lock() and grab().
The most recent grab comes first, then the previous grab... then the oldest lock, then the most recent lock.

Let’s go through basic example of lock() and grab()
1) Lock
---------------------------------------------
`include "uvm_pkg.sv"
import uvm_pkg::*;
typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_e;
//-----Packet/Transaction-----
class my_seq_item extends uvm_sequence_item;
rand inst_e inst;
`uvm_object_utils_begin(my_seq_item)
`uvm_field_enum(inst_e,inst, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_seq_item");
super.new(name);
endfunction : new
function string convert2string();
convert2string = $sformatf("inst:%s", inst.name());
endfunction : convert2string
endclass : my_seq_item
//-----Sequencer-----
class my_sequencer extends uvm_sequencer #(my_seq_item);
`uvm_sequencer_utils(my_sequencer)
function new (string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
`uvm_update_sequence_lib_and_item(my_seq_item)
endfunction : new
endclass : my_sequencer
//-----Driver-----
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
function new (string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction : new
task run_phase (uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
$display($time," Driving pkt %s", req.convert2string());
#10;
seq_item_port.item_done();
end
endtask : run_phase
endclass : my_driver
//-----Agent-----
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
`uvm_component_utils(my_agent)
function new (string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = my_sequencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
//-----seq_a-----
class seq_a extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_a)
function new(string name="seq_a");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_A; });
end
endtask : body
endclass : seq_a
//-----seq_b-----
class seq_b extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_b)
function new(string name="seq_b");
super.new(name);
endfunction : new
virtual task body();
lock(); // or lock(m_sequencer);
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_B; });
end
unlock(); // or unlock(m_sequencer);
endtask : body
endclass : seq_b
//-----seq_c-----
class seq_c extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_c)
function new(string name="seq_c");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == POP_C; });
end
endtask : body
endclass : seq_c
//-----Virtual sequence-----
class parallel_sequence extends uvm_sequence #(my_seq_item);
seq_a s_a;
seq_b s_b;
seq_c s_c;
`uvm_object_utils(parallel_sequence)
function new(string name="parallel_sequence");
super.new(name);
endfunction : new
virtual task body();
s_a = seq_a::type_id::create("s_a");
s_b = seq_b::type_id::create("s_b");
s_c = seq_c::type_id::create("s_c");
fork
s_a.start(m_sequencer);
s_b.start(m_sequencer);
s_c.start(m_sequencer);
join
endtask : body
endclass : parallel_sequence
//-----Test-----
class my_test extends uvm_test;
my_agent agent;
`uvm_component_utils(my_test)
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
endfunction : build_phase
task run_phase(uvm_phase phase);
parallel_sequence p_seq;
p_seq = parallel_sequence::type_id::create("p_seq");
phase.raise_objection(this);
p_seq.start(agent.sqr);
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
// Output:
// 0 Driving pkt inst:PUSH_A
// 10 Driving pkt inst:PUSH_B
// 20 Driving pkt inst:PUSH_B
// 30 Driving pkt inst:PUSH_B
// 40 Driving pkt inst:PUSH_B
// 50 Driving pkt inst:POP_C
// 60 Driving pkt inst:PUSH_A
// 70 Driving pkt inst:POP_C
// 80 Driving pkt inst:PUSH_A
// 90 Driving pkt inst:POP_C
// 100 Driving pkt inst:PUSH_A
// 110 Driving pkt inst:POP_C
view raw lock_in_uvm.sv hosted with ❤ by GitHub
---------------------------------------------

2) Grab
---------------------------------------------
`include "uvm_pkg.sv"
import uvm_pkg::*;
typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_e;
//-----Packet/Transaction-----
class my_seq_item extends uvm_sequence_item;
rand inst_e inst;
`uvm_object_utils_begin(my_seq_item)
`uvm_field_enum(inst_e,inst, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_seq_item");
super.new(name);
endfunction : new
function string convert2string();
convert2string = $sformatf("inst:%s", inst.name());
endfunction : convert2string
endclass : my_seq_item
//-----Sequencer-----
class my_sequencer extends uvm_sequencer #(my_seq_item);
`uvm_sequencer_utils(my_sequencer)
function new (string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
`uvm_update_sequence_lib_and_item(my_seq_item)
endfunction : new
endclass : my_sequencer
//-----Driver-----
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
function new (string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction : new
task run_phase (uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
$display($time," Driving pkt %s", req.convert2string());
#10;
seq_item_port.item_done();
end
endtask : run_phase
endclass : my_driver
//-----Agent-----
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
`uvm_component_utils(my_agent)
function new (string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = my_sequencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
//-----seq_a-----
class seq_a extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_a)
function new(string name="seq_a");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_A; });
end
endtask : body
endclass : seq_a
//-----seq_b-----
class seq_b extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_b)
function new(string name="seq_b");
super.new(name);
endfunction : new
virtual task body();
grab(); // or grab(m_sequencer);
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_B; });
end
ungrab(); // or ungrab(m_sequencer);
endtask : body
endclass : seq_b
//-----seq_c-----
class seq_c extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_c)
function new(string name="seq_c");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == POP_C; });
end
endtask : body
endclass : seq_c
//-----Virtual Sequence-----
class parallel_sequence extends uvm_sequence #(my_seq_item);
seq_a s_a;
seq_b s_b;
seq_c s_c;
`uvm_object_utils(parallel_sequence)
function new(string name="parallel_sequence");
super.new(name);
endfunction : new
virtual task body();
fork
`uvm_do(s_a)
`uvm_do(s_b)
`uvm_do(s_c)
join
endtask : body
endclass : parallel_sequence
//-----Test-----
class my_test extends uvm_test;
my_agent agent;
`uvm_component_utils(my_test)
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent = my_agent::type_id::create("agent", this);
endfunction : build_phase
task run_phase(uvm_phase phase);
parallel_sequence p_seq;
p_seq = parallel_sequence::type_id::create("p_seq");
phase.raise_objection(this);
p_seq.start(agent.sqr);
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
// Output:
// 0 Driving pkt inst:PUSH_B
// 10 Driving pkt inst:PUSH_B
// 20 Driving pkt inst:PUSH_B
// 30 Driving pkt inst:PUSH_B
// 40 Driving pkt inst:PUSH_A
// 50 Driving pkt inst:POP_C
// 60 Driving pkt inst:PUSH_A
// 70 Driving pkt inst:POP_C
// 80 Driving pkt inst:PUSH_A
// 90 Driving pkt inst:POP_C
// 100 Driving pkt inst:PUSH_A
// 110 Driving pkt inst:POP_C
view raw grab_in_uvm.sv hosted with ❤ by GitHub
---------------------------------------------
From output of above two examples, we can see the difference between lock() and grab() as mentioned above.


When a hierarchical sequence locks/grab a sequencer, then its child sequences will have access to the sequencer.
If one of the child sequences issues a lock/grab, then the parent sequence will not be able to start any parallel sequences or send any sequence_items until the child sequence has unlocked.
---------------------------------------------
`include "uvm_pkg.sv"
import uvm_pkg::*;
typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_e;
//-----Packet/Transaction-----
class my_seq_item extends uvm_sequence_item;
rand inst_e inst;
`uvm_object_utils_begin(my_seq_item)
`uvm_field_enum(inst_e,inst, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_seq_item");
super.new(name);
endfunction : new
function string convert2string();
convert2string = $sformatf("inst:%s", inst.name());
endfunction : convert2string
endclass : my_seq_item
//-----Sequencer-----
class my_sequencer extends uvm_sequencer #(my_seq_item);
`uvm_sequencer_utils(my_sequencer)
function new (string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
`uvm_update_sequence_lib_and_item(my_seq_item)
endfunction : new
endclass : my_sequencer
//-----Driver-----
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
function new (string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction : new
task run_phase (uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
$display($time," Driving pkt %s", req.convert2string());
#10;
seq_item_port.item_done();
end
endtask : run_phase
endclass : my_driver
//-----Agent-----
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
`uvm_component_utils(my_agent)
function new (string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = my_sequencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
//-----Virtual Sequencer-----
class my_virtual_sqr extends uvm_sequencer;
my_sequencer sqr1;
my_sequencer sqr2;
`uvm_component_utils(my_virtual_sqr)
function new (string name = "my_virtual_sqr", uvm_component parent = null);
super.new(name, parent);
endfunction : new
endclass : my_virtual_sqr
//-----seq_a-----
class seq_a extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_a)
function new(string name="seq_a");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_A; });
end
endtask : body
endclass : seq_a
//-----seq_b-----
class seq_b extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_b)
function new(string name="seq_b");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_B; });
end
endtask : body
endclass : seq_b
//-----seq_c-----
class seq_c extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_c)
function new(string name="seq_c");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == POP_C; });
end
endtask : body
endclass : seq_c
//-----Virtual Sequence-----
class parallel_sequence extends uvm_sequence #(my_seq_item);
seq_a s_a;
seq_b s_b;
seq_c s_c;
`uvm_object_utils(parallel_sequence)
`uvm_declare_p_sequencer(my_virtual_sqr)
function new(string name="parallel_sequence");
super.new(name);
endfunction : new
virtual task body();
// parallel_sequence put grab on sequencer sqr1,
// but still it's child sequences (seq_a, seq_b, seq_c) will have access of sequencer sqr1
grab(p_sequencer.sqr1);
fork
begin
`uvm_create_on(s_a, p_sequencer.sqr1)
`uvm_rand_send(s_a)
end
begin
`uvm_create_on(s_b, p_sequencer.sqr1)
`uvm_rand_send(s_b)
end
begin
`uvm_create_on(s_c, p_sequencer.sqr1)
`uvm_rand_send(s_c)
end
join
ungrab(p_sequencer.sqr1);
endtask : body
endclass : parallel_sequence
//-----Test-----
class my_test extends uvm_test;
my_agent agent1;
my_agent agent2;
my_virtual_sqr vsqr;
`uvm_component_utils(my_test)
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent1 = my_agent::type_id::create("agent1", this);
agent2 = my_agent::type_id::create("agent2", this);
vsqr = my_virtual_sqr::type_id::create("vsqr", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
vsqr.sqr1 = agent1.sqr;
vsqr.sqr2 = agent2.sqr;
endfunction : connect_phase
task run_phase(uvm_phase phase);
parallel_sequence p_seq;
p_seq = parallel_sequence::type_id::create("p_seq");
phase.raise_objection(this);
p_seq.start(vsqr);
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
// Output:
// 0 Driving pkt inst:PUSH_A
// 10 Driving pkt inst:PUSH_B
// 20 Driving pkt inst:POP_C
// 30 Driving pkt inst:PUSH_A
// 40 Driving pkt inst:PUSH_B
// 50 Driving pkt inst:POP_C
// 60 Driving pkt inst:PUSH_A
// 70 Driving pkt inst:PUSH_B
// 80 Driving pkt inst:POP_C
// 90 Driving pkt inst:PUSH_A
// 100 Driving pkt inst:PUSH_B
// 110 Driving pkt inst:POP_C
---------------------------------------------


A locking or grabbing sequence must always unlock before it completes,
---------------------------------------------
`include "uvm_pkg.sv"
import uvm_pkg::*;
typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_e;
//-----Packet/Transaction-----
class my_seq_item extends uvm_sequence_item;
rand inst_e inst;
`uvm_object_utils_begin(my_seq_item)
`uvm_field_enum(inst_e,inst, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_seq_item");
super.new(name);
endfunction : new
function string convert2string();
convert2string = $sformatf("inst:%s", inst.name());
endfunction : convert2string
endclass : my_seq_item
//-----Sequencer-----
class my_sequencer extends uvm_sequencer #(my_seq_item);
`uvm_sequencer_utils(my_sequencer)
function new (string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
`uvm_update_sequence_lib_and_item(my_seq_item)
endfunction : new
endclass : my_sequencer
//-----Driver-----
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
function new (string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction : new
task run_phase (uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
$display($time," Driving pkt %s", req.convert2string());
#10;
seq_item_port.item_done();
end
endtask : run_phase
endclass : my_driver
//-----Agent-----
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
`uvm_component_utils(my_agent)
function new (string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = my_sequencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
//-----Virtual Sequencer-----
class my_virtual_sqr extends uvm_sequencer;
my_sequencer sqr1;
my_sequencer sqr2;
`uvm_component_utils(my_virtual_sqr)
function new (string name = "my_virtual_sqr", uvm_component parent = null);
super.new(name, parent);
endfunction : new
endclass : my_virtual_sqr
//-----seq_a-----
class seq_a extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_a)
function new(string name="seq_a");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_A; });
end
endtask : body
endclass : seq_a
//-----seq_b-----
class seq_b extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_b)
function new(string name="seq_b");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == PUSH_B; });
end
endtask : body
endclass : seq_b
//-----seq_c-----
class seq_c extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_c)
function new(string name="seq_c");
super.new(name);
endfunction : new
virtual task body();
repeat(4) begin
`uvm_do_with(req, { inst == POP_C; });
end
endtask : body
endclass : seq_c
//-----Virtual Sequence-----
class parallel_sequence extends uvm_sequence #(my_seq_item);
seq_a s_a;
seq_b s_b;
seq_c s_c;
`uvm_object_utils(parallel_sequence)
`uvm_declare_p_sequencer(my_virtual_sqr)
function new(string name="parallel_sequence");
super.new(name);
endfunction : new
virtual task body();
fork
begin
`uvm_create_on(s_a, p_sequencer.sqr1)
`uvm_rand_send(s_a)
end
begin
`uvm_create_on(s_b, p_sequencer.sqr1)
s_b.grab(p_sequencer.sqr1);
// or p_sequencer.sqr1.grab(s_b);
`uvm_rand_send(s_b)
s_b.ungrab(p_sequencer.sqr1);
// or p_sequencer.sqr1.ungrab(s_b);
ungrab(p_sequencer.sqr1);
end
begin
`uvm_create_on(s_c, p_sequencer.sqr1)
`uvm_rand_send(s_c)
end
join
endtask : body
endclass : parallel_sequence
//-----Test-----
class my_test extends uvm_test;
my_agent agent1;
my_agent agent2;
my_virtual_sqr vsqr;
`uvm_component_utils(my_test)
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent1 = my_agent::type_id::create("agent1", this);
agent2 = my_agent::type_id::create("agent2", this);
vsqr = my_virtual_sqr::type_id::create("vsqr", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
vsqr.sqr1 = agent1.sqr;
vsqr.sqr2 = agent2.sqr;
endfunction : connect_phase
task run_phase(uvm_phase phase);
parallel_sequence p_seq;
p_seq = parallel_sequence::type_id::create("p_seq");
phase.raise_objection(this);
p_seq.start(vsqr);
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
// Output:
// 0 Driving pkt inst:PUSH_B
// 10 Driving pkt inst:PUSH_B
// 20 Driving pkt inst:PUSH_B
// 30 Driving pkt inst:PUSH_B
// UVM_ERROR .../uvm-1.2/seq/uvm_sequencer_base.svh(1296) @ 40: uvm_test_top.agent1.sqr [SEQFINERR] Parent sequence 'uvm_test_top.vsqr.p_seq.s_b' should not finish before locks from itself and descedent sequences are removed. The lock held by the child sequence 'uvm_test_top.vsqr.p_seq.s_b' is being removed.
// UVM_WARNING @ 40: uvm_test_top.agent1.sqr [SQRUNL] Sequence 'uvm_test_top.vsqr.p_seq.s_b' called ungrab / unlock, but didn't have lock
// UVM_WARNING @ 40: uvm_test_top.agent1.sqr [SQRUNL] Sequence 'uvm_test_top.vsqr.p_seq' called ungrab / unlock, but didn't have lock
// 40 Driving pkt inst:PUSH_A
// 50 Driving pkt inst:POP_C
// 60 Driving pkt inst:PUSH_A
// 70 Driving pkt inst:POP_C
// 80 Driving pkt inst:PUSH_A
// 90 Driving pkt inst:POP_C
// 100 Driving pkt inst:PUSH_A
// 110 Driving pkt inst:POP_C
---------------------------------------------

Now let’s go through one example where 2 virtual sequences are running in parallel and one virtual sequence put lock/grab on agent’s sequencer.

---------------------------------------------
`include "uvm_pkg.sv"
import uvm_pkg::*;
typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_e;
//-----Packet/Transaction-----
class my_seq_item extends uvm_sequence_item;
rand inst_e inst;
`uvm_object_utils_begin(my_seq_item)
`uvm_field_enum(inst_e,inst, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_seq_item");
super.new(name);
endfunction : new
function string convert2string();
convert2string = $sformatf("inst:%s", inst.name());
endfunction : convert2string
endclass : my_seq_item
//-----Sequencer-----
class my_sequencer extends uvm_sequencer #(my_seq_item);
`uvm_sequencer_utils(my_sequencer)
function new (string name = "my_sequencer", uvm_component parent = null);
super.new(name, parent);
`uvm_update_sequence_lib_and_item(my_seq_item)
endfunction : new
endclass : my_sequencer
//-----Driver-----
class my_driver extends uvm_driver #(my_seq_item);
`uvm_component_utils(my_driver)
function new (string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction : new
task run_phase (uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_name(), $sformatf("Driving pkt %s", req.convert2string()), UVM_LOW)
#10;
seq_item_port.item_done();
end
endtask : run_phase
endclass : my_driver
//-----Agent-----
class my_agent extends uvm_agent;
my_sequencer sqr;
my_driver drv;
`uvm_component_utils(my_agent)
function new (string name = "my_agent", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
sqr = my_sequencer::type_id::create("sqr", this);
drv = my_driver::type_id::create("drv", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction : connect_phase
endclass : my_agent
//-----Virtual Sequencer-----
class my_virtual_sqr extends uvm_sequencer;
my_sequencer sqr1;
my_sequencer sqr2;
`uvm_component_utils(my_virtual_sqr)
function new (string name = "my_virtual_sqr", uvm_component parent = null);
super.new(name, parent);
endfunction : new
endclass : my_virtual_sqr
//-----seq_a-----
class seq_a extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_a)
function new(string name="seq_a");
super.new(name);
endfunction : new
virtual task body();
for (int unsigned i=0; i<4; i ++) begin
`uvm_do_with(req, { inst == PUSH_A; });
`uvm_info(get_full_name(), $sformatf("completed :%0d iteration", i), UVM_LOW)
end
endtask : body
endclass : seq_a
//-----seq_b-----
class seq_b extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_b)
function new(string name="seq_b");
super.new(name);
endfunction : new
virtual task body();
for (int unsigned i=0; i<4; i ++) begin
`uvm_do_with(req, { inst == PUSH_B; });
`uvm_info(get_full_name(), $sformatf("completed :%0d iteration", i), UVM_LOW)
end
endtask : body
endclass : seq_b
//-----seq_c-----
class seq_c extends uvm_sequence #(my_seq_item);
my_seq_item req;
`uvm_object_utils(seq_c)
function new(string name="seq_c");
super.new(name);
endfunction : new
virtual task body();
for (int unsigned i=0; i<4; i ++) begin
`uvm_do_with(req, { inst == POP_C; });
`uvm_info(get_full_name(), $sformatf("completed :%0d iteration", i), UVM_LOW)
end
endtask : body
endclass : seq_c
//-----Virtual Sequence-----
class parallel_sequence extends uvm_sequence #(my_seq_item);
seq_a s_a;
seq_b s_b;
seq_c s_c;
`uvm_object_utils(parallel_sequence)
`uvm_declare_p_sequencer(my_virtual_sqr)
function new(string name="parallel_sequence");
super.new(name);
endfunction : new
virtual task body();
grab(p_sequencer.sqr1);
fork
begin
`uvm_create_on(s_a, p_sequencer.sqr1)
`uvm_rand_send(s_a)
`uvm_info(get_name(), $sformatf("s_a completed"), UVM_LOW)
end
begin
`uvm_create_on(s_b, p_sequencer.sqr1)
`uvm_rand_send(s_b)
`uvm_info(get_name(), $sformatf("s_b completed"), UVM_LOW)
end
begin
`uvm_create_on(s_c, p_sequencer.sqr2)
`uvm_rand_send(s_c)
`uvm_info(get_name(), $sformatf("s_c completed"), UVM_LOW)
end
join
ungrab(p_sequencer.sqr1);
endtask : body
endclass : parallel_sequence
//-----Virtual Sequence-----
class parallel_sequence1 extends uvm_sequence #(my_seq_item);
seq_a s_a;
seq_b s_b;
seq_c s_c;
`uvm_object_utils(parallel_sequence1)
`uvm_declare_p_sequencer(my_virtual_sqr)
function new(string name="parallel_sequence1");
super.new(name);
endfunction : new
virtual task body();
fork
begin
`uvm_create_on(s_a, p_sequencer.sqr1)
`uvm_rand_send(s_a)
`uvm_info(get_name(), $sformatf("s_a completed"), UVM_LOW)
end
begin
`uvm_create_on(s_b, p_sequencer.sqr1)
`uvm_rand_send(s_b)
`uvm_info(get_name(), $sformatf("s_b completed"), UVM_LOW)
end
begin
`uvm_create_on(s_c, p_sequencer.sqr2)
`uvm_rand_send(s_c)
`uvm_info(get_name(), $sformatf("s_c completed"), UVM_LOW)
end
join
endtask : body
endclass : parallel_sequence1
//-----Test-----
class my_test extends uvm_test;
my_agent agent1;
my_agent agent2;
my_virtual_sqr vsqr;
`uvm_component_utils(my_test)
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
agent1 = my_agent::type_id::create("agent1", this);
agent2 = my_agent::type_id::create("agent2", this);
vsqr = my_virtual_sqr::type_id::create("vsqr", this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
vsqr.sqr1 = agent1.sqr;
vsqr.sqr2 = agent2.sqr;
endfunction : connect_phase
task run_phase(uvm_phase phase);
parallel_sequence p_seq;
parallel_sequence1 p1_seq;
p_seq = parallel_sequence::type_id::create("p_seq");
p1_seq = parallel_sequence1::type_id::create("p1_seq");
phase.raise_objection(this);
fork
p_seq.start(vsqr);
p1_seq.start(vsqr);
join
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
// Output:
// UVM_INFO grab_top_4.sv(42) @ 0: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(42) @ 0: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(128) @ 10: uvm_test_top.agent2.sqr@@p_seq.s_c [uvm_test_top.vsqr.p_seq.s_c] completed :0 iteration
// UVM_INFO grab_top_4.sv(94) @ 10: uvm_test_top.agent1.sqr@@p_seq.s_a [uvm_test_top.vsqr.p_seq.s_a] completed :0 iteration
// UVM_INFO grab_top_4.sv(42) @ 10: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(42) @ 10: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(111) @ 20: uvm_test_top.agent1.sqr@@p_seq.s_b [uvm_test_top.vsqr.p_seq.s_b] completed :0 iteration
// UVM_INFO grab_top_4.sv(128) @ 20: uvm_test_top.agent2.sqr@@p1_seq.s_c [uvm_test_top.vsqr.p1_seq.s_c] completed :0 iteration
// UVM_INFO grab_top_4.sv(42) @ 20: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(42) @ 20: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(128) @ 30: uvm_test_top.agent2.sqr@@p_seq.s_c [uvm_test_top.vsqr.p_seq.s_c] completed :1 iteration
// UVM_INFO grab_top_4.sv(94) @ 30: uvm_test_top.agent1.sqr@@p_seq.s_a [uvm_test_top.vsqr.p_seq.s_a] completed :1 iteration
// UVM_INFO grab_top_4.sv(42) @ 30: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(42) @ 30: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(111) @ 40: uvm_test_top.agent1.sqr@@p_seq.s_b [uvm_test_top.vsqr.p_seq.s_b] completed :1 iteration
// UVM_INFO grab_top_4.sv(128) @ 40: uvm_test_top.agent2.sqr@@p1_seq.s_c [uvm_test_top.vsqr.p1_seq.s_c] completed :1 iteration
// UVM_INFO grab_top_4.sv(42) @ 40: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(42) @ 40: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(128) @ 50: uvm_test_top.agent2.sqr@@p_seq.s_c [uvm_test_top.vsqr.p_seq.s_c] completed :2 iteration
// UVM_INFO grab_top_4.sv(94) @ 50: uvm_test_top.agent1.sqr@@p_seq.s_a [uvm_test_top.vsqr.p_seq.s_a] completed :2 iteration
// UVM_INFO grab_top_4.sv(42) @ 50: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(42) @ 50: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(111) @ 60: uvm_test_top.agent1.sqr@@p_seq.s_b [uvm_test_top.vsqr.p_seq.s_b] completed :2 iteration
// UVM_INFO grab_top_4.sv(128) @ 60: uvm_test_top.agent2.sqr@@p1_seq.s_c [uvm_test_top.vsqr.p1_seq.s_c] completed :2 iteration
// UVM_INFO grab_top_4.sv(42) @ 60: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(42) @ 60: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(128) @ 70: uvm_test_top.agent2.sqr@@p_seq.s_c [uvm_test_top.vsqr.p_seq.s_c] completed :3 iteration
// UVM_INFO grab_top_4.sv(94) @ 70: uvm_test_top.agent1.sqr@@p_seq.s_a [uvm_test_top.vsqr.p_seq.s_a] completed :3 iteration
// UVM_INFO grab_top_4.sv(161) @ 70: uvm_test_top.vsqr@@p_seq [p_seq] s_c completed
// UVM_INFO grab_top_4.sv(151) @ 70: uvm_test_top.vsqr@@p_seq [p_seq] s_a completed
// UVM_INFO grab_top_4.sv(42) @ 70: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(42) @ 70: uvm_test_top.agent2.drv [drv] Driving pkt inst:POP_C
// UVM_INFO grab_top_4.sv(111) @ 80: uvm_test_top.agent1.sqr@@p_seq.s_b [uvm_test_top.vsqr.p_seq.s_b] completed :3 iteration
// UVM_INFO grab_top_4.sv(128) @ 80: uvm_test_top.agent2.sqr@@p1_seq.s_c [uvm_test_top.vsqr.p1_seq.s_c] completed :3 iteration
// UVM_INFO grab_top_4.sv(156) @ 80: uvm_test_top.vsqr@@p_seq [p_seq] s_b completed
// UVM_INFO grab_top_4.sv(195) @ 80: uvm_test_top.vsqr@@p1_seq [p1_seq] s_c completed
// UVM_INFO grab_top_4.sv(42) @ 80: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(94) @ 90: uvm_test_top.agent1.sqr@@p1_seq.s_a [uvm_test_top.vsqr.p1_seq.s_a] completed :0 iteration
// UVM_INFO grab_top_4.sv(42) @ 90: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(111) @ 100: uvm_test_top.agent1.sqr@@p1_seq.s_b [uvm_test_top.vsqr.p1_seq.s_b] completed :0 iteration
// UVM_INFO grab_top_4.sv(42) @ 100: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(94) @ 110: uvm_test_top.agent1.sqr@@p1_seq.s_a [uvm_test_top.vsqr.p1_seq.s_a] completed :1 iteration
// UVM_INFO grab_top_4.sv(42) @ 110: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(111) @ 120: uvm_test_top.agent1.sqr@@p1_seq.s_b [uvm_test_top.vsqr.p1_seq.s_b] completed :1 iteration
// UVM_INFO grab_top_4.sv(42) @ 120: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(94) @ 130: uvm_test_top.agent1.sqr@@p1_seq.s_a [uvm_test_top.vsqr.p1_seq.s_a] completed :2 iteration
// UVM_INFO grab_top_4.sv(42) @ 130: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(111) @ 140: uvm_test_top.agent1.sqr@@p1_seq.s_b [uvm_test_top.vsqr.p1_seq.s_b] completed :2 iteration
// UVM_INFO grab_top_4.sv(42) @ 140: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_A
// UVM_INFO grab_top_4.sv(94) @ 150: uvm_test_top.agent1.sqr@@p1_seq.s_a [uvm_test_top.vsqr.p1_seq.s_a] completed :3 iteration
// UVM_INFO grab_top_4.sv(185) @ 150: uvm_test_top.vsqr@@p1_seq [p1_seq] s_a completed
// UVM_INFO grab_top_4.sv(42) @ 150: uvm_test_top.agent1.drv [drv] Driving pkt inst:PUSH_B
// UVM_INFO grab_top_4.sv(111) @ 160: uvm_test_top.agent1.sqr@@p1_seq.s_b [uvm_test_top.vsqr.p1_seq.s_b] completed :3 iteration
// UVM_INFO grab_top_4.sv(190) @ 160: uvm_test_top.vsqr@@p1_seq [p1_seq] s_b completed
---------------------------------------------

Thursday, 16 February 2017

Difference between @event and wait(event.triggered) in SystemVerilog and usage of non-blocking event

The event data type provides a handle to a synchronization object.

There are two ways through which we can wait for particular event to be triggered.
So let's understand what is the exact difference between those 2 ways.

-----------------------------------------------
event e;
// Triggered event 'e'
->e;
// wait for event 'e' to be triggered (instantaneous)
@(e);
// wait for event 'e' to be triggered (Persistent)
wait(e.triggered);
view raw sv_event_1.sv hosted with ❤ by GitHub

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

An event trigger ->e is an instantaneous event. The event waiting process @ shall execute before the triggering process -> executes. If the trigger executes first, then the waiting process remains blocked.

The triggered event property evaluates to true (1'b1) if the given event has been triggered in the current time step and false (1'b0) otherwise.
Now you no longer have to worry which came first, the triggering process –> or the waiting process @ statement. But you still have to execute the waiting process @ in the current time slot to catch the event.

Let’s see the behavior with examples,
-----------------------------------------------
class event_class;
event ev;
task trig;
->ev;
endtask : trig
task execute1;
@(ev);
$display("Testing of the event @");
endtask : execute1
// First trigger event and after that
// wait for event to be triggered in same simulation time
task ex1;
trig;
execute1;
endtask : ex1
endclass : event_class
module top();
event_class E;
initial begin
E = new();
fork
begin
E.ex1; // wait using @
end
begin
#10;
$display("Missed trigger of event");
end
join_any
disable fork;
end
endmodule : top
// Output:
// Missed trigger of event
view raw sv_event_2.sv hosted with ❤ by GitHub

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

-----------------------------------------------
class event_class;
event ev;
task trig;
->ev;
endtask : trig
task execute2;
wait(ev.triggered);
$display("Testing of the event wait(.triggered)");
endtask : execute2
// First trigger event and after that
// wait for event to be triggered in same simulation time
task ex2;
trig;
execute2;
endtask : ex2
endclass : event_class
module top();
event_class E;
initial begin
E = new();
fork
begin
E.ex2; // wait using wait(event.triggered)
end
begin
#10;
$display("Missed trigger of event");
end
join_any
disable fork;
end
endmodule : top
// Output:
// Testing of the event wait(.triggered)
view raw sv_event_3.sv hosted with ❤ by GitHub

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

The other workaround is to use nonblocking event

Non-blocking event ->>
Nonblocking events are triggered using the ->> operator.

The effect of the ->> operator is that the statement executes without blocking, and it creates a nonblocking assign update event in the time in which the delay control expires or the event control occurs. The effect of this update event shall be to trigger the referenced event in the nonblocking assignment region of the simulation cycle.

-----------------------------------------------
module top();
event blocking_ev;
event non_blocking_ev;
process process_b;
process process_nb;
initial begin
fork
begin
process_b = process::self();
-> blocking_ev;
@ blocking_ev;
end
begin
process_nb = process::self();
->> non_blocking_ev;
@ non_blocking_ev;
end
join_none
#10;
$display("process_b.status:%s", process_b.status);
$display("process_nb.status:%s", process_nb.status);
end
endmodule : top
// Output:
// process_b.status:WAITING
// process_nb.status:FINISHED
-----------------------------------------------