AMBA APB · Module 9
Error-Response Timing
The exact cycle PSLVERR must be asserted — coincident with PREADY on the actual completion edge — and the sample-window discipline it inherits. Why getting the error value right is useless if it lands in the wrong cycle, and how the verdict tracks the completion edge across zero- and N-wait accesses.
You already know what PSLVERR means — a pass/fail verdict, not an abort. This chapter is the rigorous when: the verdict is only correct if it is driven in exactly the right cycle. The single idea to carry: PSLVERR must be asserted coincident with PREADY high on the completion edge, produced on the same registered edge, stable across the entire sample window — don't-care before completion and gone after. A correct error value in the wrong cycle is a wrong error. Error-response correctness is a timing property pinned to the completion edge.
1. Problem statement
The problem is placing a one-bit verdict on exactly one clock edge — the edge where the transfer completes — with no margin for it to be early, late, or smeared.
Chapter 9.1 settled the semantics: PSLVERR is sampled at completion, the transfer happens regardless, recovery lives above the bus. That left the hard part untouched — the temporal contract. APB samples PSLVERR at precisely one instant per access: the PCLK rising edge where PSEL && PENABLE && PREADY are all high. There is no second look, no hold, no "was it high recently." So the slave's obligation is brutally specific:
- The verdict and the completion are the same event.
PSLVERRandPREADYmust rise on the same registered edge. If the verdict resolves one cycle beforePREADY(during a wait) it is sampled as0and the error is lost; if it resolves one cycle after, it is attributed to whatever access completes next. - The bit must be stable across the sample window. At the completion edge,
PSLVERRmust meet setup and hold aroundPCLKlike any sampled signal — no glitch, noX, no mid-window transition. A combinational error path that is still settling when the edge arrives can be sampled as a metastable or wrong value. - It tracks the actual completion edge, not a fixed cycle. Whether the access completes at zero waits or after N waits, the verdict lands wherever
PREADYlands. The slave cannot hard-code "assertPSLVERRin the access cycle" — it must assert it in the cycle it actually completes.
So the job is not "compute the error" — Chapter 9.1 did that. The job is "deliver that computed verdict on one specific edge, clean and stable, wherever that edge happens to be."
2. Why previous knowledge is insufficient
Chapter 9.1 — PSLVERR behaviour taught you the meaning of the bit: verdict not abort, sampled at completion, recovery above the bus. That is the semantic contract. Module 8 — PREADY timing taught the temporal discipline of the completion signal: register it, keep it glitch-free, respect the sample window. And Module 3 — the PREADY handshake gave you the completion edge itself. This chapter sits at the intersection — and the gaps are exactly where those three stop:
- 9.1 told you the bit is sampled at completion; it did not tell you how to produce it there. "Valid at the completion edge" is a consumer-side statement. The producer-side question — how does the slave guarantee the bit is correct on that exact edge across any wait count — is this chapter. Knowing where to read is not knowing how to drive.
PSLVERRinherits Module 8'sPREADYtiming discipline, but on a second signal that must match the first. Module 8 madePREADYa clean registered edge.PSLVERRmust be a clean registered edge and the same edge asPREADY. The new failure mode is not "glitchyPSLVERR" alone — it is misalignment between two signals that the protocol assumes were generated together. That cross-signal alignment requirement does not exist anywhere in Module 8.- The completion edge moves, and the verdict must move with it. Module 3 fixed the structure of the handshake; it did not stress that the completion edge floats by wait count. A verdict pinned to a fixed cycle silently desynchronises from completion the moment wait states change — a class of bug the handshake chapter never had to consider because it only tracked one signal.
So the model to add is the alignment model: PSLVERR is not just "a clean bit valid at completion" — it is "the bit that must be born on the same registered edge as PREADY, and ride that edge wherever it lands." (Tracing a reported error back to which access actually caused it is the 9.6 discipline that depends entirely on this alignment being correct.)
3. Mental model
The model: PSLVERR and PREADY are two halves of one stamp pressed at the same instant. PREADY is the half that says "delivered now"; PSLVERR is the half that says "pass or fail." The protocol presses both halves with a single press — the completion edge. If the two halves are not aligned under that press, you get a smudged stamp: the "fail" half lands on the wrong package, or in the gap between packages where no one reads it.
Three refinements make it precise:
- One press, one edge. The slave registers
PSLVERRandPREADYoff the sameposedge PCLK. They are computed from the same condition (access completing this cycle) and latched together. There is no legitimate implementation wherePREADYis registered andPSLVERRis combinational off a still-settling decode — that is two presses at two times, and the protocol only reads one. - The press lands wherever completion lands. On a zero-wait access the stamp presses one cycle into the access phase; on a two-wait access it presses three cycles in. The verdict follows
PREADY. The slave must never assertPSLVERR"in the access phase" generically — only in the specific cycle it drivesPREADYhigh. - Stable for the whole window, blank outside it. Across the sample window around the completion edge,
PSLVERRmust hold its value with proper setup and hold — no glitch, noX, no mid-window edge. Before completion it is don't-care (drive0); after completion it is gone (back to0). The only legible reading is the value coincident withPREADYhigh, held still.
4. Real SoC implementation
In silicon the alignment is enforced structurally: PSLVERR and PREADY are written in the same always_ff branch, from the same completion condition, so by construction they cannot drift apart. The error term itself may be computed combinationally upstream, but it is captured on the completion edge alongside PREADY — never sampled directly by the manager while still settling. Here is the registered pair across an N-wait access, with the verdict tracking the actual completion edge:
// APB subordinate: PREADY and PSLVERR produced on the SAME registered edge.
// The access takes a variable number of wait states (wcnt counts them down).
// err_cond is the slave's "I cannot honour THIS access" term (unmapped addr,
// PPROT mismatch, internal fault). It is computed combinationally but must be
// CAPTURED on the completion edge alongside PREADY — never sampled mid-flight.
logic [1:0] wcnt; // remaining wait states for the current access
wire access_phase = psel && penable;
wire completing = access_phase && (wcnt == 2'd0); // THIS is the completion edge
always_ff @(posedge pclk or negedge presetn) begin
if (!presetn) begin
wcnt <= 2'd0;
pready <= 1'b0;
pslverr <= 1'b0;
end else if (psel && !penable) begin
// SETUP phase: load this access's wait count, hold outputs deasserted
wcnt <= wait_states_for(paddr); // 0 for fast regs, N for slow ones
pready <= 1'b0;
pslverr <= 1'b0; // don't-care to the manager, but drive 0
end else if (completing) begin
// COMPLETION EDGE: assert PREADY and stamp the verdict TOGETHER.
// Because both are written here, from the same `completing`, they are
// aligned by construction — the verdict rides the ACTUAL completion edge,
// whether wcnt started at 0 (zero-wait) or counted down from N (N-wait).
pready <= 1'b1;
pslverr <= err_cond; // SAME edge as PREADY — never one off
end else begin
// ACCESS · WAIT: still counting down. PREADY low, verdict not yet valid.
wcnt <= wcnt - 2'd1;
pready <= 1'b0;
pslverr <= 1'b0; // don't leak a stale/early verdict
end
end
// Contract: pslverr is meaningful ONLY in the cycle pready is high, and it is
// born on that same edge. There is no path where the verdict resolves a cycle
// early (lost in a wait) or a cycle late (blamed on the next access).Two facts drive the real design. First, the two signals share one register stage and one condition, so they cannot be one cycle apart — the most expensive error-timing bug (misattribution to the next access) is eliminated by structure, not by review. Second, the wait count makes the completion edge a runtime value, so the verdict must be gated on completing (the actual edge) rather than a fixed access-phase cycle; a slave that asserted PSLVERR whenever err_cond is true and access_phase is high would assert it through the waits — exactly the early-pulse bug Chapter 9.1's debug case showed. The combinational err_cond is fine as an input because it is captured by a flop on the completion edge; the manager never sees it un-registered.
5. Engineering tradeoffs
Error-response timing is a choice about where the alignment guarantee comes from — and each choice trades structural safety against timing slack.
| Error-path implementation | Alignment guarantee | Timing risk | When to choose it |
|---|---|---|---|
PSLVERR registered with PREADY (same branch, same condition) | Guaranteed by construction — both born on one edge | None beyond the shared flop | Almost always — the safe default for any failable slave |
PSLVERR combinational off err_cond, PREADY registered | None — verdict can settle in a wait or after completion | High: early-pulse loss, off-by-one misattribution, glitch in window | Never on APB; the two signals desynchronise |
PSLVERR registered, but off a different condition than PREADY | Fragile — relies on two conditions staying identical forever | Medium: drifts the day wait-state logic changes | Avoid; collapse to one shared completing term |
err_cond combinational, captured by the completion flop | Guaranteed — only the captured value reaches the bus | Low: err_cond must settle before setup, but never sampled raw | Good — keeps decode flexible while pinning the sampled bit |
Glitchy decode driving PSLVERR directly (no capture flop) | None — a glitch can land in the sample window | High on fast clocks: spurious or metastable verdict | Never — the verdict bit must be a clean flop output |
The throughline: on APB the error-response timing problem is not "compute the right verdict" — it is "guarantee the right verdict reaches exactly the completion edge, stable." The only robust way to guarantee that is to make PSLVERR and PREADY two outputs of one registered decision. Every other row buys nothing and reintroduces the off-by-one and glitch hazards.
6. Common RTL mistakes
7. Debugging scenario
A faulting peripheral does report an error — but the bus-fault names the wrong access. Bring-up software hits a known-bad register (access A), and the fault handler fires while pointing at the next, perfectly valid access (B). The fault is real; the attribution is off by one.
-
Observed symptom: every time the firmware touches the faulting register A and then immediately touches a good register B, the bus-fault / error-status logs blame B, not A. Access A itself returns clean. Re-ordering so a clean access follows A makes the new follower take the blame — the error always slides to whatever completes next.
-
Waveform clue: at access A's completion edge (
PSEL && PENABLE && PREADYhigh),PSLVERRis low. OnePCLKlater — at access B's completion edge —PSLVERRis high, even though B is a valid access.PREADYis correctly aligned to each access; onlyPSLVERRis shifted one cycle to the right ofPREADY. -
Root cause:
PSLVERRwas registered through an extra flop (or off acompletingterm that was itself a cycle delayed), so the verdict for A is latched one edge after A'sPREADY. The slave computed the right verdict for the right access — but pinned it to the wrong completion edge, so it lands on B. The error value is correct; the error timing is off by one. -
Correct RTL: register
PSLVERRin the same branch and off the samecompletingcondition asPREADY, with no extra pipeline stage:else if (completing) begin pready <= 1'b1; pslverr <= err_cond; end. Now the verdict is born on A's completion edge by construction and can never slide onto B. -
Verification assertion: assert that whenever
PSLVERRis high it is on a completion edge (so it cannot float into the next access), and that the verdict is stable and known at that edge:Azvya Education Pvt. Ltd.VLSI MentorSnippet// PSLVERR may be high ONLY on a completion edge — never a cycle off. ap_pslverr_on_completion: assert property (@(posedge pclk) disable iff (!presetn) pslverr |-> (psel && penable && pready)); // At every completion edge the verdict must be a known, stable value. ap_pslverr_known_at_done: assert property (@(posedge pclk) disable iff (!presetn) (psel && penable && pready) |-> !$isunknown(pslverr)); -
Debug habit: when an error is reported on the wrong access, do not audit the error-decode logic — its value is right. Overlay
PSLVERRonPREADYand check they rise on the same edge. IfPSLVERRis even one cycle offPREADY, it is an alignment bug (an extra flop, a delayedcompleting), and the fix is in the register stage, never in the decode.
8. Verification perspective
PSLVERR timing is a cross-signal alignment property, so verification must pin both signals together and across every wait count — a checker that only watches PSLVERR in isolation, or only on zero-wait accesses, misses the entire bug class.
- Assert alignment to the actual completion edge. The core property is
pslverr |-> (psel && penable && pready)—PSLVERRmay be high only on a completion edge. This single assertion catches the off-by-one (verdict on the next access), the early pulse (verdict in a wait), and the stuck-high (verdict bleeding past completion). Pair it with(psel && penable && pready) |-> !$isunknown(pslverr)so the verdict is neverXat the sampled edge. These two are the non-negotiable error-timing checks. - Cover the verdict across zero-wait and N-wait completions. Because the completion edge floats, functional coverage must cross
PSLVERR=1with wait-count buckets: error on a zero-wait access, error on a 1-wait, error on an N-wait. A suite that only ever errors at zero waits never exercises the moving completion edge — exactly where the "verdict pinned to a fixed cycle" bug hides. Also cover error-then-clean back-to-back (the misattribution corner) so a shifted verdict is caught landing on the follower. - Take it to gate level for glitches. RTL hides combinational glitches; a
PSLVERRdriven off a settling decode can glitch in the sample window only on a real clock. Run the alignment and never-Xassertions at gate level / with SDF so a glitch or a setup/hold violation on the verdict bit around the completion edge is caught. On fast clocks this is where a "passing in RTL" error path fails in silicon.
The point: error-response correctness is verified as a timing property, not a value property. Assert PSLVERR is high only on a real completion edge and never X there, cover the verdict across zero/N waits and the error-then-clean corner, and confirm at gate level that the verdict bit is glitch-free in the sample window. (Methodology for tracing a reported error to its true source builds directly on these alignment guarantees holding.)
9. Interview discussion
"When exactly must PSLVERR be asserted, and what breaks if it's a cycle off?" separates someone who memorised "sampled at completion" from someone who has debugged it. The weak answer stops at "at the completion edge." The strong answer makes it a cross-signal alignment statement and names the failure modes.
Frame it as one edge, two signals, born together: PSLVERR must be asserted coincident with PREADY high on the completion edge (PSEL && PENABLE && PREADY), produced on the same registered edge from the same completion condition, and stable across the sample window — don't-care/0 before completion, gone after. Then deliver the depth: the completion edge floats with wait count, so the verdict tracks the actual edge — at zero waits one cycle in, at N waits N+1 cycles in — and a slave that pins PSLVERR to a fixed access cycle desynchronises the moment waits change; the signature bug is an off-by-one, where PSLVERR is a flop late relative to PREADY, so a fault on access A slips onto the clean access B that completes next — the value is right, the cycle is wrong, and you find it by overlaying PSLVERR on PREADY and checking they rise on the same edge; and the glitch hazard — a combinational PSLVERR off a settling decode can be sampled mid-glitch on a fast clock, so the verdict must be a clean flop output, not raw combinational logic. Closing with "this is the PREADY-timing discipline of Module 8 applied to the verdict bit, except now two signals must match at the edge, not just one be clean" shows you see the structure: error correctness is a timing property pinned to the completion edge, and getting the value right is worthless if it lands in the wrong cycle.
10. Practice
- Pin the edge across waits. For a zero-wait and a two-wait access of the same faulting slave, mark the exact cycle
PSLVERRmust go high in each, and state why it is a different cycle but the same relationship toPREADY. - Spot the off-by-one. Given a waveform where
PSLVERRrises onePCLKafterPREADYacross two back-to-back accesses, identify which access the error is attributed to versus which one caused it, and the one-line RTL fix. - Write the aligned pair. From memory, write the
always_ffbranch that assertsPREADYandPSLVERRtogether on the completion edge of an N-wait access, and explain why putting them in the same branch makes misalignment structurally impossible. - Combinational vs captured. Explain why a combinational
err_condfeeding a capture flop is safe, but a combinationalPSLVERRdriven straight to the bus is not — in terms of the sample window on a fast clock. - Assert the window. Write the two SVA properties that catch (a)
PSLVERRhigh off a completion edge and (b)PSLVERRunknown at the completion edge, and say which bug each one catches.
11. Q&A
12. Key takeaways
- The verdict and the completion are one event.
PSLVERRmust be asserted coincident withPREADYhigh on the completion edge — produced on the same registered edge, from the same completion condition. Two outputs of one decision, not two racing paths. - A right value in the wrong cycle is a wrong error. If
PSLVERRresolves a cycle early it is lost in a wait; a cycle late it is misattributed to the next access. The cycle is part of the verdict. - The completion edge floats — the verdict tracks it. At zero waits it lands one cycle in; at N waits, N+1 cycles in. Gate
PSLVERRon the actualcompletingcondition, never a fixed access cycle, or it desynchronises when wait states change. - The signature bug is an off-by-one —
PSLVERRa flop late relative toPREADY, so a fault on access A lands on the clean access B. Find it by overlayingPSLVERRonPREADY; the fix is in the register stage, not the decode. - Drive a clean, stable flop output across the sample window —
err_condmay be combinational as an input if captured by the completion flop, but the bus must never see raw combinationalPSLVERR, or a decode glitch can be sampled in the window on a fast clock. Don't-care/0before completion, gone after. - Verify it as a timing property: assert
PSLVERRhigh only on a real completion edge and neverXthere, cover the verdict across zero/N waits and the error-then-clean corner, and confirm at gate level that the verdict bit is glitch-free in the sample window.