text: /*
   * (c) Copyright 2020 Xilinx, Inc. All rights reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   */
   #pragma once
  
  #include <adf.h>
  #include "system_settings.h"
  #include "aie_kernels.h"
  
  
  std::vector<cint16> taps = std::vector<cint16>({
  	{   -82,  -253},{     0,  -204},{    11,   -35},{  -198,   273},
  	{  -642,   467},{ -1026,   333},{  -927,     0},{  -226,   -73},
  	{   643,   467},{   984,  1355},{   550,  1691},{     0,   647},
  	{   538, -1656},{  2860, -3936},{  6313, -4587},{  9113, -2961},
  	{  9582,     0},{  7421,  2411},{  3936,  2860},{  1023,  1409},
  	{  -200,  -615},{     0, -1778},{   517, -1592},{   467,  -643},
  	{  -192,   140},{  -882,   287},{ -1079,     0},{  -755,  -245},
  	{  -273,  -198},{    22,    30},{    63,   194},{     0,   266}
  });
  
  	#define PHASE(N,NP) {taps[N],taps[N+NP],taps[N+2*NP],taps[N+3*NP]}
  
  	std::vector<cint16> taps8p[8] = { PHASE(0,8), PHASE(1,8), PHASE(2,8), PHASE(3,8),
  		PHASE(4,8), PHASE(5,8), PHASE(6,8), PHASE(7,8) };
  
  		const int SHIFT = 0; // to analyze the output generated by impulses at the input
  		//const int SHIFT = 15; // for realistic input samples
  
  
  		using namespace adf;
  
  
  		class FIRGraph_SSR8: public adf::graph
  		{
  		private:
  			kernel k[8][8];
  
  		public:
  			input_port in[32]; // 8 columns, 2 streams per kernel, x2 for the row direction
  			output_port out[16]; // 8 columns, 2 streams per kernel
  
  			FIRGraph_SSR8()
  			{
  				// k[N][0] is always the first in the cascade stream
  				// Topology of the TopGraph
  				//
  				//      7,7   ...   ...   7,0 <--
  				//  --> 6,0   ...   ...   6,7
  				//       .     .     .     .
  				//       .     .     .     .
  				//       .     .     .     .
  				//      1,7   ...   ...   1,0 <--
  				//  --> 0,0   ...   ...   0,7
  
  				const int NPhases = 8;
  
  				for(int row = 0;row<NPhases; row++)
  				for(int col=0;col<NPhases; col++)
  				{
  					int col1 = (row%2?NPhases-col-1:col); // Revert col order on odd rows
  
  					// Which Phase in (row,col) ?
  					int PhaseSelect = col-row;
  					if(PhaseSelect<0) PhaseSelect += NPhases;
  					int DiscardSample = (row>col?1:0); // Must discard 1 sample on some aie_kernels
  
  					if(DiscardSample == 1) // Don't know how to make DiscardSample a constant suitable for a template parameter
  					{
  						// kernel instanciation
  						if(col1==0)
  						{
  							k[row][col1] = kernel::create_object<DoubleStream::FIR_MultiKernel_cout<NUM_SAMPLES,SHIFT,true,false>>(taps8p[PhaseSelect]);
  						}
  						else
  						{
  							if(col1==NPhases-1)
  							{
  								k[row][col1] = kernel::create_object<DoubleStream::FIR_MultiKernel_cin<NUM_SAMPLES,SHIFT,true,false>>(taps8p[PhaseSelect]);
  							}
  							else
  							{
  								k[row][col1] = kernel::create_object<DoubleStream::FIR_MultiKernel_cincout<NUM_SAMPLES,SHIFT,true,false>>(taps8p[PhaseSelect]);
  							}
  						}
  					}
  					else
  					{
  						// kernel instanciation
  						if(col1==0)
  						{
  							k[row][col1] = kernel::create_object<DoubleStream::FIR_MultiKernel_cout<NUM_SAMPLES,SHIFT,false,false>>(taps8p[PhaseSelect]);
  						}
  						else
  						{
  							if(col1==NPhases-1)
  							{
  								k[row][col1] = kernel::create_object<DoubleStream::FIR_MultiKernel_cin<NUM_SAMPLES,SHIFT,false,false>>(taps8p[PhaseSelect]);
  							}
  							else
  							{
  								k[row][col1] = kernel::create_object<DoubleStream::FIR_MultiKernel_cincout<NUM_SAMPLES,SHIFT,false,false>>(taps8p[PhaseSelect]);
  							}
  						}
  					}
  				}
  
  				for(int i=0;i<NPhases;i++)
  				for(int j=0;j<NPhases;j++)
  				{
  					runtime<ratio>(k[i][j]) = 0.9;
  					source(k[i][j]) = "aie_kernels/FirDoubleStream.cpp";
  					headers(k[i][j]) = {"aie_kernels/FirDoubleStream.h"};
  				}
  
  				// Constraints: location of the first kernel in the cascade
  				for(int i=0;i<NPhases;i++)
  				{
  					int j = (i%2?30:23); // 23 on even rows and 230on odd rows
  					location<kernel>(k[i][0]) = tile(j,i);
  				}
  
  
  				// Cascade Connections
  				for(int row=0;row<NPhases;row++)
  				{
  					for(int i=0;i<NPhases-1;i++) connect<cascade> (k[row][i].out[0],k[row][i+1].in[2]);
  					connect<stream> (k[row][NPhases-1].out[0],out[2*row]);
  					connect<stream> (k[row][NPhases-1].out[1],out[2*row+1]);
  				}
  
  				// Input Streams connections
  				for(int row = 0;row<NPhases;row++)
  				for(int col=0;col<NPhases;col++)
  				{
  					int col1 = (row%2?NPhases-col-1:col); // kernel col is inverted on odd rows
  					int offset = (row%2);
  
  					connect<stream>(in[4*col+offset],k[row][col1].in[0]);
  					connect<stream>(in[4*col+2+offset],k[row][col1].in[1]);
  				}
  			};
  		};
  
  
  		class TopGraph: public adf::graph
  		{
  		public:
  			FIRGraph_SSR8 G1;
  
  			input_port in[32]; // 8 columns, 2 streams per kernel, x2 for the row direction
  			output_port out[16]; // 8 columns, 2 streams per kernel
  
  			TopGraph()
  			{
  				for(int i=0;i<16;i++)
  				{
  					connect<> (in[2*i],G1.in[2*i]);
  					connect<> (in[2*i+1],G1.in[2*i+1]);
  					connect<> (G1.out[i],out[i]);
  				}
  			}
  		};
  