Skip to content
VLSI Mentor

UVM RAL · Chapter 9 · Callbacks

Field Callbacks

Callbacks attach at field granularity too, and fields bring a hook that is central to modelling real hardware: the post-predict hook. It fires whenever a field's mirrored value is updated in the model, whether from a frontdoor write's prediction, a backdoor poke, or an explicit predict call. That all-paths coverage makes it the right hook for modelling a hardware side-effect on a specific field, because the side-effect must occur however the field's value moved, not only when a bus write caused it. By contrast, the post-write hook fires only on a frontdoor write and is blind to the other paths. This lesson explains how to register a callback onto a field and use the post-predict hook, then breaks the bug where a field side-effect uses post-write instead, so backdoor pokes and explicit predictions silently skip it and the model drifts out of sync.

Foundation12 min readUVM RALfield callbackspost_predictpost_writemirror drift

Chapter 9 · Section 9.2 · Callbacks

1. Why Should I Learn This?

Modelling a hardware side-effect on a specific field — a bit the hardware sets, a field whose change must update a shadow — is a field-callback job, and it only works if the callback fires however the field's value changes. Knowing that post_predict catches all update paths (frontdoor, backdoor, explicit predict) while post_write catches only frontdoor writes is what lets you pick the hook that keeps the field model in sync, instead of one that silently drifts when the value moves by a route you did not anticipate.

Learning field callbacks and the all-paths reach of post_predict builds on the register-callback mechanism (9.1), sharpens against the bus-access hooks of 9.3, and sets up the advanced modelling of 9.4. It is the difference between a field model that tracks the hardware and one that quietly falls behind.

2. Industry Story — the field model that drifted on backdoor

A team models a status field whose change must update a shadow model, using a field callback. They chose post_write for the hook — reasonable-sounding, since 'the field changes when it is written.' Frontdoor tests all passed: a bus write to the field fired post_write, the shadow updated, everything tracked.

Then tests that used backdoor poke to set up state — and tests that used explicit predict() — began producing a stale shadow: the field's modelled value changed, but the shadow did not follow, because post_write fires only on a frontdoor write and is blind to backdoor and explicit-prediction updates. The field model drifted out of sync exactly on the paths post_write does not cover, and because backdoor seeding is common in setup (8.4), the drift was widespread but intermittent-looking — present only where backdoor/predict were used. The fix was a one-word hook change: post_predict instead of post_write, which fires on every field-value update regardless of path, so the shadow tracked the field however it moved. The post-mortem lesson: post_predict fires on all field-value updates (frontdoor prediction, backdoor poke, explicit predict), while post_write fires only on a frontdoor write — so a field side-effect that must occur however the value changes belongs in post_predict; using post_write makes the model silently drift on the backdoor and explicit-prediction paths it does not cover.

3. Concept — register on the field, use post_predict for all update paths

Field callbacks work like register callbacks (9.1) with two field-specific points:

  • Register onto the field. You attach the callback to a field (via the field's callback registration / uvm_callbacks with the field), so it fires for that field's updates — a finer scope than a whole register.
  • post_predict is the all-update-paths field hook. It fires whenever the field's mirrored value is predicted/updated in the model, which covers every route the value can change by:
    • the prediction after a frontdoor write (the field's value updates in the model),
    • the prediction after a backdoor poke (8.4),
    • an explicit predict() call (2.x/6.x). This makes it the correct hook for a hardware side-effect on a field — a change that must happen however the field moved.
  • post_write fires only on a frontdoor write. It is the right hook for 'react specifically to a bus write' (9.3), but it is blind to backdoor and explicit-prediction updates — so using it to model an all-paths side-effect causes drift.

Here is the difference: post_predict fires on all three update paths, post_write on only one:

post_predict fires on frontdoor write, backdoor poke, and explicit predict; post_write only on frontdoor writeupdate sourceuvm_reg_field (model)field callbackfrontdoor write ->predict valuepost_predict fires(and post_writefires)backdoor poke ->predict value (8.4)post_predict fires(post_write doesNOT)explicit predict()callpost_predict fires(post_write doesNOT)
Figure 1 — why post_predict is the field hook for a value-change side-effect. A field's mirrored value can update by three routes: the prediction after a FRONTDOOR write, the prediction after a BACKDOOR poke (8.4), and an EXPLICIT predict() call. post_predict fires on ALL THREE — so a side-effect placed there (shadow update, companion-field ripple) happens however the value moved. post_write fires ONLY on the frontdoor write, so a side-effect placed there is SKIPPED on the backdoor and explicit-predict paths, and the model drifts. Choose post_predict when the reaction must track every value change; post_write only when you specifically mean 'a bus write happened'.

4. Mental Model — post_predict watches the value; post_write watches the bus

5. Working Example — a field side-effect with post_predict

Attach a field callback that keeps a shadow in sync on every update path by using post_predict:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A field callback whose side-effect must happen however the field's value changes -> post_predict.
class field_shadow_cb extends uvm_reg_cbs;
  `uvm_object_utils(field_shadow_cb)
  shadow_model shadow;
  function new(string name = "field_shadow_cb"); super.new(name); endfunction

  // post_predict fires on EVERY field-value update: frontdoor prediction, backdoor poke, explicit predict.
  virtual function void post_predict(input uvm_reg_field  fld,
                                     input uvm_reg_data_t previous,
                                     inout uvm_reg_data_t value,
                                     input uvm_predict_e  kind,
                                     input uvm_path_e     path,
                                     input uvm_reg_map    map);
    shadow.set(fld.get_full_name(), value);   // tracks the field no matter HOW it moved
  endfunction
endclass
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Register onto the FIELD (finer scope than a register), then it fires for that field's updates.
field_shadow_cb cb = field_shadow_cb::type_id::create("cb");
cb.shadow = env.shadow;
uvm_callbacks#(uvm_reg_field, uvm_reg_cbs)::add(status_reg.mode, cb);   // attach to the FIELD
// Now a frontdoor write, a backdoor poke (8.4), AND an explicit predict all update the shadow.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Contrast: the SAME side-effect in post_write would fire ONLY on a frontdoor write:
//   virtual task post_write(uvm_reg_item rw); shadow.set(...); endtask   // frontdoor-only -> DRIFTS
// Backdoor pokes and explicit predicts would change the field's value but SKIP this -> stale shadow.

post_predict makes the shadow follow the field on every update path; the same logic in post_write would silently miss the backdoor and explicit-predict paths — the bug of the next section.

6. Debugging Session — a field side-effect that drifts on backdoor and predict

1

A field side-effect placed in post_write fires only on frontdoor writes, so backdoor pokes and explicit predictions skip it and the field model drifts — the fix is post_predict, which fires on all update paths

post_predict, NOT post_write
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A shadow that must track a field however it changes — but hooked on post_write (frontdoor-only):
class field_shadow_cb extends uvm_reg_cbs;
  virtual task post_write(uvm_reg_item rw);         // BUG: fires ONLY on a frontdoor write
    shadow.set(rw.element.get_full_name(), rw.value[0]);
  endtask
endclass
// ...
uvm_callbacks#(uvm_reg_field, uvm_reg_cbs)::add(status_reg.mode, cb);
// Backdoor pokes and explicit predict() change 'mode' but never fire post_write -> shadow goes stale.
Symptom

Frontdoor tests all pass — a bus write to the field fires post_write, the shadow updates, everything tracks. But tests that set the field via backdoor poke (common in setup, 8.4) or via explicit predict() produce a stale shadow: the field's modelled value changed, yet the shadow did not follow. The drift appears only on the backdoor/predict paths, so it looks intermittent — fine in pure-frontdoor tests, wrong wherever backdoor seeding or explicit prediction was used — and downstream checks that trust the shadow fail confusingly, with no obvious link to the callback.

Root Cause

The side-effect was a value-tracking one — the shadow must follow the field however its value changes — but it was hooked on post_write, which fires only on a frontdoor (bus) write. A field's mirrored value, though, can change by three routes: the prediction after a frontdoor write, the prediction after a backdoor poke, and an explicit predict() call. post_write covers only the first; the backdoor and explicit-prediction updates change the field's value in the model without a bus write, so they never fire post_write, and the shadow is not updated. It seemed to work because, in frontdoor-only tests, every value change was caused by a bus write — the one path post_write covers — so the gap only appears once a backdoor poke or explicit predict moves the value off the covered path. The right hook for an all-paths value-tracking side-effect is post_predict, which fires on every field-value update regardless of route; post_write was the wrong tool for a reaction that must track the value however it moves.

Fix

Move the side-effect from post_write to post_predict, which fires on all field-value updates (frontdoor prediction, backdoor poke, explicit predict), so the shadow tracks the field however it changes — the same shadow logic, correct hook. The rule the bug teaches: for a field side-effect that must occur however the value changes, use post_predict (all update paths), not post_write (frontdoor writes only); hooking a value-tracking reaction on post_write makes the model drift silently on the backdoor and explicit-prediction paths it does not cover. The general test for choosing the hook: if backdoor pokes and explicit predictions should also trigger your reaction, it is a post_predict reaction; if you specifically mean 'a bus write occurred,' it is post_write (9.3).

7. Common Mistakes

  • Using post_write for a value-tracking field side-effect. It fires only on frontdoor writes; backdoor pokes and explicit predicts skip it and the model drifts — use post_predict.
  • Forgetting that backdoor and explicit predict update the field's value. A field's value changes by three routes, not just bus writes; a value-tracking reaction must cover all of them (8.4).
  • Testing a field callback only through the frontdoor. Frontdoor-only tests hide post_write-vs-post_predict drift entirely — exercise backdoor and explicit-predict paths too.
  • Registering on the register when you meant the field. Field callbacks attach to the field for field-scoped updates — match the registration scope to intent (9.1).
  • Reaching for post_predict when you truly mean a bus write. If the event is specifically a frontdoor write (protocol reaction, error injection on write), post_write is correct (9.3).

8. Industry Best Practices

  • Model value-tracking field side-effects in post_predict. It fires on every update path (frontdoor, backdoor, explicit predict), so the model follows the field however it changes.
  • Use post_write/post_read only for genuine bus-access reactions. When the bus event itself is what you mean to catch, not any value change (9.3).
  • Exercise all update paths when testing a field callback. Frontdoor, backdoor poke, and explicit predict() — or post_write-vs-post_predict drift stays hidden.
  • Register field callbacks at field scope. Attach to the field whose updates you care about, keeping the callback's reach precise.
  • Decide the hook by the event you react to. Value change -> post_predict; bus write -> post_write. Naming the event picks the hook.

9. Interview / Review Questions

10. Key Takeaways

  • Callbacks attach at field granularity too — register onto the field — and fields lean on post_predict, the hook that fires whenever a field's mirrored value is updated in the model.
  • post_predict covers all update paths: the prediction after a frontdoor write, the prediction after a backdoor poke (8.4), and an explicit predict() — making it the right hook for a hardware side-effect on a field that must occur however the value moved.
  • post_write fires only on a frontdoor write — correct for a genuine bus-write reaction (9.3), but blind to backdoor and explicit-prediction updates.
  • The signature bug: a value-tracking side-effect placed in post_write — it works frontdoor but silently drifts on the backdoor and explicit-predict paths; the fix is post_predict.
  • Choose the hook by the event you react to: field value change -> post_predict, bus write -> post_write — and test all update paths (frontdoor, backdoor, explicit predict), or the drift stays hidden.

Standards & specifications

Governing standard
IEEE Std 1800.2 (UVM)(opens IEEE in a new tab)

Defines the UVM class library's API and semantics. Testbench structure, naming conventions and project methodology around that API are engineering practice, not requirements of the standard.

Reference implementation
Accellera UVM reference implementation(opens Accellera Systems Initiative in a new tab)

The open-source class library and user guide that most simulators ship. Useful alongside IEEE 1800.2 when a page discusses the implementation rather than the standardised API.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of UVM RAL Callbacks — Extending Register Behaviour.