跳转前必须禁用中断、清空流水线并重映射向量表,否则会触发HardFault;需依次执行__disable_irq()、__DSB()+__ISB()、清除活跃异常,校验VTOR地址对齐与MSP有效性,显式设置MSP/CONTROL/PRIMASK,禁用MPU及还原内存映射。

跳转前必须禁用所有中断并清空流水线
直接 reinterpret_cast<void>(app_entry)()</void> 跳转会导致 HardFault,常见现象是跳转后几条指令执行异常或立即进入 Default_Handler。根本原因是 Cortex-M 系统在跳转瞬间仍处于 Bootloader 的中断上下文,NVIC 未重映射、内核寄存器(如 CONTROL、PRIMASK)残留、指令流水线未刷新。
实操必须按顺序做三件事:
- 执行
__disable_irq()(或直接写__asm volatile ("cpsid i")),确保 IRQ/FIQ 全关 - 调用
__DSB()+__ISB()强制数据同步并清空取指流水线 - 手动清除
SysTick、PendSV、SVC等活跃异常状态(读ICSR并清对应位)
中断向量表重映射必须在跳转前完成且校验有效
仅靠 SCB->VTOR = app_vector_table_addr 不够。如果 app_vector_table_addr 指向的内存未使能、未对齐(必须 256 字节对齐)、或首地址不是有效栈顶值(MSP 初始值),CPU 复位后第一次异常就会挂掉。
关键检查点:
立即学习“C++免费学习笔记(深入)”;
-
app_vector_table_addr必须是 256 字节对齐的地址,建议用reinterpret_cast<uint32_t>(app_base_addr) & ~0xFFU</uint32_t>计算 - 读取该地址处的首个字(MSP 初始值):若为
0x00000000或明显超出 RAM 范围(如0x20000000以下),说明应用未正确链接或 Flash 未编程 - 确认目标区域已取消写保护(对 STM32 需调用
HAL_FLASH_Unlock()后再设SCB->VTOR)
跳转时需重建初始执行环境
Cortex-M 应用启动代码(如 Reset_Handler)默认假设 MSP 已由向量表首项初始化、CONTROL 为 0(使用 MSP)、PSP 未被使用。Bootloader 若曾切到 PSP 模式或修改过 CONTROL,跳转后会错乱。
安全做法是显式恢复核心寄存器状态:
- 用
__set_MSP(*reinterpret_cast<uint32_t>(app_vector_table_addr))</uint32_t>设置主栈指针 - 用
__set_CONTROL(0)强制使用 MSP 并进入特权线程模式 - 用
__set_PRIMASK(0)清除 PRIMASK(虽然之前已关中断,但保险起见) - 最后才调用函数指针:
typedef void (*func_ptr)(void); func_ptr reset = reinterpret_cast<func_ptr>(app_entry); reset();</func_ptr>
常见陷阱:Flash 重映射与 MPU 冲突
某些芯片(如 STM32H7、LPC55S69)在 Bootloader 中启用了 MPU 或配置了 AXI/Flash 重映射(如 SYSCFG->MEMRMP),跳转后应用看到的地址空间可能错位——例如本该访问 0x08000000 的代码,实际从 0x0C000000 取指。
务必在跳转前:
- 禁用所有 MPU region(
MPU->CTRL = 0,再__DSB(); __ISB();) - 还原
SYSCFG->MEMRMP到复位值(如 STM32H7 的0) - 确认
SCB->CPACR中协处理器访问权限未被 Bootloader 锁死(尤其涉及 FPU 的应用)
这些细节不打印日志、不仿真很难暴露,往往表现为跳转后停在 0x00000000 或反复进 HardFault_Handler。最稳妥的方式是在跳转前加一段极简验证代码:读向量表第二项(Reset_Handler 地址),判断是否在预期 Flash 区间内,再执行跳转。


















