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

ARM Cortex-M3能否从SRAM启动?如何用startup.s实现代码搬移?

从SRAM(0x20000000-0x3fffffff)启动的实现方案

是的,你必须把代码从Flash/ROM(0x00000000-0x1fffffff)复制到目标SRAM区域——因为MCU的硬件启动逻辑默认从0x00000000地址取指令,而SRAM是易失性存储,上电后为空,必须先把代码从非易失的Flash/ROM复制过去,再跳转到SRAM执行。下面是两种具体实现方式,以及关键配置要点:

一、通过Startup.s完成代码复制与跳转

这是最常用的方式,因为启动代码是系统执行的第一段代码,能确保在任何全局变量初始化前完成复制操作。

汇编代码示例(修改startup.s)

; 定义地址与代码长度,根据实际项目调整
FLASH_START    EQU     0x00000000
SRAM_START     EQU     0x20000000
CODE_SIZE      EQU     0x10000  ; 假设代码总长度为64KB,需从编译后的.map文件获取实际值

Reset_Handler    PROC
                EXPORT  Reset_Handler             [WEAK]
                ; 保留原有栈初始化代码
                LDR     R0, =__initial_sp
                MOV     SP, R0

                ; 开始复制代码到SRAM
                LDR     R1, =FLASH_START
                LDR     R2, =SRAM_START
                LDR     R3, =CODE_SIZE

copy_loop:
                CMP     R3, #0
                BEQ     copy_done
                LDR     R4, [R1], #4
                STR     R4, [R2], #4
                SUB     R3, R3, #4
                B       copy_loop

copy_done:
                ; 跳转到SRAM中的复位向量(向量表第2项为PC初始值)
                LDR     R0, =SRAM_START + 0x4
                LDR     R0, [R0]
                BX      R0
                ENDP

配套链接脚本修改

必须调整链接脚本,指定代码段的加载地址(LMA,Flash中的位置)和运行地址(VMA,SRAM中的位置),否则代码中的地址引用会指向Flash,导致跳转后执行错误:

MEMORY
{
  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K  ; 根据实际Flash大小调整
  SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K  ; 根据实际SRAM大小调整
}

SECTIONS
{
  .text :
  {
    . = ALIGN(4);
    *(.isr_vector)          /* 中断向量表 */
    *(.text)                /* 主代码段 */
    *(.text*)               /* 其他代码段 */
    *(.rodata)              /* 只读数据 */
    *(.rodata*)
    . = ALIGN(4);
    _etext = .;             /* 代码段结束标记 */
  } >SRAM AT>FLASH  ; 运行在SRAM,加载在FLASH

  .data : AT = _etext
  {
    . = ALIGN(4);
    _sdata = .;             /* 初始化数据段起始 */
    *(.data)
    *(.data*)
    . = ALIGN(4);
    _edata = .;             /* 初始化数据段结束 */
  } >SRAM

  .bss :
  {
    . = ALIGN(4);
    _sbss = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    _ebss = .;
  } >SRAM
}

二、通过C代码实现(适用于不想修改汇编的场景)

如果不想改动startup.s,可以在main函数最开始执行代码复制,但要注意:此时全局变量还未初始化,复制函数不能依赖任何全局数据,必须用硬编码地址和寄存器操作。

C代码示例

#include <stdint.h>

// 硬编码地址与代码长度,需匹配实际项目
#define FLASH_START 0x00000000UL
#define SRAM_START  0x20000000UL
#define CODE_SIZE   0x10000UL  // 从.map文件获取实际代码长度

// 纯寄存器操作的复制函数,不依赖全局变量
static void copy_code_to_sram(void)
{
    uint32_t *src = (uint32_t*)FLASH_START;
    uint32_t *dest = (uint32_t*)SRAM_START;
    uint32_t count = CODE_SIZE / 4;  // 按4字节对齐复制

    while (count--)
    {
        *dest++ = *src++;
    }
}

// 标记该函数存放在SRAM区域
__attribute__((section(".sram_main"))) void sram_main(void)
{
    // 这里编写实际业务代码,比如LED闪烁、外设初始化等
    while (1)
    {
        // 示例操作
    }
}

int main(void)
{
    // 先完成代码复制
    copy_code_to_sram();

    // 跳转到SRAM中的业务函数
    void (*sram_main_ptr)(void) = (void(*)())&sram_main;
    sram_main_ptr();

    // 永远不会执行到此处
    while (1);
}

配套链接脚本调整

需要在链接脚本中添加.sram_main段的配置,确保它被分配到SRAM区域:

// 在SECTIONS中添加:
.sram_main :
{
    . = ALIGN(4);
    *(.sram_main)
    . = ALIGN(4);
} >SRAM

关键注意事项

  1. 代码长度必须准确:CODE_SIZE不能小于编译后的实际代码总长度(可从.map文件的_etext - FLASH_START计算),否则会复制不完整;也不要过大,避免覆盖SRAM中的栈、堆区域。
  2. 栈指针配置:确保startup.s中的栈指针指向SRAM的空闲区域,不要和代码段重叠。
  3. 中断向量表处理:如果需要在SRAM中响应中断,必须把中断向量表复制到SRAM,然后配置MCU的向量表偏移寄存器(如STM32的SCB->VTOR)指向SRAM中的向量表起始地址。
  4. 调试注意事项:调试时要注意选择"从SRAM运行"的调试配置,避免调试器仍从Flash加载代码。

内容的提问来源于stack exchange,提问作者ADemonevil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:50:19