Universal Verification Methodology (UVM) provides a robust mechanism for verification engineers to develop reusable and scalable testbenches. One powerful feature of UVM is the callback mechanism, which allows dynamic modification of testbench behavior without altering the original codebase. Please refer systemverilog callback to have a better understanding.
UVM callbacks are a way to insert custom behavior into pre-existing components dynamically. They enable greater flexibility by allowing testbench modifications without directly changing the UVM component code. For example, in a UVM driver, callbacks can be used to alter transaction handling, inject errors, or monitor signals dynamically.

UVM Callback Macros:
UVM provides macros that simplify the definition and use of callbacks:
`uvm_register_cb(TYP, CB_TYP)
`uvm_register_cb(CLASS, CALLBACK_CLASS)
CLASSis the UVM component that will invoke the callbacks.CALLBACK_CLASSis the callback class containing user-defined methods.
This macro automatically defines and instantiates the UVM callback mechanism for a given component.
`uvm_do_callbacks(TYP, CB_TYP, METHOD_NAME, ARGUMENTS…)
- Executes all registered callbacks for
TYP, calling the methodMETHOD_NAME.
These macros reduce the amount of boilerplate code required and ensure consistency in callback implementation.
UVM Callback Classes
A UVM callback class is an extension of uvm_callback. This class defines methods that can be overridden to modify behavior. uvm_callback is inherited by uvm_object.
Example Callback Class Definition:
class my_driver_callback extends uvm_callback;
`uvm_object_utils(my_driver_callback)
// Custom method for modifying driver behavior
virtual function void pre_drive(uvm_sequence_item txn);
`uvm_info("CALLBACK", "pre_drive method in callback", UVM_MEDIUM)
endfunction
endclass
- This class extends
uvm_callback, making it a valid callback object. - The
pre_drivemethod can be implemented in different ways by different callback instances.
UVM Callback Methods
UVM components that support callbacks typically provide methods for managing them:
- Adding a Callback
uvm_callbacks#(my_driver, my_driver_callback)::add(driver_inst, callback_inst);
Registers a my_driver_callback instance to modify my_driver behavior.
- Removing a Callback
uvm_callbacks#(my_driver, my_driver_callback)::delete(driver_inst, callback_inst);
Removes a registered callback instance.
- Executing Callbacks in a Component
`uvm_do_callbacks(my_driver, my_driver_callback, pre_drive, txn);
Calls pre_drive() for all registered callback instances associated with my_driver.
Example:
Let’s take a simple example to see how the callback works in real time scenario. We will define some functions in callback class and these functions will be called by the driver. We can make changes according to our needs in callback functions which can alter the result of the driver without changing the original code of driver.
In this example, we are taking a simple transaction class with ‘data’ field and sending it through the driver. Let’s go step by step.
- Callback Class (my_callback):
- Defines
pre_send()andpost_send()virtual functions. - These functions will be called by the driver.
- Uses
uvm_object_utilsfor UVM object functionalities.
- Defines
class my_callback extends uvm_callback;
virtual function void pre_send(int data);
`uvm_info("MY_CALLBACK", $sformatf("Pre-send callback called with data: %0d", data), UVM_LOW)
endfunction
virtual function void post_send(int data);
`uvm_info("MY_CALLBACK", $sformatf("Post-send callback called with data: %0d", data), UVM_LOW)
endfunction
`uvm_object_utils(my_callback)
function new(string name = "my_callback");
super.new(name);
endfunction
endclass
- Transaction (my_transaction):
- A simple transaction class with a
datafield. - Uses
uvm_object_utils_begin/endanduvm_field_intfor UVM object and field automation.
- A simple transaction class with a
- Driver (my_driver):
- Retrieves transactions from the sequencer.
- Callback Execution:
- Uses `
uvm_do_callbacks#(my_driver, my_callback, functions) to get the registered callback instances. - Calls
pre_send()before sending data. - Calls
post_send()after sending data.
- Uses `
- Simulates the sending of the data with a delay.
class my_driver extends uvm_driver#(my_transaction);
`uvm_component_utils(my_driver)
`uvm_register_cb(my_driver,my_callback)
my_callback cb;
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
my_transaction req;
forever begin
seq_item_port.get_next_item(req);
// Pre-send callback
`uvm_do_callbacks(my_driver,my_callback,pre_send(req.data));
`uvm_info("MY_DRIVER", $sformatf("Driver sending data: %0d", req.data), UVM_LOW);
//post-send callback
`uvm_do_callbacks(my_driver,my_callback,post_send(req.data));
seq_item_port.item_done();
end
endtask
endclass
- Sequencer (my_sequencer):
- Standard UVM sequencer.
- Sequence (my_sequence):
- Generates random transactions and sends them to the sequencer.
- Agent (my_agent):
- Instantiates the driver and sequencer.
- Connects the driver and sequencer ports.
- Environment (my_env):
- Instantiates the agent.
- Test (my_test):
- Instantiates the environment and sequence.
- Creates an instance of the callback class.
- Callback Registration:
- Uses
uvm_callbacks#(my_driver, my_callback)::add(env.agt.drv, my_cb_instance);to register the callback instance with the driver.
- Uses
- Starts the sequence.
class my_test extends uvm_test;
my_env env;
my_sequence seq;
my_callback my_cb_instance;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
seq = my_sequence::type_id::create("seq");
my_cb_instance = my_callback::type_id::create("my_cb_instance",this);
// uvm_callbacks#(my_driver, my_callback)::add(env.agt.drv, my_cb_instance);
endfunction
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_callbacks#(my_driver, my_callback)::add(env.agt.drv, my_cb_instance);
endfunction
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
seq.start(env.agt.seqr);
phase.drop_objection(this);
endtask
endclass
- Top Module (top):
- Runs the test.
Output:
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO sequence.sv(16) @ 0: uvm_test_top.env.agt.seqr@@seq [MY_SEQUENCE] Sequence sending data: -517620205
UVM_INFO driver_callback.sv(4) @ 0: reporter [MY_CALLBACK] Pre-send callback called with data: -517620205
UVM_INFO driver.sv(19) @ 0: uvm_test_top.env.agt.drv [MY_DRIVER] Driver sending data: -517620205
UVM_INFO driver_callback.sv(8) @ 0: reporter [MY_CALLBACK] Post-send callback called with data: -517620205
UVM_INFO sequence.sv(16) @ 0: uvm_test_top.env.agt.seqr@@seq [MY_SEQUENCE] Sequence sending data: 774544662
UVM_INFO driver_callback.sv(4) @ 0: reporter [MY_CALLBACK] Pre-send callback called with data: 774544662
UVM_INFO driver.sv(19) @ 0: uvm_test_top.env.agt.drv [MY_DRIVER] Driver sending data: 774544662
UVM_INFO driver_callback.sv(8) @ 0: reporter [MY_CALLBACK] Post-send callback called with data: 774544662
UVM_INFO sequence.sv(16) @ 0: uvm_test_top.env.agt.seqr@@seq [MY_SEQUENCE] Sequence sending data: 1471791918
UVM_INFO driver_callback.sv(4) @ 0: reporter [MY_CALLBACK] Pre-send callback called with data: 1471791918
UVM_INFO driver.sv(19) @ 0: uvm_test_top.env.agt.drv [MY_DRIVER] Driver sending data: 1471791918
UVM_INFO driver_callback.sv(8) @ 0: reporter [MY_CALLBACK] Post-send callback called with data: 1471791918
UVM_INFO sequence.sv(16) @ 0: uvm_test_top.env.agt.seqr@@seq [MY_SEQUENCE] Sequence sending data: 459431653
UVM_INFO driver_callback.sv(4) @ 0: reporter [MY_CALLBACK] Pre-send callback called with data: 459431653
UVM_INFO driver.sv(19) @ 0: uvm_test_top.env.agt.drv [MY_DRIVER] Driver sending data: 459431653
UVM_INFO driver_callback.sv(8) @ 0: reporter [MY_CALLBACK] Post-send callback called with data: 459431653
UVM_INFO sequence.sv(16) @ 0: uvm_test_top.env.agt.seqr@@seq [MY_SEQUENCE] Sequence sending data: -377759977
UVM_INFO driver_callback.sv(4) @ 0: reporter [MY_CALLBACK] Pre-send callback called with data: -377759977
UVM_INFO driver.sv(19) @ 0: uvm_test_top.env.agt.drv [MY_DRIVER] Driver sending data: -377759977
UVM_INFO driver_callback.sv(8) @ 0: reporter [MY_CALLBACK] Post-send callback called with data: -377759977
UVM_INFO /apps/vcsmx/vcs/U-2023.03-SP2//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 0: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /apps/vcsmx/vcs/U-2023.03-SP2//etc/uvm-1.2/src/base/uvm_report_server.svh(904) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 23
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[MY_CALLBACK] 10
[MY_DRIVER] 5
[MY_SEQUENCE] 5
[RNTST] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
$finish called from file "/apps/vcsmx/vcs/U-2023.03-SP2//etc/uvm-1.2/src/base/uvm_root.svh", line 527.
$finish at simulation time

