the path · 0/15
start the path

the kernel path · The machines · lesson 01 of 9

The TPU chip

A TPU is one bet cast in silicon: almost all of the work is matrix multiplication, so build one enormous unit for exactly that and spend the rest of the chip feeding it.

the goal Given a kernel and a TPU generation, name which unit runs each line of it and where every byte sits on its way to the MXU.

mastery work · this chapter0/4
  1. go →
  2. go →
manual items are your word; auto items complete from your streaks, labs, and can-you ticks · stored in your browser only
§ 01

One bet, cast in silicon

A CPU spends most of its area deciding what to do next: branch predictors, reorder buffers, cache hierarchies that guess your access pattern. A TPU spends almost none. The designers looked at what training and serving actually run and found matrix multiplication, over and over, at every scale. So the chip commits. One huge matrix unit does that work, a vector unit handles the arithmetic around it, and everything else on the die exists to keep those two fed.

The commitment shows up in what is missing. There is no branch predictor to speak of, no out-of-order engine, no hardware cache deciding for you what stays close to the compute. When this course later has you choreograph data by hand with a BlockSpec, this lesson is the reason: the hardware chose not to guess, which means someone has to say. On a TPU that someone is the compiler, and in Pallas it is you.

§ 02

The systolic array

Picture the multiply as a piece of machinery rather than a loop. Weights get parked in a square grid of cells, one value per cell, and stay there. Activations enter from one edge, one diagonal per cycle, and every cell they pass does a single multiply-accumulate: take the incoming value, multiply by the parked weight, add to the partial sum flowing through, pass both along. Results drain out the far edge. Nothing fetches an instruction per operation and nothing asks a cache for its operand; the schedule is the geometry. An array built to pulse data through itself this way is called a systolic array, and the MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → is one: 128x128 cells on most generations, 256x256 on v6e.

The geometry explains the costs you will keep meeting. The array has a fill phase while the first diagonals march in, a steady state where all cells work every cycle, and a drain phase at the end. Small matmuls live mostly in fill and drain, which is why they waste the unit; the arithmetic below a (8, 128)-shaped tile can't even occupy one edge. Big matmuls amortize the ramp and run the array flat out. When stage 0 had you predict that a skinny matmul lands memory-bound and a square one compute-bound, this grid is the machinery behind the prediction.

The schedule is the geometry: data moves, and the movement is the computation.
EX·19 the systolic array, cycle by cycle
activations enter →↓ partial sums draincycle 0fill1/36 cellsscaled to 128x128 (v5e):455 MACs this cycle
a 6x6 stand-in for the real grid · fill, steady state, drain · MAC readout scaled to 128x128 (v5e) or 256x256 (v6e) per jax-ml.github.io/scaling-book/tpus/
§ 03

Everything that is not a matmul

Softmax needs an exponential, layernorm needs a square root, and neither is a matrix product. That work goes to the VPUThe vector unit for elementwise work, organized as (8, 128) lanes; the origin of the tiling lattice every layer above obeys.taught in /l/tpu →, a vector unit that applies the same operation across wide registers of data. It is the second citizen of the chip by area and the first by variety: adds, multiplies, exponentials, comparisons, casts, everything elementwise a kernel does between matmuls. On v5p each core carries 64 32-bit vector registers to stage that work.

The split matters for how you read a kernel's cost. A fused attention kernel is not one workload; it is matmuls on the MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → with exponentials and rescaling on the VPUThe vector unit for elementwise work, organized as (8, 128) lanes; the origin of the tiling lattice every layer above obeys.taught in /l/tpu → stitched between them. If the elementwise work is thin, it hides behind the matmuls. If it is thick, the VPU becomes the bottleneck while the MXU idles, and no rooflineThe floor model: latency is at least the larger of FLOPs over peak compute and bytes over bandwidth. Predict first, measure second.taught in /l/tpu → over FLOPs alone will tell you. The habit to build now: when you meet a kernel, sort its lines into MXU lines and VPU lines before you estimate anything.

§ 04

The scratchpad and the staging

Between the compute units and HBMThe chip’s main memory: large, far, and the resource memory-bound ops spend. 8.2e11 bytes per second on v5e, 1.6e12 on v6e.taught in /l/tpu → sits VMEMThe TPU’s software-managed vector scratchpad, about 128 MiB. Blocks must be staged here before compute touches them; what is resident is what your schedule staged.taught in /l/tpu →, roughly 128 MiB of on-chip memory on v5e. Calling it a cache would miss the design. A cache decides for you what stays close; VMEM holds exactly what software staged into it, nothing more. Every block a kernel touches was placed there by a DMAAn asynchronous copy between memories that runs while compute continues; the grid pipeline is DMAs the runtime writes for you.taught in /l/pallas → that something explicitly issued, and the compute units read only from there. Kernel engineering on TPU is mostly the choreography of that staging: which block arrives when, and whether the next transfer overlaps the current compute.

Two smaller memories complete the picture. SMEMScalar memory: lengths, flags, and indices live here, feeding control flow without ever entering the vector datapath.taught in /l/tpu → holds scalars: loop bounds, block indices, flags, the values a kernel branches on. And a scalar core runs alongside the vector units executing the control flow your kernel compiles to, issuing the DMAAn asynchronous copy between memories that runs while compute continues; the grid pipeline is DMAs the runtime writes for you.taught in /l/pallas → descriptors that move blocks from HBMThe chip’s main memory: large, far, and the resource memory-bound ops spend. 8.2e11 bytes per second on v5e, 1.6e12 on v6e.taught in /l/tpu → into VMEM. The guide sections below walk these two in detail, with the diagram most block diagrams omit; here it is enough to hold the division: MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → and VPUThe vector unit for elementwise work, organized as (8, 128) lanes; the origin of the tiling lattice every layer above obeys.taught in /l/tpu → compute, the scalar core decides and fetches, VMEMThe TPU’s software-managed vector scratchpad, about 128 MiB. Blocks must be staged here before compute touches them; what is resident is what your schedule staged.taught in /l/tpu → is where the two worlds meet.

§ 05

Sparsecore, the odd one out

One more unit sits on the die and no kernel in this course touches it. SparseCore exists for embedding lookups, the wide scattered reads that recommendation models hammer and that a dense 128x128 grid is exactly wrong for. It earns its area on those workloads and stays dark on ours. It belongs in your picture of the machine anyway, as the exception that proves the bet: when a workload class mattered enough and fit the MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → badly enough, it got its own silicon rather than bending the array.

§ 01

The parts the diagram usually omits

Every diagram of a TPU core you have seen so far draws two blocks: the MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → doing matrix multiplies, the VPUThe vector unit for elementwise work, organized as (8, 128) lanes; the origin of the tiling lattice every layer above obeys.taught in /l/tpu → doing elementwise work, both fed from VMEM. That picture is accurate as far as it goes, but it leaves out the part that turns a kernel into more than a fixed loop. Somewhere, something has to decide which block of a ragged batch to skip, which index a block spec should read next, whether a flag says stop early. None of that is vector work, and the MXU and VPU diagram never shows where it happens.

one TPU chip, v5e numbers from the chip table: everything the roofline needs is on this picture
HBM 16 GBfar, wide 8.2e11 B/s VMEM ~128 MiBsoftware managedyour schedule stages it MXU 128x128 systolic arraymatmuls only · 1.97e14 bf16 FLOP/s VPU (8, 128) laneseverything elementwise SMEM + scalar core control flow, DMA issue DMA descriptors ICI ×4 4.5e10 B/s each way copper = compute · steel = memory · dashed = control

That somewhere is SMEMScalar memory: lengths, flags, and indices live here, feeding control flow without ever entering the vector datapath.taught in /l/tpu →, scalar memory. It holds the scalars a kernel branches on: sequence lengths, loop bounds, block indices, boolean flags. Scalar prefetch, the mechanism that reads an index array before the pipeline needs it, lands its result in SMEM, not VMEM. A ragged attention kernel checking whether a row's real length is 41 percent of the padded max is reading SMEM to make that call. The multi-kilobyte tile sitting in vector memory is not what the branch decision looks at.

A scalar core runs alongside the vector units to make those decisions and act on them. It executes the kernel's control flow, the loops and branches your Pallas source compiles down to, and it issues the DMAAn asynchronous copy between memories that runs while compute continues; the grid pipeline is DMAs the runtime writes for you.taught in /l/pallas → descriptors that move the next block from HBMThe chip’s main memory: large, far, and the resource memory-bound ops spend. 8.2e11 bytes per second on v5e, 1.6e12 on v6e.taught in /l/tpu → into VMEM. The MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → and VPUThe vector unit for elementwise work, organized as (8, 128) lanes; the origin of the tiling lattice every layer above obeys.taught in /l/tpu → are compute engines. The scalar core is the one deciding what they compute next and fetching it, and it runs at the same time as the vector units, not in alternating turns.

One more unit belongs in the full picture, briefly: SparseCore. It exists specifically for embedding workloads, the wide, sparse lookups that recommendation and ranking models depend on, a shape a dense MXUThe systolic matmul array: 128x128 on v5e, 256x256 on v6e. Matmuls only; everything else is the VPU’s job.taught in /l/tpu → is not built for. Nothing in this stack's kernels touches it, but its presence on the chip matters to what a completed picture of the machine means. Not every workload a TPU runs is a matmul, even though every kernel in this course happens to be one.

The last omitted detail is tiling by dtype, and it is the one you will meet as an error message before you ever meet it as a diagram. TPU register tiles change shape with data width: f32 tiles at (8, 128), bf16 at (16, 128), int8 at (32, 128). Narrower types pack more elements into the same physical lane group, so the sublane dimension grows as the element shrinks. This is not a style rule MosaicThe MLIR dialect Pallas lowers to, and the last layer of the TPU stack you can read; only LLO below it is closed.taught in /l/mosaic → enforces. It is the register file's actual layout, and it is why a lattice error names 8 and 128 specifically: the tile an f32 array's shape has to be divisible by, not a round number someone chose.

before you move on

Check yourself

01 A kernel's inner loop is mostly exponentials and rescaling, with a small matmul at the end. Which unit is your bottleneck candidate, and why won't a FLOPs roofline warn you?

The VPU. The elementwise work runs there while the MXU idles, and a roofline over FLOPs mostly counts matmul arithmetic, so a VPU-bound kernel can sit far below the FLOPs roof and still be at its own ceiling.

02 Why does a small matmul waste the MXU even when its operands are already resident in VMEM?

The systolic array spends its opening cycles filling the wavefront and its closing cycles draining it. A small operand spends most of its time in those ramps, so few cycles run with every cell active, no matter what the memory did.

03 VMEM is often described as a cache. What breaks in that analogy?

A cache decides for itself what stays close. VMEM holds exactly what software staged into it by DMA, nothing is fetched or evicted behind your back, and that is why the staging is yours to choreograph.

assigned

Readings