A priority encoder in Verilog takes several request inputs and outputs the binary index of the highest-priority input that is active. Unlike a plain encoder, it gives a defined answer when more than one input is high at the same time, and a valid output tells you whether any input was active at all.
10 min read | Beginner Level
What You'll Learn
- How a priority encoder resolves multiple active inputs, with its truth table and equations
- Three ways to write a priority encoder:
if-else-if,casezand a parameterizedforloop - Why an
if-else-ifchain synthesizes into priority logic here but into a plain mux elsewhere - How to verify every input pattern with a self-checking testbench
What is a Priority Encoder?
An encoder converts N input lines into a log2(N)-bit binary code. A plain encoder assumes that only one input is active at a time. If two inputs go high together, its output is the OR of both codes, which is meaningless.
A priority encoder fixes this by ranking the inputs. When several inputs are active, the output is the index of the one with the highest priority, and lower-priority inputs are ignored. A 4 to 2 priority encoder has four request inputs (req[3:0]), a 2-bit index output (idx) and a valid output. In this article, req[3] has the highest priority.
| req[3] | req[2] | req[1] | req[0] | idx | valid |
|---|---|---|---|---|---|
| 1 | x | x | x | 2'd3 | 1 |
| 0 | 1 | x | x | 2'd2 | 1 |
| 0 | 0 | 1 | x | 2'd1 | 1 |
| 0 | 0 | 0 | 1 | 2'd0 | 1 |
| 0 | 0 | 0 | 0 | 2'd0 | 0 |
An x in the table means "don't care": once a higher-priority input is 1, the lower ones have no effect. Reading the table row by row gives these equations:
idx[1] = req[3] | req[2] idx[0] = req[3] | (~req[2] & req[1]) valid = req[3] | req[2] | req[1] | req[0]
The valid output is required because idx = 0 has two meanings: "only req[0] is active" and "nothing is active". Without valid, the logic that uses the encoder cannot tell them apart.
Syntax
Verilog has no priority encoder keyword. You describe the ranking with one of these constructs inside a combinational always @(*) block.
Basic Forms:
// 1. if-else-if chain: the first true condition wins
always @(*) begin
if (req[3]) idx = 2'd3; // Highest priority, checked first
else if (req[2]) idx = 2'd2;
else if (req[1]) idx = 2'd1;
else idx = 2'd0; // Final else: idx is always assigned
end
// 2. casez with wildcards: ? matches either 0 or 1
always @(*) begin
casez (req)
4'b1??? : idx = 2'd3;
4'b01?? : idx = 2'd2;
4'b001? : idx = 2'd1;
default : idx = 2'd0;
endcase
end
// 3. for loop: the last assignment in the loop wins
always @(*) begin
idx = 0;
for (i = 0; i < N; i = i + 1)
if (req[i]) idx = i;
end
Components:
req- Request inputs, N bits wide (required)idx- Index of the highest-priority active request,$clog2(N)bits wide (required)valid- High when at least one request is active (strongly recommended)
Design: Using if-else-if
The if-else-if chain is the most direct way to write priority, because the order of the conditions is the priority order. See Verilog if-else-if for the statement itself.
module pr_en_4to2 (
input [3:0] req, // Request inputs; req[3] has the highest priority
output reg [1:0] idx, // Index of the highest-priority active request
output reg valid // 1 when any request is active
);
always @(*) begin
valid = 1'b1; // Assume a request is present
if (req[3]) idx = 2'd3; // Checked first, so req[3] always wins
else if (req[2]) idx = 2'd2; // Reached only when req[3] is 0
else if (req[1]) idx = 2'd1; // Reached only when req[3:2] are 0
else if (req[0]) idx = 2'd0;
else begin // No request at all
idx = 2'd0; // Assign idx here too, or a latch is inferred
valid = 1'b0;
end
end
endmodule
Synthesis result: An if-else-if chain describes a priority mux chain: a cascade of 2 to 1 muxes where req[3] controls the stage nearest the output. A lower-priority request passes through more stages, so in the raw structure its path to idx is longer. For a small encoder like this one, the synthesis tool then flattens the chain into the two-level equations shown in the truth table section.
Why the Priority Stays in This Design
The same if-else-if syntax can also describe a multiplexer, as in if (sel == 2'b00) ... else if (sel == 2'b01) .... In that case every condition compares one signal against a different constant, so only one condition can ever be true. Synthesis detects this and removes the priority, producing a plain parallel mux (see Verilog 4 to 1 Multiplexer).
In a priority encoder, each condition tests a different request bit. req[3] and req[1] can both be 1 at the same time, so the order of the conditions changes the result. Synthesis must keep that ordering, which is what makes this circuit a priority encoder.
Quick Check
Q: With req = 4'b0110, what are idx and valid?
Show Answer
idx = 2 and valid = 1. Both req[2] and req[1] are active, but req[3] is 0, so the first true condition is req[2]. req[1] is ignored.
Where is a Priority Encoder Used?
Interrupt controllers use a priority encoder to pick which pending interrupt the processor services first. Each interrupt source sets one request bit, and the encoder outputs the index of the highest-priority pending source. The processor uses that index to look up the matching handler in its interrupt vector table.
Design: Using casez
A casez statement treats ? (and z) in a case item as a wildcard that matches 0 or 1. This lets you write the truth table rows almost exactly as they appear above. See Verilog case statement for how case, casez and casex differ.
module pr_en_4to2_casez (
input [3:0] req, // req[3] has the highest priority
output reg [1:0] idx,
output reg valid
);
always @(*) begin
valid = 1'b1;
casez (req)
4'b1??? : idx = 2'd3; // req[3] set: lower bits don't matter
4'b01?? : idx = 2'd2; // req[3] clear, req[2] set
4'b001? : idx = 2'd1; // req[3:2] clear, req[1] set
4'b0001 : idx = 2'd0; // Only req[0] set
default : begin // req = 4'b0000: no request
idx = 2'd0;
valid = 1'b0;
end
endcase
end
endmodule
Synthesis result: Each item includes the leading zeros of the higher-priority bits, so no two items can match the same input. The priority is encoded in the patterns themselves, and synthesis builds the same logic as the if-else-if version.
If you write overlapping items such as 4'b1??? followed by 4'b?1??, casez still picks the first matching item, so the order sets the priority. Writing non-overlapping patterns makes the intent visible in the code and does not depend on item order.
Fixed-Priority Bus Arbiters
When several bus masters, such as a CPU core, a DMA engine and a display controller, request the same memory bus in the same cycle, a fixed-priority arbiter grants access to one of them. Its core is a priority encoder over the request lines, and its output selects which master drives the bus. Designers place latency-critical masters at the top of the priority order.
Design: Parameterized with a for Loop
For wide encoders, such as 16, 32 or 64 inputs, writing every condition by hand is error-prone. A for loop inside the always block scales to any width set by a parameter.
module pr_en #(
parameter N = 8, // Number of request inputs
parameter W = $clog2(N) // Index width; derived from N, do not override
) (
input [N-1:0] req, // req[N-1] has the highest priority
output reg [W-1:0] idx,
output reg valid
);
integer i;
always @(*) begin
idx = {W{1'b0}}; // Defaults prevent latches when req is all 0
valid = 1'b0;
// Scan from the lowest to the highest bit. Each active bit overwrites
// idx, so the last write comes from the highest active bit.
for (i = 0; i < N; i = i + 1) begin
if (req[i]) begin
idx = i;
valid = 1'b1;
end
end
end
endmodule
Synthesis result: The synthesis tool unrolls the loop into N if statements in sequence. A later assignment overrides an earlier one, so the unrolled code is a priority chain with req[N-1] at the top, which is the same structure as the if-else-if version. The loop does not run in hardware; it only generates the logic. See Verilog for Loop for more.
Priority Encoder vs Multiplexer
Both circuits often appear together in a design, and both can be written with similar syntax, but they do different jobs.
| Priority Encoder | Multiplexer | |
|---|---|---|
| Inputs | N request bits | N data words plus a select |
| Output | Index (log2N bits) and valid | One data word |
| Several inputs active | Highest-priority input wins | Not applicable; select picks exactly one |
| Typical code | if-else-if on different bits, casez, for loop | case on select, conditional operator |
| Synthesizes into | Priority logic | Parallel mux |
The two are commonly combined: in an arbiter, the priority encoder produces an index, and that index drives the select input of a mux that forwards the winning master's address and data.
Hardware Schematic
For the 4 to 2 encoder, synthesis reduces all three coding styles to the equations from the truth table:
- idx[1]: one 2-input OR gate (
req[3] | req[2]) - idx[0]: an inverter on req[2], a 2-input AND with req[1], and a 2-input OR with req[3]
- valid: one 4-input OR gate (or a tree of 2-input ORs)
On an FPGA, each output depends on at most 4 inputs, so each fits in a single LUT.
PPA Impact: A literal priority chain has a delay that grows linearly with N, because the lowest-priority request passes through every stage. For wide encoders, synthesis tools restructure the logic into a tree whose depth grows with log2N. Even so, 32-bit and 64-bit priority encoders are often on the critical path. They are frequently built as a tree of small 4-bit encoders.
Leading-One Detection in Arithmetic Units
Floating-point units must normalize results by shifting the mantissa until its most significant 1 sits in the leading position. A priority encoder over the mantissa bits finds the position of that leading 1, and the index sets the shift amount. Count-leading-zeros instructions in processors use the same circuit.
Testbench
A 4-input encoder has only 16 possible input patterns, so the testbench checks every one of them. For each pattern it computes the expected idx and valid with a simple reference model and reports any mismatch. This makes the test self-checking: you read one PASS or FAIL line instead of comparing every value by hand. See Verilog Testbench for testbench structure.
module tb_pr_en;
reg [3:0] req; // Drives the design input
wire [1:0] idx; // Collects the design outputs
wire valid;
reg [1:0] exp_idx; // Expected values from the reference model
reg exp_valid;
integer i, errors;
// Device under test: the if-else-if version
pr_en_4to2 dut (.req(req), .idx(idx), .valid(valid));
initial begin
errors = 0;
// Apply all 16 input patterns, one every 5 time units
for (i = 0; i < 16; i = i + 1) begin
req = i;
#5;
// Reference model: written differently from the design on purpose
exp_valid = |req;
exp_idx = req[3] ? 2'd3 :
req[2] ? 2'd2 :
req[1] ? 2'd1 : 2'd0;
$display("[%0t] req=%b idx=%0d valid=%b", $time, req, idx, valid);
// !== compares x and z as well, so an undriven output is caught
if (idx !== exp_idx || valid !== exp_valid) begin
$display(" ERROR: expected idx=%0d valid=%b", exp_idx, exp_valid);
errors = errors + 1;
end
end
if (errors == 0) $display("PASS: all 16 input patterns");
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmodule
Because all three designs have the same ports, you can swap pr_en_4to2 for pr_en_4to2_casez, or for pr_en #(.N(4)), and rerun the same testbench.
Quick Check
Q: In the for loop design, what happens if you change the loop to count down, from i = N-1 to 0?
Show Answer
The last assignment would come from the lowest active bit, so req[0] would get the highest priority. Loop direction sets the priority order in this style.
Common Beginner Mistakes
Mistake #1: Using a plain case statement with full patterns
Wrong: case (req) 4'b1000: idx = 3; 4'b0100: idx = 2; ... endcase
Right: casez (req) 4'b1???: idx = 3; 4'b01??: idx = 2; ... endcase
Why? A plain case compares every bit exactly, so 4'b1000 matches only that one pattern. An input such as 4'b1100 matches no item and falls to the default, even though req[3] is active. This builds a one-hot encoder, not a priority encoder. Use casez with ? for the lower-priority bits.
Mistake #2: Leaving out the valid output
Wrong: An encoder with only an idx output
Right: Add output reg valid and set it to |req
Why? idx = 0 is the output for both "req[0] is the only request" and "no request". The downstream logic, for example an interrupt controller, would service interrupt 0 even when nothing is pending.
Mistake #3: No final else or default branch
Wrong: if (req[3]) idx = 3; else if (req[2]) idx = 2; else if (req[1]) idx = 1; else if (req[0]) idx = 0;
Right: End the chain with else idx = 0;, or assign defaults at the top of the block
Why? When req is 4'b0000, no branch assigns idx, so it must keep its old value. Holding a value requires memory, and synthesis infers a latch on idx. The for loop design avoids this by assigning defaults before the loop.
Mistake #4: Scanning the for loop in the wrong direction
Wrong: for (i = N-1; i >= 0; i = i - 1) if (req[i]) idx = i; when req[N-1] should win
Right: for (i = 0; i < N; i = i + 1) if (req[i]) idx = i;
Why? In this style, the last assignment wins, so the bit scanned last gets the highest priority. Counting down makes req[0] the winner. The code compiles and simulates without warnings, so this bug shows up only when two requests are active together. Testing every input pattern, as the testbench above does, catches it.
Practice Exercise
Challenge: 8 to 3 Priority Encoder with req[0] as Highest Priority
Write a module pr_en_8to3_low with an 8-bit req input, a 3-bit idx output and a valid output. Unlike the designs above, req[0] must have the highest priority and req[7] the lowest. Use a for loop.
Hint
The last assignment in the loop wins. To make req[0] win, it must be the last bit the loop visits, so scan from bit 7 down to bit 0.
Solution
module pr_en_8to3_low (
input [7:0] req, // req[0] has the highest priority
output reg [2:0] idx,
output reg valid
);
integer i;
always @(*) begin
idx = 3'd0; // Defaults for req = 8'b0
valid = 1'b0;
// Scan from bit 7 down to bit 0, so bit 0 writes last and wins
for (i = 7; i >= 0; i = i - 1) begin
if (req[i]) begin
idx = i;
valid = 1'b1;
end
end
end
endmodule
For example, with req = 8'b1010_0100 the active bits are 7, 5 and 2. Bit 2 is visited last, so idx = 3'd2. Synthesis unrolls the loop into a priority chain with req[0] at the top.
Summary
In this tutorial, you learned:
- A priority encoder outputs the index of the highest-priority active request and ignores lower-priority ones
- A
validoutput is needed to tell "no request" apart from "request 0" - The order of an
if-else-ifchain is the priority order, and it synthesizes into priority logic because the conditions test different bits - The same
if-else-ifsyntax on one select signal becomes a plain mux, because those conditions are mutually exclusive casezwith?wildcards lets you write the truth table directly- A
forloop scales to any width, and the loop direction sets which bit has the highest priority - Assign defaults or end with
else/defaultto avoid latches, and test every input pattern