module altera_merlin_arbitrator
#(
    parameter NUM_REQUESTERS = 8,
    // --------------------------------------
    // Implemented schemes
    // "round-robin"
    // "fixed-priority"
    // "no-arb"
    // --------------------------------------
    parameter SCHEME         = "round-robin",
    parameter PIPELINE       = 0
)
(
    input clk,
    input reset,
   
    // --------------------------------------
    // Requests
    // --------------------------------------
    input [NUM_REQUESTERS-1:0]  request,
   
    // --------------------------------------
    // Grants
    // --------------------------------------
    output [NUM_REQUESTERS-1:0] grant,

    // --------------------------------------
    // Control Signals
    // --------------------------------------
    input                       increment_top_priority,
    input                       save_top_priority
);

    // --------------------------------------
    // Signals
    // --------------------------------------
    wire [NUM_REQUESTERS-1:0]   top_priority;
    reg  [NUM_REQUESTERS-1:0]   top_priority_reg;
    reg  [NUM_REQUESTERS-1:0]   last_grant;
    wire [2*NUM_REQUESTERS-1:0] result;

    // --------------------------------------
    // Scheme Selection
    // --------------------------------------
    generate
        if (SCHEME == "round-robin" && NUM_REQUESTERS > 1) begin
            assign top_priority = top_priority_reg;
        end
        else begin
            // Fixed arbitration (or single-requester corner case)
            assign top_priority = 1'b1;
        end
    endgenerate

    // --------------------------------------
    // Decision Logic
    // --------------------------------------
    altera_merlin_arb_adder
    #(
        .WIDTH (2 * NUM_REQUESTERS)
    ) 
    adder
    (
        .a ({ ~request, ~request }),
        .b ({{NUM_REQUESTERS{1'b0}}, top_priority}),
        .sum (result)
    );

  
    generate if (SCHEME == "no-arb") begin

        // --------------------------------------
        // No arbitration: just wire request directly to grant
        // --------------------------------------
        assign grant = request;

    end else begin
        // Do the math in double-vector domain
        wire [2*NUM_REQUESTERS-1:0] grant_double_vector;
        assign grant_double_vector = {request, request} & result;

        // --------------------------------------
        // Extract grant from the top and bottom halves
        // of the double vector.
        // --------------------------------------
        assign grant =
            grant_double_vector[NUM_REQUESTERS - 1 : 0] |
            grant_double_vector[2 * NUM_REQUESTERS - 1 : NUM_REQUESTERS];

    end
    endgenerate

    // --------------------------------------
    // Left-rotate the last grant vector to create top_priority.
    // --------------------------------------
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            top_priority_reg <= 1'b1;
        end
        else begin
            if (PIPELINE) begin
                if (increment_top_priority) begin
                    top_priority_reg <= (|request) ? {grant[NUM_REQUESTERS-2:0],
                        grant[NUM_REQUESTERS-1]} : top_priority_reg;
                end
            end else begin
                if (save_top_priority) begin
                    top_priority_reg <= grant; 
                end
                if (increment_top_priority) begin
                    if (|request)
                        top_priority_reg <= { grant[NUM_REQUESTERS-2:0],
                            grant[NUM_REQUESTERS-1] };
                    else
                        top_priority_reg <= { top_priority_reg[NUM_REQUESTERS-2:0], top_priority_reg[NUM_REQUESTERS-1] };
                end
            end
        end
    end

endmodule