ARM VFP Calling convention
Declare an embedded function with floating point parameters
In order to declare an embedded function that takes one or more float/double parameters, it is quite important to understand which ARM ABI is used by the compiler. There are two possible cases: SoftFP ABI and HardFP ABI.
HARDFP ABI
HardFP ABI means that there is full support for the VFP instructions, and floating point parameters of an embedded function are passed to the VFP registers, i.e. s0, s1, ..., sn for float parameters and d0, d1, ..., dn for double parameters.
SOFTFP ABI
SoftFP ABI means that there is full support for the VFP instructions, but floating point parameters of an embedded function are passed to the basic registers (r0, r1, ..., rn) as if those parameters were integers.
LogicLab ARM32/THUMB2 VFP2 compiler function call
LogicLab ARM32/THUMB2 compiler always passes floating point parameters to the basic registers r0, r1, ..., rn, independently of which ARM ABI is being used by the firmware compiler convention.
Firmware definitions of embedded function for LogicLab
In order to invoke such functions inside LogicLab, you should declare and implement these functions (at firmware side) with the macros listed below.
DECLARE_FLOAT_PARAM and DECLARE_DOUBLE_PARAM to declare float and double arguments, respectively.
FLOAT_RET_TYPE and DOUBLE_RET_TYPE to declare float and double results, respectively.
USE_FLOAT_PARAM and USE_DOUBLE_PARAM to use those float and double parameters inside the corpse of the function, respectively. They should be specified only once within the function for each float/double parameter.
RETURN_FLOAT and RETURN_DOUBLE to return the result (to be used inside the corpse of the function).
These macros are defined by the AlPlcRealInterface.h file as follows:
#ifdef ALPLCREAL_UINT32_INTF /* IT'S DEFINED IN CASE OF HARD ABI */ #define DECLARE_FLOAT_PARAM(param) uint32_t __tmp_##param #define USE_FLOAT_PARAM(param) float32_t param = *((float32_t *) &__tmp_##param); #define FLOAT_RET_TYPE uint32_t #define RETURN_FLOAT(param) { float32_t __tmp_return = param; return *((uint32_t *) &__tmp_return); } #define DECLARE_DOUBLE_PARAM(param) uint64_t __tmp_##param #define USE_DOUBLE_PARAM(param) float64_t param = *((float64_t *) &__tmp_##param); #define DOUBLE_RET_TYPE uint64_t #define RETURN_DOUBLE(param) { float64_t __tmp_return = param; return *((uint64_t *) &__tmp_return); } #else #define DECLARE_FLOAT_PARAM(param) ALPLCREAL_TYPE param #define USE_FLOAT_PARAM(param) #define FLOAT_RET_TYPE ALPLCREAL_TYPE #define RETURN_FLOAT(param) return param; #define DECLARE_DOUBLE_PARAM(param) float64_t param #define USE_DOUBLE_PARAM(param) #define DOUBLE_RET_TYPE float64_t #define RETURN_DOUBLE(param) return param; #endif
For example the following embedded function:
float32_t function_name(float32_t a, float32_t b) { return a + b; }
Implemented with the following syntax:
FLOAT_RET_TYPE function_name( DECLARE_FLOAT_PARAM(a), DECLARE_FLOAT_PARAM(b) ) { // Macros to declare a and b as float32_t variables USE_FLOAT_PARAM(a); USE_FLOAT_PARAM(b); // Standard C variable declaration and subsequent usage float32_t c; c = a + b; // Return a floating point value RETURN_FLOAT(c); }
will work both on HardFP ABI and SoftFP ABI compilers