An integrator who cannot state the requirement set cannot tell a bug from a feature. NanoSoC v1 arrives as a pile of blocks, a CPU, a crossbar, two SRAMs, a ROM, a DMA with its stream bridge, an APB tree with five slow peripherals, and the reset, clock and power controller, and your first artefact is the one-page brief that pins every number the later stages check against.
Goal: write down the v1 requirement set so that every later checker has an oracle.
Inputs: the block list, the map, the clocks, the reset order, the power states, the interrupt map, the boot total of 12.581 ms.
Steps: copy the requirement table below into your notes; confirm the boot shares and the area and power totals with the checker; tick off the deliverables table as each stage lands.
NanoSoC v1 is one generic 32-bit in-order core with a 4 KB instruction cache, a 2-master by 4-slave AXI4 fabric (M0 the CPU through its async bridge, M1 the stream bridge), 64 KB SRAM0 retained in the always-on domain plus 64 KB secure-only SRAM1, a 16 KB boot ROM at zero, the streaming DMA fed by its stream bridge, and UART, SPI, GPIO, interrupt controller and reset controller on a 24 MHz peripheral segment. Clocks are 192, 96 and 24 MHz from a 768 MHz PLL output (24 MHz times 32); resets release fabric before CPU; power is one always-on domain plus one switchable domain with 114 isolation clamps; interrupts are 8 level inputs, lower number wins; boot takes 12.581 ms, of which the SPI image load is 86.9 percent and the UART banner 11.0 percent.
Requirement | Pass criterion | Checked in |
|---|---|---|
Clock chain | 24 x 32 = 768 MHz; /4 = 192 and /8 = 96 exactly | Stage 2 clock plan |
Hardware reset sequence | 53.948 us, CPU released last | Stage 2 release order |
Boot total | 12,580.917 us (12.581 ms) | Stage 3 boot flow |
Wake from SLEEP | 14,072.9 ns | Stage 2 power-up |
Isolation clamps | 47 + 52 + 13 + 2 = 114, every signal clamped | Stage 2 power |
Block area / core / budget | 0.585 / 0.655 / 0.940 mm2, 69.7% | Final script, manifest area and utilisation |
RUN / IDLE / SLEEP power | 82.74 / 16.89 / 1.40 mW | Final script, manifest power |
Each stage hands in one artefact and each artefact has a checker and a done condition. Tick the rows off as the lab goes.
Stage | Artefact | Checker | Done when |
|---|---|---|---|
1 Memory map and interconnect | Map table and link table | overlap and alignment check, slot-select audit, response codes, link rates | 10 rows, 0 overlaps, 361.4 and 180.7 MB/s |
2 Clocks, resets, power | Clock plan, crossing list, reset plan, power plan | crossing audit, release times, clamp audit, watchdog model | 10 crossings, CPU released last at 53.948 us, 114 clamps |
3 Interrupts and boot | Interrupt map, DMA driver sequence, boot record | connectivity test, driver and contract check, boot sum | inputs 0 to 5 wired, 12.581 ms |
4 Verification and signoff | Test matrix, signed manifest, waiver list | matrix, manifest comparison, waiver ageing, final script | the PASS line, plus a written list of open items |
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 ----
hw = [1024 * T_PER, 2 * T_PER, 260 * T_PER, 2 * T_PER, 18 * T_SYS, 8 * T_SYS + 2 * T_CPU]
fetch, read_cause, clear = 24 * T_CPU, 54 * T_CPU, 1024 * T_SYS
uart, banner = 3 * 54 * T_CPU, 160 / (24e6 / 208) * 1e9
selftest = 5 * 125 + 7 * 125 + 72 * T_SYS + 26 * T_CPU + 6000
spi, checksum = 8196 * 8 / 6e6 * 1e9, 2048 * 18 * T_CPU
total = (sum(hw) + fetch + read_cause + clear + uart + banner + selftest + spi + checksum) / 1000
assert abs(total - 12580.917) < 0.01, total
assert abs(spi / 1000 / total - 0.869) < 0.001 and abs(banner / 1000 / total - 0.110) < 0.001
assert abs(sum(hw) / 1000 - 53.948) < 0.01
assert abs(10.928 / (total - 0.192) - 0.869) > 0.01 # drop the checksum step and the share check fires
assert 12 + 32 + 3 + 14 + 32 + 4 + 2 + 12 + 1 + 2 == 114
sw = [0.150, 0.025, 0.015, 0.040, 0.012, 0.010, 0.006, 0.004, 0.100, 0.010]
aon = [0.100, 0.012, 0.008, 0.008, 0.006, 0.004, 0.015, 0.060]
assert abs(sum(sw) - 0.372) < 1e-9 and abs(sum(aon) - 0.213) < 1e-9
assert abs((sum(sw) + sum(aon)) * 1.12 - 0.655) < 0.001 and abs(0.655 / 0.940 - 0.697) < 0.001
assert [round(a + b, 2) for a, b in [(77.10, 5.64), (11.25, 5.64), (0.16, 1.24)]] == [82.74, 16.89, 1.40]
print("PASS: v1 totals agree: boot 12.581 ms, SPI 86.9%, reset 53.948 us, 114 clamps, 69.7% utilisation")One PASS line restating the totals. Every duration is computed from the three clock periods, so a wrong clock changes the total and the shares move with it. The line that drops the checksum step shows the share assert reacting. If any assert fires, the requirement set in your notes disagrees with the lab data: fix the notes, never the checker.
A boot-share failure means a step duration was copied wrongly, usually the banner or the SPI load. A clamp-count failure means a group was dropped, usually the 2 interrupt clamps. An area failure means the 12 percent tree, routing and fill overhead was added twice or not at all.
Hint 1: the checker adds the hardware reset and every ROM step, all computed in nanoseconds, so convert once at the end. Hint 2: the two shares divide by the same total, so check the total before either share. Hint 3: the area rows add before the 12 percent is applied, and utilisation divides last. Answer: boot 12,580.917 us, SPI share 86.9 percent, banner 11.0 percent, reset sequence 53.948 us, 114 clamps, 69.7 percent utilisation.
53.948 us against 12,580.917 us is 0.43 percent, yet a wrong release order loses the first fetch forever, so its weight is availability, not time.