Combinational Logic in VHDL
Combinational logic has no memory. Its outputs depend only on the current inputs. In an FPGA, combinational VHDL usually becomes lookup tables, mux trees, comparators, decoders, adders, and routing between registers.
Learning Objectives
By the end of this lesson, you should be able to:
- describe Boolean functions with concurrent assignments;
- write muxes and decoders using selected and conditional assignments;
- use
process(all)for readable combinational logic; - prevent accidental latch inference with defaults and complete branches;
- estimate why large combinational paths can hurt timing.
Concurrent Assignment
Concurrent assignments are active all the time.
library ieee;
use ieee.std_logic_1164.all;
entity majority3 is
port (
a : in std_logic;
b : in std_logic;
c : in std_logic;
y : out std_logic
);
end entity majority3;
architecture rtl of majority3 is
begin
y <= (a and b) or (a and c) or (b and c);
end architecture rtl;
This majority function outputs 1 when at least two inputs are 1.
| a | b | c | y |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
Conditional Assignment
A two-input mux can be written compactly:
y <= b when sel = '1' else a;
Hardware meaning: sel controls which input reaches y. It does not mean the FPGA executes an if statement in time.
Selected Assignment
For multi-way selection, with select is often clearer:
with sel select
y <= d0 when "00",
d1 when "01",
d2 when "10",
d3 when others;
Use others when the type permits values beyond the listed patterns. With std_logic_vector, simulation can contain values such as X, U, or Z.
Combinational Process
Use process(all) when the decision logic is easier to read procedurally.
process(all)
begin
y <= '0';
if enable = '1' then
y <= a xor b;
end if;
end process;
The default assignment is the key. It tells synthesis what y should be when enable is not 1.
Latch Inference
A latch is inferred when a combinational process leaves an output unassigned on some path.
process(all)
begin
if enable = '1' then
y <= a;
end if;
end process;
When enable = '0', this code says y must remember its old value. That is storage, not pure combinational logic. If storage was not intended, add a default:
process(all)
begin
y <= '0';
if enable = '1' then
y <= a;
end if;
end process;
Worked Example: 2-to-4 Decoder
library ieee;
use ieee.std_logic_1164.all;
entity decoder2to4 is
port (
sel : in std_logic_vector(1 downto 0);
y : out std_logic_vector(3 downto 0)
);
end entity decoder2to4;
architecture rtl of decoder2to4 is
begin
process(all)
begin
y <= "0000";
case sel is
when "00" => y <= "0001";
when "01" => y <= "0010";
when "10" => y <= "0100";
when "11" => y <= "1000";
when others => y <= "0000";
end case;
end process;
end architecture rtl;
Every path assigns y. The default also makes the intended inactive output obvious.
Hardware View
Large combinational paths between registers reduce maximum clock frequency. For a synchronous path:
Tclk >= Tco + Tlogic + Trouting + Tsetup + Tskew
where Tlogic is the delay through LUT levels and carry/mux logic, and Trouting is the FPGA interconnect delay.
Exercise
Write a 4-to-1 mux named mux4.
Requirements:
- inputs:
d0,d1,d2,d3asstd_logic; - select:
sel : std_logic_vector(1 downto 0); - output:
y; - implement it once using
with select; - implement it once using
process(all)andcase.
Explained Solution
Selected assignment version:
library ieee;
use ieee.std_logic_1164.all;
entity mux4 is
port (
d0 : in std_logic;
d1 : in std_logic;
d2 : in std_logic;
d3 : in std_logic;
sel : in std_logic_vector(1 downto 0);
y : out std_logic
);
end entity mux4;
architecture with_select of mux4 is
begin
with sel select
y <= d0 when "00",
d1 when "01",
d2 when "10",
d3 when others;
end architecture with_select;
Process version:
architecture proc_case of mux4 is
begin
process(all)
begin
y <= d0;
case sel is
when "00" => y <= d0;
when "01" => y <= d1;
when "10" => y <= d2;
when "11" => y <= d3;
when others => y <= d0;
end case;
end process;
end architecture proc_case;
Both versions describe a mux. The process version uses a default so y is never left unassigned.
Common Mistakes
- Missing a default assignment in a combinational process.
- Forgetting
when othersfor vectors with unknown simulation values. - Assuming statement order creates hardware delay.
- Using incomplete sensitivity lists instead of
process(all). - Building a very deep combinational path and then blaming the clock.
- Mixing arithmetic and bit-field types without conversion.
Summary
Use concurrent assignments for simple Boolean logic, selected assignments for clean muxes, and process(all) for decision logic. In every combinational process, assign every output for every possible path unless you intentionally want storage.
Next: Sequential Logic in VHDL.