Skip to content

UVM RAL · Chapter 4 · Accessing Registers

Mirror, Update & Predict APIs

Beyond write and read, RAL has a family of APIs that manage the three values every field carries: the desired value you intend, the mirrored value the model believes the hardware holds, and the actual value in the DUT. Knowing exactly which store each API touches is the whole skill. The set and get calls change and read the desired value in the model with no bus activity. The update call drives every field whose desired differs from its mirror out to the DUT. The mirror call reads the DUT and refreshes the mirror, optionally checking first. The predict call sets the mirror directly with no bus access at all. The danger is that predict looks like it programs the register but only updates the model's belief. This page breaks a test that uses predict where it meant write, so the model thinks a register is configured while the hardware sits at reset.

Foundation12 min readUVM RALsetupdatemirrorpredictDesired vs Mirrored

Chapter 4 · Section 4.2 · Accessing Registers

1. Why Should I Learn This?

These four APIs are where engineers most often reach for the wrong one, because the names do not advertise the crucial distinction: which ones touch the DUT and which only touch the model. write() and update() drive the bus and change hardware; set() and predict() change only the model and drive nothing. Confuse them and you get the two nastiest silent failures in RAL: a set() with no update() that changes intent but never programs the DUT, and a predict() used for write() that convinces the model a register is configured while the hardware was never touched.

Learning to map each API to the value store it moves — desired, mirrored, or actual — is what lets you configure registers efficiently with set/update, resync and verify volatile ones with mirror, keep the model honest across backdoor changes with predict, and never confuse changing the model with changing the hardware. It is the conceptual core of the access model.

2. Industry Story — the register the model thought it had programmed

An engineer is speeding up a long configuration routine. Profiling shows the bus writes dominate, so they replace a series of reg.write() calls with reg.predict() calls, reasoning — from the name — that predict 'sets the register.' The configuration routine now runs almost instantly, and the immediate register read-backs pass, because predict() updated the mirror and the subsequent read() compares against that mirror.

But the block does not work. It behaves as if it was never configured — because it was not. predict() updates only the model's mirror; it drives no bus transaction, so the DUT's configuration registers are still at reset. The read-backs 'passed' only because they compared the DUT against a mirror that predict() had set to the intended values — a mirror lying in perfect agreement with itself. The bug surfaces as a functional failure downstream ('the block ignores its configuration'), far from the configuration routine, and the engineer spends a day before realizing the hardware was never programmed. The lesson: predict() changes the model's belief, not the hardware; to program the DUT you must write() (or set() + update()). Substituting predict() for write() makes the model think the register is configured while the silicon sits at reset — and a read that checks the DUT against that predicted mirror will happily agree.

3. Concept — which API touches which value store

Map each API to the desired / mirrored / actual stores:

  • set(value) / get() — change and read the desired value in the model. No bus, nothing driven. set() records intent; get() returns the desired value.
  • update(status) — for every field where desired differs from mirrored, drive that field to the DUT (a bus write) and sync the mirror. This is the push half of the set/update idiom: batch several set()s, then one update() programs only what changed.
  • mirror(status, check)read the DUT and refresh the mirror to the read value; if check is UVM_CHECK, first compare the DUT against the current mirror (raising an error on mismatch) before refreshing. Use it to resync a volatile register or to verify-and-refresh.
  • predict(value, kind) — set the mirror (and desired) to value with no bus access. Tells the model 'the DUT now holds this,' used after a backdoor change (4.3) or a known hardware event the predictor did not catch.
  • get_mirrored_value() — read the mirror, RAL's current estimate of the hardware.

The dividing line is the one that matters: write and update touch the DUT; set and predict touch only the model. Here is the API surface as movements between the three stores:

Three value stores (desired, mirrored, actual) with update driving desired to DUT and read/mirror bringing DUT to the mirrorupdate(): drive changed fields to DUT + sync mirrorupdate(): drivechanged fields to…read() / mirror(): DUT value refreshes mirror (mirror can check first)read() / mirror():DUT value refreshes…desiredintent — set() / get() (nobus)mirroredRAL belief —get_mirrored_value();predict() sets it (no bus)actual (DUT)real hardware12
Figure 1 — the three value stores and the APIs that move data between them. set()/get() touch only DESIRED (intent, no bus). update() drives fields whose desired differs from mirrored out to the DUT and syncs the mirror (a bus write). read()/mirror() read the DUT and refresh the mirror (mirror can check first). predict() sets the MIRROR with NO bus — it changes the model's belief only. The dividing line: write and update change hardware; set and predict change only the model.

4. Mental Model — some APIs move the hardware, some only move your beliefs

5. Working Example — set/update, mirror, and predict in their proper roles

Each API used for what it is: set/update to program efficiently, mirror to verify-and-resync a volatile status, predict to record a backdoor change:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
uvm_status_e s;
 
// set() + update(): batch intent in the model, then push only the changed fields to the DUT.
cfg.mode.set(2'd3);          // desired(mode) = 3   (no bus)
cfg.prescale.set(8'h10);     // desired(prescale)  (no bus)
cfg.update(s);               // ONE bus pass: drives mode and prescale (desired != mirror), syncs mirror
if (s != UVM_IS_OK) `uvm_error("REG", "cfg update failed on the bus")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// mirror(UVM_CHECK): read a volatile STATUS register, first checking the DUT against
// the current mirror, then refreshing the mirror to the live value.
status.mirror(s, UVM_CHECK);   // verifies DUT == mirror, then updates mirror to what the DUT holds
if (status.busy.get_mirrored_value() == 0) ...   // now the mirror is current
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// predict(): after a BACKDOOR poke (4.3) that changed the DUT without a frontdoor access,
// tell the model the new value so the mirror stays honest — NO bus access.
ctrl.poke(s, 32'hDEAD_BEEF);          // backdoor: changes the DUT directly
void'(ctrl.predict(32'hDEAD_BEEF));   // record it in the mirror so the next frontdoor read matches

Each stays on its side of the boundary: update drives the DUT; mirror reads the DUT and refreshes; predict records a belief without touching the DUT. The next section shows what happens when predict is used to try to program.

6. Debugging Session — predict() used where write() was meant

1

Using predict() in place of write() updates the mirror but never programs the DUT, so read-backs pass against a fiction while the hardware stays at reset

PREDICT INSTEAD OF WRITE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A configuration routine 'sped up' by replacing write() with predict():
void'(cfg.predict(32'h0000_0013));   // BUG: sets the MIRROR to 0x13; drives NO bus
void'(mode.predict(2'd3));           // BUG: model believes mode=3; DUT is untouched
// ... immediate read-back 'passes' because it checks the DUT against the predicted mirror ...
cfg.read(s, got);                    // if the DUT (still at reset) matched, fine; but it should not
// The DUT's config registers are still at RESET. The block is never configured.
Symptom

The configuration routine runs suspiciously fast (no bus traffic), and its immediate register read-backs pass — but the block behaves as though it was never configured: it ignores the mode, runs at the reset prescale, does not enable. The functional failure appears far downstream from the configuration code, so it looks unrelated to the registers. Most confusingly, a read() of a 'configured' register may pass its auto-check, because predict() set the mirror to the intended value and the read compares the DUT against that mirror — a check of the hardware against a fiction the test invented.

Root Cause

predict() updates only the model's mirror; it drives no bus transaction, so the DUT is never programmed. Substituting it for write() leaves every configuration register at reset while the mirror confidently holds the intended values. The read-backs 'pass' not because the hardware is correct but because the auto-check compares the DUT against the predicted mirror — and if the DUT happens to match (or the check is against the predicted value rather than an independent expectation), the model agrees with itself. The verification collapsed into a tautology: the model was told what the register holds, then checked the register against what it was told. The hardware, meanwhile, was never touched.

Fix

Program the DUT with write() (or set() + update()), which drive real bus transactions and then update the mirror to match reality:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cfg.write(s, 32'h0000_0013);   // drives the bus; DUT is actually configured; mirror synced
if (s != UVM_IS_OK) `uvm_error("REG", "cfg write failed")
// Or the batched idiom:  cfg.mode.set(3); cfg.prescale.set(...); cfg.update(s);

Reserve predict() for recording a change RAL did not drive — after a backdoor poke, or a known hardware event the predictor missed. The rule the bug teaches: predict() changes the model's belief, not the hardware; to program the DUT you must write() or update(). A read that checks the DUT against a predicted mirror is checking the hardware against a fiction — so if configuration 'ran but did nothing,' suspect a bookkeeping API used where a driving one was needed.

7. Common Mistakes

  • Using predict() to program a register. It updates the mirror only; the DUT is untouched — the block never gets configured.
  • set() without update(). Desired changes but nothing is driven; the DUT stays as it was.
  • Expecting set() to change what a read returns. set() touches desired, not the DUT or (directly) the mirror; a read reflects the hardware.
  • Forgetting mirror(UVM_CHECK) on volatile registers. Without a resync, the mirror of a hardware-updated register goes stale (1.6).
  • Reading the wrong value getter. get() returns desired; get_mirrored_value() returns the mirror — different stores.

8. Industry Best Practices

  • Draw the model-hardware boundary in your head for every API. write/update cross it (drive the DUT); set/predict do not.
  • Use set + update for multi-field programming. Batch intent, push the changed fields in one pass — expressive and efficient.
  • Use mirror(UVM_CHECK) to verify-and-resync volatile registers. It checks the DUT against the mirror, then refreshes.
  • Reserve predict() for out-of-band changes. Backdoor pokes and hardware events RAL did not drive — never as a substitute for write.
  • When configuration 'did nothing,' suspect a bookkeeping API. A predict/set where a write/update was needed leaves the DUT unprogrammed.

9. Interview / Review Questions

10. Key Takeaways

  • The three values from 0.2 — desired, mirrored, actual — are moved by distinct APIs, and the key distinction is which cross the model-hardware boundary.
  • set()/get() touch only desired (no bus); predict() sets the mirror (no bus) — both are pure bookkeeping.
  • update(status) drives every field whose desired differs from mirrored to the DUT (bus write) and syncs the mirror; mirror(status, check) reads the DUT and refreshes the mirror, optionally checking first.
  • The catastrophic confusion is predict() in place of write(): it updates the mirror but never programs the DUT, so read-backs pass against a fiction while the hardware sits at reset.
  • Use write/set+update to change the DUT, mirror(UVM_CHECK) to verify-and-resync volatile registers, and predict() only to record an out-of-band change RAL did not drive.