module axi_ad9963_if #(

  // this parameter controls the buffer type based on the target device.

  parameter   FPGA_TECHNOLOGY = 0,
  parameter   ADC_IODELAY_ENABLE = 0,
  parameter   IO_DELAY_GROUP = "dev_if_delay_group",
  parameter   DELAY_REFCLK_FREQUENCY = 200) (

  // physical interface (receive)

  input               trx_clk,
  input               trx_iq,
  input       [11:0]  trx_data,

  // physical interface (transmit)

  input               tx_clk,
  output              tx_iq,
  output      [11:0]  tx_data,

  // clock (common to both receive and transmit)

  input               adc_rst,
  input               dac_rst,
  output              adc_clk,
  output              dac_clk,

  // receive data path interface

  output reg          adc_valid,
  output reg  [23:0]  adc_data,
  output reg          adc_status,
  input               up_adc_ce,

  // transmit data path interface

  input               out_valid_q,
  input               out_valid_i,
  input       [23:0]  dac_data,
  input               up_dac_ce,
  input               tx_sample_hold,

  // delay interface

  input               up_clk,
  input       [12:0]  up_adc_dld,
  input       [64:0]  up_adc_dwdata,
  output      [64:0]  up_adc_drdata,
  input               delay_clk,
  input               delay_rst,
  output              delay_locked);

  // internal registers

  reg     [11:0]  rx_data_p = 0;
  reg     [11:0]  tx_data_p = 'd0;
  reg     [11:0]  tx_data_n = 'd0;
  reg     [23:0]  constant_sample = 'd0;

  // internal signals

  wire    [11:0]  rx_data_p_s;
  wire    [11:0]  rx_data_n_s;
  wire            rx_iq_p_s;
  wire            rx_iq_n_s;

  wire            div_clk;

  genvar          l_inst;

  always @(posedge adc_clk) begin
    if( rx_iq_p_s == 1'b1) begin
      adc_data  <= {rx_data_n_s, rx_data_p_s} ;  // data[11:00] I
      adc_valid <= 1'b1;                        // data[23:12] Q
    end else begin
      rx_data_p <= rx_data_p_s;               // if this happens it means that risedge data is sampled on falledge
      adc_data  <= {rx_data_p, rx_data_n_s};  // so we take current N data with previous P data
      adc_valid <= 1'b1;                      // in order to have data sampled at the same instance sent to the DMA
    end
  end

  always @(posedge dac_clk) begin
    if (dac_rst == 1'b1) begin
        tx_data_p <= 24'd0;
        tx_data_n <= 24'd0;
        constant_sample <= 24'd0;
    end else begin
      if(out_valid_i == 1'b1) begin
        tx_data_p <= dac_data[11: 0];
        constant_sample[11: 0] <= tx_sample_hold ? dac_data[11: 0] : 12'd0;
      end else begin
        tx_data_p <= constant_sample[11:0] ;
      end
      if(out_valid_q == 1'b1) begin
        tx_data_n <= dac_data[23:12];
        constant_sample[23:12] <= tx_sample_hold ? dac_data[23:12] : 12'd0;
      end else begin
        tx_data_n <= constant_sample[23:12];
      end
    end
  end

  always @(posedge adc_clk) begin
    if (adc_rst == 1'b1) begin
      adc_status <= 1'b0;
    end else begin
      adc_status <= 1'b1;
    end
  end

  // device clock interface (receive clock)

  BUFGCTRL #(
      .INIT_OUT(0),
      .PRESELECT_I0("FALSE"),
      .PRESELECT_I1("FALSE")
      )
  bufgctrl_adc (
      .O(adc_clk),
      .CE0(1'b1),
      .CE1(1'b0),
      .I0(trx_clk),
      .I1(1'b0),
      .IGNORE0(1'b0),
      .IGNORE1(1'b0),
      .S0(up_adc_ce),
      .S1(1'b0)
      );

  // receive data interface, ibuf -> idelay -> iddr

  generate
  for (l_inst = 0; l_inst <= 11; l_inst = l_inst + 1) begin: g_rx_data
  ad_data_in #(
    .SINGLE_ENDED (1),
    .FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
    .IODELAY_ENABLE (ADC_IODELAY_ENABLE),
    .IODELAY_CTRL (0),
    .IODELAY_GROUP (IO_DELAY_GROUP),
    .REFCLK_FREQUENCY (DELAY_REFCLK_FREQUENCY))
  i_rx_data (
    .rx_clk (adc_clk),
    .rx_data_in_p (trx_data[l_inst]),
    .rx_data_in_n (1'b0),
    .rx_data_p (rx_data_p_s[l_inst]),
    .rx_data_n (rx_data_n_s[l_inst]),
    .up_clk (up_clk),
    .up_dld (up_adc_dld[l_inst]),
    .up_dwdata (up_adc_dwdata[((l_inst*5)+4):(l_inst*5)]),
    .up_drdata (up_adc_drdata[((l_inst*5)+4):(l_inst*5)]),
    .delay_clk (delay_clk),
    .delay_rst (delay_rst),
    .delay_locked ());
  end
  endgenerate

  // receive iq interface, ibuf -> idelay -> iddr

  ad_data_in #(
    .SINGLE_ENDED (1),
    .FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
    .IODELAY_ENABLE (ADC_IODELAY_ENABLE),
    .IODELAY_CTRL (1),
    .IODELAY_GROUP (IO_DELAY_GROUP))
  i_rx_iq (
    .rx_clk (adc_clk),
    .rx_data_in_p (trx_iq),
    .rx_data_in_n (1'b0),
    .rx_data_p (rx_iq_p_s),
    .rx_data_n (rx_iq_n_s),
    .up_clk (up_clk),
    .up_dld (up_adc_dld[12]),
    .up_dwdata (up_adc_dwdata[64:60]),
    .up_drdata (up_adc_drdata[64:60]),
    .delay_clk (delay_clk),
    .delay_rst (delay_rst),
    .delay_locked (delay_locked));

  // transmit data interface

  BUFR #(.BUFR_DIVIDE(2)) i_div_clk_buf (
    .CLR (1'b0),
    .CE (1'b1),
    .I (tx_clk),
    .O (div_clk));

  BUFGCTRL #(
    .INIT_OUT(0),
    .PRESELECT_I0("FALSE"),
    .PRESELECT_I1("FALSE")
    )
  bufgctrl_dac (
    .O(dac_clk),
    .CE0(1'b1),
    .CE1(1'b0),
    .I0(div_clk),
    .I1(1'b0),
    .IGNORE0(1'b0),
    .IGNORE1(1'b0),
    .S0(up_dac_ce),
    .S1(1'b0)
    );

  generate
  for (l_inst = 0; l_inst <= 11; l_inst = l_inst + 1) begin: g_tx_data
  ODDR #(
    .DDR_CLK_EDGE ("SAME_EDGE"),
    .INIT (1'b0),
    .SRTYPE ("SYNC"))
  i_tx_data_oddr (
    .CE (1'b1),
    .R (dac_rst),
    .S (1'b0),
    .C (dac_clk),
    .D1 (tx_data_p[l_inst]),
    .D2 (tx_data_n[l_inst]),
    .Q (tx_data[l_inst]));
    end
  endgenerate

  ODDR #(
    .DDR_CLK_EDGE ("SAME_EDGE"),
    .INIT (1'b0),
    .SRTYPE ("SYNC"))
  i_tx_data_oddr (
    .CE (1'b1),
    .R (dac_rst),
    .S (1'b0),
    .C (dac_clk),
    .D1 (1'b1),
    .D2 (1'b0),
    .Q (tx_iq));

endmodule