HomeProblemsTheoryUVM
Advanced3 min readChapter 13

Callbacks

Extend component behavior without modifying source code using the UVM callback mechanism.

UVM Callbacks

Callbacks let you inject custom behavior into existing components without modifying their source code. They're a lightweight alternative to factory overrides.


The Idea

Think of callbacks as hooks in a component. The component says: "At this point, I'll call any registered callbacks." You provide the callback implementation separately.


Step 1: Define the Callback Interface

systemverilog
class driver_callback extends uvm_callback; `uvm_object_utils(driver_callback) function new(string name = "driver_callback"); super.new(name); endfunction // Hook: called before driving a transaction virtual task pre_drive(my_driver drv, my_txn txn); // Default: do nothing endtask // Hook: called after driving a transaction virtual task post_drive(my_driver drv, my_txn txn); // Default: do nothing endtask endclass

Step 2: Add Hooks to the Component

systemverilog
class my_driver extends uvm_driver #(my_txn); `uvm_component_utils(my_driver) `uvm_register_cb(my_driver, driver_callback) // ... build_phase, etc. ... task run_phase(uvm_phase phase); my_txn req; forever begin seq_item_port.get_next_item(req); // ── Fire pre-drive callbacks ── `uvm_do_callbacks(my_driver, driver_callback, pre_drive(this, req)) drive_item(req); // ── Fire post-drive callbacks ── `uvm_do_callbacks(my_driver, driver_callback, post_drive(this, req)) seq_item_port.item_done(); end endtask endclass

Step 3: Implement and Register a Callback

systemverilog
class error_inject_cb extends driver_callback; `uvm_object_utils(error_inject_cb) int error_count = 0; function new(string name = "error_inject_cb"); super.new(name); endfunction // Flip a random bit with 5% probability task pre_drive(my_driver drv, my_txn txn); if ($urandom_range(100) < 5) begin txn.data[0] = ~txn.data[0]; error_count++; `uvm_info("ERR_CB", $sformatf( "Injected error #%0d on data=0x%h", error_count, txn.data ), UVM_LOW) end endtask endclass // In the test: class error_test extends base_test; `uvm_component_utils(error_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); error_inject_cb err_cb; err_cb = error_inject_cb::type_id::create("err_cb"); uvm_callbacks#(my_driver, driver_callback)::add( env.agent.driver, err_cb ); endfunction endclass

Callbacks vs Factory Overrides

AspectCallbacksFactory Overrides
ScopeAdd behavior at hook pointsReplace entire component
StackingMultiple callbacks can stackOnly one override active
InvasivenessMinimal — just add hooksMust extend the class
Best forLogging, error injection, coverageDifferent driver logic

Common Use Cases

Use CaseExample
Error injectionCorrupt data, drop transactions
CoverageSample coverage at specific points
Protocol checkingVerify rules inline
LoggingLog every transaction without modifying driver
PerformanceMeasure latency between events

Best Practices

  1. Define callbacks with virtual empty methods — safe defaults.
  2. Place hooks at natural boundaries — pre/post drive, pre/post compare.
  3. Keep callbacks lightweight — no complex logic.
  4. Multiple callbacks execute in registration order.
  5. Use \uvm_register_cb to declare which callbacks a component supports.