module ad_serdes_in #(

  parameter   FPGA_TECHNOLOGY = 0,
  parameter   CMOS_LVDS_N = 0,
  parameter   DDR_OR_SDR_N = 0,
  parameter   SERDES_FACTOR = 8,
  parameter   DATA_WIDTH = 16,
  parameter   DRP_WIDTH = 5,
  parameter   IODELAY_CTRL = 0,
  parameter   IODELAY_GROUP = "dev_if_delay_group",
  parameter   REFCLK_FREQUENCY = 200) (

  // reset and clocks

  input                           rst,
  input                           clk,
  input                           div_clk,
  input                           loaden,
  input   [ 7:0]                  phase,
  input                           locked,

  // data interface

  output  [(DATA_WIDTH-1):0]      data_s0,  // last bit received
  output  [(DATA_WIDTH-1):0]      data_s1,
  output  [(DATA_WIDTH-1):0]      data_s2,
  output  [(DATA_WIDTH-1):0]      data_s3,
  output  [(DATA_WIDTH-1):0]      data_s4,
  output  [(DATA_WIDTH-1):0]      data_s5,
  output  [(DATA_WIDTH-1):0]      data_s6,
  output  [(DATA_WIDTH-1):0]      data_s7,  // 1st bit received
  input   [(DATA_WIDTH-1):0]      data_in_p,
  input   [(DATA_WIDTH-1):0]      data_in_n,

  // delay-data interface

  input                           up_clk,
  input   [(DATA_WIDTH-1):0]      up_dld,
  input   [((DATA_WIDTH*DRP_WIDTH)-1):0]  up_dwdata,
  output  [((DATA_WIDTH*DRP_WIDTH)-1):0]  up_drdata,

  // delay-control interface

  input                           delay_clk,
  input                           delay_rst,
  output                          delay_locked);

  localparam  SEVEN_SERIES  = 1;
  localparam  ULTRASCALE  = 2;
  localparam  ULTRASCALE_PLUS  = 3;
  localparam  DATA_RATE = (DDR_OR_SDR_N) ? "DDR" : "SDR";

  localparam SIM_DEVICE = FPGA_TECHNOLOGY == SEVEN_SERIES ? "7SERIES" :
                          FPGA_TECHNOLOGY == ULTRASCALE ? "ULTRASCALE" :
                          FPGA_TECHNOLOGY == ULTRASCALE_PLUS ? "ULTRASCALE_PLUS" :
                          "UNSUPPORTED";

  localparam SIM_DEVICE_IDELAYCTRL = FPGA_TECHNOLOGY == SEVEN_SERIES ? "7SERIES" :
                          FPGA_TECHNOLOGY == ULTRASCALE ? "ULTRASCALE" :
                          FPGA_TECHNOLOGY == ULTRASCALE_PLUS ? "ULTRASCALE" :
                          "UNSUPPORTED";

  // internal signals

  wire    [(DATA_WIDTH-1):0]      data_in_ibuf_s;
  wire    [(DATA_WIDTH-1):0]      data_in_idelay_s;
  wire    [(DATA_WIDTH-1):0]      data_shift1_s;
  wire    [(DATA_WIDTH-1):0]      data_shift2_s;

  // delay controller

  generate
  if (IODELAY_CTRL == 1) begin
  (* IODELAY_GROUP = IODELAY_GROUP *)
  IDELAYCTRL #(
    .SIM_DEVICE(SIM_DEVICE_IDELAYCTRL)
  ) i_delay_ctrl (
    .RST (delay_rst),
    .REFCLK (delay_clk),
    .RDY (delay_locked));
  end else begin
  assign delay_locked = 1'b1;
  end
  endgenerate

  // received data interface: ibuf -> idelay -> iserdes
  genvar l_inst;
  generate
    for (l_inst = 0; l_inst <= (DATA_WIDTH-1); l_inst = l_inst + 1) begin: g_io
      if (CMOS_LVDS_N == 0) begin
        IBUFDS i_ibuf (
          .I (data_in_p[l_inst]),
          .IB (data_in_n[l_inst]),
          .O (data_in_ibuf_s[l_inst]));
       end else begin
        IBUF i_ibuf (
          .I (data_in_p[l_inst]),
          .O (data_in_ibuf_s[l_inst]));
       end
    end
  endgenerate

  generate if (FPGA_TECHNOLOGY == SEVEN_SERIES) begin
    for (l_inst = 0; l_inst <= (DATA_WIDTH-1); l_inst = l_inst + 1) begin: g_data

      (* IODELAY_GROUP = IODELAY_GROUP *)
      IDELAYE2 #(
        .CINVCTRL_SEL ("FALSE"),
        .DELAY_SRC ("IDATAIN"),
        .HIGH_PERFORMANCE_MODE ("FALSE"),
        .IDELAY_TYPE ("VAR_LOAD"),
        .IDELAY_VALUE (0),
        .REFCLK_FREQUENCY (REFCLK_FREQUENCY),
        .PIPE_SEL ("FALSE"),
        .SIGNAL_PATTERN ("DATA"))
      i_idelay (
        .CE (1'b0),
        .INC (1'b0),
        .DATAIN (1'b0),
        .LDPIPEEN (1'b0),
        .CINVCTRL (1'b0),
        .REGRST (1'b0),
        .C (up_clk),
        .IDATAIN (data_in_ibuf_s[l_inst]),
        .DATAOUT (data_in_idelay_s[l_inst]),
        .LD (up_dld[l_inst]),
        .CNTVALUEIN (up_dwdata[DRP_WIDTH*l_inst +: DRP_WIDTH]),
        .CNTVALUEOUT (up_drdata[DRP_WIDTH*l_inst +: DRP_WIDTH]));

      ISERDESE2  #(
        .DATA_RATE (DATA_RATE),
        .DATA_WIDTH (SERDES_FACTOR),
        .DYN_CLKDIV_INV_EN ("FALSE"),
        .DYN_CLK_INV_EN ("FALSE"),
        .INIT_Q1 (1'b0),
        .INIT_Q2 (1'b0),
        .INIT_Q3 (1'b0),
        .INIT_Q4 (1'b0),
        .INTERFACE_TYPE ("NETWORKING"),
        .IOBDELAY ("IFD"),
        .NUM_CE (2),
        .OFB_USED ("FALSE"),
        .SERDES_MODE ("MASTER"),
        .SRVAL_Q1 (1'b0),
        .SRVAL_Q2 (1'b0),
        .SRVAL_Q3 (1'b0),
        .SRVAL_Q4 (1'b0))
      i_iserdes (
        .O (),
        .Q1 (data_s0[l_inst]),
        .Q2 (data_s1[l_inst]),
        .Q3 (data_s2[l_inst]),
        .Q4 (data_s3[l_inst]),
        .Q5 (data_s4[l_inst]),
        .Q6 (data_s5[l_inst]),
        .Q7 (data_s6[l_inst]),
        .Q8 (data_s7[l_inst]),
        .SHIFTOUT1 (),
        .SHIFTOUT2 (),
        .BITSLIP (1'b0),
        .CE1 (1'b1),
        .CE2 (1'b1),
        .CLKDIVP (1'b0),
        .CLK (clk),
        .CLKB (~clk),
        .CLKDIV (div_clk),
        .OCLK (1'b0),
        .DYNCLKDIVSEL (1'b0),
        .DYNCLKSEL (1'b0),
        .D (1'b0),
        .DDLY (data_in_idelay_s[l_inst]),
        .OFB (1'b0),
        .OCLKB (1'b0),
        .RST (rst),
        .SHIFTIN1 (1'b0),
        .SHIFTIN2 (1'b0));
      end /* g_data */

    end
  endgenerate

  generate if (FPGA_TECHNOLOGY == ULTRASCALE || FPGA_TECHNOLOGY == ULTRASCALE_PLUS) begin


    for (l_inst = 0; l_inst <= (DATA_WIDTH-1); l_inst = l_inst + 1) begin: g_data

    wire   div_dld;
    reg [4:0] vtc_cnt = {5{1'b1}};

    sync_event  sync_load(
      .in_clk (up_clk),
      .in_event (up_dld[l_inst]),
      .out_clk (div_clk),
      .out_event (div_dld)
    );

    (* IODELAY_GROUP = IODELAY_GROUP *)
    IDELAYE3 #(
       .CASCADE ("NONE"),          // Cascade setting (MASTER, NONE, SLAVE_END, SLAVE_MIDDLE)
       .DELAY_FORMAT ("TIME"),     // Units of the DELAY_VALUE (COUNT, TIME)
       .DELAY_SRC ("IDATAIN"),     // Delay input (DATAIN, IDATAIN)
       .DELAY_TYPE ("VAR_LOAD"),   // Set the type of tap delay line (FIXED, VARIABLE, VAR_LOAD)
       .DELAY_VALUE (0),           // Input delay value setting
       .IS_CLK_INVERTED (1'b0),    // Optional inversion for CLK
       .IS_RST_INVERTED (1'b0),    // Optional inversion for RST
       .REFCLK_FREQUENCY (500.0),  // IDELAYCTRL clock input frequency in MHz (200.0-2667.0)
       .SIM_DEVICE (SIM_DEVICE),   // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,
                                   // ULTRASCALE_PLUS_ES2)
       .UPDATE_MODE ("ASYNC")      // Determines when updates to the delay will take effect (ASYNC, MANUAL, SYNC)
    )
    i_idelay(
       .CASC_OUT (),                                       // 1-bit output: Cascade delay output to ODELAY input cascade
       .CNTVALUEOUT (up_drdata[DRP_WIDTH*l_inst +: DRP_WIDTH]), // 9-bit output: Counter value output
       .DATAOUT (data_in_idelay_s[l_inst]),                // 1-bit output: Delayed data output
       .CASC_IN (1'b0),                                    // 1-bit input: Cascade delay input from slave ODELAY CASCADE_OUT
       .CASC_RETURN (1'b0),                                // 1-bit input: Cascade delay returning from slave ODELAY DATAOUT
       .CE (1'b0),                                         // 1-bit input: Active high enable increment/decrement input
       .CLK (div_clk),                                     // 1-bit input: Clock input
       .CNTVALUEIN (up_dwdata[DRP_WIDTH*l_inst +: DRP_WIDTH]),   // 9-bit input: Counter value input
       .DATAIN (1'b0),                                     // 1-bit input: Data input from the logic
       .EN_VTC (en_vtc),                                   // 1-bit input: Keep delay constant over VT
       .IDATAIN (data_in_ibuf_s[l_inst]),                  // 1-bit input: Data input from the IOBUF
       .INC (1'b0),                                        // 1-bit input: Increment / Decrement tap delay input
       .LOAD (ld_cnt),                                     // 1-bit input: Load DELAY_VALUE input
       .RST (rst)                                          // 1-bit input: Asynchronous Reset to the DELAY_VALUE
    );


    always @(posedge div_clk) begin
      if (div_dld) begin
        vtc_cnt <= 'h0;
      end else if (~(&vtc_cnt)) begin
        vtc_cnt <= vtc_cnt + 1;
      end
    end

    assign en_vtc = &vtc_cnt;
    assign ld_cnt = ~vtc_cnt[4] & (&vtc_cnt[3:0]);

    ISERDESE3 #(
       .DATA_WIDTH (8),            // Parallel data width (4,8)
       .FIFO_ENABLE ("FALSE"),     // Enables the use of the FIFO
       .FIFO_SYNC_MODE ("FALSE"),  // Enables the use of internal 2-stage synchronizers on the FIFO
       .IS_CLK_B_INVERTED (1'b0),  // Optional inversion for CLK_B
       .IS_CLK_INVERTED (1'b0),    // Optional inversion for CLK
       .IS_RST_INVERTED (1'b0),    // Optional inversion for RST
       .SIM_DEVICE (SIM_DEVICE)    // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,
                                   // ULTRASCALE_PLUS_ES2)
    )
    i_iserdes(
       .FIFO_EMPTY (),                // 1-bit output: FIFO empty flag
       .INTERNAL_DIVCLK (),           // 1-bit output: Internally divided down clock used when FIFO is
                                      // disabled (do not connect)

       .Q ({data_s0[l_inst],
            data_s1[l_inst],
            data_s2[l_inst],
            data_s3[l_inst],
            data_s4[l_inst],
            data_s5[l_inst],
            data_s6[l_inst],
            data_s7[l_inst]}),        // 8-bit registered output
       .CLK (clk),                    // 1-bit input: High-speed clock
       .CLKDIV (div_clk),             // 1-bit input: Divided Clock
       .CLK_B (~clk),                 // 1-bit input: Inversion of High-speed clock CLK
       .D (data_in_idelay_s[l_inst]), // 1-bit input: Serial Data Input
       .FIFO_RD_CLK (div_clk),        // 1-bit input: FIFO read clock
       .FIFO_RD_EN (1'b1),            // 1-bit input: Enables reading the FIFO when asserted
       .RST (rst)                     // 1-bit input: Asynchronous Reset
    );
   end
  end
  endgenerate

endmodule