Sync betaflight to Gitea
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
*.bin
|
||||
*.elf
|
||||
*.o
|
||||
*.map
|
||||
@@ -0,0 +1,266 @@
|
||||
# Betaflight N6 OpenBootloader build.
|
||||
#
|
||||
# Architecture:
|
||||
# - Skeleton sources here: main.c (boot decision), system_stm32n6xx_obl.c,
|
||||
# stm32n6xx_hal_msp.c, stm32n6xx_it.c, startup, linker, flash driver.
|
||||
# - The OBL DFU stack (CubeN6 Middlewares/ST/OpenBootloader + USB Device
|
||||
# Library + the Application/OpenBootloader/USB_Device glue) is pulled
|
||||
# in via VPATH from the submodule, NOT modified or copied — so re-
|
||||
# pulling CubeN6 keeps us up to date. Currently disabled (dfu_stub.c
|
||||
# stands in) until task #6 wires it up.
|
||||
#
|
||||
# Usage:
|
||||
# make produce obl.elf and obl.bin (skeleton only)
|
||||
# make signed re-sign into prebuilt/obl_signed.stm32
|
||||
# make OBL_FLASH_DRIVER=mx66uw1g45g pick driver source (default)
|
||||
# make clean
|
||||
|
||||
ROOT := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))/../../../..)
|
||||
TOOLCHAIN_BIN := $(ROOT)/tools/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi/bin
|
||||
PREFIX := $(if $(wildcard $(TOOLCHAIN_BIN)/arm-none-eabi-gcc),$(TOOLCHAIN_BIN)/,)arm-none-eabi-
|
||||
|
||||
CC := $(PREFIX)gcc
|
||||
AS := $(PREFIX)gcc -x assembler-with-cpp
|
||||
LD := $(PREFIX)gcc
|
||||
OBJCOPY := $(PREFIX)objcopy
|
||||
SIZE := $(PREFIX)size
|
||||
|
||||
OBL_FLASH_DRIVER ?= mx66uw1g45g
|
||||
|
||||
HAL_DIR := $(ROOT)/lib/modules/STM32N6/Drivers/STM32N6xx_HAL_Driver
|
||||
CMSIS_ST := $(ROOT)/lib/modules/STM32N6/Drivers/CMSIS/Device/ST/STM32N6xx/Include
|
||||
CMSIS := $(ROOT)/lib/modules/STM32N6/Drivers/CMSIS/Include
|
||||
OBL_MW := $(ROOT)/lib/modules/STM32N6/Middlewares/ST/OpenBootloader
|
||||
OBL_USBLIB := $(OBL_MW)/STM32_USB_Device_Library
|
||||
OBL_PROJ := $(ROOT)/lib/modules/STM32N6/Projects/STM32N6570-DK/Applications/OpenBootloader
|
||||
|
||||
INCLUDES := \
|
||||
-I. \
|
||||
-Iflash \
|
||||
-I$(ROOT)/src/platform/STM32/include \
|
||||
-I$(HAL_DIR)/Inc \
|
||||
-I$(CMSIS_ST) \
|
||||
-I$(CMSIS) \
|
||||
-I$(OBL_MW)/Core \
|
||||
-I$(OBL_MW)/Util \
|
||||
-I$(OBL_MW)/Modules/Mem \
|
||||
-I$(OBL_MW)/Modules/USB \
|
||||
-I$(OBL_MW)/Modules/USART \
|
||||
-I$(OBL_USBLIB)/Core/Inc \
|
||||
-I$(OBL_USBLIB)/Class/DFU/Inc \
|
||||
-I$(OBL_PROJ)/Core/Inc \
|
||||
-I$(OBL_PROJ)/OpenBootloader/App \
|
||||
-I$(OBL_PROJ)/OpenBootloader/Target \
|
||||
-I$(OBL_PROJ)/USB_Device/App \
|
||||
-I$(OBL_PROJ)/USB_Device/Target
|
||||
|
||||
DEFINES := \
|
||||
-DSTM32N657xx \
|
||||
-DUSE_HAL_DRIVER \
|
||||
-DHSE_VALUE=48000000
|
||||
|
||||
# Bring-up override: make OBL_FORCE_DFU=1 to build an OBL that always
|
||||
# enters DFU regardless of nor0 contents.
|
||||
ifdef OBL_FORCE_DFU
|
||||
DEFINES += -DOBL_FORCE_DFU
|
||||
endif
|
||||
|
||||
# Bring-up override: make OBL_FORCE_RECOVERY=1 to build an OBL that
|
||||
# always enters Recovery mode (exposes nor0 from 0x70000000 so OBL+BF
|
||||
# can be reinstalled). RAM-load only — never sign and flash this.
|
||||
ifdef OBL_FORCE_RECOVERY
|
||||
DEFINES += -DOBL_FORCE_RECOVERY
|
||||
endif
|
||||
|
||||
# OBL is the only S-state code on the device, so build it with the Cortex-M
|
||||
# Security Extensions enabled. -mcmse sets __ARM_FEATURE_CMSE=3, which makes
|
||||
# the ST CMSIS headers select the secure-alias peripheral base addresses
|
||||
# (RCC, RIFSC, GPIOx, DBGMCU, TAMP, PWR, RTC, EXTI, GPDMA1, HPDMA1, ...). All
|
||||
# peripheral access in OBL therefore goes through the S aliases naturally
|
||||
# via the CMSIS struct pointers instead of hand-coded addresses.
|
||||
ARCH_FLAGS := -mthumb -mcpu=cortex-m55 -mfloat-abi=hard -mfpu=fpv5-d16 -mcmse
|
||||
|
||||
CFLAGS := \
|
||||
$(ARCH_FLAGS) \
|
||||
-Os -g3 \
|
||||
-ffunction-sections -fdata-sections \
|
||||
-Wall -Wextra \
|
||||
-Wno-unused-parameter \
|
||||
-std=gnu11 \
|
||||
$(INCLUDES) $(DEFINES)
|
||||
|
||||
ASFLAGS := $(ARCH_FLAGS) -g3 $(INCLUDES) $(DEFINES)
|
||||
|
||||
LDSCRIPT := STM32N657XX_OBL.ld
|
||||
LDFLAGS := \
|
||||
$(ARCH_FLAGS) \
|
||||
-T$(LDSCRIPT) \
|
||||
-nostartfiles \
|
||||
-specs=nano.specs -specs=nosys.specs \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,-Map=obl.map \
|
||||
-Wl,--print-memory-usage
|
||||
|
||||
LOCAL_C_SRCS := \
|
||||
main.c \
|
||||
obl_app.c \
|
||||
app_openbootloader.c \
|
||||
stm32n6xx_hal_msp.c \
|
||||
stm32n6xx_it.c \
|
||||
system_stm32n6xx_obl.c \
|
||||
external_memory_interface.c \
|
||||
otp_interface.c \
|
||||
usbd_conf.c \
|
||||
usbd_desc.c \
|
||||
usbd_dfu.c \
|
||||
usbd_dfu_if.c \
|
||||
flash/$(OBL_FLASH_DRIVER).c
|
||||
|
||||
HAL_C_SRCS := \
|
||||
stm32n6xx_hal.c \
|
||||
stm32n6xx_hal_cortex.c \
|
||||
stm32n6xx_hal_rcc.c \
|
||||
stm32n6xx_hal_rcc_ex.c \
|
||||
stm32n6xx_hal_pwr.c \
|
||||
stm32n6xx_hal_pwr_ex.c \
|
||||
stm32n6xx_hal_gpio.c \
|
||||
stm32n6xx_hal_xspi.c \
|
||||
stm32n6xx_hal_dma.c \
|
||||
stm32n6xx_hal_dma_ex.c \
|
||||
stm32n6xx_hal_iwdg.c \
|
||||
stm32n6xx_hal_pcd.c \
|
||||
stm32n6xx_hal_pcd_ex.c \
|
||||
stm32n6xx_ll_usb.c
|
||||
|
||||
# OBL middleware (protocol-agnostic core, memory mgr, USB transport bindings,
|
||||
# utilities). Pulled in unchanged from the CubeN6 submodule.
|
||||
OBL_MW_C_SRCS := \
|
||||
Core/openbl_core.c \
|
||||
Util/openbl_util.c \
|
||||
Modules/Mem/openbl_mem.c \
|
||||
Modules/USB/openbl_usb_cmd.c
|
||||
|
||||
# USB Device Library (DFU class + core). Note: usbd_dfu.c is overridden
|
||||
# locally (see LOCAL_C_SRCS) — the Betaflight copy patches the per-alt
|
||||
# iInterface descriptor lookup so all alts use DfuInterface->pStrDesc.
|
||||
OBL_USB_C_SRCS := \
|
||||
Core/Src/usbd_core.c \
|
||||
Core/Src/usbd_ctlreq.c \
|
||||
Core/Src/usbd_ioreq.c
|
||||
|
||||
# Application-level glue from the reference OBL project. We provide our
|
||||
# own external_memory_interface.c (above), so that submodule file is NOT
|
||||
# listed here.
|
||||
OBL_APP_C_SRCS := \
|
||||
OpenBootloader/Target/common_interface.c \
|
||||
OpenBootloader/Target/iwdg_interface.c \
|
||||
OpenBootloader/Target/ram_interface.c \
|
||||
OpenBootloader/Target/usb_interface.c \
|
||||
USB_Device/App/usb_device.c
|
||||
|
||||
LOCAL_OBJS := \
|
||||
main.o \
|
||||
obl_app.o \
|
||||
app_openbootloader.o \
|
||||
stm32n6xx_hal_msp.o \
|
||||
stm32n6xx_it.o \
|
||||
system_stm32n6xx_obl.o \
|
||||
external_memory_interface.o \
|
||||
otp_interface.o \
|
||||
usbd_conf.o \
|
||||
usbd_desc.o \
|
||||
usbd_dfu.o \
|
||||
usbd_dfu_if.o \
|
||||
flash/$(OBL_FLASH_DRIVER).o \
|
||||
startup_stm32n657xx_obl.o
|
||||
HAL_OBJS := $(HAL_C_SRCS:.c=.hal.o)
|
||||
OBL_MW_OBJS := $(notdir $(OBL_MW_C_SRCS:.c=.mw.o))
|
||||
OBL_USB_OBJS:= $(notdir $(OBL_USB_C_SRCS:.c=.usbd.o))
|
||||
OBL_APP_OBJS:= $(notdir $(OBL_APP_C_SRCS:.c=.app.o))
|
||||
OBJS := $(LOCAL_OBJS) $(HAL_OBJS) $(OBL_MW_OBJS) $(OBL_USB_OBJS) $(OBL_APP_OBJS)
|
||||
|
||||
ELF := obl.elf
|
||||
BIN := obl.bin
|
||||
|
||||
SIGNING_TOOL ?= STM32_SigningTool_CLI
|
||||
SIGNED_BLOB := prebuilt/obl_$(OBL_FLASH_DRIVER)_signed.stm32
|
||||
|
||||
# Build fails loudly if the OBL output exceeds the linker-declared RAM
|
||||
# region. Matches LENGTH in STM32N657XX_OBL.ld.
|
||||
OBL_SIZE_LIMIT := 65536 # 64 KiB
|
||||
|
||||
.PHONY: all signed size_check clean
|
||||
all: $(BIN) size_check
|
||||
|
||||
$(BIN): $(ELF)
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
$(SIZE) $<
|
||||
|
||||
$(ELF): $(OBJS) $(LDSCRIPT)
|
||||
$(LD) $(LDFLAGS) $(OBJS) -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.s
|
||||
$(AS) $(ASFLAGS) -c $< -o $@
|
||||
|
||||
flash/%.o: flash/%.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.hal.o: $(HAL_DIR)/Src/%.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Pattern rules for the OBL middleware / USB Device Library / application
|
||||
# glue. Each rule strips the suffix used to disambiguate object files
|
||||
# (.mw.o, .usbd.o, .app.o) and finds the .c by leaf-name search across
|
||||
# the upstream subdirectories. notdir + foreach keeps the rule chain
|
||||
# manageable without listing every nested directory in VPATH.
|
||||
|
||||
OBL_MW_DIRS := \
|
||||
$(OBL_MW)/Core \
|
||||
$(OBL_MW)/Util \
|
||||
$(OBL_MW)/Modules/Mem \
|
||||
$(OBL_MW)/Modules/USB \
|
||||
$(OBL_MW)/Modules/USART
|
||||
|
||||
OBL_USB_DIRS := \
|
||||
$(OBL_USBLIB)/Core/Src \
|
||||
$(OBL_USBLIB)/Class/DFU/Src
|
||||
|
||||
OBL_APP_DIRS := \
|
||||
$(OBL_PROJ)/OpenBootloader/App \
|
||||
$(OBL_PROJ)/OpenBootloader/Target \
|
||||
$(OBL_PROJ)/USB_Device/App \
|
||||
$(OBL_PROJ)/USB_Device/Target
|
||||
|
||||
vpath %.c $(OBL_MW_DIRS) $(OBL_USB_DIRS) $(OBL_APP_DIRS)
|
||||
|
||||
%.mw.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.usbd.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.app.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Hard size cap — keeps OBL fit-for-@FSBL-DFU even as middleware grows.
|
||||
size_check: $(BIN)
|
||||
@bin_size=$$(stat -c %s $(BIN)); \
|
||||
if [ $$bin_size -gt $(OBL_SIZE_LIMIT) ]; then \
|
||||
echo "ERROR: $(BIN) is $$bin_size bytes, exceeds $(OBL_SIZE_LIMIT) byte cap (boot ROM @FSBL alt limit)"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "OBL size OK: $$bin_size / $(OBL_SIZE_LIMIT) bytes"; \
|
||||
fi
|
||||
|
||||
signed: $(SIGNED_BLOB)
|
||||
$(SIGNED_BLOB): $(BIN) | prebuilt
|
||||
$(SIGNING_TOOL) -bin $< -nk -of 0x80000000 -t ssbl -hv 2.3 -align -o $@ -s
|
||||
|
||||
prebuilt:
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -f $(LOCAL_OBJS) $(HAL_OBJS) $(ELF) $(BIN) obl.map
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Linker script for the Betaflight N6 OpenBootloader (OBL).
|
||||
*
|
||||
* Boot ROM behaviour (BOOT0=USER, BOOT1=2):
|
||||
* 1. Reads signed FSBL header from XSPI 0x70000000
|
||||
* 2. Verifies + copies payload into AXISRAM2 secure starting at
|
||||
* 0x34180400 (the 0x400 offset matches `STM32_SigningTool_CLI -align`)
|
||||
* 3. Branches to Reset_Handler at the loaded entry point
|
||||
*
|
||||
* The boot ROM caps @FSBL DFU staging at ~485 KiB empirically — the
|
||||
* destination region from 0x34180400 to end of AXISRAM2 secure is 521 KiB
|
||||
* but the boot ROM reserves ~36 KiB of scratch. The 480 KiB region below
|
||||
* is the safe budget for OBL + flash driver + DFU stack + self-installer.
|
||||
*/
|
||||
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
_Min_Stack_Size = 0x2000; /* 8 KiB — DFU upload buffer + USB ISR + libc */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* Loaded region: signed FSBL slot in AXISRAM2 secure. Boot ROM lands
|
||||
* the .stm32 payload here. 64 KiB caps OBL well below the boot-ROM
|
||||
* @FSBL DFU staging limit and leaves the rest of AXISRAM2-secure
|
||||
* free for any future secure code. */
|
||||
RAM (rwx) : ORIGIN = 0x34180400, LENGTH = 64K
|
||||
}
|
||||
|
||||
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||
_sstack = _estack - _Min_Stack_Size;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector))
|
||||
. = ALIGN(4);
|
||||
} > RAM
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.eh_frame)
|
||||
. = ALIGN(4);
|
||||
} > RAM
|
||||
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
. = ALIGN(4);
|
||||
} > RAM
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
} > RAM
|
||||
|
||||
.ARM :
|
||||
{
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} > RAM
|
||||
|
||||
/* Embedded "self" payload: the OBL writes the bytes of its own loaded
|
||||
* image to nor0 0x0 during recovery self-install. The payload starts
|
||||
* at the FSBL header (0x34180000 in AXISRAM, which is 0x34180400 minus
|
||||
* the 0x400 alignment header — but boot ROM only stages from 0x34180400
|
||||
* onwards, so the "self" we replicate is the post-header content). The
|
||||
* signed header has to be re-applied externally — at install time we
|
||||
* read it from nor0 too via memory-mapped XSPI before erase. */
|
||||
_self_load_start = ORIGIN(RAM);
|
||||
_self_load_end = .; /* updated by linker after .data */
|
||||
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
__bss_start__ = .;
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = .;
|
||||
/* End-of-image marker for newlib's _sbrk. We don't use malloc,
|
||||
* but linking pulls _sbrk in regardless; satisfy it with a
|
||||
* symbol that points past .bss so any incidental allocation
|
||||
* lands in unused heap space below the stack reservation. */
|
||||
PROVIDE(end = .);
|
||||
PROVIDE(_end = .);
|
||||
} > RAM
|
||||
|
||||
/* Stack reservation at the top of RAM. Linker fails the build if BSS
|
||||
* overflows into the stack region. */
|
||||
._stack_reservation :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} > RAM
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.ARM.attributes)
|
||||
*(.comment)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
# Unbricking the STM32N6 (TSV + system DFU)
|
||||
|
||||
Use this when OBL or BF in XSPI flash is broken and the board no longer
|
||||
boots into a usable state — `bl rom` from the BF CLI, OBL DFU on a
|
||||
crashed BF, and SWD-load workflows are all unavailable. The boot ROM's
|
||||
built-in system DFU is always reachable, but it can't write the on-board
|
||||
XSPI flash on its own; CubeProgrammer drives it through a TSV that
|
||||
stages two helper binaries into RAM first.
|
||||
|
||||
## Why the on-chip DFU can't just write XSPI
|
||||
|
||||
The N6 boot ROM exposes a system DFU bootloader (`0483:DF11` with the
|
||||
ST descriptor "STM32 BOOTLOADER", reached via BOOT0=DEV) but the alts
|
||||
it offers are RAM- and OTP-shaped:
|
||||
|
||||
```
|
||||
@FSBL /0x34180400/01*512Kg -- staged FSBL → AXISRAM2 secure
|
||||
@RSSE_FW ...
|
||||
@RSSE_BLOB ...
|
||||
@RSSE_PLUGIN ...
|
||||
@virtual ...
|
||||
```
|
||||
|
||||
There's no `@nor0` alt and no XSPI alt — the ROM has no XSPI driver. To
|
||||
write nor0 you have to:
|
||||
|
||||
1. Upload a signed FSBL into RAM via the `@FSBL` alt. The ROM verifies
|
||||
it, copies it to AXISRAM2, and runs it.
|
||||
2. The FSBL you upload is itself a DFU loader (ST's "OpenBootloader")
|
||||
that exposes `@nor0` plus an "external memory loader" alt for the
|
||||
chosen flash chip's driver.
|
||||
3. Upload the matching XSPI flash driver into RAM via the `@External
|
||||
memory loader` alt; the OpenBootloader links it in and the rest of
|
||||
the DFU session can read/write nor0.
|
||||
4. Now write your application binary to nor0 at the right offset.
|
||||
|
||||
The TSV (Target Sequence Vector) file is just a script for
|
||||
`STM32_Programmer_CLI -d <FlashLayout.tsv>` that orchestrates these
|
||||
four steps in order so it looks like a single flash operation from the
|
||||
host's point of view.
|
||||
|
||||
## When to use this
|
||||
|
||||
- `bl rom` from BF CLI / OBL DFU `:leave` doesn't reboot the chip into
|
||||
a working state.
|
||||
- BF crashes early enough that the BF↔OBL IWDG-recovery path doesn't
|
||||
trigger (e.g., a fault before OBL's iwdg_start runs, or OBL itself is
|
||||
corrupt).
|
||||
- You want to nuke and re-flash both OBL and BF from a clean slate.
|
||||
|
||||
If the chip is reachable via SWD (BOOT1 in the position that bypasses
|
||||
the FSBL on the dev kit, or BF still alive enough that OBL DFU comes
|
||||
up), prefer the SWD-load or OBL-DFU paths — they don't require touching
|
||||
hardware switches.
|
||||
|
||||
## What you need
|
||||
|
||||
- **STM32CubeProgrammer 2.18+ with the N6 patches installed.** The
|
||||
Linux build prior to 2.22 has bugs around the N6 external-loader
|
||||
workflow; current ST tooling works.
|
||||
- **`OpenBootloader_STM32N6570-DK-trusted.stm32`** — ST's signed
|
||||
OpenBootloader image. Ships in CubeN6 at
|
||||
`lib/modules/STM32N6/Projects/STM32N6570-DK/Applications/OpenBootloader/Binaries/NOR_Binary/`.
|
||||
- **`MX66UW1G45G_STM32N6570-DK.bin`** — XSPI flash driver matching the
|
||||
on-board MX66UW1G45G. Same directory in CubeN6.
|
||||
- The binaries you want in nor0:
|
||||
- `obl_mx66uw1g45g_signed.stm32` — our OBL, output of
|
||||
`make -C lib/main/STM32/n6_obl signed`.
|
||||
- Optionally `betaflight_<version>_STM32N657_<config>.bin` — your BF
|
||||
build, if you're flashing OBL + BF together. Skip when you want
|
||||
only the OBL written and the BF slot zeroed (next section).
|
||||
|
||||
## Recovery image variants
|
||||
|
||||
| Variant | What lands in nor0 | When to use |
|
||||
|------------------------|--------------------------------------------------------------|-------------|
|
||||
| **OBL + BF combined** | OBL at `0x0..0x87A0`, zeros to `0x100000`, BF at `0x100000+` | Normal flash after both binaries are ready. |
|
||||
| **OBL only, BF erased**| OBL at `0x0..0x87A0`, zeros from end-of-OBL through `0x200000` | After an OBL change that may make BF crash on first boot (e.g. when OBL now transfers to BF in NS state). The empty BF slot fails OBL's vector-table validity check, so the next boot lands straight in OBL DFU recovery — no IWDG round-trip needed. |
|
||||
|
||||
### Combined image construction (OBL + BF)
|
||||
|
||||
```bash
|
||||
cp lib/main/STM32/n6_obl/prebuilt/obl_mx66uw1g45g_signed.stm32 /tmp/obl_bf_combined.bin
|
||||
truncate -s 1048576 /tmp/obl_bf_combined.bin
|
||||
cat obj/betaflight_*_OPENN657V1.bin >> /tmp/obl_bf_combined.bin
|
||||
```
|
||||
|
||||
Layout to use: `lib/main/STM32/n6_fsbl/FlashLayout_OBL.tsv`.
|
||||
|
||||
### OBL-only image construction (BF slot erased)
|
||||
|
||||
```bash
|
||||
install -m 0644 lib/main/STM32/n6_obl/prebuilt/obl_mx66uw1g45g_signed.stm32 \
|
||||
lib/main/STM32/n6_obl/prebuilt/obl_only_combined.bin
|
||||
truncate -s 2097152 lib/main/STM32/n6_obl/prebuilt/obl_only_combined.bin
|
||||
```
|
||||
|
||||
The 2 MiB size covers both the 1 MiB OBL slot and the 1 MiB BF slot with
|
||||
a single contiguous write at nor0 offset 0; everything past the OBL
|
||||
payload is zero from `truncate`, so the BF slot lands erased.
|
||||
|
||||
Layout to use: `lib/main/STM32/n6_fsbl/FlashLayout_OBL_only.tsv`.
|
||||
|
||||
## TSV layout
|
||||
|
||||
Save as `Unbricking.tsv` next to `OpenBootloader_STM32N6570-DK-trusted.stm32`
|
||||
and `MX66UW1G45G_STM32N6570-DK.bin`, then edit the two
|
||||
`<...your-path...>` lines to point at your OBL and BF artefacts.
|
||||
|
||||
```tsv
|
||||
#Opt Id Name Type IP Offset Binary
|
||||
P 0x1 fsbl-openbl Binary none 0x0 OpenBootloader_STM32N6570-DK-trusted.stm32
|
||||
P 0x3 fsbl-extfl Binary none 0x0 MX66UW1G45G_STM32N6570-DK.bin
|
||||
P 0x2 obl-app Binary nor0 0x0 <path/to>/obl_mx66uw1g45g_signed.stm32
|
||||
P 0x4 bf-app Binary nor0 0x100000 <path/to>/obj/betaflight_<version>_STM32N657_<config>.bin
|
||||
```
|
||||
|
||||
Columns:
|
||||
|
||||
- `Opt`: `P` = program (write); `PE` = program + erase first.
|
||||
- `Id`: ST's partition ID. The OpenBootloader recognises:
|
||||
- `0x1` → @FSBL: stage a signed FSBL image to AXISRAM2.
|
||||
- `0x2` → nor0 offset 0 (OBL slot, signed `.stm32` artefact).
|
||||
- `0x3` → @External memory loader: stage the XSPI driver to RAM.
|
||||
- `0x4` → nor0 offset 0x100000 (BF slot).
|
||||
- `Name`: free-text label, shown in CubeProg output.
|
||||
- `Type`: `Binary` for raw bytes; `Force not used` to skip a row.
|
||||
- `IP`: target memory — `none` means "load to RAM" (used by Id 0x1
|
||||
and 0x3); `nor0` is the on-board XSPI flash.
|
||||
- `Offset`: byte offset inside the target memory.
|
||||
- `Binary`: path (relative to the TSV) of the file to write.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. Power cycle the board.
|
||||
2. Slide **BOOT0** to **DEV** (toward the edge of the board on the
|
||||
N6570-DK; the boot ROM samples this pin at reset and enters system
|
||||
DFU when high).
|
||||
3. Press the reset button. `lsusb` should now show:
|
||||
|
||||
```
|
||||
Bus xxx Device xxx: ID 0483:df11 STMicroelectronics STM Device in DFU Mode
|
||||
```
|
||||
4. Run CubeProg against the TSV (substitute `FlashLayout_OBL_only.tsv`
|
||||
if you want OBL written and the BF slot erased):
|
||||
|
||||
```bash
|
||||
~/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_Programmer_CLI \
|
||||
-c port=USB1 -d Unbricking.tsv
|
||||
```
|
||||
|
||||
You'll see four phases scroll past — `fsbl-openbl` upload, then a
|
||||
re-enumeration as the OpenBootloader starts, then `fsbl-extfl`
|
||||
upload, then `obl-app` and `bf-app` writes to nor0.
|
||||
5. Slide **BOOT0** back to **USER**.
|
||||
6. Press reset. The chip boots from XSPI into our OBL → BF.
|
||||
|
||||
## Verifying
|
||||
|
||||
After the reset in step 6:
|
||||
|
||||
```bash
|
||||
lsusb | grep 0483
|
||||
# Expect:
|
||||
# Bus xxx Device xxx: ID 0483:3754 STMicroelectronics STLINK-V3
|
||||
# Bus xxx Device xxx: ID 0483:5740 STMicroelectronics Virtual COM Port
|
||||
```
|
||||
|
||||
`/dev/ttyACM1` is the BF VCP. Send `#` + LF to escape MSP into the BF
|
||||
CLI; `version` should report the build you just flashed.
|
||||
|
||||
## Footnotes
|
||||
|
||||
- `STM32_Programmer_CLI -el .stldr -w` with the `.stldr` form of the
|
||||
XSPI driver SIGSEGVs on Linux at least up to v2.22.0; the TSV path
|
||||
works around that.
|
||||
- `port=USB1` selects the first USB DFU device; if you have several DFU
|
||||
devices attached, add `sn=<serial>` to disambiguate. List with
|
||||
`STM32_Programmer_CLI --list usb`.
|
||||
- The TSV's relative-path resolution is done from the TSV's own
|
||||
directory, which is why ST's two helper binaries are kept alongside
|
||||
it. OBL and BF artefact paths can be absolute or relative to that
|
||||
same directory.
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Override of CubeN6's app_openbootloader.c.
|
||||
*
|
||||
* The submodule version registers USART + USB + IWDG transports. We only
|
||||
* use USB + IWDG (FCs don't expose UART pins for bootloader purposes),
|
||||
* so dropping USART trims a few KiB of code and lets us skip building
|
||||
* the openbl_usart_cmd / usart_interface sources entirely.
|
||||
*
|
||||
* Picked up before the submodule version because -I. and the local
|
||||
* source path come first.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "openbl_core.h"
|
||||
|
||||
#include "openbl_usb_cmd.h"
|
||||
|
||||
#include "app_openbootloader.h"
|
||||
#include "usb_interface.h"
|
||||
#include "iwdg_interface.h"
|
||||
#include "otp_interface.h"
|
||||
|
||||
extern OPENBL_MemoryTypeDef RAM_Descriptor;
|
||||
extern OPENBL_MemoryTypeDef RAM_FlashLoader_Descriptor;
|
||||
extern OPENBL_MemoryTypeDef EXTERNAL_MEMORY_Descriptor;
|
||||
|
||||
static OPENBL_HandleTypeDef USB_Handle;
|
||||
static OPENBL_HandleTypeDef IWDG_Handle;
|
||||
|
||||
static OPENBL_OpsTypeDef USB_Ops =
|
||||
{
|
||||
OPENBL_USB_Configuration,
|
||||
OPENBL_USB_DeInit,
|
||||
OPENBL_USB_ProtocolDetection,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static OPENBL_OpsTypeDef IWDG_Ops =
|
||||
{
|
||||
OPENBL_IWDG_Configuration,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
uint16_t SpecialCmdList[SPECIAL_CMD_MAX_NUMBER] =
|
||||
{
|
||||
SPECIAL_CMD_DEFAULT
|
||||
};
|
||||
|
||||
uint16_t ExtendedSpecialCmdList[EXTENDED_SPECIAL_CMD_MAX_NUMBER] =
|
||||
{
|
||||
SPECIAL_CMD_DEFAULT
|
||||
};
|
||||
|
||||
void OpenBootloader_Init(void)
|
||||
{
|
||||
USB_Handle.p_Ops = &USB_Ops;
|
||||
USB_Handle.p_Cmd = NULL;
|
||||
if (OPENBL_RegisterInterface(&USB_Handle) != SUCCESS) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
IWDG_Handle.p_Ops = &IWDG_Ops;
|
||||
IWDG_Handle.p_Cmd = NULL;
|
||||
if (OPENBL_RegisterInterface(&IWDG_Handle) != SUCCESS) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
OPENBL_Init();
|
||||
|
||||
if (OPENBL_MEM_RegisterMemory(&RAM_Descriptor) != SUCCESS) {
|
||||
Error_Handler();
|
||||
}
|
||||
if (OPENBL_MEM_RegisterMemory(&RAM_FlashLoader_Descriptor) != SUCCESS) {
|
||||
Error_Handler();
|
||||
}
|
||||
if (OPENBL_MEM_RegisterMemory(&EXTERNAL_MEMORY_Descriptor) != SUCCESS) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
OPENBL_OTP_Init();
|
||||
}
|
||||
|
||||
void OpenBootloader_DeInit(void)
|
||||
{
|
||||
System_DeInit();
|
||||
}
|
||||
|
||||
void OpenBootloader_ProtocolDetection(void)
|
||||
{
|
||||
static uint32_t interface_detected = 0U;
|
||||
|
||||
if (interface_detected == 0U) {
|
||||
interface_detected = OPENBL_InterfaceDetection();
|
||||
if (interface_detected == 1U) {
|
||||
OPENBL_InterfacesDeInit();
|
||||
}
|
||||
}
|
||||
|
||||
if (interface_detected == 1U) {
|
||||
OPENBL_CommandProcess();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Override of CubeN6's external_memory_interface.c.
|
||||
*
|
||||
* ST's stock implementation reads function pointers from a fixed RAM
|
||||
* address (0x38000000) populated by a separately-staged flash loader
|
||||
* binary. That two-stage pattern is what STM32CubeProgrammer's TSV does
|
||||
* (slot 0x3 stages MX66UW1G45G_*-OBL.bin into RAM before slot 0x4 writes
|
||||
* to nor0). We don't want that — our OBL bundles the flash driver
|
||||
* statically and the EXTERNAL_MEMORY_Descriptor below dispatches direct
|
||||
* to flash_iface ops.
|
||||
*
|
||||
* This file is picked up by the Makefile in place of the submodule's
|
||||
* external_memory_interface.c; the rest of the OBL middleware
|
||||
* (openbl_core, openbl_mem, openbl_usb_cmd, the USB Device Library) is
|
||||
* pulled in unchanged via VPATH.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "stm32n6xx_hal.h"
|
||||
#include "openbl_mem.h"
|
||||
#include "openbootloader_conf.h"
|
||||
|
||||
#include "flash_iface.h"
|
||||
|
||||
#define MEMMAP_BASE EXT_MEMORY_START_ADDRESS /* 0x70000000 */
|
||||
|
||||
static uint32_t ext_init(uint32_t Address)
|
||||
{
|
||||
(void)Address;
|
||||
/* OBL_run_dfu_* in main.c calls flash_init() before entering the OBL
|
||||
* loop, so the driver is already up. Return success regardless. */
|
||||
return 1U;
|
||||
}
|
||||
|
||||
static uint8_t ext_read(uint32_t Address)
|
||||
{
|
||||
/* OBL Reads come through here a byte at a time. flash_memmap_on()
|
||||
* idempotently re-engages memory-mapped mode; reads are direct CPU
|
||||
* loads against the AXI window. */
|
||||
if (!flash_memmap_on()) {
|
||||
return 0xFFU;
|
||||
}
|
||||
return *(volatile uint8_t *)Address;
|
||||
}
|
||||
|
||||
static void ext_write(uint32_t Address, uint8_t *Data, uint32_t DataLength)
|
||||
{
|
||||
if (Address < MEMMAP_BASE) {
|
||||
return;
|
||||
}
|
||||
const uint32_t offset = Address - MEMMAP_BASE;
|
||||
(void)flash_program(offset, Data, DataLength);
|
||||
}
|
||||
|
||||
static void ext_jump(uint32_t Address)
|
||||
{
|
||||
/* OBL never jumps to external memory — the betaflight boot decision
|
||||
* lives in main.c and runs after the DFU loop ends via
|
||||
* NVIC_SystemReset, not via a direct jump. Stub. */
|
||||
(void)Address;
|
||||
}
|
||||
|
||||
static void ext_mass_erase(uint32_t Address)
|
||||
{
|
||||
/* The OBL DFU memory-map advertises only the BF slot (or the OBL
|
||||
* slot during recovery), so DFU-driven mass erase is bounded. We
|
||||
* implement it as "erase the advertised slot" rather than "erase
|
||||
* the whole chip" to avoid wiping the OBL by accident. */
|
||||
(void)Address;
|
||||
}
|
||||
|
||||
static void ext_sector_erase(uint32_t StartAddress, uint32_t EndAddress)
|
||||
{
|
||||
if (StartAddress < MEMMAP_BASE || EndAddress < StartAddress) {
|
||||
return;
|
||||
}
|
||||
const uint32_t offset = StartAddress - MEMMAP_BASE;
|
||||
const uint32_t length = EndAddress - StartAddress + 1U;
|
||||
(void)flash_erase_range(offset, length);
|
||||
}
|
||||
|
||||
static uint64_t ext_verify(uint32_t Address, uint32_t DataAddr,
|
||||
uint32_t DataLength, uint32_t Missalignement)
|
||||
{
|
||||
(void)Missalignement;
|
||||
if (Address < MEMMAP_BASE) {
|
||||
return 0U;
|
||||
}
|
||||
const uint32_t offset = Address - MEMMAP_BASE;
|
||||
/* DataAddr in this protocol points to a host-supplied buffer in RAM
|
||||
* (already received by OBL into one of its scratch areas). Compare
|
||||
* directly. Returns 0 on match, otherwise an OPENBL-coded error. */
|
||||
if (flash_verify(offset, (const uint8_t *)DataAddr, DataLength)) {
|
||||
return 0U;
|
||||
}
|
||||
return 1U;
|
||||
}
|
||||
|
||||
OPENBL_MemoryTypeDef EXTERNAL_MEMORY_Descriptor =
|
||||
{
|
||||
EXT_MEMORY_START_ADDRESS,
|
||||
EXT_MEMORY_END_ADDRESS,
|
||||
EXT_MEMORY_SIZE,
|
||||
EXTERNAL_MEMORY_AREA,
|
||||
ext_init,
|
||||
ext_read,
|
||||
ext_write,
|
||||
ext_jump,
|
||||
ext_mass_erase,
|
||||
ext_sector_erase,
|
||||
ext_verify
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Common XSPI flash driver interface for the Betaflight N6 OpenBootloader.
|
||||
*
|
||||
* Implementations live alongside this header (mx66uw1g45g.c, etc.) and are
|
||||
* selected at build time via the OBL_FLASH_DRIVER make variable. The OBL
|
||||
* never indirects through the 0x38000000 function pointer table that ST's
|
||||
* stock OBL relies on — the driver is statically linked.
|
||||
*
|
||||
* Operations expected:
|
||||
* init : XSPI controller + GPIO + chip command set bring-up; leave
|
||||
* the chip in 1S-1S-1S 4-byte addressing in indirect mode.
|
||||
* geometry : page_size_bytes, sector_size_bytes, total_size_bytes for
|
||||
* DFU descriptor advertisement.
|
||||
* erase : sector- or block-erase at byte-aligned offset.
|
||||
* program : page-aligned write (length <= page_size).
|
||||
* memmap_on/off : engage / disengage XSPI memory-mapped mode for read.
|
||||
* Reads use plain CPU loads against 0x70xxxxxx after
|
||||
* memmap_on.
|
||||
*
|
||||
* No error reporting beyond bool — recovery image is single-purpose; if
|
||||
* anything fails the only sensible response is to halt and let the user
|
||||
* pull power and retry via @FSBL DFU again.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t page_size_bytes;
|
||||
uint32_t sector_size_bytes;
|
||||
uint32_t total_size_bytes;
|
||||
} flash_geometry_t;
|
||||
|
||||
bool flash_init(void);
|
||||
void flash_deinit(void);
|
||||
|
||||
const flash_geometry_t *flash_get_geometry(void);
|
||||
|
||||
bool flash_erase_sector(uint32_t offset);
|
||||
bool flash_program_page(uint32_t offset, const uint8_t *data, uint32_t length);
|
||||
|
||||
bool flash_memmap_on(void);
|
||||
bool flash_memmap_off(void);
|
||||
|
||||
/* Convenience: erase enough sectors to cover [offset, offset+length). */
|
||||
bool flash_erase_range(uint32_t offset, uint32_t length);
|
||||
|
||||
/* Convenience: program a buffer larger than one page, splitting into
|
||||
* page-aligned writes. Caller must have erased first. */
|
||||
bool flash_program(uint32_t offset, const uint8_t *data, uint32_t length);
|
||||
|
||||
/* Verify by re-reading via memory-mapped mode. Returns true on match. */
|
||||
bool flash_verify(uint32_t offset, const uint8_t *data, uint32_t length);
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Macronix MX66UW1G45G — 1 Gb octal STR/DTR flash driver for the
|
||||
* Betaflight N6 OpenBootloader.
|
||||
*
|
||||
* Targets the wiring on the STM32N6570-DK reference design and pin-
|
||||
* compatible boards: XSPI2 + XSPIM_P2 + GPIOM AF11 + NCS1. Boards with
|
||||
* different XSPI controllers, IO ports, or chip selects need their own
|
||||
* driver source (or this one parameterised via the OBL_FLASH_DRIVER
|
||||
* config knob).
|
||||
*
|
||||
* XSPI bring-up + 1S-1S-1S soft reset + memory-mapped engagement is
|
||||
* lifted from lib/main/STM32/n6_fsbl/main.c (proven across multiple boots
|
||||
* during the FSBL stub work). The new pieces here are the indirect-mode
|
||||
* write primitives (write enable / sector erase / block erase / page
|
||||
* program) and a memory-mapped-mode-off hook so the driver can flip
|
||||
* between read-via-memmap and write-via-indirect cleanly.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "stm32n6xx_hal.h"
|
||||
|
||||
#include "flash_iface.h"
|
||||
|
||||
/* ---------- chip command set (1S-1S-1S, post-reset state) ----------- */
|
||||
#define MX66_CMD_RESET_ENABLE 0x66U
|
||||
#define MX66_CMD_RESET 0x99U
|
||||
#define MX66_CMD_RDSR 0x05U /* read status register */
|
||||
#define MX66_SR_WIP 0x01U /* write-in-progress bit */
|
||||
#define MX66_CMD_WREN 0x06U /* write enable */
|
||||
#define MX66_CMD_READ_4B 0x0CU /* 4-byte FAST_READ */
|
||||
#define MX66_CMD_READ_4B_DUMMY 8U
|
||||
#define MX66_CMD_PP_4B 0x12U /* 4-byte page program */
|
||||
#define MX66_CMD_SE_4B 0x21U /* 4-byte 4 KiB sector erase */
|
||||
#define MX66_CMD_BE_4B 0xDCU /* 4-byte 64 KiB block erase */
|
||||
|
||||
#define MX66_PAGE_SIZE 256U
|
||||
#define MX66_SECTOR_SIZE (4U * 1024U)
|
||||
#define MX66_TOTAL_SIZE (128U * 1024U * 1024U) /* 1 Gb = 128 MiB */
|
||||
|
||||
#define MX66_RETRIES 3
|
||||
#define SETTLE_DELAY_MS 600
|
||||
#define MX66_TIMEOUT_PROGRAM_MS 50
|
||||
#define MX66_TIMEOUT_SECTOR_MS 400
|
||||
#define MX66_TIMEOUT_BLOCK_MS 2000
|
||||
#define MX66_TIMEOUT_GENERIC_MS 1000
|
||||
|
||||
#define MX66_MEMMAP_BASE 0x70000000U
|
||||
|
||||
static XSPI_HandleTypeDef hxspi2;
|
||||
static const flash_geometry_t geometry = {
|
||||
.page_size_bytes = MX66_PAGE_SIZE,
|
||||
.sector_size_bytes = MX66_SECTOR_SIZE,
|
||||
.total_size_bytes = MX66_TOTAL_SIZE,
|
||||
};
|
||||
static bool memmap_active;
|
||||
|
||||
/* HAL_XSPI_MspInit (peripheral + IO manager + GPIO N clocks, GPION pins
|
||||
* to AF9_XSPIM_P2) lives in stm32n6xx_hal_msp.c — board-level config
|
||||
* shared with whatever flash chip happens to be wired to XSPI2. A
|
||||
* manufacturer using a different XSPI controller or pin map provides
|
||||
* their own MSP file under the per-config build. */
|
||||
|
||||
static HAL_StatusTypeDef mx66_wait_ready(uint32_t timeout_ms)
|
||||
{
|
||||
XSPI_RegularCmdTypeDef cmd = {0};
|
||||
uint8_t status;
|
||||
uint32_t tickstart = HAL_GetTick();
|
||||
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
|
||||
cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
|
||||
cmd.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
|
||||
cmd.Instruction = MX66_CMD_RDSR;
|
||||
cmd.DataMode = HAL_XSPI_DATA_1_LINE;
|
||||
cmd.DataLength = 1;
|
||||
cmd.AddressMode = HAL_XSPI_ADDRESS_NONE;
|
||||
cmd.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
|
||||
cmd.DummyCycles = 0;
|
||||
|
||||
do {
|
||||
if (HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
if (HAL_XSPI_Receive(&hxspi2, &status, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
if ((status & MX66_SR_WIP) == 0) {
|
||||
return HAL_OK;
|
||||
}
|
||||
} while ((HAL_GetTick() - tickstart) < timeout_ms);
|
||||
|
||||
return HAL_TIMEOUT;
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mx66_simple_cmd_1s(uint8_t opcode)
|
||||
{
|
||||
XSPI_RegularCmdTypeDef cmd = {0};
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
|
||||
cmd.IOSelect = HAL_XSPI_SELECT_IO_7_0;
|
||||
cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
|
||||
cmd.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
|
||||
cmd.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
|
||||
cmd.Instruction = opcode;
|
||||
cmd.AddressMode = HAL_XSPI_ADDRESS_NONE;
|
||||
cmd.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
|
||||
cmd.DataMode = HAL_XSPI_DATA_NONE;
|
||||
cmd.DummyCycles = 0;
|
||||
cmd.DQSMode = HAL_XSPI_DQS_DISABLE;
|
||||
return HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mx66_cmd_8dtr(uint8_t opcode)
|
||||
{
|
||||
XSPI_RegularCmdTypeDef cmd = {0};
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
|
||||
cmd.IOSelect = HAL_XSPI_SELECT_IO_7_0;
|
||||
cmd.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES;
|
||||
cmd.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS;
|
||||
cmd.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE;
|
||||
cmd.Instruction = ((uint16_t)opcode << 8) | (uint8_t)(~opcode);
|
||||
cmd.AddressMode = HAL_XSPI_ADDRESS_NONE;
|
||||
cmd.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
|
||||
cmd.DataMode = HAL_XSPI_DATA_NONE;
|
||||
cmd.DummyCycles = 0;
|
||||
cmd.DQSMode = HAL_XSPI_DQS_DISABLE;
|
||||
return HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mx66_software_reset(void)
|
||||
{
|
||||
for (int i = 0; i < MX66_RETRIES; i++) {
|
||||
(void)mx66_cmd_8dtr(MX66_CMD_RESET_ENABLE);
|
||||
(void)mx66_cmd_8dtr(MX66_CMD_RESET);
|
||||
HAL_Delay(1);
|
||||
|
||||
if (mx66_simple_cmd_1s(MX66_CMD_RESET_ENABLE) == HAL_OK) {
|
||||
if (mx66_simple_cmd_1s(MX66_CMD_RESET) == HAL_OK) {
|
||||
HAL_Delay(1);
|
||||
return mx66_wait_ready(MX66_TIMEOUT_GENERIC_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mx66_write_enable(void)
|
||||
{
|
||||
HAL_StatusTypeDef st = mx66_simple_cmd_1s(MX66_CMD_WREN);
|
||||
return st;
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mx66_addr_cmd_1s(uint8_t opcode, uint32_t address,
|
||||
uint32_t timeout_ms)
|
||||
{
|
||||
if (mx66_write_enable() != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
XSPI_RegularCmdTypeDef cmd = {0};
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
|
||||
cmd.IOSelect = HAL_XSPI_SELECT_IO_7_0;
|
||||
cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
|
||||
cmd.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
|
||||
cmd.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
|
||||
cmd.Instruction = opcode;
|
||||
cmd.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
|
||||
cmd.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
|
||||
cmd.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
|
||||
cmd.Address = address;
|
||||
cmd.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
|
||||
cmd.DataMode = HAL_XSPI_DATA_NONE;
|
||||
cmd.DummyCycles = 0;
|
||||
cmd.DQSMode = HAL_XSPI_DQS_DISABLE;
|
||||
|
||||
if (HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return mx66_wait_ready(timeout_ms);
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mx66_page_program(uint32_t address, const uint8_t *data,
|
||||
uint32_t length)
|
||||
{
|
||||
if (length == 0 || length > MX66_PAGE_SIZE) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
/* Page-program straddles must be split by the caller — chip wraps the
|
||||
* write at the page boundary, which would corrupt earlier bytes in
|
||||
* the same page. */
|
||||
if (((address & (MX66_PAGE_SIZE - 1)) + length) > MX66_PAGE_SIZE) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
if (mx66_write_enable() != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
XSPI_RegularCmdTypeDef cmd = {0};
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG;
|
||||
cmd.IOSelect = HAL_XSPI_SELECT_IO_7_0;
|
||||
cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
|
||||
cmd.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
|
||||
cmd.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
|
||||
cmd.Instruction = MX66_CMD_PP_4B;
|
||||
cmd.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
|
||||
cmd.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
|
||||
cmd.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
|
||||
cmd.Address = address;
|
||||
cmd.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
|
||||
cmd.DataMode = HAL_XSPI_DATA_1_LINE;
|
||||
cmd.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
|
||||
cmd.DataLength = length;
|
||||
cmd.DummyCycles = 0;
|
||||
cmd.DQSMode = HAL_XSPI_DQS_DISABLE;
|
||||
|
||||
if (HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
if (HAL_XSPI_Transmit(&hxspi2, (uint8_t *)data, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return mx66_wait_ready(MX66_TIMEOUT_PROGRAM_MS);
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef xspi2_init_controller(void)
|
||||
{
|
||||
XSPIM_CfgTypeDef mgr = {0};
|
||||
|
||||
hxspi2.Instance = XSPI2;
|
||||
hxspi2.Init.FifoThresholdByte = 4;
|
||||
hxspi2.Init.MemoryMode = HAL_XSPI_SINGLE_MEM;
|
||||
hxspi2.Init.MemoryType = HAL_XSPI_MEMTYPE_MACRONIX;
|
||||
hxspi2.Init.MemorySize = HAL_XSPI_SIZE_1GB;
|
||||
hxspi2.Init.ChipSelectHighTimeCycle = 2;
|
||||
hxspi2.Init.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE;
|
||||
hxspi2.Init.ClockMode = HAL_XSPI_CLOCK_MODE_0;
|
||||
hxspi2.Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
|
||||
hxspi2.Init.ClockPrescaler = 0;
|
||||
hxspi2.Init.SampleShifting = HAL_XSPI_SAMPLE_SHIFT_NONE;
|
||||
hxspi2.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_DISABLE;
|
||||
hxspi2.Init.ChipSelectBoundary = HAL_XSPI_BONDARYOF_NONE;
|
||||
hxspi2.Init.MaxTran = 0;
|
||||
hxspi2.Init.Refresh = 0;
|
||||
hxspi2.Init.MemorySelect = HAL_XSPI_CSSEL_NCS1;
|
||||
if (HAL_XSPI_Init(&hxspi2) != HAL_OK) {
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
mgr.nCSOverride = HAL_XSPI_CSSEL_OVR_NCS1;
|
||||
mgr.IOPort = HAL_XSPIM_IOPORT_2;
|
||||
mgr.Req2AckTime = 1;
|
||||
return HAL_XSPIM_Config(&hxspi2, &mgr, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
bool flash_init(void)
|
||||
{
|
||||
if (xspi2_init_controller() != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
HAL_Delay(SETTLE_DELAY_MS);
|
||||
|
||||
if (mx66_software_reset() != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
HAL_Delay(SETTLE_DELAY_MS);
|
||||
|
||||
memmap_active = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void flash_deinit(void)
|
||||
{
|
||||
/* Leave XSPI2 fully configured + memory-mapped so the post-reset
|
||||
* boot decision can read nor0 0x0 directly without re-init churn.
|
||||
* Callers wanting a true peripheral teardown should HAL_XSPI_DeInit
|
||||
* themselves; OBL never does. */
|
||||
}
|
||||
|
||||
const flash_geometry_t *flash_get_geometry(void)
|
||||
{
|
||||
return &geometry;
|
||||
}
|
||||
|
||||
bool flash_memmap_on(void)
|
||||
{
|
||||
if (memmap_active) {
|
||||
return true;
|
||||
}
|
||||
|
||||
XSPI_RegularCmdTypeDef cmd = {0};
|
||||
cmd.IOSelect = HAL_XSPI_SELECT_IO_7_0;
|
||||
cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
|
||||
cmd.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS;
|
||||
cmd.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
|
||||
cmd.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
|
||||
cmd.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
|
||||
cmd.AddressDTRMode = HAL_XSPI_ADDRESS_DTR_DISABLE;
|
||||
cmd.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE;
|
||||
cmd.DataMode = HAL_XSPI_DATA_1_LINE;
|
||||
cmd.DataDTRMode = HAL_XSPI_DATA_DTR_DISABLE;
|
||||
cmd.DummyCycles = 0;
|
||||
cmd.DQSMode = HAL_XSPI_DQS_DISABLE;
|
||||
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_READ_CFG;
|
||||
cmd.Instruction = MX66_CMD_READ_4B;
|
||||
cmd.DummyCycles = MX66_CMD_READ_4B_DUMMY;
|
||||
if (HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
cmd.DummyCycles = 0;
|
||||
|
||||
cmd.OperationType = HAL_XSPI_OPTYPE_WRITE_CFG;
|
||||
cmd.Instruction = MX66_CMD_PP_4B; /* placeholder — never issued */
|
||||
if (HAL_XSPI_Command(&hxspi2, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
XSPI_MemoryMappedTypeDef mm = {0};
|
||||
mm.TimeOutActivation = HAL_XSPI_TIMEOUT_COUNTER_DISABLE;
|
||||
/* Prefetch off — speculative reads against a chip in an unexpected
|
||||
* mode would stall the AXI bus indefinitely. Bounded stalls only. */
|
||||
mm.NoPrefetchData = HAL_XSPI_AUTOMATIC_PREFETCH_DISABLE;
|
||||
mm.NoPrefetchAXI = HAL_XSPI_AXI_PREFETCH_DISABLE;
|
||||
if (HAL_XSPI_MemoryMapped(&hxspi2, &mm) != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memmap_active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flash_memmap_off(void)
|
||||
{
|
||||
if (!memmap_active) {
|
||||
return true;
|
||||
}
|
||||
/* Aborting an in-flight memory-mapped session is the documented way
|
||||
* to drop the controller back to indirect command mode. */
|
||||
if (HAL_XSPI_Abort(&hxspi2) != HAL_OK) {
|
||||
return false;
|
||||
}
|
||||
memmap_active = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flash_erase_sector(uint32_t offset)
|
||||
{
|
||||
if (!flash_memmap_off()) {
|
||||
return false;
|
||||
}
|
||||
return mx66_addr_cmd_1s(MX66_CMD_SE_4B, offset, MX66_TIMEOUT_SECTOR_MS) == HAL_OK;
|
||||
}
|
||||
|
||||
bool flash_program_page(uint32_t offset, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
if (!flash_memmap_off()) {
|
||||
return false;
|
||||
}
|
||||
return mx66_page_program(offset, data, length) == HAL_OK;
|
||||
}
|
||||
|
||||
bool flash_erase_range(uint32_t offset, uint32_t length)
|
||||
{
|
||||
const uint32_t mask = MX66_SECTOR_SIZE - 1;
|
||||
if (offset & mask) {
|
||||
return false;
|
||||
}
|
||||
uint32_t end = offset + length;
|
||||
/* Round end up to the next sector boundary. */
|
||||
end = (end + mask) & ~mask;
|
||||
|
||||
for (uint32_t a = offset; a < end; a += MX66_SECTOR_SIZE) {
|
||||
if (!flash_erase_sector(a)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flash_program(uint32_t offset, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
while (length) {
|
||||
uint32_t chunk = MX66_PAGE_SIZE - (offset & (MX66_PAGE_SIZE - 1));
|
||||
if (chunk > length) {
|
||||
chunk = length;
|
||||
}
|
||||
if (!flash_program_page(offset, data, chunk)) {
|
||||
return false;
|
||||
}
|
||||
offset += chunk;
|
||||
data += chunk;
|
||||
length -= chunk;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flash_verify(uint32_t offset, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
if (!flash_memmap_on()) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t *flash = (const uint8_t *)(MX66_MEMMAP_BASE + offset);
|
||||
return memcmp(flash, data, length) == 0;
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* Betaflight N6 OpenBootloader — entry, boot decision, and S→NS
|
||||
* hand-off trampoline.
|
||||
*
|
||||
* Loaded by the boot ROM into AXISRAM2 secure at 0x34180400 from XSPI
|
||||
* nor0 0x0 on cold boot, or via USB DFU @FSBL alt during recovery.
|
||||
* The two cases are distinguished by checking nor0 0x0 for the signed
|
||||
* FSBL header magic.
|
||||
*
|
||||
* Boot decision:
|
||||
* - nor0 0x0 has no valid OBL header → recovery DFU (writes nor0 0x0)
|
||||
* - RCC->RSR has IWDGRSTF/LCKRSTF/
|
||||
* WWDGRSTF set → previous BF crashed → DFU
|
||||
* - BF vector table at 0x70100000
|
||||
* invalid → DFU
|
||||
* - otherwise → arm IWDG, BXNS to BF in NS
|
||||
*
|
||||
* The DFU loop itself is supplied by the OBL middleware (CubeN6
|
||||
* OpenBootloader, pulled in via Makefile VPATH and re-entered via
|
||||
* obl_app.c::OBL_run_dfu_*). This file owns the boot decision, IWDG
|
||||
* arming, and the BF jump trampoline. OBL is built with -mcmse so the
|
||||
* CMSIS peripheral pointers (RCC, RIFSC, GPIOx, DBGMCU, RISAF*, …)
|
||||
* resolve to their secure-alias bases automatically.
|
||||
*/
|
||||
|
||||
#include <arm_cmse.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "flash_iface.h"
|
||||
|
||||
/* ---------- Layout constants ---------- */
|
||||
#define XSPI_NOR_BASE 0x70000000U
|
||||
#define BF_VECTOR_BASE 0x70100000U
|
||||
#define BF_SLOT_OFFSET 0x00100000U /* 1 MiB OBL reserve before BF */
|
||||
|
||||
/* "STM2" little-endian — the signed FSBL header magic at offset 0. */
|
||||
#define STM2_MAGIC_LE 0x324D5453U
|
||||
|
||||
/* IWDG window: ~10 s @ LSI 32 kHz / (256 × 1250). BF must refresh from
|
||||
* its scheduler-driven watchdog task before this expires. */
|
||||
#define OBL_IWDG_PRESCALER IWDG_PRESCALER_256
|
||||
#define OBL_IWDG_RELOAD 1250U
|
||||
#define OBL_IWDG_WINDOW 1250U /* window disabled (= reload) */
|
||||
|
||||
IWDG_HandleTypeDef hiwdg;
|
||||
|
||||
/* Forward-declared from obl_app.c. Both run the OBL middleware DFU
|
||||
* loop and never return — they end via NVIC_SystemReset. */
|
||||
__attribute__((noreturn)) void OBL_run_dfu_normal(void);
|
||||
__attribute__((noreturn)) void OBL_run_dfu_recovery(void);
|
||||
|
||||
static void SystemClock_Config(void);
|
||||
extern void HAL_MspInit(void);
|
||||
|
||||
/* ---------- Boot decision helpers ---------- */
|
||||
|
||||
static bool nor0_has_valid_obl(void)
|
||||
{
|
||||
if (!flash_memmap_on()) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t magic = *(volatile uint32_t *)(XSPI_NOR_BASE);
|
||||
return magic == STM2_MAGIC_LE;
|
||||
}
|
||||
|
||||
static bool bf_vector_table_valid(void)
|
||||
{
|
||||
if (!flash_memmap_on()) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t bf_sp = *(volatile uint32_t *)(BF_VECTOR_BASE);
|
||||
const uint32_t bf_pc = *(volatile uint32_t *)(BF_VECTOR_BASE + 4U);
|
||||
|
||||
/* SP must point into AXISRAM (top nibble 0x2). */
|
||||
if ((bf_sp & 0xF8000000U) != 0x20000000U) {
|
||||
return false;
|
||||
}
|
||||
/* Reset_Handler must live in the BF XIP slot (top 12 bits == 0x701)
|
||||
* with the thumb bit set. */
|
||||
if ((bf_pc & 0xFFF00000U) != BF_VECTOR_BASE) {
|
||||
return false;
|
||||
}
|
||||
if ((bf_pc & 1U) == 0U) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- IWDG ---------- */
|
||||
|
||||
static void iwdg_start(void)
|
||||
{
|
||||
hiwdg.Instance = IWDG;
|
||||
hiwdg.Init.Prescaler = OBL_IWDG_PRESCALER;
|
||||
hiwdg.Init.Reload = OBL_IWDG_RELOAD;
|
||||
hiwdg.Init.Window = OBL_IWDG_WINDOW;
|
||||
if (HAL_IWDG_Init(&hiwdg) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
/* IWDG cannot be disabled until system reset. BF must call
|
||||
* HAL_IWDG_Refresh() from its scheduler, or the timeout fires NRST
|
||||
* and OBL routes the next boot to DFU via RSR.IWDGRSTF. */
|
||||
}
|
||||
|
||||
/* ---------- Jump trampoline ---------- */
|
||||
|
||||
/* All-8-CIDs read + write enable for RISAF region CIDCFGR. Equivalent to
|
||||
* RDENC0_Msk | RDENC1_Msk | ... | RDENC7_Msk | WRENC0_Msk | ... | WRENC7_Msk
|
||||
* but kept as a single hex literal because the OR'd form is noisy. */
|
||||
#define RISAF_CIDCFGR_ALL_CIDS_RW 0x00FF00FFUL
|
||||
|
||||
/* Configure one base region of a RISAF instance. `region` is 0-indexed
|
||||
* (the CMSIS RISAF_TypeDef exposes the per-region structs as REG[0..14]).
|
||||
* Per RM §7.5: write STARTR/ENDR/CIDCFGR before CFGR so BREN gates the
|
||||
* region atomically. CIDCFGR opens RDENC[0..7] and WRENC[0..7]. */
|
||||
static inline void risaf_region_config(RISAF_TypeDef *risaf, uint32_t region,
|
||||
uint32_t start, uint32_t end,
|
||||
bool sec)
|
||||
{
|
||||
risaf->REG[region].STARTR = start;
|
||||
risaf->REG[region].ENDR = end;
|
||||
risaf->REG[region].CIDCFGR = RISAF_CIDCFGR_ALL_CIDS_RW;
|
||||
risaf->REG[region].CFGR = (sec ? RISAF_REGx_CFGR_SEC_Msk : 0UL)
|
||||
| RISAF_REGx_CFGR_BREN_Msk;
|
||||
}
|
||||
|
||||
static __attribute__((noreturn)) void jump_to_bf(void)
|
||||
{
|
||||
const uint32_t bf_sp = *(volatile uint32_t *)(BF_VECTOR_BASE);
|
||||
const uint32_t bf_pc = *(volatile uint32_t *)(BF_VECTOR_BASE + 4U);
|
||||
|
||||
HAL_SuspendTick();
|
||||
__disable_irq();
|
||||
|
||||
/* Leave XSPI memory-mapped — BF runs XIP from 0x70100000. */
|
||||
SCB_DisableICache();
|
||||
SCB_DisableDCache();
|
||||
|
||||
/* === Hand off to BF in NS state ===========================
|
||||
*
|
||||
* BF runs as a Non-Secure application; OBL is the only S-state
|
||||
* code on the device. RISAFs must be programmed so the NS CPU
|
||||
* can reach BF's data (AXISRAM1) and code (XSPI memory-mapped)
|
||||
* before BXNS. Boot-ROM default leaves every RISAF region
|
||||
* disabled, which per RM §3.5.7 means "Locations outside any
|
||||
* enabled region belong to the secure OS" — so without explicit
|
||||
* opens, NS access is silently RAZ.
|
||||
*
|
||||
* Order matters: RISAFs are configured AFTER OBL's last XSPI
|
||||
* read (bf_sp/bf_pc above) so OBL's S-state reads still see the
|
||||
* boot-ROM permissive default. Once XSPI's RISAF12 region is
|
||||
* flipped to NS, OBL itself can no longer read XSPI; only BF
|
||||
* (NS) can. AXISRAM2 (where OBL lives) is left at the boot-ROM
|
||||
* default (secure-only) so OBL keeps executing through the BXNS.
|
||||
*
|
||||
* RISAF6 is deliberately not programmed — ST's
|
||||
* Template_Isolation_XIP reference for the equivalent topology
|
||||
* (FSBL → AppliSecure → AppliNS in XSPI) leaves it alone too. */
|
||||
|
||||
/* RISAF peripheral clock — boot ROM leaves AHB3ENR.RISAFEN=0,
|
||||
* which makes every access to RISAF register space stall the bus. */
|
||||
RCC->AHB3ENSR = RCC_AHB3ENSR_RISAFENS;
|
||||
(void)RCC->AHB3ENR;
|
||||
|
||||
/* AXISRAM1 + AXISRAM3..6 clocks — boot ROM only clocks AXISRAM2
|
||||
* (where OBL itself runs). The NS BF lives in AXISRAM1 (data+stack)
|
||||
* and AXISRAM3..6 (D2_RAM); without these clocks enabled, any NS
|
||||
* access to 0x24010000+ or 0x24200000+ bus-faults. Idempotent with
|
||||
* the SystemInit set. */
|
||||
RCC->MEMENSR = RCC_MEMENSR_AXISRAM1ENS | RCC_MEMENSR_AXISRAM2ENS
|
||||
| RCC_MEMENSR_AXISRAM3ENS | RCC_MEMENSR_AXISRAM4ENS
|
||||
| RCC_MEMENSR_AXISRAM5ENS | RCC_MEMENSR_AXISRAM6ENS;
|
||||
(void)RCC->MEMENR;
|
||||
|
||||
/* Slave-side RISAFs — open the resources BF (NS) needs.
|
||||
*
|
||||
* Boot-ROM default: every RISAF region disabled. RM §3.5.7 says a
|
||||
* memory location covered by a RISAF but outside any enabled region
|
||||
* "belongs to the secure OS", i.e., NS access bus-faults. So every
|
||||
* slave-RISAF-protected bank BF touches needs an explicit NS region.
|
||||
*
|
||||
* RISAF3 (AXISRAM2, 1 MiB): region offset 0x91000..0xFFFFF NS
|
||||
* (= 0x24191000..0x241FFFFF, 444 KiB)
|
||||
* — BF's data/bss/stack/.ram_code. The
|
||||
* low half stays uncovered → S-only →
|
||||
* OBL's RAM at 0x34180400+ is protected.
|
||||
* RISAF12 (XSPI2, 256 MiB): region covers offsets 0x100000..end
|
||||
* NS, leaving the OBL slot at offsets
|
||||
* 0x00000..0xFFFFF uncovered. Per RM
|
||||
* §3.5.7 an uncovered range defaults
|
||||
* to S-only, so accidental NS deref of
|
||||
* 0x70000000..0x700FFFFF bus-faults
|
||||
* instead of returning OBL image bytes.
|
||||
*
|
||||
* Intentionally NOT programmed:
|
||||
* - RISAF2 (AXISRAM1, 1 MiB): NS access to this bank silently RAZ's
|
||||
* despite a correctly-programmed RISAF2 region 0 — root cause
|
||||
* unknown after exhaustive diagnostic (boot-ROM RISAF2 left at
|
||||
* default, RIFSC SEC bits clear, IASR clean, CIDCFGR opens CID 1
|
||||
* which the M55 presents). Empirical workaround: BF moved to
|
||||
* AXISRAM2 high half (above OBL's 64 KiB block) via RISAF3, which
|
||||
* matches the ST template pattern of "S app in AXISRAM1, NS app
|
||||
* in AXISRAM2".
|
||||
* - AXISRAM3..6 (NPURAM0..3, 0x24200000..0x243BFFFF, 1792 KiB):
|
||||
* no slave RISAF exists for this range; bank security is gated by
|
||||
* RIFSC->RISC_SECCFGRx[5] bits 17..20, which system_stm32n6xx_obl.c
|
||||
* clears in its RIFSC open loop. NS access works once enabled. */
|
||||
/* RISAF3 region 0 (AXISRAM2 NS slice for BF's data/bss/stack/.ram_code).
|
||||
* BF lives in AXISRAM2 above the 64 KiB OBL block — RAM origin at
|
||||
* 0x24191000, length 444 KiB. RISAF3 granularity is 4 KiB so the
|
||||
* region is offset 0x91000..0xFFFFF (relative to AXISRAM2 base
|
||||
* 0x24100000). The lower half of AXISRAM2 (offsets 0x00000..0x90FFF,
|
||||
* which includes OBL's secure RAM) stays outside any enabled region
|
||||
* and defaults to S-only per RM §3.5.7 — protecting OBL from any
|
||||
* stray BF NS reach into its address space.
|
||||
*
|
||||
* RISAF2 (AXISRAM1) and RISAF6 are intentionally not programmed —
|
||||
* BF doesn't use AXISRAM1 in this layout, and RISAF6 matches the
|
||||
* ST Template_Isolation_XIP reference. */
|
||||
risaf_region_config(RISAF3, 0U, 0x00091000UL, 0x000FFFFFUL, false);
|
||||
risaf_region_config(RISAF12, 0U, 0x00100000UL, 0x0FFFFFFFUL, false);
|
||||
|
||||
/* === Per-CPU TrustZone setup for the NS application ====
|
||||
*
|
||||
* NSACR (S-only): grant NS code access to FPU coprocessor banks
|
||||
* (CP10 + CP11). Without this, BF's first FPU instruction in NS
|
||||
* state takes a NOCP UsageFault.
|
||||
*
|
||||
* CPACR_NS: NS-bank CPACR is at reset value (FPU access disabled
|
||||
* for NS). BF's __libc_init_array runs before its own systemInit
|
||||
* and may emit FPU instructions (newlib helpers, struct passing).
|
||||
* Enable CP10/CP11 in the NS bank now so the early NS code can run.
|
||||
*
|
||||
* AIRCR.BFHFNMINS: set to 1 so NS HardFault / BusFault / NMI route to
|
||||
* NS handlers. Required for BF to diagnose its own faults via NS
|
||||
* HardFault_Handler. Without this NS faults route to S where OBL has
|
||||
* only Default_Handler (infinite loop) — chip wedges silently. */
|
||||
SCB->NSACR = SCB_NSACR_CP10_Msk | SCB_NSACR_CP11_Msk;
|
||||
SCB_NS->CPACR = (3UL << 20) | (3UL << 22); /* CP10/CP11 full access in NS bank */
|
||||
SCB->AIRCR = (SCB->AIRCR & ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_BFHFNMINS_Msk))
|
||||
| (0x05FAUL << SCB_AIRCR_VECTKEY_Pos)
|
||||
| SCB_AIRCR_BFHFNMINS_Msk;
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* SAU (Security Attribution Unit) — REQUIRED for BXNS to NS code.
|
||||
* Per RM0486 §3.5.1: "at reset, the SAU unilaterally determines
|
||||
* that the entire memory is secure." Without SAU regions defining
|
||||
* NS, BXNS to 0x7010xxxx resolves the destination as S → SecureFault.
|
||||
*
|
||||
* Per-region NS instead of ALLNS=1: with explicit regions, addresses
|
||||
* outside any region stay S-default — which is what OBL needs for
|
||||
* its S-state accesses at 0x34xxxxxx, 0x50xxxxxx, 0x54xxxxxx etc.
|
||||
*
|
||||
* Region 0: AXISRAM1 (0x24000000..0x240FFFFF) NS — BF main RAM
|
||||
* Region 1: AHB/APB NS-alias peripherals (0x40000000..0x4FFFFFFF)
|
||||
* Region 2: XSPI memory-mapped (0x70100000..0x7FFFFFFF) NS — skip
|
||||
* the OBL slot at 0x70000000..0x700FFFFF so a stray NS
|
||||
* deref of an OBL address is rejected by the SAU before
|
||||
* it reaches the bus (matches the RISAF12 region split).
|
||||
* Region 3: AXISRAM3..6 (0x24200000..0x243BFFFF) NS — D2_RAM /
|
||||
* LCD framebuffer. AXISRAM2 NS (0x24100000..0x241FFFFF)
|
||||
* is intentionally NOT covered: nothing in BF uses it,
|
||||
* and leaving it outside any SAU region means a stray
|
||||
* NS access bus-faults instead of reaching the bank.
|
||||
*
|
||||
* RBAR/RLAR are 32-byte granularity (low 5 bits ignored).
|
||||
*
|
||||
* Per UM3234 the boot ROM also configures SAU. Disable the unit and
|
||||
* clear every region (RLAR.ENABLE=0) before programming ours,
|
||||
* otherwise leftover boot-ROM regions ≥ 4 keep their classification
|
||||
* and can shadow what we configure. */
|
||||
{
|
||||
SAU->CTRL = 0;
|
||||
for (uint32_t i = 0; i < 8U; i++) {
|
||||
SAU->RNR = i;
|
||||
SAU->RBAR = 0;
|
||||
SAU->RLAR = 0;
|
||||
}
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
const struct { uint32_t base; uint32_t limit; } regs[] = {
|
||||
/* BF RAM lives in AXISRAM2 NS above OBL's 64 KiB block.
|
||||
* AXISRAM2 lower half + OBL's secure region stays outside
|
||||
* any SAU region → SAU-S by default, blocking any stray
|
||||
* NS deref of OBL's address space. */
|
||||
{ 0x24191000UL, 0x241FFFE0UL }, /* AXISRAM2 high (BF) */
|
||||
{ 0x40000000UL, 0x4FFFFFE0UL }, /* NS peripherals */
|
||||
{ 0x70100000UL, 0x7FFFFFE0UL }, /* XSPI (excl. OBL) */
|
||||
{ 0x24200000UL, 0x243BFFE0UL }, /* AXISRAM3..6 / D2RAM */
|
||||
};
|
||||
for (uint32_t i = 0; i < (uint32_t)(sizeof(regs) / sizeof(regs[0])); i++) {
|
||||
SAU->RNR = i;
|
||||
SAU->RBAR = regs[i].base;
|
||||
SAU->RLAR = regs[i].limit | SAU_RLAR_ENABLE_Msk;
|
||||
}
|
||||
SAU->CTRL = SAU_CTRL_ENABLE_Msk;
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
/* NS VTOR — point BF at its vector table at the XIP slot base. */
|
||||
SCB_NS->VTOR = BF_VECTOR_BASE;
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* Clear every NVIC IRQ enable + pending bit before the hand-off so
|
||||
* no S-side leftover interrupt fires the instant the NS app does
|
||||
* `__enable_irq()`. BF's own startup clears these again; doing it
|
||||
* here protects the transition window. */
|
||||
for (uint32_t i = 0; i < (uint32_t)(sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0])); i++) {
|
||||
NVIC->ICER[i] = 0xFFFFFFFFUL;
|
||||
NVIC->ICPR[i] = 0xFFFFFFFFUL;
|
||||
}
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* Re-target every external IRQ to NS so BF can enable/handle them
|
||||
* through the NS NVIC bank. Reset default is ITNS = 0 (all IRQs
|
||||
* Secure-targeted), which would route every peripheral IRQ (USB,
|
||||
* GPDMA, EXTI, TIMx, …) into S handlers OBL doesn't implement —
|
||||
* typically a tight Default_Handler loop. With NS-targeted IRQs the
|
||||
* NVIC banks the configuration registers to the NS bank so BF's
|
||||
* NVIC_EnableIRQ / SetPriority / SetTargetState calls land where
|
||||
* the IRQ actually fires. */
|
||||
for (uint32_t i = 0; i < (uint32_t)(sizeof(NVIC->ITNS) / sizeof(NVIC->ITNS[0])); i++) {
|
||||
NVIC->ITNS[i] = 0xFFFFFFFFUL;
|
||||
}
|
||||
|
||||
/* SysTick is a system exception, not an external IRQ — NVIC->ITNS
|
||||
* doesn't cover it. Its NS/S target lives in ICSR.STTNS (bit 24,
|
||||
* Secure-only writable). Without this, BF's NS SysTick fires the
|
||||
* Secure-side SysTick exception, which OBL doesn't implement, so the
|
||||
* exception pends in S forever (NS-side ICSR shows PENDSTSET=1 +
|
||||
* VECTPENDING=15 with VECTACTIVE=0) and BF's NS handler never runs —
|
||||
* uwTick / sysTickUptime stay at 0 and the first HAL_Delay /
|
||||
* delay() call after the BF init that doesn't poll hardware directly
|
||||
* hangs forever. */
|
||||
SCB->ICSR |= SCB_ICSR_STTNS_Msk;
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
/* CMSIS-compliant ARMv8-M S→NS hand-off.
|
||||
*
|
||||
* `cmse_nonsecure_call` on the function-pointer type makes the
|
||||
* compiler emit the proper BXNS-style branch *and* the AAPCS-CMSE
|
||||
* register-clearing prologue (zero R0-R3 / R12 / FPSCR caller-saved
|
||||
* state, so no Secure data leaks into the NS app). The function-
|
||||
* pointer cast also implicitly clears the LSB of the target so the
|
||||
* branch always transitions S → NS (with LSB=1 BXNS stays in S and
|
||||
* would SecureFault on the NS-attributed XSPI fetch).
|
||||
* __TZ_set_MSP_NS installs BF's stack pointer in the NS bank. */
|
||||
__TZ_set_MSP_NS(bf_sp);
|
||||
|
||||
/* Clear S PRIMASK / FAULTMASK before BXNS. The __disable_irq() above
|
||||
* leaves PRIMASK_S=1; empirically the Cortex-M55 keeps NS exceptions
|
||||
* (SysTick, PendSV, peripheral IRQs targeted NS via ITNS) pending in
|
||||
* the NS-bank ICSR but never delivers them while PRIMASK_S is set —
|
||||
* even though Armv8-M masks are nominally banked per Security state.
|
||||
* Re-enable S-side IRQs so NS exception delivery works post-BXNS. */
|
||||
__enable_irq();
|
||||
__set_FAULTMASK(0);
|
||||
__set_BASEPRI(0);
|
||||
__DSB();
|
||||
__ISB();
|
||||
|
||||
typedef void __attribute__((cmse_nonsecure_call)) (*ns_reset_fn)(void);
|
||||
const ns_reset_fn bf_reset = (ns_reset_fn)(bf_pc & ~1UL);
|
||||
bf_reset();
|
||||
|
||||
while (1) {
|
||||
__NOP();
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Entry ---------- */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* RCC->RSR must be sampled before HAL_Init / SystemClock_Config touch
|
||||
* RCC — HAL_RCC_DeInit (called from System_DeInit during
|
||||
* SystemClock_Config) writes RMVF and erases the IWDGRSTF / LCKRSTF /
|
||||
* WWDGRSTF flags the boot decision below depends on. Snapshot the
|
||||
* value here and clear so subsequent boots see a stable state. */
|
||||
const uint32_t saved_rsr = RCC->RSR;
|
||||
RCC->RSR = RCC_RSR_RMVF;
|
||||
(void)RCC->RSR;
|
||||
|
||||
HAL_Init();
|
||||
SystemClock_Config();
|
||||
|
||||
if (!flash_init()) {
|
||||
Error_Handler();
|
||||
}
|
||||
if (!flash_memmap_on()) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
#ifdef OBL_FORCE_RECOVERY
|
||||
/* Bring-up only: SWD-loaded OBL used to refresh nor0 0x0. Skip the
|
||||
* magic check so we always enter Recovery, exposing nor0 from 0x0
|
||||
* for OBL+BF reinstall. The committed signed OBL has this flag
|
||||
* undefined. */
|
||||
(void)nor0_has_valid_obl;
|
||||
OBL_run_dfu_recovery();
|
||||
/* unreachable */
|
||||
#endif
|
||||
|
||||
if (!nor0_has_valid_obl()) {
|
||||
OBL_run_dfu_recovery();
|
||||
/* unreachable */
|
||||
}
|
||||
|
||||
/* Reset-cause boot decision. RCC->RSR is the only on-chip primitive
|
||||
* that survives any reset short of POR/BOR — its flags are cleared
|
||||
* only by RMVF (handled above) or POR, and persist across NRST /
|
||||
* SRST / SYSRESETREQ. IWDGRSTF / LCKRSTF / WWDGRSTF set means the
|
||||
* previous boot ended with a crash (watchdog timeout, CPU lockup,
|
||||
* or window WD); route to DFU instead of re-launching the broken
|
||||
* BF in a loop. */
|
||||
const uint32_t crash_flags = RCC_RSR_IWDGRSTF | RCC_RSR_LCKRSTF | RCC_RSR_WWDGRSTF;
|
||||
const bool was_crash_reset = (saved_rsr & crash_flags) != 0U;
|
||||
|
||||
#ifdef OBL_FORCE_DFU
|
||||
/* Bring-up override: SWD-load OBL with this flag set when we want
|
||||
* DFU mode regardless of nor0 contents — used to flash a fresh BF
|
||||
* via dfu-util on top of an existing valid (but stale) BF. */
|
||||
OBL_run_dfu_normal();
|
||||
/* unreachable */
|
||||
#endif
|
||||
|
||||
if (was_crash_reset || !bf_vector_table_valid()) {
|
||||
OBL_run_dfu_normal();
|
||||
/* unreachable */
|
||||
}
|
||||
|
||||
iwdg_start();
|
||||
jump_to_bf();
|
||||
}
|
||||
|
||||
/* ---------- Plumbing ---------- */
|
||||
|
||||
void Error_Handler(void)
|
||||
{
|
||||
__disable_irq();
|
||||
while (1) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Tear the clock tree back to reset defaults before SystemClock_Config_Impl
|
||||
* runs OscConfig. Boot ROM's FSBL-load path leaves PLL1 active and the
|
||||
* CPU sourced from it, which makes HAL_RCC_OscConfig fail. Also resolves
|
||||
* the OpenBootloader_DeInit reference in CubeN6's app_openbootloader.c. */
|
||||
void System_DeInit(void)
|
||||
{
|
||||
HAL_RCC_DeInit();
|
||||
}
|
||||
|
||||
extern void SystemClock_Config_Impl(void);
|
||||
static void SystemClock_Config(void)
|
||||
{
|
||||
SystemClock_Config_Impl();
|
||||
|
||||
/* Re-assert DBGMCU.CR after the clock-tree reconfig — the brief
|
||||
* debug-clock outage during the CPU-clock switch can drop the
|
||||
* bits. Same value as in SystemInit. */
|
||||
DBGMCU->CR = DBGMCU_CR_DBGCLKEN
|
||||
| DBGMCU_CR_DBG_SLEEP
|
||||
| DBGMCU_CR_DBG_STOP
|
||||
| DBGMCU_CR_DBG_STANDBY;
|
||||
(void)DBGMCU->CR;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "stm32n6xx_hal.h"
|
||||
|
||||
void Error_Handler(void);
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* OBL DFU loop entry points called from main.c's boot decision.
|
||||
*
|
||||
* Both run the same OBL middleware loop (OpenBootloader_Init +
|
||||
* OpenBootloader_ProtocolDetection in a busy loop). They differ in how
|
||||
* the DFU memory map is configured before the loop starts:
|
||||
* - normal mode protects the OBL slot (advertise @External_Flash from
|
||||
* nor0 0x100000 onwards — BF slot only);
|
||||
* - recovery mode exposes nor0 from offset 0 so the host can rewrite
|
||||
* OBL itself.
|
||||
*
|
||||
* The mode is communicated to USB_DFU_If_* in usbd_dfu_if.c via the
|
||||
* obl_recovery_mode flag below — checked when DFU initialises its memory
|
||||
* descriptor string.
|
||||
*
|
||||
* Once a host completes a DFU download, our patched usbd_dfu.c sets
|
||||
* dfu_leave_pending; the loop below detaches USB and triggers a system
|
||||
* reset so the freshly-flashed BF runs without manual NRST.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "app_openbootloader.h"
|
||||
#include "usbd_core.h"
|
||||
|
||||
#include "flash_iface.h"
|
||||
|
||||
bool obl_recovery_mode;
|
||||
|
||||
extern void obl_dfu_apply_mode(bool recovery);
|
||||
extern volatile bool dfu_leave_pending;
|
||||
extern volatile uint32_t dfu_last_write_tick;
|
||||
extern USBD_HandleTypeDef hUsbDeviceFS;
|
||||
|
||||
/* Time for the host's final GET_STATUS to return dfuMANIFEST-WAIT-RESET
|
||||
* before the device disappears off the bus. dfu-util prints a clean
|
||||
* "File downloaded successfully" once the manifest poll loop completes
|
||||
* within ~250 ms; 400 ms is generous. */
|
||||
#define DFU_LEAVE_DRAIN_MS 400U
|
||||
|
||||
/* Auto-reboot after this many ms of write inactivity once at least one
|
||||
* data block has landed. dfu-util without --reset finishes the download
|
||||
* and exits without triggering manifest, so we infer "host is done" from
|
||||
* a stretch of silence. 1500 ms is comfortably above the gap between
|
||||
* dfu-util's per-block transfers (~ms) and below any reasonable interactive
|
||||
* wait. */
|
||||
#define DFU_IDLE_REBOOT_MS 1500U
|
||||
|
||||
static __attribute__((noreturn)) void run_obl_loop(bool recovery)
|
||||
{
|
||||
obl_recovery_mode = recovery;
|
||||
/* Patch the DFU descriptor string before USB enumeration starts so
|
||||
* the host sees the right memory geometry on first GET_DESCRIPTOR. */
|
||||
obl_dfu_apply_mode(recovery);
|
||||
|
||||
OpenBootloader_Init();
|
||||
|
||||
/* IWDG is NOT started in DFU mode — host upload can take tens of
|
||||
* seconds and the BF watchdog window (~30 s) is too tight for that.
|
||||
* The watchdog is armed only when handing off to BF (iwdg_start in
|
||||
* main.c). */
|
||||
while (1) {
|
||||
OpenBootloader_ProtocolDetection();
|
||||
|
||||
const bool leave_request = dfu_leave_pending;
|
||||
const uint32_t last_tick = dfu_last_write_tick;
|
||||
const bool idle_complete = (last_tick != 0U)
|
||||
&& ((HAL_GetTick() - last_tick) >= DFU_IDLE_REBOOT_MS);
|
||||
|
||||
if (leave_request || idle_complete) {
|
||||
HAL_Delay(DFU_LEAVE_DRAIN_MS);
|
||||
(void)USBD_Stop(&hUsbDeviceFS);
|
||||
(void)USBD_DeInit(&hUsbDeviceFS);
|
||||
HAL_Delay(50U);
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void OBL_run_dfu_normal(void)
|
||||
{
|
||||
run_obl_loop(false);
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void OBL_run_dfu_recovery(void)
|
||||
{
|
||||
run_obl_loop(true);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Override of CubeN6's openbootloader_conf.h.
|
||||
*
|
||||
* The submodule version uses _S secure aliases (SRAM2_AXI_BASE_S etc.)
|
||||
* which are only defined when the build sets CPU_IN_SECURE_STATE +
|
||||
* -mcmse — see feedback_n6_peripheral_alias_cmse.md. We don't (matches
|
||||
* the FSBL stub and main BF build), so all peripheral / SRAM macros
|
||||
* resolve to NS variants. RIFSC is configured for OPEN-lifecycle silicon
|
||||
* to permit NS access regardless.
|
||||
*
|
||||
* Picked up before the submodule version because -I. comes first in
|
||||
* INCLUDES.
|
||||
*/
|
||||
|
||||
#ifndef OPENBOOTLOADER_CONF_H
|
||||
#define OPENBOOTLOADER_CONF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#define DEVICE_ID_MSB 0x04U
|
||||
#define DEVICE_ID_LSB 0x86U
|
||||
|
||||
/* RAM scratch — OBL middleware uses these for its receive/scratch
|
||||
* buffers. Targets SRAM2 AXI (NS alias) per ST's reference. */
|
||||
#define RAM_MEM_START_ADDRESS (SRAM2_AXI_BASE_NS + 0xB9000U)
|
||||
#define RAM_MEM_SIZE (SRAM2_AXI_SIZE - 0xB9000U)
|
||||
#define RAM_MEM_END_ADDRESS (RAM_MEM_START_ADDRESS + RAM_MEM_SIZE - 1U)
|
||||
|
||||
#define FLASH_LOADER_START_ADDRESS SRAM1_AHB_BASE_NS
|
||||
#define FLASH_LOADER_SIZE (SRAM1_AHB_SIZE + SRAM2_AHB_SIZE)
|
||||
#define FLASH_LOADER_END_ADDRESS (FLASH_LOADER_START_ADDRESS + FLASH_LOADER_SIZE - 1U)
|
||||
|
||||
#define FLASHLAYOUT_ADDRESS (RAM_MEM_START_ADDRESS + RAM_MEM_SIZE - 1024U)
|
||||
|
||||
#define RAM_WRITE_ADDRESS RAM_MEM_START_ADDRESS
|
||||
#define FLASH_LOADER_WRITE_ADDRESS FLASH_LOADER_START_ADDRESS
|
||||
|
||||
#define EXT_MEMORY_START_ADDRESS 0x70000000U
|
||||
#define EXT_MEMORY_END_ADDRESS 0x78000000U
|
||||
#define EXT_MEMORY_SIZE 0x08000000U
|
||||
#define EXT_MEMORY_SECTOR_SIZE 0x10000U
|
||||
|
||||
#define OPENBL_DEFAULT_MEM 0xFFFFFFFFU
|
||||
#define UNDEF_ADDRESS 0xFFFFFFFFU
|
||||
|
||||
#define RDP_LEVEL_0 0xEEEEEEEEU
|
||||
#define RDP_LEVEL_1 0xEEEEEEEEU
|
||||
|
||||
#define AREA_ERROR 0x0U
|
||||
#define RAM_AREA 0x1U
|
||||
#define OTP_AREA 0x2U
|
||||
#define EXTERNAL_MEMORY_AREA 0x3U
|
||||
|
||||
#define FLASH_MASS_ERASE 0xFFFFU
|
||||
|
||||
/* Only USB transport in our build (no USART/I2C/SPI/CAN). The
|
||||
* INTERFACES_SUPPORTED count gates how many OPENBL_RegisterInterface
|
||||
* slots OBL pre-allocates. We register USB + IWDG = 2 in
|
||||
* app_openbootloader.c. */
|
||||
#define INTERFACES_SUPPORTED 2U
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPENBOOTLOADER_CONF_H */
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* OTP interface stub.
|
||||
*
|
||||
* ST's CubeN6 reference OBL ships an OTP fuse-access path via the BSEC
|
||||
* peripheral HAL. We don't enable BSEC HAL (no security/lifecycle work
|
||||
* needed for our recovery + BF-loader use case), so the upstream
|
||||
* otp_interface.c won't compile in our build. This stub provides the
|
||||
* Init/DeInit symbols app_openbootloader.c calls and stubs Read/Write so
|
||||
* any host attempt at OTP commands no-ops cleanly rather than wedging
|
||||
* the device. If a manufacturer needs OTP commissioning later, replace
|
||||
* this file with the real implementation behind a per-config knob.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "otp_interface.h"
|
||||
|
||||
void OPENBL_OTP_Init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void OPENBL_OTP_DeInit(void)
|
||||
{
|
||||
}
|
||||
|
||||
Otp_Partition_t OPENBL_OTP_Read(void)
|
||||
{
|
||||
Otp_Partition_t result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
result.Version = OPENBL_OTP_VERSION;
|
||||
result.GlobalState = BSEC_SEC_OTP_INVALID;
|
||||
return result;
|
||||
}
|
||||
|
||||
int OPENBL_OTP_Write(Otp_Partition_t Otp)
|
||||
{
|
||||
(void)Otp;
|
||||
return OTP_ERROR;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,946 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file startup_stm32n657xx.s
|
||||
* @author GPM Application Team
|
||||
* @brief STM32N657XX device vector table for GCC toolchain.
|
||||
* This startup file should be used for the FSBL (after bootROM execution).
|
||||
* This module performs:
|
||||
* - Set the initial SP
|
||||
* - Set the initial PC == Reset_Handler,
|
||||
* - Set the vector table entries with the exceptions ISR address
|
||||
* - Branches to main in the C library (which eventually
|
||||
* calls main()).
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2023 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.arch armv8.1-m.main
|
||||
.fpu softvfp
|
||||
.thumb
|
||||
|
||||
.global g_pfnVectors
|
||||
.global Default_Handler
|
||||
|
||||
/* start address for the initialization values of the .data section.
|
||||
defined in linker script */
|
||||
.word _sidata
|
||||
/* start address for the .data section. defined in linker script */
|
||||
.word _sdata
|
||||
/* end address for the .data section. defined in linker script */
|
||||
.word _edata
|
||||
/* start address for the .bss section. defined in linker script */
|
||||
.word _sbss
|
||||
/* end address for the .bss section. defined in linker script */
|
||||
.word _ebss
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor first
|
||||
* starts execution following a reset event. Only the absolutely
|
||||
* necessary set is performed, after which the application
|
||||
* supplied main() routine is called.
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/
|
||||
|
||||
.section .text.Reset_Handler
|
||||
.weak Reset_Handler
|
||||
.type Reset_Handler, %function
|
||||
Reset_Handler:
|
||||
ldr r0, =_sstack
|
||||
msr MSPLIM, r0
|
||||
ldr r0, =_estack
|
||||
mov sp, r0 /* set stack pointer */
|
||||
|
||||
/* .data copy + .bss zero must run BEFORE SystemInit. SystemInit and
|
||||
* HAL_Init touch globals (uwTick, SystemCoreClock); doing them with
|
||||
* uninitialised RAM corrupts state in ways that only show up later.
|
||||
* See feedback_h5_startup_data_init_order.md. */
|
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */
|
||||
ldr r0, =_sdata
|
||||
ldr r1, =_edata
|
||||
ldr r2, =_sidata
|
||||
movs r3, #0
|
||||
b LoopCopyDataInit
|
||||
|
||||
CopyDataInit:
|
||||
ldr r4, [r2, r3]
|
||||
str r4, [r0, r3]
|
||||
adds r3, r3, #4
|
||||
|
||||
LoopCopyDataInit:
|
||||
adds r4, r0, r3
|
||||
cmp r4, r1
|
||||
bcc CopyDataInit
|
||||
|
||||
/* Zero fill the bss segment. */
|
||||
ldr r2, =_sbss
|
||||
ldr r4, =_ebss
|
||||
movs r3, #0
|
||||
b LoopFillZerobss
|
||||
|
||||
FillZerobss:
|
||||
str r3, [r2]
|
||||
adds r2, r2, #4
|
||||
|
||||
LoopFillZerobss:
|
||||
cmp r2, r4
|
||||
bcc FillZerobss
|
||||
|
||||
/* Call the clock system initialization function.*/
|
||||
bl SystemInit
|
||||
/* Call the application's entry point.*/
|
||||
bl main
|
||||
|
||||
LoopForever:
|
||||
b LoopForever
|
||||
|
||||
.size Reset_Handler, .-Reset_Handler
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor receives an
|
||||
* unexpected interrupt. This simply enters an infinite loop, preserving
|
||||
* the system state for examination by a debugger.
|
||||
*
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/
|
||||
.section .text.Default_Handler,"ax",%progbits
|
||||
Default_Handler:
|
||||
Infinite_Loop:
|
||||
b Infinite_Loop
|
||||
.size Default_Handler, .-Default_Handler
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* The STM32N657XX vector table. Note that the proper constructs
|
||||
* must be placed on this to ensure that it ends up at physical address
|
||||
* 0x0000.0000.
|
||||
*
|
||||
******************************************************************************/
|
||||
.section .isr_vector,"a",%progbits
|
||||
.type g_pfnVectors, %object
|
||||
.size g_pfnVectors, .-g_pfnVectors
|
||||
|
||||
g_pfnVectors:
|
||||
.word _estack
|
||||
.word Reset_Handler
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
.word SecureFault_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SVC_Handler
|
||||
.word DebugMon_Handler
|
||||
.word 0
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
.word PVD_PVM_IRQHandler /* PVDOUT through the EXTI line */
|
||||
.word 0 /* Reserved */
|
||||
.word DTS_IRQHandler /* Thermal sensor interruption */
|
||||
.word RCC_IRQHandler /* RCC global interrupt */
|
||||
.word LOCKUP_IRQHandler /* LOCKUP - no overstack in Cortex-M55 */
|
||||
.word CACHE_ECC_IRQHandler /* Cache ECC error */
|
||||
.word TCM_ECC_IRQHandler /* TCM ECC error */
|
||||
.word BCK_ECC_IRQHandler /* Backup RAM interrupts (SEC and DED) */
|
||||
.word FPU_IRQHandler /* FPU safety flag */
|
||||
.word 0 /* Reserved */
|
||||
.word RTC_S_IRQHandler /* RTC secure interrupt */
|
||||
.word TAMP_IRQHandler /* TAMP secure and non-secure synchronous interrupt line */
|
||||
.word RIFSC_TAMPER_IRQHandler /* RIF can generate an interrupt when a laser attack is detected */
|
||||
.word IAC_IRQHandler /* IAC global interrupt */
|
||||
.word RCC_S_IRQHandler /* RCC global secure interrupt */
|
||||
.word 0 /* Reserved */
|
||||
.word RTC_IRQHandler /* RTC interrupt */
|
||||
.word 0 /* Reserved */
|
||||
.word IWDG_IRQHandler /* Independent watchdog interrupt */
|
||||
.word WWDG_IRQHandler /* Window watchdog interrupt */
|
||||
.word EXTI0_IRQHandler /* EXTI Line 0 interrupt through the EXTI line */
|
||||
.word EXTI1_IRQHandler /* EXTI Line 1 interrupt through the EXTI line */
|
||||
.word EXTI2_IRQHandler /* EXTI Line 2 interrupt through the EXTI line */
|
||||
.word EXTI3_IRQHandler /* EXTI Line 3 interrupt through the EXTI line */
|
||||
.word EXTI4_IRQHandler /* EXTI Line 4 interrupt through the EXTI line */
|
||||
.word EXTI5_IRQHandler /* EXTI Line 5 interrupt through the EXTI line */
|
||||
.word EXTI6_IRQHandler /* EXTI Line 6 interrupt through the EXTI line */
|
||||
.word EXTI7_IRQHandler /* EXTI Line 7 interrupt through the EXTI line */
|
||||
.word EXTI8_IRQHandler /* EXTI Line 8 interrupt through the EXTI line */
|
||||
.word EXTI9_IRQHandler /* EXTI Line 9 interrupt */
|
||||
.word EXTI10_IRQHandler /* EXTI Line 10 interrupt */
|
||||
.word EXTI11_IRQHandler /* EXTI Line 11 interrupt */
|
||||
.word EXTI12_IRQHandler /* EXTI Line 12 interrupt */
|
||||
.word EXTI13_IRQHandler /* EXTI Line 13 interrupt */
|
||||
.word EXTI14_IRQHandler /* EXTI Line 14 interrupt */
|
||||
.word EXTI15_IRQHandler /* EXTI Line 15 interrupt */
|
||||
.word SAES_IRQHandler /* SAES global interrupt */
|
||||
.word CRYP_IRQHandler /* CRYP global interrupt */
|
||||
.word PKA_IRQHandler /* PKA global interrupt */
|
||||
.word HASH_IRQHandler /* HASH global interrupt */
|
||||
.word RNG_IRQHandler /* RNG global interrupt */
|
||||
.word 0 /* Reserved */
|
||||
.word MCE1_IRQHandler /* MCE1 global interrupt */
|
||||
.word MCE2_IRQHandler /* MCE2 global interrupt */
|
||||
.word MCE3_IRQHandler /* MCE3 global interrupt */
|
||||
.word MCE4_IRQHandler /* MCE4 global interrupt */
|
||||
.word ADC1_2_IRQHandler /* ADC1/ADC2 global interrupt */
|
||||
.word CSI_IRQHandler /* CSI global interrupt */
|
||||
.word DCMIPP_IRQHandler /* DCMIPP global interrupt */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word PAHB_ERR_IRQHandler /* Write posting errors on Cortex-M55 PAHB interface */
|
||||
.word NPU0_IRQHandler /* NPU mst_ints[0] line */
|
||||
.word NPU1_IRQHandler /* NPU mst_ints[1] line */
|
||||
.word NPU2_IRQHandler /* NPU mst_ints[2] line */
|
||||
.word NPU3_IRQHandler /* NPU mst_ints[3] line */
|
||||
.word CACHEAXI_IRQHandler /* ATON interrupt cache */
|
||||
.word LTDC_LO_IRQHandler /* LCD low-layer global interrupt */
|
||||
.word LTDC_LO_ERR_IRQHandler /* LCD low-layer error interrupt */
|
||||
.word DMA2D_IRQHandler /* DMA2D global interrupt */
|
||||
.word JPEG_IRQHandler /* JPEG global interrupt */
|
||||
.word VENC_IRQHandler /* VENC global interrupt */
|
||||
.word GFXMMU_IRQHandler /* GFXMMU global interrupt */
|
||||
.word GFXTIM_IRQHandler /* GFXTIM global interrupt */
|
||||
.word GPU2D_IRQHandler /* GPU2D global interrupt */
|
||||
.word GPU2D_ER_IRQHandler /* GPU2D global interrupt */
|
||||
.word ICACHE_IRQHandler /* GPU cache interrupt */
|
||||
.word HPDMA1_Channel0_IRQHandler /* HPDMA1 Channel 0 interrupt */
|
||||
.word HPDMA1_Channel1_IRQHandler /* HPDMA1 Channel 1 interrupt */
|
||||
.word HPDMA1_Channel2_IRQHandler /* HPDMA1 Channel 2 interrupt */
|
||||
.word HPDMA1_Channel3_IRQHandler /* HPDMA1 Channel 3 interrupt */
|
||||
.word HPDMA1_Channel4_IRQHandler /* HPDMA1 Channel 4 interrupt */
|
||||
.word HPDMA1_Channel5_IRQHandler /* HPDMA1 Channel 5 interrupt */
|
||||
.word HPDMA1_Channel6_IRQHandler /* HPDMA1 Channel 6 interrupt */
|
||||
.word HPDMA1_Channel7_IRQHandler /* HPDMA1 Channel 7 interrupt */
|
||||
.word HPDMA1_Channel8_IRQHandler /* HPDMA1 Channel 8 interrupt */
|
||||
.word HPDMA1_Channel9_IRQHandler /* HPDMA1 Channel 9 interrupt */
|
||||
.word HPDMA1_Channel10_IRQHandler /* HPDMA1 Channel 10 interrupt */
|
||||
.word HPDMA1_Channel11_IRQHandler /* HPDMA1 Channel 11 interrupt */
|
||||
.word HPDMA1_Channel12_IRQHandler /* HPDMA1 Channel 12 interrupt */
|
||||
.word HPDMA1_Channel13_IRQHandler /* HPDMA1 Channel 13 interrupt */
|
||||
.word HPDMA1_Channel14_IRQHandler /* HPDMA1 Channel 14 interrupt */
|
||||
.word HPDMA1_Channel15_IRQHandler /* HPDMA1 Channel 15 interrupt */
|
||||
.word GPDMA1_Channel0_IRQHandler /* GPDMA1 channel 0 interrupt */
|
||||
.word GPDMA1_Channel1_IRQHandler /* GPDMA1 channel 1 interrupt */
|
||||
.word GPDMA1_Channel2_IRQHandler /* GPDMA1 channel 2 interrupt */
|
||||
.word GPDMA1_Channel3_IRQHandler /* GPDMA1 channel 3 interrupt */
|
||||
.word GPDMA1_Channel4_IRQHandler /* GPDMA1 channel 4 interrupt */
|
||||
.word GPDMA1_Channel5_IRQHandler /* GPDMA1 channel 5 interrupt */
|
||||
.word GPDMA1_Channel6_IRQHandler /* GPDMA1 channel 6 interrupt */
|
||||
.word GPDMA1_Channel7_IRQHandler /* GPDMA1 channel 7 interrupt */
|
||||
.word GPDMA1_Channel8_IRQHandler /* GPDMA1 channel 8 interrupt */
|
||||
.word GPDMA1_Channel9_IRQHandler /* GPDMA1 channel 9 interrupt */
|
||||
.word GPDMA1_Channel10_IRQHandler /* GPDMA1 channel 10 interrupt */
|
||||
.word GPDMA1_Channel11_IRQHandler /* GPDMA1 channel 11 interrupt */
|
||||
.word GPDMA1_Channel12_IRQHandler /* GPDMA1 channel 12 interrupt */
|
||||
.word GPDMA1_Channel13_IRQHandler /* GPDMA1 channel 13 interrupt */
|
||||
.word GPDMA1_Channel14_IRQHandler /* GPDMA1 channel 14 interrupt */
|
||||
.word GPDMA1_Channel15_IRQHandler /* GPDMA1 channel 15 interrupt */
|
||||
.word I2C1_EV_IRQHandler /* I2C1 event interrupt */
|
||||
.word I2C1_ER_IRQHandler /* I2C1 error interrupt */
|
||||
.word I2C2_EV_IRQHandler /* I2C2 event interrupt */
|
||||
.word I2C2_ER_IRQHandler /* I2C2 error interrupt */
|
||||
.word I2C3_EV_IRQHandler /* I2C3 event interrupt */
|
||||
.word I2C3_ER_IRQHandler /* I2C3 error interrupt */
|
||||
.word I2C4_EV_IRQHandler /* I2C4 event interrupt */
|
||||
.word I2C4_ER_IRQHandler /* I2C4 error interrupt */
|
||||
.word I3C1_EV_IRQHandler /* I3C1 event interrupt */
|
||||
.word I3C1_ER_IRQHandler /* I3C1 error interrupt */
|
||||
.word I3C2_EV_IRQHandler /* I3C2 event interrupt */
|
||||
.word I3C2_ER_IRQHandler /* I3C2 error interrupt */
|
||||
.word TIM1_BRK_IRQHandler /* TIM1 Break interrupt */
|
||||
.word TIM1_UP_IRQHandler /* TIM1 Update interrupt */
|
||||
.word TIM1_TRG_CCU_IRQHandler /* TIM1 Trigger and Commutation interrupts */
|
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare interrupt */
|
||||
.word TIM2_IRQHandler /* TIM2 global interrupt */
|
||||
.word TIM3_IRQHandler /* TIM3 global interrupt */
|
||||
.word TIM4_IRQHandler /* TIM4 global interrupt */
|
||||
.word TIM5_IRQHandler /* TIM5 global interrupt */
|
||||
.word TIM6_IRQHandler /* TIM6 Global interrupt */
|
||||
.word TIM7_IRQHandler /* TIM7 Global interrupt */
|
||||
.word TIM8_BRK_IRQHandler /* TIM8 Break interrupt */
|
||||
.word TIM8_UP_IRQHandler /* TIM8 Update interrupt */
|
||||
.word TIM8_TRG_CCU_IRQHandler /* TIM8 Trigger and Commutation interrupts */
|
||||
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare interrupt */
|
||||
.word TIM9_IRQHandler /* TIM9 Global interrupt */
|
||||
.word TIM10_IRQHandler /* TIM10 Global interrupt */
|
||||
.word TIM11_IRQHandler /* TIM11 Global interrupt */
|
||||
.word TIM12_IRQHandler /* TIM12 Global interrupt */
|
||||
.word TIM13_IRQHandler /* TIM13 Global interrupt */
|
||||
.word TIM14_IRQHandler /* TIM14 Global interrupt */
|
||||
.word TIM15_IRQHandler /* TIM15 global interrupt */
|
||||
.word TIM16_IRQHandler /* TIM16 global interrupt */
|
||||
.word TIM17_IRQHandler /* TIM17 global interrupt */
|
||||
.word TIM18_IRQHandler /* TIM18 Global interrupt */
|
||||
.word LPTIM1_IRQHandler /* LPTIM1 global interrupt */
|
||||
.word LPTIM2_IRQHandler /* LPTIM2 global interrupt */
|
||||
.word LPTIM3_IRQHandler /* LPTIM3 global interrupt */
|
||||
.word LPTIM4_IRQHandler /* LPTIM4 global interrupt */
|
||||
.word LPTIM5_IRQHandler /* LPTIM5 global interrupt */
|
||||
.word ADF1_FLT0_IRQHandler /* ADF1 filter 0 global interrupt */
|
||||
.word MDF1_FLT0_IRQHandler /* MDF global Interrupt for Filter0 */
|
||||
.word MDF1_FLT1_IRQHandler /* MDF global Interrupt for Filter1 */
|
||||
.word MDF1_FLT2_IRQHandler /* MDF global Interrupt for Filter2 */
|
||||
.word MDF1_FLT3_IRQHandler /* MDF global Interrupt for Filter3 */
|
||||
.word MDF1_FLT4_IRQHandler /* MDF global Interrupt for Filter4 */
|
||||
.word MDF1_FLT5_IRQHandler /* MDF global Interrupt for Filter5 */
|
||||
.word SAI1_A_IRQHandler /* SAI1 global interrupt A */
|
||||
.word SAI1_B_IRQHandler /* SAI1 global interrupt B */
|
||||
.word SAI2_A_IRQHandler /* SAI2 global interrupt A */
|
||||
.word SAI2_B_IRQHandler /* SAI2 global interrupt B */
|
||||
.word SPDIFRX_IRQHandler /* SPDIFRX global interrupt */
|
||||
.word SPI1_IRQHandler /* SPI1 global interrupt A */
|
||||
.word SPI2_IRQHandler /* SPI2 global interrupt A */
|
||||
.word SPI3_IRQHandler /* SPI3 global interrupt A */
|
||||
.word SPI4_IRQHandler /* SPI4 global interrupt A */
|
||||
.word SPI5_IRQHandler /* SPI5 global interrupt A */
|
||||
.word SPI6_IRQHandler /* SPI6 global interrupt A */
|
||||
.word USART1_IRQHandler /* USART1 Global interrupt */
|
||||
.word USART2_IRQHandler /* USART2 Global interrupt */
|
||||
.word USART3_IRQHandler /* USART3 Global interrupt */
|
||||
.word UART4_IRQHandler /* UART4 Global interrupt */
|
||||
.word UART5_IRQHandler /* UART5 Global interrupt */
|
||||
.word USART6_IRQHandler /* USART6 Global interrupt */
|
||||
.word UART7_IRQHandler /* UART7 Global interrupt */
|
||||
.word UART8_IRQHandler /* UART8 Global interrupt */
|
||||
.word UART9_IRQHandler /* UART9 Global interrupt */
|
||||
.word USART10_IRQHandler /* USART10 Global interrupt */
|
||||
.word LPUART1_IRQHandler /* LPUART1 global interrupt */
|
||||
.word XSPI1_IRQHandler /* XSPI1 global interrupt */
|
||||
.word XSPI2_IRQHandler /* XSPI2 global interrupt */
|
||||
.word XSPI3_IRQHandler /* XSPI3 global interrupt */
|
||||
.word FMC_IRQHandler /* FMC global interrupt */
|
||||
.word SDMMC1_IRQHandler /* SDMMC1 global interrupt */
|
||||
.word SDMMC2_IRQHandler /* SDMMC2 global interrupt */
|
||||
.word UCPD1_IRQHandler /* UCPD global interrupt */
|
||||
.word USB1_OTG_HS_IRQHandler /* USB OTG1 HS global interrupt */
|
||||
.word USB2_OTG_HS_IRQHandler /* USB OTG2 HS global interrupt */
|
||||
.word ETH1_IRQHandler /* Ethernet global interrupt */
|
||||
.word FDCAN1_IT0_IRQHandler /* FDCAN1 interrupt 0 */
|
||||
.word FDCAN1_IT1_IRQHandler /* FDCAN1 interrupt 1 */
|
||||
.word FDCAN2_IT0_IRQHandler /* FDCAN2 interrupt 0 */
|
||||
.word FDCAN2_IT1_IRQHandler /* FDCAN2 interrupt 1 */
|
||||
.word FDCAN3_IT0_IRQHandler /* FDCAN3 interrupt 0 */
|
||||
.word FDCAN3_IT1_IRQHandler /* FDCAN3 interrupt 1 */
|
||||
.word FDCAN_CU_IRQHandler /* Clock calibration unit interrupt line(FDCAN1 only) */
|
||||
.word MDIOS_IRQHandler /* MDIOS global Interrupt */
|
||||
.word DCMI_PSSI_IRQHandler /* DCMI/PSSI global interrupt */
|
||||
.word WAKEUP_PIN_IRQHandler /* Wake-up pin interrupts */
|
||||
.word CTI_INT0_IRQHandler /* Debug monitor (Cortex-M55 related) */
|
||||
.word CTI_INT1_IRQHandler /* Debug monitor (Cortex-M55 related) */
|
||||
.word 0 /* Reserved */
|
||||
.word LTDC_UP_IRQHandler /* LCD up-layer global interrupt */
|
||||
.word LTDC_UP_ERR_IRQHandler /* LCD up-layer error interrupt */
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Provide weak aliases for each Exception handler to the Default_Handler.
|
||||
* As they are weak aliases, any function with the same name will override
|
||||
* this definition.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler,Default_Handler
|
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler,Default_Handler
|
||||
|
||||
.weak MemManage_Handler
|
||||
.thumb_set MemManage_Handler,Default_Handler
|
||||
|
||||
.weak BusFault_Handler
|
||||
.thumb_set BusFault_Handler,Default_Handler
|
||||
|
||||
.weak UsageFault_Handler
|
||||
.thumb_set UsageFault_Handler,Default_Handler
|
||||
|
||||
.weak SecureFault_Handler
|
||||
.thumb_set SecureFault_Handler,Default_Handler
|
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler,Default_Handler
|
||||
|
||||
.weak DebugMon_Handler
|
||||
.thumb_set DebugMon_Handler,Default_Handler
|
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler,Default_Handler
|
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler,Default_Handler
|
||||
|
||||
.weak PVD_PVM_IRQHandler
|
||||
.thumb_set PVD_PVM_IRQHandler,Default_Handler
|
||||
|
||||
.weak DTS_IRQHandler
|
||||
.thumb_set DTS_IRQHandler,Default_Handler
|
||||
|
||||
.weak RCC_IRQHandler
|
||||
.thumb_set RCC_IRQHandler,Default_Handler
|
||||
|
||||
.weak LOCKUP_IRQHandler
|
||||
.thumb_set LOCKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak CACHE_ECC_IRQHandler
|
||||
.thumb_set CACHE_ECC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TCM_ECC_IRQHandler
|
||||
.thumb_set TCM_ECC_IRQHandler,Default_Handler
|
||||
|
||||
.weak BCK_ECC_IRQHandler
|
||||
.thumb_set BCK_ECC_IRQHandler,Default_Handler
|
||||
|
||||
.weak FPU_IRQHandler
|
||||
.thumb_set FPU_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_S_IRQHandler
|
||||
.thumb_set RTC_S_IRQHandler,Default_Handler
|
||||
|
||||
.weak TAMP_IRQHandler
|
||||
.thumb_set TAMP_IRQHandler,Default_Handler
|
||||
|
||||
.weak RIFSC_TAMPER_IRQHandler
|
||||
.thumb_set RIFSC_TAMPER_IRQHandler,Default_Handler
|
||||
|
||||
.weak IAC_IRQHandler
|
||||
.thumb_set IAC_IRQHandler,Default_Handler
|
||||
|
||||
.weak RCC_S_IRQHandler
|
||||
.thumb_set RCC_S_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_IRQHandler
|
||||
.thumb_set RTC_IRQHandler,Default_Handler
|
||||
|
||||
.weak IWDG_IRQHandler
|
||||
.thumb_set IWDG_IRQHandler,Default_Handler
|
||||
|
||||
.weak WWDG_IRQHandler
|
||||
.thumb_set WWDG_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI0_IRQHandler
|
||||
.thumb_set EXTI0_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI1_IRQHandler
|
||||
.thumb_set EXTI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI2_IRQHandler
|
||||
.thumb_set EXTI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI3_IRQHandler
|
||||
.thumb_set EXTI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI4_IRQHandler
|
||||
.thumb_set EXTI4_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI5_IRQHandler
|
||||
.thumb_set EXTI5_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI6_IRQHandler
|
||||
.thumb_set EXTI6_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI7_IRQHandler
|
||||
.thumb_set EXTI7_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI8_IRQHandler
|
||||
.thumb_set EXTI8_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI9_IRQHandler
|
||||
.thumb_set EXTI9_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI10_IRQHandler
|
||||
.thumb_set EXTI10_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI11_IRQHandler
|
||||
.thumb_set EXTI11_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI12_IRQHandler
|
||||
.thumb_set EXTI12_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI13_IRQHandler
|
||||
.thumb_set EXTI13_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI14_IRQHandler
|
||||
.thumb_set EXTI14_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI15_IRQHandler
|
||||
.thumb_set EXTI15_IRQHandler,Default_Handler
|
||||
|
||||
.weak SAES_IRQHandler
|
||||
.thumb_set SAES_IRQHandler,Default_Handler
|
||||
|
||||
.weak CRYP_IRQHandler
|
||||
.thumb_set CRYP_IRQHandler,Default_Handler
|
||||
|
||||
.weak PKA_IRQHandler
|
||||
.thumb_set PKA_IRQHandler,Default_Handler
|
||||
|
||||
.weak HASH_IRQHandler
|
||||
.thumb_set HASH_IRQHandler,Default_Handler
|
||||
|
||||
.weak RNG_IRQHandler
|
||||
.thumb_set RNG_IRQHandler,Default_Handler
|
||||
|
||||
.weak MCE1_IRQHandler
|
||||
.thumb_set MCE1_IRQHandler,Default_Handler
|
||||
|
||||
.weak MCE2_IRQHandler
|
||||
.thumb_set MCE2_IRQHandler,Default_Handler
|
||||
|
||||
.weak MCE3_IRQHandler
|
||||
.thumb_set MCE3_IRQHandler,Default_Handler
|
||||
|
||||
.weak MCE4_IRQHandler
|
||||
.thumb_set MCE4_IRQHandler,Default_Handler
|
||||
|
||||
.weak ADC1_2_IRQHandler
|
||||
.thumb_set ADC1_2_IRQHandler,Default_Handler
|
||||
|
||||
.weak CSI_IRQHandler
|
||||
.thumb_set CSI_IRQHandler,Default_Handler
|
||||
|
||||
.weak DCMIPP_IRQHandler
|
||||
.thumb_set DCMIPP_IRQHandler,Default_Handler
|
||||
|
||||
.weak PAHB_ERR_IRQHandler
|
||||
.thumb_set PAHB_ERR_IRQHandler,Default_Handler
|
||||
|
||||
.weak NPU0_IRQHandler
|
||||
.thumb_set NPU0_IRQHandler,Default_Handler
|
||||
|
||||
.weak NPU1_IRQHandler
|
||||
.thumb_set NPU1_IRQHandler,Default_Handler
|
||||
|
||||
.weak NPU2_IRQHandler
|
||||
.thumb_set NPU2_IRQHandler,Default_Handler
|
||||
|
||||
.weak NPU3_IRQHandler
|
||||
.thumb_set NPU3_IRQHandler,Default_Handler
|
||||
|
||||
.weak CACHEAXI_IRQHandler
|
||||
.thumb_set CACHEAXI_IRQHandler,Default_Handler
|
||||
|
||||
.weak LTDC_LO_IRQHandler
|
||||
.thumb_set LTDC_LO_IRQHandler,Default_Handler
|
||||
|
||||
.weak LTDC_LO_ERR_IRQHandler
|
||||
.thumb_set LTDC_LO_ERR_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2D_IRQHandler
|
||||
.thumb_set DMA2D_IRQHandler,Default_Handler
|
||||
|
||||
.weak JPEG_IRQHandler
|
||||
.thumb_set JPEG_IRQHandler,Default_Handler
|
||||
|
||||
.weak VENC_IRQHandler
|
||||
.thumb_set VENC_IRQHandler,Default_Handler
|
||||
|
||||
.weak GFXMMU_IRQHandler
|
||||
.thumb_set GFXMMU_IRQHandler,Default_Handler
|
||||
|
||||
.weak GFXTIM_IRQHandler
|
||||
.thumb_set GFXTIM_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPU2D_IRQHandler
|
||||
.thumb_set GPU2D_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPU2D_ER_IRQHandler
|
||||
.thumb_set GPU2D_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak ICACHE_IRQHandler
|
||||
.thumb_set ICACHE_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel0_IRQHandler
|
||||
.thumb_set HPDMA1_Channel0_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel1_IRQHandler
|
||||
.thumb_set HPDMA1_Channel1_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel2_IRQHandler
|
||||
.thumb_set HPDMA1_Channel2_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel3_IRQHandler
|
||||
.thumb_set HPDMA1_Channel3_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel4_IRQHandler
|
||||
.thumb_set HPDMA1_Channel4_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel5_IRQHandler
|
||||
.thumb_set HPDMA1_Channel5_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel6_IRQHandler
|
||||
.thumb_set HPDMA1_Channel6_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel7_IRQHandler
|
||||
.thumb_set HPDMA1_Channel7_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel8_IRQHandler
|
||||
.thumb_set HPDMA1_Channel8_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel9_IRQHandler
|
||||
.thumb_set HPDMA1_Channel9_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel10_IRQHandler
|
||||
.thumb_set HPDMA1_Channel10_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel11_IRQHandler
|
||||
.thumb_set HPDMA1_Channel11_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel12_IRQHandler
|
||||
.thumb_set HPDMA1_Channel12_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel13_IRQHandler
|
||||
.thumb_set HPDMA1_Channel13_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel14_IRQHandler
|
||||
.thumb_set HPDMA1_Channel14_IRQHandler,Default_Handler
|
||||
|
||||
.weak HPDMA1_Channel15_IRQHandler
|
||||
.thumb_set HPDMA1_Channel15_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel0_IRQHandler
|
||||
.thumb_set GPDMA1_Channel0_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel1_IRQHandler
|
||||
.thumb_set GPDMA1_Channel1_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel2_IRQHandler
|
||||
.thumb_set GPDMA1_Channel2_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel3_IRQHandler
|
||||
.thumb_set GPDMA1_Channel3_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel4_IRQHandler
|
||||
.thumb_set GPDMA1_Channel4_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel5_IRQHandler
|
||||
.thumb_set GPDMA1_Channel5_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel6_IRQHandler
|
||||
.thumb_set GPDMA1_Channel6_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel7_IRQHandler
|
||||
.thumb_set GPDMA1_Channel7_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel8_IRQHandler
|
||||
.thumb_set GPDMA1_Channel8_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel9_IRQHandler
|
||||
.thumb_set GPDMA1_Channel9_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel10_IRQHandler
|
||||
.thumb_set GPDMA1_Channel10_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel11_IRQHandler
|
||||
.thumb_set GPDMA1_Channel11_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel12_IRQHandler
|
||||
.thumb_set GPDMA1_Channel12_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel13_IRQHandler
|
||||
.thumb_set GPDMA1_Channel13_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel14_IRQHandler
|
||||
.thumb_set GPDMA1_Channel14_IRQHandler,Default_Handler
|
||||
|
||||
.weak GPDMA1_Channel15_IRQHandler
|
||||
.thumb_set GPDMA1_Channel15_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_EV_IRQHandler
|
||||
.thumb_set I2C1_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_ER_IRQHandler
|
||||
.thumb_set I2C1_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C2_EV_IRQHandler
|
||||
.thumb_set I2C2_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C2_ER_IRQHandler
|
||||
.thumb_set I2C2_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C3_EV_IRQHandler
|
||||
.thumb_set I2C3_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C3_ER_IRQHandler
|
||||
.thumb_set I2C3_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C4_EV_IRQHandler
|
||||
.thumb_set I2C4_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C4_ER_IRQHandler
|
||||
.thumb_set I2C4_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I3C1_EV_IRQHandler
|
||||
.thumb_set I3C1_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I3C1_ER_IRQHandler
|
||||
.thumb_set I3C1_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I3C2_EV_IRQHandler
|
||||
.thumb_set I3C2_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I3C2_ER_IRQHandler
|
||||
.thumb_set I3C2_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_BRK_IRQHandler
|
||||
.thumb_set TIM1_BRK_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_UP_IRQHandler
|
||||
.thumb_set TIM1_UP_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_TRG_CCU_IRQHandler
|
||||
.thumb_set TIM1_TRG_CCU_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_CC_IRQHandler
|
||||
.thumb_set TIM1_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM2_IRQHandler
|
||||
.thumb_set TIM2_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM3_IRQHandler
|
||||
.thumb_set TIM3_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM4_IRQHandler
|
||||
.thumb_set TIM4_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM5_IRQHandler
|
||||
.thumb_set TIM5_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM6_IRQHandler
|
||||
.thumb_set TIM6_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM7_IRQHandler
|
||||
.thumb_set TIM7_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_BRK_IRQHandler
|
||||
.thumb_set TIM8_BRK_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_UP_IRQHandler
|
||||
.thumb_set TIM8_UP_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_TRG_CCU_IRQHandler
|
||||
.thumb_set TIM8_TRG_CCU_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_CC_IRQHandler
|
||||
.thumb_set TIM8_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM9_IRQHandler
|
||||
.thumb_set TIM9_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM10_IRQHandler
|
||||
.thumb_set TIM10_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM11_IRQHandler
|
||||
.thumb_set TIM11_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM12_IRQHandler
|
||||
.thumb_set TIM12_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM13_IRQHandler
|
||||
.thumb_set TIM13_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM14_IRQHandler
|
||||
.thumb_set TIM14_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM15_IRQHandler
|
||||
.thumb_set TIM15_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM16_IRQHandler
|
||||
.thumb_set TIM16_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM17_IRQHandler
|
||||
.thumb_set TIM17_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM18_IRQHandler
|
||||
.thumb_set TIM18_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPTIM1_IRQHandler
|
||||
.thumb_set LPTIM1_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPTIM2_IRQHandler
|
||||
.thumb_set LPTIM2_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPTIM3_IRQHandler
|
||||
.thumb_set LPTIM3_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPTIM4_IRQHandler
|
||||
.thumb_set LPTIM4_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPTIM5_IRQHandler
|
||||
.thumb_set LPTIM5_IRQHandler,Default_Handler
|
||||
|
||||
.weak ADF1_FLT0_IRQHandler
|
||||
.thumb_set ADF1_FLT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDF1_FLT0_IRQHandler
|
||||
.thumb_set MDF1_FLT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDF1_FLT1_IRQHandler
|
||||
.thumb_set MDF1_FLT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDF1_FLT2_IRQHandler
|
||||
.thumb_set MDF1_FLT2_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDF1_FLT3_IRQHandler
|
||||
.thumb_set MDF1_FLT3_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDF1_FLT4_IRQHandler
|
||||
.thumb_set MDF1_FLT4_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDF1_FLT5_IRQHandler
|
||||
.thumb_set MDF1_FLT5_IRQHandler,Default_Handler
|
||||
|
||||
.weak SAI1_A_IRQHandler
|
||||
.thumb_set SAI1_A_IRQHandler,Default_Handler
|
||||
|
||||
.weak SAI1_B_IRQHandler
|
||||
.thumb_set SAI1_B_IRQHandler,Default_Handler
|
||||
|
||||
.weak SAI2_A_IRQHandler
|
||||
.thumb_set SAI2_A_IRQHandler,Default_Handler
|
||||
|
||||
.weak SAI2_B_IRQHandler
|
||||
.thumb_set SAI2_B_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPDIFRX_IRQHandler
|
||||
.thumb_set SPDIFRX_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI1_IRQHandler
|
||||
.thumb_set SPI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI2_IRQHandler
|
||||
.thumb_set SPI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI3_IRQHandler
|
||||
.thumb_set SPI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI4_IRQHandler
|
||||
.thumb_set SPI4_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI5_IRQHandler
|
||||
.thumb_set SPI5_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI6_IRQHandler
|
||||
.thumb_set SPI6_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART1_IRQHandler
|
||||
.thumb_set USART1_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART2_IRQHandler
|
||||
.thumb_set USART2_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART3_IRQHandler
|
||||
.thumb_set USART3_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART4_IRQHandler
|
||||
.thumb_set UART4_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART5_IRQHandler
|
||||
.thumb_set UART5_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART6_IRQHandler
|
||||
.thumb_set USART6_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART7_IRQHandler
|
||||
.thumb_set UART7_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART8_IRQHandler
|
||||
.thumb_set UART8_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART9_IRQHandler
|
||||
.thumb_set UART9_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART10_IRQHandler
|
||||
.thumb_set USART10_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPUART1_IRQHandler
|
||||
.thumb_set LPUART1_IRQHandler,Default_Handler
|
||||
|
||||
.weak XSPI1_IRQHandler
|
||||
.thumb_set XSPI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak XSPI2_IRQHandler
|
||||
.thumb_set XSPI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak XSPI3_IRQHandler
|
||||
.thumb_set XSPI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak FMC_IRQHandler
|
||||
.thumb_set FMC_IRQHandler,Default_Handler
|
||||
|
||||
.weak SDMMC1_IRQHandler
|
||||
.thumb_set SDMMC1_IRQHandler,Default_Handler
|
||||
|
||||
.weak SDMMC2_IRQHandler
|
||||
.thumb_set SDMMC2_IRQHandler,Default_Handler
|
||||
|
||||
.weak UCPD1_IRQHandler
|
||||
.thumb_set UCPD1_IRQHandler,Default_Handler
|
||||
|
||||
.weak USB1_OTG_HS_IRQHandler
|
||||
.thumb_set USB1_OTG_HS_IRQHandler,Default_Handler
|
||||
|
||||
.weak USB2_OTG_HS_IRQHandler
|
||||
.thumb_set USB2_OTG_HS_IRQHandler,Default_Handler
|
||||
|
||||
.weak ETH1_IRQHandler
|
||||
.thumb_set ETH1_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN1_IT0_IRQHandler
|
||||
.thumb_set FDCAN1_IT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN1_IT1_IRQHandler
|
||||
.thumb_set FDCAN1_IT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN2_IT0_IRQHandler
|
||||
.thumb_set FDCAN2_IT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN2_IT1_IRQHandler
|
||||
.thumb_set FDCAN2_IT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN3_IT0_IRQHandler
|
||||
.thumb_set FDCAN3_IT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN3_IT1_IRQHandler
|
||||
.thumb_set FDCAN3_IT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak FDCAN_CU_IRQHandler
|
||||
.thumb_set FDCAN_CU_IRQHandler,Default_Handler
|
||||
|
||||
.weak MDIOS_IRQHandler
|
||||
.thumb_set MDIOS_IRQHandler,Default_Handler
|
||||
|
||||
.weak DCMI_PSSI_IRQHandler
|
||||
.thumb_set DCMI_PSSI_IRQHandler,Default_Handler
|
||||
|
||||
.weak WAKEUP_PIN_IRQHandler
|
||||
.thumb_set WAKEUP_PIN_IRQHandler,Default_Handler
|
||||
|
||||
.weak CTI_INT0_IRQHandler
|
||||
.thumb_set CTI_INT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak CTI_INT1_IRQHandler
|
||||
.thumb_set CTI_INT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak LTDC_UP_IRQHandler
|
||||
.thumb_set LTDC_UP_IRQHandler,Default_Handler
|
||||
|
||||
.weak LTDC_UP_ERR_IRQHandler
|
||||
.thumb_set LTDC_UP_ERR_IRQHandler,Default_Handler
|
||||
|
||||
.weak SystemInit
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectonics *****END OF FILE****/
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* HAL configuration for the Betaflight N6 OpenBootloader. Mirrors the FSBL
|
||||
* stub's trimmed config and adds IWDG (boot watchdog armed before BF jump)
|
||||
* and PCD (USB device for the DFU loop).
|
||||
*/
|
||||
|
||||
#ifndef STM32N6xx_HAL_CONF_H
|
||||
#define STM32N6xx_HAL_CONF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HAL_MODULE_ENABLED
|
||||
#define HAL_CORTEX_MODULE_ENABLED
|
||||
#define HAL_RCC_MODULE_ENABLED
|
||||
#define HAL_PWR_MODULE_ENABLED
|
||||
#define HAL_GPIO_MODULE_ENABLED
|
||||
#define HAL_DMA_MODULE_ENABLED
|
||||
#define HAL_XSPI_MODULE_ENABLED
|
||||
#define HAL_IWDG_MODULE_ENABLED
|
||||
#define HAL_PCD_MODULE_ENABLED
|
||||
|
||||
#if !defined(HSE_VALUE)
|
||||
#define HSE_VALUE 48000000UL
|
||||
#endif
|
||||
#if !defined(HSE_STARTUP_TIMEOUT)
|
||||
#define HSE_STARTUP_TIMEOUT 100UL
|
||||
#endif
|
||||
#if !defined(LSE_VALUE)
|
||||
#define LSE_VALUE 32768UL
|
||||
#endif
|
||||
#if !defined(LSE_STARTUP_TIMEOUT)
|
||||
#define LSE_STARTUP_TIMEOUT 5000UL
|
||||
#endif
|
||||
#if !defined(MSI_VALUE)
|
||||
#define MSI_VALUE 4000000UL
|
||||
#endif
|
||||
#if !defined(HSI_VALUE)
|
||||
#define HSI_VALUE 64000000UL
|
||||
#endif
|
||||
#if !defined(LSI_VALUE)
|
||||
#define LSI_VALUE 32000UL
|
||||
#endif
|
||||
|
||||
#define VDD_VALUE 3300UL
|
||||
#define TICK_INT_PRIORITY 15U
|
||||
#define USE_RTOS 0U
|
||||
|
||||
#define USE_HAL_XSPI_REGISTER_CALLBACKS 0U
|
||||
#define USE_HAL_PWR_REGISTER_CALLBACKS 0U
|
||||
#define USE_SPI_CRC 0U
|
||||
|
||||
#ifdef HAL_RCC_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_rcc.h"
|
||||
#endif
|
||||
#ifdef HAL_GPIO_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_gpio.h"
|
||||
#endif
|
||||
#ifdef HAL_CORTEX_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_cortex.h"
|
||||
#endif
|
||||
#ifdef HAL_PWR_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_pwr.h"
|
||||
#endif
|
||||
#ifdef HAL_DMA_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_dma.h"
|
||||
#endif
|
||||
#ifdef HAL_XSPI_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_xspi.h"
|
||||
#endif
|
||||
#ifdef HAL_IWDG_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_iwdg.h"
|
||||
#endif
|
||||
#ifdef HAL_PCD_MODULE_ENABLED
|
||||
#include "stm32n6xx_hal_pcd.h"
|
||||
#endif
|
||||
|
||||
#define assert_param(expr) ((void)0U)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* STM32N6xx_HAL_CONF_H */
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Minimal HAL MSP for the FSBL stub. Only XSPI2's MSP is non-trivial: it
|
||||
* enables the peripheral + XSPIM + GPION clocks and configures the eleven
|
||||
* GPIO N pins for XSPIM_P2. The boot ROM already picked a working XSPI2
|
||||
* clock source; we don't reconfigure it. 1S-1S-1S READ at the boot-ROM rate
|
||||
* is sufficient for XIP and works regardless of HSLV fuse state, so no BSEC
|
||||
* read is needed.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
void HAL_MspInit(void)
|
||||
{
|
||||
HAL_PWREx_EnableVddIO3();
|
||||
}
|
||||
|
||||
void HAL_XSPI_MspInit(XSPI_HandleTypeDef *hxspi)
|
||||
{
|
||||
if (hxspi->Instance != XSPI2) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
/* PWR clock is left on by the boot ROM. __HAL_RCC_PWR_CLK_ENABLE
|
||||
* lives behind CPU_IN_SECURE_STATE in stm32n6xx_hal_rcc.h, which pulls
|
||||
* in HAL_MPU_*_NS variants that require -mcmse. */
|
||||
HAL_PWREx_EnableVddIO3();
|
||||
HAL_PWREx_ConfigVddIORange(PWR_VDDIO3, PWR_VDDIO_RANGE_1V8);
|
||||
|
||||
__HAL_RCC_XSPIM_CLK_ENABLE();
|
||||
__HAL_RCC_XSPI2_CLK_ENABLE();
|
||||
__HAL_RCC_GPION_CLK_ENABLE();
|
||||
|
||||
/* PN0 -> DQS0, PN1 -> NCS1, PN2..PN5 -> IO0..IO3, PN6 -> CLK,
|
||||
* PN8..PN11 -> IO4..IO7. AF9 = XSPIM_P2 on every pin in this bank. */
|
||||
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3
|
||||
| GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_8
|
||||
| GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
GPIO_InitStruct.Alternate = GPIO_AF9_XSPIM_P2;
|
||||
HAL_GPIO_Init(GPION, &GPIO_InitStruct);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Interrupt handlers for the FSBL stub. Only SysTick is wired up — HAL_Init
|
||||
* starts a 1 ms tick that HAL_XSPI_Command and friends use for timeout
|
||||
* accounting. Everything else stays defaulted to the spin-loops in the
|
||||
* startup file (the FSBL doesn't enable any interrupts beyond SysTick).
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
HAL_IncTick();
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* CMSIS system file for the Betaflight N6 OpenBootloader.
|
||||
*
|
||||
* Built with -mcmse so __ARM_FEATURE_CMSE=3 and the CMSIS headers resolve
|
||||
* every peripheral pointer (RCC, RIFSC, GPIOx, DBGMCU, TAMP, PWR, RTC,
|
||||
* EXTI, GPDMA1, HPDMA1, RNG, SYSCFG, ...) to its secure-alias base. OBL
|
||||
* is the only S-state code on the device, so all of its peripheral
|
||||
* accesses must go through the secure aliases.
|
||||
*
|
||||
* SystemInit opens every relevant slave + master so the NS application
|
||||
* can reach RAM, XSPI memory-mapped flash and AHB/APB peripherals after
|
||||
* the BXNS hand-off in jump_to_bf().
|
||||
*
|
||||
* SystemClock_Config_Impl is called from main() to lift the CPU from
|
||||
* the boot-ROM hand-off clock (HSI/4 ≈ 16 MHz) to 400 MHz so USB OTG_HS
|
||||
* gets a clean SOF stream when the OBL DFU branch runs.
|
||||
*/
|
||||
|
||||
#include "stm32n6xx.h"
|
||||
#include <math.h>
|
||||
|
||||
#if !defined(HSE_VALUE)
|
||||
#define HSE_VALUE 48000000UL
|
||||
#endif
|
||||
#if !defined(HSI_VALUE)
|
||||
#define HSI_VALUE 64000000UL
|
||||
#endif
|
||||
#if !defined(MSI_VALUE)
|
||||
#define MSI_VALUE 4000000UL
|
||||
#endif
|
||||
#if !defined(EXTERNAL_I2S_CLOCK_VALUE)
|
||||
#define EXTERNAL_I2S_CLOCK_VALUE 12288000UL
|
||||
#endif
|
||||
|
||||
uint32_t SystemCoreClock = HSI_VALUE;
|
||||
|
||||
extern void *g_pfnVectors;
|
||||
#define INTVECT_START ((uint32_t)&g_pfnVectors)
|
||||
|
||||
void SystemInit(void)
|
||||
{
|
||||
SCB->VTOR = INTVECT_START;
|
||||
|
||||
/* Caches stay off throughout OBL: the boot decision reads XSPI flash
|
||||
* and we want fresh fetches; BF re-enables caches in its own startup. */
|
||||
SCB_DisableICache();
|
||||
SCB_InvalidateICache();
|
||||
SCB_DisableDCache();
|
||||
SCB_InvalidateDCache();
|
||||
|
||||
/* Debug subsystem clock. */
|
||||
RCC->MISCENSR = RCC_MISCENSR_DBGENS;
|
||||
(void)RCC->MISCENR;
|
||||
|
||||
/* APB3 bus clock — DBGMCU lives on APB3 and accesses there read 0 /
|
||||
* silently drop until the bus is ungated. */
|
||||
RCC->BUSENSR = RCC_BUSENSR_APB3ENS;
|
||||
(void)RCC->BUSENR;
|
||||
|
||||
/* RIFSC: every peripheral slave NS+Unpriv, debug-AP master CID 0. */
|
||||
for (uint32_t i = 0; i < 6U; i++) {
|
||||
RIFSC->RISC_SECCFGRx[i] = 0;
|
||||
RIFSC->RISC_PRIVCFGRx[i] = 0;
|
||||
}
|
||||
RIFSC->RIMC_CR &= ~0x7UL;
|
||||
(void)RIFSC->RIMC_CR;
|
||||
|
||||
/* DBGMCU.CR: hold the M55 debuggable across run / sleep / stop /
|
||||
* standby so SWD attach to running user code works on OPEN-lifecycle
|
||||
* silicon. */
|
||||
DBGMCU->CR = DBGMCU_CR_DBGCLKEN
|
||||
| DBGMCU_CR_DBG_SLEEP
|
||||
| DBGMCU_CR_DBG_STOP
|
||||
| DBGMCU_CR_DBG_STANDBY;
|
||||
(void)DBGMCU->CR;
|
||||
|
||||
/* TAMP is RIF-aware: its security/priv gating lives in its own
|
||||
* SECCFGR/PRIVCFGR (not RIFSC). Opening to NS+Unpriv exposes all
|
||||
* 32 BKPxR for read/write from NS world. */
|
||||
TAMP->SECCFGR = 0;
|
||||
TAMP->PRIVCFGR = 0;
|
||||
(void)TAMP->SECCFGR;
|
||||
|
||||
/* GPIO SECCFGR + PRIVCFGR per port — every pin NS + Unpriv. Without
|
||||
* these the NS application's pinmux for SPI / I2C / UART / etc. is
|
||||
* silently rejected even with RIFSC opened. The bank-enable on
|
||||
* AHB4ENSR has to happen first: writes to a GPIO bank with its
|
||||
* clock gated off silently drop on the N6, so the SECCFGR loop
|
||||
* below would no-op for any port the boot ROM hadn't already
|
||||
* ungated. */
|
||||
RCC->AHB4ENSR = RCC_AHB4ENR_GPIOAEN_Msk | RCC_AHB4ENR_GPIOBEN_Msk
|
||||
| RCC_AHB4ENR_GPIOCEN_Msk | RCC_AHB4ENR_GPIODEN_Msk
|
||||
| RCC_AHB4ENR_GPIOEEN_Msk | RCC_AHB4ENR_GPIOFEN_Msk
|
||||
| RCC_AHB4ENR_GPIOGEN_Msk | RCC_AHB4ENR_GPIOHEN_Msk
|
||||
| RCC_AHB4ENR_GPIONEN_Msk | RCC_AHB4ENR_GPIOOEN_Msk
|
||||
| RCC_AHB4ENR_GPIOPEN_Msk | RCC_AHB4ENR_GPIOQEN_Msk;
|
||||
(void)RCC->AHB4ENR;
|
||||
|
||||
static GPIO_TypeDef * const gpio_banks[] = {
|
||||
GPIOA, GPIOB, GPIOC, GPIOD,
|
||||
GPIOE, GPIOF, GPIOG, GPIOH,
|
||||
GPION, GPIOO, GPIOP, GPIOQ,
|
||||
};
|
||||
for (unsigned i = 0; i < sizeof(gpio_banks) / sizeof(gpio_banks[0]); i++) {
|
||||
gpio_banks[i]->SECCFGR = 0;
|
||||
gpio_banks[i]->PRIVCFGR = 0;
|
||||
}
|
||||
|
||||
/* GPDMA1 + HPDMA1: clock-enable first (boot ROM hands them off
|
||||
* ungated) then open per-controller SECCFGR + PRIVCFGR. SECCFGR is
|
||||
* a bit-per-channel register at controller +0x00; writing 0 routes
|
||||
* every channel under NS attribution. */
|
||||
RCC->AHB1ENSR = RCC_AHB1ENSR_GPDMA1ENS;
|
||||
RCC->AHB5ENSR = RCC_AHB5ENSR_HPDMA1ENS;
|
||||
GPDMA1->SECCFGR = 0;
|
||||
GPDMA1->PRIVCFGR = 0;
|
||||
HPDMA1->SECCFGR = 0;
|
||||
HPDMA1->PRIVCFGR = 0;
|
||||
|
||||
/* Per-peripheral SECCFGR + PRIVCFGR — peripherals carrying their
|
||||
* own security config above RIFSC. Clearing each = NS + Unpriv. */
|
||||
RCC->SECCFGR0 = 0;
|
||||
RCC->SECCFGR1 = 0;
|
||||
RCC->SECCFGR2 = 0;
|
||||
RCC->SECCFGR3 = 0;
|
||||
RCC->SECCFGR4 = 0;
|
||||
PWR->SECCFGR = 0;
|
||||
PWR->PRIVCFGR = 0;
|
||||
EXTI->SECCFGR1 = 0;
|
||||
EXTI->PRIVCFGR1 = 0;
|
||||
EXTI->SECCFGR2 = 0;
|
||||
EXTI->PRIVCFGR2 = 0;
|
||||
EXTI->SECCFGR3 = 0;
|
||||
EXTI->PRIVCFGR3 = 0;
|
||||
RTC->SECCFGR = 0;
|
||||
|
||||
/* RIMC master attributes — every bus master (CPU, DMAs, USB, ETH, …)
|
||||
* presents as NS + Unpriv + CID 0 by default. */
|
||||
for (unsigned i = 0; i < 13U; i++) {
|
||||
RIFSC->RIMC_ATTRx[i] = 0;
|
||||
}
|
||||
|
||||
/* RNG reset + clock-disable. The boot ROM may have left it ticking. */
|
||||
RCC->AHB3RSTSR = RCC_AHB3RSTSR_RNGRSTS;
|
||||
RCC->AHB3RSTCR = RCC_AHB3RSTCR_RNGRSTC;
|
||||
RCC->AHB3ENCR = RCC_AHB3ENCR_RNGENC;
|
||||
|
||||
/* SYSCFG clock + VDDIOx supply rails (errata ES0620). */
|
||||
RCC->APB4ENSR2 = RCC_APB4ENSR2_SYSCFGENS;
|
||||
(void)RCC->APB4ENR2;
|
||||
|
||||
SYSCFG->INITSVTORCR = SCB->VTOR;
|
||||
|
||||
PWR->SVMCR1 |= PWR_SVMCR1_VDDIO4SV;
|
||||
PWR->SVMCR2 |= PWR_SVMCR2_VDDIO5SV;
|
||||
PWR->SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV;
|
||||
|
||||
SYSCFG->VDDIO2CCCR = 0x00000287UL;
|
||||
SYSCFG->VDDIO3CCCR = 0x00000287UL;
|
||||
SYSCFG->VDDIO4CCCR = 0x00000287UL;
|
||||
SYSCFG->VDDIO5CCCR = 0x00000287UL;
|
||||
SYSCFG->VDDCCCR = 0x00000287UL;
|
||||
|
||||
/* VDDADC clamp + VREF buffer. */
|
||||
PWR->SVMCR3 |= PWR_SVMCR3_ASV;
|
||||
PWR->SVMCR3 |= PWR_SVMCR3_AVMEN;
|
||||
(void)PWR->SVMCR3;
|
||||
RCC->APB4ENR1 |= RCC_APB4ENR1_VREFBUFEN;
|
||||
|
||||
/* Pulse APB4ENR2 bit 4 — required by ST's reference bring-up to
|
||||
* lower power; no documented register name. */
|
||||
RCC->APB4ENR2 |= 0x00000010UL;
|
||||
(void)RCC->APB4ENR2;
|
||||
RCC->APB4ENR2 &= ~0x00000010UL;
|
||||
|
||||
/* LSI on. IWDG is clocked from LSI exclusively; without this the
|
||||
* watchdog hardware never ticks and a wedged BF never resets back
|
||||
* into OBL recovery. Boot ROM doesn't reliably leave LSI on. */
|
||||
RCC->CSR = RCC_CSR_LSIONS;
|
||||
while ((RCC->SR & RCC_SR_LSIRDY) == 0U) {
|
||||
;
|
||||
}
|
||||
|
||||
/* Reset XSPI2 + XSPIM so we start from a known state regardless of how
|
||||
* boot ROM left them after loading us. */
|
||||
RCC->AHB5RSTSR = RCC_AHB5RSTSR_XSPIMRSTS | RCC_AHB5RSTSR_XSPI2RSTS;
|
||||
RCC->AHB5RSTCR = RCC_AHB5RSTCR_XSPIMRSTC | RCC_AHB5RSTCR_XSPI2RSTC;
|
||||
|
||||
/* TIM2 reset + clock-disable. */
|
||||
RCC->APB1RSTSR1 = RCC_APB1RSTSR1_TIM2RSTS;
|
||||
RCC->APB1RSTCR1 = RCC_APB1RSTCR1_TIM2RSTC;
|
||||
RCC->APB1ENCR1 = RCC_APB1ENCR1_TIM2ENC;
|
||||
|
||||
/* Enable AXISRAM1..6 clocks. Boot ROM only clocks AXISRAM2; the rest
|
||||
* must be brought up here or the first store from a consumer of
|
||||
* those banks busfaults silently. */
|
||||
RCC->MEMENSR = RCC_MEMENSR_AXISRAM1ENS | RCC_MEMENSR_AXISRAM2ENS
|
||||
| RCC_MEMENSR_AXISRAM3ENS | RCC_MEMENSR_AXISRAM4ENS
|
||||
| RCC_MEMENSR_AXISRAM5ENS | RCC_MEMENSR_AXISRAM6ENS;
|
||||
(void)RCC->MEMENR;
|
||||
|
||||
(void)SYSCFG->INITSVTORCR;
|
||||
RCC->APB4ENCR2 = RCC_APB4ENCR2_SYSCFGENC;
|
||||
|
||||
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
|
||||
SCB->CPACR |= ((3UL << 20U) | (3UL << 22U)); /* CP10/CP11 full S access */
|
||||
#endif
|
||||
|
||||
/* NSACR — bits 10/11 grant NS access to CP10/CP11 (FPU + MVE).
|
||||
* 0x0FFF is the broadest "NS can touch every implemented CP" set;
|
||||
* the harmless bits 0..7 hand future-proof access. NSACR is
|
||||
* Secure-only-writable so it has to be set here, before the BXNS
|
||||
* hand-off. (AIRCR.BFHFNMINS is deferred to jump_to_bf — flipping it
|
||||
* here routes any S-side fault during OBL's own remaining execution
|
||||
* to an NS state that doesn't yet have a vector table or stack,
|
||||
* which deterministically wedges the chip into LOCKUP.) */
|
||||
SCB->NSACR = 0x00000FFFUL;
|
||||
}
|
||||
|
||||
void SystemCoreClockUpdate(void)
|
||||
{
|
||||
uint32_t sysclk = 0;
|
||||
uint32_t pllm = 0;
|
||||
uint32_t plln = 0;
|
||||
uint32_t pllfracn = 0;
|
||||
uint32_t pllp1 = 0;
|
||||
uint32_t pllp2 = 0;
|
||||
uint32_t pllcfgr;
|
||||
uint32_t pllsource = 0;
|
||||
uint32_t pllbypass = 0;
|
||||
uint32_t ic_divider;
|
||||
float_t pllvco;
|
||||
|
||||
switch (RCC->CFGR1 & RCC_CFGR1_CPUSWS) {
|
||||
case 0:
|
||||
sysclk = HSI_VALUE >> ((RCC->HSICFGR & RCC_HSICFGR_HSIDIV) >> RCC_HSICFGR_HSIDIV_Pos);
|
||||
break;
|
||||
|
||||
case RCC_CFGR1_CPUSWS_0:
|
||||
sysclk = (READ_BIT(RCC->MSICFGR, RCC_MSICFGR_MSIFREQSEL) == 0UL) ? MSI_VALUE : 16000000UL;
|
||||
break;
|
||||
|
||||
case RCC_CFGR1_CPUSWS_1:
|
||||
sysclk = HSE_VALUE;
|
||||
break;
|
||||
|
||||
case (RCC_CFGR1_CPUSWS_1 | RCC_CFGR1_CPUSWS_0):
|
||||
switch (READ_BIT(RCC->IC1CFGR, RCC_IC1CFGR_IC1SEL)) {
|
||||
case 0:
|
||||
pllcfgr = READ_REG(RCC->PLL1CFGR1);
|
||||
pllsource = pllcfgr & RCC_PLL1CFGR1_PLL1SEL;
|
||||
pllbypass = pllcfgr & RCC_PLL1CFGR1_PLL1BYP;
|
||||
if (pllbypass == 0U) {
|
||||
pllm = (pllcfgr & RCC_PLL1CFGR1_PLL1DIVM) >> RCC_PLL1CFGR1_PLL1DIVM_Pos;
|
||||
plln = (pllcfgr & RCC_PLL1CFGR1_PLL1DIVN) >> RCC_PLL1CFGR1_PLL1DIVN_Pos;
|
||||
pllfracn = READ_BIT(RCC->PLL1CFGR2, RCC_PLL1CFGR2_PLL1DIVNFRAC) >> RCC_PLL1CFGR2_PLL1DIVNFRAC_Pos;
|
||||
pllcfgr = READ_REG(RCC->PLL1CFGR3);
|
||||
pllp1 = (pllcfgr & RCC_PLL1CFGR3_PLL1PDIV1) >> RCC_PLL1CFGR3_PLL1PDIV1_Pos;
|
||||
pllp2 = (pllcfgr & RCC_PLL1CFGR3_PLL1PDIV2) >> RCC_PLL1CFGR3_PLL1PDIV2_Pos;
|
||||
}
|
||||
break;
|
||||
case RCC_IC1CFGR_IC1SEL_0:
|
||||
pllcfgr = READ_REG(RCC->PLL2CFGR1);
|
||||
pllsource = pllcfgr & RCC_PLL2CFGR1_PLL2SEL;
|
||||
pllbypass = pllcfgr & RCC_PLL2CFGR1_PLL2BYP;
|
||||
if (pllbypass == 0U) {
|
||||
pllm = (pllcfgr & RCC_PLL2CFGR1_PLL2DIVM) >> RCC_PLL2CFGR1_PLL2DIVM_Pos;
|
||||
plln = (pllcfgr & RCC_PLL2CFGR1_PLL2DIVN) >> RCC_PLL2CFGR1_PLL2DIVN_Pos;
|
||||
pllfracn = READ_BIT(RCC->PLL2CFGR2, RCC_PLL2CFGR2_PLL2DIVNFRAC) >> RCC_PLL2CFGR2_PLL2DIVNFRAC_Pos;
|
||||
pllcfgr = READ_REG(RCC->PLL2CFGR3);
|
||||
pllp1 = (pllcfgr & RCC_PLL2CFGR3_PLL2PDIV1) >> RCC_PLL2CFGR3_PLL2PDIV1_Pos;
|
||||
pllp2 = (pllcfgr & RCC_PLL2CFGR3_PLL2PDIV2) >> RCC_PLL2CFGR3_PLL2PDIV2_Pos;
|
||||
}
|
||||
break;
|
||||
case RCC_IC1CFGR_IC1SEL_1:
|
||||
pllcfgr = READ_REG(RCC->PLL3CFGR1);
|
||||
pllsource = pllcfgr & RCC_PLL3CFGR1_PLL3SEL;
|
||||
pllbypass = pllcfgr & RCC_PLL3CFGR1_PLL3BYP;
|
||||
if (pllbypass == 0U) {
|
||||
pllm = (pllcfgr & RCC_PLL3CFGR1_PLL3DIVM) >> RCC_PLL3CFGR1_PLL3DIVM_Pos;
|
||||
plln = (pllcfgr & RCC_PLL3CFGR1_PLL3DIVN) >> RCC_PLL3CFGR1_PLL3DIVN_Pos;
|
||||
pllfracn = READ_BIT(RCC->PLL3CFGR2, RCC_PLL3CFGR2_PLL3DIVNFRAC) >> RCC_PLL3CFGR2_PLL3DIVNFRAC_Pos;
|
||||
pllcfgr = READ_REG(RCC->PLL3CFGR3);
|
||||
pllp1 = (pllcfgr & RCC_PLL3CFGR3_PLL3PDIV1) >> RCC_PLL3CFGR3_PLL3PDIV1_Pos;
|
||||
pllp2 = (pllcfgr & RCC_PLL3CFGR3_PLL3PDIV2) >> RCC_PLL3CFGR3_PLL3PDIV2_Pos;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pllcfgr = READ_REG(RCC->PLL4CFGR1);
|
||||
pllsource = pllcfgr & RCC_PLL4CFGR1_PLL4SEL;
|
||||
pllbypass = pllcfgr & RCC_PLL4CFGR1_PLL4BYP;
|
||||
if (pllbypass == 0U) {
|
||||
pllm = (pllcfgr & RCC_PLL4CFGR1_PLL4DIVM) >> RCC_PLL4CFGR1_PLL4DIVM_Pos;
|
||||
plln = (pllcfgr & RCC_PLL4CFGR1_PLL4DIVN) >> RCC_PLL4CFGR1_PLL4DIVN_Pos;
|
||||
pllfracn = READ_BIT(RCC->PLL4CFGR2, RCC_PLL4CFGR2_PLL4DIVNFRAC) >> RCC_PLL4CFGR2_PLL4DIVNFRAC_Pos;
|
||||
pllcfgr = READ_REG(RCC->PLL4CFGR3);
|
||||
pllp1 = (pllcfgr & RCC_PLL4CFGR3_PLL4PDIV1) >> RCC_PLL4CFGR3_PLL4PDIV1_Pos;
|
||||
pllp2 = (pllcfgr & RCC_PLL4CFGR3_PLL4PDIV2) >> RCC_PLL4CFGR3_PLL4PDIV2_Pos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pllsource) {
|
||||
case 0:
|
||||
sysclk = HSI_VALUE >> ((RCC->HSICFGR & RCC_HSICFGR_HSIDIV) >> RCC_HSICFGR_HSIDIV_Pos);
|
||||
break;
|
||||
case RCC_PLL1CFGR1_PLL1SEL_0:
|
||||
sysclk = (READ_BIT(RCC->MSICFGR, RCC_MSICFGR_MSIFREQSEL) == 0UL) ? MSI_VALUE : 16000000UL;
|
||||
break;
|
||||
case RCC_PLL1CFGR1_PLL1SEL_1:
|
||||
sysclk = HSE_VALUE;
|
||||
break;
|
||||
case (RCC_PLL1CFGR1_PLL1SEL_1 | RCC_PLL1CFGR1_PLL1SEL_0):
|
||||
sysclk = EXTERNAL_I2S_CLOCK_VALUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pllbypass == 0U) {
|
||||
pllvco = ((float_t)sysclk * ((float_t)plln + ((float_t)pllfracn / (float_t)0x1000000UL))) / (float_t)pllm;
|
||||
sysclk = (uint32_t)((float_t)(pllvco / (((float_t)pllp1) * ((float_t)pllp2))));
|
||||
}
|
||||
ic_divider = (READ_BIT(RCC->IC1CFGR, RCC_IC1CFGR_IC1INT) >> RCC_IC1CFGR_IC1INT_Pos) + 1UL;
|
||||
sysclk = sysclk / ic_divider;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SystemCoreClock = sysclk;
|
||||
}
|
||||
|
||||
/*
|
||||
* SystemClock_Config_Impl — bring the CPU up to a USB-friendly speed.
|
||||
*
|
||||
* Lifted from CubeN6's OBL reference (Projects/STM32N6570-DK/.../OBL/
|
||||
* Core/Src/main.c::SystemClock_Config). Targets:
|
||||
* PLL1 VCO = HSE 48 MHz / PLLM 3 * PLLN 50 = 800 MHz
|
||||
* PLL1 output = VCO / (PLLP1 1 * PLLP2 1) = 800 MHz
|
||||
* CPU = IC1 /2 = 400 MHz
|
||||
* SYSCLK = IC2/IC6/IC11 /2 = 400 MHz (AXI / NPU / AXISRAM3..6)
|
||||
* HCLK = SYSCLK /2 = 200 MHz
|
||||
* PCLK1..5 = HCLK / 1 = 200 MHz
|
||||
* XSPI2 kclk = IC3 = PLL1 / 24 = 33 MHz (sufficient for indirect-mode
|
||||
* program / erase; XIP reads happen via the same kclk
|
||||
* once memory-mapped mode is on)
|
||||
* USB1_OTG_HS = HSE_DIRECT / 2 (handled in usbd_conf.c::HAL_PCD_MspInit)
|
||||
* USBPHYC_CR.FSEL = 0b010 (24 MHz) — set in HAL_PCD_MspInit
|
||||
*/
|
||||
void SystemClock_Config_Impl(void)
|
||||
{
|
||||
RCC_OscInitTypeDef osc = {0};
|
||||
RCC_ClkInitTypeDef clk = {0};
|
||||
RCC_PeriphCLKInitTypeDef pclk = {0};
|
||||
|
||||
/* Boot ROM may hand off with PLL1 already running (it cranks PLL1
|
||||
* for fast XSPI staging on the FSBL-load path). HAL_RCC_OscConfig
|
||||
* refuses to reconfigure PLL1 while it's the active source, so
|
||||
* we tear the clock tree back to defaults first. Caches must be
|
||||
* disabled across the teardown — boot ROM leaves stale lines, and
|
||||
* an instruction-prefetch refill during the CPU-clock switch from
|
||||
* PLL1 to HSI bus-faults and escalates to LOCKUP. */
|
||||
SCB_DisableICache();
|
||||
SCB_DisableDCache();
|
||||
System_DeInit();
|
||||
|
||||
osc.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_HSE;
|
||||
osc.HSIState = RCC_HSI_ON;
|
||||
osc.HSIDiv = RCC_HSI_DIV1;
|
||||
osc.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
|
||||
osc.HSEState = RCC_HSE_ON;
|
||||
|
||||
osc.PLL1.PLLState = RCC_PLL_ON;
|
||||
osc.PLL1.PLLSource = RCC_PLLSOURCE_HSE;
|
||||
osc.PLL1.PLLM = 3;
|
||||
osc.PLL1.PLLN = 50;
|
||||
osc.PLL1.PLLP1 = 1;
|
||||
osc.PLL1.PLLP2 = 1;
|
||||
osc.PLL1.PLLFractional = 0;
|
||||
|
||||
osc.PLL2.PLLState = RCC_PLL_OFF;
|
||||
osc.PLL3.PLLState = RCC_PLL_OFF;
|
||||
osc.PLL4.PLLState = RCC_PLL_OFF;
|
||||
|
||||
if (HAL_RCC_OscConfig(&osc) != HAL_OK) {
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
clk.ClockType = RCC_CLOCKTYPE_CPUCLK | RCC_CLOCKTYPE_SYSCLK
|
||||
| RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1
|
||||
| RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_PCLK4
|
||||
| RCC_CLOCKTYPE_PCLK5;
|
||||
clk.CPUCLKSource = RCC_CPUCLKSOURCE_IC1;
|
||||
clk.SYSCLKSource = RCC_SYSCLKSOURCE_IC2_IC6_IC11;
|
||||
|
||||
clk.IC1Selection.ClockSelection = RCC_ICCLKSOURCE_PLL1;
|
||||
clk.IC1Selection.ClockDivider = 2;
|
||||
clk.IC11Selection.ClockSelection = RCC_ICCLKSOURCE_PLL1;
|
||||
clk.IC11Selection.ClockDivider = 2;
|
||||
clk.IC2Selection.ClockSelection = RCC_ICCLKSOURCE_PLL1;
|
||||
clk.IC2Selection.ClockDivider = 2;
|
||||
clk.IC6Selection.ClockSelection = RCC_ICCLKSOURCE_PLL1;
|
||||
clk.IC6Selection.ClockDivider = 2;
|
||||
|
||||
clk.AHBCLKDivider = RCC_HCLK_DIV2;
|
||||
clk.APB1CLKDivider = RCC_APB1_DIV1;
|
||||
clk.APB2CLKDivider = RCC_APB2_DIV1;
|
||||
clk.APB4CLKDivider = RCC_APB4_DIV1;
|
||||
clk.APB5CLKDivider = RCC_APB5_DIV1;
|
||||
|
||||
if (HAL_RCC_ClockConfig(&clk) != HAL_OK) {
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
/* XSPI2 kernel clock from IC3 = PLL1 / 24 = 33 MHz. Slow vs the
|
||||
* 133 MHz the chip can handle, but reliable for command-mode
|
||||
* programming and we don't need XIP throughput in OBL. */
|
||||
pclk.PeriphClockSelection = RCC_PERIPHCLK_XSPI2;
|
||||
pclk.Xspi2ClockSelection = RCC_XSPI2CLKSOURCE_IC3;
|
||||
pclk.ICSelection[RCC_IC3].ClockSelection = RCC_ICCLKSOURCE_PLL1;
|
||||
pclk.ICSelection[RCC_IC3].ClockDivider = 24;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&pclk) != HAL_OK) {
|
||||
while (1) {}
|
||||
}
|
||||
__HAL_RCC_XSPI2_CLK_ENABLE();
|
||||
|
||||
SystemCoreClockUpdate();
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Override of CubeN6's usbd_conf.c.
|
||||
*
|
||||
* The submodule version uses __HAL_RCC_PWR_CLK_ENABLE / GPIOA_CLK_ENABLE
|
||||
* macros that live behind CPU_IN_SECURE_STATE in stm32n6xx_hal_rcc.h
|
||||
* (which pull in -mcmse-only HAL_MPU_*_NS variants). Our build doesn't
|
||||
* define CPU_IN_SECURE_STATE — see feedback_n6_peripheral_alias_cmse.md.
|
||||
* This file replaces those macro calls with direct RCC->AHB4ENSR writes
|
||||
* (matching the convention in main.c::tamp_clk_enable and the FSBL
|
||||
* stub's system_stm32n6xx_fsbl.c).
|
||||
*
|
||||
* Everything else is a verbatim copy of the upstream version — the
|
||||
* PCD↔USBD callback bridges, USBD_LL_* shims around HAL_PCD_*, USB
|
||||
* device init with embedded HS-PHY in FS speed.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "usbd_dfu.h"
|
||||
|
||||
PCD_HandleTypeDef hpcd_USB_HS;
|
||||
|
||||
/* ---------------- IRQ ---------------- */
|
||||
|
||||
void USB1_OTG_HS_IRQHandler(void)
|
||||
{
|
||||
HAL_PCD_IRQHandler(&hpcd_USB_HS);
|
||||
}
|
||||
|
||||
/* ---------------- PCD MSP ---------------- */
|
||||
|
||||
void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
if (hpcd->Instance != USB1_OTG_HS) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* PWR clock — direct register write avoids the CMSE-gated HAL macro. */
|
||||
SET_BIT(RCC->AHB4ENSR, RCC_AHB4ENSR_PWRENS);
|
||||
(void)RCC->AHB4ENR;
|
||||
|
||||
/* VDD33USB independent USB voltage monitor. The PWREx_ helpers are
|
||||
* NS-safe (no CMSE-gated macros internally). */
|
||||
HAL_PWREx_EnableVddUSBVMEN();
|
||||
while (__HAL_PWR_GET_FLAG(PWR_FLAG_USB33RDY) == 0U) {
|
||||
;
|
||||
}
|
||||
HAL_PWREx_EnableVddUSB();
|
||||
|
||||
/* USB1 OTG HS kernel clock from HSE_DIRECT (48 MHz on N6570-DK). */
|
||||
RCC_PeriphCLKInitTypeDef pclk = {0};
|
||||
pclk.PeriphClockSelection = RCC_PERIPHCLK_USBOTGHS1;
|
||||
pclk.UsbOtgHs1ClockSelection = RCC_USBOTGHS1CLKSOURCE_HSE_DIRECT;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&pclk) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
/* USB OTG HS PHY1 reference clock from HSE_DIRECT. */
|
||||
pclk.PeriphClockSelection = RCC_PERIPHCLK_USBPHY1;
|
||||
pclk.UsbPhy1ClockSelection = RCC_USBPHY1CLKSOURCE_HSE_DIRECT;
|
||||
if (HAL_RCCEx_PeriphCLKConfig(&pclk) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
/* GPIOA clock — direct register write (DP/DM are PA11/PA12). */
|
||||
SET_BIT(RCC->AHB4ENSR, RCC_AHB4ENSR_GPIOAENS);
|
||||
(void)RCC->AHB4ENR;
|
||||
|
||||
LL_AHB5_GRP1_ForceReset(RCC_AHB5RSTR_OTG1PHYCTLRST);
|
||||
__HAL_RCC_USB1_OTG_HS_FORCE_RESET();
|
||||
__HAL_RCC_USB1_OTG_HS_PHY_FORCE_RESET();
|
||||
|
||||
LL_RCC_HSE_SelectHSEDiv2AsDiv2Clock();
|
||||
LL_AHB5_GRP1_ReleaseReset(RCC_AHB5RSTR_OTG1PHYCTLRST);
|
||||
|
||||
__HAL_RCC_USB1_OTG_HS_CLK_ENABLE();
|
||||
|
||||
/* USBPHYC FSEL = 0b010 (24 MHz reference). HAL never writes this and
|
||||
* the default 0b001 silently drops every SETUP packet — see
|
||||
* feedback_n6_usbphyc_fsel.md. */
|
||||
USB1_HS_PHYC->USBPHYC_CR &= ~(0x7U << 0x4U);
|
||||
USB1_HS_PHYC->USBPHYC_CR |= (0x2U << 0x4U);
|
||||
|
||||
__HAL_RCC_USB1_OTG_HS_PHY_RELEASE_RESET();
|
||||
HAL_Delay(1);
|
||||
__HAL_RCC_USB1_OTG_HS_RELEASE_RESET();
|
||||
__HAL_RCC_USB1_OTG_HS_PHY_CLK_ENABLE();
|
||||
|
||||
HAL_NVIC_SetPriority(USB1_OTG_HS_IRQn, 6, 0);
|
||||
HAL_NVIC_EnableIRQ(USB1_OTG_HS_IRQn);
|
||||
}
|
||||
|
||||
void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
if (hpcd->Instance != USB1_OTG_HS) {
|
||||
return;
|
||||
}
|
||||
HAL_PWREx_DisableVddUSBVMEN();
|
||||
HAL_PWREx_DisableVddUSB();
|
||||
|
||||
__HAL_RCC_USB1_OTG_HS_CLK_DISABLE();
|
||||
__HAL_RCC_USB1_OTG_HS_PHY_CLK_DISABLE();
|
||||
|
||||
HAL_NVIC_DisableIRQ(USB1_OTG_HS_IRQn);
|
||||
}
|
||||
|
||||
/* ---------------- PCD → USBD callbacks ---------------- */
|
||||
|
||||
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_LL_SetupStage(hpcd->pData, (uint8_t *)hpcd->Setup);
|
||||
}
|
||||
|
||||
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
|
||||
{
|
||||
USBD_LL_DataOutStage(hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
|
||||
}
|
||||
|
||||
void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
|
||||
{
|
||||
USBD_LL_DataInStage(hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff);
|
||||
}
|
||||
|
||||
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_LL_SOF(hpcd->pData);
|
||||
}
|
||||
|
||||
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_SpeedTypeDef speed = USBD_SPEED_FULL;
|
||||
switch (hpcd->Init.speed) {
|
||||
case PCD_SPEED_HIGH: speed = USBD_SPEED_HIGH; break;
|
||||
case PCD_SPEED_FULL: speed = USBD_SPEED_FULL; break;
|
||||
default: speed = USBD_SPEED_FULL; break;
|
||||
}
|
||||
USBD_LL_Reset(hpcd->pData);
|
||||
USBD_LL_SetSpeed(hpcd->pData, speed);
|
||||
}
|
||||
|
||||
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_LL_Suspend(hpcd->pData);
|
||||
}
|
||||
|
||||
void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
|
||||
{
|
||||
USBD_LL_IsoOUTIncomplete(hpcd->pData, epnum);
|
||||
}
|
||||
|
||||
void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
|
||||
{
|
||||
USBD_LL_IsoINIncomplete(hpcd->pData, epnum);
|
||||
}
|
||||
|
||||
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_LL_DevConnected(hpcd->pData);
|
||||
}
|
||||
|
||||
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
|
||||
{
|
||||
USBD_LL_DevDisconnected(hpcd->pData);
|
||||
}
|
||||
|
||||
/* ---------------- USBD → PCD shims ---------------- */
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
hpcd_USB_HS.Instance = USB1_OTG_HS;
|
||||
hpcd_USB_HS.Init.dev_endpoints = 3U;
|
||||
hpcd_USB_HS.Init.speed = PCD_SPEED_HIGH;
|
||||
hpcd_USB_HS.Init.dma_enable = DISABLE;
|
||||
hpcd_USB_HS.Init.phy_itface = USB_OTG_HS_EMBEDDED_PHY;
|
||||
hpcd_USB_HS.Init.Sof_enable = DISABLE;
|
||||
hpcd_USB_HS.Init.low_power_enable = DISABLE;
|
||||
hpcd_USB_HS.Init.lpm_enable = DISABLE;
|
||||
hpcd_USB_HS.Init.vbus_sensing_enable = DISABLE;
|
||||
hpcd_USB_HS.Init.use_dedicated_ep1 = DISABLE;
|
||||
hpcd_USB_HS.Init.use_external_vbus = DISABLE;
|
||||
|
||||
hpcd_USB_HS.pData = pdev;
|
||||
pdev->pData = &hpcd_USB_HS;
|
||||
|
||||
HAL_PCD_Init(&hpcd_USB_HS);
|
||||
HAL_PCDEx_SetRxFiFo(&hpcd_USB_HS, 0xA0U);
|
||||
HAL_PCDEx_SetTxFiFo(&hpcd_USB_HS, 0, 0xA0U);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_DeInit(USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
HAL_PCD_DeInit(pdev->pData);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
HAL_PCD_Start(pdev->pData);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_Stop(USBD_HandleTypeDef *pdev)
|
||||
{
|
||||
HAL_PCD_Stop(pdev->pData);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr,
|
||||
uint8_t ep_type, uint16_t ep_mps)
|
||||
{
|
||||
HAL_PCD_EP_Open(pdev->pData, ep_addr, ep_mps, ep_type);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||
{
|
||||
HAL_PCD_EP_Close(pdev->pData, ep_addr);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_FlushEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||
{
|
||||
HAL_PCD_EP_Flush(pdev->pData, ep_addr);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_StallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||
{
|
||||
HAL_PCD_EP_SetStall(pdev->pData, ep_addr);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_ClearStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||
{
|
||||
HAL_PCD_EP_ClrStall(pdev->pData, ep_addr);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||
{
|
||||
PCD_HandleTypeDef *hpcd = pdev->pData;
|
||||
if ((ep_addr & 0x80U) == 0x80U) {
|
||||
return hpcd->IN_ep[ep_addr & 0x7FU].is_stall;
|
||||
}
|
||||
return hpcd->OUT_ep[ep_addr & 0x7FU].is_stall;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_addr)
|
||||
{
|
||||
HAL_PCD_SetAddress(pdev->pData, dev_addr);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *pdev, uint8_t ep_addr,
|
||||
uint8_t *pbuf, uint32_t size)
|
||||
{
|
||||
HAL_PCD_EP_Transmit(pdev->pData, ep_addr, pbuf, size);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr,
|
||||
uint8_t *pbuf, uint32_t size)
|
||||
{
|
||||
HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size);
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
|
||||
{
|
||||
return HAL_PCD_EP_GetRxCount(pdev->pData, ep_addr);
|
||||
}
|
||||
|
||||
void *USBD_static_malloc(uint32_t size)
|
||||
{
|
||||
(void)size;
|
||||
static uint32_t mem[(sizeof(USBD_DFU_HandleTypeDef) / 4U) + 1U];
|
||||
return mem;
|
||||
}
|
||||
|
||||
void USBD_static_free(void *p)
|
||||
{
|
||||
(void)p;
|
||||
}
|
||||
|
||||
void USBD_LL_Delay(uint32_t Delay)
|
||||
{
|
||||
HAL_Delay(Delay);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Override of CubeN6's usbd_conf.h.
|
||||
*
|
||||
* The stock OBL exposes 5 DFU alts (Flashlayout / FSBL-EXT / FSBL-APP /
|
||||
* <user app> / OTP) wired to the OBL middleware's phase machinery
|
||||
* (PHASE_FLASHLAYOUT / PHASE_3 / PHASE_4 / PHASE_CMD / PHASE_OTP). The
|
||||
* Betaflight OBL only needs a single memory descriptor — the BF slot at
|
||||
* 0x70100000 (or nor0 0x0 in recovery mode). Collapsing to one alt does
|
||||
* three things:
|
||||
*
|
||||
* 1. The host shows our @Betaflight memory descriptor as THE name on
|
||||
* enumeration instead of "@Flashlayout /0x00/1*1Ke".
|
||||
* 2. dfu-util / STM32CubeProgrammer aim writes at alt 0 by default,
|
||||
* no `-a 3` needed.
|
||||
* 3. We can bypass the OBL middleware's alt→phase logic and the
|
||||
* OPENBL_USB_Download/EraseMemory phase-table machinery — those
|
||||
* paths are wired for ST's signed-FSBL flow and silently no-op for
|
||||
* a plain XIP user app at PHASE_CMD.
|
||||
*
|
||||
* Pulled in via the Makefile's `-I.` include path which precedes the
|
||||
* CubeN6 example's USB_Device/Target/.
|
||||
*/
|
||||
|
||||
#ifndef USBD_CONF_H
|
||||
#define USBD_CONF_H
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define USBD_CLASS_USER_STRING_DESC 0U
|
||||
|
||||
#define USBD_MAX_NUM_INTERFACES 1U
|
||||
#define USBD_MAX_NUM_CONFIGURATION 1U
|
||||
#define USBD_MAX_STR_DESC_SIZ 130U
|
||||
#define USBD_SUPPORT_USER_STRING_DESC 1U
|
||||
#define USBD_SELF_POWERED 1U
|
||||
#define USBD_DEBUG_LEVEL 0U
|
||||
|
||||
/* Single DFU alt — @Betaflight (BF flash slot, read/erase/write). */
|
||||
#define USBD_DFU_MAX_ITF_NUM 1U
|
||||
#define USBD_DFU_XFER_SIZE 1024U
|
||||
#define USBD_DFU_APP_DEFAULT_ADD 0x70100000U
|
||||
#define USBD_DFU_MAX_NB_OF_SECTORS 256U
|
||||
|
||||
#define DEVICE_FS 0U
|
||||
|
||||
#define USBD_malloc (void *)USBD_static_malloc
|
||||
#define USBD_free USBD_static_free
|
||||
#define USBD_memset memset
|
||||
#define USBD_memcpy memcpy
|
||||
#define USBD_Delay HAL_Delay
|
||||
|
||||
#if (USBD_DEBUG_LEVEL > 0U)
|
||||
#define USBD_UsrLog(...) printf(__VA_ARGS__); printf("\n");
|
||||
#else
|
||||
#define USBD_UsrLog(...)
|
||||
#endif
|
||||
|
||||
#if (USBD_DEBUG_LEVEL > 1U)
|
||||
#define USBD_ErrLog(...) printf("ERROR: "); printf(__VA_ARGS__); printf("\n");
|
||||
#else
|
||||
#define USBD_ErrLog(...)
|
||||
#endif
|
||||
|
||||
#if (USBD_DEBUG_LEVEL > 2U)
|
||||
#define USBD_DbgLog(...) printf("DEBUG : "); printf(__VA_ARGS__); printf("\n");
|
||||
#else
|
||||
#define USBD_DbgLog(...)
|
||||
#endif
|
||||
|
||||
extern PCD_HandleTypeDef hpcd_USB_HS;
|
||||
|
||||
void *USBD_static_malloc(uint32_t size);
|
||||
void USBD_static_free(void *p);
|
||||
|
||||
#endif /* USBD_CONF_H */
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Override of CubeN6's usbd_desc.c — Betaflight-branded USB descriptors.
|
||||
*
|
||||
* VID/PID kept as ST DfuSe defaults (0x0483/0xDF11) so dfu-util,
|
||||
* STM32CubeProgrammer, and BF Configurator's existing DFU plumbing all
|
||||
* work unchanged. Strings are Betaflight-branded so users see "Betaflight
|
||||
* N6 OBL" on enumerate.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "usbd_core.h"
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_conf.h"
|
||||
|
||||
/* DfuSe protocol default VID/PID. Standard tooling targets this pair. */
|
||||
#define USBD_VID 0x0483
|
||||
#define USBD_PID 0xDF11
|
||||
#define USBD_LANGID_STRING 1033
|
||||
|
||||
#define USBD_MANUFACTURER_STRING "Betaflight"
|
||||
#define USBD_PRODUCT_STRING "Betaflight N6 DFU"
|
||||
#define USBD_CONFIGURATION_STRING "BF N6 DFU Config"
|
||||
#define USBD_INTERFACE_STRING "BF N6 DFU Interface"
|
||||
|
||||
uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ];
|
||||
|
||||
static void Get_SerialNum(void);
|
||||
|
||||
uint8_t *USBD_DFU_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
uint8_t *USBD_DFU_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
uint8_t *USBD_DFU_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
uint8_t *USBD_DFU_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
uint8_t *USBD_DFU_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
uint8_t *USBD_DFU_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
uint8_t *USBD_DFU_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
|
||||
|
||||
USBD_DescriptorsTypeDef DFU_Desc =
|
||||
{
|
||||
USBD_DFU_DeviceDescriptor,
|
||||
USBD_DFU_LangIDStrDescriptor,
|
||||
USBD_DFU_ManufacturerStrDescriptor,
|
||||
USBD_DFU_ProductStrDescriptor,
|
||||
USBD_DFU_SerialStrDescriptor,
|
||||
USBD_DFU_ConfigStrDescriptor,
|
||||
USBD_DFU_InterfaceStrDescriptor
|
||||
};
|
||||
|
||||
__ALIGN_BEGIN uint8_t USBD_DFU_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
|
||||
{
|
||||
0x12,
|
||||
USB_DESC_TYPE_DEVICE,
|
||||
0x10, 0x01, /* USB 1.10 — DfuSe convention */
|
||||
0x00, 0x00, 0x00,
|
||||
USB_MAX_EP0_SIZE,
|
||||
LOBYTE(USBD_VID), HIBYTE(USBD_VID),
|
||||
LOBYTE(USBD_PID), HIBYTE(USBD_PID),
|
||||
0x00, 0x02, /* bcdDevice = 2.00 */
|
||||
USBD_IDX_MFC_STR,
|
||||
USBD_IDX_PRODUCT_STR,
|
||||
USBD_IDX_SERIAL_STR,
|
||||
USBD_MAX_NUM_CONFIGURATION
|
||||
};
|
||||
|
||||
__ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END =
|
||||
{
|
||||
USB_LEN_LANGID_STR_DESC,
|
||||
USB_DESC_TYPE_STRING,
|
||||
LOBYTE(USBD_LANGID_STRING),
|
||||
HIBYTE(USBD_LANGID_STRING)
|
||||
};
|
||||
|
||||
__ALIGN_BEGIN uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] __ALIGN_END =
|
||||
{
|
||||
USB_SIZ_STRING_SERIAL,
|
||||
USB_DESC_TYPE_STRING,
|
||||
};
|
||||
|
||||
uint8_t *USBD_DFU_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
*length = sizeof(USBD_DFU_DeviceDesc);
|
||||
return USBD_DFU_DeviceDesc;
|
||||
}
|
||||
|
||||
uint8_t *USBD_DFU_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
*length = sizeof(USBD_LangIDDesc);
|
||||
return USBD_LangIDDesc;
|
||||
}
|
||||
|
||||
uint8_t *USBD_DFU_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
USBD_GetString((uint8_t *)USBD_PRODUCT_STRING, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
||||
uint8_t *USBD_DFU_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
||||
uint8_t *USBD_DFU_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
*length = USB_SIZ_STRING_SERIAL;
|
||||
Get_SerialNum();
|
||||
return (uint8_t *)USBD_StringSerial;
|
||||
}
|
||||
|
||||
uint8_t *USBD_DFU_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
||||
uint8_t *USBD_DFU_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
(void)speed;
|
||||
USBD_GetString((uint8_t *)USBD_INTERFACE_STRING, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
||||
static void Get_SerialNum(void)
|
||||
{
|
||||
/* Read the device unique ID from the boot ROM-published location.
|
||||
* DEVICE_ID1/2/3 are CMSIS macros pointing into system memory. */
|
||||
unsigned long s0 = *(uint32_t *)DEVICE_ID1;
|
||||
unsigned long s1 = *(uint32_t *)DEVICE_ID2;
|
||||
unsigned long s2 = *(uint32_t *)DEVICE_ID3;
|
||||
char serial_string[SIZ_STRING_SERIAL + 2U];
|
||||
|
||||
snprintf(serial_string, sizeof(serial_string), "%08lX%08lX%08lX", s0, s1, s2);
|
||||
|
||||
uint8_t idx = 2U;
|
||||
const char *p = serial_string;
|
||||
while (*p != '\0' && idx < (USB_SIZ_STRING_SERIAL - 1U)) {
|
||||
USBD_StringSerial[idx++] = *p++;
|
||||
USBD_StringSerial[idx++] = 0x00U;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Override of CubeN6's usbd_dfu_if.c — Betaflight DFU memory descriptor.
|
||||
*
|
||||
* The DfuSe descriptor string advertises a memory layout to the host
|
||||
* (start address, sector count, sector size, type code). We expose two
|
||||
* different layouts depending on which mode obl_app.c entered:
|
||||
*
|
||||
* Normal mode (obl_recovery_mode == false):
|
||||
* @Betaflight /0x70100000/0100*64Kg
|
||||
* ↑ starts at the BF slot, covers 16 MiB. The OBL slot
|
||||
* (0x70000000..0x700FFFFF) is NOT exposed, so a host upload
|
||||
* can't accidentally trash a working OBL.
|
||||
*
|
||||
* Recovery mode (obl_recovery_mode == true, nor0 0x0 had no valid
|
||||
* signed-FSBL header):
|
||||
* @Betaflight /0x70000000/0800*64Kg
|
||||
* ↑ starts at nor0 0x0, covers 128 MiB. Used for the bricked-FC
|
||||
* recovery flow where the host needs to write OBL itself + BF.
|
||||
*
|
||||
* 64 KiB sector size matches the MX66UW1G45G 4-byte block-erase opcode
|
||||
* (0xDC) and minimises DFU round-trip overhead vs the chip's 4 KiB
|
||||
* sector erase. Type 'g' = read+erase+write.
|
||||
*
|
||||
* The pStrDesc pointer in USBD_DFU_Media_fops is updated before
|
||||
* OpenBootloader_Init() runs (see obl_app.c::run_obl_loop), so by the
|
||||
* time the host enumerates the DFU descriptor reflects the active mode.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "usbd_dfu_if.h"
|
||||
#include "openbl_usb_cmd.h"
|
||||
#include "usb_interface.h"
|
||||
#include "openbl_mem.h"
|
||||
#include "common_interface.h"
|
||||
#include "openbootloader_conf.h"
|
||||
#include "flash_iface.h"
|
||||
|
||||
#define MEDIA_DESC_STR_NORMAL "@Betaflight /0x70100000/0100*064Kg"
|
||||
#define MEDIA_DESC_STR_RECOVERY "@Betaflight /0x70000000/0800*064Kg"
|
||||
|
||||
/* MX66UW1G45G program/erase windows — used by the host's DFU GET_STATUS
|
||||
* to bound the polling timeout it announces in the device-state machine.
|
||||
* Generous; real hardware completes faster but the host doesn't care
|
||||
* about under-reporting. */
|
||||
#define MEDIA_ERASE_TIME (uint16_t)400U /* ms per 64 KiB block */
|
||||
#define MEDIA_PROGRAM_TIME (uint16_t)5U /* ms per page */
|
||||
|
||||
extern bool obl_recovery_mode;
|
||||
extern USBD_HandleTypeDef hUsbDeviceFS;
|
||||
|
||||
static uint16_t USB_DFU_If_Init(void);
|
||||
static uint16_t USB_DFU_If_Erase(uint32_t Add);
|
||||
static uint16_t USB_DFU_If_Write(uint8_t *pSrc, uint32_t alt, uint32_t Len, uint32_t BlockNumber);
|
||||
static uint8_t *USB_DFU_If_Read(uint32_t alt, uint8_t *pDest, uint32_t Len, uint32_t BlockNumber);
|
||||
static uint16_t USB_DFU_If_DeInit(void);
|
||||
static uint16_t USB_DFU_If_GetStatus(uint32_t Add, uint8_t Cmd, uint8_t *buffer);
|
||||
|
||||
/* USBD_DFU_Media_fops::pStrDesc is patched at runtime by
|
||||
* obl_dfu_apply_mode() (called from obl_app.c) before
|
||||
* OpenBootloader_Init enables USB enumeration. The default points at
|
||||
* the recovery string so a stray enumeration before mode-set fails safe
|
||||
* (recovery is the more permissive of the two — better than the host
|
||||
* trying to write the OBL slot through a normal-mode descriptor that
|
||||
* doesn't cover it). */
|
||||
__ALIGN_BEGIN USBD_DFU_MediaTypeDef USBD_DFU_Media_fops __ALIGN_END =
|
||||
{
|
||||
(uint8_t *)MEDIA_DESC_STR_RECOVERY,
|
||||
USB_DFU_If_Init,
|
||||
USB_DFU_If_DeInit,
|
||||
USB_DFU_If_Erase,
|
||||
USB_DFU_If_Write,
|
||||
USB_DFU_If_Read,
|
||||
USB_DFU_If_GetStatus
|
||||
};
|
||||
|
||||
void obl_dfu_apply_mode(bool recovery)
|
||||
{
|
||||
USBD_DFU_Media_fops.pStrDesc = (uint8_t *)(recovery
|
||||
? MEDIA_DESC_STR_RECOVERY
|
||||
: MEDIA_DESC_STR_NORMAL);
|
||||
}
|
||||
|
||||
static uint16_t USB_DFU_If_Init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t USB_DFU_If_DeInit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t USB_DFU_If_Erase(uint32_t Add)
|
||||
{
|
||||
if (OPENBL_MEM_GetAddressArea((uint32_t)Add) == AREA_ERROR) {
|
||||
return OPENBL_USB_SendAddressNack(&hUsbDeviceFS);
|
||||
}
|
||||
|
||||
/* CubeN6's OPENBL_MEM_Erase is a stub that always returns ERROR
|
||||
* (Modules/Mem/openbl_mem.c). Bypass the middleware and erase a
|
||||
* single descriptor sector directly via the flash driver. The DFU
|
||||
* descriptor advertises EXT_MEMORY_SECTOR_SIZE-byte sectors so the
|
||||
* host issues exactly one Erase per sector before writing into it. */
|
||||
if (Add < EXT_MEMORY_START_ADDRESS) {
|
||||
return 1U;
|
||||
}
|
||||
const uint32_t offset = Add - EXT_MEMORY_START_ADDRESS;
|
||||
if (!flash_erase_range(offset, EXT_MEMORY_SECTOR_SIZE)) {
|
||||
return 1U;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/* DfuSe address pointer captured from SET_ADDRESS_POINTER. Defaults
|
||||
* mirror the descriptor's advertised start so a data block before any
|
||||
* SET_ADDR still lands at the slot's base. */
|
||||
static uint32_t dfuse_addr_ptr_normal = 0x70100000U;
|
||||
static uint32_t dfuse_addr_ptr_recovery = 0x70000000U;
|
||||
|
||||
/* Auto-reboot watchdog for DFU downloads. dfu-util without --reset does
|
||||
* not send the protocol's DFU_DETACH / manifest-leave trigger, so
|
||||
* DFU_Leave (which sets dfu_leave_pending) never runs and the chip sits
|
||||
* in DFU forever. Stamp the tick each time we accept a real data block;
|
||||
* run_obl_loop in obl_app.c watches for idleness after the first write
|
||||
* and triggers NVIC_SystemReset on its own. 0 means "no download seen
|
||||
* yet"; obl_app.c only consults the value once it's non-zero. */
|
||||
volatile uint32_t dfu_last_write_tick;
|
||||
|
||||
static uint32_t dfuse_get_base(void)
|
||||
{
|
||||
return obl_recovery_mode ? dfuse_addr_ptr_recovery : dfuse_addr_ptr_normal;
|
||||
}
|
||||
|
||||
static uint16_t USB_DFU_If_Write(uint8_t *pSrc, uint32_t alt, uint32_t Len, uint32_t BlockNumber)
|
||||
{
|
||||
(void)alt;
|
||||
|
||||
/* DfuSe extension commands (bcdDFUVersion=0x011A) arrive as
|
||||
* BlockNumber=0, wLength==5, command byte at pSrc[0]:
|
||||
* 0x21 SET_ADDRESS_POINTER pSrc[1..4] = LE target address
|
||||
* 0x41 ERASE_PAGE pSrc[1..4] = LE sector address
|
||||
* 0x91 READ_UNPROTECT (ignored)
|
||||
* 0x92 ERASE_ALL (ignored — host issues per-sector)
|
||||
*
|
||||
* The vendored usbd_dfu.c routes every DOWNLOAD here without
|
||||
* decoding DfuSe, so this is the only point where these sub-
|
||||
* commands get dispatched. */
|
||||
if (BlockNumber == 0U && Len >= 5U) {
|
||||
const uint8_t cmd = pSrc[0];
|
||||
const uint32_t addr = (uint32_t)pSrc[1]
|
||||
| ((uint32_t)pSrc[2] << 8)
|
||||
| ((uint32_t)pSrc[3] << 16)
|
||||
| ((uint32_t)pSrc[4] << 24);
|
||||
switch (cmd) {
|
||||
case 0x21U:
|
||||
if (obl_recovery_mode) {
|
||||
dfuse_addr_ptr_recovery = addr;
|
||||
} else {
|
||||
dfuse_addr_ptr_normal = addr;
|
||||
}
|
||||
return 0U;
|
||||
case 0x41U: {
|
||||
if (addr < EXT_MEMORY_START_ADDRESS) {
|
||||
return 1U;
|
||||
}
|
||||
const uint32_t offset = addr - EXT_MEMORY_START_ADDRESS;
|
||||
if (!flash_erase_range(offset, EXT_MEMORY_SECTOR_SIZE)) {
|
||||
return 1U;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
default:
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
if (BlockNumber < 2U) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/* Block 2+ data writes — addressed relative to the most recent
|
||||
* SET_ADDRESS_POINTER (or the descriptor default if none arrived). */
|
||||
const uint32_t addr = dfuse_get_base() + (BlockNumber - 2U) * USBD_DFU_XFER_SIZE;
|
||||
if (addr < EXT_MEMORY_START_ADDRESS) {
|
||||
return 1U;
|
||||
}
|
||||
const uint32_t offset = addr - EXT_MEMORY_START_ADDRESS;
|
||||
if (!flash_program(offset, pSrc, Len)) {
|
||||
return 1U;
|
||||
}
|
||||
/* Refresh the auto-reboot timestamp; run_obl_loop reboots if no
|
||||
* further writes arrive within DFU_IDLE_REBOOT_MS. */
|
||||
dfu_last_write_tick = HAL_GetTick();
|
||||
if (dfu_last_write_tick == 0U) {
|
||||
dfu_last_write_tick = 1U;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
static uint8_t *USB_DFU_If_Read(uint32_t alt, uint8_t *pDest, uint32_t Len, uint32_t BlockNumber)
|
||||
{
|
||||
(void)alt;
|
||||
/* alt 0: memcpy from XSPI memory-mapped flash, anchored at the
|
||||
* most recent DfuSe SET_ADDRESS_POINTER. flash_memmap_on() is
|
||||
* idempotent and brings the controller back from indirect mode
|
||||
* if a prior Write() flipped it off. */
|
||||
if (BlockNumber < 2U) {
|
||||
memset(pDest, 0, Len);
|
||||
return pDest;
|
||||
}
|
||||
if (!flash_memmap_on()) {
|
||||
memset(pDest, 0, Len);
|
||||
return pDest;
|
||||
}
|
||||
const uint32_t addr = dfuse_get_base() + (BlockNumber - 2U) * USBD_DFU_XFER_SIZE;
|
||||
memcpy(pDest, (const uint8_t *)addr, Len);
|
||||
return pDest;
|
||||
}
|
||||
|
||||
static uint16_t USB_DFU_If_GetStatus(uint32_t Add, uint8_t Cmd, uint8_t *pBuffer)
|
||||
{
|
||||
(void)Add;
|
||||
switch (Cmd) {
|
||||
case DFU_MEDIA_PROGRAM:
|
||||
pBuffer[1] = (uint8_t)(MEDIA_PROGRAM_TIME & 0xFFU);
|
||||
pBuffer[2] = (uint8_t)((MEDIA_PROGRAM_TIME >> 8U) & 0xFFU);
|
||||
pBuffer[3] = 0U;
|
||||
break;
|
||||
case DFU_MEDIA_ERASE:
|
||||
default:
|
||||
pBuffer[1] = (uint8_t)(MEDIA_ERASE_TIME & 0xFFU);
|
||||
pBuffer[2] = (uint8_t)((MEDIA_ERASE_TIME >> 8U) & 0xFFU);
|
||||
pBuffer[3] = 0U;
|
||||
break;
|
||||
}
|
||||
return USBD_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user