module axi_crossbar_regport #(
  parameter REG_BASE    = 0,  // settings bus base address
  parameter FIFO_WIDTH  = 64, // AXI4-STREAM data bus width
  parameter DST_WIDTH   = 16, // Width of DST field we are routing on.
  parameter NUM_INPUTS  = 2,  // number of input AXI4-STREAM buses
  parameter NUM_OUTPUTS = 2,   // number of output AXI4-STREAM buses
  parameter REG_DWIDTH  = 32, // Width of the AXI4-Lite data bus (must be 32 or 64)
  parameter REG_AWIDTH  = 14  // Width of the address bus
)(
  input                                 clk,
  input                                 reset,
  input                                 clear,

  input                                 reg_wr_req,
  input  [REG_AWIDTH-1:0]               reg_wr_addr,
  input  [REG_DWIDTH-1:0]               reg_wr_data,

  input                                 reg_rd_req,
  input  [REG_AWIDTH-1:0]               reg_rd_addr,
  output [REG_DWIDTH-1:0]               reg_rd_data,
  output                                reg_rd_resp,

  // Inputs
  input [(FIFO_WIDTH*NUM_INPUTS)-1:0]   i_tdata,
  input [NUM_INPUTS-1:0]                i_tvalid,
  input [NUM_INPUTS-1:0]                i_tlast,
  output [NUM_INPUTS-1:0]               i_tready,
  input [NUM_INPUTS-1:0]                pkt_present,

  // Output
  output [(FIFO_WIDTH*NUM_OUTPUTS)-1:0] o_tdata,
  output [NUM_OUTPUTS-1:0]              o_tvalid,
  output [NUM_OUTPUTS-1:0]              o_tlast,
  input [NUM_OUTPUTS-1:0]               o_tready
);

  localparam XBAR_VERSION              = 32'b1;
  localparam XBAR_NUM_PORTS            = NUM_INPUTS; //or NUM_OUTPUTS

  localparam REG_XBAR_VERSION          = REG_BASE + 14'h10;
  localparam REG_XBAR_NUM_PORTS        = REG_BASE + 14'h14;
  localparam REG_XBAR_LOCAL_ADDR       = REG_BASE + 14'h18;
  localparam REG_BASE_XBAR_SETTING_REG = REG_BASE + 14'h20;
  localparam REG_END_ADDR_XBAR_SETTING_REG = REG_BASE + 14'h1000;

  // Settings bus address width
  localparam SR_AWIDTH = 12;

  wire                  xbar_set_stb;
  wire [REG_DWIDTH-1:0] xbar_set_data;
  wire [SR_AWIDTH-1:0]  xbar_set_addr;

  wire                  xbar_rb_stb;
  wire [SR_AWIDTH-1:0]  xbar_rb_addr;
  wire [REG_DWIDTH-1:0] xbar_rb_data;

  reg  [31:0]           local_addr_reg;
  reg                   reg_rd_resp_glob;
  reg  [REG_DWIDTH-1:0] reg_rd_data_glob;

  wire [REG_DWIDTH-1:0] reg_rd_data_xbar;
  wire                  reg_rd_resp_xbar;

  regport_resp_mux #(
    .WIDTH(REG_DWIDTH),
    .NUM_SLAVES(2)
  ) inst_regport_resp_mux_xbar (
    .clk(clk),
    .reset(reset),
    .sla_rd_resp({reg_rd_resp_glob, reg_rd_resp_xbar}),
    .sla_rd_data({reg_rd_data_glob, reg_rd_data_xbar}),
    .mst_rd_resp(reg_rd_resp),
    .mst_rd_data(reg_rd_data)
  );

  // Read Registers
  always @ (posedge clk) begin
    if (reset) begin
      local_addr_reg <= 32'h0;
    end
    else begin
    if (reg_wr_req)
      case (reg_wr_addr)
        REG_XBAR_LOCAL_ADDR:
          local_addr_reg  <= reg_wr_data;
      endcase
    end
  end

  // Write Registers
  always @ (posedge clk) begin
    if (reset)
      reg_rd_resp_glob <= 1'b0;

    else begin
      if (reg_rd_req) begin
        reg_rd_resp_glob <= 1'b1;

        case (reg_rd_addr)
        REG_XBAR_VERSION:
          reg_rd_data_glob <= XBAR_VERSION;

        REG_XBAR_NUM_PORTS:
          reg_rd_data_glob <= XBAR_NUM_PORTS;

        REG_XBAR_LOCAL_ADDR:
          reg_rd_data_glob <= local_addr_reg;

        default:
          reg_rd_resp_glob <= 1'b0;
        endcase
      end
      else if (reg_rd_resp_glob) begin
          reg_rd_resp_glob <= 1'b0;
      end
    end
  end

  regport_to_xbar_settingsbus #(
    .BASE(REG_BASE_XBAR_SETTING_REG),
    .END_ADDR(REG_END_ADDR_XBAR_SETTING_REG),
    .DWIDTH(REG_DWIDTH),
    .AWIDTH(REG_AWIDTH),
    .SR_AWIDTH(SR_AWIDTH),
    .ADDRESSING("WORD")
  ) inst_regport_to_xbar_settingsbus (
    .clk(clk),
    .reset(reset),

    .reg_wr_req(reg_wr_req),
    .reg_wr_addr(reg_wr_addr),
    .reg_wr_data(reg_wr_data),
    .reg_rd_req(reg_rd_req),
    .reg_rd_addr(reg_rd_addr),
    .reg_rd_data(reg_rd_data_xbar),
    .reg_rd_resp(reg_rd_resp_xbar),

    .set_stb(xbar_set_stb),
    .set_addr(xbar_set_addr),
    .set_data(xbar_set_data),
    .rb_stb(xbar_rb_stb),
    .rb_addr(xbar_rb_addr),
    .rb_data(xbar_rb_data)
  );

  axi_crossbar #(
    .BASE(0), // Set to 0 as logic for other values has not been tested
    .FIFO_WIDTH(FIFO_WIDTH),
    .DST_WIDTH(DST_WIDTH),
    .NUM_INPUTS(NUM_INPUTS),
    .NUM_OUTPUTS(NUM_OUTPUTS)
  ) axi_crossbar (
    .clk(clk),
    .reset(reset),
    .clear(1'b0),
    .local_addr(local_addr_reg),

    // settings bus for config
    .set_stb(xbar_set_stb),
    .set_addr({4'b0000,xbar_set_addr}),
    .set_data(xbar_set_data),
    .rb_rd_stb(xbar_rb_stb),
    .rb_addr(xbar_rb_addr[$clog2(NUM_INPUTS)+$clog2(NUM_OUTPUTS)-1:0]),
    .rb_data(xbar_rb_data),

    // inputs, real men flatten busses
    .i_tdata(i_tdata),
    .i_tlast(i_tlast),
    .i_tvalid(i_tvalid),
    .i_tready(i_tready),

    // outputs, real men flatten busses
    .o_tdata(o_tdata),
    .o_tlast(o_tlast),
    .o_tvalid(o_tvalid),
    .o_tready(o_tready),
    .pkt_present(pkt_present)
  );

endmodule // axi_crossbar_regport