Verilog Code for a 4:1 MUX: 3 Styles + Testbench
A 4:1 multiplexer routes one of four data inputs (d0–d3) to a single output y, chosen by a 2-bit select bus sel. Below is complete, simulator-ready Verilog code for a 4:1 mux written three ways — behavioral (case), dataflow (assign), and structural (gate-level) — each paired with a self-checking testbench that drives all four select lines and compares the output against a golden reference. Every block compiles and runs under Icarus Verilog, and the simulation log you see is the real output.
📘 New to the concept? Read the theory first: 4:1 Multiplexer: truth table & circuit.
The three coding styles (copy, compile, run)
All three modules share the same ports: a 4-bit input bus d, a 2-bit sel, and a 1-bit output y. Pick whichever style your course or RTL standard expects — they are logically identical and synthesize to the same hardware.
1. Behavioral style (case statement)
// mux4to1_case.v -- 4:1 MUX, behavioral (case) style + self-checking testbench
module mux4to1_case (
input wire [3:0] d, // data inputs d0..d3
input wire [1:0] sel, // 2-bit select
output reg y // output
);
always @(*) begin
case (sel)
2'b00: y = d[0];
2'b01: y = d[1];
2'b10: y = d[2];
2'b11: y = d[3];
default: y = 1'b0;
endcase
end
endmodule
module tb_mux4to1_case;
reg [3:0] d;
reg [1:0] sel;
wire y;
integer s, t, errors;
reg expected;
mux4to1_case dut (.d(d), .sel(sel), .y(y));
initial begin
errors = 0;
$display("time sel d[3:0] y exp");
// Drive all 4 select lines; for each, test several data patterns
for (s = 0; s < 4; s = s + 1) begin
for (t = 0; t < 4; t = t + 1) begin
sel = s[1:0];
case (t)
0: d = 4'b0000;
1: d = 4'b1111;
2: d = 4'b1010;
3: d = 4'b0101;
endcase
#5; // let combinational logic settle
expected = d[s]; // golden reference
$display("%4t %b %b %b %b", $time, sel, d, y, expected);
if (y !== expected) begin
errors = errors + 1;
$display(" ** MISMATCH at sel=%b d=%b : got %b expected %b", sel, d, y, expected);
end
end
end
if (errors == 0)
$display("PASS: all 16 vectors matched across every select line.");
else
$display("FAIL: %0d mismatch(es).", errors);
$finish;
end
endmoduletime sel d[3:0] y exp 5 00 0000 0 0 10 00 1111 1 1 15 00 1010 0 0 20 00 0101 1 1 25 01 0000 0 0 30 01 1111 1 1 35 01 1010 1 1 40 01 0101 0 0 45 10 0000 0 0 50 10 1111 1 1 55 10 1010 0 0 60 10 0101 1 1 65 11 0000 0 0 70 11 1111 1 1 75 11 1010 1 1 80 11 0101 0 0 PASS: all 16 vectors matched across every select line. /var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmp5_qxk_1s/mux4to1_case.v:54: $finish called at 80 (1s)
2. Dataflow style (assign with ternary)
// mux4to1_assign.v -- 4:1 MUX, dataflow (assign) style + self-checking testbench
module mux4to1_assign (
input wire [3:0] d, // data inputs d0..d3
input wire [1:0] sel, // 2-bit select
output wire y // output
);
// Nested conditional (ternary) operators select one of four inputs.
assign y = sel[1] ? (sel[0] ? d[3] : d[2])
: (sel[0] ? d[1] : d[0]);
endmodule
module tb_mux4to1_assign;
reg [3:0] d;
reg [1:0] sel;
wire y;
integer s, t, errors;
reg expected;
mux4to1_assign dut (.d(d), .sel(sel), .y(y));
initial begin
errors = 0;
$display("time sel d[3:0] y exp");
for (s = 0; s < 4; s = s + 1) begin
for (t = 0; t < 4; t = t + 1) begin
sel = s[1:0];
case (t)
0: d = 4'b0001;
1: d = 4'b0010;
2: d = 4'b0100;
3: d = 4'b1000;
endcase
#5;
expected = d[s];
$display("%4t %b %b %b %b", $time, sel, d, y, expected);
if (y !== expected) begin
errors = errors + 1;
$display(" ** MISMATCH at sel=%b d=%b : got %b expected %b", sel, d, y, expected);
end
end
end
if (errors == 0)
$display("PASS: dataflow mux routed d[sel] correctly for all select lines.");
else
$display("FAIL: %0d mismatch(es).", errors);
$finish;
end
endmoduletime sel d[3:0] y exp 5 00 0001 1 1 10 00 0010 0 0 15 00 0100 0 0 20 00 1000 0 0 25 01 0001 0 0 30 01 0010 1 1 35 01 0100 0 0 40 01 1000 0 0 45 10 0001 0 0 50 10 0010 0 0 55 10 0100 1 1 60 10 1000 0 0 65 11 0001 0 0 70 11 0010 0 0 75 11 0100 0 0 80 11 1000 1 1 PASS: dataflow mux routed d[sel] correctly for all select lines. /var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpqh0optou/mux4to1_assign.v:47: $finish called at 80 (1s)
3. Structural style (gate-level)
// mux4to1_struct.v -- 4:1 MUX, structural (gate-level) style + self-checking testbench
module mux4to1_struct (
input wire [3:0] d, // data inputs d0..d3
input wire [1:0] sel, // 2-bit select
output wire y // output
);
wire ns0, ns1; // inverted select lines
wire a0, a1, a2, a3; // per-input AND terms
not u_n0 (ns0, sel[0]);
not u_n1 (ns1, sel[1]);
and u_a0 (a0, d[0], ns1, ns0); // sel = 00
and u_a1 (a1, d[1], ns1, sel[0]);// sel = 01
and u_a2 (a2, d[2], sel[1], ns0);// sel = 10
and u_a3 (a3, d[3], sel[1], sel[0]);// sel = 11
or u_or (y, a0, a1, a2, a3); // combine the one active term
endmodule
module tb_mux4to1_struct;
reg [3:0] d;
reg [1:0] sel;
wire y;
integer s, errors;
reg expected;
mux4to1_struct dut (.d(d), .sel(sel), .y(y));
initial begin
errors = 0;
$display("time sel d[3:0] y exp");
// One-hot data: only d[sel] is high, so a correct mux must output 1.
for (s = 0; s < 4; s = s + 1) begin
sel = s[1:0];
d = (4'b0001 << s); // drive the selected line high
#5;
expected = d[s];
$display("%4t %b %b %b %b", $time, sel, d, y, expected);
if (y !== expected) begin
errors = errors + 1;
$display(" ** MISMATCH at sel=%b d=%b : got %b expected %b", sel, d, y, expected);
end
// Inverse one-hot: selected line low, others high -> output must be 0.
d = ~(4'b0001 << s);
#5;
expected = d[s];
$display("%4t %b %b %b %b", $time, sel, d, y, expected);
if (y !== expected) begin
errors = errors + 1;
$display(" ** MISMATCH at sel=%b d=%b : got %b expected %b", sel, d, y, expected);
end
end
if (errors == 0)
$display("PASS: gate-level mux selected d[sel] on every select line.");
else
$display("FAIL: %0d mismatch(es).", errors);
$finish;
end
endmoduletime sel d[3:0] y exp 5 00 0001 1 1 10 00 1110 0 0 15 01 0010 1 1 20 01 1101 0 0 25 10 0100 1 1 30 10 1011 0 0 35 11 1000 1 1 40 11 0111 0 0 PASS: gate-level mux selected d[sel] on every select line. /var/folders/l0/gs5hnkm56g54bmxg32r6nnwr0000gn/T/tmpuya1g926/mux4to1_struct.v:59: $finish called at 40 (1s)
How to run these files
Each block is a single self-contained file holding both the design and its testbench. With Icarus Verilog installed, compile and simulate in two commands:
iverilog -g2005 -o sim mux4to1_case.v vvp sim
Swap the filename for the dataflow or structural version. The -g2005 flag selects the Verilog-2005 standard, which every snippet here targets.
Line-by-line walkthrough
Behavioral (case). The output y is declared reg because it is assigned inside an always block. The sensitivity list @(*) means the block re-evaluates whenever any input changes, so the logic stays purely combinational. The case (sel) maps each of the four select codes to one data bit, and the default branch guards against x/z on sel during simulation — it also prevents an accidental latch by making sure y is always assigned.
Dataflow (assign). Here y is a wire driven continuously. The nested ternary reads as: if sel[1] is 1 pick from the upper half (d3/d2), otherwise the lower half (d1/d0); the inner sel[0] then picks within that half. This mirrors how a mux is often built from two layers of 2:1 selects and is the most compact of the three.
Structural (gate-level). This spells out the classic sum-of-products: invert both select bits, form one 3-input AND per data line so exactly one AND term can be high for a given sel, then OR the four terms. It is the closest to the gate schematic and is useful when a course asks you to show primitive instantiation rather than inferred logic.
The self-checking testbench. Every testbench instantiates the design under test (dut), then loops the select value s from 0 to 3 so all four select lines are driven. For each select it applies data patterns, waits #5 for the combinational output to settle, and computes the expected answer with the one-line golden model expected = d[s] — indexing the data bus by the select value. The !== comparison (case-equality) catches x and z, not just 0/1 mismatches. An errors counter decides whether the run prints PASS or FAIL, so you never have to eyeball the waveform.
Common mistakes to avoid
- Declaring
yaswirein the behavioral style. Any signal assigned inside analwaysblock must bereg. The opposite is also true: a signal driven byassignmust be awire. - Using
@(sel)instead of@(*). Ifdis missing from the sensitivity list, simulation ignores data changes and your output looks “stuck,” even though synthesis would build correct hardware. Always use@(*)for combinational logic. - Dropping the
defaultbranch. Without it, an unhandledselvalue can infer an unintended latch and hidexpropagation during simulation. - Comparing with
==in the checker.==returnsxwhen either side is unknown, so a buggy design can slip through. Use!==/===in self-checking testbenches. - Not waiting before sampling. Reading
yin the same timestep you change the inputs can capture the old value. A small delay (#5) or an event-based wait lets the combinational output settle first. - Mismatched port widths.
selmust be 2 bits for four inputs; a 1-bitselonly reachesd0andd1.
Quick check
Why must y be a reg in the behavioral version but a wire in the dataflow version?
Signals assigned inside an always block must be reg (procedural assignment), while signals driven by a continuous assign statement must be wire. reg here does not mean a hardware flip-flop — the logic is still combinational.
How many bits wide must sel be for a 4:1 mux, and why?
Two bits. Two select lines give 2² = 4 combinations (00, 01, 10, 11), exactly enough to choose one of the four data inputs.
What does the testbench line expected = d[s] represent?
It is the golden reference model: indexing the data bus by the current select value gives the bit a correct mux should output, which is then compared against the design’s actual output.
Why use !== instead of == when checking the output?
!== is case-inequality: it compares 0, 1, x, and z exactly. Plain == yields x when an operand is unknown, so an x on the output could pass the check undetected.
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