The CPU
CPU: wiring the control bits
The ALU computes, registers remember, PC advances. The CPU introduces no new parts — it does exactly one thing:wire the 16 bits of an instruction straight onto the control pins of those parts. “Decoding” is just wiring.
01What the CPU does: 16 bits onto control pins
The CPU invents nothing. It does one thing: split the fetched 16-bit instruction into control signals and drive the control pins of the ALU, registers, memory and PC.
· Tracks: the 16-bit data bus, carrying 16-bit cars;
· The factory: the ALU is the only workshop on the line;
· The 16-bit instruction: not a magic spell but a work order for an automatic switch operator —the
a bit throws the input switch (send A or M into the factory); c1~c6 select the arithmetic or logic job; d1~d3 throw the output switch (unload into A, D, or through Memory into RAM); j1~j3 read the sign and zero flags of the result and decide whether the PC track takes the default next stop (+1) or turns to a new station (a jump).The format (how A and C instructions are written) is on the machine language page; what the machine promises externally is on the Hack computer page. This page cares about one thing: how those bits become wires.
- y is
- M (RAM[A])
- ALU computes
- M+1 (control bits 110111)
- Written to
- D
- Jump
- no jump
02Where the six control bits come from
Bits 11–6 of a C instruction are in exactly the same order as the ALU’s six control bits:
| Instruction bit | Wired to | Meaning |
|---|---|---|
| bit 12(a) | Mux16.sel | whether the ALU’s y takes A or M |
| bit 11 | ALU.zx | zero x |
| bit 10 | ALU.nx | invert x |
| bit 9 | ALU.zy | zero y |
| bit 8 | ALU.ny | invert y |
| bit 7 | ALU.f | 1 = add, 0 = and |
| bit 6 | ALU.no | invert the result |
That is why the ALU page presents them in the order zx nx zy ny f no: it matches the instruction exactly, so wiring is one bit to one pin with no conversion.
03Datapath: x is always D, y follows the a bit
The ALU’s two inputs are not arbitrary:
- x = the D register: hard-wired, so every instruction implicitly computes with D.
- y = A or M: chosen by the
abit. When it picks M, M isRAM[A]— A doubles as the address.
Writing back uses three independent lines: d1 writes A, d2 writes D, d3 writes M (each gated by “this is a C instruction”).So the number of combinations on the left of = in D=M+1 is the number of control lines.
| CPU pin | Wired to |
|---|---|
| instruction[16] | where the control bits come from |
| inM[16] | RAM[A], that is M |
| addressM[15] | the low 15 bits of A (the address) |
| outM[16] | the ALU output |
| writeM | “is a C instruction” AND d3 |
| pc[15] | the low 15 bits of PC (next instruction address) |
| reset | the PC reset |
04Jumps: three bits plus two flags
The last three bits j1 j2 j3 mean “less / equal / greater”. ANDed with the ALU flags ng (negative) and zr (zero), they produce “jump or not”:
| jjj | Mnemonic | Condition |
|---|---|---|
| 001 | JGT | result > 0 |
| 010 | JEQ | result = 0 |
| 011 | JGE | result ≥ 0 |
| 100 | JLT | result < 0 |
| 101 | JNE | result ≠ 0 |
| 110 | JLE | result ≤ 0 |
| 111 | JMP | always |
Note: 0;JMP has no destination — the result is only tested, not written back anywhere.
05One instruction, step by step
| Stage | A instruction (@x) | C instruction (D=M+1;JGT) |
|---|---|---|
| Fetch | 16 bits are read from the address in PC and fed to instruction | |
| Top bit | 0: a constant | 1: a computation |
| Compute | nothing; the ALU output is ignored | x=D, y=M, the six control bits compute M+1 |
| Write | A ← the constant | D ← the result (d2=1) |
| Jump | no jump | if the result > 0, PC loads A; otherwise PC+1 |
One pass per clock tick. A “program” is just these 16-bit words lined up in memory.
06How PC gets written
PC is not a register programs write directly; it has three inputs, in priority order:
| Signal | Effect | Priority |
|---|---|---|
| reset | PC = 0 | highest (power-on / reset) |
| load (from the jump) | PC = A (the jump target) | middle |
| inc | PC = PC + 1 | lowest (the default) |
So a jump is two wires in hardware: send A to the PC input, then raise load.An A instruction puts the target address into A first; only then can a jump mean anything.
The jump condition is the three jump bits ANDed/ORed with the ALU’s zr / ng — load goes high only when it holds.When it does not hold, load=0 and PC still increments.
07Writing it in HDL
// Hack CPU: A and D registers + ALU + PC CHIP CPU { IN inM[16], instruction[16], reset; OUT outM[16], writeM, addressM[15], pc[15]; PARTS: // A input: an A-instruction writes a constant, a C-instruction writes the ALU result Mux16(a=instruction, b=aluOut, sel=instruction[15], out=aIn); Not(in=instruction[15], out=notInstruction); // A register: always written by an A-instruction; a C-instruction looks at d1 Or(a=notInstruction, b=instruction[5], out=loadA); ARegister(in=aIn, load=loadA, out=aOut, out[0..14]=addressM); // ALU y: the a bit picks A or M Mux16(a=aOut, b=inM, sel=instruction[12], out=yIn); // ALU control: on an A-instruction, pass D through (zy=ny=1, f=0 → D ∧ -1) And(a=instruction[11], b=instruction[15], out=zx); And(a=instruction[10], b=instruction[15], out=nx); Or(a=instruction[9], b=notInstruction, out=zy); Or(a=instruction[8], b=notInstruction, out=ny); And(a=instruction[7], b=instruction[15], out=f); And(a=instruction[6], b=instruction[15], out=no); ALU(x=dOut, y=yIn, zx=zx, nx=nx, zy=zy, ny=ny, f=f, no=no, out=outM, out=aluOut, zr=zr, ng=ng); // write memory: only a C-instruction with d3 And(a=instruction[15], b=instruction[3], out=writeM); // D register: only a C-instruction with d2 And(a=instruction[15], b=instruction[4], out=loadD); DRegister(in=aluOut, load=loadD, out=dOut); // jump: j1 = less, j2 = equal, j3 = greater Or(a=zr, b=ng, out=notPos); Not(in=notPos, out=pos); And(a=instruction[0], b=pos, out=jgt); And(a=instruction[1], b=zr, out=jeq); And(a=instruction[2], b=ng, out=jlt); Or(a=jgt, b=jeq, out=j1); Or(a=j1, b=jlt, out=jCond); And(a=jCond, b=instruction[15], out=jump); // PC: load A on a jump, otherwise increment PC(in=aOut, load=jump, reset=reset, inc=true, out[0..14]=pc); }
A uses ARegister and D uses DRegister: these are the course’s observable registers, and the test files read their state. A plain Register works the same, but the DRegister column in the compare file stays 0.
Three things to notice in this code: every control bit comes from instruction[..] (a few are gated), and there is no separate decoder; A is both data and address; the PC load comes from the jump logic.