VHDL · Chapter 18.4 · Advanced RTL Design
Arbiters and Resource Arbitration
When several requesters contend for one shared resource, such as a bus, a memory port, or a single FIFO, something must decide who goes next. That something is an arbiter. It takes a request from each contender and produces a one-hot grant that hands the resource to exactly one requester at a time. The policy is the key design choice. Fixed priority always favors the same order and is trivial to build, but it can starve low-priority requesters when higher ones stay busy. Round-robin rotates the priority after each grant so everyone takes turns, giving fairness with a little more logic. Good arbiters guarantee mutual exclusion, liveness so every persistent request is eventually granted, and fairness. This lesson covers the request and grant interface, both policies, the correctness properties, and how each one is built.
Foundation15 min readVHDLRTLArbiterRound-RobinPriorityFairness
1. Engineering intuition — a fair traffic cop for one lane
A shared resource is a single lane that many cars want to enter. An arbiter is the traffic cop: it sees all the waiting cars (requests) and waves exactly one through (grant) each turn. The policy is how it chooses. The lazy cop always waves the same direction first — fast, but the side streets (low priority) may never move if the main road stays busy: starvation. The fair cop rotates who gets first dibs, so every direction eventually goes: round-robin. The non-negotiables are that only one car enters at a time (mutual exclusion) and that any car that keeps waiting eventually gets through (liveness/no starvation). Choosing and building the right policy is what keeps a shared bus, memory, or FIFO both correct and fair.
2. Formal explanation — request/grant and the policies
-- INPUTS: one request bit per requester. OUTPUT: a ONE-HOT grant (exactly one, or none if no requests).
-- MUTUAL EXCLUSION: at most one grant bit high. LIVENESS: every persistent request eventually granted.
-- FIXED PRIORITY: lowest index wins (a priority encoder). Simple — but STARVATION of higher indices.
process (all) begin
grant <= (others => '0');
for i in req'low to req'high loop
if req(i) = '1' then grant(i) <= '1'; exit; end if; -- first set request wins (fixed order)
end loop;
end process;
-- ROUND-ROBIN: rotate the starting priority after each grant → FAIR, no starvation.
process (clk) begin
if rising_edge(clk) then
-- grant the next requester at/after 'rr_ptr' (wrapping); then move rr_ptr PAST the grantee.
-- (implemented as a priority encoder starting from rr_ptr, with wraparound)
if grant_valid = '1' then rr_ptr <= grantee + 1; end if; -- rotate priority
end if;
end process;An arbiter maps requests → a one-hot grant. Fixed priority is a priority encoder (lowest index wins) — simple but can starve lower-priority requesters. Round-robin rotates the priority pointer after each grant so service rotates fairly. Required properties: mutual exclusion, liveness (no starvation), fairness.
3. Production usage — arbitrating a shared port
-- Several masters share ONE resource (bus / memory port / FIFO write port):
-- req(0..N-1) : each master asserts when it wants access
-- grant(0..N-1): one-hot; the granted master drives the resource this cycle
-- The granted master typically HOLDS its request until served (and may use valid/ready, 18.2).
--
-- POLICY CHOICE:
-- FIXED PRIORITY → use when there is a TRUE priority (e.g. refresh > data); accept low-prio starvation.
-- ROUND-ROBIN → use for EQUAL peers that must all make progress (fair sharing, no starvation).
-- WEIGHTED / LRU → bias bandwidth or favor the least-recently-served for QoS.
--
-- Combine with backpressure: grant only when the resource is ready; deassert grant when not.What hardware does this become? A small combinational/sequential block: a priority encoder turning the request vector into a one-hot grant, plus (for round-robin) a rotating pointer register that moves past the last grantee so priority circulates. Fixed priority is a single encoder; round-robin is an encoder that starts from the pointer and wraps. The arbiter sits in front of the shared bus/memory/FIFO, and the granted requester drives the resource that cycle — usually holding its request until served and respecting the resource's ready/backpressure. The policy is a few gates' difference, but it decides whether the system is fair or can starve a requester.
4. Structural interpretation — N requests to one grant
5. Simulation interpretation — round-robin fairness vs fixed-priority starvation
All three request constantly: round-robin rotates; fixed priority starves R2
8 cycles6. Debugging example — starvation (or a multi-grant)
Expected: every requester eventually gets the resource, one at a time. Observed (a): a low-priority
requester never gets served while others stay busy (a master hangs/times out). Observed (b): two grants
assert at once and corrupt the shared resource (bus contention). Root cause (a): a fixed-priority arbiter
was used among equal peers, so persistently-busy high-priority requesters starve the lower ones — a
liveness failure. Root cause (b): the grant logic did not enforce one-hot (e.g. missing exit/priority
so multiple requests set grant) — a mutual-exclusion failure. Fix: for equal peers use round-robin (or
weighted/LRU) so priority rotates and every persistent request is eventually granted; ensure the grant is strictly
one-hot (priority-encode; only one bit set). Engineering takeaway: fixed priority can starve equals —
use round-robin for fairness/liveness — and always enforce a one-hot grant for mutual exclusion; check both
properties explicitly (assertions, 15.7).
-- BUG: fixed priority among equals → low-priority requester starves (never granted).
-- (always grants the lowest index that requests)
-- FIX: round-robin — rotate the starting priority past the last grantee so service circulates.
if grant_valid='1' then rr_ptr <= grantee + 1; end if; -- next time, priority starts after grantee7. Common mistakes & what to watch for
- Fixed priority among equals. It starves low-priority requesters; use round-robin (or weighted/LRU) for fair, liveness-safe sharing.
- Non-one-hot grant. Enforce exactly one grant (priority-encode); multiple grants cause resource contention.
- No liveness check. Assert that every persistent request is eventually granted (15.7); starvation is easy to miss in directed tests.
- Ignoring resource readiness. Grant only when the resource can accept; combine with backpressure/valid-ready (18.2).
- Changing grant mid-transaction. Hold the grant for the duration the granted master needs (or until it releases), or transactions tear.
8. Engineering insight & continuity
An arbiter turns N requests into one grant for a shared resource, guaranteeing mutual exclusion, liveness, and fairness: fixed priority is simple but starves equals, while round-robin rotates a priority pointer so service circulates fairly. It is the gatekeeper in front of every shared bus, memory port, or FIFO. Arbiters, FIFOs, and handshakes are the control fabric around a datapath — and separating those two concerns cleanly is the next architectural principle: Datapath and Control Separation, structuring a design into the logic that moves data and the FSM that directs it.