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
PPROTwas 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:
PSLVERRis 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
1and 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: insufficientPPROT. 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.
- Confirm it is real (Step 1). Read
PSLVERRonly at the completion edge — thePCLKedge wherePSEL && PENABLE && PREADYare all high. If it is not1there, you do not have an error: a1during 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. - 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. SamplingPADDRa cycle late captures the next access's address and sends you chasing the wrong slave. - Eliminate by address (Step 3a). Was
PADDRinside a mapped range? No → decode error (9.3) — done. Yes → continue. - Eliminate by protection (Step 3b). Was
PPROTsufficient for that target (privilege, secure, data/instruction)? No → protection violation (9.4) — done. Yes → continue. - 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.
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.
// 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
endmoduleTwo facts drive the real design. First, the capture and the classification must both key off completion_edge — addr_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 edge | What it rules IN / OUT | Source it points to |
|---|---|---|
PSLVERR=1 only outside the edge (setup/wait), 0 at completion | No real error — rules out all real sources | Phantom (9.1) — mis-sample / misaligned |
PSLVERR is X at the edge, or pulses then vanishes before completion | Undriven or combinational verdict — rules out a trustworthy result | Phantom / timing bug (9.2) |
PSLVERR=1 at the edge and PADDR not in any mapped range | Confirms real; rules IN decode | Decode error (9.3) — fix map/master |
PSLVERR=1, PADDR mapped, but PPROT insufficient for target | Rules OUT decode; rules IN protection | Protection violation (9.4) — fix privilege/policy |
PSLVERR=1, PADDR mapped, PPROT sufficient | Rules OUT decode and protection | Peripheral fault (9.5) — read status bit |
PADDR mapped, PPROT ok, but slave status bit clear | Bus clean yet no peripheral reason — rules IN a wiring/sample bug | Spurious — verdict not produced by a real condition |
A known-faulting access returns PSLVERR=0 at the edge | The error never reached the bus correctly | Lost 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 && PREADYhigh) and readsPSLVERRonly there — it is1, so the error is real (Step 1 passes; not a phantom). At that same edge the captured context isPADDR = 0x4002_0010,PWRITE = 1,PPROT = privileged-data, target =PSEL2(the FIFO-controlled peripheral). - Root cause: running the elimination —
PADDRis inside a mapped range (decode ruled out, 9.3);PPROTis 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 stickyFIFO_OVFbit set. Under load the DMA outpaces the peripheral's drain, the write FIFO overflows, and the slave correctly assertsPSLVERRfor 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 readPSLVERRat 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:
// 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)
PSLVERRthat survives decode and protection elimination must be backed by a peripheral status bit; a bus-legalPSLVERRwith no status bit is spurious and must fail in regression:
// 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
PSLVERRat 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.
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
PSLVERRis 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 aPSLVERR=1at that access's completion edge — a missing one is a lost error and must fail. No spurious error: aPSLVERR=1at the edge withaddr_mapped && prot_ok && !any_status_bithas 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 edge — PADDR, 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
- Run the elimination. Given a capture with
PSLVERR=1at the completion edge,PADDRin a mapped range, andPPROTsufficient, state the source and the one extra read you must do to confirm it. - Spot the phantom. A waveform shows
PSLVERRpulsing 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. - 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.
- Order matters. Explain why the method checks
PADDRmapped beforePPROTsufficient, and what goes wrong if you check the peripheral status bit first. - 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 whySRC_PERIPHneeds a follow-up read.
11. Q&A
12. Key takeaways
- A
PSLVERRis 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
PSLVERRonly wherePSEL && PENABLE && PREADYare high; a1elsewhere or anXat 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.
PADDRmapped? no → decode (9.3).PPROTsufficient? 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
PSLVERRthat never reached software (misaligned or unconsumed) — silent and the most dangerous. A spurious error is aPSLVERRwith 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
PSLVERRinto 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.