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.
-------------------------------------------------------------------
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`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 |
-------------------------------------------------------------------
-------------------------------------------------------------------
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------------- | |
// 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 |
-------------------------------------------------------------------
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.
-------------------------------------------------------------------
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------------- | |
// 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 |
-------------------------------------------------------------------
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
-------------------------------------------------------------------
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
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.