What is a callback ?
A callback lets you give a function to someone else’s program. When that program reaches a certain point or condition, it "calls back" your function and runs it, without needing to know exactly what your function does. It’s a way for different parts of a system to work together more flexibly, without being tightly connected or dependent on each other.
What is a Systemverilog callback ?
In Systemverilog, a class method is written to call placeholder methods. When needed, the user can extend the class and implement these placeholder methods. Here, the placeholder methods are the callback methods, and the calls to these methods act as callback hooks.
The major benefit of callbacks is decoupling of components. A verification environment, can register callbacks that can be executed during the simulation without modifying the original design.
Steps to create a callback
1. Callback Class: The callback functionality is usually implemented in a class where certain methods are defined. These methods can be overridden or extended by other classes.
class MyCallback;
virtual function void callback_function();
// Default implementation (optional)
endfunction
endclass
2. Registration of Callbacks: To use callbacks, the class containing the method must register the callback object with the system. This allows the system to store a reference to the method that will be called later.
class MyTest;
MyCallback cb;
// 2. Registration of callback
function void register_callback(MyCallback callback);
cb = callback;
endfunction
function void execute();
if (cb != null) begin
cb.callback_function();
end
endfunction
endclass
3. Implementation of Callbacks: The functionality that has to be executed when invoking the callback has to be implemented in a child class.
// 3. Callback implementation
class UserCallback extends MyCallback;
function void callback_function();
$display("User-defined callback called!");
endfunction
endclass
4. Invoking the Callback: When an event or condition occurs in the simulation, the callback is invoked by calling the registered methods. The invoking class doesn’t know the details of what the callback will do, allowing for flexible behavior.
module tb;
initial begin
MyTest test;
UserCallback user_cb;
test = new;
user_cb = new;
// Register user-defined callback
test.register_callback(user_cb);
// 4. Execute which will invoke the callback
test.execute();
end
endmodule
- MyCallback class defines a virtual function callback_function().
- MyTest class has a function to register a callback and later executes the callback function when needed.
- UserCallback extends MyCallback and overrides the callback_function() method.
- The testbench registers the UserCallback object and calls execute(), which invokes the registered callback function.
xcelium> run User-defined callback called! xmsim: *W,RNQUIE: Simulation is complete. xcelium> exit
How is it different from regular methods ?
Callbacks are designed to be dynamically registered and invoked during runtime. This means you can change the behavior of your system without modifying the original code. However, regular functions or tasks are typically called directly from the code where they are defined, and their behavior is fixed unless you rewrite them.
A callback is usually registered by one part of the code and then called by another part. The calling part doesn’t need to know what the callback will do—it just calls whatever was registered. But, a regular function or task is explicitly called by its name at a specific point in the code.
Callbacks promote loose coupling between components. The module that invokes the callback only needs to know that a callback exists; it doesn’t need to know the details of the implementation. But regular functions or tasks are tightly coupled. The calling code has direct knowledge of the function or task it’s invoking.
How regular methods can be used like a callback ?
Empty tasks can be positioned at key points within the code, allowing new code to be added to those spots later on.
For example, pre_err_callback and post_err_callback are two empty methods defined in base test which are then overridden in a child class to implement some functionality to executed before and after error injection. So, these two methods were just placeholders in the base code to allow it to be enhanced or modified to some extent without touching the base class.
class MyTest;
...
virtual task body();
// some statements
pre_err_callback();
err_inj_seq();
post_err_callback();
// more statements
endtask
virtual task pre_err_callback();
// Empty callback
endtask
virtual task post_err_callback();
// Empty callback
endtask
endclass
class MyTestExt extends MyTest;
virtual task pre_err_callback();
// Implement things to be done before error injection
endtask
virtual task post_err_callback();
// Implement things to be done after error injection
endtask
endclass