Priority arbiters in Verilog
An arbiter answers one question: several requesters want a shared resource (a bus, a memory port, a DMA channel), so who gets it this cycle? It comes up constantly in interviews because a small prompt covers a lot of ground: combinational design, priority encoding, one-hot outputs, and fairness if there’s time.
The contract for a fixed-priority arbiter: req[i] in, one-hot grant out,
lowest index wins.
Take 1: the if-else chain
Priority is exactly what an if/else chain expresses: earlier branches win.
always @(*) begin
grant = 4'b0000; // default: no grant (and no latch)
if (req[0]) grant = 4'b0001;
else if (req[1]) grant = 4'b0010;
else if (req[2]) grant = 4'b0100;
else if (req[3]) grant = 4'b1000;
end
Live and editable. Try reordering the branches and watch the priority structure move through the logic:
Interactive schematic by RapidRTL
Note the grant = 0 default on the first line. Without it, the no-requests
case assigns nothing and you’ve built a latch, the classic arbiter bug and
the subject of its own guide.
Take 2: casez
The same priority, table-style. casez treats ? as don’t-care, so each row
reads as “this bit set, lower bits clear, higher bits irrelevant”:
always @(*) begin
casez (req)
4'b???1: grant = 4'b0001;
4'b??10: grant = 4'b0010;
4'b?100: grant = 4'b0100;
4'b1000: grant = 4'b1000;
default: grant = 4'b0000; // no requests, no grant, no latch
endcase
end
Synthesizes to the same hardware as Take 1. Choose whichever your team reads
faster, and never drop the default. If casez is new to you, the
case statement family guide covers it,
its banned cousin casex, and the SystemVerilog and VHDL equivalents.
Take 3: the req & -req one-liner
In two’s complement, -req is ~req + 1. Work through what that does to the
bits and it turns out that req & -req isolates the lowest set bit.
That’s the whole arbiter:
assign grant = req & (~req + 4'd1);
Here’s what that one line actually builds: an adder carrying the priority information in its carry chain (what adders cost, and why the carry chain is always the interesting part, is the adder architectures guide).
Interactive schematic by RapidRTL
Two things worth being able to say out loud, because the follow-up questions are predictable:
- Why does it work? Adding 1 to
~reqripples a carry through the low 1-bits of~req(which are the low 0-bits ofreq) and stops at the first 0 of~req, i.e. the first 1 ofreq. Everything above keeps its inverted value and the AND clears it. - Is it better hardware? Not necessarily. You’ve asked for an adder, and the tools’ carry chains make it compact on FPGAs, but the if-else chain synthesizes to plain priority logic that’s just as good and more readable. The one-liner’s real value is that you understand it, and can say why.
Extending to round-robin
Fixed priority starves the high-index requesters. The standard fix keeps a mask of “requesters after the last grant”: arbitrate the masked requests first, and only if none are pending fall back to the unmasked ones. That’s two priority arbiters plus a mux and a register for the pointer. Try building it in the editor from the pieces on this page; the schematic makes the two-arbiter structure easy to see.
The pitfalls checklist
- A missing default assignment builds a latch. The single most common arbiter bug.
- Grant must be one-hot. A stray non-exclusive
if(instead ofelse if) can grant two requesters at once. - Registered vs combinational grants: combinational arbiters can glitch during request changes; bus protocols usually want the grant registered.
- Fairness is a spec question, not an afterthought. Say “fixed priority starves; round-robin if requesters are peers” before anyone asks.