From Kung & Leiserson 1978 to Blackwell FP4 — what an AI accelerator's MMUL really is, why every modern chip has one, and how it shapes the rest of the system.
Twenty-three slides, broadly grouped:
A modern LLM forward pass is more than 99% multiply-accumulate (MAC). On a general-purpose CPU, almost all the energy spent on that workload is not the math — it's fetching operands from DRAM and shuttling them through register files.
| Operation (45 nm) | Energy | Relative to MAC |
|---|---|---|
| 32-bit FP MUL | 3.7 pJ | 1× |
| 32-bit FP ADD | 0.9 pJ | 0.25× |
| 32-bit FP MAC (effective) | 4.6 pJ | 1× |
| 32-bit register-file read | 0.1 pJ | 0.02× |
| 32-bit on-chip SRAM read | 5 pJ | ~1× |
| Off-chip DRAM access | 640 pJ | ~140× |
An MMUL only wins if every operand fetched from DRAM is reused O(N) times across many MACs. That reuse — temporal in registers and scratchpads, spatial across thousands of PEs — is the entire reason the architecture exists.
Williams et al. (2009): performance is bounded by min(peak FLOP/s, AI · peak BW), where arithmetic intensity AI = FLOPs per byte of DRAM traffic.
1/12. Firmly memory-bound.2N/3 ≈ 683 FLOPs/byte. Compute-bound on every modern device.Matmul is the rare workload where you can spend 1 byte of DRAM bandwidth and get hundreds of FLOPs back. The MMUL is the structure that turns that arithmetic intensity into actual joules saved.
| Year | Event |
|---|---|
| 1976 | TRW MPY-016 single-chip MAC — the immediate ancestor of every "tensor" instruction |
| 1978 | Kung & Leiserson, Systolic Arrays (for VLSI) — Sparse Matrix Proceedings, SIAM |
| 1982 | H. T. Kung, Why Systolic Architectures? — IEEE Computer. Often misdated as 1979 — the canonical citation |
| 1984 | Warp (CMU + GE/Honeywell) — 10-cell linear systolic, 100 MFLOPS/cell @ 10 MHz |
| 1990 | iWarp (CMU + Intel) — single-chip 700K-transistor LIW with on-die mesh; ancestor of "compute + on-chip routing" |
| 2015 | Google TPU v1 deployed internally — 256×256 INT8 systolic, 92 TOPS @ 700 MHz |
| 2017 | NVIDIA Volta V100 ships first Tensor Core (4×4×4, FP16 mul / FP32 acc) |
| 2018 | Turing T4: INT8/INT4 in TC; NVDLA RTL released open-source |
| 2020 | A100: TF32, BF16, FP64 TC, 2:4 structured sparsity; 312 TFLOPS BF16 |
| 2022 | H100: FP8 (E4M3, E5M2), TMA, Transformer Engine; 1979 TFLOPS FP8 dense |
| 2023 | OCP MX v1.0 specification — block-scaled FP8/6/4 with shared E8M0 scale |
| 2024 | Blackwell B200: FP4 (E2M1) + NVFP4; 20 PF FP4 dense per package |
| 2024 | TPU v6e Trillium: MXU enlarged from 128×128 to 256×256 — first resize since 2017 |
| 2025 | TPU v7 Ironwood; AMD MI355X (FP4); Microsoft Maia 100 — block-scaled formats become default |
The throughline: spatial reuse via locally-connected PEs. Forty-eight years separate Kung's 1978 paper from the Blackwell datasheet — the slide deck has changed, the principle hasn't.
Kung's metaphor: blood through a heart. Data pumps through a regular array of locally-connected processing elements. Operands enter at the edges, results emerge after a fixed number of cycles, no element ever stalls waiting on global memory.
O(N) times before leaving. DRAM bandwidth requirements scale as N, not N² or N³.A 4×4 output-stationary mesh. A streams left→right (blue), B streams top→bottom (purple), accumulators stay put (green).
Used by every Google TPU (v1 onwards), AWS Trainium / Inferentia, and most "MXU-class" datacentre accelerators.
a_in to its eastern neighbour on every cycle.w_in to its southern neighbour.C[r,c] += A[r,k] * B[k,c] over K cycles — hence "output stationary".This repo's mmul_pe.sv is the canonical PE: one signed multiply, one signed add, three flops (a-skew, w-skew, accumulator). mmul_systolic_os.sv instances ROWS·COLS of them with edge wiring.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) { a_out, w_out, acc_out } <= '0;
else if (clear) { a_out, w_out, acc_out } <= '0;
else if (enable) begin
a_out <= a_in; // skew right
w_out <= w_in; // skew down
acc_out <= acc_out + acc_t'(a_in * w_in);
end
end
ROWS + COLS − 1 cycles before every PE is computing.K cycles producing R·C MACs each cycle.K + R + C − 2.The "fill bubble" is the cost. For 256×256 with K=64 (typical attention head), 511 cycles of fill+drain wrap 64 productive cycles — 87% bubble. Real TPU code amortises this by chaining many K-tiles back-to-back without draining.
Used by NVDLA, Apple Neural Engine, ARM Ethos-N, Hailo-8, Qualcomm Hexagon NPU, and most mobile / edge accelerators.
For inference, weights are fixed and reused across many activations (especially across a batch). So load weights once, hold them, stream activations. Partial sums spill to a separate buffer.
acc lives in PE; both operands stream. Best for training (activations and weights are equally hot). Used by TPU, Trainium.
B held in PE registers; A streams; partial sums move out per cycle. Best for inference where weights >> activations × batch. Used by edge NPUs.
This repo's mmul_weight_stationary.sv implements an M × N register-file held over K compute steps; a separate load_w handshake fills it from the off-chip weight stream.
Two more dataflows for completeness — both more specialised, both interesting.
Activations pinned, weights stream, partial sums flow. Less common in dense matmul, but appears in some training accelerators where the activation reuse pattern of ∂L/∂W = A^T · ∂L/∂Y dominates the gradient pass.
The most thoughtful of the four. Maps a 1-D row of CNN filter weights onto a single PE; ifmap rows stream horizontally; partial sums flow vertically. Sze and Chen showed this maximises all three reuse patterns simultaneously — weights, ifmaps, and psums.
The dataflow choice is a workload-dependent decision. Modern compilers (XLA for TPU, CUTLASS for NVIDIA, TT-Metalium for Tenstorrent) re-tile across multiple physical arrays so the user-facing kernel is dataflow-agnostic — but underneath, every implementation makes a choice.
NVIDIA's Tensor Core is conceptually not a Kung-style systolic. It's a parallel reduction tree: K multipliers feeding an adder tree, with operands fetched per warp from the register file.
D = A·B + C on 4×4 matrices.O(log K) adder-tree depth vs O(K + R + C) in a systolic. Latency matters for warp-level scheduling.mma.sync shapes like 16×8×16 — easy to compose at the warp level.This repo's mmul_dotproduct.sv is a single-cycle K-wide combinational dot product — the simplest model of one Tensor-Core lane. With K = 8 it computes a Q8.8 inner product into a 32-bit accumulator combinationally.
| Gen | GPU | New formats | Peak (per GPU, dense) |
|---|---|---|---|
| 1st | V100 (2017) | FP16 | 125 TFLOPS |
| 2nd | T4 / RTX 20 (2018) | INT8, INT4 | 130 TOPS INT8 |
| 3rd | A100 (2020) | TF32, BF16, FP64, 2:4 sparsity | 312 TFLOPS BF16 |
| 4th | H100 (2022) | FP8 (E4M3, E5M2), TMA | 1979 TFLOPS FP8 |
| 5th | B200 (2024) | FP4, NVFP4, MXFP | 20 PFLOPS FP4 |
Almost all of it. A typical decoder-only LLM forward pass spends ≥99% of its FLOPs in dense matrix multiplications — so the MMUL is the part of the chip that decides how fast a Llama or GPT runs.
| Operation | Shape | FLOPs | On the MMUL? | Comment |
|---|---|---|---|---|
| Token embedding | S × V → S × D | — | No (gather) | Indexed table lookup; some chips fuse as one-hot · W |
| RMSNorm / LayerNorm | S × D | O(S·D) | No | Reductions + elementwise; vector lane |
| Q projection | S × D · D × D | 2·S·D² | Yes | Dense GEMM |
| K projection | S × D · D × D | 2·S·D² | Yes | Dense GEMM (smaller for GQA / MQA) |
| V projection | S × D · D × D | 2·S·D² | Yes | Dense GEMM |
| Q · KT (scores) | S × d · d × S (per head) | 2·S²·D | Yes | "Batched" GEMM, one per head; quadratic in S |
| scale + softmax | S × S | O(S²) | No | Subtract max, exp, normalise — vector lane / SFU |
| scores · V | S × S · S × d | 2·S²·D | Yes | Batched GEMM |
| Output projection WO | S × D · D × D | 2·S·D² | Yes | Dense GEMM |
| FFN up + gate (SwiGLU) | S × D · D × 4D (×2) | 16·S·D² | Yes | Two GEMMs; the largest in the layer |
| SiLU / GeLU | S × 4D | O(S·D) | No | Elementwise; SFU |
| FFN down | S × 4D · 4D × D | 8·S·D² | Yes | Dense GEMM |
| Residual add | S × D | O(S·D) | No | Elementwise add |
| RoPE rotation | S × D | O(S·D) | No | 2-element rotations on Q,K (vector lane) |
| LM head logits | S × D · D × V | 2·S·D·V | Yes | Final dense GEMM (often largest matrix in the model) |
S = 1024+ tokens at once. GEMM shapes (M=N=K) are large; AI is high; MMUL utilisation 50–85%. This is what sales decks measure.
S = 1. Every projection is a tall-skinny GEMM (1 × D · D × D); arithmetic intensity drops to ~D / 2 ≈ 4096 / 2 ≈ 2000, but the actual constraint is HBM bandwidth for weights and KV. MMUL utilisation often < 5%. This is why "tokens / sec" benchmarks look so different from peak TFLOPS.
For training, the MMUL is genuinely the bottleneck — and chasing FP4 / FP8 / NVFP4 is the right thing. For LLM inference at batch=1 you can put a 20-PFLOP B200 next to a 1-PFLOP MI300X and they will both be limited by HBM bandwidth — which is why memory bandwidth (not peak TFLOPS) drives every modern inference card's spec sheet.
Halving the bit-width of the multiplier is approximately 4× fewer transistors (multiplier area is quadratic in mantissa width) and ~2× lower energy per MAC. Every generation has chased this curve.
| Format | Bits | S/E/M | Range (≈) | Used in |
|---|---|---|---|---|
| FP32 (IEEE 754) | 32 | 1/8/23 | ±3.4e38 | Baseline, accumulators everywhere |
| TF32 (NVIDIA) | 19 stored as 32 | 1/8/10 | same as FP32 | A100+ TC, FP32 in/out |
| FP16 (IEEE binary16) | 16 | 1/5/10 | ±65,504 | V100 onwards |
| BF16 | 16 | 1/8/7 | ±3.4e38 | TPU v2+, all modern; "FP32 truncated" |
| FP8 E4M3 | 8 | 1/4/3 | ±448 | H100, MI300X — fwd weights/activations |
| FP8 E5M2 | 8 | 1/5/2 | ±57,344 | H100, MI300X — gradients |
| FP6 E3M2 | 6 | 1/3/2 | ±28 | OCP MX, Blackwell |
| FP4 E2M1 | 4 | 1/2/1 | ±6 | OCP MX, Blackwell, MI355X |
| INT8 / INT4 | 8 / 4 | — | ±127 / ±7 | TPU v1, NPUs, quantised inference |
Below FP8, the dynamic range of any single element is too small to cover a full tensor. The industry's answer: group elements into a small block, give each block a shared exponent.
k = 32 elements.E8M0 — 8-bit unsigned biased exponent only, no sign or mantissa. Value = 2scale − 127.k = 16 (vs OCP's 32) — finer-grained scaling.FP8 E4M3 — mantissa bits in the scale itself, much better SNR near the boundary.An MXFP MAC is not just a small multiplier. The scale must be fanned out across the block on every cycle and re-applied to the partial sum. Blackwell adds a dedicated scale broadcast network running in parallel with the main dot-product fabric — silicon area you would not see in a pre-2023 design.
Round-to-nearest applied many times is biased. Adding 1.0 + 0.49 a million times in FP4 gives 1.0, not 491,490. In low-precision training, this kills convergence.
Keep the multiplier narrow, keep the accumulator wide:
Without this, every halving of bit-width would silently halve model quality. With it, the chip behaves numerically like a much wider design at a fraction of the area.
| Device | Year | Process | MXU shape | Datatype | Peak / chip |
|---|---|---|---|---|---|
| TPU v1 | 2015 | 28 nm | 256×256 | INT8 mul / INT32 acc | 92 TOPS @ 700 MHz |
| TPU v2 | 2017 | — | 128×128 (×2 cores) | BF16 mul / FP32 acc | ~46 TFLOPS BF16 |
| TPU v3 | 2018 | — | 128×128 (×2) | BF16 | ~123 TFLOPS |
| TPU v4 | 2021 | 7 nm | 4× 128×128 | BF16, INT8 | 275 TFLOPS BF16, OCS interconnect |
| TPU v5p | 2023 | 5 nm | 128×128 | BF16, INT8 | ~459 TFLOPS BF16 |
| TPU v6e Trillium | 2024 | 4 nm | 256×256 | BF16, INT8 | ~926 TFLOPS BF16 |
| TPU v7 Ironwood | 2025 | — | — | FP8 | ~4.6 PFLOPS FP8 |
"H100 does 989 TFLOPS" is the FP16/BF16 figure. The FP8 dense figure is 1979 TFLOPS. Many tech press articles cite the BF16 figure when comparing to TPU/AMD FP8 numbers — apples vs oranges.
| Device | Topology | Datatype | Peak | On-chip mem |
|---|---|---|---|---|
| Cerebras WSE-3 | 900K cores, full 300 mm wafer | FP16, BF16, FP8 | 125 PF (sparse FP16) | 44 GB SRAM, 21 PB/s |
| Groq LPU | Functional slices, deterministic | FP16, INT8 | 188 TF FP16 / 750 TOPS INT8 | 230 MB SRAM, no DRAM |
| Tesla Dojo D1 | 354 nodes/chip × 25-chip tile | FP32, BF16, CFloat8 | 9 PF BF16 / training tile | 11 GB SRAM/tile |
| AWS Trainium2 | 128×128 systolic Tensor Engine | BF16, FP8 | ~667 TFLOPS BF16 | 96 GB HBM3e |
| Apple M4 ANE | 16-core, weight-stationary | FP16 native | 38 TOPS (= FP16 × 2 by convention) | shared with SoC |
| Intel AMX | 8 tile-regs per core | BF16, INT8 (+FP16 on Granite Rapids) | ~1024 INT8 MACs/cyc/core | per-core |
| ARM SME / SME2 | Streaming-SVE ZA tile | FP32, BF16, FP16, INT8 (+2:4 in 2024) | VL-dependent | per-core |
| Tenstorrent Blackhole | 140 Tensix++ cores | FP8/16, BFP2/4/8 | 774 TF FP8 | 210 MB SRAM |
| Etched Sohu | Hardwired transformer dataflow | FP8 (vendor-claimed) | 500K tok/s Llama-70B (claim) | 144 GB HBM3e |
An MMUL is a giant operand-consumer. A 256×256 BF16 array at 1 GHz needs 256 KB/cycle of A operands and 256 KB/cycle of B. That's ~256 TB/s of pure operand bandwidth. No HBM stack delivers that — it has to come from on-die SRAM.
| Level | Capacity | BW | Latency | Energy / 32-bit access |
|---|---|---|---|---|
| PE register / accumulator | tens of bytes | 1 PE/cyc | 0 cyc | ~0.1 pJ |
| Scratchpad SRAM (TMEM, CMEM, GLB) | 0.1 – 1 MB | 1 op/cyc/bank | 1–4 cyc | ~1 pJ |
| L2 / Unified Buffer | 10s of MB (50 MB on H100) | ~5–10 TB/s | 20–60 cyc | ~5 pJ |
| HBM3 / HBM3e | tens to hundreds of GB | 2 – 8 TB/s | 200–500 cyc | ~7 pJ/bit (~28 pJ/word) |
| NVLink / OCS / NeuronLink | off-package | ~900 GB/s (NVL5) / per direction | ~µs | much higher |
| Off-rack network (Ethernet/IB) | cluster-wide | 200–800 Gbps/link | tens of µs | highest |
Short answer: not in the CPU sense — and modern accelerators have largely abandoned them in favour of explicitly-managed scratchpads.
| Chip | Cache? | What it actually is |
|---|---|---|
| NVIDIA H100 | 50 MB L2 ✓ | True coherent cache — needed because GPU runs general CUDA code, not only matmul |
| NVIDIA B200 | L2 + TMEM ✓ | L2 stays; TMEM is a scratchpad, software-managed |
| Google TPU v1–v6 | No cache | Unified Buffer + CMEM, both software-managed scratchpads |
| Cerebras WSE-3 | No cache | 44 GB on-die SRAM, all explicitly addressed |
| Groq LPU | No cache | 230 MB SRAM, deterministic compiler-scheduled access |
| Apple ANE / ARM Ethos-N | No cache | Tile buffers + DMA |
The H100's 50 MB L2 isn't really there to serve the Tensor Core MMUL — it's there to serve the rest of the SM (CUDA cores, texture units, atomics, indirect memory accesses). On dedicated AI chips that don't run general code, the L2 budget gets reallocated as larger user-visible scratchpad.
Putting an MMUL into a CPU socket (Intel AMX, ARM SME) inherits a CPU's cache hierarchy whether or not it's optimal. Intel AMX tile loads go through L1d/L2/L3 — convenient for programmers, less efficient than a dedicated tile RF would be. AMX is roughly half as energy-efficient per FLOP as a dedicated NPU at the same node, and the cache hierarchy is the main reason.
The peak number on the datasheet is the multiplier count × clock frequency × ops per multiplier × 2 (for FMA). Real workloads achieve 30–70% of that.
| Workload | Hardware | Peak | Sustained | % |
|---|---|---|---|---|
| GPT-3 175B training (BF16) | A100 | 312 TFLOPS | ~150–180 TFLOPS | 50–58% |
| Llama-70B training (BF16) | H100 | 989 TFLOPS | ~400–500 TFLOPS | 40–50% |
| Llama-70B training (FP8) | H100 | 1979 TFLOPS | ~700–900 TFLOPS | 35–45% |
| Llama-70B inference (single-batch) | H100 | 1979 TFLOPS | ~10–30 TFLOPS | memory-bound |
| GEMM (M=N=K=8192) BF16 | H100 | 989 TFLOPS | ~830 TFLOPS | ~85% |
The 5× gap between "MoEFLOPS marketing slide" and "actual throughput on your model" is not bad design — it's the cost of running real workloads on real memory hierarchies. Compiler engineering closes most of it.
| Format | Multiplier energy (45 nm baseline) | Roughly at TSMC 4N |
|---|---|---|
| FP32 MAC | ~4.6 pJ | ~0.9 pJ |
| BF16 MAC | ~1.5 pJ | ~0.3 pJ |
| FP8 MAC | ~0.5 pJ | ~0.1 pJ |
| FP4 MAC | ~0.15 pJ | ~0.03 pJ |
| HBM3 access (per 32-bit word) | ~28 pJ | ~28 pJ (PHY-dominated, weak process scaling) |
Two things to notice: (1) HBM access does not scale with process node — it's PHY/wire-dominated. So as compute energy per MAC drops, the relative cost of operand movement becomes higher. (2) The whole point of MX/FP4 is to get the multiplier energy below the operand-movement energy that flanks it.
Above ~700 W per package, air cooling gives up. Hence the industry-wide pivot to liquid-cooled cabinets in 2024–2026.
A modern MMUL package consumes more current than an early-2000s consumer CPU's whole motherboard. The interesting engineering isn't in the array — it's in getting the joules in and the heat out.
| Cooling | Ceiling | Used by |
|---|---|---|
| Forced air | ~400 W package | RTX 6000 Ada workstation, edge boxes |
| Direct-to-chip liquid | ~1500 W package | H100/H200 SXM, B200, TPU v4/v5/v6 |
| Two-phase liquid (immersion-style) | 2 kW+ package | some hyperscale 2025 deployments |
| Full immersion | 3 kW+, rack-density gain | experimental, Microsoft Azure pilots |
A 256×256 array at FP8 dissipates roughly 100 W in ~50 mm² of silicon — that's a flux of ~200 W/cm², several times higher than a kitchen hotplate. Most of the silicon area in a Tensor-Core SM is not multipliers; it's the operand-fan-in network, the result-spill paths, and the local SRAM banks that surround them — partly because this spreads the heat out so hotspot temperatures stay below the 105 °C electromigration limit.
At 4 nm and below, instantaneous power density (W/cm²) — not average TDP — is the design constraint. A 5th-gen Tensor Core's clock-gating scheme is as carefully tuned as its multiplier topology, because letting the array run at 100% utilisation for too long simply melts the metal stack above it.
Choose an array geometry and see the resulting cycle count and operand bandwidth for each dataflow.
Notice how the OS array's utilisation collapses for small K (fill bubble dominates) and the dot-product tree wins on latency but loses on per-cycle bandwidth. Real chips combine both — TPUs use OS-systolic for the MXU, NVIDIA TCs use trees, and both schedule many K-tiles back-to-back to hide the bubble.
Pick a format and a real number; see what gets stored.
Try 1e-4 in FP4 (you'll round to zero) versus BF16 (full precision). Try 50000 in FP8 E4M3 (saturates to ±448) versus FP8 E5M2 (representable). This is exactly why H100 pairs E4M3 (small range, more precision — for forward) with E5M2 (large range — for gradients).
The MMUL is the most architecturally settled part of an AI chip. The interesting work in 2025–2027 is not "what shape is the array" — that's been answered — but everything around it: how operands arrive, how partial sums leave, how the joules get in, and how the heat gets out.