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

Posted

技术标签:

【中文标题】如何在 Cortex-M3 (STM32) 上从 RAM 执行功能?【英文标题】:How do I execute a function from RAM on a Cortex-M3 (STM32)? 【发布时间】:2011-03-03 21:47:39 【问题描述】:

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

【问题讨论】:

嗯,我认为您应该或多或少将其发布到制造商的论坛,因为您使用的是特定硬件,但是,您能否提供更多详细信息,您使用的是什么编译器,您如何调试它,任何代码示例,也许 一个 有知识的人可能能够回答这个问题,除此之外...... @tommieb75:但我更喜欢你们! 【参考方案1】:

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

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

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

ARMExamplesRAM_Function文件夹中有一个示例。

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

【讨论】:

我遇到了同样的问题,只是我没有使用 RTX 或任何库,所以我没有包含自动将 RAM 函数加载到 RAM 中的编译器代码。我想在 RAM 中有一个加载区域或执行区域,我的代码实际上将链接到该区域,但我需要 JTAG 程序员将代码加载到另一个地址(在闪存中)。理想情况下,在 .sct 文件中(我使用的是 Keil MDK),我会有一个部分用于指定存储和链接地址。但我还没想好怎么做。 @Captain:您可能需要考虑根据您的具体情况提出一个新问题。但是,我相信上述解决方案也适用于您的情况。【参考方案2】:

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

【讨论】:

【参考方案3】:

由于 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 可以紧跟在它们出现的例程代码之后;如果您尝试使用最后一条指令后面的标签来确定例程的长度,则该长度将无法包含补充数据。

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

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

【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于如何在 Cortex-M3 (STM32) 上从 RAM 执行功能?的主要内容,如果未能解决你的问题,请参考以下文章

为啥编译器标志“-mcpu=cortex-m3”与 stm32f10x 出错?

Cortex-M3之STM32嵌入式系统设计的内容简介

Cortex-M3之STM32嵌入式系统设计的目录

如何在 C 中的 STM32 QEMU 调试器 eclipse 上从 RAM 内存执行函数?

MDK中在stm32下载出现error:flash download failed “cortex-m3”的问题

stm32启动过程cortex-m3架构堆栈代码位置编译汇编链接分析