The GCC compiler for ARM Cortex-M microcontrollers provides built-in support for fixed-point data types through its fixed-point types extension. This allows developers to use fixed-point math in their C/C++ code instead of floating point, which can provide performance and memory benefits on resource-constrained microcontroller systems.
What are fixed-point types?
Fixed-point types represent real numbers using integers and fractional values. For example, the number 1.5 could be stored as 15, with the decimal point implied between the first and second digit. This differs from floating point representations like float and double which store exponents and mantissas.
The advantage of fixed-point math is that it does not require dedicated floating point hardware, which helps minimize silicon area and power consumption on microcontrollers. Operations on fixed-point values can be performed using standard integer instructions. Additionally, fixed-point representations take up less memory than floats.
GCC fixed-point types for ARM Cortex-M
The GCC compiler includes built-in types for 8, 16, 32 and 64 bit fixed-point math. To use them in your C/C++ code, include the stdint.h header which defines the types:
#include <stdint.h>
The primary fixed-point types are:
intFX_t
– signed fixed-point typesuintFX_t
– unsigned fixed-point types
Where FX is the number of integer + fractional bits. For example:
int16_t - 16 bit signed integer
int32_8_t - 32 bit signed fixed-point with 8 fractional bits
uint48_16_t - 48 bit unsigned fixed-point with 16 fractional bits
Using fixed-point math operations
The fixed-point types can be used directly in code and behave like regular integer types. However, values are interpreted as real numbers based on the placement of the implied decimal point. For example:
int16_8_t a = 20; // Represents 20 / 2^8 = 0.3125
int16_8_t b = 30; // Represents 30 / 2^8 = 0.46875
int16_8_t c = a + b; // c = 0.78125
Arithmetic operations like add, subtract, multiply, divide will work as expected. Just be mindful of the decimal place when assigning values and interpreting results.
There are also helper functions defined for tasks like conversion between types, rounding, and determining max/min values:
int32_t a = 12345;
int16_8_t b = int32_to_int16_8(a); // b = 123.40625
int64_32_t c = -9876543;
int32_t d = int64_32_round_to_int32(c); // d = -9876500
This allows seamless integration of fixed and integer types in code. See the gcc documentation for the full list of available functions.
Benefits of fixed-point math on Cortex-M
Here are some of the major benefits of using GCC’s fixed-point types for ARM Cortex-M development:
- No floating point unit (FPU) required – Fixed-point math works on any Cortex-M core without needing dedicated floating point hardware. This saves silicon area and power.
- Faster than software floating point – Integer operations are faster than soft-float emulation of floats. Fixed-point math provides a performance boost for many use cases.
- Smaller code size – Fixed-point code compiles to smaller machine code than floating point equivalents.
- Less RAM usage – Storing fixed-point values takes half the RAM compared to single-precision floats.
- Easier debugging – Viewing fixed-point values in the debugger shows the actual number without having to print the bits.
- Range and precision control – The developer can configure the integer and fractional bits to match their application needs.
For microcontroller projects that need math operations without heavy number crunching, fixed-point provides a nice middle-ground between integers and floats.
Limitations to consider
Fixed-point math has some downsides to keep in mind:
- Limited precision – There are only so many fractional bits available, restricting precision for very small or very large values.
- Chance of overflow – Operations can overflow just like regular integer math, so care must be taken.
- No hardware division – Integer divide instructions are slower than hardware float division units.
- No native support in C – Requires use of GCC fixed-point extension types vs normal floats.
For applications that need high precision or dynamic range, hardware floating point may be a better option. Complex math-heavy code may also benefit from the precision and speed of floating point units.
Configuration options
GCC provides some configuration options to control the behavior of fixed-point math operations:
-ffixed-point - Enable fixed-point types in C/C++ code.
-fno-fixed-point - Disable fixed-point types.
-muint32-fixed32 - Use 32-bit unsigned fixed-point for "int" type.
-muint64-fixed64 - Use 64-bit unsigned fixed-point for "long long" type.
-msaturated-fixed-point - Saturate results instead of overflowing.
-mno-saturated-fixed-point - Disable saturating behavior.
This allows customizing fixed-point math to meet project needs. The defaults work well for most situations.
Putting it all together
Here is an example demonstrating fixed-point math operations on an ARM Cortex-M7 microcontroller:
#include <stdint.h>
// Function to do fixed-point matrix multiplication
void matmul_fxp(const int16_8_t *A, const int16_8_t *B, int16_8_t *C, int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int32_t sum = 0;
for (int k = 0; k < n; k++) {
// 32-bit accumulation to prevent overflow
sum += A[i*n + k] * B[k*n + j];
}
C[i*n + j] = int32_to_int16_8_round(sum);
}
}
}
int main() {
int16_8_t A[9] = {1, 2, 3,
4, 5, 6,
7, 8, 9};
int16_8_t B[9] = {9, 8, 7,
6, 5, 4,
3, 2, 1};
int16_8_t C[9];
// Call fixed-point matrix multiplication
matmul_fxp(A, B, C, 3);
return 0;
}
This performs the matrix math using only fixed-point types. The int32_t accumulator prevents overflow, and the results are rounded to the target 16.8 fixed-point format.
With GCC’s fixed-point extension, developers can build high-performance math-centric applications for ARM Cortex-M without the need for a floating point unit.