DigiFlop

HomeDebug Guides › AXI / APB / AHB Debug Guide
RTL Debug · Waveform Analysis
Reading AXI, APB and AHB Like a Debugger, Not a Spec
Symptom first, cause second, debug tactic third — for RTL design engineers debugging register-access and clock-management blocks.
Written for engineers debugging register-access and clock-control logic in mobile SoC and AI accelerator designs, using an RTL waveform debugger. No specific debug tool is assumed — navigation and terminology vary by environment, but the bug patterns below don't.

Quick Cheat Sheet

The failures that show up most often on register-access and clock-control blocks. Skim this before you go signal-by-signal.

Protocol Symptom Likely fix
AXI Waveform freezes with AWVALID/ARVALID high, nothing moves Trace READY backwards through the interconnect — almost always a full FIFO or an arbiter that never grants this master's ID
AXI Read data looks right but lands in the wrong register Check RID against the ARID of the request you think you're looking at — you're probably reading a different outstanding transaction
APB PENABLE never rises after PSEL SETUP-to-ACCESS state transition is missing in the bridge/master FSM — one-cycle bug, check the state register directly
APB Register write "doesn't take" — PREADY asserts, value never changes Address decode landed on a different offset. Compare PADDR against your register map, not against what the driver intended to send
AHB One slave inserts a wait state and the whole bus stalls Expected — AHB is single-bus, HREADY is shared. Confirm it's the target slave holding HREADYOUT low, not a sibling
CDC A status bit glitches for exactly one destination-clock cycle Missing or under-staged synchronizer. Check flop count between source logic and the first destination-domain register, not the waveform value itself

AXI4 / AXI4-Lite

Master Slave AW — write address W — write data B — write response AR — read address R — read data Five independent VALID/READY channels — purple = write path, blue = read path

Five independent channels, each with its own VALID/READY handshake, tied together only by IDs and ordering rules. Most AXI debug time goes into figuring out which outstanding transaction a signal value belongs to — not what the signal means.

The five channels

Channel Direction Key signals Carries
AW — write address M→S AWID, AWADDR, AWLEN, AWSIZE, AWBURST, AWVALID/READY Where and how a write burst will land
W — write data M→S WDATA, WSTRB, WLAST, WVALID/READY The burst payload, one beat per cycle
B — write response S→M BID, BRESP, BVALID/READY Whether the write committed cleanly
AR — read address M→S ARID, ARADDR, ARLEN, ARSIZE, ARBURST, ARVALID/READY Where a read burst starts
R — read data S→M RID, RDATA, RRESP, RLAST, RVALID/READY The returned burst, one beat per cycle

Reading the handshake: a transfer happens on the clock edge where VALID and READY are both high — not when either one first asserts. Once a master raises VALID it is not allowed to lower it again until that edge fires, so a VALID pulse that drops before READY ever rises is a protocol violation, not a cancelled request. If your testbench has an assertion-based protocol checker, let it flag this class of bug instead of eyeballing every pulse.

Common bug signatures

Waveform symptom Likely cause Where to look
AWVALID/ARVALID high for many cycles, READY never comes Downstream FIFO full, or the interconnect arbiter never grants this master's outstanding ID Walk backward through the interconnect one hop at a time; correlate READY with the sibling master that is being granted
BRESP / RDATA arrives for the wrong request BID/RID doesn't match the AWID/ARID you're tracking — transactions completed out of order, which AXI explicitly allows across different IDs Filter or color the waveform by ID and follow one ID end-to-end instead of by time order
Write appears to corrupt neighboring bytes WSTRB doesn't match AWSIZE, or a narrow transfer isn't aligned the way the slave assumes Compare WSTRB bit pattern against AWSIZE and the low address bits for that beat
Burst response comes one beat short or long WLAST/RLAST asserted on the wrong beat relative to AWLEN/ARLEN Count W/R beats against AxLEN+1 explicitly — AxLEN is "beats minus one," the classic off-by-one
Second write to the same register silently reuses stale data Master reissued AWID before the prior transaction with that ID returned its BRESP, breaking ID ordering guarantees Check that ID is retired (BRESP/RRESP seen) before the same ID is reused for a new address phase
Access to a register just past a page silently corrupts an unrelated address A burst was allowed to cross a 4KB boundary, which AXI forbids and most interconnects don't check Check AWADDR/ARADDR + burst size × length against the enclosing 4KB window at the request boundary

APB (APB4)

SETUP ACCESS (PREADY held low = wait states) PSEL PENABLE PREADY wait state Transfer completes on the edge where PSEL & PENABLE & PREADY are all high

This is almost certainly what your control/status registers sit behind. It's the simplest of the three protocols, which is exactly why bugs here are usually not protocol bugs at all — they're address decode, timing, or clock-domain-crossing bugs wearing an APB costume.

Signals and the two-phase transfer

Signal Meaning
PSEL[x] One-hot select for the addressed slave — asserted for the whole transfer
PENABLE Low in SETUP (cycle 1), high through ACCESS (cycle 2 onward) — this is what distinguishes the two phases
PADDR / PWRITE / PWDATA Must be stable from SETUP through the end of ACCESS
PREADY Slave holds this low to insert wait states; transfer completes on the edge where PSEL & PENABLE & PREADY are all high
PRDATA / PSLVERR Valid on the same edge the transfer completes
PSTRB Byte lane enables for writes (APB4+)

In the waveform: one flat cycle of PSEL alone (SETUP), then PSEL+PENABLE held together until PREADY closes it out (ACCESS). If a sensor block needs several cycles for a conversion to finish, that latency shows up entirely as PREADY held low — the bus is simply waiting, which is correct behavior, not a hang, up to whatever timeout your environment enforces.

Common bug signatures

Waveform symptom Likely cause Where to look
PSEL asserts, PENABLE never follows Master/bridge FSM has a broken SETUP→ACCESS transition Look at the driving state register directly, not the output signal — usually a one-state, one-cycle bug
Write reports success but the register never changes Address decode routed the transfer to the wrong PSEL, or the register offset doesn't match the map Diff PADDR against your register spec offset-by-offset, not against what you assume the driver sent
Two PSEL lines high at once Overlapping address decode ranges between two slaves Check the decoder's range boundaries against both slaves' address maps for an overlap or off-by-one
PREADY never returns Slave FSM waiting on an internal "done" pulse (e.g. conversion complete) that never arrives, often because that pulse lives in a different clock domain and was never synchronized in Trace the internal done/ready signal feeding PREADY back to its source domain — see the clock-domain-crossing section
PRDATA looks stale by one transfer Register read muxed off a pipeline stage that lags the address by a cycle Check the read-data mux selects with the same PADDR that was valid in SETUP, not the current cycle's PADDR

AHB / AHB-Lite

Address phase Xfer N Xfer N+1 Data phase Xfer N Xfer N+1 Xfer N's data phase overlaps Xfer N+1's address phase — one shared HREADY stalls both A wait state on either phase holds up the entire pipeline, not just one transfer

Less common in new designs but still turns up on legacy peripheral bridges. The defining trait to hold onto: it's pipelined but single-bus — only one transfer's address phase and one transfer's data phase are in flight at a time, and a wait state from any slave stalls everyone.

Signals and pipelining

Signal Meaning
HTRANS IDLE / BUSY / NONSEQ / SEQ — NONSEQ starts a new burst, SEQ continues one
HADDR / HSIZE / HBURST / HWRITE Address phase, one cycle ahead of the matching data phase
HWDATA / HRDATA Data phase, overlapping the next transfer's address phase
HREADY (in) / HREADYOUT (out) HREADYOUT from the selected slave becomes HREADY broadcast to everyone — a wait state is global, not local
HRESP OKAY or ERROR; an ERROR response is a two-cycle protocol, not one

Common bug signatures

Waveform symptom Likely cause Where to look
Entire bus stalls, not just one slave's transfer This is expected AHB behavior — HREADY is shared. Confirm it's actually your slave holding HREADYOUT low and not a sibling on the same bus Check HSEL for the stalling slave alongside its HREADYOUT, not just the merged HREADY
Slave samples the wrong address after a wait state Address wasn't held/re-registered correctly across the HREADY-low cycles Confirm the slave only advances its captured HADDR on cycles where HREADY was high
Bus appears to hang permanently after an error HRESP ERROR wasn't held for its required two cycles (first with HREADY low, then high) — a downstream master's FSM built for the two-cycle sequence gets stuck waiting for the second phase Check HRESP and HREADY together across the error response, not HRESP alone
Burst reads land at the wrong addresses HTRANS/HBURST sequencing broken — SEQ asserted without a preceding NONSEQ, or wrap boundary miscalculated Walk HTRANS cycle by cycle against HBURST type; verify wrap alignment against HSIZE

Clock-Domain Crossing (CDC)

Source clock domain Destination clock domain FF src metastability risk FF1 sync FF2 resolved No combinational logic between FF1 and FF2 — that's the structural check a waveform alone can't show you

This is where clock-management debug actually lives. A register interface typically runs on a bus clock, but a PLL, glitch-free clock mux, and dividers operate on reference and generated clocks that don't share an edge with it. Sensor blocks add a second crossing: a conversion or free-running measurement source produces a "done" pulse on its own clock, which then has to reach the register-interface domain intact.

A clean-looking waveform can still be a CDC bug. RTL simulation won't show metastability — a signal that crossed clock domains with no synchronizer at all can still look like a normal, glitch-free toggle in the waveform. The real check is structural: count the flop stages between the source-domain register and the first destination-domain register, and confirm there's no combinational logic in between. A CDC lint or formal check will catch what a waveform review alone cannot.

Crossing patterns you'll actually see

Pattern Where it shows up What "correct" looks like on the waveform
2-flop (or 3-flop) synchronizer Single status/control bits — e.g. a PLL lock indicator into the register-access domain The bit changes value cleanly aligned to a destination-clock edge, one cycle after the intermediate flop, never mid-cycle
Gray-coded pointer / async FIFO Any multi-bit value crossing domains, including config words written into a clock-generation domain Pointer bits never change more than one bit at a time between sampled values — if multiple bits change together across a crossing, that's the smoking gun for a missing gray-code stage
Request/acknowledge (4-phase) handshake A "conversion done" pulse crossing from a sensor's clock into the domain that reads back the result Req asserts and stays asserted until ack returns; a req that pulses and drops before an ack could possibly have propagated is undersized for the destination clock rate
Glitch-free clock mux (clock-management specific) Switching an output clock between reference/PLL/bypass sources The mux control sequence gates the old clock's output low, waits enough cycles for the switch logic to settle in both domains, then enables the new clock — look for that gate-then-settle-then-enable ordering explicitly, not just the final clock waveform

Common bug signatures

Waveform symptom Likely cause Where to look
Status bit glitches for exactly one destination-clock cycle then reverts Missing synchronizer, or a synchronizer with only one flop stage instead of two Count registers between source and destination domain in the RTL, don't infer it from the waveform shape
Multi-bit config value briefly reads as a value that was never written A whole bus was double-flopped as a group instead of gray-coded or handshaked — different bits arrive on different destination cycles Overlay the individual bits of the bus and check whether they all change on the same destination-clock edge
A "conversion done" pulse is sometimes missed entirely Source-domain pulse is narrower than one destination-clock period, so the destination sampling flop can miss it outright Compare pulse width in the source domain against the destination clock period; look for a toggle-based or stretched-pulse crossing instead of a raw one-cycle pulse
Output clock glitches briefly during a clock-source switch New clock enabled before the old one was fully gated, or before both domains agreed the switch was safe Check the gate/settle/enable sequence on the mux control signals against both source clocks' edges around the switch point

Waveform Debugger Tactics

Most RTL debug environments let you work above the pin level once you set them up that way. The gap between "scrolling raw signals" and "reading transactions" is almost entirely setup, done once per block and reused every session.

  1. Attach a protocol checker, don't eyeball VALID/READY. If your testbench has AXI/APB/AHB verification IP with assertion-based protocol checking, its violation reports are ground truth for handshake and ordering bugs — use them to point you at the cycle, then open the waveform there.
  2. Group signals into buses before you start. Bundle AWID/AWADDR/AWLEN/... into one composite "AW" row, same for W, B, AR, R (or PADDR/PWRITE/PSEL/PENABLE for APB). Reading decoded bus values beats scanning twenty individual traces.
  3. Filter or color by ID on AXI. With multiple outstanding transactions in flight, isolate one AWID/ARID at a time and follow it from address phase to response — don't try to read every ID's activity at once.
  4. Use transaction-level / protocol-aware views over raw signal view when available. Many debug environments can decode bus activity into transaction rows (address, data, ID, response) — much faster than reconstructing a burst by hand from beats.
  5. Cross-probe from the waveform to RTL source. When a value looks wrong, jump straight from that signal to the driving always block instead of guessing which process wrote it.
  6. Pull up an auto-extracted FSM view for sequencers. Particularly useful for a clock-switch sequencer or any conversion-state machine in a sensor block — overlay the current state on the waveform instead of decoding a state-encoding value by hand.
  7. Overlay both clocks explicitly at a CDC boundary. Put the source and destination clocks in the same waveform pane as the crossing signal so you can see the actual edge relationship, not just infer it.
  8. Save the signal-group and bookmark setup per block. Register-access and clock-control debug sessions repeat the same waveform layout constantly — save it once so the next bug starts from a readable view, not a blank signal list.

Stuck? Work It in This Order

When you don't yet know which protocol layer the bug is in, this order tends to find it fastest.

  1. Confirm the handshake actually completed. Find the exact edge where VALID/READY (or PSEL/PENABLE/PREADY, or HTRANS/HREADY) were all simultaneously satisfied. If you can't find that edge, you have a handshake bug, not a data bug — stop looking at data signals.
  2. Confirm you're looking at the right transaction. On AXI, match ID before you trust any data or response value. It's easy to debug a real bug on the wrong outstanding transaction.
  3. Confirm the address, not just the data. A large share of "wrong value" bugs on register-interface blocks are address decode or offset bugs, not data-path bugs. Check PADDR/AWADDR/ARADDR/HADDR against the register map before debugging logic.
  4. If it crosses clocks, check flop count before waveform shape. RTL simulation can't show you metastability. A structural CDC check (lint/formal) tells you more than staring at the trace.
  5. If it's intermittent, check timing relative to the slower clock. Pulses that are fine at one clock ratio can be missed at another — especially relevant if an internal clock rate can vary with operating conditions such as temperature.

Signal names and protocol behavior follow the Arm AMBA AXI4, APB, and AHB protocol specifications. Debug tactics are described generically — exact navigation and terminology vary by tool, so check your own debug environment's documentation for precise steps.

Read the AMBA AXI concept guide → Read the CDC concept guide → AMBA interview questions → ← All Debug Guides STA debug guide →