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, casez and a parameterized for loop
  • Why an if-else-if chain 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]idxvalid
1xxx2'd31
01xx2'd21
001x2'd11
00012'd01
00002'd00

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 EncoderMultiplexer
InputsN request bitsN data words plus a select
OutputIndex (log2N bits) and validOne data word
Several inputs activeHighest-priority input winsNot applicable; select picks exactly one
Typical codeif-else-if on different bits, casez, for loopcase on select, conditional operator
Synthesizes intoPriority logicParallel 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 valid output is needed to tell "no request" apart from "request 0"
  • The order of an if-else-if chain is the priority order, and it synthesizes into priority logic because the conditions test different bits
  • The same if-else-if syntax on one select signal becomes a plain mux, because those conditions are mutually exclusive
  • casez with ? wildcards lets you write the truth table directly
  • A for loop scales to any width, and the loop direction sets which bit has the highest priority
  • Assign defaults or end with else/default to avoid latches, and test every input pattern
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5