// Task `arbiter`: the reference (own code, 2026-11-04; proved for every N >= 1, rr_mask.lean, 2026-11-05). A registered
// round-robin arbiter, the textbook mask form: `mask` marks the clients above the last grant; the lowest
// masked requester wins, else the lowest requester (the wrap). Moore: gnt is a flop (the one comb block
// computes the pick from req and the mask and drives no output). Fair within N cycles: if
// client i requests and loses, the winner lies between the last grant and i, so i's cyclic distance from
// the mask's edge drops (the ranking of tasks/arbiter/README.md (e)).
// Why own code and not a real-world design (MEASURED 2026-11-05): the adapted OpenTitan prim_arbiter_tree
// (tests/arbiter/designs/arbiter_ot.sv) declares variables and localparams inside its generate blocks, which
// ls-sv does not instantiate yet; verilog-axis arbiter.v instantiates priority_encoder twice (hierarchy) over
// unpacked arrays. tasks/arbiter/README.md, "The reference".
module arbiter #(parameter int N = 8) (
  input  logic         clk, rst,
  input  logic [N-1:0] req,
  output logic [N-1:0] gnt
);
  logic [N-1:0] mask, mreq, pick;
  always_comb begin
    mreq = req & mask;
    pick = (mreq != '0) ? (mreq & (~mreq + 1'b1)) : (req & (~req + 1'b1));
  end
  always_ff @(posedge clk) begin
    if (rst) begin
      gnt <= '0;
      mask <= '1;
    end else begin
      gnt <= pick;
      if (pick != '0) mask <= ~((pick << 1) - 1'b1);
    end
  end
endmodule
