Part 10 · Advanced Topics · Intermediate

VIP Callback API Design

Guidelines for VIP authors to expose robust callback hook APIs that are stable, composable, and verification-friendly.

Why VIP callback API quality matters

When VIP authors expose callbacks well, users customize behavior by composition instead of forking source. When callback APIs are vague or sparse, projects copy and modify driver internals, creating long-term maintenance cost.

A good VIP callback API defines hook timing, mutability rules, ordering expectations, and error handling guarantees. It treats callbacks as first-class extension surfaces with versioned contracts.

diagram
[UVM][ADV] VIP author objective

  Provide enough hooks so users do not fork VIP
  while preserving:
    - protocol correctness ownership
    - deterministic execution semantics
    - forward compatibility across releases
diagram
[TEST] user experience impact

  Strong callback API:
    add callback class -> solve scenario quickly

  Weak callback API:
    clone VIP driver -> patch internals -> diverge from upstream
  • Callback API is product surface area, not optional accessory.

  • Quality hooks reduce support burden and integration churn.

  • Stable hook contracts increase VIP adoption and trust.


Designing hook taxonomy

VIP should expose hooks at semantic milestones, not random code points. A practical taxonomy includes transaction lifecycle hooks, timing hooks, error/report hooks, and observation hooks.

diagram
[DRV][ADV] recommended driver hook taxonomy

  Transaction lifecycle:
    pre_txn(ref tr), post_txn(tr)

  Beat/channel hooks:
    pre_beat(ref tr, beat_idx), post_beat(tr, beat_idx)

  Timing hooks:
    pre_wait_ready(tr), post_wait_ready(tr, cycles_waited)

  Error/report hooks:
    on_violation(tr, reason), on_retry(tr, retry_idx)
diagram
[UVM] monitor hook taxonomy

  pre_sample()
  post_sample(raw_signals)
  pre_decode(raw, ref tr)
  post_decode(tr)
  on_protocol_error(raw, reason)

  Keep monitor hooks primarily observation-centric
  • Name hooks by intent and timing to reduce ambiguity.

  • Expose both coarse and fine-grained hooks only where justified.

  • Avoid overhooking every internal helper - API becomes noisy and unstable.


Contract clarity: mutability and constraints

Each hook should specify whether transaction mutation is allowed. For mutation-enabled hooks, document legal field changes and when those changes are consumed by host logic.

Also define boundaries: callbacks must not directly drive pins, must not modify host internal FSM state unless explicitly supported, and must not block indefinitely.

diagram
[UVM][ADV] hook contract template

  Hook: pre_txn(host, ref tr)
  Timing: before packing/drive scheduling
  Mutation: allowed on payload, id, user sideband
  Forbidden: direct vif pin drive, illegal length > MAX_LEN
  Latency: function hook, no time-consuming operations
  Error handling: host logs warning and continues on callback error
systemverilog
class vip_driver_cb extends uvm_callback;
  // Contract: may mutate tr fields listed in VIP user guide.
  virtual function void pre_txn(vip_driver drv, ref vip_item tr);
  endfunction

  // Contract: observation-only, no mutation expected.
  virtual function void post_txn(vip_driver drv, vip_item tr);
  endfunction
endclass
  • Separate mutation hooks from observation hooks for cleaner mental model.

  • Document field-level mutability instead of broad 'can edit transaction' language.

  • Include timing behavior limits in hook docs to prevent simulation hazards.


Execution model and callback stack governance

VIP should define callback order semantics and provide helper utilities to inspect active chains. Users need this to reason about composed policies across large environments.

systemverilog
class vip_driver extends uvm_driver #(vip_item);
  `uvm_component_utils(vip_driver)
  `uvm_register_cb(vip_driver, vip_driver_cb)

  function void dump_cb_chain();
    vip_driver_cb cb;
    cb = uvm_callbacks#(vip_driver, vip_driver_cb)::get_first(this);
    while (cb != null) begin
      `uvm_info("VIP_CB", {"active: ", cb.get_name()}, UVM_LOW)
      cb = uvm_callbacks#(vip_driver, vip_driver_cb)::get_next(this);
    end
  endfunction
endclass
diagram
[ADV] callback governance practices

  - registration order defines execution order
  - users can inspect chain via dump utility
  - host may expose callback_enable knob for debug A/B
  - release notes call out hook contract changes explicitly
diagram
[TEST][DRV] large-env callback composition

  project policy cb
      +
  subsystem stress cb
      +
  testcase-specific cb

  Need deterministic layering and observability to debug interactions
  • Provide chain dump helpers in VIP to speed integration debugging.

  • Treat order changes as behavior changes - mention in release notes.

  • Minimize hidden callback-side global state for composability.


Walkthrough: authoring a callback-friendly driver API

This walkthrough shows a compact but robust callback API design for a hypothetical packet driver.

systemverilog
class pkt_driver_cb extends uvm_callback;
  virtual function void pre_pkt(pkt_driver drv, ref pkt_item tr); endfunction
  virtual task pre_beat(pkt_driver drv, pkt_item tr, int beat_idx); endtask
  virtual function void post_pkt(pkt_driver drv, pkt_item tr); endfunction
endclass

class pkt_driver extends uvm_driver #(pkt_item);
  `uvm_component_utils(pkt_driver)
  `uvm_register_cb(pkt_driver, pkt_driver_cb)

  task drive_item(pkt_item tr);
    `uvm_do_callbacks(pkt_driver, pkt_driver_cb, pre_pkt(this, tr))
    for (int i = 0; i < tr.n_beats; i++) begin
      `uvm_do_callbacks(pkt_driver, pkt_driver_cb, pre_beat(this, tr, i))
      drive_one_beat(tr, i);
    end
    `uvm_do_callbacks(pkt_driver, pkt_driver_cb, post_pkt(this, tr))
  endtask
endclass
diagram
[UVM][ADV] why this API works

  pre_pkt:
    coarse transaction mutation hook

  pre_beat:
    fine timing/beat hook for stress

  post_pkt:
    observation and accounting hook

  Together:
    enough power for users without exposing fragile internals
diagram
[TEST] consumer-side usage

  add checksum_fault_cb
  add beat_pause_cb
  run stress sequence
  inspect scoreboard + callback audit logs

  No VIP source fork required

Key takeaways

  • VIP callback APIs should be explicit, stable, and semantically organized.

  • Document hook timing, mutability, ordering, and constraints as a formal contract.

  • Expose utilities for callback chain introspection to aid integration debugging.

  • Well-designed hooks eliminate most user pressure to fork VIP internals.

Common pitfalls

  • Too few hooks - users fork VIP internals for common scenarios.

  • Too many low-level hooks - API churn and fragile dependence on implementation details.

  • Undocumented mutation rules - callbacks accidentally violate host invariants.

  • Silent order changes between VIP versions - regression behavior shifts unexpectedly.