A NanoSoC board came up dead: no banner, no fetch, nothing on the bus. The PLL was locked, the clocks were running, and the CPU reset had been released in the same step as the fabric reset. Its two-flop release, about 10 ns, beat the fabric's 187.5 ns, so the first fetch entered the clock-domain bridge while the interconnect was still in reset and was lost: NanoSoC bug 2, reset release order. One timeline with the order written on it would have caught the wiring at review. This lesson is that timeline.
The rule is slaves and fabric first, masters last, and the CPU last of all. From por_n release at t equals zero, the oscillator counter waits 1024 peripheral clocks for the crystal, 42.667 µs; rst_per_n releases after a 2-flop synchroniser; the PLL enables and locks over 260 clocks, 10.833 µs; the system and CPU clock gates open; rst_sys_n releases after a 16-cycle hold plus synchroniser, 18 system clocks or 187.5 ns; and rst_cpu_n releases last, 8 system clocks plus 2 CPU clocks or 93.75 ns, because its short synchroniser would otherwise outrun the fabric. The hardware total is 53.948 µs, and the script below recomputes it from the periods.
Step | Event | Waits for | Time |
|---|---|---|---|
R0 | por_n released, t = 0 | Supply above the POR threshold | 0 |
R1 | Crystal ready | Oscillator counter, 1024 clk_per | 42.667 µs |
R2 | rst_per_n released | Crystal plus 2-flop sync | 83.333 ns |
R3 | PLL locked | Enable start-up 4 plus lock 256 clk_per | 10.833 µs |
R4 | clk_sys and clk_cpu gates open | pll_lock, 2 clk_per | 83.333 ns |
R5 | rst_sys_n released | 16-cycle hold plus sync, 18 clk_sys | 187.5 ns |
R6 | rst_cpu_n released | 8 clk_sys plus 2 clk_cpu after R5 | 93.75 ns |
Hardware boot total, R1 to R6 | 53.948 µs |
# Cold-start hardware timeline recomputed, NanoSoC teaching values.
T_PER = 1.0 / 24e6 * 1e6 # period in us (0.0416667)
T_SYS = 1.0 / 96e6 * 1e6
T_CPU = 1.0 / 192e6 * 1e6
r1 = 1024 * T_PER
r2 = 2 * T_PER
r3 = 260 * T_PER
r4 = 2 * T_PER
r5 = 18 * T_SYS
r6 = 8 * T_SYS + 2 * T_CPU
total = r1 + r2 + r3 + r4 + r5 + r6
assert abs(T_PER - 0.0416667) < 1e-6
assert abs(r1 - 42.6667) < 0.001, r1
assert abs(r3 - 10.8333) < 0.001, r3
assert abs(r5 - 0.1875) < 1e-4, r5
assert abs(r6 - 0.09375) < 1e-4, r6
assert abs(total - 53.9479) < 0.01, total
assert r6 < r5 # the CPU wait is the shortest, which is why order must be enforced
print("PASS: R1 42.667 us, R3 10.833 us, R5 187.5 ns, R6 93.75 ns, total 53.948 us")The release order is drawn as fabric before CPU with the R5 to R6 gap dimensioned, plus an assertion that rst_cpu_n never rises before rst_sys_n.
Every reset is asynchronous-assert, synchronous-release in its own domain with its own two-flop synchroniser.
The minimum assertion width of 16 domain clocks is stated for every software and power reset, not just POR.
Simulate the release with the lock and crystal models at their slowest corners and check the first fetch completes: RVALID must answer the CPU's first ARVALID. Bisect a dead board in timeline order with a scope, crystal, POR, lock, per-domain resets, then the first fetch, stopping at the first link that fails. If the first fetch hangs with all resets released, the order inverted somewhere; probe rst_cpu_n against rst_sys_n and confirm the 8-clock gap.
R1: the oscillator counter counts an unstable crystal and xtal_ok arrives early or never, so every later wait is built on sand. POR must be qualified on the supply threshold with margin; a POR that releases on a ramp is a board bug, not a timing tweak.