`include "VX_platform.vh"

module VX_fair_arbiter #(
    parameter N = 1
) (
    input  wire                  clk,
    input  wire                  reset,
    input  wire [N-1:0]          requests,           
    output wire [`LOG2UP(N)-1:0] grant_index,
    output wire [N-1:0]          grant_onehot,   
    output wire                  grant_valid
  );

    if (N == 1)  begin                
        
        `UNUSED_VAR (clk)
        `UNUSED_VAR (reset)
        assign grant_index  = 0;
        assign grant_onehot = requests;
        assign grant_valid  = requests[0];

    end else begin    

       reg  [N-1:0] requests_use;
       wire [N-1:0] update_value;
       wire [N-1:0] late_value;

       wire         refill;
       wire [N-1:0] refill_value;
       reg  [N-1:0] refill_original;

       always @(posedge clk) begin
            if (reset) begin
                requests_use    <= 0;
                refill_original <= 0;
            end else begin
                if (refill) begin
                    requests_use    <= refill_value;
                    refill_original <= refill_value;
                end else begin
                    requests_use <= update_value;
                end
            end
       end

       assign refill       = (requests_use == 0);
       assign refill_value = requests;
        
        reg [N-1:0] grant_onehot_r; 

        VX_priority_encoder #(
            .N(N)
        ) priority_encoder (
            .data_in   (requests_use),
            .data_out  (grant_index ),
            .valid_out (grant_valid )
        );

        always @(*) begin
            grant_onehot_r = N'(0);
            grant_onehot_r[grant_index] = 1;
        end
        assign grant_onehot = grant_onehot_r;    
        assign late_value   = ((refill_original ^ requests) & ~refill_original);
        assign update_value = (requests_use & ~grant_onehot_r) | late_value;

    end
    
endmodule
