HardFault when 运行 代码来自 CortexM0 中的 RAM
HardFault when running code from RAM in CortexM0
我目前正在为 Arm Cortex-M0+ 微控制器开发固件,我遇到了一个相当有趣的问题。我不是在寻找任何答案,而是想与其他开发人员分享这个问题,这样我就可以(希望)阐明我面临的问题。我将在下面描述它:
我有一个程序可以从外部闪存芯片动态加载(正确编译和链接)代码,直接从 MCU RAM 中执行。有趣的是,我可以在 运行 逐步执行(通过调试器)时完美执行加载 RAM 的代码,但在自由 运行 宁时它总是会崩溃 [正式的 HardFault]。我已经尝试禁用所有中断,我已经仔细检查了指令、内存地址、字节对齐等所有内容,但我仍然无法查明异常的原因。
你们中的一些人有任何关于可能发生的事情的暗示吗?我很想知道更多关于您的经历!谢谢,
更新 1 (30/05)
Free 运行 在这种情况下,意味着不在分支到 RAM 之前设置断点。每当我进入分支并执行 RAM 中的指令时,它都会正确 运行 和 return。只要断点不存在(因此 MCU 会通过分支进行缩放),就会观察到 HardFault。请注意,即使在使用调试器但未设置断点的情况下启动时它也会崩溃。
更新 2 (30/05)
我正在使用 Cypress S6E1C3 系列 Arm Cortex M0+ FM0+ 微控制器。
更新 3 (30/05)
在深入研究并使用代码后,我可以让它正常工作!然而,它给我带来的问题多于答案。阅读有关 BLX 指令 (BLX) 的官方 ARM 文档,我发现分支地址的 LSBit 决定了 CPU 指令模式 (1 导致它运行 在 Thumb 模式下)。通过显式设置此位,我让代码始终有效,即使在 free-运行 模式 下也是如此。问题是,RAM 中的代码尚未在 Thumb 模式下编译,并且 apparent 没有理由 运行 用调试器一步一步地编译代码会导致指令模式改变...有什么想法吗?
K.
Cortex-M 仅支持Thumb模式,不支持ARM模式,所以告诉你的编译器去编译Cortex-M0+ 保证它将创建 Thumb2 代码。
这就是为什么您需要设置目标地址的低位。
https://en.wikipedia.org/wiki/ARM_Cortex-M#Instruction_sets
Only Thumb-1 and Thumb-2 instruction sets are supported in Cortex-M architectures; the legacy 32-bit ARM instruction set isn't supported.
如果您实际查看内存中的代码字节,您会发现它们是 Thumb2 指令。
唯一的问题是你的调试器是如何做到不是错误的。也许它无论如何都必须专门处理 BLX,并且不会't 模仿 fault-if-switching-to-ARM-mode on Thumb-only 微架构行为。或者也许只是处理 single-step 中断最终在 Thumb 模式下正确返回。
我认为“legacy”对于一般的 ARM 模式有点夸大其词;我认为 high-performance ARM 芯片(具有大型指令缓存)仍然可以从 ARM 模式中获益,以更少的指令完成更多工作并更容易访问更多寄存器。无论如何,这只是维基百科的措辞。
问题出在分支地址(正如@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 行中,codeBufferAddress
与 0x01
(codeBufferAddress | 0x01
) 进行或运算,这确保分支将在 Thumb 模式。在我发布这个问题时,codeBufferAddress
持有的值是 0x20003E40
,这显然没有设置 LSBit,因此会强制 MCU 在 ARM 中为 运行模式.
我认为编译器将无法推断和调整分支模式,因为地址是动态生成的(尽管我认为它可能更智能和强制在 Thumb 模式 中所有分支到 运行,因为我的 MCU 只能解码 Thumb)。
无论如何,当运行在step-by-step模式下,我无法观察到目标地址有任何变化,现在我只能猜测它强制MCU 运行在 Thumb 模式... 但这只是一个猜测。有什么想法吗?
K.
我目前正在为 Arm Cortex-M0+ 微控制器开发固件,我遇到了一个相当有趣的问题。我不是在寻找任何答案,而是想与其他开发人员分享这个问题,这样我就可以(希望)阐明我面临的问题。我将在下面描述它:
我有一个程序可以从外部闪存芯片动态加载(正确编译和链接)代码,直接从 MCU RAM 中执行。有趣的是,我可以在 运行 逐步执行(通过调试器)时完美执行加载 RAM 的代码,但在自由 运行 宁时它总是会崩溃 [正式的 HardFault]。我已经尝试禁用所有中断,我已经仔细检查了指令、内存地址、字节对齐等所有内容,但我仍然无法查明异常的原因。
你们中的一些人有任何关于可能发生的事情的暗示吗?我很想知道更多关于您的经历!谢谢,
更新 1 (30/05)
Free 运行 在这种情况下,意味着不在分支到 RAM 之前设置断点。每当我进入分支并执行 RAM 中的指令时,它都会正确 运行 和 return。只要断点不存在(因此 MCU 会通过分支进行缩放),就会观察到 HardFault。请注意,即使在使用调试器但未设置断点的情况下启动时它也会崩溃。
更新 2 (30/05)
我正在使用 Cypress S6E1C3 系列 Arm Cortex M0+ FM0+ 微控制器。
更新 3 (30/05)
在深入研究并使用代码后,我可以让它正常工作!然而,它给我带来的问题多于答案。阅读有关 BLX 指令 (BLX) 的官方 ARM 文档,我发现分支地址的 LSBit 决定了 CPU 指令模式 (1 导致它运行 在 Thumb 模式下)。通过显式设置此位,我让代码始终有效,即使在 free-运行 模式 下也是如此。问题是,RAM 中的代码尚未在 Thumb 模式下编译,并且 apparent 没有理由 运行 用调试器一步一步地编译代码会导致指令模式改变...有什么想法吗?
K.
Cortex-M 仅支持Thumb模式,不支持ARM模式,所以告诉你的编译器去编译Cortex-M0+ 保证它将创建 Thumb2 代码。
这就是为什么您需要设置目标地址的低位。
https://en.wikipedia.org/wiki/ARM_Cortex-M#Instruction_sets
Only Thumb-1 and Thumb-2 instruction sets are supported in Cortex-M architectures; the legacy 32-bit ARM instruction set isn't supported.
如果您实际查看内存中的代码字节,您会发现它们是 Thumb2 指令。
唯一的问题是你的调试器是如何做到不是错误的。也许它无论如何都必须专门处理 BLX,并且不会't 模仿 fault-if-switching-to-ARM-mode on Thumb-only 微架构行为。或者也许只是处理 single-step 中断最终在 Thumb 模式下正确返回。
我认为“legacy”对于一般的 ARM 模式有点夸大其词;我认为 high-performance ARM 芯片(具有大型指令缓存)仍然可以从 ARM 模式中获益,以更少的指令完成更多工作并更容易访问更多寄存器。无论如何,这只是维基百科的措辞。
问题出在分支地址(正如@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 行中,codeBufferAddress
与 0x01
(codeBufferAddress | 0x01
) 进行或运算,这确保分支将在 Thumb 模式。在我发布这个问题时,codeBufferAddress
持有的值是 0x20003E40
,这显然没有设置 LSBit,因此会强制 MCU 在 ARM 中为 运行模式.
我认为编译器将无法推断和调整分支模式,因为地址是动态生成的(尽管我认为它可能更智能和强制在 Thumb 模式 中所有分支到 运行,因为我的 MCU 只能解码 Thumb)。
无论如何,当运行在step-by-step模式下,我无法观察到目标地址有任何变化,现在我只能猜测它强制MCU 运行在 Thumb 模式... 但这只是一个猜测。有什么想法吗?
K.