Skip to content

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, PREADY will lead PRDATA on 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). PSEL and PENABLE stay high, the access never completes. The suspect list is the hang family: a missing/unreachable path to PREADY=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 — PREADY leads the data mux, a glitch, a phase/slave mis-gate, or a write committed on PENABLE alone. 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 same data_valid term that gates PRDATA. Naming the kind proves you understood the bin, not just patched the line.
A decision map: a top row of APB symptoms feeds a single bin-first decision (hang vs garbage), which splits into an amber hang column listing four root causes with a structural fix and a bounded-completion assertion, and a red garbage column listing four root causes with a causal fix and a ready-implies-valid assertion.
Figure 1 — the symptom-to-root-cause map a senior candidate demonstrates. The top row lists the reported symptoms (hangs, every-Nth-read-wrong, lost/misdirected writes, fails-only-when-slave-B-idle, unexpected PSLVERR, completes-before-ready). An arrow leads to a single bin-first decision: hang (a liveness failure, PREADY never rises) versus garbage (a safety failure, the bus completes but commits wrong data). The amber hang branch lists its candidate root causes — forgotten done path / FSM state with no exit, the default-ready violation by an unselected slave, undriven or wrong-polarity reset, and an unsynchronised cross-clock done term — with a structural fix family (guarantee a reachable, registered, reset-known path to PREADY=1) and the catching assertion (bounded completion: PSEL && PENABLE imply PREADY within N cycles). The red garbage branch lists PREADY-leads-the-mux, the combinational glitch, ignores-PSEL/PENABLE, and write-committed-on-PENABLE-alone, with a causal fix family (derive PREADY and the commit from the same data_valid that gates PRDATA) and its catching assertion (ready-implies-data-valid plus write-only-at-PSEL&&PENABLE&&PREADY). The footer states the senior move: bin first, then bisect to the first failed check — that failed check is the root cause.

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

A six-step flow: capture the cue, bin as garbage not hang, bisect with a back-to-back read showing PREADY high while PRDATA holds the previous value, root-cause as a wait-counter that leads the mux, causal fix deriving PREADY from data_valid_q, and a ready-implies-valid assertion that catches it in regression.
Figure 2 — one debug scenario worked end-to-end: 'every Nth read is wrong.' Step 1 captures the cue — wrong register value roughly every hundredth read, never reproducible in the single-step debugger, only in a tight production loop, with the bus completing cleanly (no hang, no PSLVERR). Step 2 bins it: because it completes, this is garbage, not a hang — a safety failure that excludes the four hang causes and narrows to the three garbage causes, so you do not chase the data path. Step 3 bisects: a back-to-back read of two different registers with annotated delays shows that at the completion edge PREADY is high but PRDATA still holds the previous register's value because the read-data mux has not settled — readiness is leading validity by one cycle. Step 4 names the root cause: PREADY was derived from a wait-counter that hits zero one cycle before the registered read-data mux updates, a proxy for 'should be done' rather than the actual data-valid event. Step 5 applies the causal fix — derive PREADY from the same data_valid_q flop that gates PRDATA so readiness follows validity. Step 6 adds the catching assertion — a completing read implies data_valid_q — so the early-assert can never pass regression again. The footer notes the signature: 'every Nth read wrong, clean on the bus' means readiness leads validity, and the bisection (not a guess) is what turned an unreproducible field bug into a one-line RTL fix plus a permanent assertion.

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

  1. 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.
  2. Predict before you poke. For the asserted-too-early hypothesis, write the exact directed stimulus and the bus-visible prediction (PREADY vs PRDATA) that would confirm or kill it. Explain why a single read can't discriminate it.
  3. 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.
  4. 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.
  5. 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 (PREADY never 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.