A lab without known symptoms teaches touring, not integrating. Ten real integration failures are injected into the clean lab data, one at a time. You receive only the observable symptom here; infer the failing boundary from the evidence and write your own hypothesis before opening the separate Solutions lesson. Your grade is the fraction you catch with checkers, not with eyes.
Goal: classify the ten symptoms, identify the evidence you would collect, and use the rubric weights.
Inputs: the symptom table below; the clean behaviour of the DMA, SBR, clocks, resets, power and interrupt map from earlier stages.
Steps: for each symptom write a hypothesis and the checker or trace that would distinguish it; read Solutions only after the checks.
Bug | Symptom | Stage |
|---|---|---|
Symptom 1 | SBR CTRL write flips DMA data bit 0 | 1 |
Symptom 2 | CPU stalls at 0x0000_0000, no RVALID | 2 |
Symptom 3 | DMA irq high, INTC RAW bit 1 is 0, bit 6 is 1 | 3 |
Symptom 4 | Only CTRL reads right; STATUS, LEN, KEY take PSLVERR and a COUNT read silently returns STATUS | 1 |
Symptom 5 | Missed interrupt once in thousands of jobs | 2 |
Symptom 6 | Spurious irq_cpu in SLEEP; sleep/wake loop | 2 |
Symptom 7 | SBR STATUS.ERR on first beat from SRAM1 | 3 |
Symptom 8 | ERR with COUNT 32, or SBR stuck BUSY | 3 |
Symptom 9 | Stale KEY; irq never drops; IRQ_EN cleared | 3 |
Symptom 10 | After CLK_EN = 0 the first access to the idle DMA hangs the bus; only reset recovers | 2 |
Two related observations are not numbered bugs. Placing the SBR source and destination in the same SRAM bank changes the stream rate (stage 1). And the SBR register slot is non-secure, so the secure-partition question remains open in NanoSoC v1 (stage 3 and the readiness review). Record both as evidence to investigate, not as answer keys.
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 ----
bugs = {1: "stage1", 2: "stage2", 3: "stage3", 4: "stage1", 5: "stage2",
6: "stage2", 7: "stage3", 8: "stage3", 9: "stage3", 10: "stage2"}
def covered(b):
return {s: sum(1 for v in b.values() if v == s) for s in ["stage1", "stage2", "stage3"]}
EXPECT = {"stage1": 2, "stage2": 4, "stage3": 4}
assert sorted(bugs) == list(range(1, 11)) and covered(bugs) == EXPECT
assert covered({**bugs, 4: "stage2"}) != EXPECT # a mis-filed bug is visible
assert decode(0x40000000) == "DMA" and decode(0x40001000) == "SBR"
print("PASS: 10 planted bugs filed: stage1 2, stage2 4, stage3 4; stage 4 proves all ten")One PASS line with the per-stage catch counts. Stage 4 files no bug of its own: it runs the matrix that proves stages 1 to 3 caught all ten. The checker validates the exercise data; it does not reveal the solution key.
A count failure means a bug was filed under the wrong stage; the table above is the oracle. A decode failure means the engine's map disagrees with your bug-1 notes: slots 0 and 1 are the DMA and SBR windows, and both must decode clean.
Choose one symptom, write two possible causes and identify the observation that would distinguish them. Preserve the clean requirement independently of the data being diagnosed. Compare your reasoning with Solutions only after you have completed the stage checks.
Grading rubric (illustrative NanoSoC teaching values): 40 percent for checkers that catch their bugs on both good and injected-bad data, 25 percent for evidence-backed diagnoses quoting the exact register, signal or cycle count, 20 percent for the final manifest check passing, 10 percent for the waiver-ageing review with every waiver evidenced, 5 percent for the readiness statement and its stated leftovers. A stage scores only when its checker prints PASS on the clean data and flags the injected fault.
The required behaviour, the observed behaviour, at least two possible explanations and an observation that would distinguish those explanations.