log2 在 C 中导致 STM32F4 MCU 出现故障
log2 causes a fault in STM32F4 MCU in C
为什么这段代码会导致硬故障并跳入死循环?
#include <stdint.h>
#include <math.h>
void myfunc()
{
const double val = 1;
double log_res = log2(val); // <----- THIS CAUSES A FAULT
//double log_res = log2(1); // This works
}
当我用硬编码的 1 替换邪恶行中的 val
时,代码有效。所以只有当我将 val
传递给 log2
时才会出现问题(如代码所示)。为什么会这样?
我正在使用 STM32CubeIDE(基于 eclipse)和 STM32F429ZI MCU。
更新:
检查反汇编中的异常 window 表明这是发生的异常:
fffffff9: Failed to execute MI command:
-data-disassemble -s 4294967289 -e 4294967429 -- 3
Error message from debugger back end:
Cannot access memory at address 0xfffffffe
有人知道为什么会这样吗?
更新 2:
在汇编指令中调试:
54 const double val = 1;
08000e0a: mov.w r3, #0
08000e0e: ldr r4, [pc, #64] ; (0x8000e50 <myfunc+88>)
08000e10: strd r3, r4, [r7, #16]
55 double log_res = log2(val);
08000e14: vldr d0, [r7, #16] // <------ X THIS LINE CAUSES THE PROBLEM X
08000e18: bl 0x8002a9c <log>
08000e1c: vmov r0, r1, d0
08000e20: add r3, pc, #36 ; (adr r3, 0x8000e48 <myfunc+80>)
08000e22: ldrd r2, r3, [r3]
08000e26: bl 0x800085c <__divdf3>
强调线有d0=0,r7=0x2002ffcc
执行完这一行后,反汇编程序跳转到WWDG_IRQHandler
。
更新 3:
GCC 汇编程序选项(不确定这是做什么的):
-mcpu=cortex-m4 -g3 -c -x assembler-with-cpp --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
GCC 编译器选项:
-mcpu=cortex-m4 -std=gnu11 -g3 -DSTM32F429I_DISC1 -DSTM32 -DSTM32F429ZITx -DSTM32F4 -DDEBUG -DSTM32F429xx -c -I..\Inc -I../Inc/CMSIS/Include -I../Inc/CMSIS/Device/ST/STM32F4xx/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
GCC 链接器选项:
-mcpu=cortex-m4 -T"C:\Users\mne\STM32CubeIDE\workspace_1.0.0\MyUSB\STM32F429ZITX_FLASH.ld" --specs=nosys.specs -Wl,-Map="${ProjName}.map" -Wl,--gc-sections -static --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -Wl,--start-group -lc -lm -Wl,--end-group
更新 4:
问题似乎发生在 math.h
的许多功能上,例如fmin
.
关于您的问题:
当我用硬编码的 1 替换邪恶行中的 val 时,代码有效。所以只有当我将 val 传递给 log2 时才会出现问题(如代码所示)。为什么会这样?
以下声明:
double position = log2(first_set);
没有将变量 val
传递给函数:log2()
我预计这样的编码错误(因为 first_set
未定义)会导致代码无法编译。
您似乎从编译器选项中省略了标志 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
,并且仅将它们包含在汇编器选项中。 (您可以将 -march=armv7-m -mtune=cortex-m4
替换为 -mcpu=cortex-m4
。)因此,您的编译器正在为错误的浮点 ABI 生成代码。您指定了硬 ABI,但正在生成对软库函数的调用而不是内联汇编指令。
编辑: 如果编译器正在生成您的 FPU 不支持的指令,正如 ImageCraft 的 Richard 观察到的那样,您可以尝试将标志更改为 --mcpu=cortex-m4 --mfpu=auto
。
请注意,任何级别的优化,甚至 -O1
,都足以让 GCC 折叠常量并优化对 double position = 0.0;
的调用。您可能需要使用函数 return position;
来让它在启用优化的情况下发出此代码。 (我假设这是一个简化的 MCVE,因为您实际上永远不需要在运行时计算 log2(1)
。)
问题出在这里:
08000e14: vldr d0, [r7, #16] // <------ X THIS LINE CAUSES THE PROBLEM X
"d0"是一个64位的VFP寄存器。然而,Cortex-M4F 只有 32 位 FPU(寄存器 s0、s1 等)。 Cortex-M7F 具有 64 位 FPU,但这不是您使用的。
所以该指令对于导致故障的Cortex-M4F内核无效。我可以用 GCC 2018-Q4 版本复制它。如果删除 -mfloat-abi=hard
,问题就会消失,因为它随后使用 ARM 内核 CPU 寄存器来传递参数。这就是我推荐的修复方法。
至于根本原因,我还需要再做一些调查。
STMF4 系列不支持双精度浮点数。将 double 更改为 float 以使用单精度浮点数。
为什么这段代码会导致硬故障并跳入死循环?
#include <stdint.h>
#include <math.h>
void myfunc()
{
const double val = 1;
double log_res = log2(val); // <----- THIS CAUSES A FAULT
//double log_res = log2(1); // This works
}
当我用硬编码的 1 替换邪恶行中的 val
时,代码有效。所以只有当我将 val
传递给 log2
时才会出现问题(如代码所示)。为什么会这样?
我正在使用 STM32CubeIDE(基于 eclipse)和 STM32F429ZI MCU。
更新:
检查反汇编中的异常 window 表明这是发生的异常:
fffffff9: Failed to execute MI command: -data-disassemble -s 4294967289 -e 4294967429 -- 3 Error message from debugger back end: Cannot access memory at address 0xfffffffe
有人知道为什么会这样吗?
更新 2:
在汇编指令中调试:
54 const double val = 1;
08000e0a: mov.w r3, #0
08000e0e: ldr r4, [pc, #64] ; (0x8000e50 <myfunc+88>)
08000e10: strd r3, r4, [r7, #16]
55 double log_res = log2(val);
08000e14: vldr d0, [r7, #16] // <------ X THIS LINE CAUSES THE PROBLEM X
08000e18: bl 0x8002a9c <log>
08000e1c: vmov r0, r1, d0
08000e20: add r3, pc, #36 ; (adr r3, 0x8000e48 <myfunc+80>)
08000e22: ldrd r2, r3, [r3]
08000e26: bl 0x800085c <__divdf3>
强调线有d0=0,r7=0x2002ffcc
执行完这一行后,反汇编程序跳转到WWDG_IRQHandler
。
更新 3:
GCC 汇编程序选项(不确定这是做什么的):
-mcpu=cortex-m4 -g3 -c -x assembler-with-cpp --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
GCC 编译器选项:
-mcpu=cortex-m4 -std=gnu11 -g3 -DSTM32F429I_DISC1 -DSTM32 -DSTM32F429ZITx -DSTM32F4 -DDEBUG -DSTM32F429xx -c -I..\Inc -I../Inc/CMSIS/Include -I../Inc/CMSIS/Device/ST/STM32F4xx/Include -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
GCC 链接器选项:
-mcpu=cortex-m4 -T"C:\Users\mne\STM32CubeIDE\workspace_1.0.0\MyUSB\STM32F429ZITX_FLASH.ld" --specs=nosys.specs -Wl,-Map="${ProjName}.map" -Wl,--gc-sections -static --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -Wl,--start-group -lc -lm -Wl,--end-group
更新 4:
问题似乎发生在 math.h
的许多功能上,例如fmin
.
关于您的问题:
当我用硬编码的 1 替换邪恶行中的 val 时,代码有效。所以只有当我将 val 传递给 log2 时才会出现问题(如代码所示)。为什么会这样?
以下声明:
double position = log2(first_set);
没有将变量 val
传递给函数:log2()
我预计这样的编码错误(因为 first_set
未定义)会导致代码无法编译。
您似乎从编译器选项中省略了标志 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
,并且仅将它们包含在汇编器选项中。 (您可以将 -march=armv7-m -mtune=cortex-m4
替换为 -mcpu=cortex-m4
。)因此,您的编译器正在为错误的浮点 ABI 生成代码。您指定了硬 ABI,但正在生成对软库函数的调用而不是内联汇编指令。
编辑: 如果编译器正在生成您的 FPU 不支持的指令,正如 ImageCraft 的 Richard 观察到的那样,您可以尝试将标志更改为 --mcpu=cortex-m4 --mfpu=auto
。
请注意,任何级别的优化,甚至 -O1
,都足以让 GCC 折叠常量并优化对 double position = 0.0;
的调用。您可能需要使用函数 return position;
来让它在启用优化的情况下发出此代码。 (我假设这是一个简化的 MCVE,因为您实际上永远不需要在运行时计算 log2(1)
。)
问题出在这里:
08000e14: vldr d0, [r7, #16] // <------ X THIS LINE CAUSES THE PROBLEM X
"d0"是一个64位的VFP寄存器。然而,Cortex-M4F 只有 32 位 FPU(寄存器 s0、s1 等)。 Cortex-M7F 具有 64 位 FPU,但这不是您使用的。
所以该指令对于导致故障的Cortex-M4F内核无效。我可以用 GCC 2018-Q4 版本复制它。如果删除 -mfloat-abi=hard
,问题就会消失,因为它随后使用 ARM 内核 CPU 寄存器来传递参数。这就是我推荐的修复方法。
至于根本原因,我还需要再做一些调查。
STMF4 系列不支持双精度浮点数。将 double 更改为 float 以使用单精度浮点数。