Mohammed Noureldin
Mohammed Noureldin

Reputation: 16976

log2 causes a fault in STM32F4 MCU in C

Why does this code cause hard fault and jumps into infinite loop?

#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
}

When I replace val in the evil line by a hard-coded 1, the code works. So the problem happens only when I pass val to log2 (as shown in code). Why is this happening?

I am using STM32CubeIDE (eclipse based), with STM32F429ZI MCU.

UPDATE:

Checking the exception in the disassembly window showed that this is the exception happened:

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

Any one has idea why is this happening?

Update 2:

Debugging in the assembly instructions:

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>

The emphisized line has d0=0, r7=0x2002ffcc

After executing this line, the disassembler jumps to WWDG_IRQHandler.

Update 3:

GCC Assembler options (not sure what does this do):

-mcpu=cortex-m4 -g3 -c -x assembler-with-cpp --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb

GCC compiler options:

-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 linker options:

-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

Udpate 4:

The problem seems to happend with many functions of math.h, e.g. fmin.

Upvotes: 2

Views: 878

Answers (4)

Richard at ImageCraft
Richard at ImageCraft

Reputation: 665

The problem is here:

08000e14: vldr d0, [r7, #16] // <------ X THIS LINE CAUSES THE PROBLEM X

"d0" is a 64-bit VFP register. However, Cortex-M4F only has 32-bit FPU (registers s0, s1, etc.). Cortex-M7F has 64-bit FPU but that's not what you are using.

So the instruction is invalid for the Cortex-M4F core which causes the fault. I could duplicate this with GCC 2018-Q4 release. The problem will go away if you remove the -mfloat-abi=hard, as it then uses the ARM core CPU registers to pass the argument. So that's the fix I'd recommend.

As for the root cause, I will need to do some more investigations.

Upvotes: 2

Dan Green
Dan Green

Reputation: 51

The STMF4 series does not support double precision floats. Change the double to float to use single precision floats.

Upvotes: 0

Davislor
Davislor

Reputation: 15164

You appear to be omitting the flags -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb from your compiler options, and including them only in your assembler options. (You might substitute -march=armv7-m -mtune=cortex-m4 for -mcpu=cortex-m4.) Therefore, your compiler is generating code for the wrong floating-point ABI. You specified the hard ABI, but are generating a call to the soft library function rather than inline assembly instructions.

Edit: If the compiler is generating instructions your FPU does not support, as Richard at ImageCraft observes, you might try changing the flags to --mcpu=cortex-m4 --mfpu=auto.

Note that any level of optimizaiton, even -O1, is enough for GCC to fold the constant and optimize the call to double position = 0.0;. You might need to have the function return position; to get it to emit this code with optimizations on. (I assume this is a simplified MCVE, as you would never actually need to compute log2(1) at runtime.)

Upvotes: 3

user3629249
user3629249

Reputation: 16550

regarding your question:

When I replace val in the evil line by a hard-coded 1, the code works. So the problem happens only when I pass val to log2 (as shown in code). Why is this happening?

The following statement:

double position = log2(first_set);

is NOT passing the variable val to the function: log2()

I would expect such a coding error (since first_set is not defined) to result in the code not compiling.

Upvotes: 0

Related Questions