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
| a | out |
|---|---|
| 0 | 1 |
| 1 | 0 |
Built from NAND
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
| a | b | out |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
Built from NAND
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
| a | b | out |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
Built from NAND
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
| a | b | out |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Built from NAND
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).
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
| a | b | sel | out |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
Symbol
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
| in | sel | a | b |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
Symbol
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.
| MUX | DMUX | |
|---|---|---|
| Direction | many → one (merge) | one → many (split) |
| Control | sel picks which line comes in | sel picks which line goes out |
| Role in the datapath | ALU input selection, register write-back, forwarding | send one result to different destinations (register / memory) |
| Extension | Mux4Way16 / Mux8Way16 | DMux4Way / DMux8Way |
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
16-bit demo
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
16-bit demo
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
16-bit demo
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
16-bit demo
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.
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
| sel | Selected |
|---|---|
| 00 | a |
| 01 | b |
| 10 | c |
| 11 | d |
Route
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
| sel | Selected |
|---|---|
| 000 | a |
| 001 | b |
| 010 | c |
| 011 | d |
| 100 | e |
| 101 | f |
| 110 | g |
| 111 | h |
Route
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
| sel | Selected |
|---|---|
| 00 | a |
| 01 | b |
| 10 | c |
| 11 | d |
Route
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
| sel | Selected |
|---|---|
| 000 | a |
| 001 | b |
| 010 | c |
| 011 | d |
| 100 | e |
| 101 | f |
| 110 | g |
| 111 | h |
Route
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
| in | out |
|---|---|
| all 0 | 0 |
| any 1 | 1 |
Route
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.