Decoder Verilog Code: 2-to-4 and 3-to-8 With Enable

A decoder takes an n-bit binary input and activates exactly one of its 2n outputs, making it the building block behind address selection, memory chip-enables, and multiplexer control. This tutorial hands you working decoder Verilog code for both a 2-to-4 and a 3-to-8 line decoder, each with an enable, written in both the behavioral and dataflow styles, plus a self-checking testbench. Every block below was compiled and simulated with Icarus Verilog, so the simulation logs you see are the real output, not a mock-up.

2-to-4 decoder: behavioral style

The behavioral version describes what the circuit does using an always block and a case statement. The enable is checked first: when en is low, the output is forced to all zeros; otherwise the 2-bit sel input picks which single output line goes high.

// 2-to-4 line decoder, behavioral style, active-high enable and outputs
module decoder_2to4_beh (
    input  wire       en,     // active-high enable
    input  wire [1:0] sel,    // 2-bit select input
    output reg  [3:0] y       // 4-bit one-hot output
);
    always @(*) begin
        if (!en)
            y = 4'b0000;              // disabled: all outputs low
        else begin
            case (sel)
                2'b00: y = 4'b0001;
                2'b01: y = 4'b0010;
                2'b10: y = 4'b0100;
                2'b11: y = 4'b1000;
                default: y = 4'b0000;
            endcase
        end
    end
endmodule

// Testbench
module tb_decoder_2to4_beh;
    reg        en;
    reg  [1:0] sel;
    wire [3:0] y;
    integer    i;

    decoder_2to4_beh dut (.en(en), .sel(sel), .y(y));

    initial begin
        $display("time en sel | y");
        $monitor("%4t  %b  %b | %b", $time, en, sel, y);

        // Enable low: output must stay 0 regardless of sel
        en = 1'b0; sel = 2'b10; #10;

        // Enable high: sweep every select value
        en = 1'b1;
        for (i = 0; i < 4; i = i + 1) begin
            sel = i[1:0];
            #10;
        end

        $display("Done: one-hot output follows sel when en=1.");
        $finish;
    end
endmodule
time en sel | y
   0  0  10 | 0000
  10  1  00 | 0001
  20  1  01 | 0010
  30  1  10 | 0100
  40  1  11 | 1000
Done: one-hot output follows sel when en=1.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpkr01tylf/decoder_2to4_beh.v:46: $finish called at 50 (1s)

2-to-4 decoder: dataflow style

The dataflow version describes the same logic as Boolean equations with assign statements. Each output line is the AND of the enable with the matching combination of the select bits. This maps directly onto gates and is a good way to see the logic that the synthesizer infers from the behavioral code above.

// 2-to-4 line decoder, dataflow style using continuous assignments
module decoder_2to4_df (
    input  wire       en,     // active-high enable
    input  wire [1:0] sel,    // 2-bit select input
    output wire [3:0] y       // 4-bit one-hot output
);
    assign y[0] = en & ~sel[1] & ~sel[0];
    assign y[1] = en & ~sel[1] &  sel[0];
    assign y[2] = en &  sel[1] & ~sel[0];
    assign y[3] = en &  sel[1] &  sel[0];
endmodule

// Testbench
module tb_decoder_2to4_df;
    reg        en;
    reg  [1:0] sel;
    wire [3:0] y;
    integer    i;

    decoder_2to4_df dut (.en(en), .sel(sel), .y(y));

    initial begin
        $display("time en sel | y");
        $monitor("%4t  %b  %b | %b", $time, en, sel, y);

        en = 1'b0; sel = 2'b11; #10;   // disabled

        en = 1'b1;
        for (i = 0; i < 4; i = i + 1) begin
            sel = i[1:0];
            #10;
        end

        $display("Done: each assign drives exactly one output line.");
        $finish;
    end
endmodule
time en sel | y
   0  0  11 | 0000
  10  1  00 | 0001
  20  1  01 | 0010
  30  1  10 | 0100
  40  1  11 | 1000
Done: each assign drives exactly one output line.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp3pr6qkai/decoder_2to4_df.v:35: $finish called at 50 (1s)

Scaling up to a 3-to-8 decoder

A 3-to-8 decoder follows the same idea with three select bits and eight outputs. Instead of writing an eight-way case, this behavioral version uses a left shift: place a single 1 at bit position sel. When en is low the output clears. The shift keeps the code compact and still synthesizes cleanly.

// 3-to-8 line decoder, behavioral style with a shift, active-high enable
module decoder_3to8_beh (
    input  wire       en,     // active-high enable
    input  wire [2:0] sel,    // 3-bit select input
    output reg  [7:0] y       // 8-bit one-hot output
);
    always @(*) begin
        if (!en)
            y = 8'b0000_0000;         // disabled: all outputs low
        else
            y = 8'b0000_0001 << sel;  // place the single '1' at position sel
    end
endmodule

// Testbench
module tb_decoder_3to8_beh;
    reg        en;
    reg  [2:0] sel;
    wire [7:0] y;
    integer    i;

    decoder_3to8_beh dut (.en(en), .sel(sel), .y(y));

    initial begin
        $display("time en sel  | y");
        $monitor("%4t  %b  %b | %b", $time, en, sel, y);

        en = 1'b0; sel = 3'b101; #10;  // disabled

        en = 1'b1;
        for (i = 0; i < 8; i = i + 1) begin
            sel = i[2:0];
            #10;
        end

        $display("Done: active output index equals sel when en=1.");
        $finish;
    end
endmodule
time en sel  | y
   0  0  101 | 00000000
  10  1  000 | 00000001
  20  1  001 | 00000010
  30  1  010 | 00000100
  40  1  011 | 00001000
  50  1  100 | 00010000
  60  1  101 | 00100000
  70  1  110 | 01000000
  80  1  111 | 10000000
Done: active output index equals sel when en=1.
/var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmphho41h0o/decoder_3to8_beh.v:37: $finish called at 90 (1s)

How the code works

All three modules share the same contract: a one-hot output where a single bit is high at a time, gated by an enable. A few details are worth calling out.

  • Sensitivity list. always @(*) tells the simulator to re-evaluate the block whenever any input changes. This is what makes the block combinational rather than clocked.
  • reg vs wire. Outputs assigned inside an always block must be declared reg (as in the behavioral modules), while outputs driven by assign are wire (the dataflow module). The word reg does not imply a flip-flop here; the logic is still combinational.
  • Blocking assignment. Combinational logic uses the blocking operator =. Non-blocking <= is reserved for clocked, sequential logic.
  • Enable semantics. When en is low, every design drives all zeros. Some textbooks use active-low enables or active-low outputs instead; the principle is identical, only the polarity changes.

The testbenches are self-driving. A for loop sweeps every value of sel with the enable asserted, and one extra vector holds the enable low to prove the output stays dormant. The $monitor task prints a line whenever a signal changes, and $finish ends the run so the simulator exits cleanly.

Common mistakes to avoid

  • Forgetting the default case. Leaving a case incomplete without a default can infer an unintended latch. Either cover every input or add default, as shown above.
  • Using the wrong vector width. A 3-to-8 decoder needs sel declared as [2:0] and y as [7:0]. Mismatched widths silently truncate and produce wrong one-hot values.
  • Driving an always-block output declared as wire. This is a compile error. Match reg/wire to the assignment style.
  • Mixing blocking and non-blocking. Using <= in combinational logic can cause simulation-versus-synthesis mismatches. Keep = for decoders.
  • Ignoring the enable. A decoder without an enable cannot be cascaded. The enable is what lets you build a 4-to-16 decoder from two 3-to-8 blocks.

To run any of these yourself, save the design and testbench into one .v file and execute iverilog -g2005 -o sim decoder_2to4_beh.v followed by vvp sim. The printed table should match the simulation log shown in each block.

Quick check

How many outputs does an n-to-2^n decoder with n = 4 have?

Sixteen. A 4-to-16 decoder activates one of 24 = 16 output lines for each 4-bit input combination.

Why must the output of the behavioral decoder be declared as reg, not wire?

Because it is assigned inside an always block. In Verilog, any signal assigned procedurally must be of type reg. It is still combinational logic, not a stored flip-flop.

What does the expression 8’b0000_0001 << sel produce when sel is 3?

8'b0000_1000. The single 1 is shifted left by three positions, placing the active bit at index 3, which is exactly the one-hot output a 3-to-8 decoder needs.

What happens to the outputs when the enable is low?

All output bits are driven to 0 regardless of the select value. The enable gates the entire decoder, which is what allows larger decoders to be built by cascading smaller ones.

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