2

When I run my program, which just calculates a sine wave:

for(i = 0; i < ADS1299_SIGNAL_WINDOW; i++){
    TEST[i] = (float32_t)(10.0f * (float32_t)(arm_sin_f32((float32_t)(3.14f * i/ADS1299_SIGNAL_WINDOW))));
}

The compiler generates the following line, which results in a hard fault:

800702a:    ed2d 8b04     vpush    {d8-d9}

What is happening? For reference, here are my flags for the compiler:

SETTINGS="-g -nostartfiles -mthumb -mthumb-interwork -march=armv7e-m -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -fsingle-precision-constant -fdata-sections -ffunction-sections -O3 -Wl,-T,../STM32F407VG_FLASH.ld"
DECLARE="-DARM_MATH_CM4 -D__FPU_PRESENT=1 -D__FPU_USED"
....  -larm_cortexM4lf_math
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SolarSunrise
  • 43
  • 1
  • 5
  • Yup. I forgot to mention that I am using STM32F407 chip. Sorry! – SolarSunrise Nov 24 '14 at 23:27
  • Yes this is bare-metal without any OS. The code above is directly in main. The FPU is enabled in the beginning of main (SCB->CPACR |= (0x3 << 10 * 2 | 0x3 << 11 * 2). – SolarSunrise Nov 24 '14 at 23:35
  • 1
    It would help to include [your complete code](http://stackoverflow.com/help/mcve) in the question - it's going to be hard to tell otherwise what is and isn't happening between power-up and the offending instruction, and I think that's what matters. – Notlikethat Nov 24 '14 at 23:58

3 Answers3

4

The problem is that you're doing both the CPACR enable, and some floating-point operations in the same scope. Because code in main uses floating-point registers, the compiler (being well-behaved and respecting the ABI), will emit code to preserve those registers on entry to main. Before any other code in main executes. Including the write to CAPCR which makes them accessible. Oops.

To avoid that, either enable FP in the CPACR before entry to main in a reset handler (if your toolchain allows), or simply do all FP operations in another function, and ensure main itself doesn't touch any FP registers.

It would also be wise (if you haven't already) to ensure you have a DSB; ISB synchronisation sequence after the CPACR write. Otherwise, you could potentially still get a fault from any stale FP instuctions already in the pipeline.

Notlikethat
  • 20,095
  • 3
  • 40
  • 77
  • Strangely, when I removed the flag: "-fsingle-precision-constant", the problem disappeared. But adding another piece of code caused the problem to reappear again. I'll try turning on the CPACR before main like you've suggested, hopefully that will solve the issue. – SolarSunrise Nov 26 '14 at 10:21
  • @SolarSunrise it's _possible_ that that change made the register allocator happen to pick from the scratch/argument registers `d0`-`d7` instead, meaning it wouldn't then emit the code to preserve them before the enable. Comparing disassemblies might be enlightening, but as you've seen it's not the sort of thing you can rely on. – Notlikethat Nov 26 '14 at 11:33
  • It turned out, according to the disassembly output, the problematic instruction was being called before the FPU initialization routine. Enabling the FPU before the main function solved the problem. Thank you! You've just saved me from another 10 hours of debug... – SolarSunrise Nov 27 '14 at 16:10
  • Also, I have a question, don't the "d0 - d7" registers only exist in cores that has double precision FPU? I thought only "s" and not "d" registers were used in the Cortex M4F core, since it's single-precision. – SolarSunrise Nov 27 '14 at 16:13
  • From the TRM: "The FPU contains 32 single-precision extension registers, which you can also access as 16 doubleword registers for load, store, and move operations." Each `D(n)` register is simply an alias of `S(2n)` and `S(2n+1)` - I imagine the main advantage of still implementing 64-bit transfers on M4F is so you can shift the entire FP register file on or off the stack with a single instruction. – Notlikethat Nov 27 '14 at 18:42
2

I think the problem is that the FPU is not enabled. I've got same problem with Nordic Semiconductors SDK examples on Keil 4. In the Keil IDE the check box for FPU enable is marked, but in the SystemInit code it is conditional compilation like this:

void SystemInit(void) {
#if (__FPU_USED == 1)
    SCB->CPACR |= (3UL << 20) | (3UL << 22);
    __DSB();
    __ISB();
#endif
}

But I think the Keil 4 IDE does not set this __FPU_USED to 1 and on the VPUSH instruction I've got a HardFault because the FPU is not enabled.

I think you need to enable the FPU in SystemInit and the problem then will be solved.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
anonymous
  • 21
  • 1
0

If you use the FPU, the stack should be aligned to 8 bytes boundaries. If you are using an RTOS, check the thread stack initialization code. If you are running on pure bare metal, check the startup code for the stack setup.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Grissiom
  • 11,355
  • 3
  • 18
  • 23