// file: ioip_exdes.v
// (c) Copyright 2009 - 2011 Xilinx, Inc. All rights reserved.
// 
// This file contains confidential and proprietary information
// of Xilinx, Inc. and is protected under U.S. and
// international copyright and other intellectual property
// laws.
// 
// DISCLAIMER
// This disclaimer is not a license and does not grant any
// rights to the materials distributed herewith. Except as
// otherwise provided in a valid license issued to you by
// Xilinx, and to the maximum extent permitted by applicable
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
// (2) Xilinx shall not be liable (whether in contract or tort,
// including negligence, or under any other theory of
// liability) for any loss or damage of any kind or nature
// related to, arising under or in connection with these
// materials, including for any direct, or any indirect,
// special, incidental, or consequential loss or damage
// (including loss of data, profits, goodwill, or any type of
// loss or damage suffered as a result of any action brought
// by a third party) even if such damage or loss was
// reasonably foreseeable or Xilinx had been advised of the
// possibility of the same.
// 
// CRITICAL APPLICATIONS
// Xilinx products are not designed or intended to be fail-
// safe, or for use in any application requiring fail-safe
// performance, such as life-support or safety devices or
// systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any
// other applications that could lead to death, personal
// injury, or severe property or environmental damage
// (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and
// liability of any use of Xilinx products in Critical
// Applications, subject only to applicable laws and
// regulations governing limitations on product liability.
// 
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
// PART OF THIS FILE AT ALL TIMES.

//----------------------------------------------------------------------------
// SelectIO wizard example design
//----------------------------------------------------------------------------
// This example design instantiates the IO circuitry
//----------------------------------------------------------------------------

`timescale 1ps/1ps

module ioip_exdes
   // Clock -> q modeling delay
 #(
   // width of the data for the system
   parameter sys_w = 4,
   // width of the data for the device
   parameter dev_w = 20)
  (
   output     [1:0]       PATTERN_COMPLETED_OUT,
   // From the system into the device
   input      [sys_w-1:0] DATA_IN_FROM_PINS_P,
   input      [sys_w-1:0] DATA_IN_FROM_PINS_N,
   output [sys_w-1:0] DATA_OUT_TO_PINS_P,
   output [sys_w-1:0] DATA_OUT_TO_PINS_N,
   input                  CLK_IN,
   input                  CLK_RESET,
   input                  IO_RESET);


   localparam             num_serial_bits = dev_w/sys_w;
   wire        clkin1;

   reg  [num_serial_bits-1:0]  count_out;
   reg [num_serial_bits-1:0] local_counter;
   reg  [num_serial_bits-1:0]  count_out1;
   reg  [num_serial_bits-1:0]  count_out2;
   reg  [num_serial_bits-1:0]  pat_out;

   wire [dev_w-1:0]       data_in_to_device;
   reg [dev_w-1:0]       data_in_to_device_int2;
   reg [dev_w-1:0]       data_in_to_device_int3;

   wire  [dev_w-1:0]       data_out_from_device;
   wire [sys_w-1:0]  oserdes_d[0:7];   
   wire [sys_w-1:0]  iserdes_q[0:7];   // fills in starting with 0
   wire   [sys_w-1:0] data_out_to_pins_predelay;
   wire        clock_enable = 1'b1;
   wire   [sys_w-1:0] data_in_from_pins_int;
   wire   [sys_w-1:0] data_in_from_pins_delay;
   wire   [sys_w-1:0] tristate_predelay;

   wire                   clkfbout;
   wire                   clk_in_pll;
   wire                   clk_in_pll1;
   wire                   clkfbout_buf;
   wire                   locked_in;
   wire                   locked_out;
   (* KEEP = "TRUE" *) wire        clk_div_in_int;
   wire                   clk_div_in;

   reg rst_sync;
   reg rst_sync_int;
   reg rst_sync_int1;
   reg rst_sync_int2;
   reg rst_sync_int3;
   reg rst_sync_int4;
   reg rst_sync_int5;
   reg rst_sync_int6;
   reg [1:0] pattern_completed = 2'b00;
   reg        bitslip = 1'b0;
   reg        bitslip_int = 1'b0;
   reg        equal = 1'b0;
   reg        equal1 = 1'b0;
   reg  [2:0] count_out3;
   reg        start_count = 1'b0;
   reg [num_serial_bits-1:0] data_delay_int1[sys_w -1 :0];
   wire [num_serial_bits-1:0] data_delay[sys_w -1 :0];
   reg [num_serial_bits-1:0] data_delay_int2[sys_w -1 :0];
   reg start_check;

  
  IBUFG clkin_in_buf
   (.O (clkin1),
    .I (CLK_IN)
     );



   // set up the fabric PLL_BASE to drive the BUFPLL
   PLL_BASE
   #(.BANDWIDTH             ("OPTIMIZED"),
     .CLK_FEEDBACK          ("CLKFBOUT"),
     .COMPENSATION          ("SYSTEM_SYNCHRONOUS"),
     .DIVCLK_DIVIDE         (1),
     .CLKFBOUT_MULT         (4),
     .CLKFBOUT_PHASE        (0.000),
     .CLKOUT0_DIVIDE        (4),
     .CLKOUT0_PHASE         (0.000),
     .CLKOUT0_DUTY_CYCLE    (0.500),
     .CLKOUT2_DIVIDE        (4*num_serial_bits),
     .CLKOUT2_PHASE         (0.000),
     .CLKOUT2_DUTY_CYCLE    (0.500),
    .CLKIN_PERIOD          (10.0),
     .REF_JITTER            (0.010))
   pll_base_inst
     // Output clocks
    (.CLKFBOUT              (clkfbout),
     .CLKOUT0               (clk_in_pll1),
     .CLKOUT1               (),
     .CLKOUT2               (clk_div_in_int),
     .CLKOUT3               (),
     .CLKOUT4               (),
     .CLKOUT5               (),
     // Status and control signals
     .LOCKED                (locked_in),
     .RST                   (CLK_RESET),
      // Input clock control
     .CLKFBIN               (clkfbout_buf),
     .CLKIN                 (clkin1));

    BUFG clkfb_buf
    (.O (clkfbout_buf),
     .I (clkfbout));

    BUFG clkf_buf
    (.O (clk_div_in),
     .I (clk_div_in_int));

    BUFG clko_buf
    (.O (clk_in_pll),
     .I (clk_in_pll1));



   always @(posedge clk_div_in or posedge IO_RESET) begin
   if (IO_RESET) begin
       rst_sync <= 1'b1;
       rst_sync_int <= 1'b1;
       rst_sync_int1 <= 1'b1;
       rst_sync_int2 <= 1'b1;
       rst_sync_int3 <= 1'b1;
       rst_sync_int4 <= 1'b1;
       rst_sync_int5 <= 1'b1;
       rst_sync_int6 <= 1'b1;
    end
   else begin
       rst_sync <= 1'b0;
       rst_sync_int <= rst_sync;
       rst_sync_int1 <= rst_sync_int;
       rst_sync_int2 <= rst_sync_int1;
       rst_sync_int3 <= rst_sync_int2;
       rst_sync_int4 <= rst_sync_int3;
       rst_sync_int5 <= rst_sync_int4;
       rst_sync_int6 <= rst_sync_int5;
   end
   end




   always @(posedge clk_div_in) begin
   if (rst_sync_int6) 
     count_out3 <= 3'b000;
   else begin
     if (equal == 1'b1 && count_out3 < 3'b100) begin
       count_out3 <= count_out3 + 1'b1;
     end
     else begin
       count_out3 <= 3'b000; 
     end
   end
 end

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
     equal1 <= 1'b0;
   end
   else if (count_out3 == 3'b100) begin
     equal1 <= equal;
   end
 end

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
 
    count_out1 <= 0;
     pat_out <= 5'b11010;
     end
   else if (locked_in) begin
     if (equal1 == 1'b0) begin
       count_out1 <= 0;
     pat_out <= 5'b11010;
     end
     else begin
     count_out1 <= count_out1 + 1'b1;
     end
     end
   end

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
     count_out2 <= 0;
   end
   else  begin
    if (equal1)
     count_out2 <= count_out1;
    else
     count_out2 <= pat_out;
   end
   end

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
     count_out <= 0;
   end
   else  begin
     count_out <= count_out2;
   end
end

   genvar assg;
   genvar pinsss;
   for (assg = 0 ; assg < num_serial_bits ; assg = assg + 1) begin
   for (pinsss = 0 ; pinsss < sys_w ; pinsss = pinsss + 1)
     begin
       assign data_out_from_device[pinsss+sys_w*assg] = count_out[assg];
   end
   end

  assign data_delay[0] = {  
                         data_in_to_device[16],
 
                         data_in_to_device[12],
 
                         data_in_to_device[8],
 
                         data_in_to_device[4],
 
   data_in_to_device[0]};
  assign data_delay[1] = {  
                         data_in_to_device[17],
 
                         data_in_to_device[13],
 
                         data_in_to_device[9],
 
                         data_in_to_device[5],
 
   data_in_to_device[1]};
  assign data_delay[2] = {  
                         data_in_to_device[18],
 
                         data_in_to_device[14],
 
                         data_in_to_device[10],
 
                         data_in_to_device[6],
 
   data_in_to_device[2]};
  assign data_delay[3] = {  
                         data_in_to_device[19],
 
                         data_in_to_device[15],
 
                         data_in_to_device[11],
 
                         data_in_to_device[7],
 
   data_in_to_device[3]};

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) 
     start_check <= 1'b0;
   else
     begin
     if (data_delay[0] != 5'b00000)
       start_check <= 1'b1;
     end 
   end 

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) 
     start_count <= 1'b0;
   else
     begin
     if (data_delay[0] == 5'b00001 && equal == 1'b1)
       start_count <= 1'b1;
     end 
   end 

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) 
 
     local_counter <= 5'b00000;
   else
     begin
   if (start_count) 
     local_counter <= local_counter + 1'b1;
   else
     local_counter <= 5'b00000;
   end
   end

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
     data_delay_int1[0] <= 5'b00000;
     data_delay_int2[0] <= 5'b00000;
     data_delay_int1[1] <= 5'b00000;
     data_delay_int2[1] <= 5'b00000;
     data_delay_int1[2] <= 5'b00000;
     data_delay_int2[2] <= 5'b00000;
     data_delay_int1[3] <= 5'b00000;
     data_delay_int2[3] <= 5'b00000;
     end
   else
     begin
     data_delay_int1[0] <= data_delay[0];
     data_delay_int2[0] <= data_delay_int1[0];

     data_delay_int1[1] <= data_delay[1];
     data_delay_int2[1] <= data_delay_int1[1];

     data_delay_int1[2] <= data_delay[2];
     data_delay_int2[2] <= data_delay_int1[2];

     data_delay_int1[3] <= data_delay[3];
     data_delay_int2[3] <= data_delay_int1[3];

   end
   end

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
     bitslip_int <= 1'b0;
     equal <= 1'b0;
     end
   else
     begin
   if (equal == 1'b0 && locked_in == 1'b1 && start_check == 1'b1) begin
       if ( (data_delay[3] == pat_out) &&
(data_delay[2] == pat_out) &&
(data_delay[1] == pat_out) &&
(data_delay[0] == pat_out))
         begin
           bitslip_int <= 1'b0;
           equal <= 1'b1;
         end
       else
         begin
           bitslip_int <= 1'b1;
           equal <= 1'b0;
         end
       end
   else begin
     bitslip_int <= 1'b0;
   end
   end
   end

   reg [2:0] bit_count;

   always @(posedge clk_div_in) begin
   if (rst_sync_int6) begin
     bitslip <= 1'b0;
     bit_count <= 3'b000;
    end
   else
     begin
     bit_count <= bit_count + 1'b1; 
   if (bit_count == 3'b111) begin
   if (bitslip_int)
     bitslip <= ~bitslip;
   else
     bitslip <= 1'b0;
   end
   else
     bitslip <= 1'b0;
   end
   end

   always @(posedge clk_div_in) begin
   if (equal) begin
       if (
          (data_delay_int2[1] == local_counter) &&
          (data_delay_int2[2] == local_counter) &&
          (data_delay_int2[3] == local_counter) &&
          (data_delay_int2[0] == local_counter)) begin
           if (local_counter == 5'b11111)
             pattern_completed <= 2'b11;
            // all complete
           else
             pattern_completed <= 2'b01;
       end
       else begin
           if (start_count == 1'b1)
             pattern_completed <= 2'b10;
           else
             pattern_completed <= pattern_completed;  
       end
   end
   else begin
      pattern_completed <= 2'b00;
   end
 end



  assign PATTERN_COMPLETED_OUT =  pattern_completed;


   BUFPLL
    #(.DIVIDE        (5))
    bufpll_inst
      (.IOCLK        (clk_in_int_buf),
       .LOCK         (locked_out),
       .SERDESSTROBE (serdesstrobe),
       .GCLK         (clk_div_in), // GCLK must be driven by BUFG
       .LOCKED       (locked_in),
       .PLLIN        (clk_in_pll1));





genvar pin_count;
  genvar slice_count;
  generate for (pin_count = 0; pin_count < sys_w; pin_count = pin_count + 1) begin: pins
    // Instantiate the buffers
    ////------------------------------
    IBUFDS
      #(.DIFF_TERM  ("FALSE"),             // Differential termination
        .IOSTANDARD ("TMDS_33"))
     ibufds_inst
       (.I          (DATA_IN_FROM_PINS_P  [pin_count]),
        .IB         (DATA_IN_FROM_PINS_N  [pin_count]),
        .O          (data_in_from_pins_int[pin_count]));
// Instantiate the serdes primitive
     ////------------------------------
     // local wire only for use in this generate loop
     wire cascade_shift;
     wire [sys_w-1:0] icascade;
     wire [sys_w-1:0] slave_shiftout;

     // decalare the iserdes
     ISERDES2
       #(.BITSLIP_ENABLE ("TRUE"),
         .DATA_RATE      ("SDR"),
         .DATA_WIDTH     (num_serial_bits),
         .INTERFACE_TYPE ("RETIMED"),
         .SERDES_MODE    ("MASTER"))
      iserdes2_master
       (.Q1         (iserdes_q[3][pin_count]),
        .Q2         (iserdes_q[2][pin_count]),
        .Q3         (iserdes_q[1][pin_count]),
        .Q4         (iserdes_q[0][pin_count]),
        .SHIFTOUT   (icascade[pin_count]),
        .INCDEC     (),
        .VALID      (),
         .BITSLIP     (bitslip),
        .CE0        (clock_enable),  // 1-bit Clock enable input
        .CLK0       (clk_in_int_buf),// 1-bit IO Clock network input. Optionally Invertible. This is the primary clock
                                     // input used when the clock doubler circuit is not engaged (see DATA_RATE
                                     // attribute).
        .CLK1       (1'b0),
         .CLKDIV         (clk_div_in),
        .D          (data_in_from_pins_int[pin_count]), // 1-bit Input signal from IOB.
        .IOCE       (serdesstrobe),                       // 1-bit Data strobe signal derived from BUFIO CE. Strobes data capture for
                                                          // NETWORKING and NETWORKING_PIPELINES alignment modes.

        .RST        (IO_RESET),        // 1-bit Asynchronous reset only.
        .SHIFTIN    (),
        // unused connections
        .FABRICOUT  (),
        .CFB0       (),
        .CFB1       (),
        .DFB        ());

     ISERDES2
       #(.BITSLIP_ENABLE ("TRUE"),
         .DATA_RATE      ("SDR"),
         .DATA_WIDTH     (num_serial_bits),
         .INTERFACE_TYPE ("RETIMED"),
         .SERDES_MODE    ("SLAVE"))

      iserdes2_slave
       (.Q1         (iserdes_q[7][pin_count]),
        .Q2         (iserdes_q[6][pin_count]),
        .Q3         (iserdes_q[5][pin_count]),
        .Q4         (iserdes_q[4][pin_count]),
        .SHIFTOUT   (),
        .INCDEC     (),
        .VALID      (),
         .BITSLIP     (bitslip),
        .CE0        (clock_enable),   // 1-bit Clock enable input
        .CLK0       (clk_in_int_buf), // 1-bit IO Clock network input. Optionally Invertible. This is the primary clock
                                      // input used when the clock doubler circuit is not engaged (see DATA_RATE
                                      // attribute).
        .CLK1       (1'b0),
         .CLKDIV         (clk_div_in),
        .D          (1'b0),           // 1-bit Input signal from IOB.
        .IOCE       (serdesstrobe),   // 1-bit Data strobe signal derived from BUFIO CE. Strobes data capture for
                                      // NETWORKING and NETWORKING_PIPELINES alignment modes.

        .RST        (IO_RESET),       // 1-bit Asynchronous reset only.
        .SHIFTIN    (icascade[pin_count]),
        // unused connections
        .FABRICOUT  (),
        .CFB0       (),
        .CFB1       (),
        .DFB        ());




     // Concatenate the serdes outputs together. Keep the timesliced
     //   bits together, and placing the earliest bits on the right
     //   ie, if data comes in 0, 1, 2, 3, 4, 5, 6, 7, ...
     //       the output will be 3210, 7654, ...
     ////---------------------------------------------------------
     for (slice_count = 0; slice_count < num_serial_bits; slice_count = slice_count + 1) begin: in_slices
        // This places the first data in time on the right
        assign data_in_to_device[slice_count*sys_w+:sys_w] =
          iserdes_q[num_serial_bits-slice_count-1];
        // To place the first data in time on the left, use the
        //   following code, instead
        // assign data_in_to_device2[slice_count*sys_w+:sys_w] =
        //   iserdes_q[slice_count];
     end
   
end
endgenerate

 
   // Instantiate the IO design
   ioip io_inst
    (
     // From the drive out to the system
     .DATA_OUT_FROM_DEVICE    (data_out_from_device),
     .DATA_OUT_TO_PINS_P      (DATA_OUT_TO_PINS_P),
     .DATA_OUT_TO_PINS_N      (DATA_OUT_TO_PINS_N),
     .CLK_IN                  (clk_in_pll1),
     .CLK_DIV_IN              (clk_div_in),
     .LOCKED_IN               (locked_in),
     .LOCKED_OUT              (locked_out),
     .IO_RESET                (rst_sync_int));

endmodule
