A power engineer clears CLK_EN bit 0 to save the DMA's idle power while the CPU keeps polling DMA STATUS. The next APB read never completes, the bridge waits forever, and the CPU hangs in what looks like a fabric deadlock. Nothing is broken except the order: the clock stopped while the bus still needed the block. This is the failure the task names for this topic, told here as symptom, capture and fix. Its one job is this story; the general ownership rules belong to the previous lesson.
The DMA answers APB accesses with one wait state: three clk_sys clocks per access, with pready low in the first access cycle and high in the second. That pready is generated by the DMA core on its gated clock. When software clears the enable, the bus side still accepts the address phase, because the fabric runs on, but the core never produces the completing cycle. The bridge holds the AXI response, the CPU holds the load, and every later master queues behind it. The CPU is itself blocked on the load, so it cannot re-enable the clock: only a reset recovers a chip whose bridge has no timeout.
The fix has three parts. Software clears the enable only when the DMA is idle, STATUS BUSY read as 0, with no APB access in flight and the DONE interrupt already cleared; it sets the enable again before touching any DMA register. Hardware refuses to close a gate while the block's STATUS.BUSY is 1 (the DMA is executing a job), so a software mistake cannot stop a working job; it does not help when an idle, gated block is later read, which is why the third part matters. And the integrator's safety net: nano_axi2apb gets a 256-cycle access timeout that completes the access with PSLVERR (SLVERR on the AXI side). 256 clk_sys cycles is 256 x 10.417 ns = 2.667 us, against 31.25 ns for the normal three-clock access, so the timeout never fires on a healthy access and a stopped block costs the CPU 2.667 us and an error instead of a hang. The test clears CLK_EN mid-poll and demands completion or that clean error, never a hang.
# Model of the failure: a 3-clock APB access against a clock gated mid-access.
# The core side must produce 3 gated edges; the bridge waits at most TIMEOUT.
TIMEOUT = 256 # clk_sys cycles, the bridge watchdog
def apb_access(ticks, timeout=None):
# ticks: per-cycle state of the DMA core clock, True = ticking
need, done = 3, 0
for cycle, ticking in enumerate(ticks, start=1):
if timeout is not None and cycle > timeout:
return "PSLVERR"
if ticking and done < need:
done += 1
if done == need:
return "COMPLETES"
return "HANGS"
stuck = [True, True] + [False] * 400 # clock stops after two edges, never returns
assert apb_access([True] * 6) == "COMPLETES"
assert apb_access(stuck) == "HANGS" # no timeout: the failure
assert apb_access(stuck, TIMEOUT) == "PSLVERR" # with the watchdog: clean error
assert apb_access([True] * 6, TIMEOUT) == "COMPLETES" # healthy access never trips it
assert abs(TIMEOUT * 1e3 / 96 - 2666.7) < 0.1 # 256 x 10.417 ns
# Hardware refuses to close a gate while the block is BUSY.
def may_gate(busy, in_flight):
return (not busy) and (not in_flight)
assert may_gate(False, False) and not may_gate(True, False) and not may_gate(False, True)
print("PASS: mid-access gate hangs; timeout gives PSLVERR at 256 cycles; busy block is not gated")The driver documents the idle-and-quiet precondition next to every write that clears a CLK_EN bit.
The verification plan contains the gate-mid-poll test by name, with hang forbidden and the 256-cycle bridge timeout (PSLVERR) as the expected outcome.
No power sequence gates a block clock while its bus segment is selected; the check is structural, not advisory.
Reproduce with a single APB read issued one cycle before the enable clears: the response must arrive, or come back as PSLVERR after the 256-cycle timeout, and never hang. Remove the timeout in the model and the same test must fail. On silicon the same hang points here when the faulting address belongs to a gated block and the hang correlates with a power transition; read back CLK_EN and STATUS before suspecting the fabric. If the symptom persists with clocks on, the bridge's own wait-state handling is next, not this lesson.
No. The SBR is the DMA's data source and sink; gating it mid-job stalls the streams and wedges the job. Gate only when both STATUS registers read idle, in either order, then re-enable before programming the next job.