Callbacks (Error Injection)

The "Open" mechanism to modify component behavior (like Drivers) dynamically without changing the original source code.

The Strategy Pattern in UVM

UVM Callbacks implement the Strategy Pattern. They allow you to define "hooks" in your base components (like a Driver or Monitor) where external logic can be injected at runtime. This is the "Clean Room" approach to verification: you never modify your golden, signed-off components to add test-specific hacks.

Callback vs. Factory Override:

  • Factory Override: Replaces the entire class. Use when you want to change core component logic.
  • Callback: Injects logic into specific points. Use for transient behavior like error injection or performance monitoring.

Registration and Execution

To use callbacks, you must follow a strict three-step registration process. This ensures that the UVM callback pool can correctly find and execute your logic.

Implementation: The Base Component

class ahb_driver extends uvm_driver #(ahb_item);
    // 1. Register the callback type with the component
    `uvm_register_cb(ahb_driver, ahb_cb)
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            // 2. The Hook: Execute all joined callbacks
            `uvm_do_callbacks(ahb_driver, ahb_cb, pre_drive(req))
            drive_transfer(req);
            seq_item_port.item_done();
        end
    endtask
endclass
                            

Case Study: Non-Intrusive Error Injection

The gold standard for error injection is to use a callback rather than polluting your transaction randomized constraints. Here is how you inject a CRC error from the Test level.

Test: Dynamic Injection

class crc_err_cb extends ahb_cb;
    // Override the virtual method
    virtual task pre_drive(ref ahb_item req);
        `uvm_info("CB", "Injecting CRC Error...", UVM_LOW)
        req.crc = 32'hDEADBEEF; // Corrupt data
    endtask
endclass
// inside test::build_phase
crc_err_cb my_cb = crc_err_cb::type_id::create("my_cb");
uvm_callbacks #(ahb_driver, ahb_cb)::add(env.drv, my_cb);
                            

Best Practices

  • Empty Virtual Methods: Always define your base callback methods as empty virtual tasks/functions. This ensures the component runs normally if no callback is registered.
  • Instance-Specific Hooks: Use the add() method carefully. You can add a callback to all drivers or just a specific driver instance.
  • Order of Execution: Remember that callbacks are executed in the order they are added. This can be critical for complex multi-layer error injection.