A Better Priority Arbiter

from FPGA Resources by GateForge Consulting Ltd.

A priority arbiter is a useful building block, but the usual manual implementations using case statements or chains of assign statements are hard to follow, and must be manually adjusted each time the number of requestors changes. For reference, see Section 3 of Matt Weber's Arbiters: Design Ideas and Coding Styles, which is dated and very ASIC-centric, but contains useful diagrams and discussion.

A better description of a priority arbiter comes from Henry S. Warren Jr.'s Hacker's Delight, which describes many, many techniques using integer arithmetic extended with Boolean operations (and vice-versa) to create branch-free implementations of common and not-so-common operations. By being branch-free, these implementations map well to hardware, and by using integer arithmetic, provide a compact, clear way to express calculations across the bits of a word via the arithmetic carry bits.

In Chapter 2 ("Basics", available on his website), he shows how a subtraction followed by a bitwise AND isolates the right-most (Least Significant) bit set, or returns zero if none set. For example:

00000 --> 00000
01101 --> 00001
01100 --> 00100

If we take the bits, right-to-left, as requests of higest-to-lowest priority, we can use this simple operation to describe a priority arbiter. And since we express it as integer arithmetic and Booleans, it will map quite naturally to the adder carry-chain and LUT logic of an FPGA. The carry-chain provides a high-speed right-to-left connection between each bit in the word. Wrapping that one line of code into a module indicates its meaning, and allows trivial parameterization to any number of requestors.

// Simple priority arbiter.
// Returns the LSB set, where bit 0 has highest priority

`default_nettype none

module Priority_Arbiter
    parameter       WORD_WIDTH          = 0
    input   wire    [WORD_WIDTH-1:0]    requests,
    output  reg     [WORD_WIDTH-1:0]    grant

    initial begin
        grant = 0;

    always @(*) begin
        grant = requests & -requests;