Research note · 2026-10-01
nRF54L15 PAwR Root Cause: the 32 kHz Clock
Date: 2026-10-01 (overnight bench session plus follow-up the same day)
This note closes the May investigation. Run IDs refer to the round-two log directory
logs/r2/2026-10-01_nrf54_tag/ and, for the re-analysis, the May logs/correlated_pawr/ runs.
Result
The Makerdiary nRF54L15 Connect Kit’s 32.768 kHz crystal (LFXO), with the board file’s internal
capacitor setting of 15.5 pF, runs +174.17 ppm fast relative to the board’s 32 MHz crystal. Firmware declares the default 50 ppm
(CONFIG_CLOCK_CONTROL_NRF_K32SRC_50PPM). PAwR and periodic-sync receive windows are sized from
the declared accuracy of both ends, so the receiver gets the first event after sync, found with a
wide window, and misses every later one: recv_cb(buf == NULL), then sync loss. A second
Connect Kit unit, the May AP, measures +167 to +182 ppm from the air. Declaring a conservative
accuracy (CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y) or using the RC oscillator fixes it.
Revised later on 1 October after a review: frequencies are stated relative to the reference actually used, the LFRC reading is no longer used as evidence about the reference, the 18 pF point is measured instead of extrapolated, and the capacitor value is described as CINT, not CL.
Bench
| Role | Board | Firmware |
|---|---|---|
| AP / gateway | nRF5340 Audio DK | ESL gateway (Thingy:91 X nRF5340 configuration, host link via the DK’s USB) |
| Device under test | nRF54L15 Connect Kit (round-two unit) | per run |
| Control | Pro Micro nRF52840 | same probe source as the DUT, later the ESL tag |
PAwR profile: interval 1408 × 1.25 ms = 1.76 s, one subevent, 2M PHY.
Runs, one variable each
| Run | Change | nRF54L15 result |
|---|---|---|
| R2 | production ESL tag service, chip-erased | 1 good event, then RX_FAILED on every event (1 of 22) |
| E5 | AP holds the connection 15 s after PAST | fails at the second event with the link still up: the disconnect is not the trigger |
| E4c | passive probe, no connection, no PAST | exactly 1 good subevent per sync: 15 syncs, 15 good, 60 NULL in 12 min |
| E7 | same probe source on the nRF52840, same AP, same time | 137 of 137 |
| T1 | nRF54L15 low-frequency clock from the RC oscillator | 157 of 157 |
| T1b | crystal kept, accuracy declared 500 ppm | 181 of 182 |
| T1c | crystal load 17.0 pF instead of 15.5, 50 ppm | 179 of 183, 1 sync loss in 6 min (marginal) |
A void run is worth recording too. A 1M-PHY variant (E6) looked like it broke onboarding, but the gateway’s persistent tag registry hadn’t been cleared after the tag was erased, so the drops were a procedure error. That run doesn’t count.
Kconfig trap found on the way. The SoftDevice Controller only includes PAwR receive support
(CONFIG_BT_CTLR_SDC_PAWR_SYNC) when both PAST sender and receiver are enabled, even with
CONFIG_BT_PER_ADV_SYNC_RSP=y. Otherwise Kconfig prints a warning, the sync is plain periodic
(num_subevents=0), and bt_le_per_adv_sync_subevent() returns -EIO.
The clock, measured on the chip
A small meter: the kernel clock (GRTC, kept by the LF clock through sleep) timed against a TIMER on the 16 MHz clock with the 32 MHz crystal running, 30-second windows, CPU asleep.
| Setting | Error | Spread over 10 windows |
|---|---|---|
| LFXO 4.0 pF | +775.21 ppm | ±0.03 ppm |
| LFXO 10.0 pF | +357.27 ppm | ±0.01 ppm |
| LFXO 15.5 pF (board file) | +174.17 ppm | ±0.03 ppm |
| LFXO 17.0 pF | +148.70 ppm | ±0.03 ppm |
| LFXO 18.0 pF (maximum) | +126.03 ppm | ±0.02 ppm |
| LFRC (sanity check only, see below) | about −6 ppm | ±50 ppm (calibration steps) |
All values are relative to the board’s 32 MHz crystal (HFXO), which hasn’t been calibrated; the board maker doesn’t publish its part or tolerance. The reading follows the load setting while the HFXO stays the same, so the measured interval is paced by the 32 kHz crystal. Even the 18 pF maximum leaves +126 ppm, so tuning can’t bring this crystal to 50 ppm.
The LFRC row only checks the measurement path. The LFRC is calibrated against the HFXO, so it reads near zero relative to the HFXO by construction and says nothing about the HFXO itself.
The devicetree value is the SoC’s internal capacitor setting (CINT), not the crystal’s rated load (CL). Nordic derives CINT from CL plus PCB and pin stray capacitance; 15.5 pF suits a crystal of roughly 9 pF CL. That even 18 pF stays at +126 ppm suggests the fitted crystal needs more load than the internal capacitors provide. The part number isn’t known, so this is not verified.
Host-side timing (device uptime against serial arrival times) could not measure this: the debugger’s USB-UART bridge adds up to 0.5 s of latency jitter.
The May AP, re-timed from the May captures
Periodic advertising has no random delay, so its packets sit on an exact grid. The raccoon sniffer stamps each packet with a 1 MHz timer on its own crystal. Rebuilding the grid index from the gaps and fitting time against it gives the transmitter’s period in that radio’s time.
| May run | Sniffer radio | Packets | Span | AP clock |
|---|---|---|---|---|
| 115809Z | 0 | 126 | 7.5 s | +167.0 ppm |
| 131239Z | 0 | 177 | 10.1 s | +170.2 ppm |
| 145757Z | 1 | 118 | 8.4 s | +182.4 ppm |
| 150808Z | 0 | 169 | 10.1 s | +167.2 ppm |
| 162544Z | 2 | 93 | 7.3 s | +178.8 ppm |
| 163537Z | 2 | 243 | 19.6 s | +176.6 ppm |
| 165004Z | 1 | 195 | 18.7 s | +174.9 ppm |
| 172925Z (direct AA) | 0 | 292 | 22.9 s | +169.4 ppm |
| 172925Z | 0 | 213 | 18.3 s | +170.3 ppm |
| 181151Z (direct AA) | 0 | 150 | 8.3 s | +179.8 ppm |
| 181151Z | 1 | 233 | 19.4 s | +176.1 ppm |
| 184712Z | 1 | 248 | 20.2 s | +174.9 ppm |
Excluded: 160433Z (56 packets, 250 µs rms residual) and 155517Z (10 packets in 0.2 s). Residuals are 28–145 µs rms against 1.2–4 ms of accumulated drift per run. The values are relative to each sniffer radio’s own crystal, which isn’t calibrated either; three independent radios agreeing within about ±8 ppm make a shared 170 ppm error on their side very unlikely. PAwR event timing is scheduled from the AP’s 32 kHz clock, so this measures that clock, not the AP’s 32 MHz crystal.
Why the pairing matters
Every board on the bench, each relative to its own 32 MHz crystal (uncalibrated, so comparisons between boards carry both crystals’ unknown errors): Audio DK +39.1 ppm, Pro Micro nRF52840 +59.6 ppm, Connect Kit +174.2 ppm. A receiver must absorb the relative drift:
| AP → receiver | Relative drift | Result |
|---|---|---|
| DK → Pro Micro | ≈ 21 ppm | works |
| DK → nRF54L15 on RC | ≈ 40 ppm | works |
| DK → nRF54L15 at 17 pF | ≈ 110 ppm | marginal |
| nRF54L15 → Pro Micro | ≈ 114 ppm | works (17 of 18 answered) |
| DK → nRF54L15 at 15.5 pF | ≈ 135 ppm | fails |
| nRF54L15 at 10 pF (+357) → Pro Micro, AP declares 50 ppm | ≈ 297 ppm | never syncs, 0 of 10 |
| same, AP declares 500 ppm | ≈ 297 ppm | 10 of 10 |
With 50 + 50 ppm declared the break falls between about 110 and 135 ppm. The last two rows show the AP’s declared accuracy reaching the tag (through SyncInfo/PAST) and protecting it.
A drift that is constant from event to event, against a fixed window margin, gives a step rather than a gradual decline. That fits the abrupt 302/303 interval edge from May. It has not been fitted quantitatively, and the May tag’s own clock was not measured. May’s subevent 0/1 split and the five-subevent rescue at interval 1408 are not explained by this note.
The fix in the product
esl_tag_node (the production ESL tag service on the nRF54L15) with one added line,
CONFIG_CLOCK_CONTROL_NRF_K32SRC_500PPM=y:
- 18 of 18 status requests answered in 2.01–3.51 s (the nRF52840 tag on the same gateway: 17 of 18).
- Soak, 10.3 h: one sync throughout, 21,006 events received, 97 failed (0.46 %), 9 missed, 0 sync losses, 246 sensor reads served.
The access-point configuration for the Connect Kit carries the same line. Choosing between 500 ppm, 250 ppm and the RC oscillator is a power question, measured next.