template <int NK>
class MultiKernelGraph: public adf::graph {
private:
adf::kernel k[NK];
public:
adf::port<input> din;
adf::port<output> dout;
MultiKernelGraph() {
for(int i=0;i<NK;i++)
{
k[i] = adf::kernel::create(passthrough);
adf::source(k[i]) = "kernels.cpp";
adf::runtime<ratio>(k[i]) = 0.9;
}
adf::connect(din, k[0].in[0]);
for(int i=0;i<NK-1;i++)
adf::connect(k[i].out[0], k[i+1].in[0]);
adf::connect(k[NK-1].out[0], dout);
};
};
class TestGraphMulti: public adf::graph {
public:
adf::input_plio plin;
adf::output_plio plout;
MultiKernelGraph<3> Multi;
TestGraphMulti()
{
plin = adf::input_plio::create("input2",adf::plio_64_bits,"data/Input_64.txt",500);
adf::connect(plin.out[0],Multi.din);
plout = adf::output_plio::create("output2",adf::plio_64_bits,"data/Output2.txt",500);
adf::connect(Multi.dout,plout.in[0]);
};
};
As seen earlier, a graph can contain kernels and sub-graphs. The test bench .cpp code can instantiate multiple graphs and run them independently.
TestGraphSimple Simple_UnitTest;
TestGraphMulti Multi_UnitTest;
int main(int argc, char ** argv) {
Simple_UnitTest.init();
Multi_UnitTest.init();
Simple_UnitTest.run(NFRAMES*NITERATIONS);
Multi_UnitTest.run(NFRAMES*NITERATIONS);
Simple_UnitTest.end();
Multi_UnitTest.end();
return 0;
}
The Vitis IDE displays two independent graphs on the graph view.
These two graphs can also be instantiated in a third graph that is instantiated in the test bench:
class TestGraph: public adf::graph {
public:
adf::input_plio plin1,plin2;
adf::output_plio plout1,plout2;
SimplestGraph Simple;
MultiKernelGraph Multi;
TestGraph()
{
plin1 = adf::input_plio::create("input1",adf::plio_64_bits,"data/Input_64.txt",500);
adf::connect(plin1.out[0],Simple.din);
plout1 = adf::output_plio::create("output1",adf::plio_64_bits,"data/Output1.txt",500);
adf::connect(Simple.dout,plout1.in[0]);
plin2 = adf::input_plio::create("input2",adf::plio_64_bits,"data/Input_64.txt",500);
adf::connect(plin2.out[0],Multi.din);
plout2 = adf::output_plio::create("output2",adf::plio_64_bits,"data/Output2.txt",500);
adf::connect(Multi.dout,plout2.in[0]);
};
};
TestGraph UnitTest;
int main(int argc, char ** argv) {
UnitTest.init();
UnitTest.run(NFRAMES*NITERATIONS);
UnitTest.end();
return 0;
}
Much more complex graphs can be created including sequential and parallel kernels, and graphs where the data flow is split and merged multiple times throughout the graph, using buffers and streams to transfer data through the AI Engine array.
VeryComplexGraph() {
// Declare Kernels
for(int i=0;i<2;i++)
{
simple[i] = adf::kernel::create(passthrough);
adf::source(simple[i]) = "kernels.cpp";
adf::runtime<ratio>(simple[i]) = 0.9;
}
// Connections
// Inputs
connect(din[0],split[0].din);
connect(din[1],split[1].din);
// Outputs
connect(merge[2].dout,dout[0]);
connect(split[3].dout[0],dout[1]);
connect(split[3].dout[1],dout[2]);
// Internal connections
connect(split[0].dout[0],multi3.din);
connect(multi3.dout,merge[2].din[0]);
connect(split[0].dout[1],merge[0].din[0]);
connect(split[1].dout[0],merge[0].din[1]);
connect(merge[0].dout,simple[0].in[0]);
connect(simple[0].out[0],split[2].din);
connect(split[2].dout[0],multi2[1].din);
connect(multi2[1].dout,merge[2].din[1]);
connect(split[2].dout[1],simple[1].in[0]);
connect(simple[1].out[0],merge[1].din[0]);
connect(split[1].dout[1],multi2[0].din);
connect(multi2[0].dout,merge[1].din[1]);
connect(merge[1].dout,split[3].din);
};
In this graph view, a complex dataflow diverges and converges in multiple points. If a part of the dataflow needs to go in and out of the PL, you will see the corresponding output ports and input ports that you connect to your PL kernels during the link phase.