STM32N6 platform support - #15214
STM32N6 platform support#15214
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis pull request adds comprehensive STM32N6 microcontroller support including OpenBootloader (OBL) integration for bootloader handoff, multiple linker variants for different boot modes, enhanced system initialization with security configuration, expanded interrupt vector coverage, platform driver updates (ADC/USB/SDIO/OctoSPI/UART), GPIO port expansion from A-I to A-O, and diagnostic CLI commands for memory inspection. ChangesSTM32N6 Enablement and OpenBootloader Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
|
Do you want to test this code? You can flash it directly from the Betaflight App:
WARNING: It may be unstable. Use only for testing! |
There was a problem hiding this comment.
Pull request overview
Adds initial STM32N6 (STM32N657) platform bring-up support, including XIP/LRUN linker variants and OpenBootloader (OBL) integration so Betaflight can boot from XSPI and provide USB VCP/DFU workflows on the N6570-DK.
Changes:
- Extends STM32 IO/generation and N6 target configuration (including BF↔OBL contract enablement).
- Adds/updates N6-specific startup, USB OTG_HS PHY + RIFSC configuration, SDMMC2 pin routing, and build/link selection logic.
- Introduces N657 XIP linker script and updates OBL DFU descriptors to support BF-slot flashing and a debug upload region.
Reviewed changes
Copilot reviewed 63 out of 66 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| Makefile | Adjusts revision detection logic used for embedding build revision. |
| src/utils/def_generated.pl | Extends GPIO port generation (A–O) and changes the output directory for generated IO defs. |
| src/main/main.c | Changes init ordering for LCD console redirect and adds a scheduler-driven LED heartbeat in run(). |
| src/platform/common/stm32/fault_handlers.c | Adds BF↔OBL hardfault debug capture to a fixed RAM address when ENABLE_BF_OBL is enabled. |
| src/platform/STM32/vcp_hal/usbd_conf_stm32n6xx.c | Fixes N6 USB IRQ vector name and adds N6 OTG_HS PHY power/reset/FSEL and RIFSC configuration. |
| src/platform/STM32/target/STM32N657/target.h | Makes USB/VCP per-board config, adds OCTOSPI/XIP-related defines, and defaults ENABLE_BF_OBL on N657. |
| src/platform/STM32/startup/stm32n6xx_hal_conf.h | Enables LTDC HAL module and includes the LTDC header. |
| src/platform/STM32/serial_uart_stm32n6xx.c | Adds additional USART1 RX/TX pin options (PE6/PE5). |
| src/platform/STM32/sdio_n6xx.c | Adds SDMMC2 pin routes for N6570-DK and configures RIFSC/GPIO security attributes to unblock SD init. |
| src/platform/STM32/mk/STM32N6.mk | Adds N6 link-mode selection (RAM_ONLY/BF_AS_FSBL/XIP/LRUN) and related build-flag/optimization tweaks. |
| src/platform/STM32/link/STM32N657XX_LRUN.ld | Updates LRUN linker script; DMA-related section handling changes. |
| src/platform/STM32/link/STM32N657XX_XIP.ld | Adds N657 XIP linker script with a NOLOAD debug exchange region and RAM-resident clock-switch code path. |
| lib/main/STM32/n6_obl/openbootloader_conf.h | Overrides OBL config to avoid CMSE-only secure alias macros and defines memory layout for OBL. |
| lib/main/STM32/n6_obl/usbd_conf.h | Overrides OBL USBD config and documents DFU alt layout for BF flashing + debug upload. |
| lib/main/STM32/n6_obl/usbd_dfu_if.c | Implements BF-slot DFU memory descriptor switching and exposes a fixed RAM region for debug uploads. |
Comments suppressed due to low confidence (2)
src/platform/STM32/link/STM32N657XX_LRUN.ld:296
- The .DMA_RW_D2 section is no longer marked NOLOAD. DMA scratch/descriptor regions are typically not loaded from FLASH (and startup code won’t initialize them), so making this a loadable section can create unexpected load/zeroing behavior and larger images. Align this with the other DMA sections by marking it NOLOAD (and only placing it in D2_RAM).
Makefile:203 - Revision detection now checks for a .git/ directory. In git worktrees/submodules, .git is commonly a file (not a directory), so this can incorrectly leave REVISION as norevision even though git metadata is available. Consider checking for .git (file or dir) or using
git rev-parse --is-inside-work-treeto decide whether to compute REVISION.
REVISION := norevision
ifneq ($(wildcard .git/),)
ifeq ($(shell git diff --shortstat),)
REVISION := $(shell git rev-parse --short=9 HEAD)
endif
endif
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/platform/STM32/startup/stm32n6xx_hal_conf.h (1)
23-37:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winConsolidate duplicated LTDC macro and include declarations.
HAL_LTDC_MODULE_ENABLEDis defined twice (lines 23 and 36) and its include block appears twice (lines 157–159 and 197–199). Remove the first occurrence at line 23 and the first include block to eliminate redundancy and reduce preprocessor-warning risk.Proposed cleanup diff
-#define HAL_LTDC_MODULE_ENABLED `#define` HAL_PCD_MODULE_ENABLED-#ifdef HAL_LTDC_MODULE_ENABLED -#include "stm32n6xx_hal_ltdc.h" -#endif /* HAL_LTDC_MODULE_ENABLED */ - `#ifdef` HAL_PCD_MODULE_ENABLED🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/startup/stm32n6xx_hal_conf.h` around lines 23 - 37, Remove the duplicate HAL_LTDC_MODULE_ENABLED definition and its first corresponding include block to avoid redundant preprocessor definitions and duplicate includes; specifically, delete the earlier occurrence of the HAL_LTDC_MODULE_ENABLED macro and the first LTDC-related include section so only the later LTDC definition and its single include block (the LTDC + RIF block) remain, ensuring references to HAL_RIF_MODULE_ENABLED and the intended LTDC include are preserved.src/platform/STM32/link/STM32N657XX_LRUN.ld (1)
286-296:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift
.DMA_RW_D2is loadable now, but it still has no flash LMA.Dropping
NOLOADhere does not make initialized.DMA_RWdata work in the LRUN image. WithoutAT > FLASHplus a matching startup copy path, the section still has no XSPI-backed load image, so any non-zero initializer placed there will come up garbage after reset. Either keep itNOLOAD, or wire it up like the other initialized RAM sections.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/link/STM32N657XX_LRUN.ld` around lines 286 - 296, The .DMA_RW_D2 section is currently loadable but lacks a flash LMA so initialized data placed into .DMA_RW will be garbage at boot; either mark the section NOLOAD to leave it uninitialized or give it an LMA and startup copy like other initialized RAM sections by changing the .DMA_RW_D2 linker stanza to include "AT > FLASH" (or mirror the existing pattern used for other initialized RAM regions) and ensure the startup code performs a copy from the flash LMA to the RAM VMA using the provided symbols (dmarw_start/dmarw_end/_sdmarw/_edmarw) so LRUN images get a valid XSPI-backed load image and initializers are preserved.
🧹 Nitpick comments (13)
src/main/fc/init.c (1)
1073-1078: ⚡ Quick winTODO acknowledged — consider raising this as a tracked issue.
Skipping
unusedPinsInit()wholesale on N6 leaves any genuinely unused but writable pin floating, which is a small EMI / current-draw risk on production hardware and a footgun if someone later writes target code that depends on the floor-clean state this function provides on other STM32 families. The right long-term fix is probably forIOTraversePins(or its callees) to consult RIFSCRISC_SECCFGR/PRIVCFGRbefore poking MODER, but that's clearly outside the scope of this PR.The
#if !defined(STM32N6)guard is fine as a short-term workaround given the PR is already marked draft. Worth tracking explicitly so it doesn't get lost.Want me to draft a small RIFSC-aware filter that
IOTraversePinscan call to skip restricted GPIO banks, and/or open a tracking issue for this TODO?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/fc/init.c` around lines 1073 - 1078, The current `#if` !defined(STM32N6) skips unusedPinsInit() on N6 which risks floating writable pins; create a tracked issue for implementing a RIFSC-aware filter (so IOTraversePins/IOConfigGPIO consult RISC_SECCFGR/PRIVCFGR and skip restricted banks) and add a concise TODO comment in init.c referencing that issue ID and explaining why the guard is temporary; include the symbols unusedPinsInit, IOTraversePins, IOConfigGPIO, RISC_SECCFGR, and PRIVCFGR in the comment so future work knows exactly what's required.src/platform/STM32/sdio_n6xx.c (2)
747-750: 💤 Low valueBoth
SDMMCx_IRQHandlerdispatch to the samehsd1— fine today, fragile later.Functionally correct given the active-controller selection in
sdioPinConfigure(), but if anyone ever enables both SDMMC1 and SDMMC2 simultaneously (or stubs in a second handle), this collapses to a single handler and the wrong instance will be polled in IRQ context. Worth a// only one of these is wired up at a time — sdioHardware->instance picks the active controllercomment so a future maintainer doesn't paste a second handle here without thinking.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/sdio_n6xx.c` around lines 747 - 750, Both SDMMC1/SDMMC2 IRQ handlers currently call HAL_SD_IRQHandler(&hsd1), which works now but is fragile if both controllers are enabled; add a brief explanatory comment in SDMMC2_IRQHandler (and SDMMC1_IRQHandler if present) noting that only one controller is wired at a time and sdioHardware->instance selects the active controller in sdioPinConfigure(), so the handler intentionally uses hsd1; if future work enables both controllers, replace the hardcoded hsd1 usage with instance-aware dispatching (e.g., choose the correct SD handle based on sdioHardware->instance) rather than copying the same handle.
197-206: ⚡ Quick winFix the misleading comment on
PRIVCFGR.The trailing comment
// NPRIV = 0reads as if there is a field calledNPRIVthat you're setting to 0. The GPIOx_PRIVCFGR field isPRIV— clearing the bit means "non-privileged access allowed". The current text inverts the polarity of what you're actually writing.📝 Suggested wording
- port->SECCFGR |= mask; // SEC = 1 - port->PRIVCFGR &= ~mask; // NPRIV = 0 + port->SECCFGR |= mask; // PRIV pin marked secure + port->PRIVCFGR &= ~mask; // PRIV = 0 → non-privileged access allowed🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/sdio_n6xx.c` around lines 197 - 206, In sdioRifMarkPin, the trailing comment on the PRIVCFGR write is misleading; instead of saying “NPRIV = 0” update the comment for port->PRIVCFGR &= ~mask to correctly reflect that clearing the PRIV bit allows non-privileged access (e.g. “PRIV = 0 → non-privileged access allowed”); keep references to IO_GPIO, IO_Pin, SECCFGR and PRIVCFGR so the change is localized to the comment in sdioRifMarkPin.src/platform/STM32/system_stm32n6xx.c (3)
161-167: 💤 Low valueNVIC ICER/ICPR loop count consistency with
startup_stm32n657xx.s.The C loop here correctly uses
sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]), but the corresponding assembly loop instartup_stm32n657xx.s(lines 67 and 74) hardcodesr3 =#16``. Cortex-M55 with up to 480 IRQs needs 15 registers; 16 is fine and harmless (writes to reserved tail are ignored), but the two loops are now visually inconsistent on a "what's the iteration count" question. Either drop the assembly down to a CMSIS-derived constant (via.equfrom a generated header) or accept the duplication with a one-line comment in both spots cross-referencing each other.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/system_stm32n6xx.c` around lines 161 - 167, The NVIC register-clear loops in system_stm32n6xx.c (the for loop using sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0])) and the hardcoded loop count in startup_stm32n657xx.s are inconsistent; fix by either (A) making the assembly use a shared CMSIS-derived constant (add a .equ in the generated header and reference it in startup_stm32n657xx.s) or (B) add a one-line comment in both places cross-referencing each other and explaining why the assembly uses 16 iterations (writes to reserved tail are ignored) so the visual discrepancy is documented; update the comment in system_stm32n6xx.c near the NVIC->ICER/NVIC->ICPR loop or modify startup_stm32n657xx.s to reference the shared constant accordingly.
308-432: 💤 Low valueEvery HAL failure path is an unrecoverable
while (1);— at least leave a fingerprint.There are seven
while (1);spins insideSystemClock_Config()(lines 321, 324, 337, 350, 368, 397, 421), each on a distinct HAL call. The PR design is "let the OBL/IWDG path recover" (and the comment insystemResetToBootloaderat 452-462 confirms this is intentional), but on a cold boot before IWDG is armed there's nothing differentiating which spin you ended up in.For bring-up reliability, write a marker into the
.bf_obl_dbgdebug buffer before each spin so the OBL@DBGRAMDFU alt can tell the host which HAL step failed:♻️ Sketch (optional)
+#define BF_OBL_FAULT(code) do { *(volatile uint32_t *)0x24100130UL = (code); __DSB(); } while (0) ... - if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY) != HAL_OK) { - while (1); - } + if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY) != HAL_OK) { + BF_OBL_FAULT(0xDEAD0001); + while (1); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/system_stm32n6xx.c` around lines 308 - 432, SystemClock_Config contains seven unrecoverable spins; before each while (1); write a distinct marker into the .bf_obl_dbg debug buffer so the OBL DFU alt can identify which HAL call failed. Concretely: in SystemClock_Config, immediately before the failure branches that follow HAL_PWREx_ConfigSupply, HAL_PWREx_ControlVoltageScaling, HAL_RCC_OscConfig (first HSI block), HAL_RCC_ClockConfig (HSI clock switch), HAL_RCC_OscConfig (HSE/PLL bring-up), HAL_RCC_ClockConfig (PLL distribution), and HAL_RCCEx_PeriphCLKConfig, store a unique byte/word identifier into the .bf_obl_dbg area (use distinct values per location) and ensure the write is non-blocking/simple memory write so it will persist through the spin; keep the existing while (1); spins unchanged otherwise. Use the .bf_obl_dbg symbol and SystemClock_Config/HAL_* function names to find the spots and pick unique markers for each failure path.
121-142: ⚡ Quick winMagic addresses for secure-alias
RCC/DBGMCU— promote to named defines.The literals
0x56028A48,0x56028248,0x56028A44,0x56028244,0x54001004(and bit positions(1<<0)..(1<<20)forDBGMCU.CR) are repeated verbatim at lines 134-142 and again at lines 295-301 after the clock-reconfig re-assert. The comments at lines 123-133 carry the only documentation for what each address/bit means. A single block of#defines at the top of the file (or inplatform.h) eliminates the duplication, the typo risk, and makes the comment-as-documentation actually link to symbols that will outlive future re-reads.♻️ Sketch
+// Secure-alias register accesses (RIFSC-gated; HAL/CMSIS macros emit NS aliases +// that get silently dropped in the no-mcmse build). See OBL's system_stm32n6xx_obl.c +// for the matching writes. +#define RCC_S_MISCENSR (*(volatile uint32_t *)0x56028A48UL) +#define RCC_S_MISCENR (*(volatile uint32_t *)0x56028248UL) +#define RCC_S_BUSENSR (*(volatile uint32_t *)0x56028A44UL) +#define RCC_S_BUSENR (*(volatile uint32_t *)0x56028244UL) +#define DBGMCU_S_CR (*(volatile uint32_t *)0x54001004UL) +#define DBGMCU_CR_DBGCLKEN (1UL << 20) +#define DBGMCU_CR_DBG_SLEEP (1UL << 0) +#define DBGMCU_CR_DBG_STOP (1UL << 1) +#define DBGMCU_CR_DBG_STBY (1UL << 2) +#define RCC_BUSENSR_APB3ENS (1UL << 10)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/system_stm32n6xx.c` around lines 121 - 142, The magic secure-alias addresses and DBGMCU.CR bit positions used in systemInit (and later re-assert) are hard-coded and duplicated; create clear named constants (e.g., RCC_S_MISCENSR_SECURE_ALIAS, RCC_S_MISCENR_NS_ALIAS, RCC_S_BUSENSR_SECURE_ALIAS, RCC_S_BUSENR_NS_ALIAS, DBGMCU_S_CR_SECURE_ALIAS and DBGMCU_CR_DBGCLKEN/DBG_SLEEP/DBG_STOP/DBG_STANDBY bit masks) at the top of this file or in the platform header, replace the literal constants and (1<<N) expressions in systemInit and the re-assert block with those defines, and update the existing comment to reference the new symbols so the meanings aren’t lost and duplication/typo risk is removed (use the existing systemInit and re-assert code locations to find all occurrences).src/platform/STM32/link/STM32N657XX_RAM.ld (2)
223-233: ⚡ Quick win
.DMA_RW_D2missingNOLOAD— inconsistent with XIP/FSBL variants.
STM32N657XX_XIP.ld(line 260) andSTM32N657XX_FSBL_FULL.ld(line 228) both mark this section(NOLOAD); this one does not. Practically benign for an empty section, but gdb will zero-fill the D2_RAM region onloadwhen this script is used, and any future input section landing in.DMA_RWwould gain unexpected initializer bytes embedded in the .elf.♻️ Proposed fix
- .DMA_RW_D2 : + .DMA_RW_D2 (NOLOAD) : { . = ALIGN(32); PROVIDE(dmarw_start = .);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/link/STM32N657XX_RAM.ld` around lines 223 - 233, The .DMA_RW_D2 output section should be marked NOLOAD to match the XIP/FSBL variants and prevent gdb from zero-filling D2_RAM or embedding unexpected initializers; update the linker script section definition for ".DMA_RW_D2" (the block that sets PROVIDE(dmarw_start), _sdmarw, _dmarw_start__, KEEP(*(.DMA_RW)), PROVIDE(dmarw_end), _edmarw, _dmarw_end__) to include the NOLOAD attribute while keeping the existing alignment and >D2_RAM placement.
40-46: 💤 Low value
__config_startaliased toeepromData— couple the 4096 size to the array.
__config_end - __config_start = 4096is hardcoded here while the actualeepromData[]size lives in C. If anyone tweaksEEPROM_SIZEwithout updating this script,isEEPROMStructureValid()walks past the array into adjacent .bss /.sram2. A_Static_assert(sizeof(eepromData) == EEPROM_SIZE, ...)near the array definition catches the drift at compile time, and there's a per-PR known caveat already noted in the PR objectives that CONFIG_IN_RAM saves aren't persistent acrossNVIC_SystemReset().🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/link/STM32N657XX_RAM.ld` around lines 40 - 46, The linker hardcodes 4096 for __config_end which can drift from the actual eepromData[] size; instead expose the array size from C and use that symbol in the linker. Add a C definition like `const unsigned long __eeprom_size = EEPROM_SIZE;` (or use sizeof(eepromData) with a _Static_assert) and then change the linker expressions for __config_start / __config_end to use ABSOLUTE(eepromData) and ABSOLUTE(eepromData) + __eeprom_size (referencing symbols __config_start, __config_end, eepromData, and __eeprom_size) so the linker-bound range always matches the real array size.src/platform/STM32/link/STM32N657XX_FSBL_FULL.ld (1)
26-58: 💤 Low value
LOAD_RAMlength is a magic 521728 — explain or derive.Line 33:
LENGTH = 521728. This is512K - 0x400(the boot-ROM header offset), but a reader has to do the arithmetic. Using512K - 0x400or a small_FSBL_HEADER_SIZE = 0x400;symbol makes the intent self-documenting and protects against silent breakage if anyone changesORIGIN.♻️ Proposed fix
- /* Loaded image — boot ROM lands the .stm32 payload here. AXISRAM2 - * runs from 0x34100000-0x341FFFFF; the FSBL slot starts at the 0x80400 - * boot-ROM offset, leaving 521 728 bytes (509 KiB) of usable space. */ - LOAD_RAM (rwx) : ORIGIN = 0x34180400, LENGTH = 521728 + /* Loaded image — boot ROM lands the .stm32 payload here. AXISRAM2 runs + * from 0x34100000-0x341FFFFF; the FSBL slot starts at the 0x80400 + * boot-ROM header offset, leaving 509 KiB of usable space. */ + LOAD_RAM (rwx) : ORIGIN = 0x34180400, LENGTH = (512K - 0x400)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/link/STM32N657XX_FSBL_FULL.ld` around lines 26 - 58, The LENGTH value for LOAD_RAM is a magic number (521728) representing 512K minus the FSBL header offset; replace the hardcoded 521728 with a self-documenting expression or symbol (e.g., define _FSBL_HEADER_SIZE = 0x400 or an equivalent _FSBL_SLOT_OFFSET) and compute LENGTH as 512K - _FSBL_HEADER_SIZE (or ORIGIN-dependent calculation) in the MEMORY entry for LOAD_RAM so the intent is clear and changes to offsets won’t silently break; update the comment near LOAD_RAM/ORIGIN (and any related symbols like LOAD_RAM, ORIGIN, or the new _FSBL_HEADER_SIZE) to reflect the derivation.src/platform/STM32/startup/startup_stm32n657xx.s (3)
131-141: 💤 Low valueAXISRAM-clock-enable bitmask
0x18F— same job is done again insystemInit(); the duplication is justified but worth a one-line note.The startup pre-enables AXISRAM1..6 here at the NS alias (
0x46028A4C), thensystem_stm32n6xx.c:275-280re-enables each one individually through CMSIS macros. Both writes are needed (this one so the.bsszero loop doesn't fault before any C runs; the C-side one to also touch the secure alias path for HAL bookkeeping), but a future reader will rightly ask "why twice?". Add a cross-reference:/* Enable AXISRAM1..6 clocks before any AXISRAM1+ store. The boot ROM only * clocks AXISRAM2 when it hands off to a signed FSBL; .bss, the stack and * the DMA-capable regions live in AXISRAM1 / AXISRAM3+ and the next loop * (.bss zero) faults if the RAM is unclocked. Idempotent for the FSBL-stub * handoff and SWD-load paths where AXISRAM1 is already clocked. Writes * RCC->MEMENSR via the NS alias (0x4602_8A4C); the register itself is - * shared regardless of alias. */ + * shared regardless of alias. + * + * NOTE: systemInit() in system_stm32n6xx.c re-enables these individually + * through CMSIS macros — this pre-enable is mandatory only here because + * the .bss zero loop below executes before any C code runs. */ ldr r2, =0x46028A4C ldr r3, =0x0000018F /* AXISRAM3..6 (b3:0) | AXISRAM1 (b7) | AXISRAM2 (b8) */🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/startup/startup_stm32n657xx.s` around lines 131 - 141, Add a one-line comment immediately above the "post-AXISRAM-clock-enable" write (the ldr/str sequence to 0x46028A4C setting 0x0000018F) explaining that this non-secure-side bulk AXISRAM1..6 enable is required so the .bss zeroing in early startup won't fault, and that systemInit() later re-enables each AXISRAM via CMSIS macros (see systemInit) to also touch the secure alias for HAL bookkeeping—this cross-reference clarifies why the same 0x18F mask is written twice.
58-87: 💤 Low valueNVIC clear loop hardcodes
r3 =#16`` — Cortex-M55 has 8 implementedICER/ICPRregisters; tail writes are wasted but harmless.Cortex-M55 with up to 480 IRQs uses up to 15
ICERregisters; the N6 CMSIS header (stm32n657xx.h) declaresICER[16](CMSIS standard array size), so 16 iterations is what the C-sidesizeof(NVIC->ICER)/sizeof(...)insystem_stm32n6xx.c:163resolves to. The two are consistent in count but inconsistent in how the count is expressed. For a small DX win, define it once:♻️ Sketch
+ /* Match CMSIS ICER[]/ICPR[] array size — see core_cm55.h */ + .equ NVIC_REGS, 16 cpsid i movs r0, `#0` ldr r1, =0xFFFFFFFF ldr r2, =0xE000E180 /* NVIC_ICER0 */ - movs r3, `#16` + movs r3, `#NVIC_REGS` ... ldr r2, =0xE000E280 /* NVIC_ICPR0 */ - movs r3, `#16` + movs r3, `#NVIC_REGS`Also, the comments "clear ISER[i]" (line 69) and "clear ISPR[i]" (line 76) describe the effect (Set-Enable / Set-Pending gets cleared), but the registers being written are
ICER/ICPR. Worth a one-word tweak for the next reader:- str r1, [r2] /* clear ISER[i] */ + str r1, [r2] /* write ICER[i] → clears ISER[i] enable bits */🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/startup/startup_stm32n657xx.s` around lines 58 - 87, Introduce a single named constant for the NVIC register count and use it instead of hardcoding "movs r3, `#16`", and tweak the two comments to refer to ICER/ICPR (the registers being written) rather than ISER/ISPR: add an assembly constant (e.g. NVIC_REG_COUNT = 16) and load it into r3 before the two loops that write 0xE000E180 (NVIC_ICER0) and 0xE000E280 (NVIC_ICPR0), replace the two "clear ISER[i]" / "clear ISPR[i]" comments with "clear ICER[i]" / "clear ICPR[i]" so the code uses one authoritative count symbol (used by both loops) and the comments accurately describe the registers being written.
93-122: ⚡ Quick winDebug-marker base address
0x24100000is duplicated between this file and the linker scripts — wire it through a single symbol.
STM32N657XX_XIP.ld:52declaresDBG_RAMat0x24100000(andSTM32N657XX_FSBL_FULL.ld/_RAM.ldneed to match the OBL@DBGRAMDFU alt). This startup file hardcodes the same base (and the offsets0x80,0x100,0x104,0x108,0x10C,0x110,0x120) again. IfDBG_RAM's ORIGIN ever moves (e.g. another secure/non-secure alias re-org), the linker, OBL, and this startup all drift independently.Export the base from the linker script and load via PC-relative literal:
♻️ Sketch
In
STM32N657XX_XIP.ld(and FSBL/RAM):DBG_RAM (rw) : ORIGIN = 0x24100000, LENGTH = 512 + /* exported so startup/OBL share the base */ + __dbg_ram_base = 0x24100000;In startup:
- ldr r3, =0xCABA0001 - ldr r2, =0x24100000 + ldr r2, =__dbg_ram_base + ldr r3, =0xCABA0001 str r3, [r2] - ldr r2, =0x24100080 + add r2, r2, `#0x80` str r3, [r2] ...🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/startup/startup_stm32n657xx.s` around lines 93 - 122, Replace the hardcoded debug RAM base and offsets with the linker-exported DBG_RAM symbol so the startup stays in sync with the linker scripts; in startup_stm32n657xx.s (the block that writes CABA0001 and the self-PC), load the base via a PC-relative literal referencing DBG_RAM (e.g., use LDR r2, =DBG_RAM) and then write to DBG_RAM+0x80, DBG_RAM+0x100, +0x104, +0x108, +0x10C, +0x110 and store the PC to DBG_RAM+0x120 instead of using 0x24100000 and the hardcoded offsets, ensuring the symbol name DBG_RAM is used for all addresses so any ORIGIN change in STM32N657XX_XIP.ld (and matching FSBL/RAM/OBL) stays consistent.src/platform/STM32/link/STM32N657XX_XIP.ld (1)
145-196: ⚡ Quick winContiguity requirement is implicit — add an ASSERT to fail builds early.
The copy at
Reset_Handlerwalks_stext.._etextand reads from_sitext(= LOADADDR(.ram_code)), assuming.ram_code → .data → .fastram_data → .dmaram_dataare perfectly contiguous in both VMA and LMA with no gaps. Any future re-ordering or inserted section (NOLOAD or XIP-only) silently breaks the copy and produces a corrupt image — the comment at lines 135-141 is the only safeguard.A linker
ASSERTmakes this enforcement compile-time:♻️ Proposed safety net
.dmaram_data : { . = ALIGN(32); PROVIDE(dmaram_start = .); _sdmaram = .; _dmaram_start__ = _sdmaram; _sdmaram_data = .; *(.dmaram_data) *(.dmaram_data*) . = ALIGN(32); _edmaram_data = .; _etext = .; } >RAM AT >FLASH + + ASSERT((_etext - _stext) == (LOADADDR(.dmaram_data) + SIZEOF(.dmaram_data) - LOADADDR(.ram_code)), + "XIP: VMA span (_stext.._etext) must equal LMA span; non-contiguous copy region detected")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/link/STM32N657XX_XIP.ld` around lines 145 - 196, Add a linker-time ASSERT to ensure .ram_code, .data, .fastram_data and .dmaram_data are contiguous in memory so the Reset_Handler copy from _stext.._etext using ram_code (LOADADDR(.ram_code) / _sitext) cannot be broken by future reordering; specifically assert that ram_code_end == LOADADDR(.data) (or that ram_code_start + SIZEOF(.ram_code) == LOADADDR(.data)) and that the end of .data equals LOADADDR(.fastram_data) (or _efastram_data == LOADADDR(.fastram_data)) and that the end of .fastram_data equals LOADADDR(.dmaram_data) (or _edmaram_data == LOADADDR(.dmaram_data)), using the existing symbols ram_code_start, ram_code_end, ram_code, _stext, _etext, _sfastram_data/_efastram_data and _sdmaram/_edmaram_data so the build fails if any gap exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Makefile`:
- Line 199: The conditional uses a directory-only wildcard `$(wildcard .git/)`
which fails for git worktrees where `.git` is a file; change the wildcard to
`$(wildcard .git)` in the Makefile condition (the `ifneq ($(wildcard .git/),)`
check) so the branch that computes `REVISION` runs for both regular repos and
worktrees, ensuring `REVISION` is populated instead of staying `norevision`.
In `@src/config`:
- Line 1: The PR currently pins the submodule src/config to commit
17b73b32e7d07ddf7c9b73c14c3cda7c4c879a1a while companion PR
betaflight/config#1096 is still open; do not merge this change until that
companion PR is merged to the config repo default branch, then update the
submodule pointer in src/config to the merged commit (or re-run git submodule
update --init && git add src/config after the upstream merge) so downstream
consumers and CI do not encounter a dangling submodule reference.
In `@src/main/cli/cli.c`:
- Around line 6289-6294: The code reads addr and count using strtoul without
checking if parsing consumed any digits, so invalid tokens become 0 and later
touch memory; update the parsing logic around the strtoul calls that produce
addr and count (the lines using uint32_t addr = (uint32_t)strtoul(p, &p, 16);
and the subsequent count parsing) to validate the endptr: ensure p advanced past
the input digit(s) (and optionally check errno for overflow) before using addr,
and similarly validate the second strtoul call (or the branch that sets count)
so non-numeric inputs are rejected or produce a controlled error; apply the same
validation to the analogous block around the other occurrence you noted (the
block at 6317-6324) and avoid proceeding to memory reads/writes if parsing
fails.
In `@src/platform/common/stm32/fault_handlers.c`:
- Around line 117-125: The handler assumes an 8-word exception frame; before
loading stacked PC/LR (the LDRs using [r0,`#24`] and [r0,`#20`]), detect whether the
EXC_RETURN indicates an extended FP frame (test LR bit 4) and if so add the
FP-stack size (32 bytes) to r0 so the subsequent loads point to the correct
LR/PC. Concretely, after selecting MSP/PSP into r0 (the existing "mrseq r0, msp
/ mrsne r0, psp"), insert a "tst lr, `#16`" and conditionally add 32 to r0 (e.g.
"addeq"/"addne" variant or conditional add) before the "ldr r3, [r0, `#24`]" and
"ldr r3, [r0, `#20`]" instructions so the OBL snapshot records the correct
faulting PC/LR for FP-using frames.
In `@src/platform/STM32/adc_stm32n6xx.c`:
- Around line 418-423: When skipping a device because dmaSpec is NULL, you must
also undo the earlier reservation of dmaBufferIndex slots and clear any stale
mapping in adcOperatingConfig[]. Specifically, when you hit the branch in
adc_stm32n6xx.c that checks if (!dmaSpec), in addition to setting
adc->channelBits = 0, decrement the global/local dmaBufferIndex by the number of
channels previously consumed for this dev (the same count used to advance
dmaBufferIndex at Line 398) and set adcOperatingConfig[dev].dmaIndex to an
invalid sentinel (e.g., -1 or 0xFF) so the start loop and adcGetChannelValues()
won't rely on a stale mapping.
In `@src/platform/STM32/link/STM32N657XX_FSBL_FULL.ld`:
- Around line 168-218: The .fastram_data and .dmaram_data sections are marked
(NOLOAD) so their initializer bytes are omitted and initialiseMemorySections()
(which uses LOADADDR(.fastram_data)) ends up copying garbage; change those
section declarations (referencing .fastram_data and .dmaram_data in the linker
script) to be loadable like .data by removing (NOLOAD) and giving them an LMA
via AT/LOAD region (e.g. keep VMA >FASTRAM or >RAM but set LMA to the load
region using AT >LOAD_RAM or AT (_sfastram_idata) ), preserving the ALIGN() and
existing symbols (_sfastram_idata, _sdmaram_idata, _sfastram_data,
_sdmaram_data, _efastram_data, _edmaram_data) so initialiseMemorySections() and
FAST_DATA/DMA_DATA-initialized variables get their initializer bytes copied
correctly.
In `@src/platform/STM32/link/STM32N657XX_XIP.ld`:
- Around line 94-109: The linker script currently excludes only .text from
clock-switch objects but not their read-only data, so .rodata from
system_stm32n6xx.o / stm32n6xx_hal_rcc.o / stm32n6xx_hal_rcc_ex.o can be placed
in FLASH and consumed before RAM code is set up; update the EXCLUDE_FILE(...)
usage in the .text output section (the pattern with EXCLUDE_FILE and symbols
system_stm32n6xx.o, stm32n6xx_hal_rcc.o, stm32n6xx_hal_rcc_ex.o) to also exclude
.rodata and .rodata* (or otherwise ensure those object files’ .rodata* sections
are consumed by the .ram_code pattern) so their const data is placed in RAM-safe
sections rather than FLASH/XSPI.
In `@src/platform/STM32/sdio_n6xx.c`:
- Around line 195-234: sdioRifConfigure writes SDMMC2 attributes to the wrong
RIMC_ATTRx index and clobbers the register; for SDMMC2 use RIFSC->RIMC_ATTRx[4]
(not [3]) and perform a masked read-modify-write (e.g., via MODIFY_REG or
explicit mask/OR/AND) instead of plain assignment to RIFSC->RIMC_ATTRx so you
don't overwrite unrelated fields; keep the existing bit sets to
RIFSC->RISC_SECCFGRx[1] and RIFSC->RISC_PRIVCFGRx[1] but confirm the bit
positions (21 for SDMMC1, 22 for SDMMC2) against the reference manual.
In `@src/platform/STM32/system_stm32n6xx.c`:
- Around line 169-184: The pending NVIC IRQ for USB1_OTG_HS can be latched
between the initial NVIC->ICPR clear and the AHB5 OTG1 reset; to fix, after
pulsing the OTG1 reset (the SET_BIT calls on RCC->AHB5RSTSR/AHB5RSTCR and their
readbacks) add a second NVIC pending-clear so the latched pending bit is dropped
before interrupts are re-enabled: clear NVIC->ICPR (write 0xFFFFFFFF to the
appropriate NVIC->ICPR[i] words or call NVIC_ClearPendingIRQ(OTG1_IRQn) /
NVIC_ClearPendingIRQ(USB1_OTG_HS_IRQn)) immediately after the OTG1 reset
sequence and before the __enable_irq() call.
In `@src/platform/STM32/vcp_hal/usbd_conf_stm32n6xx.c`:
- Around line 122-125: HAL_PCD_MspInit currently does an unbounded wait on the
USB 3.3V ready flag (calling HAL_PWREx_EnableVddUSBVMEN() and looping on
__HAL_PWR_GET_FLAG(PWR_FLAG_USB33RDY)), which can hang boot; change this to a
bounded wait using a timeout (e.g., capture start = HAL_GetTick(), loop until
flag set or timeout expires) and on timeout cleanly abort USB init: disable the
VDD USB enable, clear any partial PCD state, and return an error/skip further
USB setup from HAL_PCD_MspInit (so callers of HAL_PCD_Init/PCD start will see
init failure and USB is effectively disabled). Use/introduce a descriptive
timeout constant (USB33RDY_WAIT_MS) and reference HAL_PWREx_EnableVddUSBVMEN,
__HAL_PWR_GET_FLAG, PWR_FLAG_USB33RDY, and HAL_PCD_MspInit in your changes.
---
Outside diff comments:
In `@src/platform/STM32/link/STM32N657XX_LRUN.ld`:
- Around line 286-296: The .DMA_RW_D2 section is currently loadable but lacks a
flash LMA so initialized data placed into .DMA_RW will be garbage at boot;
either mark the section NOLOAD to leave it uninitialized or give it an LMA and
startup copy like other initialized RAM sections by changing the .DMA_RW_D2
linker stanza to include "AT > FLASH" (or mirror the existing pattern used for
other initialized RAM regions) and ensure the startup code performs a copy from
the flash LMA to the RAM VMA using the provided symbols
(dmarw_start/dmarw_end/_sdmarw/_edmarw) so LRUN images get a valid XSPI-backed
load image and initializers are preserved.
In `@src/platform/STM32/startup/stm32n6xx_hal_conf.h`:
- Around line 23-37: Remove the duplicate HAL_LTDC_MODULE_ENABLED definition and
its first corresponding include block to avoid redundant preprocessor
definitions and duplicate includes; specifically, delete the earlier occurrence
of the HAL_LTDC_MODULE_ENABLED macro and the first LTDC-related include section
so only the later LTDC definition and its single include block (the LTDC + RIF
block) remain, ensuring references to HAL_RIF_MODULE_ENABLED and the intended
LTDC include are preserved.
---
Nitpick comments:
In `@src/main/fc/init.c`:
- Around line 1073-1078: The current `#if` !defined(STM32N6) skips
unusedPinsInit() on N6 which risks floating writable pins; create a tracked
issue for implementing a RIFSC-aware filter (so IOTraversePins/IOConfigGPIO
consult RISC_SECCFGR/PRIVCFGR and skip restricted banks) and add a concise TODO
comment in init.c referencing that issue ID and explaining why the guard is
temporary; include the symbols unusedPinsInit, IOTraversePins, IOConfigGPIO,
RISC_SECCFGR, and PRIVCFGR in the comment so future work knows exactly what's
required.
In `@src/platform/STM32/link/STM32N657XX_FSBL_FULL.ld`:
- Around line 26-58: The LENGTH value for LOAD_RAM is a magic number (521728)
representing 512K minus the FSBL header offset; replace the hardcoded 521728
with a self-documenting expression or symbol (e.g., define _FSBL_HEADER_SIZE =
0x400 or an equivalent _FSBL_SLOT_OFFSET) and compute LENGTH as 512K -
_FSBL_HEADER_SIZE (or ORIGIN-dependent calculation) in the MEMORY entry for
LOAD_RAM so the intent is clear and changes to offsets won’t silently break;
update the comment near LOAD_RAM/ORIGIN (and any related symbols like LOAD_RAM,
ORIGIN, or the new _FSBL_HEADER_SIZE) to reflect the derivation.
In `@src/platform/STM32/link/STM32N657XX_RAM.ld`:
- Around line 223-233: The .DMA_RW_D2 output section should be marked NOLOAD to
match the XIP/FSBL variants and prevent gdb from zero-filling D2_RAM or
embedding unexpected initializers; update the linker script section definition
for ".DMA_RW_D2" (the block that sets PROVIDE(dmarw_start), _sdmarw,
_dmarw_start__, KEEP(*(.DMA_RW)), PROVIDE(dmarw_end), _edmarw, _dmarw_end__) to
include the NOLOAD attribute while keeping the existing alignment and >D2_RAM
placement.
- Around line 40-46: The linker hardcodes 4096 for __config_end which can drift
from the actual eepromData[] size; instead expose the array size from C and use
that symbol in the linker. Add a C definition like `const unsigned long
__eeprom_size = EEPROM_SIZE;` (or use sizeof(eepromData) with a _Static_assert)
and then change the linker expressions for __config_start / __config_end to use
ABSOLUTE(eepromData) and ABSOLUTE(eepromData) + __eeprom_size (referencing
symbols __config_start, __config_end, eepromData, and __eeprom_size) so the
linker-bound range always matches the real array size.
In `@src/platform/STM32/link/STM32N657XX_XIP.ld`:
- Around line 145-196: Add a linker-time ASSERT to ensure .ram_code, .data,
.fastram_data and .dmaram_data are contiguous in memory so the Reset_Handler
copy from _stext.._etext using ram_code (LOADADDR(.ram_code) / _sitext) cannot
be broken by future reordering; specifically assert that ram_code_end ==
LOADADDR(.data) (or that ram_code_start + SIZEOF(.ram_code) == LOADADDR(.data))
and that the end of .data equals LOADADDR(.fastram_data) (or _efastram_data ==
LOADADDR(.fastram_data)) and that the end of .fastram_data equals
LOADADDR(.dmaram_data) (or _edmaram_data == LOADADDR(.dmaram_data)), using the
existing symbols ram_code_start, ram_code_end, ram_code, _stext, _etext,
_sfastram_data/_efastram_data and _sdmaram/_edmaram_data so the build fails if
any gap exists.
In `@src/platform/STM32/sdio_n6xx.c`:
- Around line 747-750: Both SDMMC1/SDMMC2 IRQ handlers currently call
HAL_SD_IRQHandler(&hsd1), which works now but is fragile if both controllers are
enabled; add a brief explanatory comment in SDMMC2_IRQHandler (and
SDMMC1_IRQHandler if present) noting that only one controller is wired at a time
and sdioHardware->instance selects the active controller in sdioPinConfigure(),
so the handler intentionally uses hsd1; if future work enables both controllers,
replace the hardcoded hsd1 usage with instance-aware dispatching (e.g., choose
the correct SD handle based on sdioHardware->instance) rather than copying the
same handle.
- Around line 197-206: In sdioRifMarkPin, the trailing comment on the PRIVCFGR
write is misleading; instead of saying “NPRIV = 0” update the comment for
port->PRIVCFGR &= ~mask to correctly reflect that clearing the PRIV bit allows
non-privileged access (e.g. “PRIV = 0 → non-privileged access allowed”); keep
references to IO_GPIO, IO_Pin, SECCFGR and PRIVCFGR so the change is localized
to the comment in sdioRifMarkPin.
In `@src/platform/STM32/startup/startup_stm32n657xx.s`:
- Around line 131-141: Add a one-line comment immediately above the
"post-AXISRAM-clock-enable" write (the ldr/str sequence to 0x46028A4C setting
0x0000018F) explaining that this non-secure-side bulk AXISRAM1..6 enable is
required so the .bss zeroing in early startup won't fault, and that systemInit()
later re-enables each AXISRAM via CMSIS macros (see systemInit) to also touch
the secure alias for HAL bookkeeping—this cross-reference clarifies why the same
0x18F mask is written twice.
- Around line 58-87: Introduce a single named constant for the NVIC register
count and use it instead of hardcoding "movs r3, `#16`", and tweak the two
comments to refer to ICER/ICPR (the registers being written) rather than
ISER/ISPR: add an assembly constant (e.g. NVIC_REG_COUNT = 16) and load it into
r3 before the two loops that write 0xE000E180 (NVIC_ICER0) and 0xE000E280
(NVIC_ICPR0), replace the two "clear ISER[i]" / "clear ISPR[i]" comments with
"clear ICER[i]" / "clear ICPR[i]" so the code uses one authoritative count
symbol (used by both loops) and the comments accurately describe the registers
being written.
- Around line 93-122: Replace the hardcoded debug RAM base and offsets with the
linker-exported DBG_RAM symbol so the startup stays in sync with the linker
scripts; in startup_stm32n657xx.s (the block that writes CABA0001 and the
self-PC), load the base via a PC-relative literal referencing DBG_RAM (e.g., use
LDR r2, =DBG_RAM) and then write to DBG_RAM+0x80, DBG_RAM+0x100, +0x104, +0x108,
+0x10C, +0x110 and store the PC to DBG_RAM+0x120 instead of using 0x24100000 and
the hardcoded offsets, ensuring the symbol name DBG_RAM is used for all
addresses so any ORIGIN change in STM32N657XX_XIP.ld (and matching FSBL/RAM/OBL)
stays consistent.
In `@src/platform/STM32/system_stm32n6xx.c`:
- Around line 161-167: The NVIC register-clear loops in system_stm32n6xx.c (the
for loop using sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0])) and the hardcoded
loop count in startup_stm32n657xx.s are inconsistent; fix by either (A) making
the assembly use a shared CMSIS-derived constant (add a .equ in the generated
header and reference it in startup_stm32n657xx.s) or (B) add a one-line comment
in both places cross-referencing each other and explaining why the assembly uses
16 iterations (writes to reserved tail are ignored) so the visual discrepancy is
documented; update the comment in system_stm32n6xx.c near the
NVIC->ICER/NVIC->ICPR loop or modify startup_stm32n657xx.s to reference the
shared constant accordingly.
- Around line 308-432: SystemClock_Config contains seven unrecoverable spins;
before each while (1); write a distinct marker into the .bf_obl_dbg debug buffer
so the OBL DFU alt can identify which HAL call failed. Concretely: in
SystemClock_Config, immediately before the failure branches that follow
HAL_PWREx_ConfigSupply, HAL_PWREx_ControlVoltageScaling, HAL_RCC_OscConfig
(first HSI block), HAL_RCC_ClockConfig (HSI clock switch), HAL_RCC_OscConfig
(HSE/PLL bring-up), HAL_RCC_ClockConfig (PLL distribution), and
HAL_RCCEx_PeriphCLKConfig, store a unique byte/word identifier into the
.bf_obl_dbg area (use distinct values per location) and ensure the write is
non-blocking/simple memory write so it will persist through the spin; keep the
existing while (1); spins unchanged otherwise. Use the .bf_obl_dbg symbol and
SystemClock_Config/HAL_* function names to find the spots and pick unique
markers for each failure path.
- Around line 121-142: The magic secure-alias addresses and DBGMCU.CR bit
positions used in systemInit (and later re-assert) are hard-coded and
duplicated; create clear named constants (e.g., RCC_S_MISCENSR_SECURE_ALIAS,
RCC_S_MISCENR_NS_ALIAS, RCC_S_BUSENSR_SECURE_ALIAS, RCC_S_BUSENR_NS_ALIAS,
DBGMCU_S_CR_SECURE_ALIAS and DBGMCU_CR_DBGCLKEN/DBG_SLEEP/DBG_STOP/DBG_STANDBY
bit masks) at the top of this file or in the platform header, replace the
literal constants and (1<<N) expressions in systemInit and the re-assert block
with those defines, and update the existing comment to reference the new symbols
so the meanings aren’t lost and duplication/typo risk is removed (use the
existing systemInit and re-assert code locations to find all occurrences).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f1dc479d-eab3-4e9a-9aa0-3b72efb3ef9d
⛔ Files ignored due to path filters (38)
lib/main/STM32/n6_fsbl/.gitignoreis excluded by!lib/**lib/main/STM32/n6_fsbl/FlashLayout.tsvis excluded by!**/*.tsv,!lib/**lib/main/STM32/n6_fsbl/FlashLayout_BF_AS_FSBL.tsvis excluded by!**/*.tsv,!lib/**lib/main/STM32/n6_fsbl/Makefileis excluded by!lib/**lib/main/STM32/n6_fsbl/STM32N657XX_FSBL.ldis excluded by!lib/**lib/main/STM32/n6_fsbl/main.cis excluded by!lib/**lib/main/STM32/n6_fsbl/main.his excluded by!lib/**lib/main/STM32/n6_fsbl/prebuilt/bf_as_fsbl.stm32is excluded by!lib/**lib/main/STM32/n6_fsbl/prebuilt/n6_fsbl_signed.stm32is excluded by!lib/**lib/main/STM32/n6_fsbl/startup_stm32n657xx_fsbl.sis excluded by!lib/**lib/main/STM32/n6_fsbl/stm32n6xx_hal_conf.his excluded by!lib/**lib/main/STM32/n6_fsbl/stm32n6xx_hal_msp.cis excluded by!lib/**lib/main/STM32/n6_fsbl/stm32n6xx_it.cis excluded by!lib/**lib/main/STM32/n6_fsbl/system_stm32n6xx_fsbl.cis excluded by!lib/**lib/main/STM32/n6_obl/.gitignoreis excluded by!lib/**lib/main/STM32/n6_obl/Makefileis excluded by!lib/**lib/main/STM32/n6_obl/STM32N657XX_OBL.ldis excluded by!lib/**lib/main/STM32/n6_obl/Unbricking.mdis excluded by!lib/**lib/main/STM32/n6_obl/app_openbootloader.cis excluded by!lib/**lib/main/STM32/n6_obl/external_memory_interface.cis excluded by!lib/**lib/main/STM32/n6_obl/flash/flash_iface.his excluded by!lib/**lib/main/STM32/n6_obl/flash/mx66uw1g45g.cis excluded by!lib/**lib/main/STM32/n6_obl/main.cis excluded by!lib/**lib/main/STM32/n6_obl/main.his excluded by!lib/**lib/main/STM32/n6_obl/obl_app.cis excluded by!lib/**lib/main/STM32/n6_obl/openbootloader_conf.his excluded by!lib/**lib/main/STM32/n6_obl/otp_interface.cis excluded by!lib/**lib/main/STM32/n6_obl/prebuilt/obl_mx66uw1g45g_signed.stm32is excluded by!lib/**lib/main/STM32/n6_obl/startup_stm32n657xx_obl.sis excluded by!lib/**lib/main/STM32/n6_obl/stm32n6xx_hal_conf.his excluded by!lib/**lib/main/STM32/n6_obl/stm32n6xx_hal_msp.cis excluded by!lib/**lib/main/STM32/n6_obl/stm32n6xx_it.cis excluded by!lib/**lib/main/STM32/n6_obl/system_stm32n6xx_obl.cis excluded by!lib/**lib/main/STM32/n6_obl/usbd_conf.cis excluded by!lib/**lib/main/STM32/n6_obl/usbd_conf.his excluded by!lib/**lib/main/STM32/n6_obl/usbd_desc.cis excluded by!lib/**lib/main/STM32/n6_obl/usbd_dfu.cis excluded by!lib/**lib/main/STM32/n6_obl/usbd_dfu_if.cis excluded by!lib/**
📒 Files selected for processing (27)
Makefilesrc/configsrc/main/cli/cli.csrc/main/drivers/bf_obl_contract.hsrc/main/fc/faults.csrc/main/fc/init.csrc/main/fc/tasks.csrc/main/main.csrc/main/target/common_post.hsrc/platform/STM32/adc_stm32n6xx.csrc/platform/STM32/bus_octospi_stm32n6xx.csrc/platform/STM32/io_stm32.csrc/platform/STM32/link/STM32N657XX_FSBL_FULL.ldsrc/platform/STM32/link/STM32N657XX_LRUN.ldsrc/platform/STM32/link/STM32N657XX_RAM.ldsrc/platform/STM32/link/STM32N657XX_XIP.ldsrc/platform/STM32/mk/STM32N6.mksrc/platform/STM32/sdio_n6xx.csrc/platform/STM32/serial_uart_stm32n6xx.csrc/platform/STM32/startup/startup_stm32n657xx.ssrc/platform/STM32/startup/stm32n6xx_hal_conf.hsrc/platform/STM32/system_stm32n6xx.csrc/platform/STM32/target/STM32N657/target.hsrc/platform/STM32/vcp_hal/usbd_conf_stm32n6xx.csrc/platform/common/stm32/fault_handlers.csrc/platform/common/stm32/io_def_generated.hsrc/utils/def_generated.pl
|
PR review pass — push at 7dcde47. Addressed (11):
Declined (4):
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/platform/STM32/link/STM32N657XX_LRUN.ld (1)
179-189:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winStale
FLASH1references in comments — sections now load fromFLASH.After moving all
AT >directives fromFLASH1toFLASH, two comments in.ram_codestill reference the old region:
- Line 179:
/* LMA in FLASH1 for initialiseMemorySections() */— the LMA is now inFLASH(0x70100000).- Line 188:
/* end of all FLASH1-backed sections — startup copies _stext.._etext */— those sections are nowFLASH-backed.Misleading for anyone tracing the boot-time copy path or debugging
_sitext/_etext.📝 Suggested wording fix
- ram_code = LOADADDR(.ram_code); /* LMA in FLASH1 for initialiseMemorySections() */ + ram_code = LOADADDR(.ram_code); /* LMA in FLASH for initialiseMemorySections() */ .ram_code : { . = ALIGN(4); ram_code_start = .; *(.ram_code) *(.ram_code*) . = ALIGN(4); ram_code_end = .; - _etext = .; /* end of all FLASH1-backed sections — startup copies _stext.._etext */ + _etext = .; /* end of all FLASH-backed sections — startup copies _stext.._etext */ } >RAM AT >FLASH🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/platform/STM32/link/STM32N657XX_LRUN.ld` around lines 179 - 189, Update the two stale comments in the .ram_code linker block to reflect that the load memory address (LOADADDR(.ram_code)) and the FLASH-backed sections (_stext.._etext) now reside in FLASH (e.g., 0x70100000) instead of FLASH1; specifically edit the comment on the ram_code LMA line and the comment after ram_code_end/_etext so they mention FLASH (and optionally the concrete address) and the startup copy path used by initialiseMemorySections() and the _stext.._etext copy.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/platform/STM32/link/STM32N657XX_LRUN.ld`:
- Around line 179-189: Update the two stale comments in the .ram_code linker
block to reflect that the load memory address (LOADADDR(.ram_code)) and the
FLASH-backed sections (_stext.._etext) now reside in FLASH (e.g., 0x70100000)
instead of FLASH1; specifically edit the comment on the ram_code LMA line and
the comment after ram_code_end/_etext so they mention FLASH (and optionally the
concrete address) and the startup copy path used by initialiseMemorySections()
and the _stext.._etext copy.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6208e9b4-53b3-460c-a0b9-ed2594def43f
⛔ Files ignored due to path filters (3)
lib/main/STM32/n6_obl/openbootloader_conf.his excluded by!lib/**lib/main/STM32/n6_obl/usbd_conf.his excluded by!lib/**lib/main/STM32/n6_obl/usbd_dfu_if.cis excluded by!lib/**
📒 Files selected for processing (9)
src/main/cli/cli.csrc/main/main.csrc/platform/STM32/adc_stm32n6xx.csrc/platform/STM32/link/STM32N657XX_LRUN.ldsrc/platform/STM32/link/STM32N657XX_RAM.ldsrc/platform/STM32/link/STM32N657XX_XIP.ldsrc/platform/STM32/sdio_n6xx.csrc/platform/STM32/system_stm32n6xx.csrc/platform/STM32/vcp_hal/usbd_conf_stm32n6xx.c
🚧 Files skipped from review as they are similar to previous changes (8)
- src/main/cli/cli.c
- src/platform/STM32/vcp_hal/usbd_conf_stm32n6xx.c
- src/main/main.c
- src/platform/STM32/adc_stm32n6xx.c
- src/platform/STM32/link/STM32N657XX_XIP.ld
- src/platform/STM32/link/STM32N657XX_RAM.ld
- src/platform/STM32/system_stm32n6xx.c
- src/platform/STM32/sdio_n6xx.c
25c6450 to
50d58b2
Compare
Adds STM32N6 platform support, with STM32N657DK_DSK320 as the first working target. End-to-end on the dev kit: boot ROM → Betaflight OpenBootloader (lib/main/STM32/n6_obl) → BF running from XSPI at 600 MHz, USB VCP enumerates as 0483:5740, CLI accessible. Companion target-config PR: betaflight/config#1096. Vendor SDK + CMSIS: - lib/main/STM32N6: CubeN6 v1.3.0 (HAL, CMSIS, USBD library, OpenBootloader middleware). - lib/main/CMSIS/Core/Include: ARMv8.1-M / Cortex-M55 core headers (core_cm55.h, cachel1_armv7.h, pmu_armv8.h). Platform (src/platform/STM32): - N6 family startup, system init, vector table, XIP/LRUN linker scripts (with ALIGN(4) on .pg_registry / .pg_resetdata / .ARM sections so .data LMA stays in step with VMA). - USB OTG_HS PHY bring-up: VddUSB / USB33RDY wait, OTG/PHY reset pulse, HSE/2 reference, USBPHYC_CR.FSEL = 0b010, RIF master + slave attributes wired so SETUP packets reach the controller's RX FIFO. - Secure-alias DBGMCU/RCC writes (NS-tagged transactions through the CMSIS macros silently drop on RIFSC; SWD attach to a running BF fails AP1 examination without these). - SDIO/SDMMC2 RIF master + per-pin secure attributes (HAL_SD_Init itself still wedges on STAR; pin/RIF infrastructure is in place). - ADC: skip the device when no DMA spec is configured rather than dereferencing a NULL dmaSpec. - IO subsystem extended through GPIO port O via the io_def_generated.pl perl helper. - systemResetToBootloader spins with IRQs masked so OBL's IWDG fires and OBL routes the next boot to DFU via RCC->RSR.IWDGRSTF (a clean SYSRESETREQ would set SFTRSTF and OBL would just relaunch BF). - LTDC LCD console backend for the N6570-DK RK050HR18-CT panel (off by default; bring-up still wedges the chip when enabled — see feedback notes in source). Betaflight OpenBootloader (lib/main/STM32/n6_obl): - Custom signed FSBL loaded from XSPI nor0 0x0; sets up clocks, memory-maps XSPI, validates BF vector table at 0x70100000. - Decides DFU vs jump from RCC->RSR (IWDGRSTF / LCKRSTF / WWDGRSTF). - Arms a 10 s IWDG before jumping to BF. - DFU mode re-flashes BF over USB without touching BOOT0. - Optional OBL_FORCE_DFU build flag short-circuits the boot decision for SWD-load development iteration. - Unbricking.md covers the full TSV recovery path via the boot ROM system DFU when OBL itself is broken. BF ↔ OBL contract: - drivers/bf_obl_contract.h: BF_OBL_IWDG_REFRESH macro. ENABLE_BF_OBL defaulted off in common_post.h; N6 target.h opts in. - BF refreshes OBL's IWDG from TASK_SERIAL (the scheduler exempts it from the time-budget check, so it is the most reliable carrier). - bl rom from CLI / MSP DFU spins with IRQs masked so IWDG fires and OBL routes the next boot to DFU. - 1 Hz LED1 toggle in run() — visible 'BF is alive' heartbeat independent of any task running. Misc fixes: - fc/faults.c: __attribute__((used)) on systemFaultAction so LTO doesn't drop the symbol the inline-asm tail-calls in HardFault reach. - Skip unusedPinsInit() on N6 — IOTraversePins wedges on at least one RIFSC-restricted port; TODO to teach the IO layer about restricted ports. - Startup vector table rewritten to match the CMSIS IRQn ordering for N6. Status: - Working: boot path, USB VCP + CLI, virtual gyro/acc/baro/mag stack, ADC1 + VBAT sense. - Pending: real LSM6DSK320X on SPI1, LIS2MDL + LPS22DF on I2C1, SDMMC2 init wedge on STAR, LTDC LCD bring-up wedge. PR marked draft until the real-sensor / SDIO / LCD TODOs are closed.
|
AI Generated comment This PR's Makefile diff silently reverted #15209. #15209 changed the Confirmed via |
Summary
Adds STM32N6 platform support, with STM32N657DK_DSK320 as the first
working target. End-to-end on the dev kit: boot ROM → Betaflight
OpenBootloader (
lib/main/STM32/n6_obl) → BF running from XSPI at600 MHz, USB VCP enumerates as
0483:5740, CLI accessible. Companiontarget-config PR: betaflight/config#1096.
What's new
lib/main/STM32N6/: CubeN6 v1.3.0 vendor SDK (HAL, CMSIS, USBD library, OpenBootloader middleware).lib/main/CMSIS/Core/Include/: ARMv8.1-M / Cortex-M55 core headers (core_cm55.h,cachel1_armv7.h,pmu_armv8.h).src/platform/STM32(N6 family): startup, system init, linker scripts (XIP/LRUN), USB OTG_HS PHY bring-up (USB33RDY, FSEL, RIF master/slave attrs), SDIO RIF wiring, secure-alias DBGMCU writes (RIFSC drops NS-tagged writes), IO subsystem extended through GPIO port O.lib/main/STM32/n6_obl/: Betaflight OpenBootloader. Custom signed FSBL loaded from XSPI nor0 0x0; sets up clocks, memory-maps XSPI, decides DFU-vs-jump fromRCC->RSR(IWDGRSTF / LCKRSTF / WWDGRSTF), arms a 10 s IWDG before jumping to BF. DFU mode re-flashes BF over USB without touching BOOT0.Unbricking.mdcovers the full TSV recovery path via boot ROM system DFU.ENABLE_BF_OBL, default off; N6target.hopts in): BF refreshes OBL's IWDG fromTASK_SERIAL;bl romfrom CLI / MSP DFU spins with IRQs masked so IWDG routes the next boot to DFU; visible 1 Hz LED1 heartbeat inrun().Status
Working: boot path through OBL into BF, USB VCP + CLI, virtual gyro/acc/baro/mag stack so the scheduler runs, ADC1 + VBAT sense.
Not yet wired (TODOs in the per-board config):
STAR; pin/RIF infra is in).N6-specific quirks left as TODOs in the code:
unusedPinsInit()skipped on N6 —IOTraversePinswedges on at least one RIFSC-restricted port; needs the IO layer to learn about restricted ports..persistent_datadoesn't surviveNVIC_SystemReset();CONFIG_IN_RAMCLI saves don't persist across reboot. Bake defaults into per-boardconfig.hfor now.Marked draft until the real-sensor / SDIO / LCD TODOs are closed out.
Summary by CodeRabbit
New Features
Bug Fixes
Chores