The short answer: UVM callbacks let a test add or change behaviour at predefined points inside a component, such as just before a driver sends a transaction, without editing or overriding the component. The component author defines a callback class with empty virtual methods, registers it with `uvm_register_cb and calls the hooks with `uvm_do_callbacks. A test extends the callback class and attaches an instance with uvm_callbacks::add. Typical uses are error injection, extra checks and statistics.
Callbacks and factory overrides solve similar problems. Overrides replace a whole class; callbacks insert small pieces of behaviour at agreed points and can be stacked. I use callbacks mostly for error injection in VIP that other people own.
- The five steps
- The component side
- The test side
- Callbacks or factory overrides?
- Tips
Table of Contents
Part 1: The five steps

Part 2: The component side
// 1) define the hook
class driver_cb extends uvm_callback;
`uvm_object_utils(driver_cb)
function new(string name = "driver_cb"); super.new(name); endfunction
virtual task pre_drive(bus_driver drv, bus_item req); endtask
virtual task post_drive(bus_driver drv, bus_item req); endtask
endclass
// 2) register and 3) call it in the driver
class bus_driver extends uvm_driver #(bus_item);
`uvm_component_utils(bus_driver)
`uvm_register_cb(bus_driver, driver_cb)
...
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
`uvm_do_callbacks(bus_driver, driver_cb, pre_drive(this, req))
drive(req);
`uvm_do_callbacks(bus_driver, driver_cb, post_drive(this, req))
seq_item_port.item_done();
end
endtask
endclass
A forward typedef of bus_driver is needed before the callback class if they’re in the same file.
Part 3: The test side
// 4) extend
class corrupt_cb extends driver_cb;
`uvm_object_utils(corrupt_cb)
int unsigned percent = 5;
function new(string name = "corrupt_cb"); super.new(name); endfunction
virtual task pre_drive(bus_driver drv, bus_item req);
if ($urandom_range(99) < percent) begin
req.data ^= 32'h1;
`uvm_info("CB", "corrupted one data bit", UVM_MEDIUM)
end
endtask
endclass
// 5) add, in the test (after the env is built)
function void connect_phase(uvm_phase phase);
corrupt_cb cb = corrupt_cb::type_id::create("cb");
uvm_callbacks#(bus_driver, driver_cb)::add(env.agt_in.drv, cb);
endfunction
Remember to tell the scoreboard about deliberate corruption, or every injected error becomes a mismatch.
Part 4: Callbacks or factory overrides?
| Callbacks | Factory override | |
|---|---|---|
| Granularity | Small hooks at defined points | The whole class |
| Needs the author to plan for it | Yes: hooks must exist | No: any virtual method can change |
| Can combine several | Yes, callbacks stack | One override per type or instance |
| Typical use | Error injection, extra checks, statistics | Different item types, alternative drivers |
Part 5: Tips
- Keep hooks few and meaningful: before and after the main action is usually enough.
- Pass the component and the transaction to the hook, so callbacks have context.
- Document every hook in the VIP; users can’t find callbacks they don’t know exist.
- Callbacks have a small performance cost; avoid hooks in tight per-cycle loops.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Design Verification problems · Daily challenge
Previous in my UVM series: The register model (RAL). Next: Reporting and messaging.
FAQ
What is a UVM callback?
A hook inside a component where a test can attach extra behaviour, without modifying or overriding the component.
What is the difference between callbacks and factory overrides?
Callbacks add behaviour at specific predefined points and can be stacked; overrides replace a whole class.
What does `uvm_register_cb do?
It declares that a component type supports a given callback type, so callbacks of that type can be attached to it.
When should callbacks be added?
After the component exists, typically in the test’s connect_phase or end_of_elaboration_phase.
