c - 在 STM32 中从 RAM 执行代码

标签 c gcc linker arm stm32

我最近开始在 STM32F4 核板上编程。我刚刚发现只能在有限的时间内对闪存进行编程(虽然不是少数,但它是一个评估板,它将被一遍又一遍地编程以开发不同的项目)。之后我在某处读到可以直接编程到 RAM 而不是闪存中,但找不到任何关于它的技术信息。

有人知道如何修改链接器/makefile来编译和链接要从RAM的起始地址而不是闪存执行的程序吗?

ps:我使用STM32CubeMX为系统工作台生成的代码和一个脚本来为项目生成makefile

最佳答案

如果您最近开始使用它,那么在闪光灯磨损之前您还有很长时间。您可能会遇到驱动器已满错误,只需拔下并重新插入电路板即可。这些东西我已经有很多年了,闪光灯还没有磨损。并不是说它不能完成,它可以,但除非您编写了一个磨损它的闪存抖动程序,否则您不太可能在那里。

您将需要 openocd(或其他一些调试器,也许您的 IDE 提供了,我不使用它们,因此无法提供帮助)。 openocd 和 gnu 工具是微不足道的,所以要走一遍。

从正确的目录,或通过从 openocd 复制这些文件

openocd -f stlink-v2-1.cfg -f stm32f4x.cfg

(其中一个或两个可能具有依赖项,它们包含的其他文件,可以将它们拉入或需要任何其他文件)。

应该以这样的方式结束,而不是退出到命令行
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints

在另一个窗口
telnet localhost 4444

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> 

在那个窗口中你可以停止处理器
> halt
stm32f4x.cpu: target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x61000000 pc: 0x080000b2 msp: 0x20000ff0
> 

全尺寸 ARM 处理器您的切入点是一个指令,您只需
开始执行。 cortex-m 使用一个 vector 表,你不能只是在那里分支。
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl notmain
    b hang

.thumb_func
hang:   b .

理论上,您可以分支到重置处理程序地址,但链接描述文件将希望在闪存中使用该地址,任何与位置相关的内容都将不起作用。如果您依靠 vector 表来执行此操作,则可能不会设置您的堆栈指针。所以这样的事情会起作用,这是完整示例的一部分

SRAM
.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
    ldr r0,stacktop
    mov sp,r0
    bl notmain
    b .

.align
stacktop: .word 0x20001000

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

不是main.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );

int notmain ( void )
{
    unsigned int ra;
    ra=GET32(0x20000400);
    PUT32(0x20000404,ra);
    PUT32(0x20000400,ra+1);
    return(0);
}

文件
MEMORY
{    
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .rodata : { *(.rodata*) } > ram
    .bss : { *(.bss*) } > ram
}

基本上用 ram 替换 rom 引用。 (你的链接器脚本,如果 gnu 可能比这个更复杂,但这工作得很好,可以根据需要在此处添加 .data )。
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.flash.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.flash.elf > notmain.flash.list
arm-none-eabi-objcopy notmain.flash.elf notmain.flash.bin -O binary
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 sram.s -o sram.o
arm-none-eabi-ld -o notmain.sram.elf -T sram.ld sram.o notmain.o
arm-none-eabi-objdump -D notmain.sram.elf > notmain.sram.list
arm-none-eabi-objcopy notmain.sram.elf notmain.sram.hex -O ihex
arm-none-eabi-objcopy notmain.sram.elf notmain.sram.bin -O binary

我的程序的 flash 版本和 sram 版本的构建。

所以现在我们有我们的 telnet 进入 openocd 服务器,处理器停止,让我们看看内存位置并更改它
> mdw 0x20000400
0x20000400: 7d7d5889 
> mww 0x20000400 0x12345678
> mdw 0x20000400           
0x20000400: 12345678 

并运行我们新的基于 sram 的程序
> load_image /path/to/notmain.sram.elf
64 bytes written at address 0x20000000
downloaded 64 bytes in 0.008047s (7.767 KiB/s)
> resume 0x20000001

让它运行,脚本速度可能仍然很慢,但肯定花时间输入停止命令是足够的。
> halt
stm32f4x.cpu: target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x41000000 pc: 0x20000008 msp: 0x20001000
> mdw 0x20000400 10
0x20000400: 12345679 12345678 ce879a24 fc4ba5c7 997e5367 9db9a851 40d5083f fbfbcff8 
0x20000420: 035dce6b 65a7f13c 
> 

所以程序运行,程序读取 0x20000400 将其保存为 0x20000404 增量并将其保存为 0x20000400 并且它完成了所有这些。
> load_image /path/to/notmain.sram.elf
64 bytes written at address 0x20000000
downloaded 64 bytes in 0.008016s (7.797 KiB/s)
> resume 0x20000000
> halt
stm32f4x.cpu: target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x41000000 pc: 0x20000008 msp: 0x20001000
> mdw 0x20000400 10                           
0x20000400: 1234567a 12345679 ce879a24 fc4ba5c7 997e5367 9db9a851 40d5083f fbfbcff8 
0x20000420: 035dce6b 65a7f13c 
> 

所以我们不需要或用一个开始地址,你用 BX 做的,他们必须把地址直接推到电脑里,和/或为我们做正确的事情。

如果您只想修改链接器脚本以将 rom 替换为 ram。
20000000 <_start>:
20000000:   20001000
20000004:   20000041
20000008:   20000047
2000000c:   20000047
20000010:   20000047
20000014:   20000047
20000018:   20000047
2000001c:   20000047
20000020:   20000047
20000024:   20000047
20000028:   20000047
2000002c:   20000047
20000030:   20000047
20000034:   20000047
20000038:   20000047
2000003c:   20000047

20000040 <reset>:
20000040:   f000 f806   bl  20000050 <notmain>
20000044:   e7ff        b.n 20000046 <hang>

您可以使用 0x20000041 地址作为您的入口点(恢复 0x20000041),但您必须首先处理堆栈指针。

通过做这样的事情
> reg sp 0x20001000
sp (/32): 0x20001000
> reg sp
sp (/32): 0x20001000
> resume 0x20000041

请注意,论文中的 ram 比 rom 快,并且在增加时钟频率时不需要等待状态,因此如果您确实增加了时钟频率并仅在 ram 中调试,如果您不记得切换到闪存时它可能会失败设置闪存等待状态...除此之外,如果您愿意,您可以整天在 ram 中开发程序的空间显着减少。

一个不错的功能是您可以保持暂停和重新加载。我不知道在这个设备/调试器上,如果你打开缓存(一些 cortex-m4s 有缓存,如果不是全部)你必须小心确保在你更改程序时关闭。写入内存是一种数据操作,获取指令是一种指令获取操作,如果您在 0x20000100 处执行某条指令并将其缓存在 I 缓存中,则该操作可能会进入指令缓存。然后您停止使用调试器,然后编写一个新程序,其中包括缓存中的地址(0x20000100),当您运行它时,I 缓存尚未刷新,因此您将混合运行缓存中的先前程序和数据中的新程序,其中充其量是一场灾难。因此,要么在以这种方式运行时永远不要打开缓存,要么想出解决此问题的方法(在停止程序之前清除缓存,使用重置按钮在运行、电源循环等之间重置处理器)。

关于c - 在 STM32 中从 RAM 执行代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42612329/

相关文章:

c++ - 即使使用 malloc,两个 char * 也可以具有相同的内存地址吗?

C,段错误(核心转储),Linux。我能做些什么?

c - C 中的共享全局 const 变量,在 mexFunction() 中定义

C .pc 文件警告

c++ - 如何为 flex/bison 实现更好的错误消息

c++ - 数组中的函数指针(nix c++)

c++ - 用于从 FORTRAN 代码调用的 C++ 函数调用 C++ 代码的 Cmake 配置文件

iOS:-dynamic 未指定以下标志无效:-sectcreate

c++ - 如何在 GCC 中使用 OpenSSL?

c++ - 防止意外对象不兼容?