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 Johnson counter
* 
*  <p>
*
*
*
*  <p>
*
*
*
*
*
* Inputs : No inputs to the core<p>
*
* Outputs connected to XQ and YQ<p>
*<p>
*
*<pre>
* Bugs:
* 1) no of bits must be multiple of 2
*</pre>
*<p>
*<p> 
*
*<pre>
*
* X  in LSB
*
*Growth direction:
* --->cols
*
* </pre>
*
*
*
*
*  <p>
*  Copyright (c) 2000 Jamil Khatib.<p>
*
*  Created    0.1  16 October 2000<br>
*  Author     Jamil Khatib<br>
*/
public class JCounter extends RTPCore 

{

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

    Port OverflowPort;
    Port clkPort;

    
    int NoCols = 0;

    int MyTag =     0x0805;
    
    /* 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  CountBits      no of counter bits
     * @param  clk            the external net to be connected to the CLK port
     * @param  Overflow       the external Net to be connected to the Over flow(max count) port 
     * 
     */

    public JCounter(String instanceName, int CountBits, Net clk, Net Overflow) 
    {
        super(instanceName);


        if(CountBits % 2 != 0)
            {
                System.out.println("Invalid no of bits");
                System.exit(-1);
     
            }
        /* 
           Calculate  no of columens  based on CountBits
        */
        NoCols = CountBits/2;
        

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

        /* Define the core's ports */
        clkPort = newInputPort("CLK", clk);
        OverflowPort = newOutputPort("Overflow", Overflow);



        return;
        

    }

    /////////////////////////////
    /**
     * Implement this Counter
     */
    public final void implement()
    {

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

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

        PrintDebugMsg("[JCounter] Location  Row = "+ row + " Col " + col);
        
        // configure the clbs using relative location
        configureCLBs(row, col,slice);

        InternalRoute(row, col,slice);
        

        // Create pins
        CreatePins(row, col, slice);

        return ;
        
    }/*implement()*/

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

    /**
     * Create pins
     *
     * @param  row    Row location of the core
     * @param  col    Column location of the core
     * @param  slice  Slice location of the core
     *
     */
    private void CreatePins(int row, int col, int slice)
    { 
Pin output;

        try{
            
  PrintDebugMsg("[JCounter]in Create Pines ");
                    

                    output = new Pin(Pin.CLB, row, col, Wires.Slice_Y[slice]);
                 
                    OverflowPort.setPin(0, output);

                    

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

        return ;
        
    }/*
       CreatePins(int row, int col, int slice)*/

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

    /**
     * Perform internal routings.
     *
     * @param  row    Row location of the core
     * @param  col    Column location of the core
     * @param  slice  Slice location of the core
     */
    private void InternalRoute(int row, int col, int slice)
    {
        try{
            
/* Route the Overflow logic */
            Pin MSB_pin; 
            Pin PRE_MSB_pin; 
            Pin F1_pin;
            Pin F2_pin;
            
            MSB_pin =  new Pin(Pin.CLB,row,col+(NoCols),Wires.Slice_YQ[slice]);
            F2_pin = new Pin(Pin.CLB,row,col,Wires.SliceF2[slice]);

  Bitstream.connect(MSB_pin, F2_pin);


 
            PRE_MSB_pin = new Pin(Pin.CLB,row,col+(NoCols),Wires.Slice_XQ[slice]);
            F1_pin = new Pin(Pin.CLB,row,col,Wires.SliceF1[slice]);

  Bitstream.connect(PRE_MSB_pin, F1_pin);


/* Route the shift logic */
    for(int i = 0; i< (NoCols); i++)
                        {

                            PrintDebugMsg("[JCounter]in InternalRoute 1 ");

                    
                            Pin BY_pin; 
                            Pin XQ_pin; 

                            Pin BX_pin; 
                            Pin YQ_pin; 

                            XQ_pin = new Pin(Pin.CLB,row,col+i,Wires.Slice_XQ[slice]);
                            BY_pin = new Pin(Pin.CLB,row,col+i,Wires.SliceBY[slice]);
                            Bitstream.connect(XQ_pin, BY_pin);
       
                            YQ_pin = new Pin(Pin.CLB,row,col+i,Wires.Slice_YQ[slice]);
                            if(i == (NoCols)-1)
                                {
                                    PrintDebugMsg("[JCounter] Last bit was reached = " + i);
                                    
                                    BX_pin = new Pin(Pin.CLB,row,col,Wires.SliceBX[slice]); 
                                }
                            
                            else
                                {
                                    
                                    BX_pin = new Pin(Pin.CLB,row,col+i+1,Wires.SliceBX[slice]);
                                }
                            
                            Bitstream.connect(YQ_pin, BX_pin);
                        }
    
  } catch (ConnectionException e) {
            e.printStackTrace();

            
            System.exit(-1);
        }

       
        return;
    }/*InternalRoute(int row, int col, int slice)*/
    

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

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

        Pin[] clkPin = new Pin[NoCols];

    
        try{

            PrintDebugMsg("[Jcounter] in ConfigureCLB 1 ");

/* Set the Overflow logic */
            Bitstream.set(row, col, LUT.F[slice] , OVF_LOGIC);
            Bitstream.set(row, col, SliceControl.X.X[slice], SliceControl.X.FOUT[slice]); 



/* set the first 2bits */
            /* define the clock pin */
            clkPin[0] = new Pin(Pin.CLB, row, col, Wires.SliceClk[slice]);

            if(slice == 0)
                Bitstream.set(row, col, S0CE.S0CE, S0CE.OFF);
            else
                Bitstream.set(row, col, S1CE.S1CE, S1CE.OFF);

/* Invert the input of the first bit which is connected to the last bit */
            Bitstream.set(row, col, SliceControl.LatchMode[slice], SliceControl.OFF[slice]);

            Bitstream.set(row, col,SliceControl.BxInvert[slice], SliceControl.ON[slice]);

            Bitstream.set(row, col,SliceControl.XDin.XDin[slice], SliceControl.XDin.BX[slice]);


            Bitstream.set(row, col,SliceControl.YDin.YDin[slice], SliceControl.YDin.BY[slice]); 

            if(slice == 0)
                {
                    
                    Bitstream.set(row , col, CLB.SLICE0_XQ, 0);

                    Bitstream.set(row , col, CLB.SLICE0_YQ, 0);
                }
            else
                {
                    Bitstream.set(row , col, CLB.SLICE1_XQ, 0);

                    Bitstream.set(row , col, CLB.SLICE1_YQ, 0);
                }  
                    
                            

            tagCLB(row, col, MyTag);

/* set the rest of the bits */

            for(int i = 1; i < NoCols; i++)
                {
                    
                    /* define the clock pin */
                    clkPin[i] = new Pin(Pin.CLB, row, col+i, Wires.SliceClk[slice]);
                    if(slice == 0)
                        Bitstream.set(row, col+i, S0CE.S0CE, S0CE.OFF);
                    else
                        Bitstream.set(row, col+i, S1CE.S1CE, S1CE.OFF);


                    Bitstream.set(row, col+i, SliceControl.LatchMode[slice], SliceControl.OFF[slice]);

                    Bitstream.set(row, col+i,SliceControl.XDin.XDin[slice], SliceControl.XDin.BX[slice]);


                    Bitstream.set(row, col,SliceControl.YDin.YDin[slice], SliceControl.YDin.BY[slice]); 


            if(slice == 0)
                {
                    
                    Bitstream.set(row , col+i, CLB.SLICE0_XQ, 0);

                    Bitstream.set(row , col+i, CLB.SLICE0_XQ, 0);
                }
            else
                {
                    Bitstream.set(row , col+i, CLB.SLICE1_XQ, 0);

                    Bitstream.set(row , col+i, CLB.SLICE1_XQ, 0);
                }  


                    tagCLB(row, col+i, MyTag);
                }
            

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


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


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

    }/*configureCLBs(int row, int col, int slice)*/
    

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

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


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


    /////////////////////////////
    /** 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;
        
    }/*tagCLB(int row, int col, int tag)*/

    /////////////////////////////
    /** calculates core hight based on word size and no of taps
     */
    public static int calcHeight()
    {
    
        return 1;
    }/*calcHeight()*/


    /////////////////////////////
    /** calculates core width
     * @param Bits no of teh counter bits
     */
    public static int calcWidth(int Bits) 
    {
        return Bits/2;
    }/*calcWidth(int Bits)*/

    /////////////////////////////    
    /** Prints a debug Message
     */

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