Skip to content

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 by get(). Pure model state; touches neither the mirror nor the DUT.
  • Mirrored — what RAL believes the DUT holds. Read by get_mirrored_value(); updated by write, 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. After update(), 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:

set changes desired, diverging from mirrored; get returns desired; update drives the difference to the DUT and converges the twoset() diverges desired from mirrored; update() reconcilesset() diverges desired from mirrored; update() reconcilesTestModel (desired / mirrored)DUTset(mode, 3): desired=3, mirrored unchangedset(mode, 3):desired=3, mirrored…get() returns 3 — the DESIRED value, NOT the DUTget() returns 3 —the DESIRED value,…needs_update()? yes (desired != mirrored)needs_update()? yes(desired !=…update(): drive fields where desired != mirroredupdate(): drivefields where desire…bus write mode=3done -> mirrored = 3 (synced)done ->mirrored = 3…now desired == mirrored == DUT (converged)now desired ==mirrored == DUT…
Figure 1 — desired and mirrored over the life of a set/update. set(3) changes the desired value to 3 while the mirror stays at its old value, so the two diverge and needs_update() becomes true; crucially, get() now returns the DESIRED 3 — your intent, not the hardware. update() drives the difference to the DUT and syncs the mirror, so after it desired == mirrored == the DUT (converged). Reading get() between the set and the update returns your own intent, which is why a set/get round-trip cannot confirm the hardware.

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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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  — converged

And the check that actually confirms the hardware — a read(), not a get():

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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()

1

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
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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.
Symptom

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.

Root Cause

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.

Fix

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):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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() without update(). 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 — use read() (or get_mirrored_value()) to check the hardware/belief.
  • Assuming set() changes the mirror. It changes only desired; the mirror is unchanged until update()/write/predict.
  • Ignoring needs_update(). It is the model's own report of unpushed intent — a quick way to catch a forgotten update().
  • 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 an update(). Stage intent, then push it; in review, a set() with no matching update() is an immediate flag.
  • Confirm hardware with read(), not get(). 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+update for multi-field programming. Efficient and expressive — but only if the update() 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 by get()); mirrored is what RAL believes the DUT holds (read by get_mirrored_value()).
  • They are equal at reset and after write(), diverge on set() (desired moves, mirrored stays), and reconcile on update() (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 a set()/get() round-trip confirms only the model, never the hardware.
  • The signature bug is set() without update() checked by get(): every assertion passes (intent against intent) while the DUT is never programmed.
  • Verify configuration with read() (checks the actual DUT), not get(); pair every set() with an update(), and use needs_update() to catch a forgotten one.