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.
📘 New to the concept? Read the theory first: Encoders & Decoders: the theory.
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
endmoduletime 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
endmoduletime 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
endmoduletime 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
alwaysblock must be declaredreg(as in the behavioral modules), while outputs driven byassignarewire(the dataflow module). The wordregdoes 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
enis 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
caseincomplete without adefaultcan infer an unintended latch. Either cover every input or adddefault, as shown above. - Using the wrong vector width. A 3-to-8 decoder needs
seldeclared as[2:0]andyas[7:0]. Mismatched widths silently truncate and produce wrong one-hot values. - Driving an
always-block output declared aswire. This is a compile error. Matchreg/wireto 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.
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