The Vitis libraries targeting AI Engine are currently split into two levels:
- Level 1
- Basic kernels
- Level 2
- Graphs that use multiple Level 1 kernels with parameters specified through templates. The following examples contain PL data movers and host code that uses Level 2 functions in hardware.
This example is created with the DSP Library (DSPLib
)
containing two filters, with the first filter being built with five sections to fulfill the
throughput of the overall system. The system settings are shown below:
#include <adf.h>
#include "fir_sr_sym_graph.hpp"
#include "fir_interpolate_hb_graph.hpp"
// Filter parameters
#define DATA_TYPE cint16
#define COEFF_TYPE int16
#define FIR_LEN_CHAN 151
#define SHIFT_CHAN 15
#define ROUND_MODE_CHAN 0
#define AIES_CHAN 5
#define FIR_LEN_HB 43
#define SHIFT_HB 15
#define ROUND_MODE_HB 0
#define WINDOW_SIZE 1024
// Simulation parameters
#define NUM_ITER 8
The graph instantiating the filter sub-graph is shown below:
using namespace adf;
namespace dsplib = xf::dsp::aie;
class FirGraph: public graph
{
private:
// Channel Filter coefficients
std::vector<int16> chan_taps = std::vector<int16>{
-17, -65, -35, 34, -13, -6, 18, -22,
18, -8, -5, 18, -26, 26, -16, -1,
21, -36, 40, -31, 8, 21, -46, 59,
-53, 26, 13, -54, 81, -83, 56, -6,
-54, 102, -122, 101, -43, -38, 116, -164,
161, -102, 1, 114, -204, 235, -190, 74,
83, -231, 319, -310, 193, 5, -229, 406,
-468, 380, -147, -174, 487, -684, 680, -437,
-10, 553, -1030, 1262, -1103, 474, 596, -1977,
3451, -4759, 5660, 26983};
// HalfBand Filter coefficients
std::vector<int16> hb_taps = std::vector<int16>{
23, -63, 143, -281, 503, -845, 1364, -2173,
3557, -6568, 20729, 32767};
using channel = xf::dsp::aie::fir::sr_sym::
fir_sr_sym_graph<DATA_TYPE, COEFF_TYPE, FIR_LEN_CHAN, SHIFT_CHAN, ROUND_MODE_CHAN, WINDOW_SIZE, AIES_CHAN>;
using halfband = xf::dsp::aie::fir::interpolate_hb::
fir_interpolate_hb_graph<DATA_TYPE, COEFF_TYPE, FIR_LEN_HB, SHIFT_HB, ROUND_MODE_HB, WINDOW_SIZE>;
public:
port<input> in;
port<output> out;
// Constructor - with FIR graph classes initialization
FirGraph(){
channel chan_FIR(chan_taps);
halfband hb_FIR(hb_taps);
// Margin gets automatically added within the FIR graph class.
// Margin equals to FIR length rounded up to nearest multiple of 32 Bytes.
connect(in, chan_FIR.in[0]);
connect(chan_FIR.out[0], hb_FIR.in[0]);
connect(hb_FIR.out[0], out);
};
};
As usual the connections to the external world are specified in another graph to allow for more flexibility in this graph use:
class TopGraph : public graph
{
public:
input_plio in;
output_plio out;
FirGraph F;
TopGraph()
{
in = input_plio::create("128 bits read in",
adf::plio_128_bits,"data/input_128b.txt", 250);
out = output_plio::create("128 bits read out",
adf::plio_128_bits,"data/output_128b.txt", 250);
connect (in.out[0],F.in);
connect (F.out, out.in[0]);
};
};
And, finally the testbench used to launch the simulation is shown below:
TopGraph LibBasedGraph;
int main(void) {
LibBasedGraph.init() ;
LibBasedGraph.run(NUM_ITER) ;
LibBasedGraph.end() ;
return 0 ;
}
A Vitis Analyzer view of this graph is shown below:
As you can see chan_FIR
is built on
five kernels (AIES_CHAN = 5
) which are grouped in
a single graph, and hb_FIR
is based on a single
kernel in its own sub-graph.