module adrv9001_tx #(
  parameter CMOS_LVDS_N = 0,
  parameter NUM_LANES = 4,
  parameter FPGA_TECHNOLOGY = 0,
  parameter USE_RX_CLK_FOR_TX = 0
) (
  input                   ref_clk,
  input                   up_clk,

  input                   mssi_sync,
  input                   tx_output_enable,

  // physical interface (transmit)
  output                  tx_dclk_out_n_NC,
  output                  tx_dclk_out_p_dclk_out,
  input                   tx_dclk_in_n_NC,
  input                   tx_dclk_in_p_dclk_in,
  output                  tx_idata_out_n_idata0,
  output                  tx_idata_out_p_idata1,
  output                  tx_qdata_out_n_qdata2,
  output                  tx_qdata_out_p_qdata3,
  output                  tx_strobe_out_n_NC,
  output                  tx_strobe_out_p_strobe_out,

  input                   rx_clk_div,
  input                   rx_clk,
  input                   rx_ssi_rst,

  // internal resets and clocks

  input                   dac_rst,
  output                  dac_clk_div,

  input       [7:0]       dac_data_0,
  input       [7:0]       dac_data_1,
  input       [7:0]       dac_data_2,
  input       [7:0]       dac_data_3,
  input       [7:0]       dac_data_strb,
  input       [7:0]       dac_data_clk,
  input                   dac_data_valid
);

  localparam  SEVEN_SERIES  = 1;
  localparam  ULTRASCALE  = 2;
  localparam  ULTRASCALE_PLUS  = 3;

  // internal wire
  wire                 tx_dclk_in_s;
  wire                 dac_fast_clk;
  wire [NUM_LANES-1:0] serdes_out_p;
  wire [NUM_LANES-1:0] serdes_out_n;
  wire [NUM_LANES-1:0] data_s0;
  wire [NUM_LANES-1:0] data_s1;
  wire [NUM_LANES-1:0] data_s2;
  wire [NUM_LANES-1:0] data_s3;
  wire [NUM_LANES-1:0] data_s4;
  wire [NUM_LANES-1:0] data_s5;
  wire [NUM_LANES-1:0] data_s6;
  wire [NUM_LANES-1:0] data_s7;
  wire                 ssi_rst;

  ad_serdes_out #(
    .CMOS_LVDS_N (CMOS_LVDS_N),
    .DDR_OR_SDR_N(1),
    .DATA_WIDTH(NUM_LANES),
    .SERDES_FACTOR(8),
    .FPGA_TECHNOLOGY (FPGA_TECHNOLOGY))
  i_serdes (
    .rst (dac_rst|ssi_rst),
    .clk (dac_fast_clk),
    .div_clk (dac_clk_div),
    .loaden (1'b0),
    .data_oe (tx_output_enable),
    .data_s0 (data_s0),
    .data_s1 (data_s1),
    .data_s2 (data_s2),
    .data_s3 (data_s3),
    .data_s4 (data_s4),
    .data_s5 (data_s5),
    .data_s6 (data_s6),
    .data_s7 (data_s7),
    .data_out_se (),
    .data_out_p (serdes_out_p),
    .data_out_n (serdes_out_n));

  generate
  if (CMOS_LVDS_N == 0) begin

    IBUFGDS i_dac_clk_in_ibuf (
      .I (tx_dclk_in_p_dclk_in),
      .IB (tx_dclk_in_n_NC),
      .O (tx_dclk_in_s));

    assign data_s0 = {dac_data_clk[7],dac_data_strb[7],dac_data_1[7],dac_data_0[7]};
    assign data_s1 = {dac_data_clk[6],dac_data_strb[6],dac_data_1[6],dac_data_0[6]};
    assign data_s2 = {dac_data_clk[5],dac_data_strb[5],dac_data_1[5],dac_data_0[5]};
    assign data_s3 = {dac_data_clk[4],dac_data_strb[4],dac_data_1[4],dac_data_0[4]};
    assign data_s4 = {dac_data_clk[3],dac_data_strb[3],dac_data_1[3],dac_data_0[3]};
    assign data_s5 = {dac_data_clk[2],dac_data_strb[2],dac_data_1[2],dac_data_0[2]};
    assign data_s6 = {dac_data_clk[1],dac_data_strb[1],dac_data_1[1],dac_data_0[1]};
    assign data_s7 = {dac_data_clk[0],dac_data_strb[0],dac_data_1[0],dac_data_0[0]};

    assign {tx_dclk_out_p_dclk_out,
            tx_strobe_out_p_strobe_out,
            tx_qdata_out_p_qdata3,
            tx_idata_out_p_idata1} = serdes_out_p;

    assign {tx_dclk_out_n_NC,
            tx_strobe_out_n_NC,
            tx_qdata_out_n_qdata2,
            tx_idata_out_n_idata0} = serdes_out_n;
  end else begin

    IBUF i_dac_clk_in_ibuf (
      .I (tx_dclk_in_p_dclk_in),
      .O (tx_dclk_in_s));

    assign data_s0 = {dac_data_clk[7],dac_data_strb[7],dac_data_3[7],dac_data_2[7],dac_data_1[7],dac_data_0[7]};
    assign data_s1 = {dac_data_clk[6],dac_data_strb[6],dac_data_3[6],dac_data_2[6],dac_data_1[6],dac_data_0[6]};
    assign data_s2 = {dac_data_clk[5],dac_data_strb[5],dac_data_3[5],dac_data_2[5],dac_data_1[5],dac_data_0[5]};
    assign data_s3 = {dac_data_clk[4],dac_data_strb[4],dac_data_3[4],dac_data_2[4],dac_data_1[4],dac_data_0[4]};
    assign data_s4 = {dac_data_clk[3],dac_data_strb[3],dac_data_3[3],dac_data_2[3],dac_data_1[3],dac_data_0[3]};
    assign data_s5 = {dac_data_clk[2],dac_data_strb[2],dac_data_3[2],dac_data_2[2],dac_data_1[2],dac_data_0[2]};
    assign data_s6 = {dac_data_clk[1],dac_data_strb[1],dac_data_3[1],dac_data_2[1],dac_data_1[1],dac_data_0[1]};
    assign data_s7 = {dac_data_clk[0],dac_data_strb[0],dac_data_3[0],dac_data_2[0],dac_data_1[0],dac_data_0[0]};

    assign {tx_dclk_out_p_dclk_out,
            tx_strobe_out_p_strobe_out,
            tx_qdata_out_p_qdata3,
            tx_qdata_out_n_qdata2,
            tx_idata_out_p_idata1,
            tx_idata_out_n_idata0} = serdes_out_p;

    assign {tx_dclk_out_n_NC,
            tx_strobe_out_n_NC} = 2'b0;
  end

  if (USE_RX_CLK_FOR_TX == 0) begin

    if (FPGA_TECHNOLOGY == SEVEN_SERIES) begin

      // SERDES fast clock
      BUFIO i_dac_clk_in_gbuf (
        .I (tx_dclk_in_s),
        .O (dac_fast_clk));

      // SERDES slow clock
      BUFR #(.BUFR_DIVIDE("4")) i_dac_div_clk_rbuf (
        .CLR (mssi_sync),
        .CE (1'b1),
        .I (tx_dclk_in_s),
        .O (dac_clk_div_s));

      BUFG I_bufg (
        .I (dac_clk_div_s),
        .O (dac_clk_div)
      );

      assign ssi_rst = mssi_sync;

    end else begin

      reg mssi_sync_d = 1'b0;
      reg mssi_sync_2d = 1'b0;
      always @(posedge dac_fast_clk) begin
        mssi_sync_d <= mssi_sync;
        mssi_sync_2d <= mssi_sync_d;
      end

      BUFGCE #(
         .CE_TYPE ("SYNC"),
         .IS_CE_INVERTED (1'b0),
         .IS_I_INVERTED (1'b0)
      ) i_dac_clk_in_gbuf (
         .O (dac_fast_clk),
         .CE (1'b1),
         .I (tx_dclk_in_s)
      );

      BUFGCE_DIV #(
         .BUFGCE_DIVIDE (4),
         .IS_CE_INVERTED (1'b0),
         .IS_CLR_INVERTED (1'b0),
         .IS_I_INVERTED (1'b0)
      ) i_dac_div_clk_rbuf (
         .O (dac_clk_div),
         .CE (1'b1),
         .CLR (mssi_sync_2d),
         .I (tx_dclk_in_s)
      );

      assign ssi_rst = mssi_sync_2d;

    end

  end else begin

    assign dac_fast_clk = rx_clk;
    assign dac_clk_div = rx_clk_div;
    assign ssi_rst = rx_ssi_rst;

  end

  endgenerate

endmodule