C++ Arbitrary Precision Fixed-Point Types - 2023.2 English

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 English
Important: The arbitrary precision fixed-point types require the header file ap_fixed.h to be included in the code.

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 by B in the following figure).
  • The type of rounding or quantization (Q) can be selected.
  • The overflow behavior (O and N) can be selected.
Figure 1. Arbitrary Precision Fixed-Point Types
Tip: In the preceding figure the integer value (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;
}