2010-06-15 826 views
7

我试图从Cortex-M3处理器(STM32)上的RAM执行一个函数。该功能擦除和重写内部闪存,所以我肯定需要在RAM中,但我该怎么做?如何从Cortex-M3(STM32)上的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

当复位发生时,IMPRECISERR位在总线错误寄存器中置1。

+0

嗯,我认为你应该多或交小于制造商的论坛,您使用的是特殊的硬件,但是,你能不能提供更多的细节,你使用的是什么编译器,你怎么调试它,任何代码样本,也许*一个*有知识的人可以回答这个问题,除此之外...... – t0mm13b 2010-06-15 09:39:20

+1

@ tommieb75:但我更喜欢你们! – c0m4 2010-06-15 09:42:36

回答

7

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

我相信你可以通过在函数定义中附加__ram指令来标记一个函数,以便用CARM正确地编译和复制到RAM中。有关如何做同样的用的RealView编译器指令看到EXECUTING FUNCTIONS IN RAM技术支持文章:

的μVision让你 定位模块是 在对话框项目进入特定的存储区 - 选项 - 目标。为此,请右键点击源文件(或文件组) 并打开对话框选项 - 属性。然后选择内存 区域根据内存分配

在文件夹ARMExamplesRAM_Function中有一个示例。

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

+0

优秀的答案! – c0m4 2010-06-15 13:54:18

+0

我有同样的问题,除了我没有使用RTX或任何库,所以我不包括编译器的代码,自动将RAM函数加载到RAM中。 我想有一个加载区或执行区的RAM在我的代码实际上将被链接到,但我需要的JTAG程序员的代码加载到另一个地址(闪光灯)。 理想情况下,在.sct文件中(我正在使用Keil MDK)我会有一段指定存储和链接地址的部分。 但我还没有制定出如何做到这一点呢。 – 2010-08-10 03:54:38

+0

@Captain:你可能想考虑用你的具体情况开一个新的问题。不过,我相信上述解决方案也适用于您的情况。 – 2010-08-10 13:04:50

2

不知道更多关于你的情况我只能提出一些一般的东西......确保你有一个有效的堆栈(或避免函数中的所有堆栈操作),你的中断被禁用,系统向量表中的任何向量都不会指向擦除闪存时消失的代码。最后,确保你的功能是链接运行在你放置它的地址...代码可能不可重定位,并可能跳转到旧地点的位置。

1

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

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]。