Part 0 · 2 chapters · ~15 min

From Transistors to Instructions

Switches to gates to adders to registers, and the fetch-decode-execute cycle that runs every program: one RISC-V add instruction followed from the program counter through decode, the register file and the ALU to write-back.

1

Transistors, gates and arithmetic

switches all the way down
  1. Transistors are voltage-controlled switches. CMOS pairs them so almost no power is drawn at rest.
  2. Gates are built from a handful of transistors. NAND alone can build any other gate.
  3. A half adder produces a sum (XOR) and a carry (AND).
  4. A full adder also takes a carry in from the previous bit.
  5. Chained adders let the carry ripple through, so real CPUs use lookahead adders.
  6. Latches, flip-flops, registers and multiplexers are the parts of a data path.
code
// a full adder and a 64-bit ripple-carry adder, in software, bit by bit
const fullAdder = (a: number, b: number, cin: number) => ({
  sum: a ^ b ^ cin,
  cout: (a & b) | (a & cin) | (b & cin),          // majority
});
function add64(x: bigint, y: bigint): bigint {
  let carry = 0, out = 0n;
  for (let bit = 0n; bit < 64n; bit++) {
    const { sum, cout } = fullAdder(Number((x >> bit) & 1n), Number((y >> bit) & 1n), carry);
    out |= BigInt(sum) << bit; carry = cout;           // the ripple: bit n waits for bit n-1
  }
  return out;                                         // overflow past bit 63 is dropped, as in hardware
}
add64(40n, 2n);                                       // 42n
TRANSISTORS TO AN ADDER
switches become gates, gates become a full adder, adders become arithmetic
swipe the figure sideways, or tap expand for full screen
1/6
transistor
The transistor: a MOSFET conducts between source and drain when its gate voltage is high (n-type) or low (p-type). CMOS pairs one of each so that, in a steady state, one is always off: almost no current flows except while switching, which is why chips are efficient and why power grows with switching frequency.
2

The fetch-decode-execute cycle

one loop, billions of times a second
  1. Fetch the instruction at the PC.
  2. Decode its fields into control signals.
  3. Read the source registers.
  4. Execute in the ALU.
  5. Write the result back, going through memory first for loads.
  6. Set the next PC.
architecturestylewhere you meet it
x86-64CISC: variable-length instructions (1 to 15 bytes) decoded into internal micro-opsmost servers and laptops before 2020; Intel, AMD
ARM64 (AArch64)RISC: fixed 4-byte instructions, load/store architectureevery phone, Apple silicon, AWS Graviton, most new servers
RISC-VRISC, open standard, modular extensionsembedded, research, rising in servers
WebAssemblya virtual stack machine, compiled again to one of the abovethe browser; the Build Your Own compiler's VM is a cousin
the frontend link
The phones your users carry run ARM. The 2 GB Android tier from the Architecture course is a phone with in-order or modestly out-of-order ARM cores, small caches and a low clock, which is why the same JavaScript takes six times longer there than on your laptop. Part 1 explains where that factor comes from.
FETCH, DECODE, EXECUTE
one instruction through a simple CPU: the program counter, the instruction, the register file, the ALU and memory
swipe the figure sideways, or tap expand for full screen
1/6
fetch
Fetch: the program counter (PC) holds the address of the next instruction. The CPU reads 4 bytes from that address (from the instruction cache): 0x002081B3, the machine code for add x3, x1, x2.