Skip to content

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_write fires)post_predict fires(and post_write…backdoor poke -> predict value (8.4)backdoor poke ->predict value…post_predict fires (post_write does NOT)post_predict fires(post_write does…explicit predict() callexplicitpredict()…post_predict fires (post_write does NOT)post_predict fires(post_write does…
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.