import com.xilinx.JBits.CoreTemplate.*;
import com.xilinx.JBits.Virtex.ConfigurationException;
import com.xilinx.JRoute.Virtex.ResourceDB.*;
import com.xilinx.JBits.Virtex.Expr;
import com.xilinx.JBits.Virtex.Bits.*;

import com.xilinx.JBits.Virtex.Util;

/** 
*  This Core defines a 2D Shift registers used in the "Distributed Arithmetic" Core.
* 
*  <p>
*
*
*
*  <p>
*
*
*
*
*
* Inputs connected to F1 and G1 of Slice 0 at the selected row<p>
*
* Outputs connected to XQ and YQ located at col+word width<p>
*<p>
*
*<pre>
* Bugs:
* 1) Check no or rows and cols VS filter parameter and min size
* 2) There is a conflict between the input ports and the internal shift ports (must do load at clk 1 then shift at clk 2)
* 3) Must check if no of rows is small 
* 4) Load pin must be routed to all load pins in the logic CLBs
* 5) Check if the InvertIntArray to be used in the LUT configuration
* =========================
* 6) Minimum size is 2X2 (i.e. CLB granularity) 2 taps X 2-bit words
* 7) Muat change the graularity from CLB to slice because minimum growth in each direction is 2.
*</pre>
*<p>
*<p> 
*
*<pre>This matrix represents no of FF
*where no of rows represents no of taps
*no of columns represents word size
*Only the first row gets input data 
*and only the last column sends the output data.
*
*</pre>
*<pre>IN
*||||||||
*||||||||O
*||||||||U
*||||||||T
*
*
*
*Slice 0, X & F LUT are in MSB
*
*Growth direction:
* |--->cols
* |
*\/ 
*rows</pre>
*
*<pre>
* Each column is represented by a single FF (i.e. it uses either XQ or YQ)
* Each row is represented by a slice (i.e. it uses both XQ & YQ)
*</pre>
*
*
*  <p>
*  Copyright (c) 2000 Jamil Khatib.<p>
*
*  Created    0.1  25 September 2000<br>
*  Author     Jamil Khatib<br>
*/
public class Shift2D extends RTPCore 

{

    private static int[] F_MUX  = Util.InvertIntArray(Expr.F_LUT("(F1 & ~F2 & ~F3) | (F1 & F2 & ~F3) | (~F1 & F2 & F3) | (F1 & F2 & F3)"));

    private static int[] G_MUX  = Util.InvertIntArray(Expr.G_LUT("(G1 & ~G2 & ~G3) | (G1 & G2 & ~G3) | (~G1 & G2 & G3) | (G1 & G2 & G3)"));
    

    Port DoutPort;
    Port DinPort;
    Port clkPort;
    Port LoadPort;
    
    int NoRows = 0;
    int NoCols = 0;

    int MyTag =     0x0800;
    
    /* Define this for debug purposes only */
    boolean debug = true;

    
    /////////////////////////////  
    /**
     * Creates an instance of a 2D shifter to be used in DA filter.
     *
     * @param  instanceName   the name assigned to this instance
     * @param  clk            the external net to be connected to the CLK port
     * @param  Din            the external bus to be connected to the Data in port, the size of Din port specifies the word width
     * @param  Dout           the external bus to be connected to the Data out port, the size of Dout port specifies no of filter taps
     * @param  Load           the external Net to be connected to the Load new data port 
     * 
     */

    public Shift2D(String instanceName, Net clk, Bus Dout, Bus Din, Net Load) 
    {
        super(instanceName);


        /* 
           Calculate No of rows and no columens (taps and word size) based on the input and output pins 
        */


        NoRows =  Dout.getWidth();
        NoCols =  Din.getWidth();
 
        if(NoRows < 2 || NoCols < 2)
            {
                System.out.println("Can not implement small cores with dimensions less than 4x4");
                System.exit(-1);
     
            }
 
        if(debug)
            System.out.println("Rows = " + NoRows + " Cols = " + NoCols);
 

        /* compute core width and height */      
        setHeight(NoRows);
        setWidth(NoCols);

        /* Define the core's ports */
        clkPort = newInputPort("CLK", clk);
        LoadPort = newInputPort("Load", Load);
        DoutPort = newOutputPort("DataOut", Dout);
        DinPort = newInputPort("DataIn", Din);

        return ;
        
    }

    /////////////////////////////
    /**
     * Implement this 2D shifter instance
     */
    public final void implement()
    {

        // set the core absolute offset
  
        Offset offset = calcAbsoluteOffset();

        int row = offset.getVerOffset(Gran.CLB);
        int col = offset.getHorOffset(Gran.CLB);

        PrintDebugMsg("[Shifter] Location  Row = "+ row + " Col " + col);

        // configure the clbs using relative location
        configureCLBs(row, col);

        InternalRoute( row, col);
        

        // Create pins
        CreatePins(row, col);

        return ;
        
    }

    /////////////////////////////  

    /**
     * Create pins
     *
     * @param  row    Row location of the core
     * @param  col    Column location of the core
     *
     * The output pins are connected to YQ
     */
    private void CreatePins(int row, int col)
    {
        
        Pin output;
        Pin input;

        Pin[]  load = new Pin[NoCols];
        

        /* connect output pins to ports */
        try {
            int j =0;

            /*In this I moved the coordinate transformation from the loop to its body*/
            int incRow = 0;
            
            for(int i = 0 ; i < (NoRows); i++)
                {
                    
                    PrintDebugMsg("in Create Pines 1 ");
                    PrintDebugMsg("Port id["+j+"] Slice["+i%2+"] incRow = " +incRow);
                    

                    output = new Pin(Pin.CLB, row-incRow, col+NoCols-1, Wires.Slice_YQ[i%2]);


                    DoutPort.setPin(j++, output);

                    incRow += (i%2);
                    
                }

            PrintDebugMsg("No of used Rows = " +incRow);
            PrintDebugMsg("No of connected Out ports = " + j);
            

        } catch (WidthMismatchException wme) {
            System.out.println(wme);
            System.exit(-1);
        }
       

        /*S0F1 is the LSB and S0G1 is the MSB*/
        /* It assumes slice 0 */
        /* F1 = Shift pin
           F2 = Loaded data
           F3 = Load pin 
        */
        /* connect input pins to ports */
        try {
            int j =0;
            
            for (int i = 0 ; i < (NoCols/2); i++) 
                {
              
                    PrintDebugMsg("in Create Pines 2 ");
                    

                    load[j++] =  new Pin(Pin.CLB, row, col+i,Wires.SliceF3[0]);
                 
                
                    load[j++] =  new Pin(Pin.CLB, row, col+i,Wires.SliceG3[0]);
                 

                    input = new Pin(Pin.CLB, row, col+i,Wires.SliceF2[0]);
                 
                    DinPort.setPin(i, input);

                    output = new Pin(Pin.CLB, row, col+i, Wires.SliceG2[0]);

                    DinPort.setPin(i+1, input);
                    

                }

//        PrintDebugMsg("No of used Cols = " +i);
        
            PrintDebugMsg("No of connected In ports = " + j);

        } catch (WidthMismatchException wme) 
            {
                System.out.println(wme);
                System.exit(-1);
            }
 

        try {
            LoadPort.setPin(0, load);
        } catch (WidthMismatchException ce) {
            System.out.println(ce);
            System.exit(-1);
        }

        return;
        
    }
    


    /////////////////////////////  

    /**
     * Perform internal routings.
     *
     * @param  row    Row location of the core
     * @param  col    Column location of the core
     */
    private void InternalRoute(int row, int col)
    {
        int iRow = row;
        int iCol = col;
        
        try{
         
            /* First 2 rows with slice0 and slice 1 */
            /* This row connects to the MUX inputs */

            for(iCol = col; iCol< (col+(NoCols/2)-1); iCol++)
                {
                    PrintDebugMsg("in InternalRoute 1 col = " + iCol);
                   

                    /* Pin names did not change */
                    Pin BY_0_pin; /* G1 instead of BY */
                    Pin XQ_0_pin; /* XQ instead of XQ */

                    Pin BX_0_pin; /* F1 instead of BX */
                    Pin YQ_0_pin; /* YQ instead of YQ */

                    XQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[0]);
                    BY_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceG1[0]);
                    Bitstream.connect(XQ_0_pin, BY_0_pin);
       
                    YQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[0]);
                    BX_0_pin = new Pin(Pin.CLB,iRow,iCol+1,Wires.SliceF1[0]);
                    Bitstream.connect(YQ_0_pin, BX_0_pin);

                    Pin BY_1_pin; 
                    Pin XQ_1_pin; 

                    Pin BX_1_pin; 
                    Pin YQ_1_pin; 

                    XQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[1]);
                    BY_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceBY[1]);
                    Bitstream.connect(XQ_1_pin, BY_1_pin);
       
                    YQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[1]);
                    BX_1_pin = new Pin(Pin.CLB,iRow,iCol+1,Wires.SliceBX[1]);
                    Bitstream.connect(YQ_1_pin, BX_1_pin);

                }

     // This routes the last column in the first 2 rows
            {
//                iCol++;

                PrintDebugMsg("in InternalRoute 2 Col = "+ iCol);
                /* Last col*/
                Pin BY_0_pin; /* G1 instead of BY */
                Pin XQ_0_pin; /* XQ instead of XQ */

                Pin BX_0_pin; 
                Pin YQ_0_pin;

                XQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[0]);
                BY_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceG1[0]);
                Bitstream.connect(XQ_0_pin, BY_0_pin);
       
                YQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[0]);
                BX_0_pin = new Pin(Pin.CLB,iRow,col,Wires.SliceBX[1]);
                Bitstream.connect(YQ_0_pin, BX_0_pin);

                Pin BY_1_pin; 
                Pin XQ_1_pin; 

                Pin BX_1_pin; 
                Pin YQ_1_pin; 

                XQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[1]);
                BY_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceBY[1]);
                Bitstream.connect(XQ_1_pin, BY_1_pin);
       
                YQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[1]);
                BX_1_pin = new Pin(Pin.CLB,iRow-1,col,Wires.SliceBX[0]);
                Bitstream.connect(YQ_1_pin, BX_1_pin);


            }
     
// This routes the reset of the rows and columns except the last column in each row
            for(iRow =row-1 ; iRow > (row-(NoRows/2)); iRow--)
                {
               
                    PrintDebugMsg("in InternalRoute 3 Row = "+ iRow + "   Col= " +iCol);
                
                    for(iCol = col; iCol< (col+(NoCols/2)-1); iCol++)
                        {

                            PrintDebugMsg("in InternalRoute 4 Col = "+ iCol);

                    
                            Pin BY_0_pin; 
                            Pin XQ_0_pin; 

                            Pin BX_0_pin; 
                            Pin YQ_0_pin; 

                            XQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[0]);
                            BY_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceBY[0]);
                            Bitstream.connect(XQ_0_pin, BY_0_pin);
       
                            YQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[0]);
                            BX_0_pin = new Pin(Pin.CLB,iRow,iCol+1,Wires.SliceBX[0]);
                            Bitstream.connect(YQ_0_pin, BX_0_pin);



                            Pin BY_1_pin; 
                            Pin XQ_1_pin; 

                            Pin BX_1_pin; 
                            Pin YQ_1_pin; 

                            XQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[1]);
                            BY_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceBY[1]);
                            Bitstream.connect(XQ_1_pin, BY_1_pin);
       
                            YQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[1]);
                            BX_1_pin = new Pin(Pin.CLB,iRow,iCol+1,Wires.SliceBX[1]);
                            Bitstream.connect(YQ_1_pin, BX_1_pin);

                        }

// This routes the last column of the rows    
                 for(iRow =row-1 ; iRow > (row-(NoRows/2)); iRow--)
                    {
//                        iCol++;
                        PrintDebugMsg("in InternalRoute 5 col = " +iCol);
                        /* Last col*/
                        Pin BY_0_pin;
                        Pin XQ_0_pin;

                        Pin BX_0_pin;
                        Pin YQ_0_pin;

                        XQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[0]);
                        BY_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceBY[0]);
                        Bitstream.connect(XQ_0_pin, BY_0_pin);
       
                        YQ_0_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[0]);
                        BX_0_pin = new Pin(Pin.CLB,iRow,col,Wires.SliceBX[1]);
                        Bitstream.connect(YQ_0_pin, BX_0_pin);

                        Pin BY_1_pin; 
                        Pin XQ_1_pin; 
                        
                        Pin BX_1_pin; 
                        Pin YQ_1_pin; 
                        
                        XQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_XQ[1]);
                        BY_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.SliceBY[1]);
                        Bitstream.connect(XQ_1_pin, BY_1_pin);
                        
                        YQ_1_pin = new Pin(Pin.CLB,iRow,iCol,Wires.Slice_YQ[1]);
                        BX_1_pin = new Pin(Pin.CLB,iRow-1,col,Wires.SliceBX[0]);
                        Bitstream.connect(YQ_1_pin, BX_1_pin);

                    }    



                }
    

         } catch (ConnectionException e) {
            e.printStackTrace();
            System.out.println("Col No " +iCol);
            
            System.exit(-1);
        }

        

        return;
        
    }
    

    /////////////////////////////  

    /**
     * Configure CLBs.
     *
     * @param  row    Row location of the core
     * @param  col    Column location of the core
     *
     */
    private void configureCLBs(int row, int col)
    {

        int iRow = row;
     
        int iCol = col;
     
        Pin[] clkPin = new Pin[NoRows*NoCols/2];

        int i =0;
    
        try{
         
            // First 2 rows where Slice0 gets the inputs
            for(iCol = col; iCol< (col+(NoCols/2)); iCol++)
                {
                    PrintDebugMsg("in ConfigureCLB 1 ");

                    /* define the clock pin */
                    clkPin[i++] = new Pin(Pin.CLB, iRow, iCol, Wires.SliceClk[0]);
                    clkPin[i++] = new Pin(Pin.CLB, iRow, iCol, Wires.SliceClk[1]);

                    Bitstream.set(iRow, iCol, S0CE.S0CE, S0CE.OFF);

                    Bitstream.set(iRow, iCol, S1CE.S1CE, S1CE.OFF);

                    Bitstream.set(iRow, iCol, SliceControl.LatchMode[0], SliceControl.OFF[0]);

                    Bitstream.set(iRow, iCol, SliceControl.LatchMode[1], SliceControl.OFF[1]);


                    /*Slice 0 that loads new data from the input or shift its data */

                    /*Set the input MUX */
                    Bitstream.set(iRow, iCol, LUT.SLICE0_F, F_MUX);


                    Bitstream.set(iRow, iCol, LUT.SLICE0_G, G_MUX);


                    Bitstream.set(iRow, iCol, SliceControl.X.X[0], SliceControl.X.FOUT[0]); 
                    Bitstream.set(iRow, iCol, SliceControl.XDin.XDin[0], SliceControl.XDin.X[0]);
                   
 
                    Bitstream.set(iRow, iCol, SliceControl.Y.Y[0], SliceControl.Y.GOUT[0]);
                    Bitstream.set(iRow, iCol, SliceControl.YDin.YDin[0], SliceControl.YDin.Y[0]);



/***********************************************************************/
                    /* Slice 1 shifts only its contents*/

                    Bitstream.set(iRow, iCol,SliceControl.XDin.XDin[1], SliceControl.XDin.BX[1]);


                    Bitstream.set(iRow, iCol,SliceControl.YDin.YDin[1], SliceControl.YDin.BY[1]); 


                    tagCLB(iRow, iCol, MyTag);
               

                }

            /* set default value */
            if(debug)
                Bitstream.set(row-1 , col, CLB.SLICE1_XQ, 1);


        } catch (ConfigurationException ce) {
            System.out.println(ce);
            System.exit(-1);
        }


        try{
         
            for(iRow =row-1 ; iRow > (row-(NoRows/2)); iRow--)
                {
               
                    PrintDebugMsg("in ConfigureCLB 2 ");
                
                    for(iCol = col; iCol< (col+(NoCols/2)); iCol++)
                        {

                            PrintDebugMsg("in ConfigureCLB 3 ");

                            /* define the clock pin */
                            clkPin[i++] = new Pin(Pin.CLB, iRow, iCol, Wires.SliceClk[0]);
                            clkPin[i++] = new Pin(Pin.CLB, iRow, iCol, Wires.SliceClk[1]); 

                            Bitstream.set(iRow, iCol, S0CE.S0CE, S0CE.OFF);
                            Bitstream.set(iRow, iCol, S1CE.S1CE, S1CE.OFF);
           
                            Bitstream.set(iRow, iCol, SliceControl.LatchMode[0], SliceControl.OFF[0]);

                            Bitstream.set(iRow, iCol, SliceControl.LatchMode[1], SliceControl.OFF[1]);



                            /*Slice 0 */
                            Bitstream.set(iRow, iCol,SliceControl.XDin.XDin[0], SliceControl.XDin.BX[0]);



                            Bitstream.set(iRow, iCol,SliceControl.YDin.YDin[0], SliceControl.YDin.BY[0]); 




                            /*Slice 1*/

                            Bitstream.set(iRow, iCol,SliceControl.XDin.XDin[1], SliceControl.XDin.BX[1]);



                            Bitstream.set(iRow, iCol,SliceControl.YDin.YDin[1], SliceControl.YDin.BY[1]); 



                            tagCLB(iRow, iCol, MyTag);


                        }
                  
                }
 
        } catch (ConfigurationException ce) {
            System.out.println(ce);
            System.exit(-1);
        }
        
        
    PrintDebugMsg("No of clocks = " +i);  

        try {
            clkPort.setPin(0, clkPin);
        } catch (WidthMismatchException ce) {
            System.out.println(ce);
            System.exit(-1);
        }

 
        return;
        
    }
    
    /////////////////////////////  

    /**
     * Get the hight granularity of the core.
     *
     */
    public int getHeightGran()
    {
        return Gran.CLB;
    }


    /////////////////////////////  
    /**
     * Get the width granularity of the core.
     *
     */
    public int getWidthGran() 
    {
        return Gran.CLB;
    }


    /////////////////////////////
    /** tag CLB to make BoardScope viewing easier
     * @param  row    Row location of the clb
     * @param  col    Column location of the clb
     * @param  tag    Tag ID

     */
    private void tagCLB(int row, int col, int tag)
    {
   
        try 
            {

                Bitstream.getVirtex().setTag(row, col, tag);

            } catch (ConfigurationException ce) 
                {

                    System.out.println("ERROR: unable to tag CLB " +
                                       "at R" + row + "C" + col);
                    System.out.println(ce);
                    System.exit(-1);
                }
        return ;
        
    }

    /////////////////////////////
    /** calculates core hight based on no of rows or word size
     * @param  rows    No. of rows or word size
     */
    public static int calcHeight(int rows) {
    
        return rows;
    }


    /////////////////////////////
    /** calculates core width based on no. of columns or no of taps
     * @param  cols    No.of columns or no of taps
     */
    public static int calcWidth(int cols) 
    {
        return cols;
    }

    /////////////////////////////    


    private void PrintDebugMsg(String msg)
    {
        if(debug)
            System.out.println(msg);
        
        return;
        
    }
    
}
