Fixedpoint library
Fixedpoint data types represent fractional numbers as integer values with a fixed number of bits used for the integer part of the value and a fixed number of bits used for the fractional part. Fixed point data types can have an arbitrary number of total bits and are distinguished between signed and unsigned data. This software module only supports a total bit number of 32. The fixedpoint library provides a data type definition for the PS to ease the handling of them.
This software module is not intended to do fixedpoint math on the processor and does not provide functions for it!
It is only intended to be used in the lowest software layer of IPCore drivers to read/write to and from the PL (i.e., functions that are named with _hw
).
The aim is to provide a clean way to interact with IPCores that use fixedpoint data representation.
Thus, all functions accept float
values as their inputs and return float
values, all fixed point handling is done internally.
The read and write functions check against boundaries of the fixedpoint data type  the data itself is always a 32bit integer internally on the PS, which is not exposed to the user.
The 32bit limitation is due to the AXI data width of 32bit.
Data is passed and received from the software module as
float
valuesData is stored as 32bit integers on the processor internally (signed/unsigned).
The integer is divided into fractional and integer bits
The first \(N\)bits are the fractional bits depending on the representation
The following \(M\)bits are the integer bits depending on the representation
The fixedpoint data is \(K=N+M\) bits wide with \(K \leq 32\)
Fig. 71 showcases the splitup of a 32bit integer variable. Note that this is just a mental model for the user, neither the processor nor the compiler do know about the split up.
Scaling and precision
The represented fixedpoint number is scaled by a fixed scaling factor \(s\). The software assumes binary scaling, that is, base 2 is used for scaling the fixed point value. The scaling factor \(s\) is determined by the number of fractional bits \(N\):
A floatingpoint value \(x_f\) is converted to a fixed point number (stored integer) \(x_i\) by:
The inverse conversion from the stored integer \(x_i\) to the floatingpoint representation \(x_f\) is done by:
Please note that there are conflicting definitions of fixedpoint data types. The fixed point data type used by Vitis HLS and Matlab HDLCoder is defined as two’s complement. The definition outlined of the software module is therefore consistent with Vitis HLS and Matlab HDLCoder. See the following information for more details:
https://www.xilinx.com/html_docs/xilinx2021_1/vitis_doc/use_arbitrary_precision_data_type.html
https://de.mathworks.com/help/simulink/ug/fixedpointnumbers.html
https://de.mathworks.com/help/dsp/ug/conceptsandterminology.html
https://de.mathworks.com/help/simulink/ug/scalingprecisionrangequantization.html
Unsigned fixed point data
For unsigned fixedpoint data, the smallest and largest values that the data type can represent are calculated by:
Example: unsigned fixed point with 16 bits of which \(N=5\) bits are used for the fraction and \(M=165=11\) bits are used for the integer part. The smallest representable value of unsigned fixedpoint data type is zero (\(min=0\)). The largest number is:
Signed fixedpoint data
A signed fixedpoint data with \(M\) integer bits and \(N\) fractional bits can represent integers from:
Example: signed fixed point with 16 bits of which \(N=5\) bits are used for the fraction and \(M=165=11\) bits are used for the integer part. The precision of the data type is equal to the inverse of the scaling \(s^{1}=2^{N}=2^{5}=0.03125\). The smallest and largest representable numbers for this data type are:
Rounding
Since all input and output functions of this software module use singleprecision floatingpoint values (i.e., float
), the values have to be rounded to be represented in fixedpoint data type.
Note that floatingpoint values also introduce rounding errors and can not represent values exactly.
See the following for details:
Given the limited precision of the fixedpoint data type (determined by the number of fractional bits \(N\)), the floatingpoint value is rounded when converting to fixedpoint precision. The following rounding methods are possible:
Round to nearest integer (roundf, https://en.cppreference.com/w/c/numeric/math/round)
Round down (floorf, https://en.cppreference.com/w/c/numeric/math/floor)
Round up (ceilf, https://en.cppreference.com/w/c/numeric/math/ceil)
Round towards zero (truncf, https://en.cppreference.com/w/c/numeric/math/trunc)
The software module always rounds towards the nearest integer!
Conversion
Converting the floatingpoint value of \(x_f=2.9\) to a signed fixedpoint data type with \(M=14\) bits for the integer part and \(N=2\) bits for the fraction yields the scaling factor \(s=2^{2}=4\). Note that the different rounding modes are shown here to highlight their importance and to keep in mind that round to nearest integer is used by the software module.
The stored integer is calculated by:
The result is rounded by a rounding function:
ceil: \(x_i=12\) (\(x_f=3.0\))
floor: \(x_i=11\) (\(x_f=2.75\))
round: \(x_i=12\) (\(x_f=3.0\))
trunc: \(x_i=11\) (\(x_f=2.75\))
To convert back to a floatingpoint value, the stored integer \(x_i\) is multiplied by the inverse scaling factor:
Note how the rounding method determines if the error is \(0.1\) or \(0.15\).
Examples
Write
Write a value that is a float
in the processor to an IPCore that expects signed fixedpoint data with 3 integer and 4 fraction bits.
#include "uz_fixedpoint.h"
#define TEST_ADDRESS 0x00F
struct uz_fixedpoint_definition_t def={
.is_signed=true,
.fractional_bits=4,
.integer_bits=3
};
float write_value=1.0f;
uz_fixedpoint_axi_write(TEST_ADDRESS,write_value,def);
Read
Read a value from an IPCore that is an unsigned fixedpoint with 10 integer bits and 2 fractional bits and pass it to the processor as a float
.
#include "uz_fixedpoint.h"
#define TEST_ADDRESS 0x00F
struct uz_fixedpoint_definition_t def={
.is_signed=false,
.fractional_bits=2,
.integer_bits=10
};
float data=uz_fixedpoint_axi_read(TEST_ADDRESS,def);
Reference

struct uz_fixedpoint_definition_t
Configuration struct for the fixed point data type.

float uz_fixedpoint_axi_read(uint32_t memory_address, struct uz_fixedpoint_definition_t fixedpoint_definition)
Reads a fixedpoint data type from AXI and converts it to float given the fixedpoint definition.
 Parameters
memory_address – Address of the AXI register
fixedpoint_definition – Definition of fixedpoint data type
 Returns
float

void uz_fixedpoint_axi_write(uint32_t memory_address, float data, struct uz_fixedpoint_definition_t fixedpoint_definition)
Converts the input data to a fixedpoint data type, rounds to nearest integer, and writes it to AXI.
 Parameters
memory_address – Address of the AXI register
data – Data that is written to AXI
fixedpoint_definition – Definition of fixedpoint data type

void uz_fixedpoint_check_limits(float data, struct uz_fixedpoint_definition_t fixedpoint_definition)
Checks that the data is within the min/max that is representable by the fixedpoint data type.
 Parameters
data – Data that is checked
fixedpoint_definition – Definition of fixedpoint data type

float uz_fixedpoint_get_precision(struct uz_fixedpoint_definition_t input)
Calculates the precision of the specified data type.
 Parameters
input – Definition of fixedpoint data type
 Returns
float

float uz_fixedpoint_get_max_representable_value(struct uz_fixedpoint_definition_t input)
Calculates the biggest representable value of the given data type definition.
 Parameters
input – Definition of fixedpoint data type
 Returns
float

float uz_fixedpoint_get_min_representable_value(struct uz_fixedpoint_definition_t input)
Calculates the smallest representable valueof the given data type definition.
 Parameters
input – Definition of fixedpoint data type
 Returns
float