ARM VFP Calling convention

From Axel Public Wiki
Jump to navigation Jump to search

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