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 if (access_phase) 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 else begin
    // IDLE / UNSELECTED. Two things matter here and both are easy to miss.
    //
    // 1. Do NOT decrement wcnt - there is no access in flight, and a 2-bit
    //    counter decremented from 0 wraps to 3.
    // 2. Drive PREADY HIGH, not low. An unselected subordinate holding PREADY
    //    low hangs any bridge that ANDs its subordinates' PREADY outputs -
    //    a legal and common bridge implementation. AMBA leaves the combining
    //    to the bridge, so driving high when unselected is the portable habit.
    wcnt    <= wcnt;
    pready  <= 1'b1;
    pslverr <= 1'b0;
  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).
//
// LATENCY NOTE: because both outputs are registered, `completing` asserting in
// one cycle makes PREADY high in the NEXT. So wait_states_for() returning 0
// still costs one ACCESS wait cycle - the minimum latency of any registered
// PREADY, discussed under the tradeoffs below. Name the function for what it
// controls (additional waits) rather than for the total, or the datasheet
// number and the silicon will disagree by one.

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.

4b. The error timing on one waveform

Errored access with wait states, then a clean access

8 cycles
An APB errored access showing SETUP, two wait cycles with PREADY and PSLVERR both low, a completing cycle where PREADY and PSLVERR rise together, PENABLE dropping, and a following normal access completing with PSLVERR lowSETUP — one cycle; the subordinate loads its wait countSETUP — one cycle; thesubordinate loads its waitcountwaiting: PREADY low, and PSLVERR low with it — no verdict yetwaiting: PREADY low, andPSLVERR low with it — noverdict yetcompleting cycle: PREADY and PSLVERR rise on the SAME edgecompleting cycle: PREADYand PSLVERR rise on theSAME edgenext SETUP — the subordinate recovered, not just reportednext SETUP — thesubordinate recovered, notjust reportedclean completion: PSLVERR low, PRDATA validclean completion: PSLVERRlow, PRDATA validPCLKPSELPENABLEPADDRRSVDRSVDRSVDRSVD--OKOKOKPREADYPSLVERRPRDATA00000000--0000D1t0t1t2t3t4t5t6t7
Figure 2b — an errored access with two wait states, and the normal access that follows it. Cycle 1 is SETUP: PSEL high, PENABLE low, exactly one cycle, and the subordinate loads its wait count. Cycles 2 to 4 are ACCESS: PENABLE is high throughout, PREADY is low for the two waiting cycles, and PSLVERR stays low with it — a verdict is not valid until the transfer completes. On cycle 4 PREADY and PSLVERR rise together on the same registered edge, which is the completing cycle and the only cycle on which PSLVERR carries meaning. PRDATA is driven to zero rather than to a stale or partially-computed value, because an errored read returned nothing. Cycle 5 shows PENABLE dropping, and cycle 6 is the SETUP of the next transfer, which completes normally on cycle 8 with PSLVERR low — proving the subordinate recovered rather than merely reported. The two things this figure fixes in most people's mental model: PSLVERR is not a level that persists through the access, and it is not sampled anywhere except the cycle PREADY is high.

4c. Checking it

apb_error_sva.sv — PSLVERR validity and error completion
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module apb_error_sva #(parameter int MAX_WAIT_POLICY = 32) (
  input logic pclk, presetn,
  input logic psel, penable, pwrite, pready, pslverr,
  input logic [31:0] paddr, pwdata, prdata
);
  wire completing = psel && penable && pready;
 
  // --- PSLVERR validity -------------------------------------------------
  // The verdict exists only on the completing cycle. Asserting it during a
  // wait is the "early pulse" bug; asserting it in SETUP is worse, because
  // the manager may sample a level that belongs to no transfer at all.
  a_pslverr_only_on_completion: assert property (@(posedge pclk) disable iff (!presetn)
    pslverr |-> completing)
    else $error("PSLVERR asserted outside the completing ACCESS cycle");
 
  // It must be a known value when it IS sampled - an X here propagates
  // straight into the manager's fault decision.
  a_pslverr_known: assert property (@(posedge pclk) disable iff (!presetn)
    completing |-> !$isunknown(pslverr));
 
  // --- The alignment guarantee ------------------------------------------
  // PSLVERR must not rise before PREADY and then fall again before the
  // transfer completes - the verdict would be lost in the wait.
  a_no_early_verdict: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && penable && !pready) |-> !pslverr)
    else $error("PSLVERR asserted while the access was still waiting");
 
  // --- Request stability across the stall, including for errored access --
  a_request_stable: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && !completing) |=>
      $stable(paddr) && $stable(pwrite) && $stable(pwdata));
 
  // --- An errored read returns no meaningful data. This asserts the house
  //     rule that it is driven to a defined value rather than left stale,
  //     which keeps a failure reproducible. AMBA does not require a
  //     particular value, so treat this as project policy.
  a_errored_read_data_defined: assert property (@(posedge pclk) disable iff (!presetn)
    (completing && pslverr && !pwrite) |-> !$isunknown(prdata));
 
  // --- Recovery: the transfer after an error must be able to complete.
  a_recovers: assert property (@(posedge pclk) disable iff (!presetn)
    (completing && pslverr) |=> !penable);
 
  // --- ENGINEERING POLICY, not protocol. AMBA sets no maximum wait count.
  a_completes_within_policy: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && penable && !pready) |-> ##[1:MAX_WAIT_POLICY] pready);
 
  // --- Coverage. Every property above is vacuous on a run with no errors.
  c_err_zero_wait: cover property (@(posedge pclk)
                     (psel && !penable) ##1 (penable && pready && pslverr));
  c_err_waited:    cover property (@(posedge pclk)
                     (psel && penable && !pready) ##[1:$] (pready && pslverr));
  c_err_then_ok:   cover property (@(posedge pclk)
                     (completing && pslverr) ##[1:$] (completing && !pslverr));
endmodule

c_err_waited is the cover that matters most. A suite that only errors on zero-wait accesses never exercises the alignment the whole page is about, and every alignment property above passes vacuously.

1

A bus fault was reported for the wrong peripheral

PSLVERR-EARLY-PULSE
Symptom

A driver reported bus faults on a timer peripheral that had no fault condition and, on inspection, was working correctly. The faults were intermittent — perhaps one access in thirty — and correlated with system load rather than with anything the timer was doing.

The faulting accesses were always timer reads that immediately followed an access to a different peripheral: the ADC, which is behind a slow analog block and inserts several wait states. Accesses to the timer in isolation never faulted.

Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// PSLVERR driven from the error condition whenever the access phase is active.
assign pslverr = access_phase && err_cond;    // ✗ true throughout the WAITS
assign pready  = access_phase && (wcnt == 0);
Diagnostic Evidence

A waveform across the peripheral boundary made it visible:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  cycle          1      2      3      4      5      6
  target       ADC    ADC    ADC    ADC    --    TIMER
  PSEL_adc       1      1      1      1      0      0
  PENABLE        0      1      1      1      0      1
  PSLVERR        0      1      1      1      0      0     <- high during WAITS
  PREADY         0      0      0      1      1      1
                                     ^completing

The ADC asserted PSLVERR from the first ACCESS cycle and held it through its wait states. On the ADC's own completing cycle the verdict was correct, so the ADC's accesses behaved properly — and the manager, in this SoC, sampled PSLVERR on every ACCESS cycle rather than only on the completing one.

That is a manager bug in itself, but the subordinate handed it the opportunity. The stray high on cycles 2 and 3 was attributed by the manager's fault logic to whatever transfer it was tracking, and under back-to-back traffic that was sometimes the following timer access.

Root Cause

PSLVERR is defined as valid only on the cycle where PSEL, PENABLE and PREADY are all high. The subordinate drove it from access_phase && err_cond, which is true from the first ACCESS cycle onwards — so the verdict was published before the transfer had completed and held through every wait state.

Two properties make this expensive rather than merely untidy. The error is attributed to the wrong peripheral, because the stray assertion overlaps the window in which a manager may be tracking a different transfer — so the debugging starts at an innocent device. And it is load-dependent, appearing only when a slow access is followed closely by another, which makes it look like a concurrency or arbitration problem rather than a signal-validity one.

The subordinate is not compliant here even though a strict manager would be immune. AMBA specifies when PSLVERR is valid, and the widely-followed practice is to drive it low outside that window precisely so that a manager sampling more eagerly cannot be misled.

Fix

Register the verdict onto the completion edge, so it cannot exist before the transfer does:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire completing = access_phase && (wcnt == 2'd0);
 
always_ff @(posedge pclk or negedge presetn)
  if (!presetn) begin
    pready <= 1'b0; pslverr <= 1'b0;
  end else if (completing) begin
    pready  <= 1'b1;
    pslverr <= err_cond;        // born on the SAME edge as PREADY
  end else begin
    pready  <= 1'b0;
    pslverr <= 1'b0;            // no verdict outside the completing cycle
  end

Writing both from one condition in one register stage makes the alignment structural: there is no path in which the verdict resolves a cycle early or a cycle late, so the failure is unreachable rather than merely absent.

The test that fails on the old RTL and passes on the new one is a waited errored access with the assertion pslverr |-> psel && penable && pready. The original suite had none, because every error it injected was zero-wait — and on a zero-wait access the buggy and correct forms are indistinguishable.

Two habits generalise.

Any signal that is "valid only on cycle X" should be driven only on cycle X. Relying on the consumer to sample at the right moment is relying on someone else's correctness for your own compliance, and it converts your bug into their bug report.

Cover the waited case separately from the zero-wait case. Alignment properties are vacuous when there is nothing to be misaligned with, so a suite that only errors on fast accesses proves nothing about the mechanism it was written to check.

See PREADY timing for the sampling-window rules this rests on and PREADY common design bugs for the related wait-count failures.

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

11b. Where This Is Specified

  • Arm AMBA APB Protocol Specification (ARM IHI 0024). The IDLE → SETUP → ACCESS model; PSEL asserted with PENABLE low for exactly one SETUP cycle; the transfer completing only when PSEL && PENABLE && PREADY; PSLVERR being valid only on that completing cycle; the requirement that address, direction and write data are stable for the whole transfer; and the absence of any maximum wait-state count.
  • Arm AMBA APB — PSLVERR. Defined as an optional signal; where a subordinate does not implement it the manager sees it tied low. AMBA does not specify what PRDATA carries on an errored read, nor mandate driving PSLVERR low outside its valid window — both are widely-followed practice rather than requirements.
  • IEEE 1800-2023 §16 — Assertions. Concurrent properties, $stable, $isunknown, cover property, and the ##[m:n] bounded range that makes the policy completion bound falsifiable in simulation.

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.

Standards & specifications

Governing standard
Arm AMBA APB Protocol Specification (IHI 0024)(opens Arm in a new tab)

Defines the APB setup/access phases and the PREADY and PSLVERR signalling. Peripheral register layout and verification approach are outside its scope.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the AMBA APB curriculum.