A senior APB interview is not "explain PREADY" — it is "the access hangs; find out why," and the filter is whether you localise to a root cause or guess-and-poke. This chapter is the interview question-bank for debug scenarios: each one hands you a symptom — the access hangs, every Nth read is wrong, writes hit the wrong register, it only fails when slave B is idle, it works in RTL but fails on the FPGA, PSLVERR fires unexpectedly, the bridge completes before the peripheral is ready — and the model answer drives it to root cause and fix the way a bring-up engineer actually does. It builds directly on Module 16's bug families: bin the symptom first (hang vs garbage), then bisect to the first failed check. The single idea to carry: a debug scenario tests a process, not a fact — the candidate who narrates symptom → hypothesis → isolate → fix → prevent and names the assertion that would have caught it has debugged silicon; the one who jumps straight to a fix has only read the spec.
1. What problem is being solved?
The problem is demonstrating a repeatable localisation process under interview pressure — taking any APB symptom and driving it to a proven root cause without guessing, the same way you would on a bench at 2 a.m. during bring-up.
A debug-scenario question gives you exactly what a real bug report gives you: a symptom and nothing else. "The access hangs." "Every hundredth read is wrong." "It only fails on the FPGA." The interviewer is not checking whether you know that a stuck-low PREADY hangs the bus — that is in chapter 16's catalogue. They are checking whether, handed a symptom with five plausible causes, you can eliminate down to the one that is real and prove it. The skill has a fixed shape:
- Bin the symptom into hang or garbage first, because that single split halves the suspect list before you look at any RTL.
- Form a hypothesis tied to a bus-visible fact, not a hunch — "if it is asserted-too-early,
PREADYwill leadPRDATAon a back-to-back read." - Isolate by bisection — a capture, a directed stimulus, or a binary search across slaves/clock-ratios that the hypothesis predicts will pass or fail.
- Fix structurally (for a hang) or causally (for garbage), and name the assertion that would have caught it so the same bug never ships twice.
Every scenario below is that process applied to one symptom. Solving the chapter means being able to run it cold on a symptom you have never seen.
2. Why previous knowledge is insufficient
Module 16 gave you the bug families — the named catalogue of how APB RTL ships broken, each with wrong-versus-right code. That is the reference this chapter points back to constantly, but a catalogue is not a debugging skill.
- The PREADY bug catalogue taught you the named bugs — stuck-low, asserted-too-early, default-ready violation, cross-clock — and the hang-vs-garbage binning. That is the answer key. But an interviewer does not hand you a labelled bug; they hand you a symptom and watch you find the label. Knowing the catalogue is necessary; navigating to the right entry from a symptom is the tested skill.
- The error-debug methodology taught the forensic procedure for a
PSLVERR— confirm at the completion edge, capture context, eliminate the source in order. That is exactly the shape of process a debug scenario rewards, but it covers one symptom class (error responses). The scenarios here span hangs, garbage, lost writes, and platform-specific failures too. - The individual bug chapters — pready-stuck-low, incorrect-prdata, write-corruption, address-decode-bugs — each taught one fault deeply. But a real symptom does not announce which chapter it belongs to; that mapping is the work.
The gap: prior chapters taught what each bug is. This chapter teaches how to get from a symptom to the bug, under the constraint that you have one waveform and a hostile clock. That is a method — bin, hypothesise, isolate, fix, prevent — and it is what the senior screen is actually probing.
3. Mental model
The model: a debug scenario is a search problem, and binning the symptom is the first and most valuable cut. You do not start from the RTL; you start from the symptom and halve the suspect space before reading a single line.
The binning is Module 16's two-promise split. PREADY promises "you may complete" and "the data is valid," and every symptom breaks one:
- It hangs (liveness failure).
PSELandPENABLEstay high, the access never completes. The suspect list is the hang family: a missing/unreachable path toPREADY=1— forgotten done term, FSM state with no exit, reset-stuck, a default-ready violation, or a cross-clock term that never resolves. You are looking for why nothing good happens. - It reads/writes garbage (safety failure). The access completes but commits the wrong data. The suspect list is the garbage family: readiness running ahead of validity —
PREADYleads the data mux, a glitch, a phase/slave mis-gate, or a write committed onPENABLEalone. You are looking for what bad thing happened.
Once binned, you bisect — pick a test the hypothesis predicts will discriminate. Three refinements turn the model from a slogan into a method:
- The symptom's locality is itself a clue. "Fails only when slave B is idle" or "fails only on the FPGA" are non-local: the bug is not in the slave you are reading, or not in RTL at all. A non-local symptom redirects the search to the bus or the level (gate/FPGA timing), not the addressed slave.
- Intermittence points at causality or timing, not logic. A bug that is deterministic in a directed test but "every Nth" in production is almost always a causality bug (readiness leading validity) or a condition bug (clock phase, reset deassertion) — never a plain logic error, which would fail every time.
- The fix's kind follows the bin. A hang fix is structural — guarantee a reachable, registered, reset-known path to
PREADY=1. A garbage fix is causal — derive readiness and the commit from the samedata_validterm that gatesPRDATA. Naming the kind proves you understood the bin, not just patched the line.
4. Real SoC / hardware context
Debug scenarios are drawn from real bring-up because that is where APB bugs that survived simulation finally bite — under real traffic, real clock ratios, and real silicon delays. Two scenarios in this chapter are grounded in events that recur across teams.
The first is the "works in RTL, fails on the FPGA" trap. A slave passes every RTL regression, but on the FPGA prototype reads intermittently return garbage or the bus occasionally hangs. The root cause is almost always one of two things RTL cannot see: a combinational PREADY (or PRDATA-valid term) built off a rippling PADDR decode that glitches into the sample window — invisible in zero-delay RTL, real on routed FPGA fabric with actual net delays — or a cross-clock done term sampled without a synchroniser, which RTL's idealised clocks happen to align but the FPGA's real PLLs do not. The debug move is to recognise that "RTL-clean, platform-fails" is the symptom and to stop re-running RTL: the bug lives at a level RTL does not model, so you register the suspect signal or add the synchroniser and confirm on the platform, not in the simulator.
The second is the DMA-under-load family: a peripheral that is fine on the bench faults or misbehaves only when a DMA engine drives it at full rate — a write FIFO overflow that surfaces as an unexpected PSLVERR, or a status read that is asserted-too-early and only shows when back-to-back reads target different registers fast enough that the mux has not settled. The lesson bring-up teaches: a symptom that needs load to reproduce is a causality or capacity bug, and the bench will never show it — you reproduce it with a stress loop, capture with annotated delays, and bisect. In both families the diagnostic discipline is the same as for a clean functional bug; only the level and the stimulus change.
5. Engineering tradeoff table
This is the debug-scenario table: each row is a symptom as an interviewer states it, the weak answer (the guess-and-poke that fails the screen), the strong answer (the localisation), and the catching assertion that closes it out. Read it as the spine of the chapter.
| Symptom (as stated) | Weak answer (guess-and-poke) | Strong answer (localise) | Catching assertion |
|---|---|---|---|
| "The access hangs." | "Reset the block / the slave is broken — bump the timeout." | Bin: hang. PSEL/PENABLE high forever ⇒ PREADY never rises. Bisect the hang family: forgotten done path, FSM state with no exit, reset-stuck, or a different idle slave pulling the shared line low. Fix is structural. | Bounded completion: (psel && penable) |-> ##[1:N] pready. |
| "Every Nth read is wrong." | "Add a settling delay / it's a data-path bug." | Bin: garbage (it completes). Intermittence ⇒ causality. Hypothesis: PREADY leads the read-data mux; capture a back-to-back read of different regs with delays and check PREADY vs PRDATA settle. Fix is causal. | Ready-implies-valid: (psel && penable && pready && !pwrite) |-> data_valid_q. |
| "Writes are lost / hit the wrong register." | "The firmware address is wrong." | Bin: garbage. Two sub-cases: write committed on PENABLE alone (writes during waits ⇒ stale/duplicated) — check the commit term; or address captured a cycle off / decode aliases — check PADDR at the completion edge. | Commit only at completion: wr_en |-> (psel && penable && pready); and stable PADDR setup→completion. |
| "It only fails when slave B is idle." | "Slave A has a bug, but we can't find it." | Non-local symptom ⇒ default-ready violation. Idle slave B drives the shared PREADY low and stalls slave A. Suspect every slave on the return, not the addressed one. Drive PREADY=1 (or out of the mux) when !sel. | Idle slave must not stall: !sel |-> pready per slave. |
| "It works in RTL but fails on the FPGA." | "Re-run RTL until it fails." | Level clue ⇒ a bug RTL cannot model: a combinational glitch off a rippling decode, or a cross-clock term sampled raw. Stop re-running RTL; register the input / add a synchroniser; confirm on the platform. | Register-and-CDC: never-X on PREADY while selected + no raw cross-domain signal in the PREADY cone. |
"PSLVERR fires unexpectedly." | "Disable the error / mask it in software." | Run the forensic order: confirm PSLVERR at the completion edge (else phantom), capture context, eliminate decode → protection → peripheral. Often a peripheral overflow under load, not a decode bug. | A bus-legal PSLVERR must have a backing status bit: (…&&pslverr&&mapped&&prot_ok) |-> (|status). |
| "The bridge completes before the peripheral is ready." | "The peripheral is too slow — speed it up." | Bin: garbage on reads / lost on writes. The bridge generated its own PREADY or counted fixed cycles instead of honouring the peripheral's real ready. Make bridge completion follow the peripheral's data_valid/ready, never a fixed count. | Bridge ready follows peripheral ready: bridge_pready |-> periph_ready_q. |
The throughline: every weak answer skips the bin and pokes at a fix; every strong answer bins, predicts a bus-visible fact, and isolates by a capture the hypothesis chose. And every row ends in a named, reusable assertion — because the senior deliverable is not "I fixed it once," it is "this class of bug can never ship again."
6. Common RTL / debug mistakes
7. Interview framing
Debug scenarios are the senior filter because they cannot be passed by recall. A candidate can memorise every bug in the catalogue and still fail "the access hangs — go" if they cannot navigate from symptom to cause. The interviewer is reading your process, so make the process audible.
The framing that lands is narrate the five moves out loud: bin → hypothesise → isolate → fix → prevent. Do not silently arrive at "it's asserted-too-early"; say "it completes, so it's garbage not a hang; intermittence points at causality; my hypothesis is PREADY leading the read-data mux; I'd confirm with a back-to-back read of two different registers under annotated delays and check whether PREADY rises before PRDATA settles; the fix is to derive PREADY from the same data_valid_q that gates PRDATA; and I'd add a ready-implies-valid assertion so it can't ship again." That single sentence demonstrates every senior signal: the bin, the bus-visible hypothesis, the isolating capture, the causal fix, and prevention. Three moves separate strong from weak. First, always bin before theorising — it proves you have a method, not a memory. Second, tie the hypothesis to a fact you can see on the bus, never a hunch — that is the difference between a hypothesis and a guess. Third, close with the assertion — naming the property that catches the class signals you think in regression, not in one-off fixes. The closing line that signals real bring-up: "the symptom told me where to look — non-local means the bus, intermittent means causality, RTL-clean-but-platform-fails means the gate level — and the first failed check was the root cause."
8. Q&A
9. Practice
- Bin five symptoms. For "never completes," "every 100th read wrong," "fails only when slave B idle," "RTL-clean but FPGA hangs," and "config read-back is stale" — assign each to hang or garbage, name the suspected bug family, and state the one clue in the symptom that decided it.
- Predict before you poke. For the asserted-too-early hypothesis, write the exact directed stimulus and the bus-visible prediction (
PREADYvsPRDATA) that would confirm or kill it. Explain why a single read can't discriminate it. - Name the level. For "works in RTL, fails on the FPGA," list the two RTL-invisible bug classes, the FPGA reason each manifests, and the prevention check for each — and state why re-running RTL is the wrong move.
- Structural vs causal. Given a hang and a garbage scenario, write the fix kind for each in one sentence and the catching assertion, and explain why swapping the fix kinds would not work.
- Defend the timeout. A reviewer says "we already have a manager timeout, so a slave hang can't hurt us." In three sentences, separate surviving a hang from removing it, and state what the timeout is and isn't.
10. Key takeaways
- A debug scenario tests a process, not a fact. Run bin → hypothesise → isolate → fix → prevent out loud; the candidate who narrates the process out-debugs the one who recalls the catalogue.
- Bin the symptom first — hang vs garbage. A hang is a liveness failure (
PREADYnever rises → the hang family, structural fix); garbage is a safety failure (completes wrong → the garbage family, causal fix). This one split halves the suspects before you read any RTL. - The symptom's shape routes the search. Non-local ("slave B idle") ⇒ the bus / a different slave (default-ready violation). Intermittent ("every Nth," "under load") ⇒ causality, not logic. Platform-specific ("RTL-clean, FPGA-fails") ⇒ a gate/CDC-level bug RTL can't model — stop re-running RTL.
- The guess-and-poke is the red flag. Jumping to a fix — reset it, add a delay, mask the error, bump the timeout — without binning or a bus-visible hypothesis fails the senior screen, even when the poke happens to work.
- Close every scenario with the catching assertion. Bounded-completion for hangs, ready-implies-valid for garbage, commit-at-completion for writes, idle-doesn't-stall for default-ready, backing-status for
PSLVERR. Naming the property that catches the class — and binding it in an APB monitor — is the difference between "I fixed it once" and "it can't ship again." APB IHI 0024C §2.1 fixes the completion contract every one of these properties guards.