Skip to main content

Decoder

A decoder converts a binary-encoded input into a one-hot output where exactly one output bit is high.

2-to-4 Decoder

A 2-bit input selects one of 4 outputs:

component Decoder2to4(A[2]) -> (Y[4]) {
# Inverters for complement signals
notA1: NOT;
notA2: NOT;

# AND gates for each output
and1: AND; # Y[1] = ~A[2] & ~A[1] (input 00)
and2: AND; # Y[2] = ~A[2] & A[1] (input 01)
and3: AND; # Y[3] = A[2] & ~A[1] (input 10)
and4: AND; # Y[4] = A[2] & A[1] (input 11)

connect {
# Generate complements
A[1] -> notA1.A;
A[2] -> notA2.A;

# Y[1]: selected when A = 00
notA2.O -> and1.A;
notA1.O -> and1.B;
and1.O -> Y[1];

# Y[2]: selected when A = 01
notA2.O -> and2.A;
A[1] -> and2.B;
and2.O -> Y[2];

# Y[3]: selected when A = 10
A[2] -> and3.A;
notA1.O -> and3.B;
and3.O -> Y[3];

# Y[4]: selected when A = 11
A[2] -> and4.A;
A[1] -> and4.B;
and4.O -> Y[4];
}
}

Truth Table

A[2]A[1]Y[1]Y[2]Y[3]Y[4]
001000
010100
100010
110001

How It Works

Each output Y[i] corresponds to a specific input combination. For a 2-to-4 decoder:

  • Y[1] = active when input is 00 → needs ~A[2] AND ~A[1]
  • Y[2] = active when input is 01 → needs ~A[2] AND A[1]
  • Y[3] = active when input is 10 → needs A[2] AND ~A[1]
  • Y[4] = active when input is 11 → needs A[2] AND A[1]

3-to-8 Decoder

Extending to 3 inputs and 8 outputs:

component Decoder3to8(A[3]) -> (Y[8]) {
# Inverters
>i[3]{
not{i}: NOT;
}

# 8 AND gates (each needs 3 inputs, so we chain ANDs)
>i[8]{
and{i}_1: AND; # First pair
and{i}_2: AND; # Combine with third
}

connect {
# Generate complements
>i[3]{
A[{i}] -> not{i}.A;
}

# Y[1]: 000 -> ~A[3] & ~A[2] & ~A[1]
not3.O -> and1_1.A;
not2.O -> and1_1.B;
and1_1.O -> and1_2.A;
not1.O -> and1_2.B;
and1_2.O -> Y[1];

# Y[2]: 001 -> ~A[3] & ~A[2] & A[1]
not3.O -> and2_1.A;
not2.O -> and2_1.B;
and2_1.O -> and2_2.A;
A[1] -> and2_2.B;
and2_2.O -> Y[2];

# Y[3]: 010 -> ~A[3] & A[2] & ~A[1]
not3.O -> and3_1.A;
A[2] -> and3_1.B;
and3_1.O -> and3_2.A;
not1.O -> and3_2.B;
and3_2.O -> Y[3];

# Y[4]: 011 -> ~A[3] & A[2] & A[1]
not3.O -> and4_1.A;
A[2] -> and4_1.B;
and4_1.O -> and4_2.A;
A[1] -> and4_2.B;
and4_2.O -> Y[4];

# Y[5]: 100 -> A[3] & ~A[2] & ~A[1]
A[3] -> and5_1.A;
not2.O -> and5_1.B;
and5_1.O -> and5_2.A;
not1.O -> and5_2.B;
and5_2.O -> Y[5];

# Y[6]: 101 -> A[3] & ~A[2] & A[1]
A[3] -> and6_1.A;
not2.O -> and6_1.B;
and6_1.O -> and6_2.A;
A[1] -> and6_2.B;
and6_2.O -> Y[6];

# Y[7]: 110 -> A[3] & A[2] & ~A[1]
A[3] -> and7_1.A;
A[2] -> and7_1.B;
and7_1.O -> and7_2.A;
not1.O -> and7_2.B;
and7_2.O -> Y[7];

# Y[8]: 111 -> A[3] & A[2] & A[1]
A[3] -> and8_1.A;
A[2] -> and8_1.B;
and8_1.O -> and8_2.A;
A[1] -> and8_2.B;
and8_2.O -> Y[8];
}
}

With Enable

Add an enable signal that disables all outputs when low:

component Decoder2to4Enable(A[2], En) -> (Y[4]) {
notA1: NOT;
notA2: NOT;

# First stage: decode
dec1: AND;
dec2: AND;
dec3: AND;
dec4: AND;

# Second stage: AND with enable
>i[4]{
out{i}: AND;
}

connect {
A[1] -> notA1.A;
A[2] -> notA2.A;

notA2.O -> dec1.A;
notA1.O -> dec1.B;

notA2.O -> dec2.A;
A[1] -> dec2.B;

A[2] -> dec3.A;
notA1.O -> dec3.B;

A[2] -> dec4.A;
A[1] -> dec4.B;

# Gate outputs with enable
dec1.O -> out1.A;
En -> out1.B;
out1.O -> Y[1];

dec2.O -> out2.A;
En -> out2.B;
out2.O -> Y[2];

dec3.O -> out3.A;
En -> out3.B;
out3.O -> Y[3];

dec4.O -> out4.A;
En -> out4.B;
out4.O -> Y[4];
}
}

Testing with PySHDL

from SHDL import Circuit

with Circuit("decoder.shdl") as dec:
for i in range(4):
dec.poke("A", i + 1) # Remember: SHDL uses 1-based!
dec.step()

y = dec.peek("Y")
expected = 1 << i # One-hot encoding

print(f"A={i}: Y={y:04b} (expected {expected:04b})")
assert y == expected

print("All decoder tests passed!")

Common Uses

Decoders are fundamental building blocks used in:

  • Memory addressing - Select which memory cell to read/write
  • Instruction decoding - Determine which operation to perform
  • Demultiplexing - Route one signal to one of many destinations
  • Seven-segment displays - Convert binary to display segments