如何在STM32上从RAM中运行代码?

4
我有一个非常简单的“Hello World”软件(请查看我的Github项目),运行在STM32WB55 Nucleo板上(基本上,它通过USART1每1000毫秒发送"HELLO WORLD\n")。
如果我能够成功地将这个软件从Flash中转移到RAM中运行,而不是Flash中,我会特别高兴。这个MCU有196604字节的RAM。而我的项目总大小为13332字节。所以大小不应该成为一个问题。
我想做的是:程序开始时:将程序加载到RAM中。然后禁用Flash,以提高程序的整体性能(更快的访问时间)和降低功耗。
根据我所了解的,我应该修改链接脚本,创建一个特殊的代码段来放置代码,然后禁用MCU的Flash。
作为一名初学者,我不知道从哪里开始。
PS:我正在使用CLion(CMake)和GCC。

请提供足够的代码,以便他人更好地理解或重现问题。 - Community
大多数微控制器(MCU)直接从闪存中存储和执行代码,这当然也适用于STM32 MCU,即使那些声明为“const”的变量也存储在闪存中。只有会改变的变量才会加载到RAM中,因此在MCU中,RAM主要用于堆栈和堆。这就是为什么大多数MCU具有比RAM更多的闪存的原因。这与PC / Mac / Linux系统中使用的通用微处理器(CPU)的一般目的不同,在该系统中,程序从存储中加载并在RAM中执行,并且在退出应用程序时释放内存。 - hcheung
你需要一个小的启动程序,它可以从闪存中运行并将其余的程序复制到RAM中。你可以将其编写为引导加载程序并单独链接,或者编辑源代码并添加部分属性,以便可以一次性链接并放置在不同的位置。无论哪种方式,你都需要制作一个自定义的链接器脚本(*.ld文件)。 - Tom V
1个回答

4

使用链接脚本将所有内容放入RAM中。我看到你已经有了一个链接脚本(STM32WB55RGVX_RAM.ld),看起来是可以实现这个功能的。你可能只需通过更改 set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/STM32WB55RGVX_FLASH.ld) 即可激活它。

您可以看到它定义了两个内存区域:

MEMORY
{
RAM (xrw)           : ORIGIN = 0x20000000, LENGTH = 192K
FLASH (rx)          : ORIGIN = 0x08000000, LENGTH = 512K
}

你可以看到它将所有代码位都放在了RAM中。虽然定义了“FLASH”存储区域,但实际上从未使用过这个链接器脚本,因此它会保持为空。与_FLASH.ld链接器脚本进行比较。

构建应用程序后,您可以使用gdb加载.elf文件。gdb将忠实地将所有部分加载到链接器放置它们的区域。

最后一部分就是实际运行代码。

如果入口点设置正确,gdb还将初始化程序计数器以指向那里。然后,您可以像平常一样使用"continue next"等命令,在RAM中调试代码。入口点可以通过gcc链接器命令中的-e 0x20000000之类的东西任意移动。

# example gdb commands to execute output.elf from RAM

# load the elf into gdb so that we can get debug information from it
file output.elf

# reset the mcu
monitor reset init

# load the elf into RAM
load output.elf

# verify that the register are correct
info registers

# run until main
break main
continue

执行程序时,首先运行的不是主函数main,而是所有设置C执行环境所需的启动代码。这包括设置堆栈和在startup_stm32wb55rgvx.s文件中的其他内容。因此,我已经在主函数上设置了断点并执行到那里。
顺便提一下,如果您实际上不需要启动代码,您可以构建非常小的可执行文件。如果您可以接受不完整的执行环境,则可以自己设置堆栈指针。当您想要将其余RAM用于其他事情或想要优化加载时间时,这非常有用。考虑设置一些仅微控制器可以访问的后期制作步骤。甚至是后期生产测试。为此,我推荐使用Python库pyswd,它是一个绝对福音。
--编辑--
我刚才注意到您想让您烧录的程序从RAM执行一些代码...在这种情况下,甚至更简单。
您甚至不需要编辑链接器脚本。 只需将函数放置在RAM节(方便的RamFunc存在)中即可。
__attribute__ ((longcall, section(".RamFunc"))) void test(){
    // disable_flash... see your datasheet
    // we do this from RAM, because disabling FLASH while executing from FLASH would be dumb.
    disable_flash();
    // do the RAM things... remember that all the symbols you use must be RAM symbols at this point.
    dothething();
}

int main(void) {
    // jump to RAM
    test();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接