UVM RAL · Chapter 4 · Accessing Registers
Backdoor Peek & Poke APIs
The frontdoor drives a real bus transaction through the map, adapter, and sequencer, exercising the protocol as software would. The backdoor is the other way in. It reaches a register's storage directly through the RTL hierarchy using a configured HDL path, with no bus cycle at all. Peek reads the register straight from the HDL with no side effects, so it can sample a read-to-clear or FIFO register without disturbing it. Poke forces a value directly in the RTL, which is ideal for reaching a deep state fast. Because the backdoor bypasses the bus, it proves nothing about the protocol and can leave the mirror stale unless you predict. This lesson contrasts the two paths and breaks a wrong HDL path where peek returns X and poke reaches nothing, so setup silently runs on garbage.
Foundation12 min readUVM RALBackdoorpeekpokehdl_pathFrontdoor
Chapter 4 · Section 4.3 · Accessing Registers
1. Why Should I Learn This?
The backdoor is the tool that makes hard scenarios reachable and side-effecting registers observable — poking a deep state that would take hundreds of bus cycles to reach the front way, or peeking a read-to-clear counter without emptying it. Used well, it turns intractable setups into one line and gives you non-destructive observability. Used carelessly, it verifies nothing (because it never touches the bus), desyncs the mirror (because it changes the DUT outside the frontdoor path), or — most commonly — fails silently because the HDL path it depends on is wrong, so it reads X or pokes into the void.
Learning peek and poke — what they are for, what they are not for, and the HDL path they require — lets you use the backdoor to set up and sample without misusing it to 'verify,' and to recognize the wrong-path failure that makes backdoor access return garbage. It completes the access model: frontdoor to prove the bus, backdoor to reach and observe state.
2. Industry Story — the setup that poked into thin air
A test needs a large counter register deep in its count to exercise a rollover corner case. Reaching that count the front way would take an impractical number of bus writes, so the engineer uses a backdoor poke to force the counter directly — a perfect use of the backdoor. But the register's hdl_path was configured with a typo in the RTL hierarchy: it points at a path that does not resolve to the counter's flip-flops.
The poke runs without an obvious error and the test proceeds — but the counter in the DUT is untouched, because the poke wrote to a path that does not reach it (or reaches nothing). The rollover scenario never actually sets up: the counter is still near zero, the corner case is never hit, and the test 'passes' having verified nothing of what it intended. Worse, a companion peek used to confirm the setup reads X (the unresolved path has no defined value), and because the confirming logic was loose, the X slips through. The team believes they have rollover coverage they do not have, and the gap is found only much later when rollover fails in a different test. The lesson: backdoor access depends entirely on a correct HDL path; a wrong hdl_path makes poke reach nothing and peek return X, so scenario setup silently runs on garbage — and because the backdoor produces no bus activity, nothing external flags the failure.
3. Concept — the backdoor, peek, poke, and the HDL path
The backdoor and its operations:
- The backdoor path. Instead of a bus transaction, the backdoor reaches a register's storage directly through the RTL hierarchy, using the register's configured
hdl_path— a string naming the register's location in the DUT (set on the block/register inbuild_phase, from 2.5). No adapter, no sequencer, no protocol. peek(status, value)— read the register's value directly from the HDL. Instant, and with no read side effects, so it observesRC, FIFO, and other side-effecting registers without perturbing them (the non-destructive observability from 1.5 and 1.7). It updates the mirror to the peeked value.poke(status, value)— force the register's value directly in the RTL. Instant, no bus cycle. Used to reach a state the frontdoor would take many cycles to build. It updates the mirror to the poked value (so predict-after-poke is usually implicit in current UVM, but be deliberate about keeping the mirror in sync — 0.1).pathon read/write.read/writealso takeUVM_BACKDOORto route the same operation through the backdoor;peek/pokeare the dedicated backdoor primitives.
The two properties that define when to use it: it is instant and side-effect-free, and it bypasses the bus — so it proves nothing about the protocol and can desync the mirror. Here is frontdoor versus backdoor:
Frontdoor vs backdoor — two ways to reach a register
uvm ral4. Mental Model — the backdoor is a teleporter to the flip-flops
5. Working Example — poke to set up, peek to sample
Backdoor poke reaches a deep state instantly; backdoor peek samples a side-effecting register without disturbing it:
uvm_status_e s; logic [31:0] got;
// poke(): force a large counter deep into its range to reach a rollover corner case,
// which would take an impractical number of frontdoor writes to build. No bus cycle.
count_reg.poke(s, 32'hFFFF_FFFE); // DUT counter is now near rollover; mirror updated
if (s != UVM_IS_OK) `uvm_error("BKD", "poke failed — check hdl_path")
// peek(): sample a read-to-clear EVENT_COUNT without the frontdoor read that would CLEAR it.
event_count.peek(s, got); // reads the true count from the HDL; no RC side effect
if (s != UVM_IS_OK) `uvm_error("BKD", "peek failed — check hdl_path")
// 'got' is the live count; the FIFO/counter is untouched, unlike a frontdoor read.Because backdoor changes bypass the frontdoor path, keep the mirror in sync when it matters (0.1):
// After a backdoor poke, a subsequent FRONTDOOR read auto-checks against the mirror.
// Current UVM predicts the mirror on poke; be deliberate where prediction is not automatic:
ctrl.poke(s, 32'hDEAD_BEEF);
void'(ctrl.predict(32'hDEAD_BEEF)); // ensure the mirror matches the poked value
ctrl.read(s, got); // frontdoor read now matches the mirror (no false mismatch)The backdoor did the hard part — reaching a state and sampling without side effects — with no bus traffic; the frontdoor remains the thing that proves the bus works. The next section shows what happens when the coordinates are wrong.
6. Debugging Session — the wrong HDL path
A wrong hdl_path makes poke reach nothing and peek return X, so scenario setup silently runs on garbage with no bus activity to flag it
WRONG HDL PATH// The register's hdl_path has a typo / wrong hierarchy — it does not resolve to the flops.
count_reg.add_hdl_path_slice("cntr_q_WRONG", 0, 32); // BUG: node does not exist / is wrong
// Setup pokes a deep count to reach rollover:
count_reg.poke(s, 32'hFFFF_FFFE); // writes to a path that reaches nothing; DUT counter unchanged
// A peek to 'confirm' reads X (unresolved path has no defined value):
count_reg.peek(s, got); // got == 'x; loose confirming logic lets it throughA backdoor-based setup does not actually set up the state it claims: the rollover corner case is never reached, so the test passes having verified nothing of what it intended, and the coverage it was supposed to close stays open. A confirming peek returns X, which slips through loose checking. There is no bus activity to inspect (the backdoor drives none), so nothing external flags the failure, and the gap is invisible until the same corner case fails in a different test much later. The register model looks correct and the frontdoor works fine — only the backdoor is broken.
Backdoor access reaches a register through its configured hdl_path, and this path is wrong — a typo or a hierarchy mismatch — so it does not resolve to the register's actual flip-flops. A poke to a non-resolving path changes nothing in the DUT (the target does not exist or is not the register), and a peek reads X because the path has no defined value. Because the backdoor produces no bus transaction, there is no protocol activity, no adapter, and no status-carrying bus response to expose the failure the way a frontdoor bus error would — the operation quietly does nothing. The register model, the map, and the frontdoor are all correct; the single wrong string is the hdl_path.
Set the hdl_path to the register's true RTL location, and verify it rather than trusting it: run the built-in uvm_reg_mem_hdl_paths_seq, which walks every register's HDL path and checks it resolves and round-trips (peek/poke against frontdoor), catching wrong and missing paths across the whole model in one pass. Tighten confirming logic so a peeked X is treated as a failure, not a pass. The rule the bug teaches: the backdoor depends entirely on a correct hdl_path, and a wrong one makes poke reach nothing and peek return X with no bus activity to flag it — so configure paths carefully, verify them with the HDL-path sequence, and never let a peeked X slip through as valid data.
7. Common Mistakes
- A wrong or missing
hdl_path.pokereaches nothing,peekreturns X — and no bus activity flags it. - Using the backdoor to 'verify.' It never touches the bus, so it proves nothing about the protocol; frontdoor verifies, backdoor sets up and samples.
- Frontdoor-reading a side-effecting register for observation. Use
peek(non-destructive) to sampleRC/FIFO registers, not a frontdoor read that pops/clears them (1.5, 1.7). - Poking without keeping the mirror in sync. A backdoor change can leave the mirror stale; predict it so the next frontdoor read does not false-mismatch (0.1).
- Letting a peeked
Xpass. Loose confirming logic treats an unresolved-pathXas data — tighten it.
8. Industry Best Practices
- Frontdoor to prove the bus, backdoor to reach and observe state. Keep the roles distinct; a suite that only pokes and peeks has never verified the register interface.
- Verify every
hdl_pathwithuvm_reg_mem_hdl_paths_seq. It resolves and round-trips every path, catching wrong/missing ones across the model. - Use
peekfor non-destructive sampling. Observe side-effecting registers without triggering their effects. - Use
poketo reach hard states cheaply. Force a deep count, a specific FIFO occupancy, an error condition — then verify behaviour the frontdoor way. - Keep the mirror in sync across backdoor changes. Predict after a poke where prediction is not automatic, so frontdoor reads stay coherent.
9. Interview / Review Questions
10. Key Takeaways
- The backdoor reaches a register's storage directly through the RTL hierarchy (via its
hdl_path) — no bus, no protocol, no side effects, and instant. peek(status, value)reads the register from the HDL non-destructively (safe forRC/FIFO registers);poke(status, value)forces its value directly, reaching deep states cheaply.- The defining fact is that the backdoor bypasses the bus: use it to set up state (
poke) and sample non-destructively (peek), never to verify the bus (which it never touches). - A backdoor change can desync the mirror (it skips the frontdoor path) — predict it so a later frontdoor read does not false-mismatch (0.1).
- The signature bug is a wrong
hdl_path:pokereaches nothing andpeekreturns X with no bus activity to flag it — configure paths carefully, verify them withuvm_reg_mem_hdl_paths_seq, and never let a peekedXpass as data.