UVM RAL · Chapter 4 · Accessing Registers
Desired vs Mirrored Values
Two of a field's three values are the source of the most persistent RAL confusion, and the mix-up produces a silent, common bug. The desired value is what you intend the field to hold, staged in the model but not necessarily programmed yet. The mirrored value is what RAL believes the hardware currently holds. They are equal at reset and after a plain write, which is why they are easy to conflate, but they diverge the instant you call set, which changes only the desired value and touches neither the mirror nor the design. They reconcile only when you call update, which drives the difference to the design and syncs the mirror. The trap is in the getters: get returns the desired value while a separate call returns the mirrored one. The lesson ends with a routine that sets a batch of fields, checks get, sees its own intent, and ships without ever calling update.
Foundation11 min readUVM RALDesiredMirroredsetupdateneeds_update
Chapter 4 · Section 4.4 · Accessing Registers
1. Why Should I Learn This?
The desired-versus-mirrored distinction is the conceptual seam where a set() that changed nothing in hardware masquerades as a completed configuration. Because get() returns the desired value, staging a value with set() and then reading it back with get() always agrees — you get exactly what you set — which feels like confirmation but confirms only that the model remembered your intent, not that the DUT received it. Miss the distinction and you write configuration routines that look correct, pass their own read-backs, and never touch the hardware.
Learning which value is which, when they diverge, and how update() reconciles them lets you use the efficient set/update idiom correctly, read the right getter for the right question, and recognize the 'it looked configured but did nothing' failure the moment it appears. It is the precise version of the three-value model, focused on the two values that fool people.
2. Industry Story — the configuration that only the model believed
An engineer writes a tidy configuration helper: it set()s a batch of fields across several registers — mode, prescale, enable, a few thresholds — and then, to 'verify the configuration took,' reads each field back with get() and asserts it equals the intended value. Every assertion passes. The helper looks airtight and goes into the regression as the standard setup.
The block does not work. It runs at the reset prescale, ignores the mode, never enables — because it was never programmed. set() changed only the desired values in the model; without an update(), not one bus write was issued, so the DUT sat at reset the whole time. The get() read-backs 'passed' because get() returns the desired value — the helper was checking its own staged intent against itself, a tautology that can never fail. The bug surfaces as a functional failure far downstream, and because the setup helper is 'verified' (its assertions pass), it is the last place anyone looks. The fix is a single missing update(). The lesson: set() stages intent in the desired value and drives nothing; get() returns that desired value, so a set()/get() round-trip confirms only the model, never the hardware — you must update() to program the DUT, and check the mirror or a real read to confirm it.
3. Concept — two values, diverging and reconciling
Track the two values through their lifecycle:
- Desired — what you intend the field to hold. Changed by
set(value); read byget(). Pure model state; touches neither the mirror nor the DUT. - Mirrored — what RAL believes the DUT holds. Read by
get_mirrored_value(); updated bywrite,read,mirror,predict, and the predictor. RAL's estimate of the hardware. - They start equal at reset and after a plain
write()(which sets both). - They diverge on
set(): desired changes, mirrored does not — the model now wants a value it does not believe the hardware has. needs_update()returns true exactly when desired differs from mirrored — the model's own report that there is unpushed intent.- They reconcile on
update(): for every field where desired differs from mirrored,update()drives that field to the DUT (a bus write) and syncs the mirror to the desired value. Afterupdate(), desired equals mirrored equals (barring a DUT bug) actual.
The getters are the trap: get() returns desired, get_mirrored_value() returns mirrored — so after a set(), they return different values, and reading the wrong one answers the wrong question. Here is the divergence and reconciliation over time:
4. Mental Model — the shopping list and the fridge
5. Working Example — set, needs_update, update, and the two getters
The efficient idiom stages several fields, confirms there is work to do, pushes it, and confirms it converged — reading the right value at each step:
uvm_status_e s;
// Stage intent across several fields — desired changes, mirrored does not, DUT untouched.
cfg.mode.set(2'd3);
cfg.prescale.set(8'h10);
cfg.enable.set(1'b1);
$display("get (desired) = %0h", cfg.mode.get()); // 3 — the DESIRED value
$display("get_mirrored (belief) = %0h", cfg.mode.get_mirrored_value()); // 0 — still reset (not pushed)
$display("needs_update? = %0b", cfg.needs_update()); // 1 — desired != mirrored
// Reconcile: drive the fields whose desired differs from mirrored, sync the mirror.
cfg.update(s);
if (s != UVM_IS_OK) `uvm_error("REG", "cfg update failed on the bus")
$display("get_mirrored (belief) = %0h", cfg.mode.get_mirrored_value()); // 3 — now synced to the DUT
$display("needs_update? = %0b", cfg.needs_update()); // 0 — convergedAnd the check that actually confirms the hardware — a read(), not a get():
logic [31:0] got;
cfg.read(s, got); // frontdoor read: auto-checks the DUT against the mirror (now 0x...13)
// This confirms the HARDWARE, unlike get(), which would only echo the desired value back.Between the set() and the update(), get() returns 3 and get_mirrored_value() returns 0 — the two values are simply different, and knowing which answers your question is the whole skill. The next section shows what happens when the update() is missing.
6. Debugging Session — set() without update()
Staging fields with set() and checking with get() confirms the intent, not the hardware, so a missing update() leaves the DUT unprogrammed while every assertion passes
SET WITHOUT UPDATE// A configuration helper stages fields and 'verifies' with get() — but never update()s.
cfg.mode.set(2'd3);
cfg.prescale.set(8'h10);
cfg.enable.set(1'b1);
// 'Verify the configuration took':
assert(cfg.mode.get() == 3); // PASSES — get() returns the DESIRED value we just set
assert(cfg.prescale.get() == 8'h10); // PASSES — same tautology
assert(cfg.enable.get() == 1); // PASSES
// BUG: no cfg.update(s). Not one bus write was issued; the DUT is still at reset.The configuration helper's own assertions all pass, so it is considered verified — yet the block behaves as if it was never configured: reset prescale, ignored mode, never enabled. The functional failure appears far from the setup code, and because the setup 'passes,' it is the last suspect. A later frontdoor read() of any 'configured' register returns its reset value and mismatches the mirror (which is also still reset, since nothing updated it) — the only honest signal in the whole flow, and it points at the registers being unprogrammed.
set() changes only the desired value; it drives no bus transaction and does not touch the mirror or the DUT. The helper never called update(), so the staged intent was never pushed to hardware — the DUT stayed at reset. The get() assertions passed because get() returns the desired value, so they compared the values just set() against themselves: a tautology that can never fail and confirms nothing about the hardware. The configuration lived entirely in the model's intent, never reached the silicon, and the 'verification' checked intent against intent. The DUT is untouched; the routine only ever talked to itself.
Call update() to push the staged fields to the DUT, and confirm with a read() (which checks the actual hardware) rather than get() (which echoes intent):
cfg.mode.set(2'd3); cfg.prescale.set(8'h10); cfg.enable.set(1'b1);
cfg.update(s); // NOW the DUT is programmed; mirror synced
if (s != UVM_IS_OK) `uvm_error("REG", "cfg update failed")
cfg.read(s, got); // confirm the HARDWARE (auto-check vs mirror)The rule the bug teaches: set() stages intent and drives nothing; it needs an update() to reach the DUT. And get() returns the desired value, so a set()/get() round-trip confirms only the model — to confirm the hardware, read() it. When a configuration 'passed its own checks but did nothing,' look for a set() with no update() and a get()-based self-check.
7. Common Mistakes
set()withoutupdate(). Intent is staged but never driven; the DUT stays as it was.- Checking configuration with
get(). It returns the desired value, so it echoes your intent — useread()(orget_mirrored_value()) to check the hardware/belief. - Assuming
set()changes the mirror. It changes only desired; the mirror is unchanged untilupdate()/write/predict. - Ignoring
needs_update(). It is the model's own report of unpushed intent — a quick way to catch a forgottenupdate(). - Confusing which getter is which.
get()= desired,get_mirrored_value()= mirrored; reading the wrong one answers the wrong question.
8. Industry Best Practices
- Pair every
set()with anupdate(). Stage intent, then push it; in review, aset()with no matchingupdate()is an immediate flag. - Confirm hardware with
read(), notget(). A read auto-checks the DUT;get()only echoes desired. - Use
needs_update()as a sanity check. After configuration,needs_update()should be false; a true value means unpushed intent. - Prefer
set+updatefor multi-field programming. Efficient and expressive — but only if theupdate()is actually there. - Name the getter by the question. Intent ->
get(); belief ->get_mirrored_value(); reality ->read().
9. Interview / Review Questions
10. Key Takeaways
- Desired is what you intend the field to hold (changed by
set(), read byget()); mirrored is what RAL believes the DUT holds (read byget_mirrored_value()). - They are equal at reset and after
write(), diverge onset()(desired moves, mirrored stays), and reconcile onupdate()(drive the difference to the DUT, sync the mirror);needs_update()reports the divergence. - The getters are the trap:
get()returns desired,get_mirrored_value()returns mirrored — so aset()/get()round-trip confirms only the model, never the hardware. - The signature bug is
set()withoutupdate()checked byget(): every assertion passes (intent against intent) while the DUT is never programmed. - Verify configuration with
read()(checks the actual DUT), notget(); pair everyset()with anupdate(), and useneeds_update()to catch a forgotten one.