C++ functions can take advantage of the arbitrary precision fixed-point types included with Vitis HLS. The following figure summarizes the basic features of these fixed-point types:
- The word can be signed (
ap_fixed
) or unsigned (ap_ufixed
). - A word with of any arbitrary size
W
can be defined. - The number of places above the decimal point I, also defines the number
of decimal places in the word,
W-I
(represented byB
in the following figure). - The type of rounding or quantization (
Q
) can be selected. - The overflow behavior (
O
andN
) can be selected.
I
) specifies the number of integer bits to the left
of the binary point, including the sign bit. Arbitrary precision fixed-point types use more memory during C simulation
and if you are using very large arrays of ap_[u]fixed
types.
The advantages of using fixed-point types are:
- They allow fractional number to be easily represented.
- When variables have a different number of integer and decimal place bits, the alignment of the decimal point is handled.
- There are numerous options to handle how rounding should happen: when there are too few decimal bits to represent the precision of the result.
- There are numerous options to handle how variables should overflow: when the result is greater than the number of integer bits can represent.
These attributes are summarized by examining the code in the example below.
First, the header file ap_fixed.h is included. The
ap_fixed
types are then defined using the typedef
statement:
- A 10-bit input: 8-bit integer value with 2 decimal places.
- A 6-bit input: 3-bit integer value with 3 decimal places.
- A 22-bit variable for the accumulation: 17-bit integer value with 5 decimal places.
- A 36-bit variable for the result: 30-bit integer value with 6 decimal places.
The function contains no code to manage the alignment of the decimal point after operations are performed. The alignment is done automatically.
The following code sample shows ap_fixed
type.
#include "ap_fixed.h"
typedef ap_ufixed<10,8, AP_RND, AP_SAT> din1_t;
typedef ap_fixed<6,3, AP_RND, AP_WRAP> din2_t;
typedef ap_fixed<22,17, AP_TRN, AP_SAT> dint_t;
typedef ap_fixed<36,30> dout_t;
dout_t cpp_ap_fixed(din1_t d_in1, din2_t d_in2) {
static dint_t sum;
sum += d_in1;
return sum * d_in2;
}
Using ap_(u)fixed
types, the C++
simulation is bit accurate. Fast simulation can validate the algorithm and its accuracy.
After synthesis, the RTL exhibits the identical bit-accurate behavior.
Arbitrary precision fixed-point types can be freely assigned literal values
in the code. This is shown in the test bench (see the example below) used with the example
above, in which the values of in1
and in2
are declared and assigned constant values.
When assigning literal values involving operators, the literal values must
first be cast to ap_(u)fixed
types. Otherwise, the C
compiler and Vitis HLS interpret the literal as an
integer or float/double
type and might fail to find a
suitable operator. As shown in the following example, in the assignment of in1 = in1 + din1_t(0.25)
, the literal 0.25 is cast to an ap_fixed
type.
#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;
#include "ap_fixed.h"
typedef ap_ufixed<10,8, AP_RND, AP_SAT> din1_t;
typedef ap_fixed<6,3, AP_RND, AP_WRAP> din2_t;
typedef ap_fixed<22,17, AP_TRN, AP_SAT> dint_t;
typedef ap_fixed<36,30> dout_t;
dout_t cpp_ap_fixed(din1_t d_in1, din2_t d_in2);
int main()
{
ofstream result;
din1_t in1 = 0.25;
din2_t in2 = 2.125;
dout_t output;
int retval=0;
result.open(result.dat);
// Persistent manipulators
result << right << fixed << setbase(10) << setprecision(15);
for (int i = 0; i <= 250; i++)
{
output = cpp_ap_fixed(in1,in2);
result << setw(10) << i;
result << setw(20) << in1;
result << setw(20) << in2;
result << setw(20) << output;
result << endl;
in1 = in1 + din1_t(0.25);
in2 = in2 - din2_t(0.125);
}
result.close();
// Compare the results file with the golden results
retval = system(diff --brief -w result.dat result.golden.dat);
if (retval != 0) {
printf(Test failed !!!\n);
retval=1;
} else {
printf(Test passed !\n);
}
// Return 0 if the test passes
return retval;
}