You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,默认程序需加载完整的更新程序

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:05:17