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_callbackswith the field), so it fires for that field's updates — a finer scope than a whole register. post_predictis 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_writefires 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:
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:
// 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// 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.// 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
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// 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.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.
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.
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_writefor a value-tracking field side-effect. It fires only on frontdoor writes; backdoor pokes and explicit predicts skip it and the model drifts — usepost_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_predictdrift 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_predictwhen you truly mean a bus write. If the event is specifically a frontdoor write (protocol reaction, error injection on write),post_writeis 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_readonly 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 explicitpredict()— orpost_write-vs-post_predictdrift 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_predictcovers all update paths: the prediction after a frontdoor write, the prediction after a backdoorpoke(8.4), and an explicitpredict()— making it the right hook for a hardware side-effect on a field that must occur however the value moved.post_writefires 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 ispost_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.
