Skip to content

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

decode_encode_forms.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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

decode_encode_uses.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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

decoder binary to one-hot and priority encoder bits to highest-indexexpandcompress(priority)binary valuen bitsdecoder→ one-hot (one of 2^nactive)request bitsany patternpriority encoder→ index of highest set bit+ valid12
A decoder fans a binary value out to one-hot: exactly one of 2^n outputs is active for each input value (address decode, chip selects). A priority encoder compresses the reverse direction: it scans the input bits and outputs the binary index of the highest-priority set bit, plus a valid flag — the structure behind interrupt and arbitration logic. Both are pure combinational translations between binary-index and one-hot/position encodings.

5. Simulation interpretation — one-hot tracks input; encoder picks highest

Decoder one-hot output, and a priority encoder picking the highest set bit

8 cycles
Decoder one-hot output, and a priority encoder picking the highest set bitdecoder: sel=10 → one-hot 0100decoder: sel=10 → one-…encoder: req=0110 → highest set bit is 2 → index=2encoder: req=0110 → hi…req=1010 → highest is 3 → index=3 (priority)req=1010 → highest is …sel0001101100011011onehot00010010010010000001001001001000req00000010011010100000100000110101index01230312t0t1t2t3t4t5t6t7
The decoder output is one-hot for each select value — a pure expansion. The priority encoder returns the index of the HIGHEST set bit of req (so 0110 gives 2, and 1010 gives 3), the priority behaviour interrupts and arbiters rely on. Both are combinational: the outputs track the inputs with no memory.

6. 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.

prio_encoder_fix.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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/valid unassigned → latch. Default them at the top.
  • Wrong scan direction / priority order. Decide highest- vs lowest-priority and make the loop direction (or if order) match.
  • Incomplete decoder coverage. A with/select decoder needs when others; a process-style decoder needs a default.
  • Index width too small. to_unsigned(i, index'length) must fit the largest index; size index for req'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.