How a Chip Works, From Logic Gates to Systolic Arrays: Reiner Pope's Blackboard Lecture

Open on YouTube ↗
Overview

Reiner Pope is CEO of the AI chip startup MatX and previously worked on TPU architecture at Google. In this blackboard session with Dwarkesh Patel (who disclosed at the start that he is an angel investor in MatX), Pope starts at the smallest unit of chip design and works up toward a production AI chip. His recurring point is that nearly every design decision trades useful computation against the cost of moving data. That trade-off appears in the bit width of a multiplier, in the register file feeding it, in the shape of a systolic array, and in how the clock is set. Along the way the conversation covers FPGAs, CPU caches, branch predictors, the brain, and the difference between GPUs and TPUs.

23 min read

Why the multiply-accumulate is the basic primitive

At the lowest level, Pope explains, a chip is built from logic gates such as AND, OR, and NOT, connected by wires laid out as metal traces. The main thing an AI chip computes is matrix multiplication, and its basic operation is a multiply-accumulate on pairs of numbers.

He shows why with the loop structure of a matrix multiply. There are loops over i, j, and k, and the body is output[i, k] += input[i, j] × other_input[j, k]. Every step of that loop is a multiply-accumulate.

He adds a point he describes as specific to AI chips: the accumulation almost always runs at higher precision than the multiplication. When Dwarkesh asks why, since only two numbers seem to be added, Pope points out that the accumulation repeats across the whole j loop. Rounding errors build up over many additions, while each product involves only one multiplication and so accumulates little error. That is why his worked example multiplies two 4-bit numbers and adds the result to an 8-bit accumulator.

Building the multiplier: partial products from AND gates

Pope works the calculation by hand with long multiplication. One 4-bit number (1001) is multiplied by each bit position of the other, and each row is shifted one place. Where the other number has a 1, the row is a copy of 1001. Where it has a 0, the row is all zeros. The 8-bit accumulator term is copied in as an extra row, since it has to be summed anyway. The result is a five-way sum.

Each of the 16 bits in these partial-product rows comes from a single AND gate. A partial-product bit is 1 only when both input bits are 1, because multiplying by 0 gives 0. The 4×4 case therefore uses 16 AND gates, and a p-bit by q-bit multiply uses p × q ANDs. Pope stresses that most of the work is still to come, in the summation.

Full adders and the Dadda multiplier

For summing, Pope introduces the full adder, which he calls the largest logic gate you typically use (the AND gate is near the smallest). He warns software people that it does not add two 32-bit numbers. It adds three single bits from the same column. The result is 0, 1, 2, or 3, which fits in two bits, so it is also called a 3→2 compressor. Dwarkesh checks his understanding with examples: inputs 101 give 10, 111 give 11, 000 give 00, and 010 give 01. Pope confirms that the full adder simply counts the ones and writes the count in binary. One output bit is the sum, which stays in the same column. The other is the carry, which moves to the next column.

He then sums the grid in a way he admits is unnatural for humans. Working from the rightmost column leftward, he repeatedly takes three bits from a column, runs them through a full adder, crosses them out, and writes the two outputs, with the carry written explicitly instead of held in memory. This continues until one number remains. Pope says this is a Dadda multiplier, the standard design for area-efficient multipliers built from full adders.

The gate count follows from a simple argument. The process starts with 24 bits (16 partial products plus 8 accumulator bits) and ends with an 8-bit result. Each full adder turns three bits into two, removing one bit, so the circuit needs 24 − 8 = 16 full adders. Dwarkesh generalizes it: input bits are p×q + (p+q), output bits are p+q, so the full-adder count is p×q. Pope says this neat algebra is a second reason to treat multiply-accumulate as the primitive, alongside its central role in matrix multiplication. Every step on the blackboard corresponds to a physical gate with wires joining its inputs and outputs, and this circuit at various bit widths is the main primitive inside an AI chip.

FP4, FP8, and quadratic scaling with bit width

Dwarkesh asks why Nvidia quotes FP4 throughput as a multiple of FP8 throughput, which implies the circuits are interchangeable. Pope says that as drawn they are not. How much FP4 and how much FP8 hardware to include is one of the main decisions in chip design. Sometimes it follows customer requirements, and sometimes designers try to equalize the power budget between the formats. He says the exact 2x ratio is not purely a matter of die area. It also has a data-movement explanation: two 4-bit numbers pack into the storage of one 8-bit number, which fits neatly with the widths of the on-chip buses carrying data to and from memory.

Dwarkesh notes that since multiplier area grows with the product of the bit widths, halving precision should give more than a 2x gain. Pope agrees, calls this very important, and says it is the single reason low-precision arithmetic has worked so well for neural nets. According to Pope, Nvidia historically doubled the quoted FLOP count each time precision was halved, up until B100 or B200. From B300 onward the specs list FP4 at three times FP8. Pope and Dwarkesh remark that it "should be 4x." Pope adds that his example is the simplest integer case. Floating-point formats like FP4 and FP8 also have an exponent term that complicates things.

The hidden cost: register files and muxes

Next Pope places the multiply-accumulate unit in context, using how GPUs worked before Tensor Cores, which he says matches how CPUs work. A generic CUDA core or CPU core has a register file (in his example, eight entries) and an ALU. The multiply-accumulate reads three arbitrary registers and writes its result back. He calls this the core data path of most processors.

Selecting an arbitrary register requires a multiplexer, or mux. With only AND and OR available, Pope builds it the simplest way. Each entry is ANDed with a one-hot mask that is 1 only for the wanted entry, and then all rows are ORed together. An n-input mux over p-bit values costs n × p AND gates and (n − 1) × p OR gates. He walks through a two-input example: the chosen row passes through its ANDs, the other row becomes all zeros, and four ORs combine them. He notes that this looks like the addition circuit, with the full adders swapped for plain ORs. Dwarkesh comments that software people never think about how much circuitry sits behind "just select element three." Pope calls this the first of the hidden data-movement costs.

Then he compares numbers. Three inputs need three muxes, so the data movement costs 3 × n × p AND gates. With n = 8, that is 24p gates, against about 4p for the multiplier when q = 4. In this design, he says, roughly seven-eighths of the cost goes to reading and writing the register file, and only a small fraction goes to the logic doing the useful work. In Pope's account, this was essentially the situation before Nvidia's Volta generation, and it is the problem that motivated Tensor Cores, which he says are more generally called systolic arrays.

Systolic arrays: moving the loop into hardware

The aim, Pope says, is to make the useful logic larger while the data-movement overhead stays about the same. The earlier design put a single multiply-accumulate, the innermost loop body, into hardware. A systolic array goes two loop levels up and puts a whole block of the loop into fixed-function hardware, so that the costs paid at the inputs and outputs are spread across much more computation. He points to two effects. More work gets done per trip through the register file, and some operands stay fixed over part of the loop.

He illustrates with a matrix-vector product, using a 2×2 matrix times a vector (3, 7), laid out so each output is a dot product summed down a column. There is one multiply-accumulate per matrix entry, so four in this case. Dwarkesh confirms that each output is a dot product, and Pope notes that the accumulation starts from zero. The design goal is x × y compute with only about x communication, so the advantage grows with y.

The key trick is that in AI workloads the matrix, the weights, stays fixed for a long time. Instead of reading it from the register file every cycle, the array stores each weight in a register next to its multiplier and reuses it for many input vectors. The input vector enters from the side and is broadcast along rows. Zeros enter at the top of each column, and finished dot products come out at the bottom. The dot product is laid out in space the same way it is laid out on paper. Only x values come in and x values go out per step, which meets the budget. Dwarkesh restates the principle: a matrix multiply does many multiplications per output value, so the stored weights, which have an extra dimension compared with the vectors, can sit where the computation happens.

That leaves the question of how the weights get into the array. Pope's answer is to load them slowly. In the simplest scheme, a new row enters the top of the array each clock cycle and every row below shifts down one place. After y cycles the array is full, and the wiring crossing the array boundary is still proportional to x, not x × y. Dwarkesh summarizes it this way: since weights are loaded rarely, the design minimizes bandwidth, which costs die area, and accepts a longer loading time. Pope agrees.

He drew a 2×2 array but says older TPUs were described as 128×128 arrays of this circuit, and he calls the systolic array the most efficient known circuit for matrix multiplication.

Compute versus communication, and the sizing questions

Dwarkesh links this to their earlier conversation about inference across many chips, where the goal was more compute per unit of memory bandwidth. Pope says the same pattern shows up all the way up and down the stack. Near the bottom it appears in number format precision, and one level up in matrix size. In both, a quadratic compute term is set against a linear communication term.

Asked which trade-offs are genuinely hard, Pope says most chip design decisions are about sizing. Every AI chip has a systolic array with a register file nearby, and even at that level the designer must choose how large each should be. The two choices are linked. One approach is to set a budget, for example 10% of area for data movement and 90% for the systolic array, and size the register file from that. Larger register files are more flexible and give more application-level performance, but they take area from the array.

What a clock cycle is

Pope explains the clock as a synchronization mechanism. A chip with around 100 billion transistors is massively parallel, and parallel units must synchronize. Software uses costly tools like mutexes. Chips instead have all circuitry pause and synchronize roughly every nanosecond, and the whole chip moves in lockstep to the next step. Physically, this is done with registers: storage elements at the inputs and outputs of each "cloud of logic," all driven by a global clock signal. When the clock ticks, whatever value is on a wire is captured.

Faster clocks mean more operations per second, so running at 2 GHz does twice the work of 1 GHz. But all logic between registers must finish within one cycle, so reducing that delay is a major optimization target. Dwarkesh asks whether designers ever gamble on a computation finishing in time. Pope says standard practice leaves a margin so that failure is many standard deviations away, with signals arriving about 25% of a cycle early. The exception is clock domain crossings between different clocks, where designers really do have to reason about the probability.

When Dwarkesh uses Factorio as an example of a system with no global clock, Pope explains why one is needed. If computations f and g run on separate paths and meet at h, manufacturing variation means either path could be slightly slower on a given chip. Without synchronization, f's result could meet the previous or next value of g. Dwarkesh draws the conclusion that two chips on the same TSMC node, say 3 nm, can reach different clock speeds depending on how well their designers kept the critical path short. Pope agrees.

Pipeline registers, feedback loops, and going too far

Placing registers, Pope says, is a large part of chip design and is done with a mix of manual and automated methods. The simplest move is pipeline register insertion. A logic cloud is split in half with a register between the halves, which can roughly double the clock frequency at the cost of an extra register. He calls this a pure trade-off between clock speed and area.

The harder case is logic that feeds back into itself, such as an adder that adds a new number to a running total every cycle. Adding a pipeline register inside that loop changes what it computes. Instead of one running sum, you get two: one over the even-cycle inputs and one over the odd-cycle inputs. Pope says every chip has loops like this somewhere, and they are the hardest thing to address and what ultimately sets the clock cycle. When Dwarkesh asks what a register "inside" an addition would even mean, Pope points back to the multiplier demonstration. Addition takes many steps, so a register can be placed between the early steps and the later ones.

Dwarkesh asks why designers cannot just take TSMC's PDK primitives and keep adding registers until they reach any target clock speed. Pope says the chip architect sets the clock cycle. Primitives such as AND gates or full adders take roughly 10 picoseconds, and depending on voltage and library choice, about 10 to 30 fit in sequence within one cycle. In principle, a register looping through a single AND gate could run above four, five, or six gigahertz. But the AND gate is one gate-equivalent of area, and the register is maybe eight. Nearly all the area would go to synchronization rather than logic. Chip throughput is work per cycle, which depends on area efficiency, multiplied by cycles per second, so pushing the clock too high reduces total throughput. Dwarkesh compares this to batch size in inference, where small batches give each user faster tokens but fewer total tokens per hour. Pope agrees that you get less parallelism if you drive the clock speed up too far.

FPGAs versus ASICs

Dwarkesh mentions a Jane Street FPGA engineer who told him that high-frequency trading values latency and deterministic timing over throughput. He asks why an ASIC cannot do the same. Pope frames it as a business decision. FPGAs and ASICs share a conceptual model of small gates, wires, and a fixed clock, and anything expressible on an FPGA can be built as an ASIC. According to Pope, the ASIC version will be about an order of magnitude cheaper and more energy-efficient. But the first FPGA costs about $10,000, while the first ASIC costs about $30 million because it needs a full tape-out. FPGAs make sense when you need deterministic latency, speed, and parallelism but expect to change the workload often, perhaps monthly.

Inside, an FPGA has registers, lookup tables (LUTs) that act as gates, and a large network of muxes. Every register and LUT has a mux in front of it that can pick an input from nearby components. Programming the FPGA means setting the control bits for all these muxes, with a small storage element next to each one recording where it takes its input. This overlays a specific circuit on generic hardware. Pope explains that "field-programmable" means programmable after deployment, out in the world, for example in a data center. It has nothing to do with electric fields.

A LUT, he explains, typically has four input bits and one output bit and stores a truth table with one entry for each of the 16 input combinations, held in configuration bits. The four inputs are read as a binary index into the table. Dwarkesh summarizes it as a programmable gate. Pope says four inputs is a sweet spot, which is another compute-versus-communication balance: with too few inputs, you need more LUTs.

Pope uses this to explain the roughly 10x overhead. A LUT is essentially a mux choosing among 16 values with a 1-bit width, so by the earlier formula it costs about 16 ANDs and 16 ORs, around 32 gates. The inputs to the LUT come from a separate set of muxes: four small muxes, one per input bit, each choosing among about eight nearby single-bit registers and LUTs. Pope describes it as "muxes all the way down." A four-input AND implemented in a LUT costs about 32 gates, while an ASIC needs three AND gates. Dwarkesh puts it as the difference between listing every entry of a truth table and simply placing the gate. Pope adds that FPGA vendors give flexibility within local neighborhoods but decide the coarser, long-distance wiring themselves.

Deterministic latency, caches, and scratchpads

On why CPUs do not offer deterministic timing, Pope says they could. Many processors inside AI chips have deterministic latency. He mentions that Groq has advertised this and that TPU cores have it as well. The difficulty is getting deterministic latency and high speed together. Non-determinism in CPUs comes from specific design choices. CPUs without those choices are unattractive in the market and are no longer made, so in a sense determinism is the simpler starting point and non-determinism was added on top.

He names the cache as probably the largest source. A CPU checks its cache before going to external DDR memory. The cache is about two orders of magnitude faster, and without it most programs would run about a hundred times slower, so it is essential. But whether a given access hits depends on the environment: other programs running, recent history, and even the random number generator inside the cache system.

The alternative is to make the decision in software, as TPUs do. The on-chip memory is a scratchpad, and there are separate instructions for reading or writing the scratchpad and for reading or writing off-chip HBM. The program says explicitly where each access goes, and the hardware does not decide.

Von Neumann machines and why CPU cores are large

Dwarkesh asks whether the "von Neumann architecture" label still describes such parallel hardware. Pope thinks it fits CPUs. He estimates CPU parallelism at about 100 cores times perhaps 16-way vector units, around 1,000-way in total. When Dwarkesh asks what all the CPU die area is used for, Pope says the cores are simply much larger and more complex. A CPU core takes about a hundredth of the die, while an FPGA LUT is about 16 gates.

For CPUs versus GPUs, Pope says much CPU area goes to cache and register files, with little on the ALUs themselves, but GPUs have equivalents of both. What GPUs lack is the branch predictor, a large area of predictors that guess when the next branch will occur and where it will go. According to Pope, removing most of that and tightening the register files accounts for much of the GPU's advantage.

He explains why branch prediction exists. Detecting a branch, evaluating its condition, updating the program counter, and fetching from instruction memory might take about five nanoseconds, which corresponds to a 200 MHz clock. Designers want 1–2 GHz, so the processor keeps executing the instructions after the branch while it resolves. If the branch turns out to be taken, that work was wrong and execution must jump to the target. The predictor tries to identify, about five cycles in advance, that a branch is coming and where it leads.

Brains versus chips

Dwarkesh lists possible differences between brains and accelerators. Brains have unstructured sparsity, where any neuron can connect to any other, while chips support only structured sparsity. Brains co-locate memory and compute, although Dwarkesh concedes that a systolic array's stored weights are a kind of co-location. And brains run much slower clocks, which he suggests saves energy because faster clocks need higher voltages.

Pope responds mainly on clock speed. Chips run fast because that increases throughput, and a GPU serves something like a batch of 1,000, whereas "there's only one of me." You could run a GPU at a megahertz instead of a gigahertz to resemble the brain, but in silicon that would not produce a 1,000x gain in energy efficiency. The circuit would settle once and then sit idle. He explains that most of a chip's energy is dynamic or switching power. A stored bit is effectively charge on a capacitor, and energy is spent each time it charges from 0 to 1 and discharges to ground. He sets aside leakage from imperfect insulators. Clocking a chip 1,000 times less often produces about 1,000 times fewer transitions and therefore about 1,000 times less energy, but the energy per operation, and so the efficiency, does not substantially improve.

A GPU is a grid of tiny TPUs

In the final part, Pope compares the top-level layout of the two accelerators. A GPU is a fairly regular grid of nearly identical streaming multiprocessors (SMs) with L2 memory in the middle. A TPU has a few large, coarse units: big matrix units (systolic arrays) around a central vector unit. Scale a TPU down to a small matrix unit plus a small vector unit and you get roughly an SM, so in Pope's view a GPU is many tiny TPUs tiled across the chip. Dwarkesh asks whether an SM's tensor core corresponds to a TPU's MXU, and Pope says it is all very similar.

Dwarkesh suggests the trade-off: many small TPUs suit less structured work, while large uniform matrix multiplies might avoid paying for per-SM registers and warp schedulers. Pope ties this to the systolic-array theme. The TPU layout allows larger arrays that spread register file costs more efficiently, while the GPU layout limits every unit to a small size. But the coarse TPU layout has a cost. All data between the vector unit and the matrix units has to cross a small perimeter, which he draws as two lines. In a GPU, vector units are everywhere and data can move across many boundaries, sixteen lines in his sketch, so vector-to-matrix bandwidth is actually much higher on a GPU. Dwarkesh adds that the distances may also be shorter, which saves energy. Pope notes that this holds within an SM, but data movement across SMs becomes more complicated and expensive.

Dwarkesh speculates that MatX might try to combine GPU-style small systolic arrays surrounded by SRAM while dropping the SM features that exist to support the CUDA architecture. Pope does not confirm details. He points to what MatX has said publicly about a "splittable systolic array," which he describes as large systolic arrays that can also act as small ones. The conversation ends there.