# 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`

).

## 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++

`-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`

).

`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,

`ap_fixed<W,I>`

where I<=33 and W-I<=32`ap_ufixed<W,I>`

where I<=32 and W-I<=32`ap_int<I>`

where I<=33`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:

- Determine if a fixed-point implementation is supported.
- Update the math functions to use
`ap_fixed`

types. - 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.
- 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.

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.

- 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.

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.