/*Check The questions in QS.txt*/

/* ToDo list
1) fix the stats variable
2) do the counter
3) use single both slices of clb in shift regs
4) move the code in set(,,row,col) to set()
5) USE LUT SHIFTER MODE INSTEAD OF FF
*/
package com.xilinx.JBits.Virtex.RTPCore;

import java.lang.reflect.Array;

import com.xilinx.JBits.Virtex.Bits.*;

import com.xilinx.JBits.Virtex.JBits;  
import com.xilinx.JBits.Virtex.Expr; 
import com.xilinx.JBits.Virtex.ConfigurationException;
import com.xilinx.JBits.Virtex.Bits.LUT;
import com.xilinx.JBits.Virtex.Bits.S0Control;  
import com.xilinx.JBits.Virtex.Bits.OUT0;
import com.xilinx.JBits.Virtex.Bits.OUT1;
import com.xilinx.JBits.Virtex.Bits.OUT2;
import com.xilinx.JBits.Virtex.Bits.OUT3;
import com.xilinx.JBits.Virtex.Bits.OUT4;
import com.xilinx.JBits.Virtex.Bits.S0F1; 
import com.xilinx.JBits.Virtex.Bits.S0F2;                                    
import com.xilinx.JBits.Virtex.Bits.S0F3; 
import com.xilinx.JBits.Virtex.Bits.S0F4; 
import com.xilinx.JBits.Virtex.Bits.S0CE;
import com.xilinx.JBits.Virtex.Bits.S0Clk;

import com.xilinx.JBits.Virtex.Bits.OutMuxToSingle;

import com.xilinx.JBits.Virtex.Util;
import com.xilinx.JBits.Virtex.State; 


/* JRoute imports */
import com.xilinx.JRoute.Virtex.Router.*;
import com.xilinx.JRoute.Virtex.ResourceDB.*;


/** 
**  This is defines a DAfilter "Distributed Arithmetic" Generator Core.
** 
**  <p>
**
** 
**
**  <p>
**
**  Copyright (c) 2000 Jamil Khatib.
**
**  <p>
**
**  @version    1.0  Jun 10, 2000
**  @author     Jamil Khatib
*/

public class DAfilter extends RTPCore {

    /* Filter specific variables */

    /* Filter Constants*/
    private int[] constants;

    /* Filter Word size */
    private int size = 8;

    /* Filter taps*/
    private int taps = 4;
  

    private int[] DALUT;

    private int[][] LUTS;
    

    /* Core specific variables and constants*/

    JBits jBits;
    
    /* Unique Tag Variable*/
    private static int uniqueTagID = 0;

    /*Buffer Declaration*/
    /* NOTE: Virtex LUT bits need to be INVERTED*/
    /*Fout = F1*/
    private static final int[] BUFFER1 = Util.InvertIntArray(Expr.F_LUT("F1"));


    /*
    **  The constructor initializes the DAfilter Object 
    **  with its <em>size</em>, <em>taps</em>
    **  and the filter<em>constants</em>.  
    **
    **  @param _size This parameter specifies the size of 
    **         the filter registers
    **
    **  @param _taps This parameter specifies number of filter
    **         taps.
    **
    **  @param _constants This parameter specifies the filter 
    **         constants.
    **          
    */


    public DAfilter(int _size, int _taps,int[] _constants)  throws Exception 

    {

        /* Core size should be defined */

        /* Set DA algorithm parameters*/
        size = _size;
        taps = _taps;
        constants = new int [_taps];

        constants = _constants;

        if(Array.getLength(_constants) != taps)
            throw new Exception("Unmatched No. of Taps and Constants Error");

        generate_romContents();
        covert_dalut_luts();
        
   
        /* Assign the Super Class variables*/
        /*clbHeigth and clbWidth*/

        /* Should be checked according to the final core size*/
        clbHeight = taps;
        clbWidth  = size;
   
        /*Allocate the State Symbol*/
        state = new State(_size*_taps);

        /*************************************/
        /*  Change the Tag */
        /*************************************/

        /*Set the Tag ID*/
        tag = 0x0800;//Taggs.SHIFTER;
   
        /* Set Unique Tag*/
        uniqueTagID = uniqueTagID + 1;
        setUniqueTag(uniqueTagID);
   
        /*Get the Name for the Core if one is defined*/
        //        name = Tags.GetName(tag);
   
        this.setName("DAfilter");
        
    } /* End DAfilter( _size, _taps, _constants)*/
   

    /**
     **  See the <em>set()</em> function in the <em>Core</em> object
     **  for a description of this function.
     **
     **  @exception ConfigurationException See the <em>Core</em> class
     **             for more information on this exception.
     **
     **  @see Core
     **
     */  
    

    public void set (JBits _jbits, int _clbRow, int _clbColumn)
        throws ConfigurationException {
   
        int row = 0;
        int col = 0;
        int i;
        int bit = 0;
   
        if (written == true) 
            throw new ConfigurationException( ConfigurationException.CORE_SET_VIOLATION);
   
        clbRow = _clbRow;
        clbColumn = _clbColumn;

        jBits = _jbits;
        
        try
            {
                generate_regs();
                generate_dalut();
                generate_serAdder();
        
                
            } 
        catch (ConfigurationException ce) {

            System.out.println("Configuration error" +ce.toString());
            System.out.println("Exiting.");
            System.exit(-5);

        }  /* end catch() */

   
    }/* End set( _jbits, _clbRow, _clbColumn)*/
    


    /******/
    public void set (JBits _jbits) throws ConfigurationException 
    {
        this.set ( jBits, clbRow,  clbColumn);

        return;
    
    }

    /*******/
    /*

      Register inputs:
      Note: Only Slice0 is used from each CLB.

      Block Internal connections: 
      Input: BX & BY
      Input Muxs: S0BX.S0BX & S0BY.S0BY muxs
      Input Singles: SINGLE_NORTH5,SINGLE_WEST14


      output: XQ & YQ 
      output muxs: OUT2.OUT2, OUT4.OUT4, OutMuxToSingle
      Output singles: SINGLE_SOUTH5, SINGLE_EAST14


      Internal Singles: SINGLE_NORTH5, SINGLE_SOUTH5



    */

    private void generate_regs() throws ConfigurationException
    {


        int row = 0;
        int col =0;

    

        for(row =clbRow; row < (taps+clbRow); row++)
            {
                
                for(col =clbColumn; col< ((size/2)+clbColumn); col++)
                    {    

                        
                        /* Set generic Slice parameters*/
                        /* Set the Flip Flops Clock*/
                        jBits.set(row, col, S0Clk.S0Clk, S0Clk.GCLK1);  
      
                        /* Disable the CE signal*/
                        jBits.set(row, col, S0CE.S0CE, S0CE.OFF);  
      
                        /*Latch Mode OFF */
                        jBits.set(row, col, S0Control.LatchMode, S0Control.OFF); 


                        /* Set CLB slice0  FF connections */


                        /*Set /CLB/Slice0/Y FF input*/
                        jBits.set(row, col, S0Control.YDin.YDin, S0Control.YDin.BY);
				
					
                        /* Set /CLB/Slice0/BY input via  S0BY input mux*/
                        /*                        jBits.set(row, col, S0BY.S0BY, S0BY.SINGLE_WEST14);

                         */				
                        /* Set /CLB/Slice0/Y FF output */
                        /* This output must be connected to X FF input in the same slice */
                        /* OUT2 mux is used to connect YQ to BX via North single*/
                        /*            jBits.set(row, col, OUT2.OUT2, OUT2.S0_YQ);
                                      jBits.set(row, col, OutMuxToSingle.OUT2_TO_SINGLE_SOUTH5, OutMuxToSingle.ON);
                        */

                        /* Set input of /CLB/Slice0/X FF */
                        jBits.set(row, col, S0Control.XDin.XDin, S0Control.XDin.BX);

                        try{// Route /CLB/Slice0/Y FF output to /CLB/Slice0/BX
                
                            Pin YQ_0_pin;
                            Pin BX_0_pin;
                            Router YQ2BX_Route; /* Internal route within the same CLB*/
                            
                            YQ_0_pin = new Pin(row,col,Wires.S0_YQ); // Output of YQ FF
                            BX_0_pin = new Pin(row,col,Wires.S0BX);  // Input to BX FF

                            YQ2BX_Route = new Router(jBits);

                            YQ2BX_Route.route(YQ_0_pin,BX_0_pin);
                       
                            System.out.println("[Route DY outputs] Col = "+col+"  Row = " +row);

                            YQ2BX_Route.setJBits(jBits);

                          

                        }
                        catch(RouteException re )
                            {
                    
                                throw new ConfigurationException("Route Exception");
                            }
            
                        /* Set /CLB/Slice0/BX input via  S0BX input mux*/
                        /*            jBits.set(row, col, S0BX.S0BX, S0BX.SINGLE_NORTH5);
                         */
                        /* Set /CLB/Slice0/X FF output */
                        /* This output must be connected to Y FF input in the next CLB */
                        /* OUT4 mux is used to connect XQ to BY via East single*/
                        /*            jBits.set(row, col, OUT4.OUT4, OUT4.S0_XQ);
                                      jBits.set(row, col, OutMuxToSingle.OUT4_TO_SINGLE_EAST14, OutMuxToSingle.ON);
                        */

                        try{// Route /CLB/Slice0/X FF output to /CLB/Slice0/BY in next col CLB 
                            // Or route last output in in the row (col = clbColumn +(size/2) -1) to next row BY
                            Pin BY_0_pin;
                            Pin XQ_0_pin;
                            Router XQ2nextBY_Route;

                           

                            if(!(col == (clbColumn+(size/2)-1)&&(row != clbRow)))
                                {
                                    
                                    XQ_0_pin = new Pin(row,col,Wires.S0_XQ);
                                    BY_0_pin = new Pin(row,col+1,Wires.S0BY);
                            
                                    XQ2nextBY_Route = new Router(jBits);

                                    XQ2nextBY_Route.route(XQ_0_pin,BY_0_pin);

                                    System.out.println("[Route normal] Col = "+col+"  Row = " +row);

                                    XQ2nextBY_Route.setJBits(jBits);


                                }
                            else
                                {
                                    XQ_0_pin = new Pin(row,col,Wires.S0_XQ);
                                    BY_0_pin = new Pin(row-1,clbColumn,Wires.S0BY);
                            
                                    XQ2nextBY_Route = new Router(jBits);

                                    XQ2nextBY_Route.route(XQ_0_pin,BY_0_pin);

                                    System.out.println("[Route edge] Col = "+col+"  Row = " +row);

                                    XQ2nextBY_Route.setJBits(jBits);


                                   
                                }
                            
                            

                        } catch(RouteException re )
                            {
                                
                                throw new ConfigurationException("Route Exception");
                            }
                        

                       
 

                        //    /* Set initial values*/
                        //                         /*Remove the row check */
                        //                         if(/*row == (taps+clbRow-1) &&*/ col == clbColumn)
                        //                             {

                        // 	                        /* Set the Initial State*/     
                        //                                 jBits.set(row, col, S0Control.XffSetResetSelect, S0Control.OFF);
                        //                                 if(row == (taps+clbRow-1))
                                                                        
                        //                                     jBits.set(row, col, S0Control.YffSetResetSelect, S0Control.OFF);
                        //                                 else
                        //                                     {
                                        
                        //                                         jBits.set(row, col, S0Control.YffSetResetSelect, S0Control.ON);
                        //                                         System.out.println("[RST val] Col = "+col+"  Row = " +row);
                        //                                     }
                                

                        //                             }
                        //                         else
                        //                             {
                        // 	                        /* Set the Initial State*/     
                        //                                 jBits.set(row, col, S0Control.XffSetResetSelect, S0Control.OFF);
                        //                                 jBits.set(row, col, S0Control.YffSetResetSelect, S0Control.OFF);

                        //                             }


                        /* Set init values */
                        if(col == clbColumn && row == (taps+clbRow-1))
                            {
                                jBits.set(row, col, S0Control.XffSetResetSelect, S0Control.OFF);
                                jBits.set(row, col, S0Control.YffSetResetSelect, S0Control.ON);
                            }
                        else
                            {
        
                        
                                jBits.set(row, col, S0Control.XffSetResetSelect, S0Control.OFF);
                                jBits.set(row, col, S0Control.YffSetResetSelect, S0Control.OFF);
                            }
                        

                        /*end init values*/

                        /*Write out Core Tag*/
                        setTag(jBits, row, col);
            
                    }/*end for(col) end configuring current CLB in this column  and row*/
                
				
				/* Last column must be connected to BY in the lower row at the first column*/
                
            }/*end for(row) end configuring all CLBs in this row*/
        



        return ;
      
    }/*end generate_regs()*/


    /*

      Register inputs:
      Note: Only Slice0 is used from each CLB.

      Block Internal connections: 
      Input: BX & BY
      Input Muxs: S0BX.S0BX & S0BY.S0BY muxs
      Input Singles: SINGLE_NORTH5,SINGLE_WEST19


      output: XQ & YQ 
      output muxs: OUT2.OUT2, OUT4.OUT4, OutMuxToSingle
      Output singles: SINGLE_SOUTH5, SINGLE_EAST19


      Internal Singles: SINGLE_NORTH5, SINGLE_SOUTH5

    */

    private void generate_dalut() throws ConfigurationException
    {

        /* ADDRESS = NO TAPS */
        /* OUTPUT = WORDSIZE */
        /*CONTENTS SIZE = WORDSIZE */

        /*    DALUT Rules 
              1) # of LUTs = wordsize
              2) wordsize should be larger than taps ( # taps <= wordsize )
              3) # taps should be multiple of 4 ( no. of luts should be divided into groups of 4 and the remaining luts )
              4) # taps can be less than 4
        */

      

        /* Checking the rules */
       
        if(taps > size)
            throw new ConfigurationException("Not supported value for taps and word size [rule2]");

        if((taps % 4 )!= 0)
            throw new ConfigurationException("Not supported value for taps [rule3]");

        /* find the location of the core*/
        int ratio = (size - taps)/2;
        int orgRow = clbRow + size + ratio -1; /*size must be divided by 2 when both slices are used*/
  
        int orgCol = clbColumn +size/2 -1;
        int col = orgCol;
        int row = orgRow;
        int lut_id = 0;


        /*NOTE:  Must connect shiftregs to inputs of LUTs*/

        for( row = orgRow; row < clbRow; row --)
            {
                jBits.set(row, col, LUT.SLICE0_G, Util.InvertIntArray(LUTS[lut_id]));    /*connected to Y and lsb*/
                jBits.set(row, col, LUT.SLICE0_F, Util.InvertIntArray(LUTS[lut_id+1])/*lut1*/);    /*connected to X and msb*/

                lut_id +=2;

/*Write out Core Tag*/
                        setTag(jBits, row, col);

            }
  

 	

        /* Set Y output to output of G LUT */
        /* This is the non-clocked output of the lut */
        jBits.set(row, col, S0Control.Y.Y, S0Control.Y.GOUT);
      
        /* Set Y flip flop input to Y output */ 
        /* This is the clocked output of the lut */
        jBits.set(row, col, S0Control.YDin.YDin, S0Control.YDin.Y);


        /* Set X output to output of F LUT */
        /* This is the non-clocked output of the lut */
        jBits.set(row, col, S0Control.X.X, S0Control.X.FOUT);
      
        /* Set X flip flop input to X output */ 
        /* This is the clocked output of the lut */
        jBits.set(row, col, S0Control.XDin.XDin, S0Control.XDin.X);

/*Write out Core Tag*/
                        setTag(jBits, row, col);


        return;	
	
    }/*end generate_dalut */


    /* 
       generates serial adder core

       Register inputs:
       Note: Only Slice0 is used from each CLB.

       Block Internal connections: 
       Input: BX & BY
       Input Muxs: S0BX.S0BX & S0BY.S0BY muxs
       Input Singles: SINGLE_NORTH5,SINGLE_WEST19


       output: XQ & YQ 
       output muxs: OUT2.OUT2, OUT4.OUT4, OutMuxToSingle
       Output singles: SINGLE_SOUTH5, SINGLE_EAST19


       Internal Singles: SINGLE_NORTH5, SINGLE_SOUTH5

    */

    private void generate_serAdder() throws ConfigurationException
    {


        return;	
	
    }/*end generate_serAdder */



    /******************************************************************
     * void generate_DALUT(unsigned int  consts[], unsigned int  rom[],unsigned int num_const)
     *
     * Generates the LUT (rom) contents
     *
     * Inputs:
     * constants[] : Filter's constants
     * DALUT[]    : Array to store the LUT constants 
     * 
     ******************************************************************/

    private void generate_romContents()
    {

        int i= 0;
        int j = 0;
        int num_const = taps; 
        int max_addr = (int)Math.pow(num_const,2);
        int flag = 0;
        int index = 0;
    
        DALUT = new int [max_addr];
    
        for (i= 0;i < max_addr; i++)/* i = rom address*/
            {
                index = i;
                DALUT[i] = 0;
        
                for (j = 0; j < num_const; j++)
                    {
                        flag = 0x1 & index;  /* Mask the first bit*/

                        DALUT[i] = DALUT [i] + ( constants[j] * flag);
            
                        index >>=1; /* get next bit in the address*/
            
                    }
            }
    
        return ;
    } /*generate_romContents()*/

    /******************************************************************
     * void covert_dalut_luts()
     *
     * Converts DALUT "rom" contents to arrays of luts
     *
     * Inputs:
     * none: it uses the gobal array DALUT 
     * 
     ******************************************************************/

    private void covert_dalut_luts()
    {
        int tmp [][];
        
        int max_addr = Array.getLength(DALUT);
                       
        tmp = new int [max_addr][size];
        

        LUTS  = new int [size][16];

       
        // Convert from inetger to binary
        for(int i= 0; i < max_addr; i++)
            {
                tmp[i] = Util.IntToIntArray(DALUT[i],size);
                
            }
        
        // Fill the LUTs
        for(int i =0; i< max_addr ; i++)
                
            {
                for(int j =0; j < size; j++)
                    {
                        LUTS[j][i%16] = tmp[i][j];

                    }	
            }
                
    }/* covert_dalut_luts() */
    

    /**************/
    public void test_parm()
    {
        int i =0;
        int max_addr = (int)Math.pow(taps,2);
    	int j=0;

        System.out.println("====DAfilter Testing procedure====");
        System.out.println("Taps = " +taps + "  Word size = " + size );
        System.out.println("Filter Constants:");
    
    

        for(i=0; i <taps ; i++)
            System.out.println("Constant[" +i+ "]= " + constants[i]);
    
        System.out.println("DALUT contents:");

        for(i=0; i <max_addr ; i++)
            System.out.println("DALUT[" +i+ "]= " + DALUT[i]);


        System.out.println("---LUTs contents---");    
		
        for(i =0; i<size ; i++)
            {
                
                System.out.println("== LUT["+ i + "] ==");
                for(j =0; j < 16; j++)
                    System.out.println("[" + LUTS[i][j] + "]");
            }
        

//         for( i =0; i< max_addr ; i++)
                
//             {
//                 System.out.println("== LUT["+ j + "] ==");

//                 for(j =0; j < size; j++)
//                     {
//                         System.out.println("i="+i + " i%16= " + (i%16) + "j= " + j);
                             
//                         System.out.println("[" + LUTS[j][i%16] + "]");
//                     }	
//             }


        return;
    
    
    }/* test_parm*/
  
    
}
