UVM RAL · Chapter 6 · Predictors
Predictor Debugging
A mirror mismatch, where a read reports that the design and the model disagree, is the most common thing you debug in RAL, and the predictor is involved in many of them. This lesson closes the chapter with a systematic diagnosis method and the predictor's one fundamental limit. The method is a short decision tree. First ask whether the design value is actually correct per the spec. If not, you have a real hardware bug. If it is correct, the mirror is wrong, and the next question is whether the change appeared as a bus transaction. If it did, check the prediction wiring for a missing predictor or a wrong access policy. If it did not, you have hit the limit: the predictor only sees the bus. Hardware-internal counters, cross-register side effects, and backdoor pokes produce no bus activity, so no predictor can track them and you need volatile, callbacks, or explicit prediction instead.
Foundation12 min readUVM RALPredictorMirror MismatchDebuggingVolatileSide Effects
Chapter 6 · Section 6.5 · Predictors
1. Why Should I Learn This?
Mirror mismatches have half a dozen distinct causes across the whole track — a real DUT bug, a missing predictor, a wrong map, double prediction, a wrong access policy, an unobservable change — and guessing among them wastes hours. A systematic diagnosis that asks a few sharp questions in order localizes any mismatch to its cause in minutes, and it is the single most useful debugging skill in register verification. Just as important is knowing the predictor's fundamental limit, because a large class of mismatches — hardware-internal updates, side effects, backdoor changes — are not predictor bugs at all and cannot be fixed by touching the predictor; recognizing them stops you from debugging a component that is working perfectly.
Learning to diagnose a mirror mismatch and to recognize the predictor's limit is what makes you fast and correct at the most common RAL failure, and it ties the whole track together: the diagnosis tree routes to lessons from Chapters 0 through 6. It is the capstone of the prediction chapter.
2. Industry Story — the counter the predictor could not see
A passive environment tracks a block's registers with a correctly-configured predictor — verified working, tracking every bus access from software. But one register, a PACKET_COUNT that hardware increments each time a packet arrives, keeps mismatching: a RAL read reports the DUT holds a higher count than the mirror.
The engineer, trusting the diagnosis 'mismatch means predictor,' spends a day checking the predictor's map, adapter, and connection — all correct. The predictor is flawless. The real cause is that PACKET_COUNT is incremented by hardware, internally, with no bus transaction — packets arrive on a data interface, not the register bus, and the counter ticks up on its own. The predictor observes the register bus, so an increment that never appears on that bus is completely invisible to it; there is nothing for it to catch. The mirror falls behind by one per packet, and no amount of predictor debugging can fix it, because the predictor is not the problem — the change simply does not happen where the predictor can see it. The fix is to mark PACKET_COUNT volatile so RAL re-reads the DUT rather than trusting the mirror (or to mirror() it before checking). The lesson: the predictor observes bus traffic, so it can only track changes that appear on the bus; a hardware-internal update, a cross-register side effect, or a backdoor change produces no bus transaction and is invisible to any predictor — those are not predictor bugs, and they need volatile, callbacks, or explicit prediction, not more predictor debugging.
3. Concept — the diagnosis, and the predictor's limit
Diagnose a mirror mismatch by asking, in order:
- Is the DUT value correct per the spec? If no, it is a real DUT bug — the model was right. If yes, the mirror is wrong; continue.
- Did the change appear as a bus transaction? This split is the key.
- If yes (the register was changed by a bus access): a predictor should have caught it, so check the prediction wiring — is a predictor present and correctly configured (6.1, 6.3), is there a predictor on this bus (6.4), and is auto-predict off so it is not double-predicting (6.2)? If the wiring is correct and it still mismatches, the cause is a wrong access policy that predicted the wrong value (0.2, 1.5).
- If no (the register changed with no bus transaction): you have hit the predictor's fundamental limit.
The fundamental limit: a predictor observes the bus, so it can only ever know about changes that occur on the bus. Three common changes produce no bus transaction and are therefore invisible to any predictor:
- Hardware-internal updates — a self-incrementing counter, a status bit hardware sets from an internal event (the counter story).
- Cross-register side effects — one register's access changing another register (1.7), where the second change is a consequence, not its own bus transaction.
- Backdoor changes — a
poke(4.3) reaches the storage directly, bypassing the bus.
These are not predictor bugs and cannot be fixed with predictor debugging; they need other mechanisms: volatile (1.6) so RAL re-reads the DUT rather than trusting the mirror, a callback (Chapter 9) to model a side effect, or explicit predict/mirror (4.2) to record an out-of-band change. Here is the diagnosis as a flow:
4. Mental Model — the predictor is a witness on the bus; it cannot testify to what did not happen there
5. Working Example — diagnosing, and handling a non-bus change
Walk the diagnosis on a mismatch, then handle the hardware-internal counter it reveals:
// A read of PACKET_COUNT mismatches. Diagnose in order:
// 1. Is the DUT value correct per spec? -> yes (hardware counted real packets). Mirror is wrong.
// 2. Did the change appear as a bus transaction? -> NO. Packets arrive on a data interface;
// hardware increments the counter internally, with no register-bus access.
// => Predictor's LIMIT: the increment is invisible to any predictor. Not a predictor bug.
// FIX for a hardware-internal change: mark the field volatile so RAL re-reads the DUT
// rather than trusting the mirror (1.6), and/or mirror() it before checking.
// (in build) count.configure(this, 32, 0, "RO", 0, /*volatile*/ 1, 0, 0);With the field marked volatile, RAL re-reads the DUT instead of trusting the stale mirror, so a read is coherent; mirror() explicitly resyncs where needed:
uvm_status_e s; logic [31:0] got;
// Volatile -> every read consults the DUT, so a hardware-internal increment is observed:
packet_count.read(s, got); // reads the live count; volatile means no stale-mirror mismatch
// Or resync explicitly before a check, for a register you know hardware changed off-bus:
packet_count.mirror(s, UVM_CHECK); // re-read the DUT and refresh the mirror (4.2)The diagnosis routed correctly: DUT right, change not on the bus, so it is the predictor's limit — handled with volatile/mirror, not by touching the (already-correct) predictor. The next section is that exact trap, undiagnosed.
6. Debugging Session — the hardware-internal counter blamed on the predictor
A register hardware increments internally produces no bus transaction, so even a perfect predictor cannot track it, and the mirror falls behind
PREDICTOR'S LIMIT// PACKET_COUNT is incremented by HARDWARE on each packet arrival — packets come in on a
// data interface, NOT the register bus, so the increment produces NO register-bus transaction.
// It is modelled RO, non-volatile, and the environment relies on the (correct) predictor:
count.configure(this, 32, 0, "RO", 0, /*volatile*/ 0, 0, 0); // BUG: non-volatile HW-updated reg
// The predictor is flawless, but it observes the register bus and never sees the increment.
packet_count.read(s, got); // DUT = 42 (real count), mirror = 40 (last bus-visible value) -> mismatchA hardware-updated counter (or status register) mismatches — DUT correct, mirror behind — even though the predictor is present, correctly configured, and demonstrably tracking every bus access. Predictor debugging turns up nothing wrong, because nothing is wrong with the predictor. The mismatch grows over time (the mirror falls further behind as more packets arrive) and correlates with hardware activity (packet arrivals) rather than any register-bus access. It looks like a predictor failure, so time is spent auditing a component that is working perfectly.
The register is changed by hardware internally, with no register-bus transaction — packets arrive on a separate data interface and hardware increments the counter on its own. The predictor observes the register bus, so a change that never appears on that bus is completely invisible to it; there is nothing for it to catch, no matter how correctly it is wired. This is the predictor's fundamental limit, not a bug: it can only track changes that happen on the bus it watches. The register was modelled non-volatile, so RAL trusts the mirror and never re-reads the DUT to discover the hardware increment. The predictor is correct; the model failed to account for a change that happens where no predictor can see it.
Handle the non-bus change with a mechanism that does not rely on the predictor: mark the field volatile (count.configure(this, 32, 0, "RO", 0, 1, 0, 0)) so RAL re-reads the DUT rather than trusting the mirror (1.6), and/or mirror(UVM_CHECK) to resync before a check (Section 5). For a cross-register side effect, a callback (Chapter 9) models the ripple; for a backdoor change, predict records it (4.2). The rule the bug teaches, and the chapter's closing point: the predictor observes bus traffic, so a change that produces no bus transaction — a hardware-internal update, a cross-register side effect, a backdoor poke — is invisible to any predictor and is not a predictor bug; fix it with volatile, callbacks, or explicit predict/mirror, not with predictor debugging. When a mismatch persists with a demonstrably-correct predictor and correlates with hardware activity rather than a bus access, the change is off the bus — stop debugging the predictor.
7. Common Mistakes
- Assuming every mismatch is a predictor bug. Hardware-internal updates, side effects, and backdoor changes are the predictor's limit, not its fault.
- Not asking whether the change was on the bus. It is the diagnostic question that separates predictor causes from the predictor's limit.
- Modelling a hardware-updated register non-volatile. RAL trusts a mirror the hardware changed off-bus (1.6).
- Debugging a working predictor. If it tracks every bus access, a mismatch on a non-bus change is not its fault.
- Skipping the DUT-correct check. A mismatch might be a real DUT bug the model correctly caught.
8. Industry Best Practices
- Diagnose mismatches in order. DUT correct? change on the bus? predictor wired? policy? — each routes to a cause and fix.
- Mark hardware-updated registers volatile. So RAL re-reads rather than trusting a mirror the predictor cannot keep current.
- Use callbacks for cross-register side effects. The predictor cannot infer a ripple that is not its own bus transaction (1.7, Chapter 9).
- Record backdoor changes with
predict. A poke is off-bus; tell the model (4.2). - Confirm the predictor is the cause before debugging it. A mismatch correlating with hardware activity rather than a bus access is off the predictor's beat.
9. Interview / Review Questions
10. Key Takeaways
- Diagnose a mirror mismatch in order: is the DUT correct? (no → real DUT bug), did the change appear on the bus?, is the predictor wired right? (no → fix it, 6.1–6.4), else wrong access policy (0.2, 1.5).
- The key split is whether the change appeared as a bus transaction — it separates predictor causes from the predictor's limit.
- The predictor's fundamental limit: it observes the bus, so a change producing no bus transaction — a hardware-internal update, a cross-register side effect (1.7), a backdoor poke (4.3) — is invisible to any predictor.
- Non-bus changes are not predictor bugs and cannot be fixed by predictor debugging — use
volatile(re-read the DUT, 1.6), a callback (model a side effect, Ch. 9), or explicitpredict/mirror(record an out-of-band change, 4.2). - The signature is a mismatch with a demonstrably-correct predictor that grows over time and correlates with hardware activity, not a bus access — the change is off the bus, so stop debugging the predictor.