module simple_arbiter #
(
    parameter PORTS = 4,
    // select round robin arbitration
    parameter ARB_TYPE_ROUND_ROBIN = 0,
    // LSB priority selection
    parameter ARB_LSB_HIGH_PRIORITY = 0
)
(
    input  wire                     clk,
    input  wire                     rst,

    input  wire [PORTS-1:0]         request,
    input  wire                     taken,

    output wire [PORTS-1:0]         grant,
    output wire                     grant_valid,
    output wire [$clog2(PORTS)-1:0] grant_encoded
);

wire request_valid;
wire [$clog2(PORTS)-1:0] request_index;
wire [PORTS-1:0] request_mask;

priority_encoder #(
    .WIDTH(PORTS),
    .LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
)
priority_encoder_inst (
    .input_unencoded(request),
    .output_valid(request_valid),
    .output_encoded(request_index),
    .output_unencoded(request_mask)
);

reg [PORTS-1:0] mask;
wire masked_request_valid;
wire [$clog2(PORTS)-1:0] masked_request_index;
wire [PORTS-1:0] masked_request_mask;

priority_encoder #(
    .WIDTH(PORTS),
    .LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
)
priority_encoder_masked (
    .input_unencoded(request & mask),
    .output_valid(masked_request_valid),
    .output_encoded(masked_request_index),
    .output_unencoded(masked_request_mask)
);

always @(posedge clk)
    if (rst) begin
        mask <= {PORTS{1'b0}};
    end else if (grant_valid && taken) begin
        if (ARB_LSB_HIGH_PRIORITY)
            mask <= {PORTS{1'b1}} << (grant_encoded + 1);
        else
            mask <= {PORTS{1'b1}} >> (PORTS - grant_encoded);
    end

if (ARB_TYPE_ROUND_ROBIN) begin
    assign grant_valid   = masked_request_valid ? masked_request_valid : request_valid;
    assign grant         = masked_request_valid ? masked_request_mask  : request_mask;
    assign grant_encoded = masked_request_valid ? masked_request_index : request_index;
end else begin
    assign grant_valid   = request_valid;
    assign grant         = request_mask;
    assign grant_encoded = request_index;
end

endmodule