AMBA APB · Module 10
PSLVERR Introduction Rationale
Why APB3 added PSLVERR — a bus on which every access silently succeeds cannot be integrated safely, so software needs a synchronous, precise per-access verdict to fault on. The pressure that forced an error channel into APB, and why a status flag beat an abort/retry mechanism.
The single idea to carry: PSLVERR exists because a bus on which every access silently succeeds cannot be integrated safely. In APB2 an unmapped address, a protection violation, a peripheral fault, and a hung access were all invisible — a class of bug the bus could not even report, only let you infer later from corruption. APB3 added one minimal per-access pass/fail bit, sampled at completion, that a manager can escalate to a bus fault — turning silent misbehaviour into a synchronous, precise, debuggable verdict. This chapter is the why of that decision and the why-not of the alternatives.
1. Problem statement
The problem PSLVERR was created to solve is that APB2 had no way to say "no." Every transfer completed and, by definition, succeeded — so the entire category of "this access was illegal, faulted, or did nothing" had no representation on the bus at all.
Consider what that means in a real system. A manager (a CPU, a DMA engine, a bridge) issues a read or write to a peripheral address. In APB2 the slave drives some data, the access completes in its fixed two cycles, and the manager records success — unconditionally. Now suppose the access was bad in one of several ways:
- Unmapped address. Nothing decodes; the read mux returns a default (often
0x0) and the manager reads it as if it were real register contents. - Protection / access violation. A non-secure or unprivileged master touches a register it should not — but APB2 carries no context and no way to refuse.
- Peripheral fault. A device is in an error state, its internal logic detected a problem, or a downstream resource (a slow memory behind the peripheral) failed — and the peripheral has no channel to report it.
- Effectively hung / dead peripheral. The access "completes" against a peripheral that did not actually act on it.
In every case the bus reports the same thing: success. The fault is not merely unhandled — it is unobservable at the moment it occurs. Software cannot fault on it, log it, retry it, or even count it, because nothing told software anything happened. The problem statement, precisely, is: a control bus that can never report a failure cannot be integrated into a system that must detect and respond to failures — which is every real SoC.
2. Why previous knowledge is insufficient
You already know the mechanics of the fix. Module 9 taught PSLVERR semantics and timing — when it is sampled, that it is only valid in the access cycle alongside the completion handshake, that it does not undo the transfer. And chapter 10.1 established the bare fact that APB2 fails silently, while chapter 10.2 named PSLVERR as one of the two signals APB3 added. That is the what. None of it is the why.
This chapter is the design rationale, and it is a distinct thing from the mechanics:
- Knowing how PSLVERR behaves does not tell you why it had to exist. You can recite the timing diagram and still not be able to answer, under interview pressure, "why couldn't software just detect bad accesses itself?" — which is exactly the question that separates someone who memorised the signal from someone who understands the protocol pressure.
- Knowing that APB2 fails silently does not tell you why that was intolerable, or why the fix took this exact shape. Why a status flag and not an abort/retry mechanism? Why one bit and not a richer error code? Those are architecture decisions, and they are the contribution of this chapter.
- This is the sibling of chapter 10.4. That chapter answered "why PREADY" — the pressure for flow control. This one answers "why PSLVERR" — the pressure for an error channel. Same evolutionary moment (APB3), two different forces. Read together they explain why APB3 happened at all.
So subtract the mechanics and the signal-delta lists you already have, and what remains — the engineering pressure that forced an error channel into a deliberately minimal bus, and the design alternatives that were rejected — is this chapter. (Forward: chapter 10.6 covers PSTRB, the APB4 byte-strobe feature, by which point error reporting is assumed.)
3. Mental model
The model: a bus without PSLVERR is a delivery service that always stamps "delivered" — even when it left the package at a demolished address, or had no right to enter the building, or dropped it in a ditch. Every outcome reads as success, so the sender has no way to know a delivery actually failed until, much later, the recipient complains that something never arrived. PSLVERR is the failed-delivery receipt: a single, synchronous "this one did not go through" handed back at the moment of the attempt, so the sender can react now instead of inferring the loss days later.
Three refinements make it precise:
- The verdict is per-access and synchronous. PSLVERR is not a separate status register you poll later; it is a bit that travels with the access and is sampled at the same instant the access completes. Every transfer gets its own pass/fail verdict, bound to that exact access. This is the whole value: the fault is reported where and when it happened, attributable to a specific instruction.
- It is a verdict, not a recovery mechanism. PSLVERR says "this failed." It does not abort, roll back, or retry the transfer — the data phase still completes. Whether to take a bus fault, log, retry, or ignore is policy, deliberately pushed above the bus into the manager and software. APB stays minimal; the bus reports, the system decides.
- One bit is enough to be foundational. It does not encode why it failed (no error code, no category). One bit — failed or not — is the minimum that lets a manager escalate to a bus-fault exception, and the minimum is exactly the APB design philosophy. Richer diagnosis lives in peripheral-specific status registers; the bus carries only the universal pass/fail verdict.
4. Real SoC implementation
In silicon the difference is small in gates but enormous in consequence. Below, the same unmapped-address read in two designs: a pre-PSLVERR APB2 slave that silently returns a default and a manager that records success, then an APB3 design where the decoder drives PSLVERR and the manager escalates to a bus fault.
// ---------------------------------------------------------------------------
// PRE-PSLVERR (APB2): the bad access is INVISIBLE.
// An unmapped address returns a default; the slave has no error output, and
// the manager has no error input. Every access is recorded as success.
// ---------------------------------------------------------------------------
module apb2_slave #(parameter AW = 12) (
input pclk, presetn,
input psel, penable, pwrite,
input [AW-1:0] paddr,
input [31:0] pwdata,
output reg [31:0] prdata
// NOTE: no pslverr output -> the slave physically CANNOT report a fault.
);
reg [31:0] csr [0:3];
always_comb begin
case (paddr[3:2])
2'd0: prdata = csr[0];
2'd1: prdata = csr[1];
// An unmapped address falls through to the default. The read "succeeds"
// and returns 0 -- indistinguishable from a register that holds 0.
default: prdata = 32'h0; // SILENT: no channel to say "illegal address"
endcase
end
endmodule
// In the pre-PSLVERR manager there is nothing to check -- success is assumed:
// data = apb_read(addr); // always "works"; a bad addr just yields 0x0
// use(data); // garbage flows downstream, unflagged
// ---------------------------------------------------------------------------
// APB3: the decoder produces a SYNCHRONOUS per-access verdict, and the
// manager ESCALATES it to a bus fault. The bad access is now reportable.
// ---------------------------------------------------------------------------
module apb3_decoder_slave #(parameter AW = 12) (
input pclk, presetn,
input psel, penable, pwrite,
input [AW-1:0] paddr,
input [31:0] pwdata,
output reg [31:0] prdata,
output pready, // APB3 completion handshake (see 10.4 rationale)
output pslverr // APB3 per-access pass/fail verdict
);
// Address decode: is this access targeting a real, legal register?
wire mapped = (paddr[AW-1:4] == 0) && (paddr[3:2] inside {2'd0, 2'd1});
reg [31:0] csr [0:3];
always_comb prdata = mapped ? csr[paddr[3:2]] : 32'h0;
assign pready = 1'b1; // single-cycle peripheral
// WHY this matters: PSLVERR is asserted at completion for ANY bad access.
// It is a verdict, not an abort -- the data phase still completes, but the
// manager now has a precise, synchronous bit it can fault on.
assign pslverr = psel && penable && !mapped; // 1 == this access FAILED
endmodule
// In the APB3 manager, the verdict is consumed at completion and escalated:
// if (psel && penable && pready) begin
// if (pslverr) bus_fault(paddr); // precise exception on THIS access
// else data = prdata; // only trust data on a clean verdict
// endThe pre-PSLVERR slave is shorter only because it shoulders no responsibility for correctness reporting — and that "saving" is exactly the bug. The APB3 version adds a handful of gates (a mapped comparison and one output bit) and, crucially, a manager that consumes the verdict. The signal is worthless if the manager ignores it: the value of PSLVERR is realised only when it is escalated into a CPU bus-fault exception. That escalation is what converts an invisible fault into a precise, attributable, software-visible event.
5. Engineering tradeoffs
Before PSLVERR, engineers did try to detect bad accesses — with out-of-band mechanisms that were strictly worse. The table shows why each pre-PSLVERR option failed and why a per-access bus verdict won.
| Option | How it detects a bad access | What it costs | Why it is insufficient / why PSLVERR won |
|---|---|---|---|
| Silent failure (do nothing) | It doesn't — every access "succeeds" | Zero gates, zero effort | No detection at all. Faults surface later as distant corruption with no link to the cause. The status quo this whole chapter exists to fix. |
| Out-of-band status registers (polled) | Peripheral sets an error flag in a CSR; software polls it later | Per-peripheral logic + polling overhead + interrupt plumbing | Not synchronous and not per-access — by the time software polls, you cannot tie the error to a specific access, and many accesses may have passed. No way to fault precisely. Races and missed errors abound. |
| Software address-map whitelisting | Software checks the target address against a known map before issuing | CPU cycles per access; a hand-maintained map | Only catches unmapped addresses it knows about — blind to peripheral faults, protection violations, and any map error. Fragile, partial, and pure software belief, not a hardware verdict. |
| "Hope bring-up catches it" | Manual testing during integration | Engineer time; false confidence | Catches only what the test stimulus happens to exercise. Field conditions, rare faults, and security-relevant accesses slip through. Not a mechanism — a wish. |
| PSLVERR — per-access bus verdict (APB3) | Slave/decoder drives one pass/fail bit, sampled at completion | One output bit + a decode comparison + manager escalation logic | Synchronous, per-access, precise, and universal. Every access gets a verdict bound to that access, the manager can fault on it immediately, and it covers all error sources through one channel. Minimal cost, foundational capability. |
The deeper trade-off is in PSLVERR's shape. APB3 could have added an abort/retry mechanism (like some higher-performance buses) — but that would force flow-control and recovery state into every slave, breaking APB's defining minimalism. Instead the designers chose a status flag: the bus reports a verdict and stops there; recovery policy (fault, log, retry, ignore) is pushed above the bus into the manager and software. One bit, no new state machine, no rollback — the smallest addition that makes failure observable. That restraint is the whole point: PSLVERR extends APB into fault-aware territory without sacrificing the simplicity that justified APB in the first place.
6. Common RTL mistakes
7. Debugging scenario
A field-classic rooted in the exact gap PSLVERR was designed to close: a system where a bad access was never given a synchronous verdict, so a firmware bug corrupted behaviour days from its cause.
- Observed symptom: an audio subsystem intermittently powers up in the wrong mode. It is reproducible only after a specific boot sequence, and only on some board revisions. There is no crash, no error log, no exception — the system simply behaves wrongly, far downstream, and only sometimes.
- Waveform clue: replaying the boot trace, a firmware write to
0x4001_F010— an address that does not decode on this board's peripheral map — sails through: SETUP, ACCESS, transfer completes, manager moves on. On this segment there is no PSLVERR line consumed (an APB2-era bridge, or an APB3 slave whosepslverrwas tied off to the manager). The write that was supposed to configure the audio PLL mode register went to nothing, did nothing, and was recorded as success. No signal anywhere went high to mark it. - Root cause: a firmware bug computed the audio config register address wrong (an off-by-one in a base-address table on the affected board revision), targeting an unmapped address. Because the segment provided no synchronous error verdict, the bad write was silently dropped, the PLL kept its reset-default mode, and the audio block came up misconfigured. The corruption manifested days of operation (and many irrelevant accesses) after the offending write — with nothing linking symptom to cause. This is precisely the failure mode PSLVERR exists to surface: a bad access that is invisible at the instant it happens.
- Correct RTL: ensure the decoder drives PSLVERR for unmapped accesses and the manager consumes it. The slave/decoder asserts
pslverr = psel && penable && !mapped;, and the manager escalates: onpslverrat completion, take a bus fault on that address instead of silently continuing. Never tie a manager'spslverrinput low "to keep things simple" — that re-creates the silent-failure world. - Verification assertion: prove that an unmapped access always produces a verdict — it can never complete cleanly:
// Any completed access to an unmapped address MUST raise PSLVERR.
// (mapped is the decoder's legal-region predicate.)
property unmapped_access_must_flag;
@(posedge pclk) disable iff (!presetn)
(psel && penable && pready && !mapped) |-> pslverr;
endproperty
a_unmapped_flags: assert property (unmapped_access_must_flag)
else $error("Unmapped access completed without PSLVERR -- silent failure!");- Debug habit: when a system misbehaves with no error reported, ask first "could a bad access have happened with no synchronous verdict?" — i.e., is there any path where an access can fail silently? Audit the segment: is PSLVERR driven and consumed end-to-end? A whole class of "impossible," delayed-corruption bugs are silent bad accesses. Make failure loud and synchronous before hunting downstream symptoms.
8. Verification perspective
An error channel is only as trustworthy as the proof that it fires for every error source and is escalated correctly. Verifying PSLVERR's rationale means treating fault reporting as a first-class functional requirement, not an afterthought.
- Inject every error source and prove each yields a synchronous verdict. The verification job is error injection: drive an unmapped address, a protection violation, a peripheral-fault condition, and confirm that each produces PSLVERR at completion of the offending access — same cycle, bound to that access. A bus where some error sources are flagged and others slip through silently is back to the APB2 problem for those cases. Coverage must enumerate the error sources, not just "PSLVERR went high once."
- Verify the escalation path, not just the signal. PSLVERR is worthless unflagged. Test that the manager consumes the verdict: a flagged access must produce the intended response (bus-fault exception, status update, no use of read data). Assert that read data following a PSLVERR is not trusted, and that a clean verdict is required before data is consumed. The end-to-end contract is "bad access ⇒ synchronous verdict ⇒ correct escalation."
- Cover bad-access detection explicitly. Functional coverage must include the negative space: every legal access completes with PSLVERR low, and every modelled illegal access completes with PSLVERR high — no silent passes, no spurious flags on legal accesses. The most dangerous coverage hole is the bad access that no test exercised, because that is exactly the field bug PSLVERR was meant to catch. Make "did we ever let a bad access through unflagged?" an explicit, measured coverage question.
The point: verifying PSLVERR is verifying that failure is observable — that there is no path by which a bad access completes without a synchronous verdict, and no path by which a verdict is dropped instead of escalated. That is the property the whole signal exists to provide.
9. Interview discussion
"Why does PSLVERR exist?" is a deceptively deep question — it filters people who memorised the signal from people who understand protocol pressure. The strong answer starts from the gap, not the signal.
Frame it as the silent-failure problem: in APB2 every transfer completes and "succeeds," so an unmapped address, a protection violation, or a peripheral fault are all invisible — the bus has no channel to report failure, and software cannot fault, log, or retry on something it was never told about. Then make the integration/safety case explicit: a CPU must take a bus-fault exception on a bad access rather than silently read garbage, and emerging safety and security requirements demand detectable faults — a bus that can never report failure cannot be safely integrated or certified. Now name the fix precisely: PSLVERR is one per-access pass/fail bit, sampled synchronously at completion, that the manager escalates to a bus fault — turning silent misbehaviour into a precise, attributable, software-visible event.
The depth signal is the design-alternative discussion. Be ready for "why a flag and not an abort/retry?" — answer that abort/retry would force recovery state machines and flow-control coupling into every slave, breaking APB's defining minimalism, so the designers chose a status flag (bus reports) with recovery policy above the bus (system decides). And be ready for "couldn't software detect bad accesses itself?" — answer that software whitelisting or polled status registers are not synchronous and not per-access, so they can never fault on the exact offending instruction; PSLVERR is a different capability, not a convenience. Close with the throughline: PSLVERR is the minimal bit that makes failure observable, which is the prerequisite for integrating a control bus into any system that must detect and respond to faults. (Its sibling, the PREADY rationale, answers the parallel "why flow control" — together they explain why APB3 happened.)
10. Practice
- State the gap. In two sentences, explain why APB2's silent success was intolerable for a real SoC — name three distinct error sources it could not report and what software loses by not knowing.
- Defend the shape. Argue why APB3 added a status flag rather than an abort/retry mechanism. What would abort/retry have cost in every slave, and what did the flag preserve?
- Demolish the workarounds. For each pre-PSLVERR option (polled status registers, software address whitelisting, "hope bring-up catches it"), state the one fatal property it lacks that a per-access bus verdict provides.
- Trace the escalation. Given an APB3 read that returns PSLVERR=1, walk the manager's correct response and explain why the read data must not be trusted, even though the data phase completed.
- Find the silent bug. Given a system where a firmware write to an unmapped address corrupts behaviour days later with no error, explain how PSLVERR (driven and consumed) would have collapsed the cause-to-detection distance — and what specifically must be true end-to-end for that to work.
11. Q&A
12. Key takeaways
- PSLVERR exists because APB2 could never say "no." Every transfer silently succeeded, so unmapped addresses, protection violations, and peripheral faults were invisible — a class of bug the bus could not report, only let you infer later from corruption.
- The pressure was integration and safety. A CPU must take a bus-fault exception on a bad access instead of reading garbage, and safety/security requirements demand detectable faults — a bus that can never report failure cannot be safely integrated or certified.
- The pre-PSLVERR workarounds were strictly worse. Polled status registers and software whitelisting are not synchronous and not per-access, so they can never fault on the exact offending instruction; "hope bring-up catches it" is a wish, not a mechanism.
- The fix is one synchronous, per-access pass/fail bit at completion that the manager escalates to a bus fault — turning silent misbehaviour into a precise, attributable, software-visible event. The signal is worthless unless the manager consumes it.
- It is a status flag, not an abort/retry — on purpose. A flag keeps APB minimal and pushes recovery policy above the bus into the manager and software; abort/retry would have forced recovery state into every slave and broken APB's defining simplicity.
- The throughline: a bus on which every access silently succeeds cannot be integrated safely. Software needs a synchronous, precise verdict to fault on, and APB3 added the minimal bit that provides it.