DMA STATUS reads DONE = 1 and the irq pin at the DMA is high, yet the ISR for IRQ 1 never runs while INTC RAW bit 6 sits at 1. Polling works, interrupts do not. The wire went to spare input 6 instead of input 1. Reviews that read the table pass; only a test that forces each source and checks its own RAW bit catches the wiring.
Goal: the wired map plus a latency bound per urgent line.
Inputs: the 8 sources with clear methods and priorities, the sync depths.
Steps: file the map; recompute best and worst latency; swap irq_dma to input 6 in the model and confirm the connectivity test names it and RAW bit 6 lights; take one flop out of a synchroniser and confirm the latency bound fires; restore.
IRQ | Source | Cleared by | Priority |
|---|---|---|---|
0 irq_pwr | PWR_IRQ OR | W1C PWR_IRQ | 0, highest |
1 irq_dma | irq_en AND (done OR err) | W1C DONE/ERR or clear IRQ_EN | 1 |
2 irq_sbr | IRQ_EN AND (DONE OR ERR) | W1C SBR STATUS | 2 |
3 irq_uart | TXIE/TXE or RXIE/RXNE | Read/write DATA | 3 |
4 irq_spi | IE AND RXNE | Read DATA | 4 |
5 irq_gpio | OR of DATA_IN AND INT_EN | Remove the condition | 5 |
6, 7 | Reserved, tied to 0 | n/a | Lowest |
MAP = [
("BOOTROM", 0x00000000, 0x4000),
("SRAM0", 0x20000000, 0x10000),
("SRAM1", 0x20010000, 0x10000),
("DMA", 0x40000000, 0x1000),
("SBR", 0x40001000, 0x1000),
("UART", 0x40002000, 0x1000),
("SPI", 0x40003000, 0x1000),
("GPIO", 0x40004000, 0x1000),
("INTC", 0x40005000, 0x1000),
("RSTPWR", 0x40006000, 0x1000),
]
SECURE_ONLY = {"SRAM1", "RSTPWR"}
SLOTS = ["DMA", "SBR", "UART", "SPI", "GPIO", "INTC", "RSTPWR"]
T_CPU, T_SYS, T_PER = 1e9 / 192e6, 1e9 / 96e6, 1e9 / 24e6
def end_of(base, size):
return base + size - 1
def overlaps(entries=MAP):
bad = []
xs = sorted(entries, key=lambda e: e[1])
for a, b in zip(xs, xs[1:]):
if b[1] < a[1] + a[2]:
bad.append((a[0], b[0]))
return bad
def misaligned(entries=MAP):
return [n for n, b, s in entries if b % s != 0]
def decode(addr, slots=SLOTS):
if addr >> 14 == 0:
return "BOOTROM"
if addr >> 16 == 0x2000:
return "SRAM0"
if addr >> 16 == 0x2001:
return "SRAM1"
if addr >> 16 == 0x4000:
slot = (addr >> 12) & 0xF
return slots[slot] if slot < 7 else "H3"
if addr < 0x20000000:
return "H1"
if addr < 0x40000000:
return "H2"
return "H4"
def response(addr, secure, slots=SLOTS):
t = decode(addr, slots)
if t in ("H1", "H2", "H4"):
return "DECERR"
if t == "H3":
return "SLVERR"
if t in SECURE_ONLY and not secure:
return "SLVERR"
return "OK"
def sustained(peak, beats):
return peak * beats / (beats + 1)
# ---- this lesson starts here ----
def latency(sync_per, ctrl, sync_cpu):
best = sync_per * T_PER + ctrl * T_PER + sync_cpu * T_CPU # two syncs, the INTC register, two syncs
return best, best + T_PER + T_CPU # worst adds one clk_per and one clk_cpu of alignment
best, worst = latency(2, 1, 2)
assert abs(best - 135.4) < 0.1 and abs(worst - 182.3) < 0.1
assert abs(latency(1, 1, 2)[0] - 135.4) > 0.1 # a missing flop moves the bound
ORACLE = {"irq_pwr": 0, "irq_dma": 1, "irq_sbr": 2, "irq_uart": 3, "irq_spi": 4, "irq_gpio": 5}
def intc_raw(wiring, active):
return sum(1 << wiring[s] for s in active) # RAW bit i is set when the line wired to input i is high
def conn_test(w):
return [s for s in ORACLE if intc_raw(w, [s]) != 1 << ORACLE[s]] # force one source at a time
wiring = dict(ORACLE)
assert conn_test(wiring) == []
assert intc_raw(wiring, ["irq_dma"]) == 0x02 and intc_raw(wiring, list(ORACLE)) == 0x3F # spares 6 and 7 stay 0
bad = dict(wiring, irq_dma=6) # bug 3
assert conn_test(bad) == ["irq_dma"] and intc_raw(bad, ["irq_dma"]) == 0x40 # RAW bit 6, not bit 1
swap = dict(wiring, irq_dma=2, irq_sbr=1)
assert conn_test(swap) == ["irq_dma", "irq_sbr"]
print("PASS: map wired 0-5, spares tied; IRQ latency 135.4 best, 182.3 worst ns")One PASS line with the wiring verdict and both latency bounds. The bad wiring is the bug-3 demonstration: forcing irq_dma lights RAW bit 6 instead of bit 1, and the test names the source. The swapped pair shows the test also catches two lines crossed, and the one-flop latency shows the bound reacting to a synchroniser change.
A latency failure means a stage was dropped: two peripheral syncs, one controller register, two CPU syncs, then one peripheral plus one CPU cycle of worst-case alignment. A connectivity failure on clean data means the oracle map drifted. An empty report on the bad dict means the test compared against the wiring under test instead of the oracle.
Hint 1: the symptom pairs a live source pin with a dead expected bit and a live spare bit. Hint 2: polling works because the DMA status is fine; only the path to the CPU is broken. Hint 3: unused inputs tie to 0, so any activity on 6 or 7 is wiring, not noise. Answer: connect irq_dma to input 1, tie 6 and 7 to 0, and force each source in turn while checking its matching RAW bit.
0x1F: the no-pending code, distinct from every valid index.