VHDL · Chapter 6.6 · Combinational Logic Design
Decoders and Encoders
If the multiplexer routes one of many, decoders and encoders translate between encodings. A decoder takes an n-bit binary value and activates exactly one of its many outputs, a one-hot pattern that is the basis of address decode, chip selects, and demultiplexing. An encoder does the reverse, compressing a one-hot input back to binary, and a priority encoder returns the index of the highest-priority set bit, which is used for interrupts and arbitration. This lesson shows the clean way to model each in VHDL, using a selected assignment or a shift for decoders and an if ladder or a for loop for priority encoders. It maps them to the hardware they build and keeps every output complete and latch-free.
Foundation14 min readVHDLDecoderEncoderPriority EncoderOne-hotRTL
1. Engineering intuition — expand and compress
A decoder expands: it turns a compact binary index into a one-hot signal where exactly one line is active — "select line number 5" becomes "line 5 high, all others low." An encoder compresses: it turns a one-hot (or, for a priority encoder, any pattern) back into the binary index of the active line. These are the translation primitives between the two ways hardware names a choice: by binary value and by one-hot position. Address decode, chip-select generation, interrupt prioritisation, and demultiplexing are all decoders and encoders.
2. Formal explanation — decoder, encoder, priority encoder
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- DECODER: n-bit binary → one-hot (2^n). Two clean styles:
with sel select -- explicit one-hot
onehot <= "0001" when "00", "0010" when "01",
"0100" when "10", "1000" when others;
-- or by shifting a single '1' to the selected position:
onehot2 <= std_logic_vector(shift_left(to_unsigned(1, 4), to_integer(unsigned(sel))));
-- PRIORITY ENCODER: highest set bit → its index (+ a 'valid' flag).
prio : process (all)
begin
index <= (others => '0'); valid <= '0'; -- defaults → latch-free
for i in req'range loop -- low to high: last match (highest i) wins
if req(i) = '1' then
index <= std_logic_vector(to_unsigned(i, index'length));
valid <= '1';
end if;
end loop;
end process;A decoder maps a binary value to a one-hot output (via with/select or a shift). An encoder
reverses it. A priority encoder scans the input and returns the index of the highest-priority set
bit plus a valid flag; a for loop scanning low-to-high naturally lets the highest index win
(last-assignment-wins, lesson 5.9), or an if ladder encodes the priority explicitly.
3. Production RTL — address decode and interrupt priority
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- (A) Chip-select decoder: activate one peripheral select from the address high bits.
cs <= std_logic_vector(shift_left(to_unsigned(1, NUM_DEV), to_integer(unsigned(addr_hi))));
-- (B) Interrupt priority encoder: highest pending IRQ wins.
irq_enc : process (all)
begin
irq_num <= (others => '0'); irq_any <= '0';
for i in irq'range loop
if irq(i) = '1' then
irq_num <= std_logic_vector(to_unsigned(i, irq_num'length));
irq_any <= '1';
end if;
end loop;
end process;What hardware does this become? (A) a binary-to-one-hot decoder driving one chip-select line per
device; (B) a priority encoder whose unrolled for loop becomes a network that outputs the index of
the highest-numbered asserted interrupt, with irq_any indicating any request. Defaults keep both
latch-free.
4. Hardware interpretation — one-hot fan-out and priority compress
5. Simulation interpretation — one-hot tracks input; encoder picks highest
Decoder one-hot output, and a priority encoder picking the highest set bit
8 cycles6. Debugging example — the priority encoder that picked the wrong bit (or latched)
Expected: the highest-priority set bit's index. Observed: the lowest index was returned, or
the output latched when no bit was set. Root cause: for "highest wins," a for loop scanning low
to high relies on last-assignment-wins so the top index overwrites — scanning the wrong direction (or
using an if/elsif in the wrong order) returns the lowest; and without a default index/valid, the
no-request case leaves them unassigned → latch. Fix: scan low-to-high for highest-wins (or order
the if ladder by priority) and default index/valid at the top. Engineering takeaway: a
priority encoder's correctness is scan direction + last-assignment-wins (or explicit if ordering),
and its safety is defaulting the index and valid for the empty input.
-- Defaults make the no-request case safe; low-to-high scan makes the HIGHEST index win.
process (all) begin
index <= (others => '0'); valid <= '0'; -- default: no request
for i in req'low to req'high loop
if req(i) = '1' then
index <= std_logic_vector(to_unsigned(i, index'length)); -- higher i overwrites lower
valid <= '1';
end if;
end loop;
end process;7. Common mistakes & what to watch for
- No default on encoder outputs. The empty-input case leaves
index/validunassigned → latch. Default them at the top. - Wrong scan direction / priority order. Decide highest- vs lowest-priority and make the loop
direction (or
iforder) match. - Incomplete decoder coverage. A
with/selectdecoder needswhen others; a process-style decoder needs a default. - Index width too small.
to_unsigned(i, index'length)must fit the largest index; sizeindexforreq'high. - Confusing a decoder with a demux. A decoder gives one-hot selects; a demux routes a data input to one of N outputs (a decoder enabling the data) — keep the intent clear.
8. Engineering insight & continuity
Decoders and encoders are the translators between the two ways hardware names choices — binary index and
one-hot position — and they appear everywhere selection logic lives: address maps, chip selects,
interrupts, arbiters. Model them with the patterns you now know: with/select or a shift for the clean
decoder, a defaulted for/if for the priority encoder, always complete and latch-free. With routing
(muxes) and encoding translation (decoders/encoders) covered, the module turns to computation: the next
lesson, Arithmetic Circuits, builds adders, subtractors, comparators, and more on numeric_std,
before Combinational Loops closes the module on the one structure to never build.