C++ classes are fully supported for synthesis with Vitis HLS. The top-level for synthesis must be a function. A class cannot
be the top-level for synthesis. To synthesize a class member function, instantiate the
class itself into function. Do not simply instantiate the top-level class into the test
bench. The following code example shows how class CFir
(defined in the header file discussed next) is instantiated in the top-level function
cpp_FIR
and used to implement an FIR
filter.
#include "cpp_FIR.h"
// Top-level function with class instantiated
data_t cpp_FIR(data_t x)
{
static CFir<coef_t, data_t, acc_t> fir1;
cout << fir1;
return fir1(x);
}
Before examining the class used to implement the design in the C++ FIR Filter
example above, it is worth noting Vitis HLS ignores
the standard output stream cout
during synthesis. When
synthesized, Vitis HLS issues the following warnings:
INFO [SYNCHK-101] Discarding unsynthesizable system call:
'std::ostream::operator<<' (cpp_FIR.h:108)
INFO [SYNCHK-101] Discarding unsynthesizable system call:
'std::ostream::operator<<' (cpp_FIR.h:108)
INFO [SYNCHK-101] Discarding unsynthesizable system call: 'std::operator<<
<std::char_traits<char> >' (cpp_FIR.h:110)
The following code example shows the header file
cpp_FIR.h, including the definition of class CFir
and its associated member functions. In this example
the operator member functions ()
and <<
are overloaded operators, which are respectively
used to execute the main algorithm and used with cout
to format the data for display during C/C++ simulation.
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;
#define N 85
typedef int coef_t;
typedef int data_t;
typedef int acc_t;
// Class CFir definition
template<class coef_T, class data_T, class acc_T>
class CFir {
protected:
static const coef_T c[N];
data_T shift_reg[N-1];
private:
public:
data_T operator()(data_T x);
template<class coef_TT, class data_TT, class acc_TT>
friend ostream&
operator<<(ostream& o, const CFir<coef_TT, data_TT, acc_TT> &f);
};
// Load FIR coefficients
template<class coef_T, class data_T, class acc_T>
const coef_T CFir<coef_T, data_T, acc_T>::c[N] = {
#include "cpp_FIR.h"
};
// FIR main algorithm
template<class coef_T, class data_T, class acc_T>
data_T CFir<coef_T, data_T, acc_T>::operator()(data_T x) {
int i;
acc_t acc = 0;
data_t m;
loop: for (i = N-1; i >= 0; i--) {
if (i == 0) {
m = x;
shift_reg[0] = x;
} else {
m = shift_reg[i-1];
if (i != (N-1))
shift_reg[i] = shift_reg[i - 1];
}
acc += m * c[i];
}
return acc;
}
// Operator for displaying results
template<class coef_T, class data_T, class acc_T>
ostream& operator<<(ostream& o, const CFir<coef_T, data_T, acc_T> &f) {
for (int i = 0; i < (sizeof(f.shift_reg)/sizeof(data_T)); i++) {
o << shift_reg[ << i << ]= << f.shift_reg[i] << endl;
}
o << ______________ << endl;
return o;
}
data_t cpp_FIR(data_t x);
The test bench in the C++ FIR Filter example is shown in the following code
example and demonstrates how top-level function cpp_FIR
is called and validated. This example highlights some of the important attributes of a
good test bench for Vitis HLS synthesis:
- The output results are checked against known good values.
- The test bench returns 0 if the results are confirmed to be correct.
#include "cpp_FIR.h"
int main() {
ofstream result;
data_t output;
int retval=0;
// Open a file to saves the results
result.open(result.dat);
// Apply stimuli, call the top-level function and saves the results
for (int i = 0; i <= 250; i++)
{
output = cpp_FIR(i);
result << setw(10) << i;
result << setw(20) << output;
result << endl;
}
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
return retval;
}
C++ Test Bench for cpp_FIR
To apply directives to objects defined in a class:
- Open the file where the class is defined (typically a header file).
- Apply the directive using the Directives tab.
As with functions, all instances of a class have the same optimizations applied to them.