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 theset/updateidiom: batch severalset()s, then oneupdate()programs only what changed.mirror(status, check)— read the DUT and refresh the mirror to the read value; ifcheckisUVM_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) tovaluewith 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:
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:
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")// 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// 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 matchesEach 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
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// 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.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.
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.
Program the DUT with write() (or set() + update()), which drive real bus transactions and then update the mirror to match reality:
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()withoutupdate(). 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/updatecross it (drive the DUT);set/predictdo not. - Use
set+updatefor 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 forwrite. - When configuration 'did nothing,' suspect a bookkeeping API. A
predict/setwhere awrite/updatewas 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 ofwrite(): 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+updateto change the DUT,mirror(UVM_CHECK)to verify-and-resync volatile registers, andpredict()only to record an out-of-band change RAL did not drive.