module axi_mcb_cmd_arbiter #
(
///////////////////////////////////////////////////////////////////////////////
// Parameter Definitions
///////////////////////////////////////////////////////////////////////////////
                    // Width of cmd_byte_addr
                    // Range: 30
  parameter integer C_MCB_ADDR_WIDTH    = 30
)
(
///////////////////////////////////////////////////////////////////////////////
// Port Declarations     
///////////////////////////////////////////////////////////////////////////////
  // AXI Slave Interface
  // Slave Interface System Signals           
  input  wire                                 clk              , 
  input  wire                                 reset            , 

  input  wire                                 wr_cmd_en        , 
  input  wire [2:0]                           wr_cmd_instr     , 
  input  wire                                 wr_wrap_cmd_sel       ,
  input  wire [5:0]                           wr_wrap_cmd_bl        , 
  input  wire [C_MCB_ADDR_WIDTH-1:0]          wr_wrap_cmd_byte_addr , 
  input  wire [5:0]                           wr_incr_cmd_bl        , 
  input  wire [C_MCB_ADDR_WIDTH-1:0]          wr_incr_cmd_byte_addr , 
  output wire                                 wr_cmd_empty     , 
  output wire                                 wr_cmd_full      , 
  input  wire                                 wr_cmd_has_next_pending ,

  input  wire                                 rd_cmd_en        , 
  input  wire [2:0]                           rd_cmd_instr     , 
  input  wire                                 rd_wrap_cmd_sel       ,
  input  wire [5:0]                           rd_wrap_cmd_bl        , 
  input  wire [C_MCB_ADDR_WIDTH-1:0]          rd_wrap_cmd_byte_addr , 
  input  wire [5:0]                           rd_incr_cmd_bl        , 
  input  wire [C_MCB_ADDR_WIDTH-1:0]          rd_incr_cmd_byte_addr , 
  output wire                                 rd_cmd_empty     , 
  output wire                                 rd_cmd_full      , 
  input  wire                                 rd_cmd_has_next_pending ,

  output wire                                 cmd_en           , 
  output wire [2:0]                           cmd_instr        , 
  output wire [5:0]                           cmd_bl           , 
  output wire [C_MCB_ADDR_WIDTH-1:0]          cmd_byte_addr    , 
  input  wire                                 cmd_empty        , 
  input  wire                                 cmd_full

);

////////////////////////////////////////////////////////////////////////////////
// Local parameters
////////////////////////////////////////////////////////////////////////////////
localparam P_ARB_ALGO = "READ_PRIORITY_REG";
//localparam P_ARB_ALGO = "ROUND_ROBIN";

////////////////////////////////////////////////////////////////////////////////
// Wires/Reg declarations
////////////////////////////////////////////////////////////////////////////////
wire                        rnw;
reg [C_MCB_ADDR_WIDTH-1:0]  cmd_byte_addr_i;
reg [5:0]                   cmd_bl_i;
reg                         wr_cmd_en_last;
reg                         rd_cmd_en_last;
////////////////////////////////////////////////////////////////////////////////
// BEGIN RTL
////////////////////////////////////////////////////////////////////////////////
assign cmd_en        = rnw ? rd_cmd_en : wr_cmd_en;
assign cmd_instr     = rnw ? rd_cmd_instr     : wr_cmd_instr;
assign cmd_bl        = cmd_bl_i;
assign cmd_byte_addr = cmd_byte_addr_i;
assign wr_cmd_empty  = cmd_empty;
assign wr_cmd_full   = rnw ? 1'b1 : cmd_full;
assign rd_cmd_empty  = cmd_empty;
assign rd_cmd_full   = ~rnw ? 1'b1 : cmd_full;


always @(*) begin
  casex ({wr_wrap_cmd_sel, rd_wrap_cmd_sel, rnw}) // synopsys parallel_case
    3'bx01: cmd_byte_addr_i = rd_incr_cmd_byte_addr;
    3'bx11: cmd_byte_addr_i = rd_wrap_cmd_byte_addr;
    3'b1x0: cmd_byte_addr_i = wr_wrap_cmd_byte_addr;
    default: cmd_byte_addr_i = wr_incr_cmd_byte_addr; // 3'b0x0
  endcase
end

always @(*) begin
  casex ({wr_wrap_cmd_sel, rd_wrap_cmd_sel, rnw}) // synopsys parallel_case
    3'bx01: cmd_bl_i = rd_incr_cmd_bl;
    3'bx11: cmd_bl_i = rd_wrap_cmd_bl;
    3'b1x0: cmd_bl_i = wr_wrap_cmd_bl;
    default: cmd_bl_i = wr_incr_cmd_bl; // 3'b0x0
  endcase
end

generate
  // TDM Arbitration scheme
  if (P_ARB_ALGO == "TDM") begin : TDM
    reg rnw_i;
    // Register rnw status
    always @(posedge clk) begin
      if (reset) begin
        rnw_i <= 1'b0;
      end else begin
        rnw_i <= ~rnw_i;
      end
    end
    assign rnw = rnw_i;
  end
  else if (P_ARB_ALGO == "ROUND_ROBIN") begin : ROUND_ROBIN
    reg rnw_i;
    // Register rnw status
    always @(posedge clk) begin
      if (reset) begin
        rnw_i <= 1'b0;
      end else begin
        rnw_i <= ~rnw_i;
      end
    end
    assign rnw = (rnw_i & rd_cmd_en) | (~rnw_i & rd_cmd_en & ~wr_cmd_en);
  end
  else if (P_ARB_ALGO == "ROUND_ROBIN") begin : ROUND_ROBIN
    reg rnw_i;
    // Register rnw status
    always @(posedge clk) begin
      if (reset) begin
        rnw_i <= 1'b0;
      end else begin
        rnw_i <= ~rnw_i;
      end
    end
    assign rnw = (rnw_i & rd_cmd_en) | (~rnw_i & rd_cmd_en & ~wr_cmd_en);
  end
  else if (P_ARB_ALGO == "READ_PRIORITY_REG") begin : READ_PRIORITY_REG
    reg rnw_i;
    reg rd_cmd_en_last;
    reg wr_cmd_en_last;
    wire rd_req;
    wire wr_req;
    always @(posedge clk) begin
      rd_cmd_en_last <= rnw & rd_cmd_en & rd_cmd_has_next_pending;
      wr_cmd_en_last <= ~rnw & wr_cmd_en & wr_cmd_has_next_pending;
    end
    // Register rnw status
    always @(posedge clk) begin
      if (reset) begin
        rnw_i <= 1'b1;
      end else begin
        // Only set RNW to 0 if there is a write pending and read is idle
        rnw_i <= rd_req & ~wr_cmd_en_last ? 1'b1 : ~wr_req;
      end
    end

    assign rd_req = rd_cmd_en | rd_cmd_en_last;
    assign wr_req = wr_cmd_en | wr_cmd_en_last;
    assign rnw = rnw_i;
  end
  else if (P_ARB_ALGO == "READ_PRIORITY") begin : READ_PRIORITY
    assign rnw = ~(wr_cmd_en & ~rd_cmd_en);
  end
  else if (P_ARB_ALGO == "WRITE_PRIORITY_REG") begin : WRITE_PRIORITY_REG
    reg rnw_i;
    // Register rnw status
    always @(posedge clk) begin
      if (reset) begin
        rnw_i <= 1'b0;
      end else begin
        // Only set RNW to 1 if there is a read pending and write is idle
        rnw_i <= (~wr_cmd_en & rd_cmd_en);
      end
    end
    assign rnw = rnw_i;
  end
  else begin : WRITE_PRIORITY
    assign rnw =  (~wr_cmd_en & rd_cmd_en);
  end
endgenerate

endmodule