AMBA AHB · Module 18
Bad HRESP
Tracing an unexpected or malformed ERROR to its cause — first split: is the ERROR well-formed but unexpected, or is the two-cycle sequence itself malformed? Well-formed → the slave deliberately errored, so trace which condition it detected (reserved/unmapped address via the default slave, write to a read-only register, protection violation, unsupported size, or a downstream bridge error propagated up) — usually the slave is correctly rejecting an access the software/master got wrong, so fix the access, not the slave. Malformed (one-cycle error, HRESP not held for both cycles, wrong HREADYOUT timing, state changed despite the error) = a slave RTL bug. Don't assume ERROR = slave bug. AHB-Lite uses only OKAY and ERROR.
The previous chapters debugged hangs (18.1), sequencing (18.2), and data bugs (18.3–18.5); this chapter debugs the response — a bad HRESP (an unexpected or malformed ERROR). The crucial first move — and the one most people skip — is a split: is the ERROR well-formed but unexpected, or is the two-cycle sequence itself malformed? These have opposite root locations. If the ERROR is well-formed but unexpected (the slave deliberately errored, with a correct two-cycle sequence), then the slave is correctly rejecting an access — and you trace which error condition it detected (a reserved/unmapped address — often via the default slave; a write to a read-only register; a protection/privilege violation; an unsupported size/misaligned access; or a downstream error in a bridge propagated up). In most cases, the slave is right and the software/master got the access wrong — so the fix is the access, not the slave. If the ERROR is malformed (the two-cycle sequence is broken — a one-cycle error, HRESP not held for both cycles, wrong HREADYOUT timing, or state changed despite the error), then it's a slave RTL bug in the response generation (16.6) — and the fix is the slave. (AHB-Lite uses only OKAY/ERROR — RETRY/SPLIT are full-AHB.) The senior insight: don't assume an ERROR is a slave bug — most unexpected ERRORs are the slave correctly faulting a bad access. This chapter builds the trace.
1. What Is It?
Bad HRESP is an unexpected or malformed ERROR; debugging it means splitting well-formed-but-unexpected from malformed. Its parts:
- The split — is the ERROR well-formed but unexpected (the slave deliberately errored) or malformed (the sequence is broken)? Different root locations.
- Well-formed → which condition — the slave correctly rejected an access: trace which condition (reserved addr, RO write, protection, size, downstream).
- Usually the access is wrong — in most cases the slave is right and the software/master got the access wrong; fix the access.
- Malformed → slave RTL bug — a one-cycle error,
HRESPnot held, wrongHREADYOUT, or state changed → fix the slave's response generation (16.6).
So bad HRESP debugging is fundamentally a classification problem — and the classification (well-formed vs malformed) determines where the bug is. This is the opposite of most debug, where you assume the responder (the slave) is wrong. With an ERROR, the default assumption should be that the slave is right — an ERROR is the slave doing its job (rejecting an invalid access). So the first question isn't "what's wrong with the slave?" but "is the ERROR legitimate?". You answer that by checking the sequence (is it a correct two-cycle ERROR?) and checking the access (was it actually a valid access?). If the sequence is well-formed and the access was invalid (reserved address, RO write, etc.), the slave is right — the bug is in the access (the software/master). Only if the sequence is malformed (a broken two-cycle ERROR) is the slave at fault. So bad HRESP debugging is the classify-then-trace problem where the slave is usually right. So it's the don't-blame-the-slave debug.
2. Why Does It Exist?
Bad HRESP debugging exists because an ERROR has two distinct meanings — a legitimate rejection (the slave correctly faulting a bad access) or a malformed response (a slave RTL bug) — and most ERRORs are legitimate, so the debug must first classify (is the slave right?) before assuming a slave bug, inverting the usual "blame the responder" instinct.
The ERROR is the slave doing its job is the root: an ERROR is not inherently a bug — it's the slave's correct response to an invalid access (a reserved address, an illegal access type, etc. — 16.6). So an ERROR is the slave working as designed — rejecting a bad access. So an unexpected ERROR is usually a legitimate rejection of an access that shouldn't have happened. So bad HRESP debugging exists because an ERROR is usually correct — the slave is doing its job. So it's a legitimate-by-default response. So ERROR is usually right.
The the access is usually wrong drives the inversion: if the ERROR is legitimate (well-formed, and the access was invalid), then the bug is in the access — the software (a bad pointer, wrong address, wrong privilege) or the master (a misconfigured transfer). So the fix is in the access, not the slave. This inverts the usual debug instinct (blame the responder) — here, the responder (the slave) is right, and the requester (the access) is wrong. So bad HRESP debugging exists to correct the instinct — check the access first. So it's an instinct inversion. So the access is the suspect.
The only a malformed sequence is a slave bug is the discriminator: the only case where the slave is at fault is a malformed ERROR — the two-cycle sequence is broken (a protocol error in the response generation). So the classification (well-formed vs malformed) is exactly what separates the slave bug from the access bug. So the debug method exists to make that classification (check the sequence). So bad HRESP debugging exists because: an ERROR is the slave doing its job (rejecting a bad access — usually correct — the why); so the access is usually the bug, not the slave (inverting the blame-the-responder instinct — the insight); and only a malformed two-cycle sequence is a slave bug (the classification that separates them — the discriminator). So bad HRESP debugging is the classify-first discipline — check whether the ERROR is well-formed (legitimate rejection → fix the access) or malformed (slave RTL bug → fix the slave) — inverting the usual instinct because the slave is usually right. So this chapter teaches the split. So classify before blaming the slave.
3. Mental Model
Model debugging a bad HRESP as investigating why a bank declined a transaction — your first question isn't "the bank's system is broken," it's "was the transaction actually valid?" Most declines are correct: insufficient funds, wrong PIN, a frozen account, a blocked merchant. The bank is doing its job — rejecting a bad transaction. You only suspect the bank's system if the decline itself is malformed (it declined but still moved the money, or gave a garbled error code). So you first check: was the transaction legitimate? Usually the customer did something wrong, not the bank.
A bank (the slave) that declines a transaction (returns an ERROR). The naive reaction is "the bank's system is broken!" — but that's usually wrong. The first question should be: was the transaction actually valid? Because most declines are correct — the bank is doing its job, rejecting a transaction that shouldn't go through: insufficient funds (a reserved/unmapped address — there's nothing there), a wrong PIN (a protection/privilege violation — not authorized), a frozen account (a read-only register — can't write it), a blocked merchant (an unsupported operation), or the partner bank declined (a downstream error in a bridge, propagated up). In all these, the bank is right — it correctly declined a bad transaction — and the fix is on the customer's side (use a valid card, the right PIN, an unfrozen account). You don't "fix the bank"; you fix the transaction. The only time you suspect the bank's system is when the decline itself is malformed: the bank declined the transaction but still moved the money (an ERROR but the state changed — no suppression — a safety bug), or it gave a garbled/half-printed error code (a one-cycle error, or HRESP not held — a malformed sequence), or the timing of the decline message was wrong (bad HREADYOUT). Then the bank's system is broken (a slave RTL bug). So you investigate in order: first, was the transaction valid? (Usually not — the customer erred.) Only if the transaction was valid, or the decline is malformed, do you suspect the bank. The decline being well-formed is the bank correctly saying "no"; the decline being malformed is the bank's system failing.
This captures bad-HRESP debug: the bank = the slave; declining a transaction = returning an ERROR; "the bank's system is broken!" = the naive blame-the-slave instinct; was the transaction valid? = is the access legitimate?; insufficient funds / wrong PIN / frozen account / blocked merchant / partner declined = reserved-addr / protection / read-only / unsupported-size / downstream-bridge-error; the bank is right, fix the transaction = the slave is right, fix the access; declined but still moved the money = ERROR but state changed (no suppression); garbled/half-printed error code = malformed two-cycle sequence (one-cycle / HRESP not held); then the bank's system is broken = a slave RTL bug. Check the transaction first — most declines are correct; only a malformed decline is the bank's fault.
Watch the split on the waveform — a well-formed ERROR vs a malformed one:
Bad HRESP: well-formed (legitimate) vs malformed (slave bug)
4 cyclesThe model's lesson: check the transaction first — most declines are correct; only a malformed decline is the bank's fault. In the waveform, the well-formed ERROR (cycles 0–1) is a legitimate rejection (fix the access), while the malformed ERROR (cycles 2–3, HRESP dropped at HREADYOUT high) is a slave RTL bug (the master misses the error). The split tells you where to look.
4. Real Hardware Perspective
In debug, you first check the two-cycle sequence (well-formed vs malformed), then — if well-formed — decode the address to the slave and check which error condition fired (against the map, attributes, protection, size, downstream), and verify whether the access was actually valid.
The check the two-cycle sequence: on the waveform, first verify the ERROR sequence is well-formed (16.6): HRESP=ERROR in both cycles, HREADYOUT low then high, the master sampling ERROR at HREADYOUT high, and (for a write) no state change. If any of these is wrong (one-cycle, HRESP dropped, bad HREADYOUT, state changed), it's malformed → a slave RTL bug. If all correct, it's well-formed → the slave deliberately errored. So in debug, check the sequence first. So it's the well-formed/malformed split. So that's step one.
The trace which condition (well-formed): if well-formed, identify the slave (decode the address — 18.4) and check which error condition it detected: is the address reserved/unmapped (caught by the default slave — 18.4 — so the address was wrong)? Is it a write to a read-only register (check the register attributes — 16.2)? A protection violation (check HPROT against the slave's required mode)? An unsupported size/misaligned access (check HSIZE/alignment)? For a bridge, a downstream error propagated up (check the downstream response — 17.10)? Each → a concrete cause. So in debug, trace which condition fired. So it's condition identification. So that finds the cause.
The verify the access: with the condition identified, verify whether the access was actually valid. Usually, it wasn't — the software/master made a bad access (a bad pointer → reserved address; a wrong privilege → protection violation; a write to a status register → RO write). So the slave is right and the fix is the access (the software/driver/master). Only if the access was valid (and the slave still errored) is there a slave bug (a wrong error condition — e.g. a mis-configured RO attribute, or a decode error). And the malformed case is always a slave bug. An assertion helps: the two-cycle-ERROR assertion (17.2) catches malformed sequences; the scoreboard + access log helps verify the access. So in debug, bad HRESP is a classify-then-trace (check the sequence → malformed=slave-bug, or well-formed → trace the condition → verify the access, usually wrong). So in debug, classify, trace, and verify the access. So that's the method.
5. System Architecture Perspective
At the system level, a bad HRESP is where the bus's error-reporting meets system-level fault handling — so debugging it spans the hardware (the slave's response) and the software (the access that triggered it) — and the default assumption (the slave is right) reflects that ERROR is a designed mechanism, not a failure.
The hardware/software fault boundary: an ERROR is the boundary between hardware (the slave detecting and reporting the fault) and software (the access that triggered it, and the handler that responds). So debugging a bad HRESP spans both — the hardware (is the response well-formed?) and the software (was the access valid?). So at the system level, it's a cross-domain debug (HW response + SW access). So it spans domains. So both sides matter.
The ERROR is designed, not a failure: an ERROR is a designed mechanism — the intended way to report an invalid access (a bad pointer, a security violation, a configuration error). So an ERROR occurring is not (usually) a failure of the hardware — it's the hardware working to report a software/access problem. So the default assumption (the slave is right) reflects this design intent. So at the system level, an ERROR is a feature (fault reporting), not a bug — and debugging it means finding what triggered the legitimate report. So it's a feature working. So ERROR is by design.
The debugging reveals access bugs: because most unexpected ERRORs are legitimate, debugging them reveals the access bugs they report — a bad pointer (reserved-address ERROR), a privilege bug (protection ERROR), a driver bug (RO-write ERROR). So the ERROR is a diagnostic signal — it's pointing at a software/access bug. So following the ERROR finds the real (access) bug. So at the system level, a bad HRESP is the hardware/software fault boundary (the slave reports, the software triggered/handles — a cross-domain debug), where ERROR is a designed mechanism (fault reporting, not a hardware failure — so the slave is usually right), and debugging it reveals the access bug it's reporting (the bad pointer / privilege / driver error). So a bad HRESP is less often a hardware bug than a signal pointing at a software/access bug — so the debug is cross-domain, the default is the slave is right, and the ERROR is a diagnostic leading to the real (access) problem. So follow the ERROR to the access bug, and only suspect the slave if the sequence is malformed. So the ERROR is usually a clue, not the bug.
6. Engineering Tradeoffs
Debugging a bad HRESP embodies the classify-first, trace-the-condition, verify-the-access approach.
- Classify first (sequence) vs assume slave bug. Classifying (well-formed vs malformed) determines where the bug is; assuming a slave bug wastes time (the slave is usually right). Classify first.
- Trace the condition vs assume the cause. For a well-formed ERROR, tracing which condition fired (addr/attr/prot/size/downstream) identifies the cause; assuming misses it. Trace the condition.
- Verify the access vs fix the slave. Verifying whether the access was valid reveals it's usually the access (software/master) that's wrong; fixing the slave (the victim of a bad access) is wrong. Verify the access.
- Malformed = slave bug. Only a malformed sequence (broken two-cycle ERROR) is a slave bug; fix the response generation (16.6). The split separates them.
The throughline: a bad HRESP is an unexpected or malformed ERROR — and the first move is a split: is the ERROR well-formed but unexpected (the slave correctly rejected an access → trace which condition — reserved addr/RO write/protection/size/downstream — and verify the access, which is usually the bug) or malformed (the two-cycle sequence is broken — a one-cycle error, HRESP not held, wrong HREADYOUT, or state changed → a slave RTL bug in the response generation — 16.6)? The senior insight: don't assume an ERROR is a slave bug — an ERROR is the slave doing its job (faulting an invalid access), so most unexpected ERRORs are the slave correctly rejecting a bad access (the software/master is wrong — fix the access). It's a cross-domain debug (HW response + SW access), where ERROR is a designed fault-reporting mechanism (a diagnostic, not a failure). AHB-Lite = OKAY/ERROR only.
7. Industry Example
Debug two bad-HRESP cases — a legitimate ERROR (bad pointer) and a malformed ERROR (slave bug).
Two issues: software crashes with a bus fault, and (separately) a verification test misses an error it expected.
- Case 1 — unexpected ERROR (legitimate; the access is wrong). Software crashes on a bus fault — an unexpected ERROR on a read. Naive reaction: "the slave is broken." Correct debug: first classify. On the waveform, the ERROR is a well-formed two-cycle sequence (
HRESP=ERRORboth cycles,HREADYOUTlow→high) — so the slave deliberately errored. Trace the condition: decode the address → it's an unmapped address (caught by the default slave — 18.4). Verify the access: the software dereferenced a bad pointer (an uninitialized variable) → an unmapped address. The slave is right (it correctly faulted the bad access). The fix: the software bug (the bad pointer), not the slave. - Case 2 — malformed ERROR (slave bug). A verification test expects an ERROR (it drives a write to a read-only register) but the scoreboard/master doesn't see it. Classify: on the waveform, the slave does drive
HRESP=ERROR— but only in the first cycle; in the second cycle (where the master samples, atHREADYOUThigh),HRESPis dropped to OKAY. The sequence is malformed (HRESPnot held — 16.6) → the master misses the error. This is a slave RTL bug (the response generation dropsHRESPearly). The fix: the slave (holdHRESP=ERRORfor both cycles). - The split in action. Case 1 (well-formed) → the access was wrong (fix the software). Case 2 (malformed) → the slave was wrong (fix the RTL). The same "bad HRESP" symptom, opposite root locations — separated by the well-formed/malformed classification.
- Add assertions. You add the two-cycle-ERROR assertion (17.2) — catching the malformed case (Case 2) automatically in verification — and confirm the default slave + error reporting path for unmapped accesses (Case 1) works (so the bad pointer is cleanly reported, not a hang).
The example shows the split determining the fix location: a well-formed ERROR (Case 1) → the access is wrong (software bad pointer — the slave is right); a malformed ERROR (Case 2) → the slave is wrong (response-generation RTL bug). Classifying first (and not assuming the slave is wrong) sent each to the right place. This is how you debug a response bug. This is the ERROR traced to its true cause.
8. Common Mistakes
9. Interview Insight
Bad HRESP is a high-signal debug interview topic — the don't-assume-slave-bug inversion, the well-formed/malformed split, and the verify-the-access discipline are the signals.
The answer that lands gives the inversion and the split: "A bad HRESP is an unexpected or malformed ERROR, and the most important thing — the thing people get wrong — is that you should not assume an ERROR is a slave bug. An ERROR is the slave doing its job: correctly rejecting an invalid access. So most unexpected ERRORs are the slave being right and the access being wrong. The first move is a split: is the ERROR well-formed but unexpected, or is the two-cycle sequence itself malformed? I check the sequence: is HRESP held ERROR for both cycles, HREADYOUT low then high, and for a write, no state change. If the sequence is malformed — a one-cycle error, HRESP dropped before the master samples, wrong HREADYOUT timing, or state changed despite the error — then it's a slave RTL bug in the response generation; fix the slave. If the sequence is well-formed, the slave deliberately and correctly errored, so I trace which error condition fired: decode the address to find which slave, then check — was it a reserved or unmapped address, often caught by the default slave; a write to a read-only register; a protection or privilege violation; an unsupported size; or, for a bridge, a downstream error propagated up. Then — the critical step — I verify whether the access was actually valid. Usually it wasn't: a bad pointer hitting an unmapped address, the wrong privilege, a write to a status register. So the slave is right and the access is wrong, and the fix is in the software or master, not the slave. Only if the access was genuinely valid and the slave still errored is there a slave bug, like a register wrongly marked read-only. The classic mistake is spending hours in the slave's response logic when the real bug is a bad pointer in the software — the slave was correctly reporting it. So I classify first, and for a well-formed ERROR, I verify the access, because it's usually the bug. And AHB-Lite only has OKAY and ERROR — RETRY and SPLIT are full AHB." The don't-assume-slave-bug inversion, the well-formed/malformed split, and the verify-the-access discipline are the senior signals.
10. Practice Challenge
Build and reason from debugging a bad HRESP.
- The inversion. Explain why you shouldn't assume an unexpected ERROR is a slave bug (the slave is usually right).
- The split. Describe the well-formed-vs-malformed split and why it's the first step (it determines where the bug is).
- Read the waveform. From Figure 2, explain how the well-formed ERROR (legit) and the malformed ERROR (slave bug) differ.
- Trace + verify. For a well-formed ERROR, explain how to trace which condition fired and verify the access (usually wrong).
- Malformed danger. Explain when a bad HRESP is a slave bug and why a malformed sequence (HRESP not held, state changed) is dangerous.
11. Key Takeaways
- Don't assume an ERROR is a slave bug — an ERROR is the slave doing its job (correctly rejecting an invalid access). Most unexpected ERRORs are the slave being right and the access being wrong.
- Split first: well-formed vs malformed — check the two-cycle sequence (
HRESP=ERRORboth cycles,HREADYOUTlow→high, no state change). The split determines where the bug is. - Well-formed → trace the condition + verify the access — which condition fired (reserved/unmapped via the default slave, RO write, protection, size, downstream bridge)? Then verify the access — usually the software/master made a bad access (bad pointer, wrong privilege). Fix the access.
- Malformed → a slave RTL bug — a one-cycle error,
HRESPnot held, wrongHREADYOUT, or state changed despite the error → fix the response generation (16.6). - A malformed sequence is dangerous —
HRESPnot held → the master misses the error; state changed → corruption (a safety/security bug); one-cycle → pipeline incoherence. Don't dismiss it. - It's a cross-domain debug — HW response + SW access; ERROR is a designed fault-reporting mechanism (a diagnostic, not a failure) — follow it to the access bug. AHB-Lite =
OKAY/ERRORonly.
12. What Comes Next
You now can trace a response bug. The next chapters cover the remaining (harder) failure modes:
- Burst Wrap Error (next) — diagnose a WRAP burst computing the wrong address.
- Arbitration Bug, Bridge Deadlock, and the Waveform-Based Debug Methodology — the rest of the debugging module.
To revisit the ERROR response, see HRESP Generation, The Two-Cycle ERROR Response, and How the Master Reacts; for the unmapped-address case, see Illegal Address Behavior and Incorrect HSEL.