Tutorials
UVM CALLBACK
Callback mechanism is used for altering the behavior of the transactor without modifying the transactor. One of the many promises of Object-Oriented programming is that it will allow for plug-and-play re-usable verification components. Verification Designers will hook the transactors together to make a verification environment. In SystemVerilog, this hooking together of transactors can be tricky. Callbacks provide a mechanism whereby independently developed objects may be connected together in simple steps.
This article describes uvm callbacks. uvm callback might be used for simple notification, two-way communication, or to distribute work in a process. Some requirements are often unpredictable when the transactor is first written. So a transactor should provide some kind of hooks for executing the code which is defined afterwards. In uvm, these hooks are created using callback methods. For instance, a driver is developed and an empty method is called before driving the transaction to the DUT. Initially this empty method does nothing. As the implementation goes, user may realize that he needs to print the state of the transaction or to delay the transaction driving to DUT or inject an error into transaction. Callback mechanism allows executing the user defined code in place of the empty callback method. Other example of callback usage is in monitor. Callbacks can be used in a monitor for collecting coverage information or for hooking up to scoreboard to pass transactions for self checking. With this, user is able to control the behavior of the transactor in verification environment and individual testcases without doing any modifications to the transactor itself.
Following are the steps to be followed to create a transactor with callbacks. We will see simple example of creating a Driver transactor to support callback mechanism.
Stpe 1) Define a facade class.
1) Extend the uvm_callback class to create a faced class.
class Driver_callback
extends uvm_callback
;
endclass : Driver_callback
2)Define required callback methods. All the callback methods must be virtual.
In this example, we will create callback methods which will be called before driving the packet and after driving the packet to DUT.
virtual task pre_send
(); endtask
virtual task post_send
(); endtask
3)Define the constructor and get_type_name methods and define type_name.
function new (string name = "Driver_callback");
super.new(name);
endfunction
static string type_name
= "Driver_callback";
virtual function string get_type_name
();
return type_name
;
endfunction
Step 2) Register the facade class with driver and call the callback methods.
1)In the driver class, using `uvm_register_cb() macro, register the facade class.
class Driver
extends uvm_component
;
`uvm_component_utils(Driver
)
`uvm_register_cb(Driver
,Driver_callback
)
function new (string name, uvm_component parent
=null);
super.new(name,parent
);
endfunction
endclass
2)Calling callback method.
Inside the transactor, callback methods should be called whenever something interesting happens.
We will call the callback method before driving the packet and after driving the packet. We defined 2 methods in facade class. We will call pre_send() method before sending the packet and post_send() method after sending the packet.
Using a `uvm_do_callbacks() macro, callback methods are called.
There are 3 argumentd to `uvm_do_callbacks(,) macro.
First argument must be the driver class and second argument is facade class.
Third argument must be the callback method in the facade class.
To call pre_send() method , use macro
`uvm_do_callbacks(Driver,Driver_callback,pre_send());
and similarly to call post_send() method,
`uvm_do_callbacks(Driver,Driver_callback,post_send());
Place the above macros before and after driving the packet.
virtual task run
();
repeat(2) begin
`uvm_do_callbacks(Driver
,Driver_callback
,pre_send
())
$display(" Driver: Started Driving the packet ...... %d",$time);
// Logic to drive the packet goes hear
// let's consider that it takes 40 time units to drive a packet.
#40;
$display(" Driver: Finished Driving the packet ...... %d",$time);
`uvm_do_callbacks(Driver
,Driver_callback
,post_send
())
end
endtask
With this, the Driver implementation is completed with callback support.
Driver And Driver Callback Class Source Code
class Driver_callback
extends uvm_callback
;
function new (string name = "Driver_callback");
super.new(name);
endfunction
static string type_name
= "Driver_callback";
virtual function string get_type_name
();
return type_name
;
endfunction
virtual task pre_send
(); endtask
virtual task post_send
(); endtask
endclass : Driver_callback
class Driver
extends uvm_component
;
`uvm_component_utils(Driver
)
`uvm_register_cb(Driver
,Driver_callback
)
function new (string name, uvm_component parent
=null);
super.new(name,parent
);
endfunction
virtual task run
();
repeat(2) begin
`uvm_do_callbacks(Driver
,Driver_callback
,pre_send
())
$display(" Driver: Started Driving the packet ...... %d",$time);
// Logic to drive the packet goes hear
// let's consider that it takes 40 time units to drive a packet.
#40;
$display(" Driver: Finished Driving the packet ...... %d",$time);
`uvm_do_callbacks(Driver
,Driver_callback
,post_send
())
end
endtask
endclass
Let's run the driver in simple testcase. In this testcase, we are not changing any callback methods definitions.
Testcase Source Code
module test
;
Driver drvr
;
initial begin
drvr
= new("drvr");
run_test
();
end
endmodule
(S) Download files
uvm_callback_1.tar
Browse the code in uvm_callback_1.tar
(S) Command to run the simulation
VCS Users : make vcs
Questa Users: make questa
(S) Log report
UVM_INFO @ 0: reporter [RNTST] Running test ...
Driver: Started Driving the packet ...... 0
Driver: Finished Driving the packet ...... 40
Driver: Started Driving the packet ...... 40
Driver: Finished Driving the packet ...... 80
UVM_ERROR @ 9200: reporter [TIMOUT] Watchdog timeout of '9200' expired.
Following steps are to be performed for using callback mechanism to do required functionality.
We will see how to use the callbacks which are implemented in the above defined driver in a testcase.
1) Implement the user defined callback method by extending facade class of the driver class.
We will delay the driving of packet be 20 time units using the pre_send() call back method.
We will just print a message from post_send() callback method.
class Custom_Driver_callbacks_1
extends Driver_callback
;
function new (string name = "Driver_callback");
super.new(name);
endfunction
virtual task pre_send
();
$display("CB_1:pre_send: Delaying the packet driving by 20 time units. %d",$time);
#20;
endtask
virtual task post_send
();
$display("CB_1:post_send: Just a message from post send callback method \n");
endtask
endclass
2) Construct the user defined facade class object.
Custom_Driver_callbacks_1 cb_1
;
cb_1
= new("cb_1");
3) Register the callback method with the driver component. uvm_callback class has static method add() which is used to register the callback.
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_1
);
Testcase 2 Source Code
class Custom_Driver_callbacks_1
extends Driver_callback
;
function new (string name = "Driver_callback");
super.new(name);
endfunction
virtual task pre_send
();
$display("CB_1:pre_send: Delaying the packet driving by 20 time units. %d",$time);
#20;
endtask
virtual task post_send
();
$display("CB_1:post_send: Just a message from post send callback method \n");
endtask
endclass
module test
;
initial begin
Driver drvr
;
Custom_Driver_callbacks_1 cb_1
;
drvr
= new("drvr");
cb_1
= new("cb_1");
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_1
);
uvm_callbacks
#(Driver
,Driver_callback
)::display
();
run_test
();
end
endmodule
(S) Download the example
uvm_callback_2.tar
Browse the code in uvm_callback_2.tar
(S) Simulation Command
VCS Users : make vcs
Questa Users: make questa
Run the testcase. See the log results; We delayed the driving of packet by 20 time units using callback mechanism. See the difference between the previous testcase log and this log.
(S) Log report
cb_1 on drvr ON
UVM_INFO @ 0: reporter [RNTST] Running test ...
CB_1:pre_send: Delaying the packet driving by 20 time units. 0
Driver: Started Driving the packet ...... 20
Driver: Finished Driving the packet ...... 60
CB_1:post_send: Just a message from post send callback method
CB_1:pre_send: Delaying the packet driving by 20 time units. 60
Driver: Started Driving the packet ...... 80
Driver: Finished Driving the packet ...... 120
CB_1:post_send: Just a message from post send callback method
UVM_ERROR @ 9200: reporter [TIMOUT] Watchdog timeout of '9200' expired.
Now we will see registering 2 callback methods.
1) Define another user defined callback methods by extending facade class.
class Custom_Driver_callbacks_2
extends Driver_callback
;
function new (string name = "Driver_callback");
super.new(name);
endfunction
virtual task pre_send
();
$display("CB_2:pre_send: Hai .... this is from Second callback %d",$time);
endtask
endclass
2) Construct the user defined facade class object.
Custom_Driver_callbacks_2 cb_2
;
cb_2
= new("cb_2");
3) Register the object
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_2
);
Testcase 3 Source Code
class Custom_Driver_callbacks_1
extends Driver_callback
;
function new (string name = "Driver_callback");
super.new(name);
endfunction
virtual task pre_send
();
$display("CB_1:pre_send: Delaying the packet driving by 20 time units. %d",$time);
#20;
endtask
virtual task post_send
();
$display("CB_1:post_send: Just a message from post send callback method \n");
endtask
endclass
class Custom_Driver_callbacks_2
extends Driver_callback
;
function new (string name = "Driver_callback");
super.new(name);
endfunction
virtual task pre_send
();
$display("CB_2:pre_send: Hai .... this is from Second callback %d",$time);
endtask
endclass
module test
;
initial begin
Driver drvr
;
Custom_Driver_callbacks_1 cb_1
;
Custom_Driver_callbacks_2 cb_2
;
drvr
= new("drvr");
cb_1
= new("cb_1");
cb_2
= new("cb_2");
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_1
);
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_2
);
uvm_callbacks
#(Driver
,Driver_callback
)::display
();
run_test
();
end
endmodule
(S) Download source code
uvm_callback_3.tar
Browse the code in uvm_callback_3.tar
(S) Command to run the simulation
VCS Users : make vcs
Questa Users: make questa
Run the testcase and analyze the result.
(S) Log report
UVM_INFO @ 0: reporter [RNTST] Running test ...
CB_1:pre_send: Delaying the packet driving by 20 time units. 0
CB_2:pre_send: Hai .... this is from Second callback 20
Driver: Started Driving the packet ...... 20
Driver: Finished Driving the packet ...... 60
CB_1:post_send: Just a message from post send callback method
CB_1:pre_send: Delaying the packet driving by 20 time units. 60
CB_2:pre_send: Hai .... this is from Second callback 80
Driver: Started Driving the packet ...... 80
Driver: Finished Driving the packet ...... 120
CB_1:post_send: Just a message from post send callback method
UVM_ERROR @ 9200: reporter [TIMOUT] Watchdog timeout of '9200' expired.
The log results show that pre_send() method of CDc_1 is called first and then pre_send() method of Cdc_2. This is because of the order of the registering callbacks.
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_1
);
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_2
);
Now we will see how to change the order of the callback method calls.
By changing the sequence of calls to add() method, order of callback method calling can be changed.
Testcase 4 Source Code
module test
;
initial begin
Driver drvr
;
Custom_Driver_callbacks_1 cb_1
;
Custom_Driver_callbacks_2 cb_2
;
drvr
= new("drvr");
cb_1
= new("cb_1");
cb_2
= new("cb_2");
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_2
);
uvm_callbacks
#(Driver
,Driver_callback
)::add
(drvr
,cb_1
);
uvm_callbacks
#(Driver
,Driver_callback
)::display
();
run_test
();
end
endmodule
(S) Download the source code
uvm_callback_4.tar
Browse the code in uvm_callback_4.tar
(S) Command to run the simulation
VCS Users : make vcs
Questa Users: make questa
Run and analyze the results.
Log results show that, pre_send() method of CDs_1 is called after calling CDs_2 pre_send() method.
(S) Log file report
UVM_INFO @ 0: reporter [RNTST] Running test ...
CB_2:pre_send: Hai .... this is from Second callback 0
CB_1:pre_send: Delaying the packet driving by 20 time units. 0
Driver: Started Driving the packet ...... 20
Driver: Finished Driving the packet ...... 60
CB_1:post_send: Just a message from post send callback method
CB_2:pre_send: Hai .... this is from Second callback 60
CB_1:pre_send: Delaying the packet driving by 20 time units. 60
Driver: Started Driving the packet ...... 80
Driver: Finished Driving the packet ...... 120
CB_1:post_send: Just a message from post send callback method
UVM_ERROR @ 9200: reporter [TIMOUT] Watchdog timeout of '9200' expired.
Methods:
(S)add_by_name:
We have seen, the usage of add() method which requires object.
Using add_by_name() method, callback can be registered with object name.
static function void add_by_name
(string name,
uvm_callback cb
,
uvm_component root
,
uvm_apprepend ordering
= UVM_APPEND)
(S)delete:
uvm also provides uvm_callbacks::delete() method to remove the callback methods which are registered.
Similar to delete, delete_by_name() method is used to remove the callback using the object name.
static function void delete_by_name
(string name,
uvm_callback cb
,
uvm_component root
)
Macros:
`uvm_register_cb
Registers the given CB callback type with the given T object type.
`uvm_set_super_type
Defines the super type of T to be ST.
`uvm_do_callbacks
Calls the given METHOD of all callbacks of type CB registered with the calling object
`uvm_do_obj_callbacks
Calls the given METHOD of all callbacks based on type CB registered with the given object, OBJ, which is or is based on type T.
`uvm_do_callbacks_exit_on
Calls the given METHOD of all callbacks of type CB registered with the calling object
`uvm_do_obj_callbacks_exit_on
Calls the given METHOD of all callbacks of type CB registered with the given object OBJ, which must be or be based on type T, and returns upon the first callback that returns the bit value given by VAL.