在CortexM0中从RAM运行代码时发生HardFault

时间:2019-05-29 20:30:31

标签: c++ assembly memory arm cortex-m

我目前正在为Arm Cortex-M0 +微控制器开发固件,并且面临一个相当有趣的问题。我不是在寻找任何答案,而是想与其他开发人员分享问题,以便我(希望)对我所面临的问题有所了解。我将在下面进行描述:

我有一个程序可以从外部闪存芯片动态加载(正确编译和链接)代码,并直接在MCU RAM中执行。有趣的是,在逐步运行(通过调试器)时,我可以完美地执行RAM加载的代码,但是在自由运行时,它总是会崩溃(正式为HardFault)。我试图禁用所有中断,我仔细检查了指令,内存地址,字节对齐方式以及所有内容,但是我仍然无法查明异常的原因。

你们中有人对可能发生的事情有任何暗示吗?我非常想知道更多有关您的经历!谢谢,

更新1(30/05)

在这种情况下,

自由运行表示未在分支到RAM之前立即设置断点。每当我进入分支并在RAM中执行指令时,它将正确运行并返回。无论断点不在哪里(因此MCU都会通过分支进行缩放),都会观察到HardFault。请注意,即使在启动调试器但未设置断点的情况下启动,它也会崩溃。

更新2(30/05)

我正在使用赛普拉斯S6E1C3系列Arm Cortex M0 + FM0 +微控制器

更新3(30/05)

深入研究并使用代码后,我可以使其正常工作!但是,它给我带来的问题多于答案。阅读有关BLX指令(BLX)的ARM官方文档后,我发现分支地址的LSBit确定CPU指令模式(1使其以Thumb模式运行)。通过显式设置此位,即使在自由运行模式 ,我也可以使代码始终运行。事实是,RAM中的代码尚未在Thumb模式下进行编译,并且没有明显原因,为什么使用调试器逐步运行代码会导致指令模式改变。 。有什么想法吗?

K。

2 个答案:

答案 0 :(得分:1)

仅Cortex-M 支持Thumb模式,不支持ARM模式,因此告诉编译器使用Cortex-M0 +的编译器可确保它将创建Thumb2代码。

这就是为什么您需要设置目标地址的低位。

  

https://en.wikipedia.org/wiki/ARM_Cortex-M#Instruction_sets

     

在Cortex-M架构中仅支持Thumb-1和Thumb-2指令集。不支持旧的32位ARM指令集。

如果您实际上查看内存中的代码字节,您会发现它们是Thumb2指令。

唯一的问题是调试器如何设法使其成为 not 错误。也许它无论如何都必须专门处理BLX,并且不能模拟故障-仅拇指微体系结构行为切换到ARM模式。也许只是处理单步中断最终会在Thumb模式下正确返回。


我认为,“传统”对于ARM模式通常有点夸大其词;我认为高性能ARM芯片(具有大指令高速缓存)仍然可以受益于ARM模式,以更少的指令完成更多的工作,更容易访问更多的寄存器。无论如何,那只是维基百科的措辞。

答案 1 :(得分:1)

问题出在分支机构的地址(由@PeterCordes正确指出)。跳转到RAM的代码是以下代码(针对此受众群体略有调整):

// Prepare address and Function Pointer
uint32_t codeBufferAddress = reinterpret_cast<uint32_t>(&buffer) + offset;
void(*methodFromRAM)(void*) = reinterpret_cast<void(*)(void*)>(codeBufferAddress | 0x01);

// Branch to RAM
// thisPointer holds an object byte-stream...
(*methodFromRAM)(reinterpret_cast<void*>(thisPointer));

请注意,在第3行中,codeBufferAddress0x01codeBufferAddress | 0x01)进行了相或运算,以确保分支将以拇指模式进行。在我发布此问题时,codeBufferAddress持有的值为0x20003E40,这显然未设置LSBit,因此将迫使MCU在 ARM模式下运行。

我认为编译器将无法推断和调整分支模式,因为地址是动态生成的(尽管我认为它可能更聪明,并强制所有分支都在其中运行拇指模式,因为我的MCU只能解码Thumb)。

无论如何,当我以逐步模式运行时,我无法观察到目标地址的任何变化,现在我只能猜测它迫使MCU在拇指模式下运行。 ..但这只是一个猜测。有什么想法吗?

K。