fix(tx16s): execute USB/SD interrupt-path code from RAM (USB->DFU crash on some MK1 units)#7554
fix(tx16s): execute USB/SD interrupt-path code from RAM (USB->DFU crash on some MK1 units)#7554AdsumSOK wants to merge 2 commits into
Conversation
On some TX16S MK1 units, instruction fetches from the upper flash bank return corrupted data under heavy IRQ/DMA load. USB joystick or mass-storage traffic then hard-faults inside the OTG/DMA interrupt handlers (UNDEFINSTR/NOCP on provably valid instructions) and the radio reboots into DFU. This is the long-standing "radio switches to DFU when plugged into USB" issue (EdgeTX#5899, EdgeTX#7356, EdgeTX#6047, EdgeTX#6414): unit- dependent (marginal flash), version-dependent (the linker moves the hot handlers across the bank boundary as the firmware grows), and the bootloader is immune (its code sits at the start of bank 1). Fix: link the USB device stack, PCD/LL drivers, MSC storage glue and SDIO diskio into a .fastcode section placed in RAM (copied at startup, ~14KB), so the hot interrupt paths never fetch from flash. Validated on an affected TX16S: continuous mass-storage stress-reads crashed after ~1 min with the code in flash, and ran clean with the code in RAM (same flash config otherwise). Build with -DNO_LTO=ON (or drop -flto from the USB object libraries): linker file-pattern matching cannot see through LTO partitions.
Linker-script file-pattern matching (used by the .fastcode RAM section) cannot see through LTO partitions; without this the hot interrupt code silently stays in flash.
📝 WalkthroughWalkthroughAdds a RAM-executed ChangesSTM32 fastcode execution
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Reset_Handler
participant Flash
participant RAM
Reset_Handler->>Flash: Read fastcode_load source
Flash-->>Reset_Handler: Provide fastcode bytes
Reset_Handler->>RAM: Copy from sfastcode to efastcode
RAM-->>Reset_Handler: Fastcode available in RAM
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Great work on the diagnosis. I've been tracking the same crash pattern (USB -> hard fault -> DFU reboot on TX16S MK1) independently and traced it to an STM32F429 errata (PA12 noise coupling into Bank2 flash reads — documented in ES0166 §2.4.6). Full analysis and evidence in my issue thread:
Your RAM-copy approach is clever and much more mergeable than my Bank1 Combining your RAM copy (for ISRs) with a freeze gate for the main loop |
|
Update after further testing on the affected unit — and thanks @ulph0 @ulph0 — thank you, this is an excellent independent confirmation. Your errata reference (ES0206, PA12/USB-DP noise coupling into Bank2 flash reads, fixed in silicon rev 3) supplies the physical mechanism behind everything we measured on our side: valid Bank2 instructions raising UNDEFINSTR/NOCP/precise bus faults only while USB is active, on an early MK1 only. Two independent investigations, same conclusion. Your warning about coverage was correct, and we can now back it with data. We extended the
The PR is left at the validated state (USB/SDIO interrupt paths in RAM: 24 min continuous MSC stress clean vs ~55 s crash baseline on the same unit). It is a real improvement for mass storage, but it is partial. Proposed plan for a deterministic fix (follow-up work):
Guidance welcome: would you rather merge the current partial-but-validated improvement now and take the deterministic layout as a follow-up PR, or hold this one until the full fix is ready? |
|
Closing this PR — further testing invalidated the approach as-is. Additional stress testing on the affected unit showed that a rebuild of the exact same source crashes in mass-storage mode within seconds: the 24-minute clean validation was obtained on a differently-laid-out build, confirming that ISR-only RAM placement leaves the outcome to per-build layout luck (as @ulph0 warned). Merging this would give affected users a false sense of fix. I'll come back with the deterministic approach outlined above (all executable code constrained to Bank1, cold assets in Bank2, plus UI freeze during USB if needed), validated across multiple intentionally-reshuffled builds on affected hardware. The diagnosis part of this PR (errata-driven Bank2 fetch corruption, crash captures) remains valid and is confirmed by two independent investigations. |
|
Please keep up the great work you are doing here. |
|
Be careful with putting things, especially interrupt handlers, into RAM.
|
|
The complete fix discussed above is now up as #7557: all code executed during USB sessions is kept in flash bank 1 by the linker (overflow = link error), UI frozen during sessions, with an MPU fence for validation builds. On the affected MK1 used for the A/B here: stock crashed at 55 s, this PR ran 35 min / 504 MB sha256-verified MSC plus active joystick sessions with the fence armed — zero faults. @ulph0 your freeze-gate + bank-1 suggestion is exactly what it implements. |
Problem
On some TX16S MK1 units, selecting USB joystick or mass-storage crashes the radio within seconds to minutes; with the cable plugged it then reboots into DFU. Long-standing reports: #5899, #7356, #6047, #6414, #3039, #4262 - always the same pattern: only some units, appeared around 2.11, 2.10.6 works, one user fixed it by swapping the main board, and the bootloader's own USB storage is immune.
Root cause
Instrumented builds (persistent fault capture across reboots on an affected unit) recorded three crashes:
popinUSB_EPStartXferstr r3, [sp, #4]inadc_start_readand.winHAL_PCD_IRQHandlerAll three faulted while executing provably valid, boundary-aligned instructions located above
0x08100000(flash bank 2). Stack overflow was ruled out by watermarking (~480B of 22KB used); illegal FreeRTOS-from-ISR calls were ruled out with runtime counters (zero hits).Conclusion: on marginal units (likely shortage-era flash), instruction fetches from the upper flash bank return corrupted data under heavy IRQ/DMA load (USB + SDIO + LCD/audio DMA). This explains every historical oddity: which units crash (flash margin varies per chip), which versions crash (the linker moves the hot handlers across the bank boundary as the firmware grows), and why the bootloader is immune (its code sits at the very start of bank 1).
Fix
Link the USB device stack (LL/PCD drivers, core, MSC class), the MSC storage glue and the SDIO diskio into a
.fastcodeoutput section placed in RAM and copied at startup (~14KB), so the hot interrupt paths never fetch from flash. The affected object libraries are kept out of LTO, since linker file-pattern matching cannot see through LTO partitions.No functional change; RAM cost ~14KB (192KB part).
Validation
A/B on an affected TX16S MK1 (2022), EdgeTX 2.12.2 base, continuous mass-storage stress reads (md5sum loop over USB from the host):
Full evidence
The complete diagnostic work is public on the fork:
bench/logs/crash-avant-c5.logdiag/usb-crash-capturebench/logs/bench.logThe three decoded crash records (2.12.2 base, TX16S MK1 2022)
🤖 Generated with Claude Code
Summary by CodeRabbit