Goal
Make AdjustBeaconTiming / AdjustBeaconTimingFine work on Jaguar2 (specifically the 8821CE, our only PCIe part) by re-downloading the reserved-page beacon after each TBTT re-latch. This unblocks the AP-beacon → network-PTP discipline loop on hardware that exists today, instead of waiting for J3-PCIe (#214).
Background — why J2 refuses today
TBTT steering works by a re-latch: toggle EN_BCN_FUNCTION off → shift the port-0 TSF by the desired µs → toggle it back on so the TBTT re-derives from the shifted TSF (J3). On J2 this is bench-proven fatal (8812BU, docs/time-distribution.md):
- Any TBTT re-latch — the 1-TU interval tweak or the beacon-function toggle — makes the J2 beacon engine lose the bcn-valid latch and stop airing, and the hardware does not retain the reserved-page beacon bytes to auto-re-download.
So AdjustBeaconTiming* deliberately return 0 on J2 rather than silently kill the downlink. There's no re-latch-free lever either: WriteTsf moves the reported TSF but not the TBTT (a separate per-port timer drives the beacon, deaf to REG_TSFTR).
Proposed backport
The hardware loses the beacon bytes, but the driver still has them (handed to StartBeacon). So the J2 actuator becomes:
steer = toggle off → shift TSF → toggle back on → re-run the reserved-page beacon download (re-arm the valid latch)
i.e. "steer, then re-StartBeacon." The re-download re-derives the TBTT from the shifted TSF, so the correction takes effect. Gate it behind the existing generation dispatch so J3 keeps its clean path.
Beacon-gap budget (why the cost is acceptable)
The re-download takes a few ms and skips a beacon or two per correction. Cost is a pure function of correction cadence, and for ms-slot coordination that's low:
- Crystal drift ~+41.6 ppm (measured on the 8821CE this session) → ~4 µs per 100 ms.
- ms-slots with ~500 µs guard → correct only when accumulated drift nears the guard ≈ every ~10 s.
- → one lost beacon per ~100 — negligible for the downlink sync/control channel.
Motivation
This session measured the read/hold half already working on the 8821CE: its Wi-Fi clock disciplines to the I226 PTP reference at ~240 ns RMS, stable over minutes (mean −9 ns, thirds 267/227/228 ns). The only missing piece to close the full AP↔PTP loop on this part is the TBTT actuator — which is exactly what this backport provides. Without it, the loop is blocked on J3-PCIe hardware (#214, not yet available); with it, the existing Radxa 8821CE closes the loop now.
Validation plan
- Implement the re-download-after-re-latch path for J2; enable
AdjustBeaconTiming* on the 8821C/8821CE.
- Beacon survival: confirm the beacon keeps airing across N steers (
tests/beacon_wire_check.cpp — the seq number must keep incrementing, TSF keep stepping; a frozen seq = the degraded post-drop state).
- Steer accuracy: an observer measures the TBTT phase step per requested µs (as
tests/beacon_interval_shift.sh does on J3) — does the re-download land the TBTT where intended?
- Gap characterization: measure the actual beacon downtime per correction and the miss rate at a realistic ~10 s cadence; confirm a slave holds lock across it.
- Closed loop: run the AP↔PTP discipline loop on the 8821CE (PCIe MMIO actuator) and measure the held TBTT residual vs the ~240 ns clock-hold floor.
Risks / failure modes
Scope
Jaguar2 only (the J3 path is unchanged). Relates to the over-the-air time distribution work (docs/time-distribution.md) and the warehouse multi-AP synchronization architecture; complements #214 (J3 PCIe) as the "use the hardware we have now" path.
Goal
Make
AdjustBeaconTiming/AdjustBeaconTimingFinework on Jaguar2 (specifically the 8821CE, our only PCIe part) by re-downloading the reserved-page beacon after each TBTT re-latch. This unblocks the AP-beacon → network-PTP discipline loop on hardware that exists today, instead of waiting for J3-PCIe (#214).Background — why J2 refuses today
TBTT steering works by a re-latch: toggle
EN_BCN_FUNCTIONoff → shift the port-0 TSF by the desired µs → toggle it back on so the TBTT re-derives from the shifted TSF (J3). On J2 this is bench-proven fatal (8812BU,docs/time-distribution.md):So
AdjustBeaconTiming*deliberately return 0 on J2 rather than silently kill the downlink. There's no re-latch-free lever either:WriteTsfmoves the reported TSF but not the TBTT (a separate per-port timer drives the beacon, deaf toREG_TSFTR).Proposed backport
The hardware loses the beacon bytes, but the driver still has them (handed to
StartBeacon). So the J2 actuator becomes:i.e. "steer, then re-
StartBeacon." The re-download re-derives the TBTT from the shifted TSF, so the correction takes effect. Gate it behind the existing generation dispatch so J3 keeps its clean path.Beacon-gap budget (why the cost is acceptable)
The re-download takes a few ms and skips a beacon or two per correction. Cost is a pure function of correction cadence, and for ms-slot coordination that's low:
Motivation
This session measured the read/hold half already working on the 8821CE: its Wi-Fi clock disciplines to the I226 PTP reference at ~240 ns RMS, stable over minutes (mean −9 ns, thirds 267/227/228 ns). The only missing piece to close the full AP↔PTP loop on this part is the TBTT actuator — which is exactly what this backport provides. Without it, the loop is blocked on J3-PCIe hardware (#214, not yet available); with it, the existing Radxa 8821CE closes the loop now.
Validation plan
AdjustBeaconTiming*on the 8821C/8821CE.tests/beacon_wire_check.cpp— the seq number must keep incrementing, TSF keep stepping; a frozen seq = the degraded post-drop state).tests/beacon_interval_shift.shdoes on J3) — does the re-download land the TBTT where intended?Risks / failure modes
Scope
Jaguar2 only (the J3 path is unchanged). Relates to the over-the-air time distribution work (
docs/time-distribution.md) and the warehouse multi-AP synchronization architecture; complements #214 (J3 PCIe) as the "use the hardware we have now" path.