Interactive Lab

Round-Robin Arbiter Lab

Watch 4 requestors fight over one resource. Toggle requests, see the grant pointer rotate, compare fairness vs starvation — the core of every SoC bus fabric.

Round-Robin Arbiter — Grant Pointer Rotates After Every Grant

Toggle requestors ON/OFF. The grant pointer always advances after each grant — nobody starves. Fair

ptr 0 REQ0 idle REQ1 idle REQ2 idle REQ3 idle
Toggle Requestors (click to request / release)
Grant Distribution
0
REQ0 Grants
0
REQ1 Grants
0
REQ2 Grants
0
REQ3 Grants
Jain's Fairness Index
1.00
1.00 = perfect  |  0.5 = moderate imbalance  |  0.25 = one requestor dominates
Total cycles: 0
Total grants: 0

Fixed Priority Arbiter — REQ0 Always Wins

Priority order: REQ0 > REQ1 > REQ2 > REQ3. Enable REQ0 to see how REQ1–REQ3 get starved.

P0 (highest) P1 P2 P3 REQ0 idle REQ1 idle REQ2 idle REQ3 idle
⚠️ Starvation detected! REQ0 is monopolising the bus. Lower-priority requestors have been waiting 0 cycles without a grant. This never happens in a round-robin arbiter.
0
REQ0 Grants
0
REQ1 Grants
0
REQ2 Grants
0
REQ3 Grants
Jain's Fairness Index
1.00
Max starvation: 0 cycles without grant
Total cycles: 0
Total grants: 0

Round-Robin vs Fixed Priority

PropertyRound-RobinFixed Priority
Fairness✓ Equal share (Jain ≈ 1.0)✗ Biased towards high-priority
Starvation✓ Impossible — pointer always advances✗ Low-priority can wait indefinitely
Latency for high priority⚠ Up to N–1 extra cycles✓ Always 1 cycle
Implementation complexity⚠ Slightly higher (rotating mask)✓ Simple priority encoder
Best use caseCPU cores, DMA channels, equal-priority mastersInterrupt controllers, real-time display controllers
Weighted variantWeighted RR: REQ0 gets 3 slots, REQ1 gets 1 slotStrict priority with aging (prevents starvation)
SoC examplesAXI interconnect, NoC routers, PCIe TLP arbitrationARM GIC interrupt controller, USB SOF arbiter

Verilog — Parameterized Round-Robin Arbiter

Classic rotating-mask implementation. Works for any N. Synthesizes to a priority encoder + register.

verilog
// Parameterized Round-Robin Arbiter
// After each grant to bit i, next arbitration starts from bit i+1
module rr_arbiter #(
  parameter N = 4   // number of requestors
)(
  input  wire         clk,
  input  wire         rst_n,
  input  wire [N-1:0] req,    // request vector (1 = requesting)
  output reg  [N-1:0] grant   // one-hot grant output
);
  reg [N-1:0] ptr;            // one-hot pointer: next highest priority

  // Rotate mask: mask off all bits at and below last grant
  // Priority within the masked window, then fall back to full req
  wire [N-1:0] mask_req  = req & ~((ptr - 1) | ptr);  // req masked by pointer
  wire [N-1:0] raw_grant, mask_grant;

  // Priority encoders (lowest index wins within active window)
  assign mask_grant = mask_req  & (~mask_req  + 1'b1); // isolate LSB
  assign raw_grant  = req       & (~req       + 1'b1);

  wire [N-1:0] next_grant = |mask_req ? mask_grant : raw_grant;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      grant <= '0;
      ptr   <= {{N-1{1'b0}}, 1'b1}; // start at position 0
    end else begin
      if (|req) begin
        grant <= next_grant;
        // Advance pointer to one past the granted position
        ptr   <= {next_grant[N-2:0], next_grant[N-1]}; // rotate left by 1
      end else begin
        grant <= '0;
      end
    end
  end
endmodule

Verilog — Fixed Priority Arbiter

verilog
// Fixed Priority Arbiter (REQ[0] = highest priority)
// Simple: grant the lowest-index active request
module fp_arbiter #(
  parameter N = 4
)(
  input  wire         clk,
  input  wire         rst_n,
  input  wire [N-1:0] req,
  output reg  [N-1:0] grant
);
  // Isolate lowest-set-bit: req & (-req) in two's complement
  wire [N-1:0] next_grant = req & (~req + 1'b1);

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) grant <= '0;
    else        grant <= |req ? next_grant : '0;
  end
endmodule

// Note: if req[0] is always asserted, req[1..N-1] never receive a grant.
// To prevent starvation with fixed priority, add an aging counter:
// promote a requestor to higher priority after waiting K cycles.

Testbench

verilog
module tb_rr_arbiter;
  parameter N = 4;
  reg         clk = 0, rst_n = 0;
  reg  [N-1:0] req;
  wire [N-1:0] grant;

  always #5 clk = ~clk; // 100 MHz

  rr_arbiter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .req(req), .grant(grant));

  integer i; reg [N-1:0] grant_cnt [0:N-1];

  initial begin
    for (i=0; i
    
Frequently Asked Questions
What is a round-robin arbiter?

A round-robin arbiter grants access to a shared resource among multiple requestors in a rotating, circular order. After granting requestor N, the next grant starts searching from requestor N+1. This ensures every requestor eventually receives a grant — preventing starvation — and distributes bandwidth fairly when all requestors have equal priority.

What is starvation and how does round-robin prevent it?

Starvation occurs in a fixed-priority arbiter when a high-priority requestor continuously asserts its request, preventing lower-priority requestors from ever being granted. Round-robin prevents starvation by advancing the grant pointer after every grant — the requestor that was just served has the lowest priority in the next cycle, guaranteeing every other active requestor gets served before it wins again.

What is Jain's fairness index?

Jain's fairness index J = (Σxᵢ)² / (n × Σxᵢ²) measures how equitably grants are distributed across n requestors. J = 1.0 means perfectly fair. J = 1/n means one requestor received everything. Round-robin achieves J ≈ 1.0 under uniform load. Fixed priority with one dominant requestor can drive J close to 1/N.

How is a round-robin arbiter implemented in hardware?

The standard implementation uses a rotating mask. A pointer register tracks the last-granted position. A mask is applied to zero out all requests at or below the pointer, giving higher priority to the remaining requests. A priority encoder selects the lowest active request in the masked window. If no masked request is active, it falls back to the full unmasked request vector. This produces correct round-robin behaviour in a single always block.

When should I use fixed priority instead of round-robin?

Use fixed priority when requestors have genuinely different latency requirements — for example, an interrupt controller where some interrupts are safety-critical, or a display controller that must fetch pixels before every frame deadline. Use round-robin when multiple masters (CPU cores, DMA channels) share a bus with similar importance and fairness matters more than minimizing peak latency for one agent.

What is weighted round-robin?

Weighted round-robin gives each requestor a configurable number of consecutive grant slots. For example, REQ0 might receive 4 grants per round while REQ1 receives 1. This provides proportional bandwidth sharing while still guaranteeing every requestor receives some grants — preventing starvation. It is widely used in network switches and SoC NoC routers to implement Quality-of-Service (QoS) policies.