如何在 Cortex-M3 (STM32) 上从 RAM 执行函数?
我正在尝试从 Cortex-M3 处理器 (STM32) 上的 RAM 执行函数。该函数会擦除并重写内部闪存,所以我肯定需要在 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 位被置位。
I'm trying to execute a function from RAM on a Cortex-M3 processor (STM32). The function erases the and rewrites the internal flash, so i definitely needs to be in RAM but how do I do that?
What I have tried is this: Copy the function to a byte array in RAM using memcpy (checking that it gets aligned correctly), setting a function pointer to point to the byte array an then calling the the function(pointer).
This works fine for maybe 10 instructions (I can follow the execution with the debugger) but then I get a buss error and the processor resets. The buss error occurs on the second pass through a loop so the code should be fine (as it works the first pass). I'm thinking that the faster RAM access mucks up the buss timing in some way...
Anyway is there a correct way to do this? How would a scatter file look like that places a function in RAM automatically (I'm using Keil uVision for Cortex-M3)?
Edit: More info:
Toolchain: RealView MDK-ARM V 4.10
Compiler: Armcc v4.0.0.728
Assembler: Armasm v4.0.0.728
Linker: ArmLink v4.0.0.728
Processor: STM32F103ZE
The IMPRECISERR bit is set in the buss fault register when the reset happens.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
循环迭代时崩溃可能是因为该函数分支到绝对地址,并且与 RAM 中的新函数位置无关。由于闪存擦除操作,此时访问原始代码位置是否会导致总线错误?
我相信您可以通过将 __ram 指令附加到函数定义来标记要使用 CARM 正确编译并复制到 RAM 的函数。有关如何使用 RealView 编译器执行相同操作的说明,请参阅 在 RAM 中执行函数技术支持文章:
这应该生成启动代码来负责将函数复制到 RAM 并将调用正确链接到该位置。否则,如果您需要动态地将任意函数复制到 RAM,请考虑编译 位置无关代码 (PIC) 与 RealView。
The crash upon loop iteration is probably because the function is branching to an absolute address and is not relative to the new function location in RAM. Would accessing the original code location at that point cause a bus error because of the flash erase operation?
I believe you can mark a function to be compiled and copied to RAM correctly with CARM by appending the
__ram
directive to the function definition. For instruction on how to do the same with the RealView compiler see the EXECUTING FUNCTIONS IN RAM technical support article:That should generate startup code to take care of copying the function to RAM and linking calls to that location correctly. Otherwise, if you need to dynamically copy arbitrary functions to RAM, then look into compiling position independent code (PIC) with RealView.
在不了解更多关于您的情况的情况下,我只能建议一些一般性的事情...确保您有该函数的有效堆栈(或避免函数中的所有堆栈操作),您的中断被禁用,并且该函数中的任何向量系统向量表不指向擦除闪存时消失的代码。最后,确保您的函数链接以在您放置的地址运行...代码可能无法重定位,并且可能跳转到旧位置中的某个位置。
Without knowing more about your situation I can only suggest a few general things... make sure you have a valid stack for that function (or avoid all stack operations in the function), that your interrupts are disabled, and that any vectors in the system vector table don't point to code that goes away when you erase flash. Lastly, make sure your function is linked to run at the address you put it... the code may not be relocatable and may jump to a spot in is old location.
由于 ARM 加载即时数据的能力有限,因此为 ARM 生成代码的实用程序经常将代码和数据并置。例如,类似的语句
最终可能会类似于:
请注意,_myVar 和 0x12345678 可能紧跟在它们出现的例程的代码后面;如果您尝试使用最后一条指令后面的标签来确定例程的长度,则该长度将无法包含补充数据。
ARM 需要注意的另一件事是,由于历史原因,即使代码实际上从半字边界开始,代码地址通常也会设置最低有效位。因此,地址为 0x12345679 的指令将占用从 0x12345678 开始的两个或四个字节。这可能会使 memcpy 之类的地址计算变得复杂。
我的建议是用汇编语言编写一个小例程来完成您需要的操作。它只有几条指令,您可以准确地知道代码正在做什么以及它可能具有哪些地址依赖关系,并且您不必担心未来的编译器版本会以破坏某些内容的方式更改您的代码[例如第三个即使
dat1
落在奇数半字边界上,上述代码的版本也不会出现问题,因为 M3 的 LDR 指令可以处理未对齐读取,但使用 LDRM 的第四个(稍快且更紧凑)版本会失败在这种情况下;即使当前版本的编译器使用四个 LDR 指令,未来版本也可能使用 LDRM]。Because the ARM has a limited ability to load immediate data, utilities which generate code for the ARM frequently juxtapose code and data. For example, a statement like
might end up as something like:
Note that _myVar and 0x12345678 may be placed immediately following the code for the routine in which they appear; if you try to determine the length of the routine using a label which follows the last instruction, such length will fail to include the supplemental data.
An additional thing to note with the ARM is that for historical reasons, code addresses will often have their least significant bit set even though code actually starts on half-word boundaries. Thus, an instruction whose address is 0x12345679 will occupy either two or four bytes starting at 0x12345678. This can complicate address calculation for things like memcpy.
My recommendation would be to write a small routine in assembly language to do what you need. It's only a few instructions, you can know exactly what the code is doing and what address dependencies it might have, and you won't have to worry about future compiler versions changing your code in such a fashion as to break something [e.g. the third version of the above code would have no problem even if
dat1
landed on an odd halfword boundary since the M3's LDR instruction can handle unaligned reads, but the fourth (slightly faster and more compact) version using LDRM would fail in such a case; even if today's version of a compiler uses four LDR instructions, a future version might use LDRM].使用 IAR 编译器(我知道你的问题是关于 Keil 但我没有它可以玩),你可以将整个项目或单个文件标记为“位置无关”。过去将其与其他处理器一起使用意味着您可以将其移动到“任何地方”并且它仍然可以正常工作
With the IAR compiler (I know your question is about Keil but I don't have it to play with) you can mark either the whole project or an individual file to be "position independent". From using this in the past with other processors it means you can move it "anywhere" and it will still work ok