CS ATLAS A map of computing

Project 1 · NAND only

Basic gates: everything from NAND

Project 1 of nand2tetris: the whole computer starts from one gate — NAND. This page follows Project 1: build the four basic gates from NAND, then the multiplexers and demultiplexers, then copy them to 16 bits, then use a multi-bit sel for 4-way and 8-way selection. Click any row of a truth table or selection table to see where the current flows.

Why NAND? Because it is functionally complete: any Boolean function can be built from it alone (Sheffer, 1913). NOT, AND, OR and XOR all come from it, so one kind of part is enough to grow a whole computer.

NAND count: NOT 1 · AND 2 · OR 3 · XOR 4

Before reading HDL: HDL is not a program and has no execution order. Line 1 and line 100 are the same thing — every line is a wire that is live at the same time. Do not read top to bottom; follow where the signals come from and go.

NOT ¬a

Built from 1 NAND

Truth table

aout
01
10

Built from NAND

in 1out 0

How it is wired

Wire the input to both pins: NAND(a, a) = ¬a.

  • Inputs: a.
  • NANDs: 1.
  • Output: out = ¬a.
CHIP Not {
  IN in;
  OUT out;
  PARTS:
  Nand(a=in, b=in, out=out);
}

Both inputs of one NAND are tied together: Nand(a=in, b=in). Inversion needs no extra part.

AND a ∧ b

Built from 2 NANDs

Truth table

about
000
010
100
111

Built from NAND

a 1b 1n 0out 1

How it is wired

NAND, then invert: inverting ¬(a ∧ b) gives a ∧ b.

  • Inputs: a, b.
  • NANDs: 2.
  • Output: out = a ∧ b.
CHIP And {
  IN a, b;
  OUT out;
  PARTS:
  Nand(a=a, b=b, out=n);
  Not(in=n, out=out);
}

NAND first, then invert. The inversion is itself a NAND — two in total.

OR a ∨ b

Built from 3 NANDs

Truth table

about
000
011
101
111

Built from NAND

a 1b 1na 0nb 0out 1

How it is wired

Invert both inputs, then NAND: ¬(¬a ∧ ¬b) = a ∨ b (De Morgan).

  • Inputs: a, b.
  • NANDs: 3.
  • Output: out = a ∨ b.
CHIP Or {
  IN a, b;
  OUT out;
  PARTS:
  Not(in=a, out=na);
  Not(in=b, out=nb);
  Nand(a=na, b=nb, out=out);
}

Invert both inputs, then combine with NAND: ¬(¬a ∧ ¬b) = a ∨ b.

XOR a ⊕ b

Built from 4 NANDs

Truth table

about
000
011
101
110

Built from NAND

a 1b 1n1 0n2 1n3 1out 0

How it is wired

First n1 = ¬(a ∧ b); NAND n1 with a and with b to get n2 and n3; then NAND n2 and n3 together.

  • Inputs: a, b.
  • NANDs: 4.
  • Output: out = a ⊕ b.
CHIP Xor {
  IN a, b;
  OUT out;
  PARTS:
  Nand(a=a, b=b, out=n1);
  Nand(a=a, b=n1, out=n2);
  Nand(a=b, b=n1, out=n3);
  Nand(a=n2, b=n3, out=out);
}

Four NANDs: n1 = ¬(a∧b), n2 = ¬(a∧n1), n3 = ¬(b∧n1), out = ¬(n2∧n3).

Selecting

Combine several lines into one (MUX), or split one into several (DMUX). They do not change the data, only where it goes.

MUX multiplexer 2:1

out = (¬sel ∧ a) ∨ (sel ∧ b) — sel=0 picks a, sel=1 picks b

Selection table

abselout
0000
0010
0100
0111
1001
1010
1101
1111

Symbol

a1b0sel0out1MUX

What it does

  • sel only selects: sel decides the route and takes no part in the computation itself.
  • The other input is dropped: the input that is not selected has no effect on the output.
  • It can be widened: Mux16 is sixteen of these side by side; Mux4Way16 uses two sel bits to choose among four.
CHIP Mux {
  IN a, b, sel;
  OUT out;
  PARTS:
  Not(in=sel, out=notsel);
  And(a=a, b=notsel, out=x1);
  And(a=b, b=sel, out=x2);
  Or(a=x1, b=x2, out=out);
}

One sel bit, with a, b and out all 1 bit wide; Mux16 is just sixteen copies of this.

DMUX demultiplexer 1:2

a = in ∧ ¬sel, b = in ∧ sel — sel=0 sends to a, sel=1 sends to b

Selection table

inselab
0000
0100
1010
1101

Symbol

in1a1b0sel0DMUX

What it does

  • One in, two out: only one output gets in at a time; the other is forced to 0.
  • The mirror image of MUX: MUX picks one line out of several; DMUX sends one line to one of several.
  • It can be widened: DMux4Way uses two sel bits to send the input to one of four outputs.
CHIP DMux {
  IN in, sel;
  OUT a, b;
  PARTS:
  Not(in=sel, out=notsel);
  And(a=in, b=notsel, out=a);
  And(a=in, b=sel, out=b);
}

Exactly symmetric to Mux: sel chooses which output gets in, and the other is forced to 0.

MUXDMUX
Directionmany → one (merge)one → many (split)
Controlsel picks which line comes insel picks which line goes out
Role in the datapathALU input selection, register write-back, forwardingsend one result to different destinations (register / memory)
ExtensionMux4Way16 / Mux8Way16DMux4Way / DMux8Way
Wider words

The second half of Project 1: copy a 1-bit gate sixteen times. Click any bit and the 16-bit result below changes at once.

Not16 out[i] = ¬in[i]

Sixteen 1-bit gates side by side

Bit slice

in[0]out[0]in[1]out[1]⋮× 16

16-bit demo

in00001111000011110x0F0F
out11110000111100000xF0F0

What it does

16 Not gates side by side; each bit inverts on its own — no carry, no shared state.

  • Inputs: in (16 bits each).
  • Output: out (16 bits).
  • Key point: bit i depends only on bit i — that is what “width” means.
CHIP Not16 {
  IN in[16];
  OUT out[16];
  PARTS:
  Not(in=in[0], out=out[0]);
  Not(in=in[1], out=out[1]);
  Not(in=in[2], out=out[2]);
  Not(in=in[3], out=out[3]);
  Not(in=in[4], out=out[4]);
  Not(in=in[5], out=out[5]);
  Not(in=in[6], out=out[6]);
  Not(in=in[7], out=out[7]);
  Not(in=in[8], out=out[8]);
  Not(in=in[9], out=out[9]);
  Not(in=in[10], out=out[10]);
  Not(in=in[11], out=out[11]);
  Not(in=in[12], out=out[12]);
  Not(in=in[13], out=out[13]);
  Not(in=in[14], out=out[14]);
  Not(in=in[15], out=out[15]);
}

Sixteen Not gates: gate i touches only in[i] and out[i]; none affects another.

And16 out[i] = a[i] ∧ b[i]

Sixteen 1-bit gates side by side

Bit slice

a[0]b[0]out[0]a[1]b[1]out[1]⋮× 16

16-bit demo

a00001111000011110x0F0F
b00000000111111110x00FF
out00000000000011110x000F

What it does

16 And gates side by side. Bit i looks only at a[i] and b[i]; no other bit affects it.

  • Inputs: a, b (16 bits each).
  • Output: out (16 bits).
  • Key point: bit i depends only on bit i — that is what “width” means.
CHIP And16 {
  IN a[16], b[16];
  OUT out[16];
  PARTS:
  And(a=a[0], b=b[0], out=out[0]);
  And(a=a[1], b=b[1], out=out[1]);
  And(a=a[2], b=b[2], out=out[2]);
  And(a=a[3], b=b[3], out=out[3]);
  And(a=a[4], b=b[4], out=out[4]);
  And(a=a[5], b=b[5], out=out[5]);
  And(a=a[6], b=b[6], out=out[6]);
  And(a=a[7], b=b[7], out=out[7]);
  And(a=a[8], b=b[8], out=out[8]);
  And(a=a[9], b=b[9], out=out[9]);
  And(a=a[10], b=b[10], out=out[10]);
  And(a=a[11], b=b[11], out=out[11]);
  And(a=a[12], b=b[12], out=out[12]);
  And(a=a[13], b=b[13], out=out[13]);
  And(a=a[14], b=b[14], out=out[14]);
  And(a=a[15], b=b[15], out=out[15]);
}

Sixteen And gates: no carry, no shared state, each bit on its own.

Or16 out[i] = a[i] ∨ b[i]

Sixteen 1-bit gates side by side

Bit slice

a[0]b[0]out[0]a[1]b[1]out[1]⋮× 16

16-bit demo

a00001111000011110x0F0F
b00000000111111110x00FF
out00001111111111110x0FFF

What it does

16 Or gates side by side. Only the gate differs from And16.

  • Inputs: a, b (16 bits each).
  • Output: out (16 bits).
  • Key point: bit i depends only on bit i — that is what “width” means.
CHIP Or16 {
  IN a[16], b[16];
  OUT out[16];
  PARTS:
  Or(a=a[0], b=b[0], out=out[0]);
  Or(a=a[1], b=b[1], out=out[1]);
  Or(a=a[2], b=b[2], out=out[2]);
  Or(a=a[3], b=b[3], out=out[3]);
  Or(a=a[4], b=b[4], out=out[4]);
  Or(a=a[5], b=b[5], out=out[5]);
  Or(a=a[6], b=b[6], out=out[6]);
  Or(a=a[7], b=b[7], out=out[7]);
  Or(a=a[8], b=b[8], out=out[8]);
  Or(a=a[9], b=b[9], out=out[9]);
  Or(a=a[10], b=b[10], out=out[10]);
  Or(a=a[11], b=b[11], out=out[11]);
  Or(a=a[12], b=b[12], out=out[12]);
  Or(a=a[13], b=b[13], out=out[13]);
  Or(a=a[14], b=b[14], out=out[14]);
  Or(a=a[15], b=b[15], out=out[15]);
}

Sixteen Or gates. Same as And16, only the gate changes.

Mux16 out[i] = sel ? b[i] : a[i]

Sixteen 1-bit gates side by side

Bit slice

MUXa[0]b[0]selout[0]MUXa[1]b[1]selout[1]⋮× 16

16-bit demo

a00001111000011110x0F0F
b00000000111111110x00FF
out00000000111111110x00FF

What it does

16 Muxes side by side with a single sel — it decides the way for all 16 bits at once.

  • Inputs: a, b (16 bits each), sel (1 bit).
  • Output: out (16 bits).
  • Key point: bit i depends only on bit i — that is what “width” means.
CHIP Mux16 {
  IN a[16], b[16], sel;
  OUT out[16];
  PARTS:
  Mux(a=a[0], b=b[0], sel=sel, out=out[0]);
  Mux(a=a[1], b=b[1], sel=sel, out=out[1]);
  Mux(a=a[2], b=b[2], sel=sel, out=out[2]);
  Mux(a=a[3], b=b[3], sel=sel, out=out[3]);
  Mux(a=a[4], b=b[4], sel=sel, out=out[4]);
  Mux(a=a[5], b=b[5], sel=sel, out=out[5]);
  Mux(a=a[6], b=b[6], sel=sel, out=out[6]);
  Mux(a=a[7], b=b[7], sel=sel, out=out[7]);
  Mux(a=a[8], b=b[8], sel=sel, out=out[8]);
  Mux(a=a[9], b=b[9], sel=sel, out=out[9]);
  Mux(a=a[10], b=b[10], sel=sel, out=out[10]);
  Mux(a=a[11], b=b[11], sel=sel, out=out[11]);
  Mux(a=a[12], b=b[12], sel=sel, out=out[12]);
  Mux(a=a[13], b=b[13], sel=sel, out=out[13]);
  Mux(a=a[14], b=b[14], sel=sel, out=out[14]);
  Mux(a=a[15], b=b[15], sel=sel, out=out[15]);
}

Copied bit by bit: bit i depends only on a[i], b[i] and the same sel.

Wide select and decode

Once sel has several bits it can choose among 4 or 8 lines. Click any row in the left table to see the route switch.

Mux4Way16 sel has 2 bits

pick 1 of 4

Selection table

selSelected
00a
01b
10c
11d

Route

MUX 4abcdoutsel[2]

What it does

Pick 1 of 4; sel has 2 bits. Built from two levels of Mux16: pair up first, then choose once more.

  • Inputs: a, b, c, d (16 bits each).
  • Output: out (16 bits).
  • Key point: Each sel bit picks the next branch.
CHIP Mux4Way16 {
  IN a[16], b[16], c[16], d[16], sel[2];
  OUT out[16];
  PARTS:
  Mux16(a=a, b=b, sel=sel[0], out=ab);
  Mux16(a=c, b=d, sel=sel[0], out=cd);
  Mux16(a=ab, b=cd, sel=sel[1], out=out);
}

Two levels of Mux16: sel[0] picks one of (a,b) and one of (c,d), then sel[1] picks between those two results.

Mux8Way16 sel has 3 bits

pick 1 of 8

Selection table

selSelected
000a
001b
010c
011d
100e
101f
110g
111h

Route

MUX 8abcdefghoutsel[3]

What it does

Pick 1 of 8; sel has 3 bits. Three levels of Mux16: 4 → 2 → 1.

  • Inputs: a, b, c, d, e, f, g, h (16 bits each).
  • Output: out (16 bits).
  • Key point: Each sel bit picks the next branch.
CHIP Mux8Way16 {
  IN a[16], b[16], c[16], d[16], e[16], f[16], g[16], h[16], sel[3];
  OUT out[16];
  PARTS:
  Mux16(a=a, b=b, sel=sel[0], out=ab);
  Mux16(a=c, b=d, sel=sel[0], out=cd);
  Mux16(a=e, b=f, sel=sel[0], out=ef);
  Mux16(a=g, b=h, sel=sel[0], out=gh);
  Mux16(a=ab, b=cd, sel=sel[1], out=abcd);
  Mux16(a=ef, b=gh, sel=sel[1], out=efgh);
  Mux16(a=abcd, b=efgh, sel=sel[2], out=out);
}

Three levels of Mux16: 4 → 2 → 1, one sel bit per level.

DMux4Way sel has 2 bits

send 1 input to one of 4 outputs

Selection table

selSelected
00a
01b
10c
11d

Route

DMUX 4abcdinsel[2]

What it does

Send one input to one of four outputs; the rest are forced to 0 by AND gates.

  • Inputs: in (1 bit).
  • Output: a, b, c, d (1 bit each).
  • Key point: Each sel bit picks the next branch.
CHIP DMux4Way {
  IN in, sel[2];
  OUT a, b, c, d;
  PARTS:
  Not(in=sel[0], out=ns0);
  Not(in=sel[1], out=ns1);
  And(a=in, b=ns1, out=t1);
  And(a=t1, b=ns0, out=a);
  And(a=t1, b=sel[0], out=b);
  And(a=in, b=sel[1], out=t2);
  And(a=t2, b=ns0, out=c);
  And(a=t2, b=sel[0], out=d);
}

Split by sel[1] into halves, then by sel[0] into two each; the other outputs are forced to 0 by And gates.

DMux8Way sel has 3 bits

send 1 input to one of 8 outputs

Selection table

selSelected
000a
001b
010c
011d
100e
101f
110g
111h

Route

DMUX 8abcdefghinsel[3]

What it does

Send one input to one of eight outputs; sel has 3 bits.

  • Inputs: in (1 bit).
  • Output: a, b, c, d, e, f, g, h (1 bit each).
  • Key point: Each sel bit picks the next branch.
CHIP DMux8Way {
  IN in, sel[3];
  OUT a, b, c, d, e, f, g, h;
  PARTS:
  Not(in=sel[0], out=ns0);
  Not(in=sel[1], out=ns1);
  Not(in=sel[2], out=ns2);
  And(a=in, b=ns2, out=t1);
  And(a=t1, b=ns1, out=t2);
  And(a=t2, b=ns0, out=a);
  And(a=t2, b=sel[0], out=b);
  And(a=t1, b=sel[1], out=t3);
  And(a=t3, b=ns0, out=c);
  And(a=t3, b=sel[0], out=d);
  And(a=in, b=sel[2], out=t4);
  And(a=t4, b=ns1, out=t5);
  And(a=t5, b=ns0, out=e);
  And(a=t5, b=sel[0], out=f);
  And(a=t4, b=sel[1], out=t6);
  And(a=t6, b=ns0, out=g);
  And(a=t6, b=sel[0], out=h);
}

Three levels of DMux4Way: every output is an And gate, decided by all three sel bits.

Or8Way 8-way reduction

reduce 8 inputs to 1 bit

Truth table

inout
all 00
any 11

Route

OR 8in[0]in[1]in[2]in[3]in[4]in[5]in[6]in[7]out

What it does

Eight inputs chained through Or: if any is 1, out is 1.

  • Inputs: in[8] (8 bits).
  • Output: out (1 bit).
  • Key point: Any input that is 1 makes out 1.
CHIP Or8Way {
  IN in[8];
  OUT out;
  PARTS:
  Or(a=in[0], b=in[1], out=t1);
  Or(a=t1, b=in[2], out=t2);
  Or(a=t2, b=in[3], out=t3);
  Or(a=t3, b=in[4], out=t4);
  Or(a=t4, b=in[5], out=t5);
  Or(a=t5, b=in[6], out=t6);
  Or(a=t6, b=in[7], out=out);
}

Eight inputs chained into one OR — if any bit is 1, the result is 1.