module e310_io (
  input areset,
  input mimo,
  // Baseband sample interface
  output radio_clk,
  output radio_rst,
  output reg [11:0] rx_i0,
  output reg [11:0] rx_q0,
  output reg [11:0] rx_i1,
  output reg [11:0] rx_q1,
  output reg rx_stb,
  input [11:0] tx_i0,
  input [11:0] tx_q0,
  input [11:0] tx_i1,
  input [11:0] tx_q1,
  output reg tx_stb,
  // AD9361 interface
  input rx_clk,
  input rx_frame,
  input [11:0] rx_data,
  output tx_clk,
  output tx_frame,
  output [11:0] tx_data
);

  // Synchronize asynchronous reset and MIMO
  synchronizer #(.STAGES(3), .INITIAL_VAL(1'b1)) sychronizer_radio_rst (
    .clk(radio_clk), .rst(areset), .in(1'b0), .out(radio_rst));

  (* mark_debug = "true" *) wire mimo_sync;
  synchronizer synchronizer_mimo (.clk(radio_clk), .rst(radio_rst), .in(mimo), .out(mimo_sync));

  /****************************************************************************
  ** RX Capture Interface
  ****************************************************************************/
  wire rx_clk_bufr; // Capture clock
  BUFR bufr_rx_clk (.I(rx_clk), .O(rx_clk_bufr));
  BUFG bufg_radio_clk (.I(rx_clk_bufr), .O(radio_clk));

  (* mark_debug = "true" *) wire [11:0] rx_i, rx_q;
  genvar n;
  generate
    for (n = 0; n < 12; n = n + 1) begin
      IDDR #(.DDR_CLK_EDGE("SAME_EDGE")) iddr (
        .C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
        .D(rx_data[n]), .Q1(rx_q[n]), .Q2(rx_i[n]));
    end
  endgenerate

  (* mark_debug = "true" *) wire rx_frame_rising, rx_frame_falling;
  IDDR #(.DDR_CLK_EDGE("SAME_EDGE")) iddr_frame (
    .C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
    .D(rx_frame), .Q1(rx_frame_rising), .Q2(rx_frame_falling));

  always @(posedge radio_clk or posedge radio_rst) begin
    if (radio_rst) begin
      rx_stb <= 1'b0;
    end else begin
      if (mimo_sync) begin
        if (rx_frame_rising) begin
          rx_i0 <= rx_i;
          rx_q0 <= rx_q;
        end else begin
          rx_i1 <= rx_i;
          rx_q1 <= rx_q;
        end
        rx_stb  <= ~rx_frame_rising;
      end else begin
        rx_i0   <= rx_i;
        rx_q0   <= rx_q;
        rx_i1   <= rx_i;
        rx_q1   <= rx_q;
        rx_stb  <= 1'b1;
      end
    end
  end

  /****************************************************************************
  ** TX Output Interface
  ****************************************************************************/
  (* mark_debug = "true" *) reg [11:0] tx_i, tx_q;
  (* mark_debug = "true" *) reg tx_frame_int = 1'b1;
  generate
    for (n = 0; n < 12; n = n + 1) begin
      ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr (
        .C(radio_clk), .CE(1'b1), .R(1'b0), .S(1'b0),
        .D1(tx_i[n]), .D2(tx_q[n]), .Q(tx_data[n]));
    end
  endgenerate

  ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_frame (
    .C(radio_clk), .CE(1'b1), .R(1'b0), .S(1'b0),
    // In SISO mode, TX frame is asserted only on the falling edge
    .D1(tx_frame_int), .D2(tx_frame_int & mimo_sync), .Q(tx_frame));

  ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_clk (
    .C(radio_clk), .CE(1'b1), .R(1'b0), .S(1'b0),
    .D1(1'b1), .D2(1'b0), .Q(tx_clk));

  (* mark_debug = "true" *) reg [11:0] tx_i1_hold, tx_q1_hold;
  always @(posedge radio_clk or posedge radio_rst) begin
    if (radio_rst) begin
      tx_stb       <= 1'b0;
      tx_frame_int <= 1'b1;
    end else begin
      if (mimo_sync) begin
        tx_stb       <= ~tx_stb;
        tx_frame_int <= tx_stb;
        if (tx_stb) begin
          tx_i       <= tx_i0;
          tx_q       <= tx_q0;
          tx_i1_hold <= tx_i1;
          tx_q1_hold <= tx_q1;
        end else begin
          tx_i       <= tx_i1_hold;
          tx_q       <= tx_q1_hold;
        end
      end else begin
        tx_stb       <= 1'b1;
        tx_frame_int <= 1'b1;
        if ({tx_i0,tx_q0} != 24'd0) begin
          tx_i <= tx_i0;
          tx_q <= tx_q0;
        end else begin
          tx_i <= tx_i1;
          tx_q <= tx_q1;
        end
      end
    end
  end

endmodule