STM32H750从FLASH分支到RAM运行更新程序异常锁死求助
STM32H750 RAM程序跳转随机锁死问题排查与解决方案
问题背景
基于STM32H750(ARM Cortex-M7)处理器,通过STM32CubeIDE开发:FLASH中的默认程序需支持独立运行,同时可将更新程序加载至RAM并跳转执行。为适配后续更新程序的个性化初始化逻辑,计划在启动代码初始化阶段前完成FLASH到RAM的分支跳转。当前默认程序与更新程序仅分支目标地址不同,未开启RDP2安全机制。
启动代码逻辑
启动代码通过检查更新程序加载成功后设置的magic cookie,决定是否分支至RAM中更新程序的ContinueInit地址(跳过复位和中断向量初始化):
Reset_Handler: ldr sp, =_estack /* set stack pointer */ ldr r1, =0x580244dc ldr r2, =0xe0000000 str r2, [r1] /* turn on the RAM1-3 clocks - this is essential! */ ldr r0, =0x580244d0 ldr r1, [r0] /* get the value in the reset status register */ ldr r2, =0x00460000 cmp r2, r1 /* compare reset status to external reset value */ ldr r2, =0x00010000 str r2, [r0] /* clear the reset status register */ bne ContinueInit /* use default code if not from external reset */ ldr r0, =magic_cookie ldr r1, [r0] /* get the value in the magic cookie */ ldr r2, =0x12345678 cmp r2, r1 /* compare magic cookie to update value */ bne ContinueInit /* use default code if no cookie match */ ldr r2, =0x00100010 str r2, [r0] /* clear the magic cookie */ b UpdateContinueInit ContinueInit: /* Copy the data segment initializers from code to SRAM */ movs r1, #0 b LoopCopyDataInit CopyDataInit: ldr r3, =_sidata ldr r3, [r3, r1] str r3, [r0, r1] adds r1, r1, #4 LoopCopyDataInit: ldr r0, =_sdata ldr r3, =_edata adds r2, r0, r1 cmp r2, r3 bcc CopyDataInit ldr r2, =_sbss b LoopFillZerobss /* Zero fill the bss segment. */ FillZerobss: movs r3, #0 str r3, [r2], #4 LoopFillZerobss: ldr r3, = _ebss cmp r2, r3 bcc FillZerobss /* Call the clock system intitialization function.*/ bl SystemInit /* Call static constructors */ bl __libc_init_array /* branch to the default main program */ bl main bx lr
链接脚本配置
默认程序链接脚本(FLASH运行)
/* Default Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = 0x20020000; /* end of DTCMRAM */ /* Generate a link error if heap and stack don't fit into RAM */ _Min_Heap_Size = 0x400; /* required amount of heap */ _Min_Stack_Size = 0x800; /* required amount of stack */ /* 1mS counter location used by ISR */ uwTick = 0x20000000; magic_cookie = 0x20000004; UpdateContinueInit = 0x24014572; /* Specify the memory areas */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K DTCMRAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x1fff8 RAM123 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K } /* Define output sections */ SECTIONS { /* The startup code goes first into FLASH */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >FLASH /* The program code and other data goes into FLASH */ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >FLASH /* Constant data goes into FLASH */ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata sections (constants, strings, etc.) */ . = ALIGN(4); } >FLASH .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH .ARM : { __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; } >FLASH .preinit_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); } >FLASH .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); } >FLASH .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); } >FLASH /* used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections goes into RAM, load LMA copy after code */ .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >DTCMRAM AT> FLASH /* Uninitialized data section */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM123 /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >DTCMRAM /* Remove information from the standard libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } }
更新程序链接脚本(RAM运行)
/* Update Entry Point */ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _estack = 0x20020000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ _Min_Heap_Size = 0x400; /* required amount of heap */ _Min_Stack_Size = 0x800; /* required amount of stack */ /* 1mS counter location used by ISR */ uwTick = 0x20000000; magic_cookie = 0x20000004; UpdateContinueInit = 0x08014572; /* Specify the memory areas */ MEMORY { DTCMRAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x1fff8 AXIRAM (xrw) : ORIGIN = 0x24000000, LENGTH = 0x80000 RAM123 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K } /* Define output sections */ SECTIONS { /* The startup code goes first into RSTRAM */ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } >AXIRAM /* The program code and other data goes into AXIRAM */ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* glue arm to thumb code */ *(.glue_7t) /* glue thumb to arm code */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* define a global symbols at end of code */ } >AXIRAM /* Constant data goes into AXIRAM */ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(4); } >AXIRAM .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >AXIRAM .ARM : { __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; } >AXIRAM .preinit_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); } >AXIRAM .init_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); } >AXIRAM .fini_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); } >AXIRAM /* used by the startup to initialize data */ _sidata = LOADADDR(.data); /* Initialized data sections goes into RAM, load LMA copy after code */ .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >DTCMRAM AT> AXIRAM /* Uninitialized data section */ . = ALIGN(4); .bss : { /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM123 /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >DTCMRAM /* Remove information from the standard libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } }
故障现象
更新程序运行时随机锁死,锁死位置不固定,涵盖:
- 跳转到
UpdateContinueInit时 - 更新程序.bss段初始化完成前
- 跳转至或执行
__libc_init_array时 - 进入更新程序
main后的任意时刻
已完成验证
- 默认程序
main中长分支跳转至更新程序小子程序可正常运行; - 核对列表文件,
SystemInit中向量表偏移寄存器配置正确; - 检查0xe000dc28起始的故障寄存器无异常记录;
- 默认程序独立运行正常,且已正确将更新程序完整加载至RAM;
- 通过JTAG写入更新程序到RAM,设置启动地址为更新程序
isr_vector并复位,程序可正常运行。
问题分析与解决方案
针对STM32H750的硬件特性,锁定以下核心问题并给出修复方案:
1. AXI RAM缓存一致性问题
STM32H7的AXI RAM(0x24000000)默认开启D-Cache,默认程序加载更新程序到RAM后,CPU可能读取缓存中的旧数据,导致指令/数据错误。需在加载完成后执行缓存失效:
/* 加载完成后执行D-Cache失效 */ ldr r0, =0x24000000 /* AXIRAM起始地址 */ ldr r1, =0x80000 /* AXIRAM长度 */ bl SCB_CleanInvalidateDCache_by_Addr
2. 跳转前未切换向量表
跳转至RAM程序前,向量表仍指向FLASH,若此时触发中断(如SysTick),会执行FLASH中的中断服务程序,引发冲突。需在跳转前更新向量表偏移:
/* 设置向量表为更新程序的isr_vector地址 */ ldr r0, =0x24000000 ldr r1, =SCB_BASE str r0, [r1, #0x08] /* SCB->VTOR = 0x24000000 */
3. 跳转指令与模式匹配问题
Cortex-M7为Thumb-2架构,所有指令需运行在Thumb模式,目标地址最低位必须为1。当前使用b指令跳转,需改为bx指令并修正地址:
/* 修正目标地址为Thumb模式,使用bx跳转 */ ldr r0, =UpdateContinueInit | 1 bx r0
4. 栈空间冲突
默认程序与更新程序共享栈地址,但跳转时默认程序的栈已被占用,更新程序初始化会覆盖栈数据。需在跳转前重置栈指针:
/* 重置栈指针为更新程序的_estack */ ldr sp, =0x20020000
5. 更新程序镜像完整性
更新程序链接脚本中.data段的LMA指向AXIRAM,默认程序需加载完整的更新程序
相关产品推荐
相关产品推荐

