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};
//FIR Graph class
dsplib::fir::sr_sym::fir_sr_sym_graph<DATA_TYPE, COEFF_TYPE, FIR_LEN_CHAN, SHIFT_CHAN, ROUND_MODE_CHAN, WINDOW_SIZE, AIES_CHAN> chan_FIR;
dsplib::fir::interpolate_hb::fir_interpolate_hb_graph<DATA_TYPE, COEFF_TYPE, FIR_LEN_HB, SHIFT_HB, ROUND_MODE_HB, WINDOW_SIZE> hb_FIR;
public:
port<input> in;
port<output> out;
// Constructor - with FIR graph classes initialization
FirGraph(): chan_FIR(chan_taps), 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);
connect<>(chan_FIR.out, hb_FIR.in);
connect<>(hb_FIR.out, 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.