A multiplexer (mux) selects one of N inputs onto its output using a select
signal. It is the fundamental routing element, every if and case
you write in RTL becomes muxes in the fabric, and an FPGA lookup table can
implement a 2:1 mux with room to spare.
Three equivalent ways to describe a mux, all in the design below:
Conditional operator: assign y = sel ? b : a; reads exactly like
the hardware: "if sel, take b, else a". Ideal for 2:1.
case in an always block: scales to any width. Note the block is
always @* (combinational) and every path assigns the output,
this matters, as the next lesson on latches shows.
Indexing: assign y = inputs[sel]; when the inputs are packed
into a vector. Compact, and synthesizes to the same thing.
In the waveform, watch y4 follow a different input each time sel
changes: the mux is "wiring" a different source through, combinationally,
no clock involved anywhere.
Experiment: in the playground, remove the default from the case and
make the select 3 bits wide with only 5 cases, then read the synthesis
warning story in the latch lesson to see what you just risked.
Schematic
The design
Verilog, design.v
// Three styles of multiplexer.
module muxes (
input wire a, b, // 2:1 inputs
input wire [3:0] in4, // 4:1 packed inputs
input wire sel, // 2:1 select
input wire [1:0] sel4, // 4:1 select
output wire y2, // style 1: ?:
output reg y4, // style 2: case
output wire y_idx // style 3: indexing
);
// style 1: conditional operator
assign y2 = sel ? b : a;
// style 2: case in a combinational always block
always @* begin
case (sel4)
2'd0: y4 = in4[0];
2'd1: y4 = in4[1];
2'd2: y4 = in4[2];
default: y4 = in4[3];
endcase
end
// style 3: vector indexing
assign y_idx = in4[sel4];
endmodule
Show the VHDL version
VHDL, design.vhd
-- Three styles of multiplexer.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity muxes is
port (
a, b : in std_logic;
in4 : in std_logic_vector(3 downto 0);
sel : in std_logic;
sel4 : in std_logic_vector(1 downto 0);
y2 : out std_logic;
y4 : out std_logic;
y_idx : out std_logic
);
end entity;
architecture rtl of muxes is
begin
-- style 1: when/else
y2 <= b when sel = '1' else a;
-- style 2: with/select
with sel4 select y4 <=
in4(0) when "00",
in4(1) when "01",
in4(2) when "10",
in4(3) when others;
-- style 3: indexing
y_idx <= in4(to_integer(unsigned(sel4)));
end architecture;
`timescale 1ns/1ns
module tb;
reg a = 0, b = 1;
reg [3:0] in4 = 4'b1010;
reg sel = 0;
reg [1:0] sel4 = 0;
wire y2, y4, y_idx;
muxes dut (.a(a), .b(b), .in4(in4), .sel(sel), .sel4(sel4),
.y2(y2), .y4(y4), .y_idx(y_idx));
initial begin
$dumpfile("wave.vcd"); $dumpvars(0, tb);
#10 sel = 1; // y2: a -> b
#10 sel4 = 2'd1; // walk the 4:1 inputs: 0,1,0,1
#10 sel4 = 2'd2;
#10 sel4 = 2'd3;
#10 in4 = 4'b0101; // change data under a fixed select
#10 $finish;
end
endmodule
Show the VHDL testbench
VHDL, tb.vhd
library ieee;
use ieee.std_logic_1164.all;
entity tb is end entity;
architecture sim of tb is
signal a : std_logic := '0';
signal b : std_logic := '1';
signal in4 : std_logic_vector(3 downto 0) := "1010";
signal sel : std_logic := '0';
signal sel4 : std_logic_vector(1 downto 0) := "00";
signal y2, y4, y_idx : std_logic;
begin
dut : entity work.muxes port map (a=>a, b=>b, in4=>in4, sel=>sel,
sel4=>sel4, y2=>y2, y4=>y4, y_idx=>y_idx);
process begin
wait for 10 ns; sel <= '1'; -- y2: a -> b
wait for 10 ns; sel4 <= "01"; -- walk the 4:1 inputs
wait for 10 ns; sel4 <= "10";
wait for 10 ns; sel4 <= "11";
wait for 10 ns; in4 <= "0101"; -- change data under fixed select
wait for 10 ns; std.env.stop;
end process;
end architecture;
The MyHDL version keeps the design and its
testbench in one design.py.
Simulated waveform
This trace was produced by actually simulating the code
above with Icarus Verilog.
Try it live
Open this lesson in a playground, edit the code, and re-run it.