Skip to content

VHDL · Chapter 6.5 · Combinational Logic Design

Multiplexers

The multiplexer is the workhorse of combinational logic. It selects one of several inputs and routes it to the output under the control of a select signal, and almost every datapath is built from muxes. VHDL gives you four natural ways to write one. The when-else and if forms build a priority mux, while the with-select and case forms build a balanced mux, and indexing an array by an integer gives a clean wide mux. This lesson maps each style to the hardware it builds, shows when priority versus balanced selection matters, and applies the default-then-override discipline so a mux never accidentally latches. Choosing the right width and selection shape is the only real design decision.

Foundation13 min readVHDLMultiplexerSelectionPriority MuxDatapathRTL

1. Engineering intuition — route one of many

A multiplexer is a switch: many inputs, one output, and a select that decides which input gets through. It is the basic act of choosing in hardware — feed a register from one of several sources, pick an ALU result, steer a bus. Because choosing is so common, VHDL offers several syntaxes for it, but they all describe the same primitive: a selection network from N inputs to one output. The only real design decisions are the width (N) and whether the selection is priority (ordered) or balanced (value-based).

2. Formal explanation — four ways, two shapes

mux_styles.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
 
-- PRIORITY mux (ordered conditions): when/else (concurrent) or if (process).
y1 <= a when s1 = '1' else b when s2 = '1' else c;           -- first true wins
 
-- BALANCED mux (one selector value): with/select (concurrent) or case (process).
with sel select
  y2 <= d0 when "00", d1 when "01", d2 when "10", d3 when others;
 
-- WIDE mux by ARRAY INDEX: index a vector-of-vectors with an integer (numeric_std).
y3 <= data_array(to_integer(unsigned(sel)));                  -- clean N:1 mux

when/else and if build a priority mux (a chain of 2:1 muxes, order = precedence); with/select and case build a balanced mux (a flat one-of-N decode). Indexing an array by an integer is the most concise wide mux. All are combinational; keep them latch-free (a final else / when others / complete index range, plus defaults in a process).

3. Production RTL — datapath source select

datapath_mux.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- 4:1 datapath mux feeding a register input, written as a balanced case in a process.
src_mux : process (all)
begin
  mux_out <= (others => '0');                 -- default → latch-free
  case src_sel is
    when "00" => mux_out <= alu_result;
    when "01" => mux_out <= mem_data;
    when "10" => mux_out <= imm_value;
    when others => mux_out <= reg_data;
  end case;
end process;

What hardware does this become? A 4:1 multiplexer selecting the register-input source — src_sel drives the mux select, and the four data sources are its inputs. The default plus full case coverage guarantees no latch. (Written as mux_out <= data_array(to_integer(unsigned(src_sel))) it is the same mux, more concisely, when the sources are collected into an array.)

4. Hardware interpretation — N:1 selection

N to 1 multiplexer selecting one input under a selectcontrolsroutes oneinputs d0..dN-1the N sourcesN:1 muxbalanced (flat) or priority(chain)selectvalue (balanced) /conditions (priority)outputthe selected input12
A multiplexer routes one of N inputs to the output under a select. A balanced mux (with/select, case, array index) is a flat one-of-N selection — the select value picks directly. A priority mux (when/else, if) is a chain of 2:1 muxes where earlier conditions outrank later ones, so it has more logic depth but expresses precedence. Choose balanced for value-based selection (the common case) and priority when the conditions are genuinely ranked.

5. Simulation interpretation — output follows the select

A mux is combinational, so its output continuously equals the currently-selected input — change the select or the selected input and the output follows after a delta.

A 4:1 mux: the select value routes the matching source to the output

8 cycles
A 4:1 mux: the select value routes the matching source to the outputsel=00 → ALU resultsel=00 → ALU resultsel=01 → MEM data (balanced: value selects directly)sel=01 → MEM data (bal…sel=11 → REG data (others)sel=11 → REG data (oth…src_sel0000010110101111mux_outALUALUMEMMEMIMMIMMREGREGt0t1t2t3t4t5t6t7
The output is always the source named by src_sel — a pure function of the select and the inputs, with no memory. Reordering the case branches would not change anything (balanced selection). A priority mux would instead resolve overlapping conditions by order, which is the right tool only when the selectors are genuinely ranked.

6. Debugging example — priority where balanced was meant (and a latch)

Expected: a value-based 4:1 mux. Observed: either subtly wrong selection, or an inferred latch. Root cause: writing a value decode as an if/elsif ladder makes it a priority mux — fine if conditions are exclusive, but it adds depth and, if the final else is missing, leaves a path unassigned → latch. Fix: use case/with/select for value-based selection (balanced, exhaustive), or ensure the if ladder has a final else. Engineering takeaway: match the construct to the selection — case/with/select for value decode, when/else/if for ranked conditions — and always close it (default / else / others) so the mux cannot latch.

mux_fix.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- RISK: value decode as a priority ladder, missing final else → latch + extra depth.
-- y <= d0 when sel="00" else d1 when sel="01" else d2 when sel="10";   -- no else → latch
-- FIX: balanced, exhaustive selection.
with sel select
  y <= d0 when "00", d1 when "01", d2 when "10", d3 when others;        -- balanced, no latch

7. Common mistakes & what to watch for

  • Missing final else / when others. Leaves a path unassigned → latch. Always close the mux.
  • Using a priority style for value-based selection. case/with/select is balanced and clearer; reserve if/when/else for ranked conditions.
  • Index out of range on an array mux. data_array(to_integer(unsigned(sel))) must have sel cover exactly the array range; an out-of-range index is an error.
  • Forgetting defaults in a process-style mux. Default the output at the top to stay latch-free.
  • Very wide priority muxes. A long if ladder has deep logic; a balanced case/array index is faster and smaller for wide selection.

8. Engineering insight & continuity

The multiplexer is selection distilled, and writing one well is mostly about two choices: balanced versus priority, and keeping it latch-free. Use case/with/select (or an array index) for the common value-based mux, reserve priority styles for genuinely ranked conditions, and always close the selection so no path is unassigned. With muxes — the route-one-of-many primitive — in hand, the next lesson covers the complementary primitives that expand and compress encodings: Decoders and Encoders, where a binary value fans out to one-hot (decode) or a one-hot/priority input compresses to binary (encode).