UVM Callbacks
The callbacks are used to alter the behavior of the component or object without modifying its code. Refer System Verilog callback to have a better understanding. A simple example of callbacks can be the phasing mechanism in UVM.
UVM Callback Usage
- Allows plug-and-play mechanism to establish a reusable verification environment.
- Based on the hook method call, the user-defined code is executed instead of the empty callback method. This brings various flavors of the component or object.
- Callbacks can be used to introduce errors or delays in the components.
UVM Callback Macros
Widely used callback macros alone are mentioned below:
Macros | Description |
`uvm_register_cb(T, CB) | Registers the user-defined callback which is extended from uvm_callback. T – Object type where user-defined callback is used and it must be derived from uvm_object. CB – user-defined callback type If a type-callback pair is not registered then a warning is issued for an attempt to call add, delete, etc methods. |
`uvm_do_callbacks(T, CB, METHOD) | Calls METHOD of user-defined callback class. T – Object type where user-defined callback is used and it must be derived from uvm_object. CB – user-defined callback type METHOD – callback method call to invoke. |
uvm_do_obj_callbacks(T, CB, OBJ, METHOD) | It is similar to `uvm_do_callbacks macro, but it has an additional OBJ argument to specify external object associated with the callback. Example: For applying callback in a sequence, OBJ could be specified as parent sequence or sequencer. |
UVM Callback Classes
The UVM callback provides a set of classes for its implementation.
Classes | Description |
uvm_callbacks | provides a base class for callback implementation and is typically used for modifying component behavior without modifying the component class. |
uvm_callback_iter | It is an iterator class for iterating over callback queues of a specific type. |
uvm_callback | Provides a base class for user-defined callback classes. |
UVM Callback Methods
The uvm_callback methods can be called using a scope resolution operator as they are static methods.
For example: uvm_callbacks#(T, CB)::add(obj,cb);
Where,
T : Object type where user-defined callback is used and it must be derived from uvm_object.
CB : user-defined callback type
obj : object handle where user-defined callback is used
cb : a user-defined callback object
Methods | Description |
add | Registers the given callback object with the given obj handle. |
add_by_name | Registers the given callback object with one or more uvm_components. |
delete | Deletes the given callback object. |
delete_by_name | Deletes the given callback object with one or more uvm_components. |
The uvm_callbacks, uvm_callback_iter, and uvm_callback classes have many other methods that are not discussed here, common methods and macros are described in the above section.
Steps to implement uvm_callback
1. Create a user-defined callback class that extends from the uvm_callback class.
class driver_cb extends uvm_callback;
2. Add an empty callback method
virtual task modify_pkt();
endtask
The driver_cb callback class code:
class driver_cb extends uvm_callback;
`uvm_object_utils(driver_cb)
function new(string name = "driver_cb");
super.new(name);
endfunction
virtual task modify_pkt();
endtask
endclass
3. Implement a callback method in the class which is extended from the above user-defined class.
class derived_cb extends driver_cb;
`uvm_object_utils(derived_cb)
function new(string name = "derived_cb");
super.new(name);
endfunction
task modify_pkt; // callback method implementation
`uvm_info(get_full_name(),"Inside modify_pkt method: Injecting error pkt",UVM_LOW);
std::randomize(pkt) with {pkt inside {BAD_ERR1, BAD_ERR2};};
endtask
endclass
4. Register user-defined callback method using `uvm_register_cb in the component or object where callbacks are called (In the below example, it is registered in the driver component).
`uvm_register_cb(driver,driver_cb)
If `uvm_register_cb is not used then a warning is issued.
Example:
UVM_WARNING @ 0: reporter [CBUNREG] Callback drvd_cb cannot be registered with object (*) because callback type derived_cb is not registered with object type uvm_object
5. Place callback hook i.e. calling callback method in the required component or object using `uvm_do_callbacks macro
`uvm_do_callbacks(driver,driver_cb,modify_pkt());
Driver component code:
typedef enum {GOOD, BAD_ERR1, BAD_ERR2} pkt_type;
class driver extends uvm_component;
`uvm_component_utils(driver)
`uvm_register_cb(driver,driver_cb) // callback registration
function new(string name = "driver", uvm_component parent = null);
super.new(name,parent);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
drive();
`uvm_do_callbacks(driver,driver_cb,modify_pkt()); // callback hook
endtask
task drive();
`uvm_info(get_full_name(),"Inside drive method",UVM_LOW);
std::randomize(pkt) with {pkt == GOOD;};
endtask
endclass
Output:
1. When base_test is executed (Run Options: +UVM_TESTNAME=base_test)
class base_test extends uvm_test;
`uvm_component_utils(base_test)
env env_o;
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_o = env::type_id::create("env_o", this);
endfunction
endclass
A GOOD packet is generated as mentioned below.
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO driver.sv(17) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Inside drive method
UVM_INFO driver.sv(13) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Driven pkt is GOOD
2. When err_test is executed (Run Options: +UVM_TESTNAME=err_test)
class err_test extends base_test;
derived_cb drvd_cb;
`uvm_component_utils(err_test)
function new(string name = "err_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drvd_cb = derived_cb::type_id::create("drvd_cb", this);
uvm_callbacks#(driver, driver_cb)::add(env_o.drv, drvd_cb);
endfunction
endclass
Using UVM callback, an error packet is generated as shown below.
VM_INFO @ 0: reporter [RNTST] Running test err_test...
UVM_INFO driver.sv(17) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Inside drive method
UVM_INFO callbacks.sv(20) @ 0: reporter [drvd_cb] Inside modify_pkt method: Injecting error pkt
UVM_INFO driver.sv(13) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Driven pkt is BAD_ERR2
UVM callback in uvm_sequence
The steps mentioned to implement callback in uvm_sequence are the same as above.
Note that `uvm_do_obj_callbacks macro is used as a callback hook with associated sequencer.
`uvm_do_obj_callbacks(sequencer,seq_cb,l_seqr,modify_pkt(req));
Where l_seqr handle points to object handle of sequencer class. Instead of this approach, p_seqeuncer can also be used here and p_sequencer must be declared using `uvm_declare_p_sequencer macro.
`uvm_do_obj_callbacks(sequencer,seq_cb,p_sequencer,modify_pkt(req));
UVM callback in uvm_sequence example
class base_seq extends uvm_sequence #(seq_item);
seq_item req;
sequencer l_seqr; // Provided sequencer hierarchy from base_test before starting the sequence.
`uvm_object_utils(base_seq)
function new (string name = "base_seq");
super.new(name);
endfunction
task body();
`uvm_info(get_type_name(), "Base seq: Inside Body", UVM_LOW);
req = seq_item::type_id::create("req");
wait_for_grant();
assert(req.randomize());
`uvm_do_obj_callbacks(sequencer,seq_cb,l_seqr,modify_pkt(req));
send_request(req);
wait_for_item_done();
endtask
endclass
Output:
1.When base_test is executed (Run Options: +UVM_TESTNAME=base_test)
class base_test extends uvm_test;
env env_o;
base_seq bseq;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_o = env::type_id::create("env_o", this);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
bseq = base_seq::type_id::create("bseq");
bseq.l_seqr = env_o.agt.seqr;
bseq.start(env_o.agt.seqr);
phase.drop_objection(this);
endtask
endclass
A GOOD packet is generated as mentioned below.
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO sequences.sv(11) @ 0: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(15) @ 0: uvm_test_top.env_o.agt.drv [uvm_test_top.env_o.agt.drv] Driving pkt =
------------------------------
Name Type Size Value
------------------------------
req seq_item - @578
addr integral 16 'he157
data integral 16 'h96b
pkt pkt_type 32 GOOD
------------------------------
UVM_INFO /apps/vcsmx/vcs/Q-2020.03-SP1-1//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
2. When err_test is executed (Run Options: +UVM_TESTNAME=err_test)
class err_test extends base_test;
derived_seq_cb drvd_seq;
`uvm_component_utils(err_test)
function new(string name = "err_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drvd_seq = derived_seq_cb::type_id::create("drvd_seq", this);
endfunction
function void end_of_elaboration();
super.end_of_elaboration();
uvm_callbacks#(sequencer, seq_cb)::add(env_o.agt.seqr,drvd_seq);
endfunction : end_of_elaboration
endclass
Using UVM callback, an error packet is generated as shown below.
UVM_INFO @ 0: reporter [RNTST] Running test err_test...
UVM_INFO sequences.sv(11) @ 0: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO callbacks.sv(20) @ 0: reporter [drvd_seq] Inside modify_pkt method: Injecting error in the seq item
--------------------------------
Name Type Size Value
--------------------------------
req seq_item - @580
addr integral 16 'hffff
data integral 16 'h96b
pkt pkt_type 32 BAD_ERR1
--------------------------------
UVM_INFO driver.sv(15) @ 0: uvm_test_top.env_o.agt.drv [uvm_test_top.env_o.agt.drv] Driving pkt =
--------------------------------
Name Type Size Value
--------------------------------
req seq_item - @580
addr integral 16 'hffff
data integral 16 'h96b
pkt pkt_type 32 BAD_ERR1
--------------------------------
UVM_INFO /apps/vcsmx/vcs/Q-2020.03-SP1-1//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM Tutorials