Skip to content

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. PSLVERR and PREADY must rise on the same registered edge. If the verdict resolves one cycle before PREADY (during a wait) it is sampled as 0 and 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, PSLVERR must meet setup and hold around PCLK like any sampled signal — no glitch, no X, 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 PREADY lands. The slave cannot hard-code "assert PSLVERR in 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.
  • PSLVERR inherits Module 8's PREADY timing discipline, but on a second signal that must match the first. Module 8 made PREADY a clean registered edge. PSLVERR must be a clean registered edge and the same edge as PREADY. The new failure mode is not "glitchy PSLVERR" 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 PSLVERR and PREADY off the same posedge PCLK. They are computed from the same condition (access completing this cycle) and latched together. There is no legitimate implementation where PREADY is registered and PSLVERR is 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 assert PSLVERR "in the access phase" generically — only in the specific cycle it drives PREADY high.
  • Stable for the whole window, blank outside it. Across the sample window around the completion edge, PSLVERR must hold its value with proper setup and hold — no glitch, no X, no mid-window edge. Before completion it is don't-care (drive 0); after completion it is gone (back to 0). The only legible reading is the value coincident with PREADY high, held still.
An APB timing diagram of a two-wait access with PSEL and PENABLE high, PREADY low for two waits then high at completion, and PSLVERR hatched as don't-care through setup and both waits, becoming the verdict only at the completion edge coincident with PREADY high, held stable across the sample window and gone afterwards.
Figure 1 — PSLVERR asserted coincident with PREADY on the actual completion edge of a two-wait access. PSEL and PENABLE are high across the access; PREADY is low through both wait cycles and high at the third edge, which completes the transfer. PSLVERR is hatched don't-care through setup and both waits — the subordinate may drive anything there — then becomes the genuine verdict only at the completion edge, registered together with PREADY off the same clock edge and held stable across the whole sample window, before going back to 0 afterwards. The figure marks that one completion edge in blue and stresses the verdict tracks the actual completion edge wherever it lands: at zero waits it would press one cycle earlier; at N waits it presses here — always the same edge as PREADY.

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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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 implementationAlignment guaranteeTiming riskWhen to choose it
PSLVERR registered with PREADY (same branch, same condition)Guaranteed by construction — both born on one edgeNone beyond the shared flopAlmost always — the safe default for any failable slave
PSLVERR combinational off err_cond, PREADY registeredNone — verdict can settle in a wait or after completionHigh: early-pulse loss, off-by-one misattribution, glitch in windowNever on APB; the two signals desynchronise
PSLVERR registered, but off a different condition than PREADYFragile — relies on two conditions staying identical foreverMedium: drifts the day wait-state logic changesAvoid; collapse to one shared completing term
err_cond combinational, captured by the completion flopGuaranteed — only the captured value reaches the busLow: err_cond must settle before setup, but never sampled rawGood — keeps decode flexible while pinning the sampled bit
Glitchy decode driving PSLVERR directly (no capture flop)None — a glitch can land in the sample windowHigh on fast clocks: spurious or metastable verdictNever — 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 && PREADY high), PSLVERR is low. One PCLK later — at access B's completion edge — PSLVERR is high, even though B is a valid access. PREADY is correctly aligned to each access; only PSLVERR is shifted one cycle to the right of PREADY.

  • Root cause: PSLVERR was registered through an extra flop (or off a completing term that was itself a cycle delayed), so the verdict for A is latched one edge after A's PREADY. 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 PSLVERR in the same branch and off the same completing condition as PREADY, 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 PSLVERR is 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 Mentor
    Snippet
    // 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 PSLVERR on PREADY and check they rise on the same edge. If PSLVERR is even one cycle off PREADY, it is an alignment bug (an extra flop, a delayed completing), and the fix is in the register stage, never in the decode.

Two stacked APB timing diagrams over two accesses A and B. Top correct case: PSLVERR registered with PREADY, high at A's completion edge in green, low at B. Bottom buggy case: PSLVERR shifted one cycle right of PREADY in red, low at A's completion edge so the fault is lost, high at B's completion edge so the clean access is wrongly blamed.
Figure 2 — correct edge-aligned PSLVERR versus an off-by-one verdict that misattributes a fault. Two back-to-back accesses: A is faulting (expect FAIL), B is clean (expect PASS). Top (correct, green): PSLVERR is registered on the same edge as PREADY, so it is high exactly at A's completion edge and low at B's — the fault is pinned to A. Bottom (bug, red): PSLVERR is registered one cycle later than PREADY, so A's FAIL verdict slips past A's completion edge and lands on B's completion edge instead; the manager samples PSLVERR high at B and blames the good access while A returns clean. The figure shows the slave computed the right verdict VALUE but pinned it to the wrong completion edge, and that the fix is to align the PSLVERR register to PREADY, not to re-examine the error logic.

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)PSLVERR may 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 never X at 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=1 with 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 PSLVERR driven off a settling decode can glitch in the sample window only on a real clock. Run the alignment and never-X assertions 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

  1. Pin the edge across waits. For a zero-wait and a two-wait access of the same faulting slave, mark the exact cycle PSLVERR must go high in each, and state why it is a different cycle but the same relationship to PREADY.
  2. Spot the off-by-one. Given a waveform where PSLVERR rises one PCLK after PREADY across two back-to-back accesses, identify which access the error is attributed to versus which one caused it, and the one-line RTL fix.
  3. Write the aligned pair. From memory, write the always_ff branch that asserts PREADY and PSLVERR together on the completion edge of an N-wait access, and explain why putting them in the same branch makes misalignment structurally impossible.
  4. Combinational vs captured. Explain why a combinational err_cond feeding a capture flop is safe, but a combinational PSLVERR driven straight to the bus is not — in terms of the sample window on a fast clock.
  5. Assert the window. Write the two SVA properties that catch (a) PSLVERR high off a completion edge and (b) PSLVERR unknown 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. PSLVERR must be asserted coincident with PREADY high 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 PSLVERR resolves 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 PSLVERR on the actual completing condition, never a fixed access cycle, or it desynchronises when wait states change.
  • The signature bug is an off-by-onePSLVERR a flop late relative to PREADY, so a fault on access A lands on the clean access B. Find it by overlaying PSLVERR on PREADY; the fix is in the register stage, not the decode.
  • Drive a clean, stable flop output across the sample windowerr_cond may be combinational as an input if captured by the completion flop, but the bus must never see raw combinational PSLVERR, or a decode glitch can be sampled in the window on a fast clock. Don't-care/0 before completion, gone after.
  • Verify it as a timing property: assert PSLVERR 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.