如何从Cortex-M3(STM32)上的RAM执行函数?

时间:2010-06-15 09:35:23

标签: c ram stm32 keil

我正在尝试从Cortex-M3处理器(STM32)上的RAM执行函数。该功能擦除并重写内部闪存,所以我肯定需要在RAM中,但我该怎么做?

我试过的是:使用memcpy将函数复制到RAM中的字节数组(检查它是否正确对齐),将函数指针设置为指向字节数组然后调用函数(指针)。

这可能适用于10条指令(我可以跟随调试器的执行)但是然后我得到一个buss错误并且处理器重置。第二次通过循环时发生总线错误,因此代码应该没问题(因为它在第一次传递时起作用)。我认为更快的RAM访问会以某种方式破坏总线时序......

无论如何有正确的方法吗?分散文件如何将函数自动放入RAM中(我正在使用Keil uVision for Cortex-M3)?

编辑:更多信息: 工具链:RealView MDK-ARM V 4.10 编译:Armcc v4.0.0.728 汇编程序:Armasm v4.0.0.728 链接器:ArmLink v4.0.0.728 处理器:STM32F103ZE

复位发生时,BURECISERR位在总线故障寄存器中置位。

4 个答案:

答案 0 :(得分:7)

循环迭代时崩溃可能是因为函数分支到绝对地址而不是相对于RAM中的新函数位置。在那一点访问原始代码位置会因闪存擦除操作而导致总线错误吗?

我相信你可以通过将__ram指令附加到函数定义来标记要使用CARM正确编译和复制到RAM的函数。有关如何对RealView编译器执行相同操作的说明,请参阅EXECUTING FUNCTIONS IN RAM技术支持文章:

  

μVision允许您定位模块   特定的内存区域   在对话框中输入项目 -   选项 - 目标。这样做,对   单击源文件(或文件组)   并打开对话框选项 -   属性即可。然后选择内存   内存分配下的区域。

     

ARMExamplesRAM_Function 文件夹中有一个示例。

这应该生成启动代码,以便将函数复制到RAM并正确地将调用链接到该位置。否则,如果您需要将任意函数动态复制到RAM,那么请查看使用RealView编译position independent code (PIC)

答案 1 :(得分:2)

在不了解您的情况的情况下,我只能建议一些常规事项...确保您拥有该函数的有效堆栈(或避免函数中的所有堆栈操作),您的中断被禁用,以及任何系统向量表中的向量不指向擦除闪存时消失的代码。最后,确保您的函数链接以在您放置它的地址运行...代码可能无法重定位并且可能跳转到旧位置的某个位置。

答案 2 :(得分:2)

由于ARM加载即时数据的能力有限,因此为ARM生成代码的实用程序经常并置代码和数据。例如,像

这样的陈述
void myRoutine(void)
{
  myVar1=0x12345678;
  myVar2=0x87654321;
}

可能最终会像:

myRoutine:        
    ldr r0,=myVar1; Load the address of _myVar
    ldr r1,=0x12345678
    str r1,[r0]
    ldr r0,=myVar1; Load the address of _myVar
    ldr r1,=0x87654321
    str r1,[r0]
    bx  lr

which would get translated into:
    ldr r0,dat1
    ldr r1,dat2
    str r1,[r0]
    ldr r0,dat3
    ldr r1,dat4
    str r1,[r0]
    bx  lr
... followed some time later by
dat1 dcd _myVar1
dat2 dcd 0x12345678
dat3 dcd _myVar2
dat4 dcd 0x12345678

or perhaps even something like:
    mar  r0,dat1
    ldrm r0,[r1,r2,r3,r4]
    str r2,[r1]
    str r4,[r3]
    bx  lr
... followed some time later by
dat1 dcd _myVar1
dat2 dcd 0x12345678
dat3 dcd _myVar2
dat4 dcd 0x12345678

请注意,_myVar和0x12345678可以紧跟在它们出现的例程的代码之后;如果您尝试使用最后一条指令后面的标签来确定例程的长度,则此长度将无法包含补充数据。

ARM需要注意的另一件事是,由于历史原因,代码地址通常会设置最低有效位,即使代码实际上是从半字边界开始的。因此,地址为0x12345679的指令将占用从0x12345678开始的两个或四个字节。这可能会使像memcpy这样的地址计算变得复杂。

我的建议是用汇编语言编写一个小例程来做你需要的。它只是一些指令,你可以准确地知道代码正在做什么以及它可能具有什么地址依赖性,你不必担心将来的编译器版本会以破坏某些东西的方式改变你的代码[例如即使dat1落在奇数半字边界上,上述代码的第三个版本也没有问题,因为M3的LDR指令可以处理未对齐的读取,但使用LDRM的第四个(稍微更快和更紧凑)版本将失败这种情况;即使今天的编译器版本使用四个LDR指令,未来版本也可能使用LDRM]。

答案 3 :(得分:1)

使用IAR编译器(我知道您的问题是关于Keil但我没有使用它)您可以将整个项目或单个文件标记为“位置无关”。从过去使用它与其他处理器,这意味着你可以“随处”移动它,它仍然可以正常工作