2017-03-05 130 views
1

我最近已经开始在STM32F4核板上编程。我刚才发现闪存编程只能在有限的时间内完成(尽管这不是少数,但是它是一个评估板,它将被反复编程以开发不同的项目)。之后,我读了一些地方,可以直接编程到RAM而不是闪存,但找不到任何有关它的技术信息。从STM32的RAM执行代码

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

PS:我用生成的代码由STM32CubeMX对系统工作台和脚本生成的Makefile项目

+0

更大的问题是您希望代码如何进入内存。你必须有一些连接到另一个设备,在重置后提供代码(或者从闪存加载代码到RAM中,但是这样做的目的是失败的[尽管你可以通过压缩代码在闪存中减少闪存写入])。 – EOF

+0

闪存编程可能有数千个,如果不是数万个,你有没有打过呢? –

+0

除了@FreddieChopin的出色答案之外,还有两点是关于在STM32上从RAM执行的; 1)对于大多数部件来说,RAM的尺寸比闪光灯大得多*,所以你会限制你的应用尺寸。 2)当从闪存运行时,读/写数据和指令访问位于不同的总线上,并且闪存具有*加速器*,允许完整的1.25DMIPS/MHz的性能表现。从ram运行导致数据和指令访问的总线争用,并大大降低了执行速度。 – Clifford

回答

3

首先的 - 不考虑节约闪光灯太多。当我开始使用微控制器时,我的计划和你一样,但后来得出结论,它根本没有意义。一个STM32F4芯片的例子有一个闪存,保证最少 10000个写/擦除周期。你将不得不每天编程你的董事会14次,每一天连续两年达到这个价值。即使到了它,也不是说闪光灯立即停止工作。最有可能的是,您不应该指望在保证的20年内保留Flash内容。考虑到耐用性和通常使用周期,所有这些努力都是不值得的(平均而言,您的主板每天可能会看到几次写入/擦除周期,而且无论如何,您可能几年后都不会玩这个游戏)。特别是如果我们谈论廉价的电路板。

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

如果你真的想从RAM 执行代码和不会写闪存可言,只记得这是可能与调试器。否则,你必须写你的代码到闪存,用一个小程序将它复制到RAM然后从那里执行 - 这完全没有意义,因为你最初的想法是保存闪存。无论如何 - 如果你想这样做,这很简单,你所要做的就是修改链接脚本。首先从MEMORY部分完全删除“ROM”(或者可能是“flash”或者类似的)内存块。现在用RAM内存块替换已删除内存的所有用途,所以你应该用“ram”替换所有“ROM”的出现(或者用“sram”或​​者那样的“flash”替换掉)。在这个阶段它应该实际上工作。你应该做的最后一件事是完全删除代码和功能来执行.data节初始化 - 这将需要修改链接描述文件(确保本节的LMA与它的VMA相同),并将初始化代码从Reset处理程序。

请注意,此过程工作,你应该:

  • 选择“从SRAM启动”与BOOT0 & BOOT1引脚,
  • 力PC和SP,以正确的地址与调试。

对于您的Nucleo板,不幸的是第一个选项不可用,因为BOOT1引脚(在本例中应该是高电平)短接到GND。

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

+0

cortex-m使用向量表,与全尺寸的arm不同,所以入口点是错误的,调试器必须进入复位或移除向量表,无论哪种方式都必须替换堆栈指针的初始化。 –

+0

@old_timer - 您在评论中提出的所有问题根本无关紧要,因为您可以调试程序(并且 - 在这种情况下 - 应该!)随心所欲地执行任何操作。调试器可以调整PC以在任何你喜欢的地方启动。它也可以调整堆栈指针。如果您想从RAM运行代码,那么使用STM32,您应该相应地设置BOOTx引脚,这只会解决您提到的所有问题。 –

+0

你在哪里提到了bootx引脚?他们在核板上发生了什么? –

3

如果你最近开始使用它,那么你需要很长时间才会出现闪光。您可能会遇到驱动器完全错误,只需拔下插头并重新插入电路板即可。我已经有这些东西多年了,还没有磨损过闪存。不要说它不能做,它可以但你不可能在那里,除非你写了一个闪存甩掉程序,它穿着它。你需要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 
> 

全尺寸手臂处理器您的切入点是一条指令,您只需 开始执行。皮层-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 . 

理论上你可以跳转到复位处理程序的地址,但链接脚本会想,在闪光灯,取决于什么位置将无法正常工作。如果你依靠向量表来做到这一点,你的堆栈指针可能不会被设置。因此,而不是像这样的工作,一个完整的示例的一部分

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 
} 

基本上取代ROM与RAM参考。 (如果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版本的程序。

所以现在我们有我们的远程登录到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这样做,他们必须只推地址正确插入到PC,和/或做正确的事情对我们来说。

如果你只是修改你的链接脚本来替换RAM的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中的时钟频率和调试更快只,如果您还没有记住设置闪存等待状态,则切换到闪存时可能会失败......除此之外,如果您需要,可以整天在内存中开发程序的空间大大减少。

一个很好的功能是,您可以继续停止和重新加载。我不知道在这个设备/调试器上,如果你打开缓存(一些cortex-m4s有缓存,如果不是全部的话),你必须小心确保当你改变程序时关闭。写入内存是一个数据操作,取指令是指令取指操作,如果你在0x20000100执行一些指令并将其缓存到I缓存中,它可以落在指令缓存中。那么你停止使用调试器,然后编写一个新程序,包括缓存中的地址(0x20000100),当你运行它时,I cache没有被刷新,所以你将运行缓存中的先前程序和数据中的新程序的混合充其量只是一场灾难。因此,无论是以这种方式运行时都不要打开缓存,或想出解决方案(在停止程序之前清除缓存,使用重置按钮在运行之间重置处理器,重新启动等)。

+0

你确定只有thumb2处理器上的''andcs'代码? – EOF

+0

只有thumb2没有这样的事情。 thumb2只是拇指的扩展。这就是反汇编器试图从这些32位值中得出的值,它们是地址。 –