//
// Copyright 2019 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rfnoc_block_radio
//
// Description:  This is the top-level file for the RFNoC radio block.
//
// Parameters:
//
//   THIS_PORTID      : CTRL port ID to which this block is connected
//   CHDR_W           : CHDR AXI-Stream data bus width
//   NIPC             : Number of radio samples per radio clock cycle
//   ITEM_W           : Radio sample width
//   NUM_PORTS        : Number of radio channels (RX/TX pairs)
//   EN_COMP_GAIN_TX  : Enable complex gain feature for TX path
//   EN_COMP_GAIN_RX  : Enable complex gain feature for RX path
//   EN_FIFO_OUT_REG  : Enable output register on shift register based FIFOs
//                      to enable higher clock frequencies. Requires more
//                      resources.
//   MTU              : Maximum transmission unit (i.e., maximum packet size)
//                      in CHDR words is 2**MTU.
//   PERIPH_BASE_ADDR : CTRL port peripheral window base address
//   PERIPH_ADDR_W    : CTRL port peripheral address space = 2**PERIPH_ADDR_W
//   SKIP_WR_ACK_WAIT : When set to 1, the radio will not wait for responses to
//                      control messages (i.e., overrun and late commands async
//                      messages). This is useful in situations where packet
//                      loss is expected. Enabling this will prevent the RX
//                      state machine from hanging due to dropped packets.
//


module rfnoc_block_radio #(
  parameter THIS_PORTID           = 0,
  parameter CHDR_W                = 64,
  parameter NIPC                  = 1,
  parameter ITEM_W                = 32,
  parameter NUM_PORTS             = 2,
  parameter EN_COMP_GAIN_TX       = 0,
  parameter EN_COMP_GAIN_RX       = 0,
  parameter EN_FIFO_OUT_REG       = 0,
  parameter MTU                   = 10,
  parameter PERIPH_BASE_ADDR      = 20'h80000,
  parameter PERIPH_ADDR_W         = 19,
  parameter [5:0] CTRL_CLK_IDX    = 6'h3F,
  parameter [5:0] TB_CLK_IDX      = 6'h3F,
  parameter SKIP_WR_ACK_WAIT      = 0
) (
  //---------------------------------------------------------------------------
  // AXIS CHDR Port
  //---------------------------------------------------------------------------

  input wire rfnoc_chdr_clk,

  // CHDR inputs from framework
  input  wire [CHDR_W*NUM_PORTS-1:0] s_rfnoc_chdr_tdata,
  input  wire [       NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
  input  wire [       NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
  output wire [       NUM_PORTS-1:0] s_rfnoc_chdr_tready,

  // CHDR outputs to framework
  output wire [CHDR_W*NUM_PORTS-1:0] m_rfnoc_chdr_tdata,
  output wire [       NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
  output wire [       NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
  input  wire [       NUM_PORTS-1:0] m_rfnoc_chdr_tready,

  // Backend interface
  input  wire [511:0] rfnoc_core_config,
  output wire [511:0] rfnoc_core_status,


  //---------------------------------------------------------------------------
  // AXIS CTRL Port
  //---------------------------------------------------------------------------

  input wire rfnoc_ctrl_clk,

  // CTRL port requests from framework
  input  wire [31:0] s_rfnoc_ctrl_tdata,
  input  wire        s_rfnoc_ctrl_tlast,
  input  wire        s_rfnoc_ctrl_tvalid,
  output wire        s_rfnoc_ctrl_tready,

  // CTRL port requests to framework
  output wire [31:0] m_rfnoc_ctrl_tdata,
  output wire        m_rfnoc_ctrl_tlast,
  output wire        m_rfnoc_ctrl_tvalid,
  input  wire        m_rfnoc_ctrl_tready,


  //---------------------------------------------------------------------------
  // CTRL Port Peripheral Interface
  //---------------------------------------------------------------------------

  output wire        m_ctrlport_req_wr,
  output wire        m_ctrlport_req_rd,
  output wire [19:0] m_ctrlport_req_addr,
  output wire [31:0] m_ctrlport_req_data,
  output wire [ 3:0] m_ctrlport_req_byte_en,
  output wire        m_ctrlport_req_has_time,
  output wire [63:0] m_ctrlport_req_time,
  input  wire        m_ctrlport_resp_ack,
  input  wire [ 1:0] m_ctrlport_resp_status,
  input  wire [31:0] m_ctrlport_resp_data,


  //---------------------------------------------------------------------------
  // Radio Interface
  //---------------------------------------------------------------------------

  input wire radio_clk,

  // Timekeeper interface
  input wire [63:0] radio_time,

  // Radio Rx interface
  input  wire [(ITEM_W*NIPC)*NUM_PORTS-1:0] radio_rx_data,
  input  wire [              NUM_PORTS-1:0] radio_rx_stb,
  output wire [              NUM_PORTS-1:0] radio_rx_running,

  // Radio Tx interface
  output wire [(ITEM_W*NIPC)*NUM_PORTS-1:0] radio_tx_data,
  input  wire [              NUM_PORTS-1:0] radio_tx_stb,
  output wire [              NUM_PORTS-1:0] radio_tx_running,

  // Radio states
  output wire [              NUM_PORTS-1:0] radio_state_rx_running,
  output wire [              NUM_PORTS-1:0] radio_state_tx_running
);

  `include "rfnoc_block_radio_regs.vh"
  `include "../../core/rfnoc_axis_ctrl_utils.vh"

  localparam RADIO_W = NIPC*ITEM_W;


  // Radio Tx data stream
  wire [RADIO_W*NUM_PORTS-1:0] axis_tx_tdata;
  wire [        NUM_PORTS-1:0] axis_tx_tlast;
  wire [        NUM_PORTS-1:0] axis_tx_tvalid;
  wire [        NUM_PORTS-1:0] axis_tx_tready;
  wire [     64*NUM_PORTS-1:0] axis_tx_ttimestamp;
  wire [        NUM_PORTS-1:0] axis_tx_thas_time;
  wire [        NUM_PORTS-1:0] axis_tx_teob;

  // Radio Rx data stream
  wire [RADIO_W*NUM_PORTS-1:0] axis_rx_tdata;
  wire [        NUM_PORTS-1:0] axis_rx_tlast;
  wire [        NUM_PORTS-1:0] axis_rx_tvalid;
  wire [        NUM_PORTS-1:0] axis_rx_tready;
  wire [     64*NUM_PORTS-1:0] axis_rx_ttimestamp;
  wire [        NUM_PORTS-1:0] axis_rx_thas_time;
  wire [        NUM_PORTS-1:0] axis_rx_teob;

  // Control port signals used for register access (NoC shell masters user logic)
  wire        ctrlport_reg_req_wr;
  wire        ctrlport_reg_req_rd;
  wire [19:0] ctrlport_reg_req_addr;
  wire        ctrlport_reg_has_time;
  wire [63:0] ctrlport_reg_time;
  wire [31:0] ctrlport_reg_req_data;
  wire [ 3:0] ctrlport_reg_req_byte_en;
  wire [31:0] ctrlport_reg_resp_data;
  wire        ctrlport_reg_resp_ack;
  wire [ 1:0] ctrlport_reg_resp_status;

  // Control port signals used for error reporting (user logic masters to NoC shell)
  wire        ctrlport_err_req_wr;
  wire [19:0] ctrlport_err_req_addr;
  wire [ 9:0] ctrlport_err_req_portid;
  wire [15:0] ctrlport_err_req_rem_epid;
  wire [ 9:0] ctrlport_err_req_rem_portid;
  wire [31:0] ctrlport_err_req_data;
  wire        ctrlport_err_req_has_time;
  wire [63:0] ctrlport_err_req_time;
  wire        ctrlport_err_resp_ack;


  //---------------------------------------------------------------------------
  // NoC Shell
  //---------------------------------------------------------------------------

  wire radio_rst;

  noc_shell_radio #(
    .THIS_PORTID      (THIS_PORTID),
    .CHDR_W           (CHDR_W),
    .CTRL_CLK_IDX     (CTRL_CLK_IDX),
    .TB_CLK_IDX       (TB_CLK_IDX),
    .MTU              (MTU),
    .NUM_PORTS        (NUM_PORTS),
    .NIPC             (NIPC),
    .ITEM_W           (ITEM_W),
    .SKIP_WR_ACK_WAIT (SKIP_WR_ACK_WAIT)
  ) noc_shell_radio_i (
    .rfnoc_chdr_clk            (rfnoc_chdr_clk),
    .rfnoc_ctrl_clk            (rfnoc_ctrl_clk),
    .radio_clk                 (radio_clk),
    .rfnoc_chdr_rst            (),
    .rfnoc_ctrl_rst            (),
    .radio_rst                 (radio_rst),
    .rfnoc_core_config         (rfnoc_core_config),
    .rfnoc_core_status         (rfnoc_core_status),
    .s_rfnoc_chdr_tdata        (s_rfnoc_chdr_tdata),
    .s_rfnoc_chdr_tlast        (s_rfnoc_chdr_tlast),
    .s_rfnoc_chdr_tvalid       (s_rfnoc_chdr_tvalid),
    .s_rfnoc_chdr_tready       (s_rfnoc_chdr_tready),
    .m_rfnoc_chdr_tdata        (m_rfnoc_chdr_tdata),
    .m_rfnoc_chdr_tlast        (m_rfnoc_chdr_tlast),
    .m_rfnoc_chdr_tvalid       (m_rfnoc_chdr_tvalid),
    .m_rfnoc_chdr_tready       (m_rfnoc_chdr_tready),
    .s_rfnoc_ctrl_tdata        (s_rfnoc_ctrl_tdata),
    .s_rfnoc_ctrl_tlast        (s_rfnoc_ctrl_tlast),
    .s_rfnoc_ctrl_tvalid       (s_rfnoc_ctrl_tvalid),
    .s_rfnoc_ctrl_tready       (s_rfnoc_ctrl_tready),
    .m_rfnoc_ctrl_tdata        (m_rfnoc_ctrl_tdata),
    .m_rfnoc_ctrl_tlast        (m_rfnoc_ctrl_tlast),
    .m_rfnoc_ctrl_tvalid       (m_rfnoc_ctrl_tvalid),
    .m_rfnoc_ctrl_tready       (m_rfnoc_ctrl_tready),
    .ctrlport_clk              (),
    .ctrlport_rst              (),
    .m_ctrlport_req_wr         (ctrlport_reg_req_wr),
    .m_ctrlport_req_rd         (ctrlport_reg_req_rd),
    .m_ctrlport_req_addr       (ctrlport_reg_req_addr),
    .m_ctrlport_req_data       (ctrlport_reg_req_data),
    .m_ctrlport_req_byte_en    (ctrlport_reg_req_byte_en),
    .m_ctrlport_req_has_time   (ctrlport_reg_has_time),
    .m_ctrlport_req_time       (ctrlport_reg_time),
    .m_ctrlport_resp_ack       (ctrlport_reg_resp_ack),
    .m_ctrlport_resp_status    (ctrlport_reg_resp_status),
    .m_ctrlport_resp_data      (ctrlport_reg_resp_data),
    .s_ctrlport_req_wr         (ctrlport_err_req_wr),
    .s_ctrlport_req_rd         (1'b0),
    .s_ctrlport_req_addr       (ctrlport_err_req_addr),
    .s_ctrlport_req_portid     (ctrlport_err_req_portid),
    .s_ctrlport_req_rem_epid   (ctrlport_err_req_rem_epid),
    .s_ctrlport_req_rem_portid (ctrlport_err_req_rem_portid),
    .s_ctrlport_req_data       (ctrlport_err_req_data),
    .s_ctrlport_req_byte_en    (4'hF),
    .s_ctrlport_req_has_time   (ctrlport_err_req_has_time),
    .s_ctrlport_req_time       (ctrlport_err_req_time),
    .s_ctrlport_resp_ack       (ctrlport_err_resp_ack),
    .s_ctrlport_resp_status    (),
    .s_ctrlport_resp_data      (),
    .axis_data_clk             (),
    .axis_data_rst             (),
    .m_in_axis_tdata           (axis_tx_tdata),
    .m_in_axis_tkeep           (),                        // Radio only transmits full words
    .m_in_axis_tlast           (axis_tx_tlast),
    .m_in_axis_tvalid          (axis_tx_tvalid),
    .m_in_axis_tready          (axis_tx_tready),
    .m_in_axis_ttimestamp      (axis_tx_ttimestamp),
    .m_in_axis_thas_time       (axis_tx_thas_time),
    .m_in_axis_tlength         (),
    .m_in_axis_teov            (),
    .m_in_axis_teob            (axis_tx_teob),
    .s_out_axis_tdata          (axis_rx_tdata),
    .s_out_axis_tkeep          ({NUM_PORTS*NIPC{1'b1}}),  // Radio only receives full words
    .s_out_axis_tlast          (axis_rx_tlast),
    .s_out_axis_tvalid         (axis_rx_tvalid),
    .s_out_axis_tready         (axis_rx_tready),
    .s_out_axis_ttimestamp     (axis_rx_ttimestamp),
    .s_out_axis_thas_time      (axis_rx_thas_time),
    .s_out_axis_teov           ({NUM_PORTS{1'b0}}),
    .s_out_axis_teob           (axis_rx_teob)
  );


  //---------------------------------------------------------------------------
  // Decode Control Port Addresses
  //---------------------------------------------------------------------------
  //
  // This block splits the NoC shell's single master control port interface
  // into three masters, connected to the shared registers, radio cores, and
  // the external CTRL port peripheral interface. The responses from each of
  // these are merged into a single response and sent back to the NoC shell.
  //
  //---------------------------------------------------------------------------

  wire        ctrlport_shared_req_wr;
  wire        ctrlport_shared_req_rd;
  wire [19:0] ctrlport_shared_req_addr;
  wire [31:0] ctrlport_shared_req_data;
  wire [ 3:0] ctrlport_shared_req_byte_en;
  wire        ctrlport_shared_req_has_time;
  wire [63:0] ctrlport_shared_req_time;
  wire        ctrlport_shared_resp_ack;
  wire [31:0] ctrlport_shared_resp_data;

  wire        ctrlport_core_req_wr;
  wire        ctrlport_core_req_rd;
  wire [19:0] ctrlport_core_req_addr;
  wire [31:0] ctrlport_core_req_data;
  wire [ 3:0] ctrlport_core_req_byte_en;
  wire        ctrlport_core_req_has_time;
  wire [63:0] ctrlport_core_req_time;
  wire        ctrlport_core_resp_ack;
  wire [ 1:0] ctrlport_core_resp_status;
  wire [31:0] ctrlport_core_resp_data;

  ctrlport_decoder_param #(
    .NUM_SLAVES (3),
    .PORT_BASE  ({PERIPH_BASE_ADDR, RADIO_BASE_ADDR,                  SHARED_BASE_ADDR}),
    .PORT_ADDR_W({PERIPH_ADDR_W,    RADIO_ADDR_W + $clog2(NUM_PORTS), SHARED_ADDR_W})
  ) ctrlport_decoder_param_i (
    .ctrlport_clk            (radio_clk),
    .ctrlport_rst            (radio_rst),
    .s_ctrlport_req_wr       (ctrlport_reg_req_wr),
    .s_ctrlport_req_rd       (ctrlport_reg_req_rd),
    .s_ctrlport_req_addr     (ctrlport_reg_req_addr),
    .s_ctrlport_req_data     (ctrlport_reg_req_data),
    .s_ctrlport_req_byte_en  (ctrlport_reg_req_byte_en),
    .s_ctrlport_req_has_time (ctrlport_reg_has_time),
    .s_ctrlport_req_time     (ctrlport_reg_time),
    .s_ctrlport_resp_ack     (ctrlport_reg_resp_ack),
    .s_ctrlport_resp_status  (ctrlport_reg_resp_status),
    .s_ctrlport_resp_data    (ctrlport_reg_resp_data),
    .m_ctrlport_req_wr       ({m_ctrlport_req_wr,
                               ctrlport_core_req_wr,
                               ctrlport_shared_req_wr}),
    .m_ctrlport_req_rd       ({m_ctrlport_req_rd,
                               ctrlport_core_req_rd,
                               ctrlport_shared_req_rd}),
    .m_ctrlport_req_addr     ({m_ctrlport_req_addr,
                               ctrlport_core_req_addr,
                               ctrlport_shared_req_addr}),
    .m_ctrlport_req_data     ({m_ctrlport_req_data,
                               ctrlport_core_req_data,
                               ctrlport_shared_req_data}),
    .m_ctrlport_req_byte_en  ({m_ctrlport_req_byte_en,
                               ctrlport_core_req_byte_en,
                               ctrlport_shared_req_byte_en}),
    .m_ctrlport_req_has_time ({m_ctrlport_req_has_time,
                               ctrlport_core_req_has_time,
                               ctrlport_shared_req_has_time}),
    .m_ctrlport_req_time     ({m_ctrlport_req_time,
                               ctrlport_core_req_time,
                               ctrlport_shared_req_time}),
    .m_ctrlport_resp_ack     ({m_ctrlport_resp_ack,
                               ctrlport_core_resp_ack,
                               ctrlport_shared_resp_ack}),
    .m_ctrlport_resp_status  ({m_ctrlport_resp_status,
                              ctrlport_core_resp_status,
                              2'b00}),
    .m_ctrlport_resp_data    ({m_ctrlport_resp_data,
                               ctrlport_core_resp_data,
                               ctrlport_shared_resp_data
                               })
  );


  //---------------------------------------------------------------------------
  // Split Radio Control Port Interfaces
  //---------------------------------------------------------------------------

  wire [   NUM_PORTS-1:0] ctrlport_radios_req_wr;
  wire [   NUM_PORTS-1:0] ctrlport_radios_req_rd;
  wire [20*NUM_PORTS-1:0] ctrlport_radios_req_addr;
  wire [32*NUM_PORTS-1:0] ctrlport_radios_req_data;
  wire [   NUM_PORTS-1:0] ctrlport_radios_req_has_time;
  wire [64*NUM_PORTS-1:0] ctrlport_radios_req_time;
  wire [   NUM_PORTS-1:0] ctrlport_radios_resp_ack;
  wire [ 2*NUM_PORTS-1:0] ctrlport_radios_resp_status;
  wire [32*NUM_PORTS-1:0] ctrlport_radios_resp_data;

  ctrlport_decoder #(
    .NUM_SLAVES   (NUM_PORTS),
    .BASE_ADDR    (0),
    .SLAVE_ADDR_W (RADIO_ADDR_W)
  ) ctrlport_decoder_i (
    .ctrlport_clk            (radio_clk),
    .ctrlport_rst            (radio_rst),
    .s_ctrlport_req_wr       (ctrlport_core_req_wr),
    .s_ctrlport_req_rd       (ctrlport_core_req_rd),
    .s_ctrlport_req_addr     (ctrlport_core_req_addr),
    .s_ctrlport_req_data     (ctrlport_core_req_data),
    .s_ctrlport_req_byte_en  (4'hF),
    .s_ctrlport_req_has_time (ctrlport_core_req_has_time),
    .s_ctrlport_req_time     (ctrlport_core_req_time),
    .s_ctrlport_resp_ack     (ctrlport_core_resp_ack),
    .s_ctrlport_resp_status  (ctrlport_core_resp_status),
    .s_ctrlport_resp_data    (ctrlport_core_resp_data),
    .m_ctrlport_req_wr       (ctrlport_radios_req_wr),
    .m_ctrlport_req_rd       (ctrlport_radios_req_rd),
    .m_ctrlport_req_addr     (ctrlport_radios_req_addr),
    .m_ctrlport_req_data     (ctrlport_radios_req_data),
    .m_ctrlport_req_byte_en  (),
    .m_ctrlport_req_has_time (ctrlport_radios_req_has_time),
    .m_ctrlport_req_time     (ctrlport_radios_req_time),
    .m_ctrlport_resp_ack     (ctrlport_radios_resp_ack),
    .m_ctrlport_resp_status  (ctrlport_radios_resp_status),
    .m_ctrlport_resp_data    (ctrlport_radios_resp_data)
  );


  //---------------------------------------------------------------------------
  // Merge Control Port Interfaces
  //---------------------------------------------------------------------------
  //
  // This block merges the master control port interfaces of all radio_cores
  // into a single master for the NoC shell.
  //
  //---------------------------------------------------------------------------

  wire [   NUM_PORTS-1:0] ctrlport_err_radio_req_wr;
  wire [20*NUM_PORTS-1:0] ctrlport_err_radio_req_addr;
  wire [10*NUM_PORTS-1:0] ctrlport_err_radio_req_portid;
  wire [16*NUM_PORTS-1:0] ctrlport_err_radio_req_rem_epid;
  wire [10*NUM_PORTS-1:0] ctrlport_err_radio_req_rem_portid;
  wire [32*NUM_PORTS-1:0] ctrlport_err_radio_req_data;
  wire [   NUM_PORTS-1:0] ctrlport_err_radio_req_has_time;
  wire [64*NUM_PORTS-1:0] ctrlport_err_radio_req_time;
  wire [   NUM_PORTS-1:0] ctrlport_err_radio_resp_ack;

  ctrlport_combiner #(
    .NUM_MASTERS (NUM_PORTS),
    .PRIORITY    (0)
  ) ctrlport_combiner_i (
    .ctrlport_clk              (radio_clk),
    .ctrlport_rst              (radio_rst),
    .s_ctrlport_req_wr         (ctrlport_err_radio_req_wr),
    .s_ctrlport_req_rd         ({NUM_PORTS{1'b0}}),
    .s_ctrlport_req_addr       (ctrlport_err_radio_req_addr),
    .s_ctrlport_req_portid     (ctrlport_err_radio_req_portid),
    .s_ctrlport_req_rem_epid   (ctrlport_err_radio_req_rem_epid),
    .s_ctrlport_req_rem_portid (ctrlport_err_radio_req_rem_portid),
    .s_ctrlport_req_data       (ctrlport_err_radio_req_data),
    .s_ctrlport_req_byte_en    ({4*NUM_PORTS{1'b1}}),
    .s_ctrlport_req_has_time   (ctrlport_err_radio_req_has_time),
    .s_ctrlport_req_time       (ctrlport_err_radio_req_time),
    .s_ctrlport_resp_ack       (ctrlport_err_radio_resp_ack),
    .s_ctrlport_resp_status    (),
    .s_ctrlport_resp_data      (),
    .m_ctrlport_req_wr         (ctrlport_err_req_wr),
    .m_ctrlport_req_rd         (),
    .m_ctrlport_req_addr       (ctrlport_err_req_addr),
    .m_ctrlport_req_portid     (ctrlport_err_req_portid),
    .m_ctrlport_req_rem_epid   (ctrlport_err_req_rem_epid),
    .m_ctrlport_req_rem_portid (ctrlport_err_req_rem_portid),
    .m_ctrlport_req_data       (ctrlport_err_req_data),
    .m_ctrlport_req_byte_en    (),
    .m_ctrlport_req_has_time   (ctrlport_err_req_has_time),
    .m_ctrlport_req_time       (ctrlport_err_req_time),
    .m_ctrlport_resp_ack       (ctrlport_err_resp_ack),
    .m_ctrlport_resp_status    (2'b0),
    .m_ctrlport_resp_data      (32'b0)
  );


  //---------------------------------------------------------------------------
  // Control Port Timer for Shared Registers
  //---------------------------------------------------------------------------
  //
  // Makes sure shared register access waits for the time if one is provided.
  // This allows timed command packets to be tested by reading the time using
  // command packets with a time supplied.
  //
  //---------------------------------------------------------------------------

  wire        ctrlport_timer_req_rd;
  wire [19:0] ctrlport_timer_req_addr;
  reg         ctrlport_timer_resp_ack     = 1'b0;
  reg  [31:0] ctrlport_timer_resp_data    = 0;

  ctrlport_timer ctrlport_timer_i (
    // Clocks and Resets
    .clk(radio_clk),
    .rst(radio_rst),
    // Timestamp (synchronous to radio_clk)
    .time_now(radio_time),
    .time_now_stb(1'b1),
    .time_ignore_bits($clog2(NIPC)),
    // Control Port Master (Request)
    .s_ctrlport_req_wr(ctrlport_shared_req_wr),
    .s_ctrlport_req_rd(ctrlport_shared_req_rd),
    .s_ctrlport_req_addr(ctrlport_shared_req_addr),
    .s_ctrlport_req_data(ctrlport_shared_req_data),
    .s_ctrlport_req_byte_en(ctrlport_shared_req_byte_en),
    .s_ctrlport_req_has_time(ctrlport_shared_req_has_time),
    .s_ctrlport_req_time(ctrlport_shared_req_time),
    // Control Port Slave (Response)
    .s_ctrlport_resp_ack(ctrlport_shared_resp_ack),
    .s_ctrlport_resp_status(),
    .s_ctrlport_resp_data(ctrlport_shared_resp_data),
    // Control Port Master (Request)
    .m_ctrlport_req_wr(),
    .m_ctrlport_req_rd(ctrlport_timer_req_rd),
    .m_ctrlport_req_addr(ctrlport_timer_req_addr),
    .m_ctrlport_req_data(),
    .m_ctrlport_req_byte_en(),
    // Control Port Master (Response)
    .m_ctrlport_resp_ack(ctrlport_timer_resp_ack),
    .m_ctrlport_resp_status(2'b00),
    .m_ctrlport_resp_data(ctrlport_timer_resp_data)
  );


  //---------------------------------------------------------------------------
  // Shared Registers
  //---------------------------------------------------------------------------
  //
  // These registers are shared by all radio channels.
  //
  //---------------------------------------------------------------------------

  localparam [15:0] compat_major = 16'd1;
  localparam [15:0] compat_minor = 16'd0;

  reg [31:0] radio_time_hi;

  always @(posedge radio_clk) begin
    if (radio_rst) begin
      ctrlport_timer_resp_ack   <= 0;
      ctrlport_timer_resp_data  <= 0;
      radio_time_hi             <= 32'h0;
    end else begin
      // Default assignments
      ctrlport_timer_resp_ack  <= 0;
      ctrlport_timer_resp_data <= 0;

      // Handle register reads
      if (ctrlport_timer_req_rd) begin
        ctrlport_timer_resp_ack  <= 1;
        case (ctrlport_timer_req_addr)
          REG_COMPAT_NUM: begin
            ctrlport_timer_resp_data <= { compat_major, compat_minor };
          end
          REG_TIME_LO: begin
            ctrlport_timer_resp_data  <= radio_time[31:0];
            radio_time_hi             <= radio_time[63:32];
          end
          REG_TIME_HI: begin
            ctrlport_timer_resp_data <= radio_time_hi;
          end
        endcase
      end
    end
  end


  //---------------------------------------------------------------------------
  // Radio Cores
  //---------------------------------------------------------------------------
  //
  // This generate block instantiates one radio core for each channel that is
  // requested by NUM_PORTS.
  //
  //---------------------------------------------------------------------------

  genvar i;
  generate
    for (i = 0; i < NUM_PORTS; i = i+1) begin : radio_core_gen

      // The radio core contains all the logic related to a single radio channel.
      radio_core #(
        .SAMP_W          (ITEM_W),
        .NSPC            (NIPC),
        .EN_COMP_GAIN_TX (EN_COMP_GAIN_TX),
        .EN_COMP_GAIN_RX (EN_COMP_GAIN_RX),
        .EN_FIFO_OUT_REG (EN_FIFO_OUT_REG)
      ) radio_core_i (
        .radio_clk                 (radio_clk),
        .radio_rst                 (radio_rst),

        // Slave Control Port (Register Access)
        .s_ctrlport_req_wr         (ctrlport_radios_req_wr[i]),
        .s_ctrlport_req_rd         (ctrlport_radios_req_rd[i]),
        .s_ctrlport_req_addr       (ctrlport_radios_req_addr[i*20 +: 20]),
        .s_ctrlport_req_data       (ctrlport_radios_req_data[i*32 +: 32]),
        .s_ctrlport_req_has_time   (ctrlport_radios_req_has_time[i]),
        .s_ctrlport_req_time       (ctrlport_radios_req_time[i*64 +: 64]),
        .s_ctrlport_resp_ack       (ctrlport_radios_resp_ack[i]),
        .s_ctrlport_resp_status    (ctrlport_radios_resp_status[i*2 +: 2]),
        .s_ctrlport_resp_data      (ctrlport_radios_resp_data[i*32 +: 32]),

        // Master Control Port (Error Reporting)
        .m_ctrlport_req_wr         (ctrlport_err_radio_req_wr[i]),
        .m_ctrlport_req_addr       (ctrlport_err_radio_req_addr[i*20 +: 20]),
        .m_ctrlport_req_portid     (ctrlport_err_radio_req_portid[i*10 +: 10]),
        .m_ctrlport_req_rem_epid   (ctrlport_err_radio_req_rem_epid[i*16 +: 16]),
        .m_ctrlport_req_rem_portid (ctrlport_err_radio_req_rem_portid[i*10 +: 10]),
        .m_ctrlport_req_data       (ctrlport_err_radio_req_data[i*32 +: 32]),
        .m_ctrlport_req_has_time   (ctrlport_err_radio_req_has_time[i]),
        .m_ctrlport_req_time       (ctrlport_err_radio_req_time[i*64 +: 64]),
        .m_ctrlport_resp_ack       (ctrlport_err_radio_resp_ack[i]),

        // Tx Data Stream
        .s_axis_tdata              (axis_tx_tdata[RADIO_W*i +: RADIO_W]),
        .s_axis_tlast              (axis_tx_tlast[i]),
        .s_axis_tvalid             (axis_tx_tvalid[i]),
        .s_axis_tready             (axis_tx_tready[i]),
        // Sideband Info
        .s_axis_ttimestamp         (axis_tx_ttimestamp[i*64 +: 64]),
        .s_axis_thas_time          (axis_tx_thas_time[i]),
        .s_axis_teob               (axis_tx_teob[i]),

        // Rx Data Stream
        .m_axis_tdata              (axis_rx_tdata[RADIO_W*i +: RADIO_W]),
        .m_axis_tlast              (axis_rx_tlast[i]),
        .m_axis_tvalid             (axis_rx_tvalid[i]),
        .m_axis_tready             (axis_rx_tready[i]),
        // Sideband Info
        .m_axis_ttimestamp         (axis_rx_ttimestamp[i*64 +: 64]),
        .m_axis_thas_time          (axis_rx_thas_time[i]),
        .m_axis_teob               (axis_rx_teob[i]),

        // Radio Data
        .radio_time                (radio_time),
        .radio_rx_data             (radio_rx_data[(RADIO_W)*i +: (RADIO_W)]),
        .radio_rx_stb              (radio_rx_stb[i]),
        .radio_rx_running          (radio_rx_running[i]),
        .radio_tx_data             (radio_tx_data[(RADIO_W)*i +: (RADIO_W)]),
        .radio_tx_stb              (radio_tx_stb[i]),
        .radio_tx_running          (radio_tx_running[i])
      );
    end
  endgenerate

  assign radio_state_rx_running = radio_rx_running;
  assign radio_state_tx_running = radio_tx_running;

endmodule

//XmlParse xml_on
//
//<regmap name="RFNOC_RADIO_REGMAP" readablestrobes="false" generatevhdl="false" generateverilog="false">
//  <group name="RFNOC_RADIO_WINDOWS">
//    <info>
//      RFNoC control requests are distributed across the following windows.
//      The shared and periphial windows are capable of handling timed requests.
//    </info>
//    <window name="SHARED_WINDOW" offset="0x00000" size="0x010">
//      <info>Provides read access to the timestamp of the radio block.</info>
//    </window>
//    <window name="RFDC_TIMING_WINDOW" offset="0x1000" size="0x0200">
//      <info>
//        Provides access to the respective radio cores. Actual size depends on the number of radio
//        cores.
//      </info>
//    </window>
//    <window name="PERIPH_WINDOW" offset="0x80000" size="0x80000" targetregmap="RADIO_CTRLPORT_REGMAP">
//      <info>Accessing the peripherals of the radio.</info>
//    </window>
//  </group>
//</regmap>
//
//XmlParse xml_off
