Vitis HLS Math Library

The Vitis™ HLS Math Library (hls_math.h) provides coverage of math functions from C++ (cmath) libraries, and can be used in both C simulation and synthesis. It offers floating-point (single-precision, double-precision, and half-precision) for all functions and fixed-point support for the majority of the functions. The functions in hls_math.h is grouped in hls namespace, and can be used as in-place replacement of function of std namespace from the standard C++ math library (cmath).

IMPORTANT: Using hls_math.h header in C code is not supported.

HLS Math Library Accuracy

The HLS math functions are implemented as synthesizable bit-approximate functions from the hls_math.h library. Bit-approximate HLS math library functions do not provide the same accuracy as the standard C function. To achieve the desired result, the bit-approximate implementation might use a different underlying algorithm than the standard C math library version. The accuracy of the function is specified in terms of ULP (Unit of Least Precision). This difference in accuracy has implications for both C simulation and C/RTL co-simulation.

The ULP difference is typically in the range of 1-4 ULP.

  • If the standard C math library is used in the C source code, there may be a difference between the C simulation and the C/RTL co-simulation due to the fact that some functions exhibit a ULP difference from the standard C math library.
  • If the HLS math library is used in the C source code, there will be no difference between the C simulation and the C/RTL co-simulation. A C simulation using the HLS math library, may however differ from a C simulation using the standard C math library.

In addition, the following seven functions might show some differences, depending on the C standard used to compile and run the C simulation:

  • copysign
  • fpclassify
  • isinf
  • isfinite
  • isnan
  • isnormal
  • signbit

C90 mode

Only isinf, isnan, and copysign are usually provided by the system header files, and they operate on doubles. In particular, copysign always returns a double result. This might result in unexpected results after synthesis if it must be returned to a float, because a double-to-float conversion block is introduced into the hardware.

C99 mode (-std=c99)

All seven functions are usually provided under the expectation that the system header files will redirect them to __isnan(double) and __isnan(float). The usual GCC header files do not redirect isnormal, but implement it in terms of fpclassify.

C++ Using math.h

All seven are provided by the system header files, and they operate on doubles.

copysign always returns a double result. This might cause unexpected results after synthesis if it must be returned to a float, because a double-to-float conversion block is introduced into the hardware.

C++ Using cmath

Similar to C99 mode(-std=c99), except that:

  • The system header files are usually different.
  • The functions are properly overloaded for:
    • float(). snan(double)
    • isinf(double)

copysign and copysignf are handled as built-ins even when using namespace std;.

C++ Using cmath and namespace std

No issues. Xilinx recommends using the following for best results:

  • -std=c99 for C
  • -fno-builtin for C and C++
Note: To specify the C compile options, such as -std=c99, use the Tcl command add_files with the -cflags option. Alternatively, use the Edit CFLAGs button in the Project Settings dialog box.

HLS Math Library

The following functions are provided in the HLS math library. Each function supports half-precision (type half), single-precision (type float) and double precision (type double).

IMPORTANT: For each function func listed below, there is also an associated half-precision only function named half_func and single-precision only function named funcf provided in the library.

When mixing half-precision, single-precision and double-precision data types, check for common synthesis errors to prevent introducing type-conversion hardware in the final FPGA implementation.

Trigonometric Functions

acos acospi asin asinpi
atan atan2 atan2pi cos
cospi sin sincos sinpi
tan tanpi

Hyperbolic Functions

acosh asinh atanh cosh
sinh tanh

Exponential Functions

exp exp10 exp2 expm1
frexp ldexp modf

Logarithmic Functions

ilogb log log10 log1p

Power Functions

cbrt hypot pow rsqrt
sqrt

Error Functions

erf erfc

Rounding Functions

ceil floor llrint llround
lrint lround nearbyint rint
round trunc

Remainder Functions

fmod remainder remquo

Floating-point

copysign nan nextafter nexttoward

Difference Functions

fdim fmax fmin maxmag
minmag

Other Functions

abs divide fabs fma
fract mad recip

Classification Functions

fpclassify isfinite isinf isnan
isnormal signbit

Comparison Functions

isgreater isgreaterequal isless islessequal
islessgreater isunordered

Relational Functions

all any bitselect isequal
isnotequal isordered select

Fixed-Point Math Functions

Fixed-point implementations are also provided for the following math functions.

All fixed-point math functions support ap_[u]fixed and ap_[u]int data types with following bit-width specification,

  1. ap_fixed<W,I> where I<=33 and W-I<=32
  2. ap_ufixed<W,I> where I<=32 and W-I<=32
  3. ap_int<I> where I<=33
  4. ap_uint<I> where I<=32

Trigonometric Functions

cos sin tan acos asin atan atan2 sincos
cospi sinpi

Hyperbolic Functions

cosh sinh tanh acosh asinh atanh

Exponential Functions

exp frexp modf exp2 expm1

Logarithmic Functions

log log10 ilogb log1p

Power Functions

pow sqrt rsqrt cbrt hypot

Error Functions

erf erfc

Rounding Functions

ceil floor trunc round rint nearbyint

Floating Point

nextafter nexttoward

Difference Functions

erf erfc fdim fmax fmin maxmag minmag

Other Functions

fabs recip abs fract divide

Classification Functions

signbit

Comparison Functions

isgreater isgreaterequal isless islessequal islessgreater

Relational Functions

isequal isnotequal any all bitselect

The fixed-point type provides a slightly-less accurate version of the function value, but a smaller and faster RTL implementation.

The methodology for implementing a math function with a fixed-point data types is:

  1. Determine if a fixed-point implementation is supported.
  2. Update the math functions to use ap_fixed types.
  3. Perform C simulation to validate the design still operates with the required precision. The C simulation is performed using the same bit-accurate types as the RTL implementation.
  4. Synthesize the design.

For example, a fixed-point implementation of the function sin is specified by using fixed-point types with the math function as follows:

#include "hls_math.h"
#include "ap_fixed.h"

ap_fixed<32,2> my_input, my_output;

my_input = 24.675;
my_output = sin(my_input);

When using fixed-point math functions, the result type must have the same width and integer bits as the input.

Verification and Math Functions

If the standard C math library is used in the C source code, the C simulation results and the C/RTL co-simulation results may be different: if any of the math functions in the source code have an ULP difference from the standard C math library it may result in differences when the RTL is simulated.

If the hls_math.h library is used in the C source code, the C simulation and C/RTL co-simulation results are identical. However, the results of C simulation using hls_math.h are not the same as those using the standard C libraries. The hls_math.h library simply ensures the C simulation matches the C/RTL co-simulation results. In both cases, the same RTL implementation is created. The following explains each of the possible options which are used to perform verification when using math functions.

Verification Option 1: Standard Math Library and Verify Differences

In this option, the standard C math libraries are used in the source code. If any of the functions synthesized do have exact accuracy the C/RTL co-simulation is different than the C simulation. The following example highlights this approach.

#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;

typedef float data_t;

data_t cpp_math(data_t angle) {
     data_t s = sinf(angle);
     data_t c = cosf(angle);
     return sqrtf(s*s+c*c);
}

In this case, the results between C simulation and C/RTL co-simulation are different. Keep in mind when comparing the outputs of simulation, any results written from the test bench are written to the working directory where the simulation executes:

  • C simulation: Folder <project>/<solution>/csim/build
  • C/RTL co-simulation: Folder <project>/<solution>/sim/<RTL>

where <project> is the project folder, <solution> is the name of the solution folder and <RTL> is the type of RTL verified (Verilog or VHDL). The following figure shows a typical comparison of the pre-synthesis results file on the left-hand side and the post-synthesis RTL results file on the right-hand side. The output is shown in the third column.

Figure 1: Pre-Synthesis and Post-Synthesis Simulation Differences

The results of pre-synthesis simulation and post-synthesis simulation differ by fractional amounts. You must decide whether these fractional amounts are acceptable in the final RTL implementation.

The recommended flow for handling these differences is using a test bench that checks the results to ensure that they lie within an acceptable error range. This can be accomplished by creating two versions of the same function, one for synthesis and one as a reference version. In this example, only function cpp_math is synthesized.

#include <cmath>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;

typedef float data_t;

data_t cpp_math(data_t angle) {
 data_t s = sinf(angle);
 data_t c = cosf(angle);
 return sqrtf(s*s+c*c);
}

data_t cpp_math_sw(data_t angle) {
 data_t s = sinf(angle);
 data_t c = cosf(angle);
 return sqrtf(s*s+c*c);
}

The test bench to verify the design compares the outputs of both functions to determine the difference, using variable diff in the following example. During C simulation both functions produce identical outputs. During C/RTL co-simulation function cpp_math produces different results and the difference in results are checked.

int main() {
 data_t angle = 0.01;
 data_t output, exp_output, diff;
 int retval=0;

 for (data_t i = 0; i <= 250; i++) {
 output = cpp_math(angle);
 exp_output = cpp_math_sw(angle);

 // Check for differences
 diff = ( (exp_output > output) ? exp_output - output : output - exp_output);
 if (diff > 0.0000005) {
 printf("Difference %.10f exceeds tolerance at angle %.10f \n", diff, angle);
 retval=1;
 }

 angle = angle + .1;
 }

 if (retval != 0) {
 printf("Test failed  !!!\n"); 
 retval=1;
 } else {
 printf("Test passed !\n");
  }
 // Return 0 if the test passes
  return retval;
}

If the margin of difference is lowered to 0.00000005, this test bench highlights the margin of error during C/RTL co-simulation:

Difference 0.0000000596 at angle 1.1100001335
Difference 0.0000000596 at angle 1.2100001574
Difference 0.0000000596 at angle 1.5100002289
Difference 0.0000000596 at angle 1.6100002527
etc..

When using the standard C math libraries (math.h and cmath.h) create a “smart” test bench to verify any differences in accuracy are acceptable.

Verification Option 2: HLS Math Library and Validate Differences

An alternative verification option is to convert the source code to use the HLS math library. With this option, there are no differences between the C simulation and C/RTL co-simulation results. The following example shows how the code above is modified to use the hls_math.h library.

Note: This option is only available in C++.
  • Include the hls_math.h header file.
  • Replace the math functions with the equivalent hls:: function.
    #include <cmath>
    #include "hls_math.h"
    #include <fstream>
    #include <iostream>
    #include <iomanip>
    #include <cstdlib>
    using namespace std;
    
    typedef float data_t;
    
    data_t cpp_math(data_t angle) {
     data_t s = hls::sinf(angle);
     data_t c = hls::cosf(angle);
     return hls::sqrtf(s*s+c*c);
    }

Verification Option 3: HLS Math Library File and Validate Differences

Including the HLS math library file lib_hlsm.cpp as a design file ensures Vitis HLS uses the HLS math library for C simulation. This option is identical to option2 however it does not require the C code to be modified.

The HLS math library file is located in the src directory in the Vitis HLS installation area. Simply copy the file to your local folder and add the file as a standard design file.

Note: This option is only available in C++.

As with option 2, with this option there is now a difference between the C simulation results using the HLS math library file and those previously obtained without adding this file. These difference should be validated with C simulation using a “smart” test bench similar to option 1.

Common Synthesis Errors

The following are common use errors when synthesizing math functions. These are often (but not exclusively) caused by converting C functions to C++ to take advantage of synthesis for math functions.

C++ cmath.h

If the C++ cmath.h header file is used, the floating point functions (for example, sinf and cosf) can be used. These result in 32-bit operations in hardware. The cmath.h header file also overloads the standard functions (for example, sin and cos) so they can be used for float and double types.

C math.h

If the C math.h library is used, the single-precision functions (for example, sinf and cosf) are required to synthesize 32-bit floating point operations. All standard function calls (for example, sin and cos) result in doubles and 64-bit double-precision operations being synthesized.

Cautions

When converting C functions to C++ to take advantage of math.h support, be sure that the new C++ code compiles correctly before synthesizing with Vitis HLS. For example, if sqrtf() is used in the code with math.h, it requires the following code extern added to the C++ code to support it:


#include <math.h>
extern “C” float sqrtf(float);

To avoid unnecessary hardware caused by type conversion, follow the warnings on mixing double and float types discussed in Floats and Doubles.