Draft preview. Lessons are being written and reviewed.

Digital Design / Module 11: Designing for silicon

63. Arbiters

Share a resource fairly.

Lecture coming soon

See it move

The idea

When several requesters want one resource, like a bus, memory port or multiplier, an arbiter decides who gets it. Its output is a grant: at most one requester wins per cycle.

Fixed priority

The lowest-numbered active request always wins. It's small and fast, and there is a neat trick for it:

assign grant = req & (~req + 1'b1);

This isolates the lowest set bit. For req = 01100, ~req + 1 = 10100, and the AND gives 00100.

The drawback is starvation: a busy high-priority requester can lock out everyone else forever.

Round robin

After granting a requester, give it the lowest priority for the next round. Everyone gets a turn, and no one waits for more than N-1 others.

module rr_arb #(parameter N = 4) (
  input                clk, rst_n,
  input      [N-1:0]   req,
  output     [N-1:0]   grant
);
  reg  [N-1:0] mask;
  wire [N-1:0] masked   = req & mask;
  wire [N-1:0] use_req  = (|masked) ? masked : req;
  assign grant = use_req & (~use_req + 1'b1);

  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n)      mask <= '1;
    else if (|grant) mask <= ~(grant | (grant - 1'b1));
endmodule

mask removes the winner and everything below it for the next round. If no masked request remains, then the design wraps round to the full set.

Other schemes

  • Weighted round robin gives some requesters more turns, while still guaranteeing service.
  • Least recently granted is fairer in odd traffic, but needs more state.
  • Matrix arbiters remember pairwise priorities and can be fast at small sizes.

Design points

A multi-cycle transaction may need to keep its grant until it finishes. Add a busy hold, so that the arbiter doesn't move mid-burst. Think too about paths: a combinational request-to-grant path can become critical, so consider registering outputs.

Common mistake

Choosing round robin without measuring the traffic. If one source must meet a hard deadline, like a display refresh, then it needs priority. Fairness isn't always what you want. Run the animation above: with all requests on, fixed priority serves R0 only, whereas round robin gives each an equal share.

Key ideas

  • Fixed priority is simple but can starve requesters.
  • Round-robin rotates priority after each grant.
  • Grants should be one-hot.

Your exercise

Write a 4-way round-robin arbiter.

Find the bug

This code has one deliberate bug. Read the symptom, then click the line you think is wrong.

What goes wrong: With all four requesting, grants alternate between R0 and R1. R2 and R3 never get a turn.

Interview questions

coreIsolating lowest set bit?

basicUsing grant = req & (~req + 1), what is the grant for req = 8'b1011_0000?

basicWhat is starvation in a fixed-priority arbiter?

coreIn the round-robin arbiter, the mask is updated to ~(grant | (grant - 1)). If requester 2 of 4 was just granted and all four ask next cycle, what is the mask and who wins?

coreThis round-robin arbiter updates its mask with ~grant instead. With all four requesting, what is the grant pattern?

always @(posedge clk or negedge rst_n)
  if (!rst_n)      mask <= {N{1'b1}};
  else if (|grant) mask <= ~grant;

coreAn 8-way round-robin arbiter grants a resource that each winner holds for 4 cycles. How long can a requester wait, at worst?

coreA winner needs the shared bus for a multi-cycle burst. How do you stop the arbiter from moving the grant in the middle?

advancedFor a 32-way arbiter at a high clock rate, the request-to-grant path fails timing. What would you consider?

advancedWhat happens if the round-robin mask is updated every cycle, even when no grant is given?

always @(posedge clk or negedge rst_n)
  if (!rst_n) mask <= {N{1'b1}};
  else        mask <= ~(grant | (grant - 1'b1));

Practise these 9 questions with animations