When compiling code for the ARM Cortex-M3 processor, you may encounter an error like “uses VFP register arguments, target CPU does not support ARM mode” or “Target does not support Thumb double precision hardfloat ABI”. This error occurs because the Cortex-M3 does not support passing floating point arguments directly in VFP registers like some other ARM processors. However, there are ways to work around this limitation and still utilize floating point code on the Cortex-M3.
The Issue
The Cortex-M3 CPU implements the Thumb-2 instruction set and only supports the Thumb interworking and Thumb2 instruction sets. It does not support ARM or ThumbEE instructions. The Thumb-2 instruction set does not include instructions for passing arguments directly in VFP registers for floating point values. Instead, the Cortex-M3 follows the standard ARM EABI conventions for passing floats and doubles in integer registers or on the stack.
This means if you have any floating point arguments in a function declaration, the compiler will try to pass them directly in VFP registers instead of integer registers. Since the Cortex-M3 does not support this, you get an error. For example: void foo(float arg1, double arg2);
Here the compiler will try to pass arg1 in s0 and arg2 in d0/d1 VFP registers, which the Cortex-M3 does not support.
Solutions
There are a few possible solutions to resolve this VFP register argument error when compiling for Cortex-M3:
1. Disable hardware floating point
One option is to disable hardware floating point support entirely in your compiler/linker settings. Without hardware floating point support enabled, the compiler will instead pass floats and doubles through integer registers like r0-r3 or on the stack according to the EABI conventions. This may reduce performance but will compile without errors.
2. Use soft-float ABI
You can specify the “-mfloat-abi=soft” option in GCC/Clang to use the soft-float ABI. This tells the compiler to pass floats in integer registers instead of trying to use VFP registers. The soft-float ABI works but loses some performance compared to hardware floating point.
3. Use hard-float ABI with softfp functions
This is the recommended approach to get good floating point performance on Cortex-M3. You can enable the hard-float ABI but compile individual functions with -mfloat-abi=softfp to pass floats in integer registers. This gives you hardware floating point when possible but falls back to soft-float ABI when needed.
For example: // Compiler settings: -mfloat-abi=hard -mfpu=fpv4-sp-d16 // Function definitions void foo(float f) __attribute__ ((pcs(“aapcs-vfp”))) { // … } void bar(float f) __attribute__ ((pcs(“aapcs”)) { // … }
Here foo() will use the hard-float ABI while bar() uses the soft-float ABI. bar() will work on Cortex-M3 while foo() will work on other ARM chips with full VFP support.
4. Use floating point emulation
Some toolchains like DS-5 and Arm Compiler 6 support software floating point emulation when compiling for Cortex-M3. This emulates VFP instructions using integer operations. It works but is slower than hardware floating point on supported chips.
Detailed Explanation
To understand why the “uses VFP register arguments” error occurs, we need to dive deeper into how the ARM EABI conventions handle floating point arguments and return values.
ARM EABI Conventions
The ARM EABI specifications define how functions arguments and return values are passed in registers or on the stack. There are two main variants supported by the EABI:
- Soft-float ABI: Floating point args are passed in integer registers
- Hard-float ABI: Floating point args are passed directly in VFP registers
The compiler follows these conventions to know where to put arguments when calling a function and where to expect arguments when defining a function.
Soft-float ABI
With the soft-float ABI, floats are passed in integer registers like r0-r3 while doubles use two registers. For example: void func(float f, double d);
This would pass f in r0 and d in r1:r2. No VFP registers are used.
Hard-float ABI
The hard-float ABI passes floats and doubles directly in VFP registers. For example: void func(float f, double d);
This would pass f in s0 and d in d0/d1 VFP registers instead of integer registers.
Cortex-M3 Limitations
The Cortex-M3 CPU implements the Thumb-2 instruction set, which does not include instructions for accessing the VFP registers directly. Therefore, it does not support the hard-float ABI for passing arguments directly in s0-s15 and d0-d7 registers.
Instead, the Cortex-M3 follows the soft-float ABI convention of passing floats in integer registers r0-r3 and doubles in r0:r1 or r2:r3 register pairs. When compiling for the Cortex-M3, the compiler needs to follow the soft-float ABI and avoid trying to pass floats in VFP registers.
Causing the VFP Register Error
The “uses VFP register arguments” error happens when you enable hardware floating point but the compiler tries to use the hard-float ABI with VFP registers instead of the soft-float ABI. This is an invalid combination on Cortex-M3.
For example, enabling “-mfpu=fpv4-sp-d16” tells the compiler that full VFPv4 hardware floating point is available. So it assumes it can use the hard-float ABI and pass floats directly in VFP registers. But on Cortex-M3 this results in an invalid instruction error when it tries to pass floats in s0-s15 registers that don’t exist.
Detailed Solutions
Now that we understand the issue, here are the detailed solutions again:
1. Disable hardware floating point
Passing the -mfloat-abi=soft compiler option disables hardware floating point support entirely. This forces the compiler to use the soft-float ABI convention of passing floats and doubles in integer registers and on the stack.
While this works, it disables hardware floating point support and loses any performance benefit. Code will run but all floating point is done in software instead.
2. Use soft-float ABI
Specifying -mfloat-abi=soft tells the compiler to always use the soft-float ABI, even with hardware floating point enabled. This passes all arguments in integer registers but allows the rest of the floating point code to still use the hardware. arm-none-eabi-gcc -mcpu=cortex-m3 -mfpu=fpv4-sp-d16 -mfloat-abi=soft
This gives you hardware floating point performance while avoiding the VFP argument error on Cortex-M3.
3. Hard-float ABI with softfp functions
This approach allows mixing both hard-float and soft-float ABIs in one program. The compiler uses hard-float ABI by default for hardware floating point support. But individual functions can be marked soft-float to pass args in integer registers when needed.
For example, compiling with: arm-none-eabi-gcc -mcpu=cortex-m3 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
Tells the compiler to use hard-float ABI. Then marking specific functions soft-float: void foo(float f) __attribute__ ((pcs(“aapcs-vfp”))) { // Uses hard-float ABI } void bar(float f) __attribute__ ((pcs(“aapcs”)) { // Uses soft-float ABI }
This gives the best of both – optimized floating point code along with compatibility with Cortex-M3 limitations.
4. Use floating point emulation
Some toolchains allow compiling Cortex-M3 with full software floating point emulation. This emulates the VFP instructions using integer operations when compiling.
For example, with DS-5: armcc -mcpu=cortex-m3 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee
The -mfp16-format=ieee setting enables the floating point emulation. This is slower but can compile hard-float code for Cortex-M3.
Other Considerations
There are a few other things to keep in mind when working with floating point code on Cortex-M3:
- Try to isolate floating point code in separate source files or libraries. This makes it easier to apply the __attribute__ tags to selectively use soft-float ABI.
- Mark any interrupt handlers or context switch code as soft-float to avoid saving VFP registers.
- Some RTOSes like FreeRTOS provide hooks for switching between hard-float and soft-float modes in different threads.
- Test code thoroughly after making ABI changes to ensure proper argument passing.
- Using hard-float ABI can result in smaller code size since float arguments don’t take integer register space.
Conclusion
The “uses VFP register arguments” error occurs due to a mismatch between the hard-float ABI assumptions and the Cortex-M3 limitations. By either disabling hardware floating point entirely, forcing soft-float ABI, or selectively marking soft-float functions, we can work around this issue and utilize floating point on Cortex-M3. Isolating floating point code and testing thoroughly for ABI issues is recommended when mixing hard-float and soft-float code.
Understanding the ARM EABI conventions for passing arguments in registers and how the compiler interacts with the CPU is key to resolving these kinds of ABI issues with Cortex-M3 and other ARM processors. Following the recommendations outlined in this article will help you properly utilize floating point capabilities in your Cortex-M3 programs.