UVM RAL · Chapter 11 · Debugging RAL
Mirror Mismatch Debug
A mirror mismatch is when the register model's mirrored value disagrees with the actual DUT, usually surfaced as an auto-compare error on a read. It is the most common RAL failure, and the fast way to root-cause it is one diagnostic question: is the mirror behind the DUT, or ahead of it? Behind means the DUT moved and the model did not, from a hardware-updated field not marked volatile, a backdoor poke without a predict, or an unpredicted side-effect. Ahead means the model moved and the DUT did not, from a write the DUT never accepted, a wrong modelled reset, or reading a write-only register. You localize the direction with three reads: the mirrored value, a backdoor peek of the true RTL, and a fresh frontdoor read. This page teaches that method and traces a recurring status-field mismatch to its root cause.
Foundation12 min readUVM RALdebuggingmirror mismatchvolatilebehind vs ahead
Chapter 11 · Section 11.1 · Debugging RAL
1. Why Should I Learn This?
Mirror mismatches are the most frequent RAL failure, and without a method they turn into hours of staring at a register that is not itself broken. Knowing to first ask behind or ahead? — and to localize with the mirrored value, a backdoor peek, and a fresh frontdoor read — is what turns a baffling auto-compare error into a directed diagnosis that lands on the real cause in minutes.
Learning the mirror-mismatch method opens the debug chapter and gives you the diagnostic backbone the rest of it builds on — prediction (11.2), adapter (11.3), and backdoor (11.4) debugging all extend this 'localize the divergence, then classify it' approach.
2. Industry Story — the status register that was always 'wrong'
A team keeps hitting uvm_reg auto-compare errors on a status register: read it, and the value does not match the mirror. It happens intermittently across many tests, and each time someone burns an hour checking the bus, the adapter, the addressing — all fine. The register 'is broken' but nothing about the access path is wrong.
The fast method would have ended it in minutes: the mirror was consistently behind the DUT — the DUT held a newer value than the mirror. That points immediately at 'the DUT moved and the model did not,' and the reason was that the status register is hardware-updated (the hardware writes it as events occur) but the model was not told it was volatile (0.1). So the mirror held whatever was last predicted from a bus access, while the hardware kept changing the real register underneath — the mirror was stale by construction, and every read that happened after a hardware update mismatched. It was not a bus bug, an adapter bug, or an addressing bug; it was a modelling gap (missing volatile), and the behind direction pointed straight at it. Once the field was modelled volatile, the auto-compare stopped treating the model's stale value as authoritative and the errors vanished. The post-mortem lesson: a mirror mismatch is diagnosed fast by first asking whether the mirror is behind or ahead of the DUT — a mirror consistently behind means the DUT moved and the model did not, which points at a hardware-updated field not marked volatile, a backdoor write without predict, or an unpredicted side-effect; determining the direction is most of the diagnosis, and it is a three-read check, not a bus hunt.
3. Concept — behind or ahead, found with three reads
A mirror mismatch means mirror != DUT. The method:
- Ask: behind or ahead? Compare what the model thinks to what the DUT actually holds.
- Mirror BEHIND (DUT moved, model didn't): the DUT holds a value the model never recorded. Causes: a hardware-updated field not marked volatile (0.1); a backdoor poke without a predict (8.4); a side-effect (
W1Cclear,RCread-clear) the model did not predict (6.x). - Mirror AHEAD (model moved, DUT didn't): the model recorded a value the DUT never took. Causes: a write the DUT rejected via an access-policy mismatch (model
RW, RTLRO, 1.5); a wrong modelled reset (1.3, 7.4); reading aWOregister expecting the written value.
- Mirror BEHIND (DUT moved, model didn't): the DUT holds a value the model never recorded. Causes: a hardware-updated field not marked volatile (0.1); a backdoor poke without a predict (8.4); a side-effect (
- Localize with three reads:
get_mirrored_value()— what the model thinks the register holds.- a backdoor
peek— the true RTL value right now (bypasses the bus, 8.4). - a fresh frontdoor
read— what the bus returns (and re-predicts the mirror).
- Read the three: mirror vs peek tells you the model-vs-RTL truth (behind/ahead); frontdoor read vs peek tells you whether the bus path is faithful (if those two disagree, it is an adapter/addressing problem — 11.3 — not a mirror-modelling one).
Here is the mirror tracking the DUT correctly through a write, then falling behind when the hardware updates the register underneath it:
4. Mental Model — find which way the clocks disagree before asking why
5. Working Example — the three-read diagnosis
Run the three reads to establish direction and localize the divergence:
// THREE READS to diagnose a mirror mismatch on reg_h:
uvm_reg_data_t mirrored, backdoor, frontdoor;
uvm_status_e s;
mirrored = reg_h.get_mirrored_value(); // 1. what the MODEL thinks
reg_h.peek(s, backdoor); // 2. the TRUE RTL value right now (bypasses bus, 8.4)
reg_h.read(s, frontdoor); // 3. what the BUS returns (also re-predicts the mirror)
`uvm_info("MIRDBG", $sformatf("mirror=%h backdoor(RTL)=%h frontdoor(bus)=%h", mirrored, backdoor, frontdoor), UVM_LOW)// INTERPRET the three reads:
// mirror != backdoor -> model disagrees with RTL: a MIRROR-MODELLING problem. Now the DIRECTION:
// backdoor NEWER than mirror (DUT moved) -> mirror BEHIND -> volatile/backdoor/side-effect (0.1/8.4/6.x)
// mirror value the DUT never took -> mirror AHEAD -> access-policy/reset/WO (1.5/1.3/7.4)
// frontdoor != backdoor -> the BUS PATH disagrees with RTL: NOT a mirror bug -> adapter/addressing (11.3)
if (frontdoor !== backdoor)
`uvm_warning("MIRDBG", "frontdoor != backdoor -> bus-path (adapter/addressing) problem, not the mirror (11.3)")// EXAMPLE resolution — mirror BEHIND a hardware-updated field: mark the field VOLATILE so the model
// stops treating its stale copy as authoritative (0.1). Then the auto-compare no longer false-fails.
// status_field.configure(this, 8, 0, "RO", 1 /* volatile */, 8'h00, 1, 1, 0);
// After: reads sample the DUT's live value instead of comparing against a stale mirror.The three reads turn 'the register is wrong' into a specific finding — behind, ahead, or a bus-path bug — in one shot. The next section walks the behind case to its root cause.
6. Debugging Session — a mirror perpetually behind a hardware-updated field
Recurring auto-compare errors on a status register because the mirror is behind a hardware-updated field never modelled volatile — the direction question lands it fast
BEHIND -> HARDWARE-UPDATED, NOT VOLATILE// A status field the HARDWARE updates as events occur, but modelled WITHOUT volatile:
class status_reg extends uvm_reg;
rand uvm_reg_field evt_count;
virtual function void build();
evt_count = uvm_reg_field::type_id::create("evt_count");
// BUG: volatile = 0. The hardware writes this field, but the model treats its mirror as authoritative.
evt_count.configure(this, 8, 0, "RO", 0 /* NOT volatile */, 8'h00, 1, 1, 0);
endfunction
endclass
// Every read after a hardware update auto-compares the DUT's live value against a STALE mirror -> mismatch.Recurring, intermittent uvm_reg auto-compare errors on the status register across many tests: a read returns a value that does not match the mirror. The access path checks out every time — bus, adapter, addressing all fine — so it looks like a phantom 'the register is broken.' The intermittency (it only mismatches after a hardware update has moved the field) makes it feel random and wastes hours re-checking the bus on each occurrence.
Applying the method: the three reads show mirror != backdoor, and the backdoor (true RTL) value is newer than the mirror — the mirror is behind. That direction immediately narrows the cause to 'the DUT moved and the model didn't,' and among those the field being a hardware-updated one points at the specific gap: evt_count is written by the hardware as events occur, but it was modelled with volatile = 0, so the model treats its mirror (last predicted from a bus access) as the authoritative expected value. The hardware keeps changing the real register underneath, the mirror stays stale, and every read that follows a hardware update auto-compares the DUT's live value against the model's stale one — a guaranteed, recurring mismatch. There is no bus, adapter, or addressing bug; the frontdoor == backdoor reads confirm the path is faithful. It is a modelling gap — a hardware-updated field not marked volatile — and the behind direction is what pointed straight at it instead of another bus hunt.
Model the field as volatile so the register model knows the hardware can change it and does not treat its (necessarily stale) mirror as authoritative — the auto-compare then stops false-failing on a value the hardware legitimately moved (0.1). More broadly, the method the debug teaches: diagnose a mirror mismatch by first establishing direction with the three reads (mirrored value, backdoor peek, frontdoor read) — a mirror behind the DUT (backdoor newer than mirror) points at DUT-moved-model-didn't causes (volatile-not-set, backdoor-without-predict, unpredicted side-effect), a mirror ahead points at model-moved-DUT-didn't causes (access-policy mismatch, wrong reset, WO), and a frontdoor-vs-backdoor disagreement points at the bus path (11.3), not the mirror. Establishing direction is most of the diagnosis; it converts a repeated bus hunt into a one-shot classification.
7. Common Mistakes
- Hunting the bus/adapter first. A mirror mismatch is usually a modelling or prediction gap — establish direction with the three reads before re-checking the access path.
- Not asking behind vs ahead. The direction splits the causes in half; skipping it means searching the whole space instead of a short list.
- Forgetting
volatileon hardware-updated fields. A hardware-updated field not marked volatile keeps the mirror perpetually behind — recurring false auto-compares (0.1). - Ignoring the frontdoor-vs-backdoor comparison. If those two disagree, it is a bus-path bug (adapter/addressing, 11.3), not a mirror bug — the three-read check reveals it.
- Treating intermittent mismatches as random. 'Only after a hardware update' or 'only after a backdoor poke' is a pattern that names the cause.
8. Industry Best Practices
- Diagnose direction first. Behind (DUT moved) vs ahead (model moved) — the fast split before any bus investigation.
- Use the three-read check.
get_mirrored_value, backdoorpeek, frontdoorread— direction plus a modelling-vs-bus-path split in one shot. - Mark hardware-updated fields volatile. So the model does not treat a necessarily-stale mirror as authoritative (0.1).
- Predict backdoor and side-effect changes. A backdoor poke or a
W1C/RCside-effect must be predicted into the model, or the mirror falls behind (8.4, 6.x). - Note the pattern of intermittency. 'After a hardware update' or 'after backdoor' points directly at the cause class.
9. Interview / Review Questions
10. Key Takeaways
- A mirror mismatch — the model's mirror disagreeing with the DUT (auto-compare error on read) — is the most common RAL failure, and the fast method is to first ask behind or ahead?
- Mirror BEHIND (DUT moved, model didn't) points at a hardware-updated field not marked volatile (0.1), a backdoor poke without predict (8.4), or an unpredicted side-effect (6.x).
- Mirror AHEAD (model moved, DUT didn't) points at an access-policy mismatch (model
RW, RTLRO, 1.5), a wrong modelled reset (1.3, 7.4), or reading aWOregister. - Localize with three reads —
get_mirrored_value(model), backdoorpeek(true RTL), frontdoorread(bus): mirror-vs-backdoor gives direction, and a frontdoor-vs-backdoor disagreement means it is a bus-path bug (adapter/addressing, 11.3), not the mirror. - Establishing direction is most of the diagnosis — it converts a repeated bus hunt into a one-shot classification; note the pattern of intermittency ('after a hardware update,' 'after backdoor') because it names the cause.