Skip to content

AMBA APB · Module 9

Error Debug Methodology

A repeatable forensic method for tracing an observed PSLVERR to its root cause — confirm it at the completion edge, capture the access context, then classify the source by elimination (decode, protection, or peripheral failure) — and how to tell a real fault from a lost or spurious error.

A PSLVERR is a symptom, not a diagnosis. This chapter is the module closer: it takes everything 9.1–9.5 established — the verdict semantics, the error timing, and each error source — and forges them into one disciplined procedure for tracing an observed error back to its root cause from a waveform. The single idea to carry: confirm the error is real at the completion edge, capture the access context at that same edge, then eliminate the source by asking three ordered questions — was the address mapped, was the protection sufficient, and if both, what does the peripheral's status bit say. Done right, a reported error becomes a root cause in minutes, not a multi-day bug hunt.

1. Problem statement

The problem is converting an observed PSLVERR into a specific root cause — fast, repeatably, and without guessing — on a protocol that gives you exactly one bit of evidence and no transaction ID, no error code, and no recovery to inspect.

When software reports "bus fault at address X" or a monitor flags a PSLVERR, you are handed a single fact: some completed access failed. APB does not tell you why. The slave that asserted the bit knows the reason internally, but the bus carries none of it. So the engineer must reconstruct the cause from the waveform — and the trap is that there are at least four distinct things a PSLVERR can mean, and they demand opposite fixes:

  • A real decode error — the address was never mapped (fix the address map or the master, 9.3).
  • A real protection violation — the address was mapped but PPROT was insufficient (fix permissions or the master's privilege, 9.4).
  • A real peripheral fault — the access was legal but the slave could not honour it (fix the peripheral or the load that broke it, 9.5).
  • Not a real error at all — a phantom: a mis-sampled, glitched, or misaligned bit that never represented a failure (fix the timing/wiring, 9.1/9.2).

Pointing at the wrong one wastes days. The job is therefore not "see the error" — it is a procedure that, applied to any waveform, lands on exactly one of those four and proves it.

2. Why previous knowledge is insufficient

Every prior chapter in this module taught you a piece of the error story, and each is necessary here — but none of them, alone, tells you how to triage a live error.

  • 9.1 PSLVERR behaviour taught the verdict: PSLVERR is sampled only on the completion edge (PSEL && PENABLE && PREADY), it is not an abort, and it means nothing elsewhere. That is the tool for Step 1 — but it tells you how to read the bit, not what its being set implies.
  • 9.2 Error-response timing taught that the verdict must be aligned to the actual completion edge across wait states. That is what lets you trust a sampled 1 and reject a misaligned pulse as a phantom — but it does not classify a real error.
  • 9.3 Illegal-address access taught one source: an unmapped PADDR. 9.4 Protection violations taught a second: insufficient PPROT. 9.5 Peripheral failure modes taught the third: a legal access the slave still cannot honour (overflow, not-ready, internal fault). Each chapter taught its source in isolation — how that error is produced and what it looks like.

What none of them did was answer the question you actually face in the lab: "I have one PSLVERR in front of me — which of these is it?" Knowing five sources separately is not the same as having a procedure to discriminate between them from a single waveform. The missing skill is elimination under evidence: a fixed order of questions where each answer rules a source in or out, so the very first "no" you hit is the root cause. This chapter supplies that discriminator and binds the five chapters into one method.

3. Mental model

The model: a PSLVERR is a crime scene, and the completion edge is the only admissible evidence. You do not start with theories; you start by confirming a crime even happened, then you collect the evidence at the scene, then you eliminate suspects in a fixed order until one remains. The procedure is the decision flow below — five steps, run top to bottom, stopping at the first failed test.

  1. Confirm it is real (Step 1). Read PSLVERR only at the completion edge — the PCLK edge where PSEL && PENABLE && PREADY are all high. If it is not 1 there, you do not have an error: a 1 during setup or a wait is a phantom (9.1), and a misaligned pulse is a timing bug (9.2). Never proceed on a bit sampled anywhere else.
  2. Capture the context (Step 2). At that same edge, latch {PADDR, PWRITE, PPROT, which PSELx}. This is the only context guaranteed coherent with the verdict. Sampling PADDR a cycle late captures the next access's address and sends you chasing the wrong slave.
  3. Eliminate by address (Step 3a). Was PADDR inside a mapped range? No → decode error (9.3) — done. Yes → continue.
  4. Eliminate by protection (Step 3b). Was PPROT sufficient for that target (privilege, secure, data/instruction)? No → protection violation (9.4) — done. Yes → continue.
  5. Attribute to the peripheral (Step 4/5). The access was mapped and permitted, so the bus was clean — the fault is internal. Confirm by reading the slave's sticky status / error bit (FIFO overflow, parity, timeout). That bit names the failure mode (9.5).

Two refinements make the model precise:

  • The order is not arbitrary — it goes from cheapest/most-external to most-internal. Address and protection are bus-visible facts you can check from the trace alone; a peripheral fault requires reading the slave's registers. Front-loading the bus checks means most errors resolve without touching the DUT's internals.
  • Reaching the bottom is itself a result. If decode and protection are both ruled out, "it must be the peripheral" is not a guess — it is the only remaining suspect, and the status bit is the confession.
A top-to-bottom flowchart: confirm PSLVERR at the completion edge (no exits to phantom), capture PADDR/PWRITE/PPROT/PSEL, then decide PADDR mapped (no to decode error 9.3), PPROT sufficient (no to protection violation 9.4), else peripheral failure 9.5 confirmed by a status bit.
Figure 1 — the forensic decision procedure as a flowchart. It starts by confirming the error is real (PSLVERR sampled at the completion edge where PSEL, PENABLE, and PREADY are all high); a no exits to a phantom/mis-sample. It then captures PADDR, PWRITE, PPROT, and the active PSEL id at that same edge, and eliminates the source in a fixed order: PADDR mapped? (no → decode error, 9.3); PPROT sufficient? (no → protection violation, 9.4); else the access was legal on the bus and the fault is peripheral-internal (9.5), confirmed by the slave's sticky status bit. Green is the clean legal path, red the fault branches, blue the decision and sampling steps, amber the capture step. The first no you reach is the root cause; reaching the bottom means the bus was clean.

4. Real SoC implementation

In a real SoC, you do not eyeball this on every error — you instrument the bus so the procedure runs automatically. A small APB error monitor, snooping the master/bridge signals, captures the context on every completion-edge PSLVERR and classifies the source by the same elimination, emitting a structured error record. The same logic is invaluable as a debug-mode hardware monitor and as a verification scoreboard.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// APB error forensics monitor: on a completion-edge PSLVERR, capture the
// access context AT THAT EDGE and classify the source by elimination.
// Mirrors the 5-step method exactly: confirm -> capture -> decode? -> prot? -> peripheral.
typedef enum logic [1:0] { SRC_DECODE, SRC_PROT, SRC_PERIPH } err_src_e;
 
module apb_err_monitor #(
  parameter int ADDR_W = 32
)(
  input  logic              pclk,
  input  logic              presetn,
  input  logic              psel,      // OR of the slave selects this monitor watches
  input  logic              penable,
  input  logic              pready,
  input  logic              pslverr,
  input  logic              pwrite,
  input  logic [ADDR_W-1:0] paddr,
  input  logic [2:0]        pprot,
  input  logic [3:0]        psel_id,   // which PSELx fired (target identity)
  // --- pre-computed system facts (from the address map / protection policy) ---
  input  logic              addr_mapped,   // is paddr in ANY mapped range?
  input  logic              prot_ok        // is pprot sufficient for this target?
);
 
  // Step 1: the completion edge is the ONLY place PSLVERR is evidence.
  wire completion_edge = psel && penable && pready;
  wire real_error      = completion_edge && pslverr;   // a 1 anywhere else is a phantom
 
  always_ff @(posedge pclk) begin
    if (presetn && real_error) begin
      err_src_e src;
      // Step 2: context is captured at the SAME edge (paddr et al. are coherent here).
      // Step 3a -> 3b -> 4: first failed test wins.
      if      (!addr_mapped) src = SRC_DECODE;  // unmapped  -> 9.3
      else if (!prot_ok)     src = SRC_PROT;     // permission -> 9.4
      else                   src = SRC_PERIPH;   // legal bus access, internal fault -> 9.5
 
      $display("[APB-ERR] t=%0t edge=1 src=%s paddr=%h write=%b pprot=%b target=%0d",
               $time, src.name(), paddr, pwrite, pprot, psel_id);
      // For SRC_PERIPH the monitor cannot see WHY from the bus alone:
      // the testbench/firmware must follow up by reading the slave's status reg.
    end
  end
endmodule

Two facts drive the real design. First, the capture and the classification must both key off completion_edgeaddr_mapped, prot_ok, and the context are only meaningful coincident with the verdict; latch them on any other cycle and you classify the wrong access. Second, the monitor can fully attribute decode and protection from bus signals alone, but it cannot see a peripheral fault's reason — the bus carries no overflow/parity/timeout encoding. So SRC_PERIPH is a negative result ("not decode, not protection") that must be confirmed by reading the slave's sticky status register; the elimination narrows it, the status bit names it.

5. Engineering tradeoffs

The forensic method is a sequence of checks, and the heart of it is the decision table — what each observation at the completion edge rules in or out. Memorise this table and you can triage any APB error from a trace.

Observation at the completion edgeWhat it rules IN / OUTSource it points to
PSLVERR=1 only outside the edge (setup/wait), 0 at completionNo real error — rules out all real sourcesPhantom (9.1) — mis-sample / misaligned
PSLVERR is X at the edge, or pulses then vanishes before completionUndriven or combinational verdict — rules out a trustworthy resultPhantom / timing bug (9.2)
PSLVERR=1 at the edge and PADDR not in any mapped rangeConfirms real; rules IN decodeDecode error (9.3) — fix map/master
PSLVERR=1, PADDR mapped, but PPROT insufficient for targetRules OUT decode; rules IN protectionProtection violation (9.4) — fix privilege/policy
PSLVERR=1, PADDR mapped, PPROT sufficientRules OUT decode and protectionPeripheral fault (9.5) — read status bit
PADDR mapped, PPROT ok, but slave status bit clearBus clean yet no peripheral reason — rules IN a wiring/sample bugSpurious — verdict not produced by a real condition
A known-faulting access returns PSLVERR=0 at the edgeThe error never reached the bus correctlyLost error — misalignment (9.2) or unconsumed verdict

The throughline: the table is read top-down at one edge, and the first row that matches is the answer. The two "false" rows bracket the real ones — a phantom is a PSLVERR that should not be trusted, a lost error is a real fault that never surfaced, and a spurious error is a 1 with no backing condition. Distinguishing them is exactly the difference between debugging the slave's logic and debugging its timing or wiring.

6. Common RTL mistakes

7. Debugging scenario

This is the method run end-to-end on a real intermittent fault — the kind that only appears in the field, under load, and looks at first like a decode bug.

  • Observed symptom: a deployed SoC throws an occasional bus-fault exception writing to a DMA-fed peripheral, but only under heavy traffic; the same firmware on the bench never faults. Software logs "fault at 0x4002_0010," and the first instinct on the team is "the address map is wrong" or "a protection bit flipped."
  • Waveform clue: capturing the bus around the fault, the engineer marks the completion edge (PSEL2 && PENABLE && PREADY high) and reads PSLVERR only there — it is 1, so the error is real (Step 1 passes; not a phantom). At that same edge the captured context is PADDR = 0x4002_0010, PWRITE = 1, PPROT = privileged-data, target = PSEL2 (the FIFO-controlled peripheral).
  • Root cause: running the elimination — PADDR is inside a mapped range (decode ruled out, 9.3); PPROT is sufficient for that target (protection ruled out, 9.4) — so the access was legal on the bus. Reading the slave's status register at fault time shows the sticky FIFO_OVF bit set. Under load the DMA outpaces the peripheral's drain, the write FIFO overflows, and the slave correctly asserts PSLVERR for the access it cannot accept. The root cause is a peripheral failure mode under load (9.5)not a decode or protection bug. The address was a red herring; it only ever looked like a map problem because nobody read PSLVERR at the right place and ran the elimination.
  • Correct RTL: the slave is behaving correctly — it errored on an access it could not honour and set its sticky bit. The real fix is system-level (throttle the DMA, add backpressure, or size the FIFO for the worst-case burst); the debug fix is to wire the status read into the flow so the source is named, not guessed:
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// On a real, bus-legal PSLVERR, the source is peripheral — name it via status.
// (addr_mapped & prot_ok already true => decode/protection eliminated.)
always_ff @(posedge pclk) begin
  if (real_error && addr_mapped && prot_ok) begin
    // Read-back of the target's sticky status pinpoints the failure mode.
    if (slave_status[FIFO_OVF]) $error("[APB-ERR] peripheral overflow under load @ %h", paddr);
    else                       $error("[APB-ERR] bus-legal PSLVERR with NO status bit -> spurious?");
  end
end
  • Verification assertion: pin the invariant the whole method leans on — every real (edge-sampled) PSLVERR that survives decode and protection elimination must be backed by a peripheral status bit; a bus-legal PSLVERR with no status bit is spurious and must fail in regression:
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// A bus-legal PSLVERR must be backed by SOME peripheral status bit (no spurious).
assert property (@(posedge pclk) disable iff (!presetn)
  (psel && penable && pready && pslverr && addr_mapped && prot_ok)
    |-> (|slave_status_error_bits));
  • Debug habit: when a fault "looks like" a decode problem, do not trust the address first — trust the edge first. Confirm PSLVERR at the completion edge, capture the context there, then eliminate in order. The address being mapped is what redirects you from the obvious-but-wrong decode theory to the real peripheral cause. The first failed test is the root cause; the address map being intact is a result, not a dead end.
An annotated APB waveform: PSLVERR sampled high at the completion edge, captured context shows PADDR mapped and PPROT sufficient so decode and protection are eliminated, and an elimination chain ends at a set FIFO_OVF status bit, root-causing a peripheral overflow under load.
Figure 2 — the same fault traced end-to-end. PSEL2 and PENABLE are high across an access with one wait state; PREADY is low through the wait and high at completion, where PSLVERR is sampled and reads 1 in red (a real error). The captured context at that edge — PADDR 0x4002_0010 (mapped), PWRITE 1, PPROT privileged-data (sufficient) — eliminates decode (9.3) and protection (9.4) in green. The elimination chain at the bottom then points to the slave's status register, where the sticky FIFO_OVF bit is set, root-causing the error to a peripheral overflow under load (9.5) rather than a bus-level decode or protection bug.

8. Verification perspective

The forensic method is not just a bench skill — it is a testbench architecture. Building it into the environment turns "we saw an error" into "every error was attributed and proven," and catches the silent failures a functional test never sees.

  • Build the monitor into the environment. Bind the error monitor from §4 to the APB interface so every completion-edge PSLVERR is captured and classified into {DECODE, PROT, PERIPH} automatically, with the context recorded. The monitor is the method, mechanised: it samples only at the edge, captures {PADDR, PWRITE, PPROT, PSEL} there, and runs the elimination on every transaction. This makes attribution a property of the environment, not a manual ritual.
  • Assert no lost and no spurious errors — the two failure modes the method exists to catch. No lost error: whenever the stimulus injected an error condition (an unmapped address, an insufficient PPROT, a forced peripheral fault), the scoreboard must see a PSLVERR=1 at that access's completion edge — a missing one is a lost error and must fail. No spurious error: a PSLVERR=1 at the edge with addr_mapped && prot_ok && !any_status_bit has no backing condition and must fail. Together these bracket every real source and forbid both invisible-real and visible-fake errors.
  • Cover that every source was both produced and correctly attributed. Functional coverage must cross each error source (decode / protection / peripheral) against the monitor's classification, so you prove not only that you generated each kind of error but that the method attributed it to the right source — a decode error classified as peripheral is a coverage hole that hides a real triage bug. Cross source × read/write × wait-count so the wait-aligned and write-error corners (where the timing phantoms live) are exercised, not just zero-wait reads.

The point: a verification plan that merely detects PSLVERR proves nothing about debuggability. Verify that the environment attributes every error to the right source, that no real error is ever lost, and that no clean access ever produces a spurious one — and cover source × direction × wait-count so the method is exercised everywhere a real silicon error could originate.

9. Interview discussion

"You see a PSLVERR on a waveform — walk me through finding the root cause" is a senior-level filter: it separates engineers who know the error sources from those who can triage them. A weak answer lists sources; a strong answer gives a procedure.

Frame it as confirm, capture, eliminate. First, confirm the error is real by reading PSLVERR only at the completion edge (PSEL && PENABLE && PREADY) — a 1 during setup or a wait, or an X at the edge, is a phantom, not a fault, and chasing it wastes days. Then capture the context at that same edgePADDR, PWRITE, PPROT, and which PSELx fired — because that is the only context coherent with the verdict. Then eliminate the source in a fixed order: was PADDR mapped? (no → decode error); was PPROT sufficient? (no → protection violation); if both pass, the access was legal on the bus, so the fault is internal — confirm it by reading the slave's sticky status bit (overflow, parity, timeout). Close with the two cases that prove you have actually debugged silicon: a lost error (a real PSLVERR driven in the wrong cycle or never consumed, so software ran on garbage and "no error" was a lie) and a spurious error (a PSLVERR with no backing condition — a sampling or wiring bug, not a slave bug). The line that lands the interview: "a PSLVERR is a symptom; the method is to read it at the completion edge and eliminate to a source — the first failed test is the root cause, and reaching the peripheral means the bus was clean."

10. Practice

  1. Run the elimination. Given a capture with PSLVERR=1 at the completion edge, PADDR in a mapped range, and PPROT sufficient, state the source and the one extra read you must do to confirm it.
  2. Spot the phantom. A waveform shows PSLVERR pulsing high during a wait state and low at completion. State whether software should see an error, which step of the method catches this, and whether it is a slave-logic or a timing bug.
  3. Lost vs spurious. Define a lost error and a spurious error precisely, give the completion-edge signature of each, and state which one is invisible to software and why that is more dangerous.
  4. Order matters. Explain why the method checks PADDR mapped before PPROT sufficient, and what goes wrong if you check the peripheral status bit first.
  5. Build the monitor. From the method, sketch the always_ff block that, on a completion-edge PSLVERR, captures the context and classifies the source into decode / protection / peripheral; explain why SRC_PERIPH needs a follow-up read.

11. Q&A

12. Key takeaways

  • A PSLVERR is a symptom, not a diagnosis. The method is confirm, capture, eliminate: confirm the error is real at the completion edge, capture the context there, then eliminate the source by ordered questions — the first failed test is the root cause.
  • Confirm at the completion edge first. Read PSLVERR only where PSEL && PENABLE && PREADY are high; a 1 elsewhere or an X at the edge is a phantom (9.1/9.2), and chasing it wastes days. Then capture {PADDR, PWRITE, PPROT, PSEL} at that same edge.
  • Eliminate in a fixed order — external before internal. PADDR mapped? no → decode (9.3). PPROT sufficient? no → protection (9.4). Both pass → the bus was legal, so the fault is peripheral-internal (9.5) — confirm it by reading the slave's sticky status bit, because the bus never names a peripheral fault.
  • Know the two false cases. A lost error is a real PSLVERR that never reached software (misaligned or unconsumed) — silent and the most dangerous. A spurious error is a PSLVERR with no backing condition — a sampling/wiring bug, not a slave bug. "No error" is not proof of a clean access.
  • Mechanise the method in the testbench. Bind an error monitor that classifies every completion-edge PSLVERR into decode/protection/peripheral, assert no lost and no spurious errors, and cover every source × read/write × wait-count so each error is both produced and correctly attributed.