7

我正在尝试从 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 位被设置。

4

4 回答 4

7

循环迭代时的崩溃可能是因为该函数正在分支到一个绝对地址,并且与 RAM 中的新函数位置无关。由于闪存擦除操作,此时访问原始代码位置会导致总线错误吗?

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

µVision 允许您将模块定位到在对话框Project-Options-Target中输入的特定内存区域。为此,请右键单击源文件(或文件组)并打开对话框Options - Properties。然后在Memory Assignment下选择内存区域。

文件夹ARMExamplesRAM_Function中有一个示例。

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

于 2010-06-15T12:53:32.637 回答
2

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

于 2010-06-15T10:14:06.890 回答
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 之类的地址计算复杂化。

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

于 2013-06-05T16:42:35.547 回答
1

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

于 2012-10-22T12:03:48.610 回答