Use timeouts systematically to localize where an AXI transaction stalled — per-channel and per-transaction watchdogs that turn a silent hang into a labeled error naming the channel, transaction, and wait condition, how to set thresholds, and how the first timer to fire points nearest the root of any stall, hang, or deadlock.
The hang chapters (17.1, 17.7) all relied on one tool: timeouts. A stalled or deadlocked AXI transaction produces a silent failure — nothing happens, no error fires, the simulation just stops progressing — which is the least debuggable symptom possible. A timeout converts that silence into information: a watchdog that fires when a channel or transaction has been waiting too long, emitting an error that names what stalled, where, and what it was waiting on. Done systematically — per channel, per transaction, per resource — timeouts don't just detect a hang, they localize it: the first timer to fire points nearest the root. This chapter is the instrumentation behind all hang debugging: how timeouts work, where to place them, how to set thresholds, and how to read which-fired-first to find the root of any stall, hang, or deadlock.
1. Why Timeouts: Turning Silence into Information
A liveness failure (something that should happen never does) is invisible by nature — there's no event to catch, the system just freezes. Without instrumentation, you discover it only because the test never finishes, and you're left manually staring at a frozen waveform inferring every party's wait condition. A timeout makes the failure announce itself: a counter increments while a transaction/channel is blocked and, past a threshold, fires an error with context. This is the difference between "the sim hung somewhere" and "the AW channel of master 2 has held VALID without READY for 5000 cycles" — the latter is a localized, actionable finding.
Figure 1 — a timeout turns a silent liveness failure into a localized, actionable error. Without a timeout, a stalled transaction is invisible: the simulation simply stops progressing with no event, and debugging means manually inferring wait conditions from a frozen waveform. A watchdog counter increments while blocked and, past a threshold, fires an error naming the channel/transaction and what it was waiting on — converting 'the sim hung' into 'this specific thing stalled waiting on that'.
2. Where to Place Timeouts: Per Channel, Per Transaction, Per Resource
Granularity determines how much a timeout localizes. Per-channel watchdogs (each VALID-without-READY, or each request awaiting acceptance) catch stuck handshakes at the channel level. Per-transaction watchdogs (each outstanding transaction from issue to completion) catch a transaction that never completes even if its channels individually progressed — and name the transaction (ID, address). Per-resource watchdogs (each buffer slot, outstanding slot, arbiter grant) catch a resource held too long — supplying the wait-for edges (17.7) needed to reconstruct a deadlock. The finer the instrumentation, the more precisely the firing localizes the problem.
Figure 2 — timeout placement granularity. Per-channel watchdogs catch a stuck handshake (VALID without READY too long) and name the channel. Per-transaction watchdogs catch a transaction that never completes (issue to response) and name the transaction (ID/address). Per-resource watchdogs catch a held buffer slot / outstanding slot / grant and supply the wait-for edges for deadlock analysis. Finer granularity localizes more precisely; a complete instrumentation has all three.
The per-transaction watchdog is the workhorse — it tracks each outstanding transaction and fires if completion takes too long:
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Per-transaction watchdog: start a timer when a transaction is issued,// clear it on completion, fire if it exceeds the threshold.foreach (outstanding[i]) begin if (outstanding[i].active) begin outstanding[i].age <= outstanding[i].age + 1; if (outstanding[i].age > TXN_TIMEOUT) $error("TIMEOUT: txn id=%0d addr=%0h issued @%0t never completed, " "stalled in phase %s (waiting on %s)", outstanding[i].id, outstanding[i].addr, outstanding[i].t_issue, outstanding[i].phase, outstanding[i].waiting_on); endend// The 'phase' and 'waiting_on' fields are what localize the stall.
3. Setting the Threshold
A timeout threshold must be long enough to not fire on legitimate backpressure/latency (a false positive that masks real behavior) but short enough to catch a hang promptly. The right value is derived from the worst-case legitimate latency: maximum outstanding depth × per-transaction latency, plus margin — anything beyond the longest a transaction could legitimately take is a hang. Too short → false timeouts on slow-but-correct traffic (and you start ignoring timeouts, the worst outcome); too long → hangs take forever to surface. Set it from the system's real latency bounds, not a guess.
Figure 3 — setting the timeout threshold. The threshold must sit above the worst-case legitimate latency (max outstanding × per-transaction latency + margin) so it never fires on correct-but-slow traffic, and below 'forever' so hangs surface promptly. Too short causes false timeouts (which lead to ignoring timeouts — the worst outcome); too long delays detection. Derive it from the system's real latency bounds, not a guess. The window between worst-case-legitimate and a true hang is where the threshold belongs.
4. Which Fired First: Localizing the Root
The diagnostic power of systematic timeouts is temporal ordering: in any hang, many timers eventually fire (every blocked party times out), but the first to fire is nearest the root, and the later ones are downstream victims waiting on it. So you don't just collect that timeouts fired — you record when each fired and read the earliest. For a single stuck point, the first timeout is the originally-blocked channel; for a deadlock, the set of timers that fire together (with no single first, all interdependent) reveals the cycle. The waveform shows a transaction's age counter climbing past the threshold and the timeout firing at the exact stall point.
Per-transaction timeout firing at the stall point
10 cycles
Figure 4 — a per-transaction timeout firing, and which-fired-first localization. A transaction's age counter increments each cycle it remains incomplete; when it crosses the threshold, the timeout fires, marking the exact point and naming the stalled transaction and its wait condition. In a multi-party hang, the FIRST timer to fire is nearest the root (downstream victims fire later, waiting on it); a set of timers firing together with no clear first indicates a deadlock cycle. Record when each fires and read the earliest.
5. Common Misconceptions
6. Debugging Insight
7. Verification Insight
8. Interview Questions
9. Summary
Timeouts are the detection and localization backbone for every liveness failure in AXI — stalls, hangs, and deadlocks are all silent (no event fires; the system just freezes), and a timeout converts that silence into a localized, labeled error: a watchdog increments while a transaction/channel/resource is blocked and, past a threshold, fires naming what stalled, where, and what it waited on. Place them at every granularity — per-channel (stuck handshake), per-transaction (never-completes, the workhorse), and per-resource (held slot/grant, supplying the deadlock wait-for edges) — because granularity is what localizes. Set the threshold from worst-case legitimate latency (max outstanding × per-transaction latency + margin): too short causes false positives that train people to ignore timeouts (the worst outcome), too long delays detection.
The diagnostic power is which-fired-first: in a propagating hang the earliest timer is nearest the root and later ones are downstream victims, while a set firing together with mutual waits and no clear first signals a deadlock cycle — so record fire times, not just the fact of firing, and read the earliest plus its named wait condition. The timeout pattern even classifies the hang (clears → stall; single root → stuck point; mutually-waiting set → deadlock) before you open the waveform. This formalizes the instrumentation that 17.1 and 17.7 assumed. The unifying principle is observability for liveness: safety failures self-announce (catch the event), but liveness failures are silent and must be made observable by bounding time and watching the bound — timeouts are that instrument, which is why they underlie the whole hang-debugging family and belong in silicon too. Next, the final chapter assembles every method in Module 17 into one repeatable waveform-debugging methodology.
10. What Comes Next
You now have the detection backbone; the final chapter assembles the whole method:
17.9 — Waveform-Based Debug Methodology(coming next) — a single repeatable method for debugging any AXI waveform, integrating the handshake, burst, response, ID, deadlock, and timeout techniques of Module 17.