Firmware:
- bsp_usart: RX moved from RXDNE byte-queue IRQ to DMA CH2 circular ring
(2KB) + IDLE-irq wakeup; bytes keep landing during flash-erase
interrupt-off windows (the reason lockstep was needed before)
- app_ota: stream mode (ble_protocol.md section 6.7) - 16B BEGIN with
flags bit0=STREAM (UART channel only), DATA acked on 4KB sector
crossings (original chunk length; two ack-gating bugs fixed during
field test), throttled BAD_STATE for go-back-N
- FreeRTOS heap 20KB->18KB: the 2KB ring pushed the stack top past the
0x2000C000 SRAM cliff (probed via SWD: accesses above fault on this
silicon, usable app SRAM is 32KB) which locked the board at boot;
merge_image.py now hard-fails the package when the image initial SP
leaves (0x20004000, 0x2000C000]
Tools:
- ble_ota_update.py: UART stream sender (8KB window, stall watchdog
rewind, auto-fallback to lockstep on pre-V1.00.24 firmware,
--lockstep to force); case-insensitive option parsing
- flash_package.py/bat: stream NSpyocd output live (chunked reads keep
the \r progress bar), vendor banner rebranded to CAIIC NSLINK UMP
- merge_image.py: initial-SP cliff guard
Verified: UART stream OTA both directions, 55.8KB in ~5.9s @9.5kB/s
0 rewinds (lockstep was 39s), PASS after reboot; board boot fixed and
verified via SWD.
Docs: ble_protocol.md section 6.7, dev log section 47 (+ SRAM cliff
post-mortem), AGENTS.md RAM rule rewritten (both cliffs) + V1.00.25
- UART binary mode switch mechanism: enter handshake marker '[ota] binary
mode ON', explicit exit via OTA_ABORT (immediate), 10s idle fallback
(was 3s, must exceed the host retry window); host resync by CR probe
- fix: otaIdleMs not re-armed on mode entry - every second 'ota' entry was
kicked out by an instant idle timeout (leftover count from prior session)
- lossy-link tolerance: device drops partial frames after a 100ms byte gap
so host resends re-align; OTA session is now single-owner (second BEGIN
on another channel gets BAD_STATE)
- ble_ota_update.py: wait for the enter marker instead of blind sleeps,
per-frame resend (2s x3), BAD_STATE+expected-offset treated as implicit
ack for duplicate DATA/BEGIN (resync after ack loss), stage-labeled
timeouts with raw-rx dump
- verified on hardware: UART OTA full pass (55.7KB/41s, 4-5 lost frames
auto-recovered) + BLE OTA regression pass; docs: dev log 44,
ble_protocol.md 6.6 rework, AGENTS.md sync
- app_ota.c: after END ok, wait until the RSP is consumed by the host
(BLE e0005 read-out / UART TX done) plus 300ms grace before resetting,
2s timeout fallback; polled from the BLE schedule task (rwip_schedule
must keep running for the ATT read to be processed)
- main.c: app_ota_reset_poll() in the BLE schedule task loop
- docs: ble_protocol.md 6.6 note, dev log section 43, AGENTS.md sync