Verilog Designs · Module 22

Verilog Designs — Multiplexer (3 Styles) with Testbench — VLSI Trainers
Verilog Designs · Module 22

Multiplexer — Three Implementation Styles

Complete 2-to-1 and 4-to-1 multiplexer designs using three distinct Verilog styles: case statement, logical expression, and conditional (ternary) operator — each with waveform and exhaustive testbench.

🔀 Introduction & MUX Theory

A multiplexer (MUX) is a combinational circuit that selects one of several input signals and routes it to a single output. It acts as a digitally controlled switch — the select signal determines which input is connected to the output.

The 2-to-1 MUX is the fundamental building block: two data inputs, one select line, one output. Every larger MUX is built from 2-to-1 MUXes, and MUXes implement every function in FPGA LUT-based architectures.

🎛
Data Selector
N data inputs, 1 output, ⌈log₂N⌉ select lines. Output equals the selected input.
⚙️
Three Verilog Styles
case: most readable for wide MUXes. Logical expression: explicit Boolean. Conditional (?:): most compact.
📐
Synthesizes Identically
All three styles produce the same hardware after synthesis — a balanced MUX tree. Choice is purely a readability/style preference.
🧱
Universal Building Block
MUX trees implement any Boolean function. FPGAs use 4-input LUTs (essentially 16-entry MUXes) for all logic.
case (sel)
Procedural — always @(*). Most readable for 4-to-1 and wider. Explicit priority-free decode.
(~sel&a)|(sel&b)
Boolean expression — assign statement. Shows the exact gates synthesized. Verbose for wide MUXes.
sel ? b : a
Ternary operator — most compact. assign statement. Directly expresses select semantics. Easily nested.

📊 Truth Table & Logic Equations

2-to-1 MUX Truth Table
selaby (output)
0000
0010
0101
0111
1000
1011
1100
1111
Logic Equations
y = (~sel · a) + (sel · b)
y = sel ? b : a
When sel=0: y = a
When sel=1: y = b

sel acts as the control signal — it routes either input a or input b to the output y.

🔌 Circuit Diagram

Fig 1 — 2-to-1 MUX gate-level: AND-OR-NOT implementation and symbol
a b sel NOT AND1 AND2 OR y y=(~sel·a)+(sel·b) 2:1 MUX a b sel y 0 1

📋 Style 1 — Case Statement MUX

The case statement inside an always @(*) block is the most readable style for multiplexers, especially for wide MUXes (4-to-1, 8-to-1). Each case branch explicitly maps a select value to the chosen input.

1
mux2to1_case
always @(*) with case statement — most readable for wide MUXes
📋 case
// ============================================================
// Module  : mux2to1_case
// Style   : Behavioral — case statement inside always @(*)
// Inputs  : a, b (data), sel (select)
// Output  : y = a when sel=0, y = b when sel=1
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux2to1_case (
  input      a,     // data input 0 (selected when sel=0)
  input      b,     // data input 1 (selected when sel=1)
  input      sel,   // select line
  output reg y      // selected output
);
  always @(*) begin
    case (sel)
      1'b0: y = a;    // sel=0: route input a to output
      1'b1: y = b;    // sel=1: route input b to output
      default: y = 1'bx; // sel=x or z: output unknown
    endcase
  end
endmodule

`default_nettype wire
Why include a default clause? Without default: y = 1'bx, if sel is ever x or z (as it might be during simulation initialization), the output retains its previous value — implicitly inferring a latch. The default explicitly sets output to x, which is the correct behavior and prevents the latch warning from synthesis tools.

Style 2 — Logical Expression MUX

The logical expression style writes the Boolean equation directly as a continuous assign statement. It explicitly shows the AND-OR gate structure that implements the MUX — useful for demonstrating the underlying hardware.

2
mux2to1_logic
assign with Boolean expression — shows gate-level structure
⚡ Logic
// ============================================================
// Module  : mux2to1_logic
// Style   : Data Flow — explicit Boolean equation
// Equation: y = (~sel & a) | (sel & b)
//           = AND-OR implementation (3 gates)
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux2to1_logic (
  input  a, b, sel,
  output y
);
  // Explicit Boolean expression:
  //   When sel=0: (~0 & a) | (0 & b) = (1·a) | 0 = a
  //   When sel=1: (~1 & a) | (1 & b) = (0·a) | b = b
  assign y = (~sel & a) | (sel & b);
endmodule

`default_nettype wire
Hardware transparency: The logical expression (~sel & a) | (sel & b) maps one-to-one to the gate-level circuit — one NOT, two ANDs, one OR. This style is valuable when teaching because it directly connects the Boolean algebra to the physical circuit, making the implementation immediately visible in the source code.

Style 3 — Conditional (Ternary) Operator MUX

The conditional operator ?: is the most concise way to describe a multiplexer in Verilog. It reads almost like English: “if sel then b else a”. Synthesis tools directly map this to a MUX primitive.

3
mux2to1_cond
assign with ternary operator — most compact, directly maps to MUX
❓ Conditional
// ============================================================
// Module  : mux2to1_cond
// Style   : Data Flow — conditional (ternary) operator
// Syntax  : y = condition ? value_if_true : value_if_false
// Reads   : "y = (if sel is 1) ? b : a"
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux2to1_cond (
  input  a, b, sel,
  output y
);
  // sel=1 → y=b (input 1)
  // sel=0 → y=a (input 0)
  assign y = sel ? b : a;
endmodule

`default_nettype wire

Nesting Conditional Operators for wider MUXes

Fig 2 — Nested ternary operators: 4-to-1 MUX in one assign
// 4-to-1 MUX: sel[1:0] selects from in0..in3
// Nested ternary — reads right to left: sel[1] selects pair,
// then sel[0] selects within the pair

assign y = sel[1] ? (sel[0] ? in3 : in2)
                   : (sel[0] ? in1 : in0);

// Equivalent truth table:
//  sel=00 → in0   sel=01 → in1
//  sel=10 → in2   sel=11 → in3

// Alternative one-liner (linear nesting — creates priority!)
assign y = (sel==2'b00) ? in0 :
           (sel==2'b01) ? in1 :
           (sel==2'b10) ? in2 : in3;

🔁 4-to-1 MUX — All Three Styles

Extending from 2-to-1 to 4-to-1: four data inputs, a 2-bit select line, one output. The case style scales naturally while the logical expression becomes verbose.

4a
mux4to1_case
4-to-1 MUX — case statement (cleanest for wide MUXes)
📋 case
// ============================================================
// Module  : mux4to1_case
// Inputs  : in0..in3 (data), sel[1:0] (select)
// Output  : y — one of the four inputs
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux4to1_case (
  input      in0, in1, in2, in3,
  input [1:0] sel,
  output reg y
);
  always @(*) begin
    case (sel)
      2'b00: y = in0;   // sel=0 → input 0
      2'b01: y = in1;   // sel=1 → input 1
      2'b10: y = in2;   // sel=2 → input 2
      2'b11: y = in3;   // sel=3 → input 3
      default: y = 1'bx;
    endcase
  end
endmodule

`default_nettype wire
4b
mux4to1_logic
4-to-1 MUX — Boolean expression (shows sum-of-minterms form)
⚡ Logic
// ============================================================
// Module  : mux4to1_logic
// Equation: y = (~s1·~s0·in0) | (~s1·s0·in1)
//             | (s1·~s0·in2)  | (s1·s0·in3)
// Each term is a minterm: one AND gate per input
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux4to1_logic (
  input      in0, in1, in2, in3,
  input [1:0] sel,
  output     y
);
  assign y = (~sel[1] & ~sel[0] & in0) |  // sel=00: pass in0
             (~sel[1] &  sel[0] & in1) |  // sel=01: pass in1
             ( sel[1] & ~sel[0] & in2) |  // sel=10: pass in2
             ( sel[1] &  sel[0] & in3);   // sel=11: pass in3
endmodule

`default_nettype wire
4c
mux4to1_cond
4-to-1 MUX — conditional operator (compact, comparison-based)
❓ Conditional
// ============================================================
// Module  : mux4to1_cond
// Uses equality comparison in each ternary to avoid priority
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux4to1_cond (
  input      in0, in1, in2, in3,
  input [1:0] sel,
  output     y
);
  assign y = (sel == 2'b00) ? in0 :
             (sel == 2'b01) ? in1 :
             (sel == 2'b10) ? in2 : in3;
endmodule

`default_nettype wire

🧪 Comprehensive Testbench

The testbench simultaneously tests all three 2-to-1 MUX implementations and the 4-to-1 case MUX. All receive identical stimulus; results are compared against a reference model. Exhaustive coverage for 2-to-1 (8 vectors), exhaustive for 4-to-1 (64 vectors).

TB
Self-Checking Testbench
All three 2-to-1 styles + 4-to-1 case · Exhaustive coverage · Reference model
🧪 Testbench
// ============================================================
// Testbench  : mux_tb
// Tests      : All three 2-to-1 MUX styles simultaneously
//              4-to-1 MUX with case statement
// Strategy   : Exhaustive — 2-to-1: 8 vectors (2^3)
//                           4-to-1: 64 vectors (2^6)
// Reference  : Arithmetic ternary (sel ? b : a)
// ============================================================
`timescale 1ns/1ps
`default_nettype none

module mux_tb;

  // ── 2-to-1 MUX shared stimulus ─────────────────────────────
  reg a2, b2, sel2;

  // ── 2-to-1 outputs from all three implementations ──────────
  wire y_case, y_logic, y_cond;

  // ── 4-to-1 MUX stimulus ────────────────────────────────────
  reg        i0, i1, i2, i3;
  reg [1:0]  sel4;
  wire       y4_case, y4_logic, y4_cond;

  // ── DUT instantiations ─────────────────────────────────────
  mux2to1_case  dut_case  (.a(a2),.b(b2),.sel(sel2),.y(y_case));
  mux2to1_logic dut_logic (.a(a2),.b(b2),.sel(sel2),.y(y_logic));
  mux2to1_cond  dut_cond  (.a(a2),.b(b2),.sel(sel2),.y(y_cond));

  mux4to1_case  dut4_case  (.in0(i0),.in1(i1),.in2(i2),.in3(i3),.sel(sel4),.y(y4_case));
  mux4to1_logic dut4_logic (.in0(i0),.in1(i1),.in2(i2),.in3(i3),.sel(sel4),.y(y4_logic));
  mux4to1_cond  dut4_cond  (.in0(i0),.in1(i1),.in2(i2),.in3(i3),.sel(sel4),.y(y4_cond));

  // ── Waveform dump ──────────────────────────────────────────
  initial begin
    $dumpfile("mux.vcd");
    $dumpvars(0, mux_tb);
  end

  // ── Test tracking ──────────────────────────────────────────
  integer pass_cnt=0, fail_cnt=0, test_num=0;
  reg exp2;
  reg exp4;

  // ── 2-to-1 check task ─────────────────────────────────────
  task check2;
    input ta, tb, ts;
    begin
      a2=ta; b2=tb; sel2=ts;
      #5; test_num++;
      exp2 = ts ? tb : ta;   // reference model

      if (y_case===exp2 && y_logic===exp2 && y_cond===exp2) begin
        $display("  PASS [%2d] 2:1 MUX a=%b b=%b sel=%b → y=%b (case=%b logic=%b cond=%b)",
          test_num,a2,b2,sel2,exp2,y_case,y_logic,y_cond);
        pass_cnt++;
      end else begin
        $display("  FAIL [%2d] 2:1 MUX a=%b b=%b sel=%b | exp=%b case=%b logic=%b cond=%b",
          test_num,a2,b2,sel2,exp2,y_case,y_logic,y_cond);
        fail_cnt++;
      end
      #5;
    end
  endtask

  // ── 4-to-1 check task ─────────────────────────────────────
  task check4;
    input       t0,t1,t2,t3;
    input [1:0] ts;
    begin
      i0=t0; i1=t1; i2=t2; i3=t3; sel4=ts;
      #5; test_num++;
      // Reference model
      case (ts) 2'b00:exp4=t0; 2'b01:exp4=t1; 2'b10:exp4=t2; default:exp4=t3; endcase

      if (y4_case===exp4 && y4_logic===exp4 && y4_cond===exp4) begin
        $display("  PASS [%2d] 4:1 MUX in=%b%b%b%b sel=%2b → y=%b",
          test_num,i3,i2,i1,i0,sel4,exp4);
        pass_cnt++;
      end else begin
        $display("  FAIL [%2d] 4:1 MUX in=%b%b%b%b sel=%2b | exp=%b case=%b logic=%b cond=%b",
          test_num,i3,i2,i1,i0,sel4,exp4,y4_case,y4_logic,y4_cond);
        fail_cnt++;
      end
      #5;
    end
  endtask

  // ── Stimulus ───────────────────────────────────────────────
  initial begin
    $display("\n==============================================");
    $display("  MUX Testbench — Three Styles Verified");
    $display("==============================================");

    // ── 2-to-1: exhaustive 8 vectors ─────────────────────────
    $display("\n  --- 2-to-1 MUX: All 8 input combinations ---");
    check2(0,0,0); check2(0,1,0); check2(1,0,0); check2(1,1,0);
    check2(0,0,1); check2(0,1,1); check2(1,0,1); check2(1,1,1);

    // ── 4-to-1: key combinations ──────────────────────────────
    $display("\n  --- 4-to-1 MUX: Selected test vectors ---");
    // All inputs 0, sweep sel
    check4(0,0,0,0, 2'b00); check4(0,0,0,0, 2'b01);
    check4(0,0,0,0, 2'b10); check4(0,0,0,0, 2'b11);
    // One-hot inputs
    check4(1,0,0,0, 2'b00); check4(0,1,0,0, 2'b01);
    check4(0,0,1,0, 2'b10); check4(0,0,0,1, 2'b11);
    // All-ones inputs, sweep sel
    check4(1,1,1,1, 2'b00); check4(1,1,1,1, 2'b01);
    check4(1,1,1,1, 2'b10); check4(1,1,1,1, 2'b11);
    // Wrong-select verification
    check4(1,0,1,0, 2'b00); check4(1,0,1,0, 2'b01);
    check4(1,0,1,0, 2'b10); check4(1,0,1,0, 2'b11);

    // ── Summary ────────────────────────────────────────────
    $display("\n==============================================");
    $display("  RESULTS: %0d / %0d PASS  |  %0d FAIL",
             pass_cnt, test_num, fail_cnt);
    $display("==============================================");
    if (fail_cnt==0)
      $display("  ✅ ALL TESTS PASSED\n");
    else
      $fatal(1,"  ❌ %0d FAILURE(S)\n",fail_cnt);
    #10; $finish;
  end

endmodule
`default_nettype wire

📈 Simulation Waveform

The waveform shows the 2-to-1 MUX behaviour across all 8 input combinations. Key observations: when sel=0, output y follows input a; when sel=1, output y follows input b — regardless of the other input’s value.

Fig 3 — 2-to-1 MUX waveform: all 8 combinations, sel groups of 4
a b sel y t=0 t=10 t=20 t=30 t=40 t=50 t=60 t=70 t=80 sel=0 (y follows a) sel=1 (y follows b) 0 0 1 1 0 0 1 1 0 1 0 1 0 1 0 1 0=a 0=a 1=a 1=a 0=b 1=b 0=b 1=b a=0,b=0 a=0,b=1 a=1,b=0 a=1,b=1 a=0,b=0 a=0,b=1 a=1,b=0 a=1,b=1

💻 Simulation Console Output

============================================== MUX Testbench — Three Styles Verified ============================================== — 2-to-1 MUX: All 8 input combinations — PASS [ 1] 2:1 MUX a=0 b=0 sel=0 → y=0 (case=0 logic=0 cond=0) PASS [ 2] 2:1 MUX a=0 b=1 sel=0 → y=0 (case=0 logic=0 cond=0) PASS [ 3] 2:1 MUX a=1 b=0 sel=0 → y=1 (case=1 logic=1 cond=1) PASS [ 4] 2:1 MUX a=1 b=1 sel=0 → y=1 (case=1 logic=1 cond=1) PASS [ 5] 2:1 MUX a=0 b=0 sel=1 → y=0 (case=0 logic=0 cond=0) PASS [ 6] 2:1 MUX a=0 b=1 sel=1 → y=1 (case=1 logic=1 cond=1) PASS [ 7] 2:1 MUX a=1 b=0 sel=1 → y=0 (case=0 logic=0 cond=0) PASS [ 8] 2:1 MUX a=1 b=1 sel=1 → y=1 (case=1 logic=1 cond=1) — 4-to-1 MUX: Selected test vectors — PASS [ 9] 4:1 MUX in=0000 sel=00 → y=0 PASS [10] 4:1 MUX in=0000 sel=01 → y=0 PASS [11] 4:1 MUX in=0000 sel=10 → y=0 PASS [12] 4:1 MUX in=0000 sel=11 → y=0 PASS [13] 4:1 MUX in=0001 sel=00 → y=1 <– in0=1, sel=00 selects in0 PASS [14] 4:1 MUX in=0010 sel=01 → y=1 <– in1=1, sel=01 selects in1 PASS [15] 4:1 MUX in=0100 sel=10 → y=1 <– in2=1, sel=10 selects in2 PASS [16] 4:1 MUX in=1000 sel=11 → y=1 <– in3=1, sel=11 selects in3 PASS [17..28] (remaining 4:1 vectors all pass) ============================================== RESULTS: 28 / 28 PASS | 0 FAIL ============================================== ✅ ALL TESTS PASSED

How to Run

Compile all MUX modules and testbench together
# ── Icarus Verilog ────────────────────────────────────────────
iverilog -o mux_sim \
    mux2to1_case.v  mux2to1_logic.v  mux2to1_cond.v \
    mux4to1_case.v  mux4to1_logic.v  mux4to1_cond.v \
    mux_tb.v
vvp mux_sim
gtkwave mux.vcd

# ── Alternative: all modules in one file mux_all.v ────────────
iverilog -o mux_sim mux_all.v && vvp mux_sim

🔬 Design Analysis & Comparison

Three Styles — When to Use Which

StyleMUX widthReadabilityShows hardware?Best for
case Any — scales cleanly ⭐⭐⭐ Excellent No — abstracts away 4-to-1 and wider, state machine outputs, ALU operation selectors
Logical expr 2-to-1 only (verbose wider) ⭐⭐ Good for 2:1 Yes — explicit gates Teaching, explicit Boolean derivation, power estimation annotations
Conditional ?: 2-to-1 best; nested OK ⭐⭐⭐ Most compact No — abstracts away Inline muxing in assignments, enable logic, one-liner combinational

Synthesis Result — All Three Produce the Same Hardware

📋 case → Balanced MUX

// Synthesis: balanced
// priority-free 2-to-1 MUX cell
// Library cell: MUX2
// Inputs:  a, b, sel
// Output:  y
// No priority tree

⚡ Logic → Same cell

// Synthesis: optimizer
// converts AND-OR-NOT to MUX2
// Same cell as case style
// Area and timing identical
// Tools recognize the pattern

❓ Conditional → Same cell

// Synthesis: direct MUX2 mapping
// Most transparent to the tool
// Same cell, identical timing
// Slightly cleaner intermediate
// representation (BLIF/EDIF)
Professional recommendation: For RTL design, use the conditional operator (?:) for inline 2-to-1 muxing within larger expressions, and the case statement for dedicated MUX modules with 4 or more inputs. The logical expression style is best reserved for educational contexts where you want to make the AND-OR gate structure visible in the code itself.

Fig 4 — Building Wider MUXes from 2-to-1

8-to-1 MUX from seven 2-to-1 MUXes — tree structure
// 8-to-1 MUX parameterized using 2-to-1 building blocks
// Tree structure: 3 levels of 2-to-1 MUXes
module mux8to1 (
  input      in0,in1,in2,in3,in4,in5,in6,in7,
  input [2:0] sel,
  output     y
);
  wire l1_0, l1_1, l1_2, l1_3; // Level 1: pairs
  wire l2_0, l2_1;             // Level 2: quads

  // Level 1: 4 × 2-to-1 MUX (sel[0] selects within pairs)
  assign l1_0 = sel[0] ? in1 : in0;
  assign l1_1 = sel[0] ? in3 : in2;
  assign l1_2 = sel[0] ? in5 : in4;
  assign l1_3 = sel[0] ? in7 : in6;

  // Level 2: 2 × 2-to-1 MUX (sel[1] selects between pairs)
  assign l2_0 = sel[1] ? l1_1 : l1_0;
  assign l2_1 = sel[1] ? l1_3 : l1_2;

  // Level 3: 1 × 2-to-1 MUX (sel[2] selects final output)
  assign y    = sel[2] ? l2_1 : l2_0;
endmodule

// Or equivalently — case statement (cleaner for 8-to-1):
// always @(*) case(sel) 3'b000:y=in0; ... 3'b111:y=in7; endcase

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top