These are the worked solutions. Compare the inputs and injected faults with your written hypothesis before running each block. Each block runs independently and prints one PASS line after its clean-data and failing-data assertions.
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 ----
assert overlaps() == [] and misaligned() == []
for name, base, size in MAP:
assert end_of(base, size) == base + size - 1
assert base % size == 0 and size & (size - 1) == 0
SEL = {n: (i << 12, 0xF000) for i, n in enumerate(SLOTS)} # slot n: addr[15:12] == n
def hits(sel, addr):
return [n for n, (v, m) in sel.items() if addr >> 16 == 0x4000 and (addr & 0xFFFF) & m == v]
def select_audit(sel):
probes = [0x40000000 + (i << 12) + o for i in range(len(SLOTS)) for o in (0x000, 0x00C, 0xFFC)]
return [hex(a) for a in probes if hits(sel, a) != [SLOTS[(a >> 12) & 0xF]]]
def apb_write(sel, regs, addr, data):
for n in hits(sel, addr):
regs[n][addr & 0xFF] = data
assert select_audit(SEL) == []
BUG = dict(SEL, DMA=(0x0000, 0xE000)) # 8 KB window: addr[15:13] == 0
assert select_audit(BUG) == ["0x40001000", "0x4000100c", "0x40001ffc"]
for sel, expect in ((SEL, None), (BUG, 1)):
regs = {n: {} for n in SLOTS}
apb_write(sel, regs, 0x4000100C, 1) # SBR CTRL, START = 1
assert regs["DMA"].get(0x0C) == expect # DMA KEY: written only by the bug
buggy = [("DMA", 0x40000000, 0x2000) if n == "DMA" else (n, b, s) for n, b, s in MAP]
assert overlaps(buggy) == [("DMA", "SBR")]
print("PASS: map clean: 10 rows, no overlap, all aligned; 8 KB DMA window caught and its stray KEY write shown")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 ----
assert decode(0x00000000) == "BOOTROM" and decode(0x2000FFFF) == "SRAM0"
assert decode(0x20010000) == "SRAM1" and decode(0x40006000) == "RSTPWR"
assert decode(0x00004000) == "H1" and decode(0x20020000) == "H2"
assert decode(0x40007000) == "H3" and decode(0x40010000) == "H4"
assert response(0x00004000, True) == "DECERR" and response(0x40007000, True) == "SLVERR"
assert response(0x40010000, True) == "DECERR"
assert response(0x20010000, False) == "SLVERR" and response(0x20010000, True) == "OK"
REG = {0x00: "CTRL", 0x04: "STATUS", 0x08: "LEN", 0x0C: "KEY", 0x10: "COUNT"}
def dma_read(bus_addr, lo, hi):
# the bus address has 12 bits; the DMA port paddr has 8 and sees bits [hi:lo] of it
if hi - lo != 7:
return "WIDTH" # a width mismatch is a lint error, never a silent truncation
return REG.get(((bus_addr & 0xFFF) >> lo) & 0xFF, "PSLVERR")
def reached(lo, hi):
return [dma_read(0x40000000 + o, lo, hi) for o in sorted(REG)]
assert reached(0, 7) == ["CTRL", "STATUS", "LEN", "KEY", "COUNT"] # apb_paddr[7:0]: correct
assert reached(2, 9) == ["CTRL", "PSLVERR", "PSLVERR", "PSLVERR", "STATUS"] # apb_paddr[9:2]: bug 4
assert reached(4, 11) == ["CTRL", "CTRL", "CTRL", "CTRL", "PSLVERR"] # apb_paddr[11:4]: worse
assert reached(0, 9)[0] == "WIDTH"
assert dma_read(0x40000104, 0, 7) == "STATUS" and dma_read(0x40000114, 0, 7) == "PSLVERR" # the alias, and offsets past 0x10
print("PASS: decode and codes agree; word-address wiring reaches 1 register right, 3 take PSLVERR, COUNT returns STATUS")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 ----
FABRIC = sustained(384.0, 16)
LINKS = {"cpu_fill": 16 / (24 * T_CPU) * 1e3, # one 16-byte line per 24 clk_cpu
"fabric": FABRIC, "sram_bank": FABRIC, # per direction; one bank port
"apb_fast": 4 * 96 / 5, # DMA and SBR registers, 3-clock access plus 2 bridge clocks
"apb_slow": 4 / 208.33e-9 / 1e6} # through nano_apb_cdc: 208.33 ns per access
def stream_rate(src_bank, dst_bank):
bank = LINKS["sram_bank"] / (2 if src_bank == dst_bank else 1) # one single-port macro serves both streams
path = {"sbr_read": LINKS["fabric"], "src_bank": bank, "dma_in": LINKS["fabric"],
"dma_out": LINKS["fabric"], "dst_bank": bank, "sbr_write": LINKS["fabric"]}
low = min(path.values())
return low, sorted(n for n, v in path.items() if v - low < 0.05)
assert abs(FABRIC - 361.4) < 0.05 and abs(LINKS["cpu_fill"] - 128.0) < 0.05
assert abs(LINKS["apb_fast"] - 76.8) < 0.01 and abs(LINKS["apb_slow"] - 19.2) < 0.05
assert abs(2 * FABRIC - 722.8) < 0.05
assert abs((64 + 8) * T_SYS - 750.0) < 0.5
rate, tied = stream_rate("SRAM0", "SRAM1")
assert abs(rate - 361.4) < 0.05 and len(tied) == 6 # six links tie: the 32-bit 96 MHz stream is the ceiling
rate, tied = stream_rate("SRAM0", "SRAM0")
assert abs(rate - 180.7) < 0.05 and tied == ["dst_bank", "src_bank"] # the shared bank port is the bottleneck
def placement_ok(src, dst):
return stream_rate(src, dst)[0] > 0.99 * FABRIC
assert placement_ok("SRAM0", "SRAM1") and not placement_ok("SRAM1", "SRAM1")
assert min(("apb_slow", LINKS["apb_slow"]), ("apb_fast", LINKS["apb_fast"]), key=lambda x: x[1])[0] == "apb_slow"
print("PASS: links agree: 361.4 per stream, 180.7 same-bank (bank port is the bottleneck), 64-word job 750 ns")The final PASS establishes consistency of the Python model and manifest. Record the known SBR attribute weakness and all implementation evidence still required in the readiness statement. The model reproduces an open partition in v1 and demonstrates a proposed secure-write guard; a model PASS alone does not close that finding.