8-to-3 Priority Encoder Verilog Code with Valid Flag

A priority encoder collapses a set of request lines into a compact binary index, reporting which of several simultaneous requests is the most important. The 8-to-3 version accepts eight inputs and produces a 3-bit code plus a valid flag that tells downstream logic whether any request was active at all. This tutorial builds a clean, synthesizable 8-to-3 priority encoder in Verilog and then verifies the one behavior every encoder must get right: priority tie-breaking when more than one input is high at the same time.

module priority_encoder_8to3 (
    input  wire [7:0] req,    // request lines, req[7] = highest priority
    output reg  [2:0] code,   // index of highest active request
    output reg        valid   // 1 when at least one request is active
);
    always @(*) begin
        valid = 1'b1;
        if      (req[7]) code = 3'd7;
        else if (req[6]) code = 3'd6;
        else if (req[5]) code = 3'd5;
        else if (req[4]) code = 3'd4;
        else if (req[3]) code = 3'd3;
        else if (req[2]) code = 3'd2;
        else if (req[1]) code = 3'd1;
        else if (req[0]) code = 3'd0;
        else begin
            code  = 3'd0;     // defined output when idle
            valid = 1'b0;
        end
    end
endmodule

module tb_priority_encoder_8to3;
    reg  [7:0] req;
    wire [2:0] code;
    wire       valid;

    priority_encoder_8to3 dut (.req(req), .code(code), .valid(valid));

    initial begin
        $display("  time |   req      | code | valid | note");
        $display("-------+------------+------+-------+--------------------");
        req = 8'b0000_0000; #5
          $display(" %4t | %b |  %0d   |   %b   | no request", $time, req, code, valid);
        req = 8'b0000_0001; #5
          $display(" %4t | %b |  %0d   |   %b   | only bit0", $time, req, code, valid);
        req = 8'b1000_0000; #5
          $display(" %4t | %b |  %0d   |   %b   | only bit7", $time, req, code, valid);
        req = 8'b1010_0100; #5
          $display(" %4t | %b |  %0d   |   %b   | tie 7,5,2 -> 7 wins", $time, req, code, valid);
        req = 8'b0011_0010; #5
          $display(" %4t | %b |  %0d   |   %b   | tie 5,4,1 -> 5 wins", $time, req, code, valid);
        req = 8'b0000_0110; #5
          $display(" %4t | %b |  %0d   |   %b   | tie 2,1 -> 2 wins", $time, req, code, valid);
        $display("-------+------------+------+-------+--------------------");
        $display("Priority tie-breaking verified: highest active index always wins.");
        $finish;
    end
endmodule
time |   req      | code | valid | note
-------+------------+------+-------+--------------------
    5 | 00000000 |  0   |   0   | no request
   10 | 00000001 |  0   |   1   | only bit0
   15 | 10000000 |  7   |   1   | only bit7
   20 | 10100100 |  7   |   1   | tie 7,5,2 -> 7 wins
   25 | 00110010 |  5   |   1   | tie 5,4,1 -> 5 wins
   30 | 00000110 |  2   |   1   | tie 2,1 -> 2 wins
-------+------------+------+-------+--------------------
Priority tie-breaking verified: highest active index always wins.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpooof6dna/priority_encoder_8to3.v:47: $finish called at 30 (1s)

How the Verilog code works

The design is written behaviorally inside a single always @(*) block, so the synthesis tool infers pure combinational logic rather than a clocked register. Because there is no clock or reset, the output follows the inputs the instant they change, which is exactly what an encoder should do.

The heart of the module is the if / else if ladder. In Verilog an if chain has an implicit top-to-bottom priority: the first condition that is true wins, and every later branch is skipped. By testing req[7] first and req[0] last, the ladder encodes a fixed rule — the highest-numbered active request always determines the output. That is precisely how tie-breaking is resolved when several request bits are high together.

Two details keep the logic safe for synthesis. First, valid is assigned 1'b1 at the top of the block and only pulled low in the final else, so it always has a known value. Second, the trailing else assigns both code and valid, meaning every signal is driven on every path. Fully assigning the outputs on all branches is what stops the tool from inferring an unwanted latch.

Why the valid flag matters

Without a valid flag the output is ambiguous. A code of 3'b000 could mean “only req[0] is active” or “nothing is active at all,” and downstream logic cannot tell the two apart. The valid bit removes that ambiguity: it is 1 whenever at least one request is asserted and 0 only when the whole input bus is idle. The simulation log above shows the idle vector producing code = 0, valid = 0, while the single-bit-0 vector produces code = 0, valid = 1.

req[7:0]codevalidreason
0000000000no request active
0000000101only bit 0
1010010071bits 7,5,2 tie, bit 7 wins
0011001051bits 5,4,1 tie, bit 5 wins

A self-checking casez alternative

The if / else if style is readable, but many engineers prefer a casez statement because the wildcard pattern makes the priority order visually obvious. In casez, the ? symbol means “don’t care,” so the first pattern 8'b1???_???? matches any vector whose top bit is set, regardless of the lower bits. The patterns are checked in order, giving the same top-down priority as the ladder. The testbench below is self-checking: it compares the encoder against an independent reference function that scans for the most-significant set bit, and prints PASS or FAIL for each vector.

module priority_encoder_casez (
    input  wire [7:0] req,
    output reg  [2:0] code,
    output reg        valid
);
    always @(*) begin
        valid = 1'b1;
        casez (req)                    // ? means "don't care" in casez
            8'b1???_????: code = 3'd7;
            8'b01??_????: code = 3'd6;
            8'b001?_????: code = 3'd5;
            8'b0001_????: code = 3'd4;
            8'b0000_1???: code = 3'd3;
            8'b0000_01??: code = 3'd2;
            8'b0000_001?: code = 3'd1;
            8'b0000_0001: code = 3'd0;
            default: begin code = 3'd0; valid = 1'b0; end
        endcase
    end
endmodule

module tb_priority_encoder_casez;
    reg  [7:0] req;
    wire [2:0] code;
    wire       valid;
    integer    i;
    integer    errors;
    reg  [3:0] exp;
    reg        ok;

    priority_encoder_casez dut (.req(req), .code(code), .valid(valid));

    // reference: index of the most significant set bit, 8 means "none"
    function [3:0] msb_index;
        input [7:0] v;
        integer k;
        begin
            msb_index = 4'd8;
            for (k = 0; k < 8; k = k + 1)
                if (v[k]) msb_index = k[3:0];
        end
    endfunction

    initial begin
        errors = 0;
        $display(" req      | code valid | expected | result");
        $display("----------+------------+----------+-------");
        for (i = 0; i < 8; i = i + 1) begin
            // several bits set at once to stress priority tie-breaking
            req = (8'b1 << i) | (8'b1 << (i / 2));
            #5;
            exp = msb_index(req);
            ok  = (valid && (code === exp[2:0]));
            if (!ok) errors = errors + 1;
            $display(" %b |  %0d    %b   |    %0d     | %s",
                     req, code, valid, exp, ok ? "PASS" : "FAIL");
        end
        req = 8'b0000_0000;
        #5;
        ok = (valid == 1'b0);
        if (!ok) errors = errors + 1;
        $display(" %b |  %0d    %b   |  none    | %s",
                 req, code, valid, ok ? "PASS" : "FAIL");
        $display("----------+------------+----------+-------");
        if (errors == 0)
            $display("ALL CHECKS PASSED: casez encoder matches MSB-priority reference.");
        else
            $display("FAILURES: %0d", errors);
        $finish;
    end
endmodule
req      | code valid | expected | result
----------+------------+----------+-------
 00000001 |  0    1   |    0     | PASS
 00000011 |  1    1   |    1     | PASS
 00000110 |  2    1   |    2     | PASS
 00001010 |  3    1   |    3     | PASS
 00010100 |  4    1   |    4     | PASS
 00100100 |  5    1   |    5     | PASS
 01001000 |  6    1   |    6     | PASS
 10001000 |  7    1   |    7     | PASS
 00000000 |  0    0   |  none    | PASS
----------+------------+----------+-------
ALL CHECKS PASSED: casez encoder matches MSB-priority reference.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp729ilj4s/priority_encoder_casez.v:69: $finish called at 45 (1s)

Common mistakes to avoid

  • Dropping the final else/default. If a branch leaves code or valid unassigned, the combinational block infers a latch and simulation shows red X values. Always drive every output on every path.
  • Using nonblocking assignments. Inside a combinational always @(*) block, use blocking =. Nonblocking <= is for clocked (sequential) logic and can produce misleading simulation here.
  • Reaching for casex. casex treats unknown x values in the input as don’t-cares too, which can silently mask a real unknown. casez only wildcards high-impedance and ?, so it is the safer choice.
  • Encoding the wrong priority direction. Decide early whether the most-significant or least-significant active bit should win, and order the branches to match. Reversing them quietly flips the tie-breaking rule.
  • Leaving code undefined when valid is 0. Even when nothing is requested, assign a known value such as 3'd0 so downstream logic never samples an X.

Quick check

If req[7], req[3] and req[0] are all high at once, what is the 3-bit code?

7 (binary 111). The if ladder tests req[7] first, so the highest-numbered active request wins the tie and lower requests are ignored.

Why does the encoder need a separate valid output?

Because code = 000 is ambiguous on its own: it could mean “only request 0 is active” or “no request is active.” The valid flag is 1 whenever any input is asserted and 0 only when the bus is idle, so downstream logic can tell the two cases apart.

What unwanted hardware is inferred if you omit the else/default branch?

A latch. In a combinational block, any path that leaves an output unassigned forces the tool to hold the previous value, which requires a latch and usually signals a design bug.

What is the difference between casez and casex for the wildcard?

casez treats z (high impedance) and ? as don’t-cares. casex additionally treats x (unknown) as a don’t-care, which can hide genuine unknown values during simulation, so casez is generally preferred for priority encoders.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD