从STM32中的RAM执行代码

时间:2017-03-05 18:34:59

标签: c gcc linker arm stm32

我最近开始在STM32F4核板上编程。我只是想到闪存中的编程只能在有限的时间内完成(尽管它不是很少,但它是一个评估板,它将被反复编程以开发不同的项目)。之后,我在某处读到可以直接编程到RAM而不是flash,但找不到任何关于它的技术信息。

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

ps:我使用STM32CubeMX为System workbench生成的代码和一个为项目生成makefile的脚本

2 个答案:

答案 0 :(得分:4)

如果您最近开始使用它,那么您需要很长时间才能消除闪光灯。您可能会遇到驱动器完全错误,只需拔下插头并重新插入电路板。我已经有这些东西多年了,还没有磨损闪光灯。不是说它不能完成,它可以,但你不可能在那里,除非你写了一个闪光捶打程序,穿着它。

您将需要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
> 

全尺寸手臂处理器您的切入点是一个指令而你就是 开始执行。 cortex-m使用的矢量表不仅可以在那里分支。

.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 .

理论上你可以分支到重置处理程序地址,但链接器脚本在flash中需要它,任何位置依赖都不起作用。如果您依赖向量表来执行此操作,则可能无法设置堆栈指针。所以相反的事情会起作用,是一个完整的例子的一部分

sram.s

.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

notmain.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);
}

sram.ld

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做的,他们必须把地址直接推到电脑上,和/或为我们做正确的事。

如果您只修改链接描述文件以用rams替换roms。

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),我的缓存还没有被刷新,所以你将运行缓存中的先前程序和数据中的新程序的混合,充其量只是一场灾难。所以要么在运行这种方式时从不打开缓存,要么找到解决此问题的方法(在停止程序之前清除缓存,使用重置按钮在运行,电源循环等之间重置处理器)。

答案 1 :(得分:3)

首先 - 不要考虑过多地保存闪存。当我开始使用微控制器时,我有与你相同的计划,但后来得出结论,它根本没有意义。一个示例STM32F4芯片具有闪存,可确保最小 10000个写/擦除周期。您必须每天对您的电路板进行14次编程,每一天,直接两年才能达到该值。即使你到达它之后,也不会说闪光灯立即停止工作。最有可能的是,您不应指望在保证的20年内保留闪存内容。考虑到耐力和通常的使用周期,所有这些努力都不值得给您带来麻烦(平均而言,您的电路板每天可能会看到一些写入/擦除周期,并且您可能无论如何都不会在几年后使用它)。特别是如果我们谈论便宜的电路板。

TL; DR:只是不要试图保存闪存。这不值得所有的麻烦。

如果您真的想从RAM 执行代码而根本不执行闪存,请记住,使用调试器只能 。否则你必须把你的代码编写到flash中,用一个小例程将它复制到RAM然后从那里执行 - 考虑到你保留flash的最初想法,这将完全没有意义。无论如何 - 如果你想这样做,它非常简单,你所要做的就是修改链接器脚本。首先从MEMORY部分完全删除“rom”(或者可能是“flash”或类似的)内存块。现在用RAM内存块替换已删除内存的所有使用,所以你应该用“ram”替换所有出现的“rom”(或者用“sram”代替“闪存”或者像那样)。在这个阶段它应该实际工作。您应该做的最后一件事是完全删除执行.data段初始化的代码和功能 - 这需要修改链接器脚本(确保此部分的LMA与其VMA相同),并删除复位处理程序例程中的初始化代码。

请注意,要使此程序有效,您应该:

  • 用BOOT0&amp;选择“从SRAM启动”。 BOOT1引脚,
  • 强制PC和SP使用调试器更正地址。

对于您的Nucleo板,遗憾的是第一个选项不可用,因为BOOT1引脚(在这种情况下应该为高电平)短接到GND。

但是再次 - 只是不要这样做,这不值得麻烦。

相关问题