The crystal wait and the PLL lock together cost 53.5 µs of the 53.948 µs hardware boot: 99 percent of the timeline is waiting for analogue physics with a digital counter. Get either wait wrong and the chip releases resets into garbage clocks; make them needlessly long and every cold boot pays for it. The crystal wait is a cold-boot cost only: a wake from SLEEP does not wait for the crystal again, because the crystal never stopped, and pays only the 256-clock PLL relock, 10.667 µs. This lesson prices both waits and the failure that follows a skipped one.
The 24 MHz crystal runs while the supply is on and bypasses the PLL, so the peripheral domain, the reset controller and the wake logic breathe before anything else. The 1024-cycle oscillator counter, 42.667 µs, covers the crystal start-up; the PLL then multiplies 24 MHz by 32 to 768 MHz, inside its 600 to 1200 MHz teaching range, and divides by 4 and 8 to the 192 and 96 MHz domains. The 4-cycle start-up plus 256-cycle lock counter costs 10.833 µs, and clocks and resets wait on the lock signal, never on a hope. If the PLL never locks on a wake, the sequencer's relock timeout fires at 1024 peripheral clocks (four times the 256-clock nominal, 42.667 µs) and the machine stops in a safe state instead of waiting for ever. On a cold boot the same wait belongs to the reset controller, which simply keeps the resets asserted until lock is high. A dead crystal is different: it produces no clock at all, so no counter on the chip can time it out, because the sequencer and its counters run on that crystal. Catch it from outside: an external supervisor or a watchdog on a separate clock, or the board-level check that por_n is released but rst_per_n never is.
# Crystal and PLL arithmetic, NanoSoC teaching values (NanoSoC clock table).
XTAL = 24e6
N, R = 32, 1
VCO = XTAL * N / R
assert VCO == 768e6
assert 600e6 <= VCO <= 1200e6 # teaching VCO range
clk_cpu, clk_sys, clk_per = VCO / 4, VCO / 8, XTAL
assert (clk_cpu, clk_sys, clk_per) == (192e6, 96e6, 24e6)
assert clk_cpu / clk_per == 8.0 and clk_sys / clk_per == 4.0
osc_us = 1024 / XTAL * 1e6
lock_us = 260 / XTAL * 1e6
assert abs(osc_us - 42.6667) < 0.001, osc_us
assert abs(lock_us - 10.8333) < 0.001, lock_us
assert abs((osc_us + lock_us) / 53.9479 - 0.992) < 0.005
print("PASS: VCO 768 MHz; cpu 192, sys 96, per 24 MHz; osc 42.667 us, lock 10.833 us")The lock wait is qualified on the lock signal with the counter as a minimum, never the counter alone.
The peripheral clock's PLL bypass is drawn: wake and reset logic provably run with the PLL off.
The PLL-never-locks path is specified: timeout value and safe landing state; and the dead-crystal case is assigned to something that does not run on that crystal, an external supervisor or a separate-clock watchdog.
Test lock three ways: nominal lock at 256 cycles, slow lock near the counter limit, and no lock at all. The first two must release resets; the third must time out with the interrupt set. On a board that never locks, check the reference first: an unloaded crystal pin or a wrong load capacitor fails identically to a dead PLL. Never shorten the lock counter to fix a slow board; fix the board.
Gate the release on lock high with the counter as a minimum wait, and add the timeout path that reports instead of releasing. Releasing on the counter alone turns every slow lock into a random start-up failure.