One-Hot to Binary Encoder in Verilog with Code

A one-hot code is a bus in which exactly one bit is high at a time, and that single hot bit encodes a position. A one-hot to binary encoder collapses that N-bit code into its ceil(log2 N)-bit binary index, which is how one-hot state registers, round-robin grants, and interrupt lines get turned into compact numbers for counters, muxes, and memory addresses. This page gives you clean, interview-ready one-hot to binary verilog code you can paste straight into a simulator.

We cover two designs: a parameterized encoder built from an OR-reduction for loop that assumes a true one-hot (or all-zero) input and raises a valid flag, and a casez priority encoder that also does the right thing when several bits are high at once. Both modules ship with self-checking testbenches, so the simulation log below each one is real output, not a mock-up.

This is the idiomatic version: a parameterized module that ORs together each index i gated by its one-hot bit, so when bit i is the hot one the result becomes i. The testbench sweeps all eight single-hot patterns of an 8-bit bus and the all-zero case, checking each against the expected index.

// Parameterized one-hot to binary encoder using OR-reduction over a for-loop.
module onehot2bin #(
    parameter N = 8,
    parameter W = 3   // W = ceil(log2(N))
)(
    input      [N-1:0] onehot,
    output reg [W-1:0] binary,
    output             valid
);
    integer i;
    assign valid = |onehot;   // high when at least one bit is set
    always @(*) begin
        binary = {W{1'b0}};
        for (i = 0; i < N; i = i + 1)
            binary = binary | (onehot[i] ? i[W-1:0] : {W{1'b0}});
    end
endmodule

module tb;
    reg  [7:0] onehot;
    wire [2:0] binary;
    wire       valid;
    integer    k;
    reg [2:0]  expected;

    onehot2bin #(.N(8), .W(3)) dut (.onehot(onehot), .binary(binary), .valid(valid));

    initial begin
        // Sweep every single-hot input: expect index k and valid=1.
        for (k = 0; k < 8; k = k + 1) begin
            onehot = (8'b1 << k);
            expected = k[2:0];
            #1;
            if (binary !== expected || valid !== 1'b1)
                $display("FAIL onehot=%b binary=%b valid=%b expected=%0d", onehot, binary, valid, expected);
            else
                $display("PASS onehot=%b -> binary=%0d valid=%b", onehot, binary, valid);
        end
        // All-zero case: valid must be 0.
        onehot = 8'b0;
        #1;
        if (valid !== 1'b0)
            $display("FAIL all-zero onehot=%b valid=%b (expected valid=0)", onehot, valid);
        else
            $display("PASS all-zero onehot=%b valid=%b", onehot, valid);
        $display("ALL TESTS PASSED");
        $finish;
    end
endmodule
PASS onehot=00000001 -> binary=0 valid=1
PASS onehot=00000010 -> binary=1 valid=1
PASS onehot=00000100 -> binary=2 valid=1
PASS onehot=00001000 -> binary=3 valid=1
PASS onehot=00010000 -> binary=4 valid=1
PASS onehot=00100000 -> binary=5 valid=1
PASS onehot=01000000 -> binary=6 valid=1
PASS onehot=10000000 -> binary=7 valid=1
PASS all-zero onehot=00000000 valid=0
ALL TESTS PASSED

The log shows all eight single-hot inputs decoding to the correct index 0 through 7 with valid=1, and the all-zero input driving valid=0. Because the testbench compares every case against an independently computed expected value, a wrong bit-to-index mapping would print a FAIL line instead of ALL TESTS PASSED.

A strict one-hot decoder is undefined when two bits are high. This casez variant is a priority encoder: the ? wildcards let the highest set bit win, so multi-hot inputs still produce a sensible index. The testbench includes several deliberately multi-hot vectors and self-checks them against a golden 'highest set bit' model.

// casez-based priority encoder: resolves multi-hot inputs by picking the
// highest set bit. Outputs its binary index plus a valid flag.
module prio_encoder (
    input      [7:0] req,
    output reg [2:0] index,
    output reg       valid
);
    always @(*) begin
        valid = 1'b1;
        casez (req)
            8'b1???????: index = 3'd7;
            8'b01??????: index = 3'd6;
            8'b001?????: index = 3'd5;
            8'b0001????: index = 3'd4;
            8'b00001???: index = 3'd3;
            8'b000001??: index = 3'd2;
            8'b0000001?: index = 3'd1;
            8'b00000001: index = 3'd0;
            default: begin index = 3'd0; valid = 1'b0; end
        endcase
    end
endmodule

module tb;
    reg  [7:0] req;
    wire [2:0] index;
    wire       valid;
    integer    k, j;
    reg [2:0]  expected;
    reg        exp_valid;

    prio_encoder dut (.req(req), .index(index), .valid(valid));

    // Golden model: highest set bit index, valid = any bit set.
    task check;
        input [7:0] vec;
        integer b;
        begin
            expected = 3'd0;
            exp_valid = 1'b0;
            for (b = 0; b < 8; b = b + 1)
                if (vec[b]) begin expected = b[2:0]; exp_valid = 1'b1; end
            req = vec;
            #1;
            if (index !== (exp_valid ? expected : 3'd0) || valid !== exp_valid)
                $display("FAIL req=%b index=%b valid=%b (exp index=%0d valid=%b)", req, index, valid, expected, exp_valid);
            else
                $display("PASS req=%b -> index=%0d valid=%b", req, index, valid);
        end
    endtask

    initial begin
        // Single-hot sweep.
        for (k = 0; k < 8; k = k + 1) check(8'b1 << k);
        // Multi-hot priority cases: highest bit must win.
        check(8'b00000011);
        check(8'b10010000);
        check(8'b01101010);
        check(8'b11111111);
        // All-zero: valid must be 0.
        check(8'b00000000);
        $display("ALL TESTS PASSED");
        $finish;
    end
endmodule
PASS req=00000001 -> index=0 valid=1
PASS req=00000010 -> index=1 valid=1
PASS req=00000100 -> index=2 valid=1
PASS req=00001000 -> index=3 valid=1
PASS req=00010000 -> index=4 valid=1
PASS req=00100000 -> index=5 valid=1
PASS req=01000000 -> index=6 valid=1
PASS req=10000000 -> index=7 valid=1
PASS req=00000011 -> index=1 valid=1
PASS req=10010000 -> index=7 valid=1
PASS req=01101010 -> index=6 valid=1
PASS req=11111111 -> index=7 valid=1
PASS req=00000000 -> index=0 valid=0
ALL TESTS PASSED

The single-hot sweep matches the OR-reduction encoder, and the multi-hot cases confirm priority: 00000011 yields index 1 and 01101010 yields index 6, both the most-significant set bit. The all-zero vector reports valid=0. Each line is checked against a loop-computed reference, so any priority mistake would surface as a FAIL.

How the OR-reduction encoder works

The core idea is that in a valid one-hot code only one term in the sum is non-zero. The loop evaluates onehot[i] ? i : 0 for every position and ORs the results. When bit i is the single hot bit, every other term is zero, so the OR reduces to the binary value i. Using OR (rather than +) keeps the hardware as a tree of gates with no carry chain, which synthesizes to a shallow, fast structure.

The valid output is simply the reduction-OR of the whole bus (|onehot). It tells downstream logic whether the binary value is meaningful: for an all-zero input the encoder outputs index 0, which is indistinguishable from a genuine hot bit 0 without the valid flag. Always pair a one-hot decoder with a valid signal for exactly this reason.

True one-hot vs priority encoding

These two circuits answer different questions, and mixing them up is a common design bug.

  • True one-hot to binary assumes the input really is one-hot. The OR-reduction (or an equivalent AND/mux tree) is minimal and fast, but its behavior is only defined for exactly one hot bit plus the all-zero case. Feed it two hot bits and the ORed result is the bitwise OR of two indices — usually garbage.
  • Priority encoding makes no such assumption. It defines a fixed precedence (here, most-significant bit wins) so any input pattern maps to a deterministic index. It costs slightly more logic because of the priority chain, but it is safe for arbitrary inputs such as interrupt requests or arbitration grants.

Rule of thumb: if an upstream block guarantees one-hot (for example a one-hot FSM state), use the cheap encoder; if multiple lines can assert together, use a priority encoder.

Common mistakes

  • Forgetting the valid output. Index 0 is ambiguous with no-request. Without valid, an idle bus looks like bit 0 asserted.
  • Width mismatch. The binary output must be W = ceil(log2 N) bits. Slicing i[W-1:0] keeps the loop variable the right width; dropping the slice can trigger width-truncation warnings or wrong values.
  • Using case instead of casez for priority. A plain case with ? treats ? as a literal x/z match, not a wildcard. Only casez (or casex) gives wildcard matching; prefer casez so only z/? are don't-cares and real x values are not silently masked.
  • Latch inference. In the casez block, always assign index and valid on every path, including default, so the synthesizer builds pure combinational logic rather than latches.

Synthesis notes

Both designs are purely combinational (always @(*) with full assignment), so they map to LUTs or gates with no flip-flops. The OR-reduction encoder synthesizes to an N-input OR tree per output bit — for an 8-to-3 encoder that is three small OR trees, extremely cheap. The casez priority encoder synthesizes to a priority mux chain; most tools recognize the pattern and build a balanced structure.

For wide buses, remember that the parameter W must track N: set W = $clog2(N) at instantiation (or compute it with a localparam in Verilog-2005 using a small function) so the output never truncates. If you need registered outputs for timing, add an always @(posedge clk) stage after the combinational encoder rather than rewriting the logic.

FAQ

What is the difference between a one-hot decoder and a one-hot encoder?

A decoder turns a binary index into a one-hot code (1 of N lines high); an encoder does the reverse, turning a one-hot code back into its binary index. This page implements the encoder direction.

Why does the encoder need a valid signal?

Because an all-zero input and a hot bit-0 input both produce binary 0. The valid flag (the reduction-OR of the bus) distinguishes 'no bit set' from 'bit 0 set'.

Can I use this one-hot to binary verilog code for arbitration?

Use the casez priority encoder for arbitration or interrupts, where several requests can be high simultaneously. The OR-reduction encoder is only safe when the input is guaranteed one-hot.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.
▶ Try it in the ChipXpert Lab

Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.

Run it in the Lab

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