如何在Cortex-M3(STM32)上从RAM执行function?

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

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

这可能适用于10条指令(我可以跟随调试器的执行)但然后我得到一个总线错误并且处理器重置。 第二次通过循环时发生总线错误,因此代码应该没问题(因为它在第一次传递时起作用)。 我认为更快的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

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

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

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

μVision允许您将模块定位到对话框项目 – 选项 – 目标中输入的特定存储区域。 为此,请右键单击源文件(或文件组),然后打开“ 选项 – 属性 ”对话框。 然后选择Memory Assignment下的内存区域。

文件夹ARMExamplesRAM_Function中有一个示例。

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

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

由于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之类的地址计算变得复杂。

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

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