Architectures · Chapter 8 of 16 · AI accelerators

Systolic arrays

A systolic array is a grid of math units that pass numbers to their neighbors in a steady rhythm, like a bucket brigade. Each number gets used many times without another trip to memory.

Data flows through a grid of multiply-accumulate units. A weight-stationary array keeps the weights in place and streams inputs past them; an output-stationary array keeps the running sums in place. Reuse inside the array cuts memory traffic.

The dataflow taxonomy (weight-, output-, input- and row-stationary), utilization and fill-and-drain overhead, mapping matrix multiplication and convolution onto an array, and published TPU designs as case studies.

Running an AI model is mostly one small job done billions of times: multiply two numbers, then add the answer to a running total. The workload chapter showed why.

The math itself is cheap. The costly part is fetching the numbers from memory. One fetch can use hundreds of times more energy than the math done with it.

A is built to fetch less. It is a grid of tiny calculators. A chip has a clock that ticks billions of times a second to keep every part in step. On each tick, every calculator does one multiply-and-add and passes its numbers to the next one. So a number fetched once gets used by every calculator it passes.

The name comes from the heart. “Systole” is the squeeze that pumps your blood, and data pulses through the grid in the same steady beat. The idea is from 1978. It became famous again when Google built its AI chip, the TPU, around a giant one.

Neural networks spend most of their time in : C=A×BC = A \times B, where every output is a sum of products. Each product-and-add is one (MAC). Done naively, a MAC needs three values read from memory (a weight, an input and the running sum) and one written back. Reading from off-chip DRAM costs about 200 times the energy of the MAC itself, so memory traffic, not arithmetic, sets the cost.

A is a grid of identical (PEs), each a MAC unit with a few registers, wired only to their nearest neighbors. Every clock cycle, each PE does one MAC and passes its values one step along. Data that enters at the edge is reused by every PE it passes, so the array does many operations per memory access. The term and the first designs come from H. T. Kung and Charles Leiserson at Carnegie-Mellon in 1978. The first TPU used one for exactly this reason: “reading a large SRAM uses much more power than arithmetic”.

This chapter covers:

  • How the bucket brigade computes a matrix product, and why the inputs enter in a staircase.
  • The main design choice: which data stays put in each PE (weights, outputs, inputs or rows).
  • How large matrices and convolutions are cut up and fed to a fixed-size array.
  • Utilization: why arrays are often far less busy than their peak numbers suggest.
  • Published designs as case studies: Google’s TPUs, Tesla’s self-driving chip, MIT’s Eyeriss and the open-source Gemmini.

For the other big answer to the same problem, many small cores running the same instructions, see SIMT and GPUs; for grids of whole cores rather than single MACs, see Dataflow and spatial meshes.

You know a neural network is mostly . A systolic array is the most direct hardware for it: an R×CR \times C mesh of MAC with registered nearest-neighbor links, fed from on-chip buffers at two edges. Kung’s 1982 argument still frames the design: a GEMM is compute-bound (O(n3)O(n^3) operations on O(n2)O(n^2) data), so a structure that uses each fetched value many times can raise throughput without raising memory bandwidth.

The details that decide whether a given array is any good are:

  • Dataflow: which operand is stationary in the PE (weights, outputs, inputs, or Eyeriss’s row-stationary mix), which sets what each level of memory is read for.
  • Mapping: how a GEMM’s MM, KK and NN, or a convolution lowered to a GEMM, are assigned to rows, columns and time, and how many folds that takes.
  • Utilization: fill and drain (about 2R+C−22R + C - 2 cycles per fold), fragmentation, weight-load stalls and memory-bound layers. TPU v1 averaged 23% of peak MACs doing useful work across six production workloads.

This chapter puts numbers on each, using published TPU, Tesla FSD, Eyeriss and Gemmini data, and derives the runtime, utilization, traffic and energy models in “Under the hood”.

memorySRAM / DRAM×+×+×+×+×+×+×+×+×+×+×+×+×+×+×+×+memory readsper cycle32for 16 MACs
Feeding

No reuse: every MAC reads two operands from memory, 32 reads per cycle for 16 MACs. Memory bandwidth, not the multipliers, sets the speed.

A 4 × 4 array doing 16 MACs per cycle, fed two ways. Read counts are simple arithmetic for this example.Share freely with credit: ‘Figure from chipfieldguide.com’

Start with one calculator and one memory. For every multiplication, the calculator has to fetch two numbers. Memory can only hand them over so fast, so the calculator spends most of its time waiting.

Now line up six calculators and pass each number down the line. Memory works just as hard, but six times as much math gets done. That is the whole trick.

A real systolic array is a square grid. Each cell holds one weight, a number the model learned in training. Other numbers flow in from the left, one cell per tick. Each cell multiplies, adds the result to a running total coming from the cell above, and passes the total down. Finished answers drop out the bottom.

There is one catch. A total takes a tick to move down one row. So each row starts one tick later than the row above it, and the busy cells form a slanted wave across the grid.

One PE, then many

Kung’s 1982 paper opens with a back-of-envelope case that still explains the design:

  • Memory can move 10 million bytes per second to and from the processor.
  • Each operation needs at least two bytes read or written.
  • So one PE that fetches its operands from memory can do at most 5 million operations per second, however fast it is.
  • Replace it with a chain of six PEs that pass each value along and use it at every stop: the same memory now supports 30 million operations per second.

This only works when the job uses each value many times. Kung called those jobs compute-bound: matrix multiplication qualifies, because every entry of one matrix meets every entry in a row or column of the other. Adding two matrices doesn’t, since each value is used once. In today’s terms, matrix multiplication has high .

A 2 × 2 example

Take A=[1234]A = \left[\begin{smallmatrix} 1 & 2 \\ 3 & 4 \end{smallmatrix}\right] (two rows of ) and B=[5678]B = \left[\begin{smallmatrix} 5 & 6 \\ 7 & 8 \end{smallmatrix}\right] (the weights). In a 2 × 2 array, PE (row kk, column jj) stores weight B[k][j]B[k][j]. Row kk of the array receives column kk of AA from the left. Each PE multiplies the arriving activation by its weight, adds the arriving from above, and passes the activation right and the new sum down.

CycleWhat happensLeaves the bottom
0PE(0,0): 1×5=51 \times 5 = 5, sum sent down—
1PE(1,0): 5+2×7=195 + 2 \times 7 = 19. PE(0,1): 1×6=61 \times 6 = 6. PE(0,0): 3×5=153 \times 5 = 15C[0][0]=19C[0][0] = 19
2PE(1,1): 6+2×8=226 + 2 \times 8 = 22. PE(1,0): 15+4×7=4315 + 4 \times 7 = 43. PE(0,1): 3×6=183 \times 6 = 18C[0][1]=22C[0][1] = 22, C[1][0]=43C[1][0] = 43
3PE(1,1): 18+4×8=5018 + 4 \times 8 = 50C[1][1]=50C[1][1] = 50

Two details make it work. First, the value 2 (A[0][1]A[0][1]) enters row 1 at cycle 1, one cycle after the 1 entered row 0, so it reaches PE(1,0) at the same moment as the partial sum 5. That deliberate delay is the . Second, every value moves through a register (a ) between PEs, so no wire is longer than one PE and the clock can be fast. Apart from power, the clock is the only signal that reaches every PE.

Each weight here was read once and used twice; each activation was read once and used twice. In a 256 × 256 array the same structure reuses each activation 256 times as it crosses a row. The first TPU’s matrix unit works this way: data flows in from the left, weights are preloaded from the top, and each multiply moves through the array “as a diagonal wavefront”. Software never sees the systolic timing; it just sees a unit that multiplies a block of inputs by a block of weights.

The PE and its timing

In the weight-stationary form used by TPU matrix units, PE(k,jk, j) holds w=B[k][j]w = B[k][j] and on every cycle computes

aout←ain,pout←pin+ain⋅wa_{\mathrm{out}} \leftarrow a_{\mathrm{in}}, \qquad p_{\mathrm{out}} \leftarrow p_{\mathrm{in}} + a_{\mathrm{in}} \cdot w

with aouta_{\mathrm{out}} registered to the right neighbor and poutp_{\mathrm{out}} to the one below. Activation A[m][k]A[m][k] enters row kk at cycle m+km + k (the ) and reaches column jj at m+k+jm + k + j. The partial sum for output (m,j)(m, j) is at row kk at the same cycle m+k+jm + k + j, so the operands always coincide, and C[m][j]C[m][j] leaves the bottom of column jj at cycle m+(R−1)+jm + (R - 1) + j. The outputs leave skewed and are de-skewed into .

Properties that follow, and that Kung listed as the criteria for a good systolic design:

  • Each input is used many times (CC times per activation, MM times per weight).
  • Extensive concurrency: all R⋅CR \cdot C PEs can work at once in steady state.
  • Only a few kinds of simple cells.
  • Simple, regular data and control flow: every wire is nearest-neighbor, so wire length doesn’t grow with array size; the only global signal is the clock.

Kung contrasted these “pure” systolic arrays with semi-systolic ones that broadcast an input to all cells or sum results through a fan-in adder tree. Broadcast and fan-in also achieve reuse, but their wires grow with the array and are hard to scale without slowing the clock. The original Kung–Leiserson matrix multiplier was a hexagonally connected array in which all three matrices moved; today’s accelerators use the rectangular, one-operand-stationary form.

TPU v1 as the reference implementation

TPU v1’s matrix unit is 256 × 256 8-bit MACs. Products (16-bit) go to 4 MiB of 32-bit accumulators below the unit, organized as 4096 vectors of 256; the unit produces one 256-element partial sum per cycle. Activations come from a 24 MiB on-chip Unified Buffer; weights come from off-chip DRAM through a Weight FIFO four tiles deep. The unit holds one 64 KiB weight tile plus a second for double buffering, “to hide the 256 cycles it takes to shift a tile in”. Mixed 8/16-bit operands run at half speed and 16/16 at a quarter.

memory: 10 MB/sPE1ThroughputMOPS51 PE: 56 PEs: 30

One PE: 10 MB/s ÷ 2 bytes per operation caps it at 5 million operations per second, however fast it is.

Kung’s 1982 example: memory moves 10 MB/s and each operation needs 2 bytes. Six PEs give 30 MOPS; other lengths follow the same arithmetic.Share freely with credit: ‘Figure from chipfieldguide.com’
Before cycle 0row 013row 124w50,0w60,1w71,0w81,1out (C)correct19, 4322, 50
Input skew
1 / 5

Before cycle 0: weights are loaded; row 1’s activations are held back one cycle (the skew).

The text’s 2 × 2 weight-stationary example. Activations (blue) move right, partial sums (pink) move down, one PE per cycle. Turn off the skew to see why it’s needed.Share freely with credit: ‘Figure from chipfieldguide.com’

Every multiply-and-add uses three kinds of numbers. There are the weights, which never change. There are the inputs, which are new every time. And there are the running totals, which grow until each answer is done.

In a grid, one kind can sit still in each cell while the others flow past. In a grid, each cell keeps a weight. Google’s TPU works this way. In a grid, each cell keeps its own running total.

Neither one always wins. It depends on the shape of the job. You can try both in the simulation below.

The pattern of which data stays and which moves is called the dataflow. MIT’s Eyeriss team classified published accelerators by it, and the names stuck.

DataflowStays in each PEMovesSaves
(WS)a weightactivations in, partial sums outweight reads
(OS)one output’s running sumactivations and weights inpartial-sum reads and writes
Input-stationary (IS)an activationweights in, partial sums outactivation reads
(RS)a filter row, part of an input row and a partial suma mix of all threeall three together
No local reuse (NLR)nothingeverything, via a large shared bufferPE area, spent on buffer instead

Why the choice matters: on-chip memory comes in levels, and smaller levels are cheaper to read. Eyeriss measured the energy of one access, relative to one MAC, in a 65 nm process:

Where the value comes fromTypical sizeEnergy per access (MAC = 1)
Off-chip DRAMgigabytes200×
On-chip global buffermore than 100 kB6×
A neighboring PE—2×
The PE’s own small register file0.5 kB1×

A dataflow decides which data gets read from which level, and how often. In a WS array each weight is read from the buffer once per tile and then used from the PE’s own register for every activation that passes. In an OS array the running sum lives in the PE, so no partial sum is ever written out and read back.

The idea predates neural networks. Kung’s 1982 paper laid out a whole family of systolic designs for one convolution problem: some where the weights stay, some where the results stay, with inputs and results moving in the same or opposite directions at the same or different speeds. Eyeriss itself introduced row-stationary, which tries to minimize the energy of all three data types at once, and reported it 1.4 to 2.5 times more energy-efficient than the others on the convolution layers of AlexNet.

Definitions, precisely

  • WS: each weight stays in a PE’s register file to maximize convolutional and filter reuse; the PE runs every MAC that uses that weight before it is replaced. Partial sums are reduced across PEs and, if not finished, spilled to the global buffer, so buffer capacity can limit how many filters are in flight.
  • OS: each output accumulates in one PE. Eyeriss distinguishes variants by which outputs are computed together: one output channel with many pixels (for convolution layers), many channels with many pixels, or many channels with one pixel (for fully connected layers).
  • IS: the mirror of WS for the activation operand. SCALE-Sim models OS, WS and IS on a systolic array and finds WS beats IS when a layer has more output pixels than weights, and the reverse otherwise, because loading the stationary matrix costs cycles with no compute; the less often it must be reloaded, the better.
  • NLR: no register file in the PE; all area goes to a larger global buffer. It has the lowest DRAM energy but pays buffer-level energy for nearly every access.
  • RS: each PE performs a 1-D convolution of a filter row with an input row, keeping the row of weights, a window of activations and one partial sum in its 0.5 kB register file; rows of PEs then sum their outputs vertically for the 2-D convolution.

Under the same area and 256 PEs, Eyeriss’s analysis found WS lowest for weight energy and OS lowest for partial-sum energy, but RS lowest in total, because it optimizes all three data types. In fully connected layers there is little reuse and every dataflow spends a significant amount of its energy reading weights.

Two observations from the original systolic work still guide the choice. When there are more weights than cells, a design where partial results stay generally needs less I/O than one where they move. And moving partial results needs wider paths than moving weights, because results carry more bits for numerical accuracy. In modern terms: WS ships 32-bit sums of 8-bit products between PEs; OS ships only the 8-bit operands.

SCALE-Sim’s authors conclude that while hyper-parameters and array size affect which dataflow is best, fixing one usually costs little, so the cost of supporting several should be weighed carefully. Gemmini, an open-source generator, makes it a configuration option: its PEs run either WS or OS. At the extreme, analog crossbars that store weights as conductances are “the ultimate form of a weight stationary dataflow”, covered in Sparsity, in-memory and analog compute.

activations →partial sums ↓weights loaded oncewwwwwwwwwStationarywweightsSavesweight reads
Dataflow

Weight-stationary: each weight is read once per tile and reused for every activation that passes. Partial sums move down to the accumulators.

The dataflow names which data is stationary in each PE (badges) and which moves between PEs (lines). Schematic, after Eyeriss and SCALE-Sim.Share freely with credit: ‘Figure from chipfieldguide.com’

Below is a small grid of 16 cells doing a multiplication job. Press Play, or Step to go one tick at a time. Watch the numbers march in from the left in a staircase and the answers drop out the bottom.

Then switch between “Weights stay” and “Sums stay”. What stays put in each cell, and when do the answers start to come out?

The array is 4 × 4 and computes C=A×BC = A \times B with AA of size M×4M \times 4 and BB of size 4×44 \times 4, using small whole numbers so you can check the sums. It starts weight-stationary. Things to try:

  • Step through the first cycles and find the diagonal wavefront. Why does row 3 start three cycles late?
  • With 4 rows streamed, what is the peak number of busy PEs? Raise the rows streamed to 7 and look again.
  • Raise the rows streamed from 4 to 16. How do the total cycles and the utilization change in each dataflow, and why does output-stationary not improve?
  • Compare buffer reads with the “no reuse” count of two reads per MAC.

The Expert view adds the array size nn (2–8), the reduction depth KK (1–24) and up to 24 rows of AA; BB is K×nK \times n. The cycle count follows the SCALE-Sim model exactly: folds×(3n+T−2)\text{folds} \times (3n + T - 2), with T=MT = M for WS (folds over KK) and T=KT = K for OS (folds over MM), and no overlap between folds. Try:

  • M=16M = 16, K=4K = 4 versus M=4M = 4, K=16K = 16 in each dataflow. Which dimension does each one amortize fill and drain over?
  • Read the bar chart: where are the load, fill and drain ramps, and how wide is the flat top?
  • Compare “buffer reads” with “minimum reads”. When does each dataflow re-read an operand, and which one?
  • At n=8n = 8, how large must MM be before WS utilization passes 50%?

Buffer reads count every operand entering the array edge; results written count every vector leaving it, including WS partial sums sent to the accumulators between KK-folds. Memory never stalls in this model.

Loading simulation…

A real AI model multiplies tables of numbers with thousands of rows and columns. That is far bigger than any grid. So the job is cut into grid-sized pieces, like covering a big floor with square tiles. The pieces run one after another, and their results are added up at the end.

The grid also needs a steady supply of numbers. So chips like this put a lot of fast memory right beside it, often more than the grid itself.

Tiling

A matrix larger than the array is split into array-sized blocks, a step called (or folding). For a weight-stationary array:

  1. Load one array-sized tile of weights into the PEs.
  2. Stream every row of activations through it. The array produces partial sums.
  3. Add those partial sums into next to the array. TPU v1 has 4 MiB of 32-bit accumulators for this.
  4. Load the next weight tile and repeat until every tile is done.

Loading a tile takes time: shifting a 256 × 256 tile into TPU v1’s matrix unit takes 256 cycles. To hide it, the unit holds a second tile and loads the next one while the current one computes (double buffering).

Convolutions become matrix multiplications

A slides small filters over an image. It can be rewritten (“lowered”) as one matrix multiplication by building a matrix whose columns are the image patches each output needs, a step usually called . In cuDNN’s small example, 3 channels of a 3 × 3 image and two 2 × 2 filters become a 2 × 12 filter matrix times a 12 × 4 patch matrix. The cost: each input value is copied up to 4 times (filter height × width), which takes memory and extra traffic.

Accelerators often build the patch matrix on the fly instead of storing it. Gemmini’s optional im2col unit does this beside the array; without it, the host CPU has to do the rearranging, and the choice of CPU then matters a lot to overall speed.

Memory to feed it

The array only stays busy if operands arrive every cycle. TPU v1’s matrix unit reads and writes 256 values per cycle, fed from a 24 MiB Unified Buffer that takes almost a third of the die; the matrix unit is about a quarter, and control just 2%. In a Gemmini configuration with a 16 × 16 array, the array is 11.3% of the area and its 256 KB 52.9%. Getting data to these buffers from off-chip memory fast enough is the subject of The memory wall.

Mapping GEMM dimensions

SCALE-Sim describes any dataflow as a projection of the GEMM C(M×N)=A(M×K)⋅B(K×N)C(M \times N) = A(M \times K) \cdot B(K \times N) onto two spatial dimensions SRS_R, SCS_C and one temporal dimension TT. For OS, MM maps to SRS_R, NN to SCS_C and KK to TT: each PE accumulates over KK in time. WS and IS keep one operand in space and stream the other, so the dimension that maps to time is the one not held by the stationary operand. In this chapter’s sim, WS holds BB (KK on rows, NN on columns) and streams the MM rows of AA; OS holds CC and streams KK.

On an R×CR \times C array the mapping needs F=⌈SR/R⌉⋅⌈SC/C⌉F = \lceil S_R/R \rceil \cdot \lceil S_C/C \rceil folds. Which dimension is folded decides which data is re-read: a WS array folding over KK sends partial sums to the accumulators after every fold and reads the activations once per column tile; an OS array folding over MM re-reads all of BB for every row tile.

Convolution dimensions

For a layer with batch NN, CC input channels, KK filters of R×SR \times S, and P×QP \times Q outputs, lowering gives a filter matrix K×CRSK \times CRS and a data matrix CRS×NPQCRS \times NPQ, with each input duplicated up to RSRS times. In GEMM terms the reduction depth is C⋅R⋅SC \cdot R \cdot S, the output columns are the KK filters and the streamed dimension is N⋅P⋅QN \cdot P \cdot Q. Three consequences:

  • Early layers tend to have few channels but large filters; late layers have many channels but small filters. The product CRSCRS, the reduction depth, is usually fairly large either way, which is why lowering performs consistently.
  • Layers with fewer output channels than the array has columns leave columns idle. TPU v1 measured that on active cycles of one CNN only about half the 65,536 MACs held useful weights “because some layers in CNN1 have shallow feature depths”.
  • Depthwise convolution (one filter per channel) has a reduction of only R⋅SR \cdot S, typically 9, so reuse is low; Gemmini reports MobileNetV2 maps poorly to spatial arrays for exactly this reason.

Accumulator sizing and precision

TPU v1 sized its accumulators from the roofline: about 1350 operations per weight byte are needed to reach peak, rounded up to 2048 accumulator vectors, then doubled to 4096 so the compiler could double-buffer. TPU v2 and v3 use a 128 × 128 array with “streaming LHS and results” and a “stationary RHS”, bfloat16 multiplies and float32 accumulation. Tesla’s FSD accelerator accumulates 8-bit × 8-bit products into 30-bit accumulators. The rule behind all three: the accumulator must be wide enough for KK products without overflow or excessive rounding, and that width is what makes moving partial sums expensive. The number formats themselves are in Number formats.

weights B1234loadarrayA slice →emptyemptyaccumulatorsloadcompute12348 units
1 / 5
Double buffering

The weight matrix is 2 × 2 tiles of the array’s size. Each tile is loaded, used, and replaced; partial sums meet in the accumulators.

Tiling a weight matrix onto a weight-stationary array, with and without double buffering. Timeline lengths are illustrative (loading as long as computing).Share freely with credit: ‘Figure from chipfieldguide.com’
input, 3 channels1234567893 × 3 imagefilters: 2 × 2im2col →ch1ch2ch3out1124512451245out2235623562356out3457845784578out4568956895689patch matrix 12 × 4(2 × 12) · (12 × 4) → 2 × 4

Tap a pixel to highlight its copies in the patch matrix, or a column to highlight its window.

im2col on cuDNN’s small example: 3 channels of a 3 × 3 image, 2 × 2 filters. The 12 × 4 patch matrix times a 2 × 12 filter matrix gives the convolution.Share freely with credit: ‘Figure from chipfieldguide.com’
area, 100% acrossmatrix unit≈ ¼buffer≈ ⅓control 2%the rest
Design

TPU v1: the 24 MiB Unified Buffer is almost a third of the die, the matrix unit about a quarter, control just 2%.

Area shares as reported for TPU v1 and one Gemmini configuration. TPU v1 shares are drawn from the paper’s “almost a third” and “about a quarter”.Share freely with credit: ‘Figure from chipfieldguide.com’

The first TPU’s grid had 65,536 calculators. That sounds unstoppable, but a calculator only helps when it is busy. Three things leave them sitting idle.

Starting and finishing. Numbers march in one cell per tick, so the far cells wait at the start. The last answers also take a while to march out. On a long job this barely matters. On a short job it wastes a lot.

Bad fit. If a job is smaller than the grid, part of the grid has nothing to do.

Waiting. If numbers don’t arrive from memory in time, the whole grid pauses.

These add up. Across six of Google’s real jobs, only about 23% of the calculators were doing useful work, on average. That is less than one in four.

is the useful MACs done divided by the most the array could have done in the same time: number of PEs × cycles. It falls for four reasons.

Fill and drain

Because inputs are skewed, the far corner of the array starts work several cycles after the near corner, and the last outputs need several cycles to leave. These are the cycles. In the simulation’s default (4 × 4 weight-stationary, 4 rows streamed) the job is 64 MACs and takes 14 cycles, including 4 to load weights, so utilization is 64÷(16×14)≈29%64 \div (16 \times 14) \approx 29\%. Streaming 16 rows instead gives 256 MACs in 26 cycles, ≈62%\approx 62\%. The overhead is fixed per pass, so the longer the stream, the less it matters.

Fit

A matrix that doesn’t divide evenly into the array leaves the edge tiles partly empty. TPU v1’s authors gave an example: a 600 × 600 weight matrix needs 9 tiles on a 256 × 256 unit (18 µs). A 512 × 512 unit would need only 4 tiles, but each takes four times as long, for 32 µs: the bigger array is slower.

Waiting for weights

In TPU v1, weights came from off-chip DRAM. For the four workloads limited by memory bandwidth (two MLPs and two LSTMs), the matrix unit was active only 8–13% of cycles, and 44–62% of cycles were stalls waiting for weights.

Small batches

A weight-stationary array reuses each weight once per row streamed, and the rows come from the : the number of requests processed together. Bigger batches mean more reuse but longer waits. One TPU v1 workload had to answer within 7 ms at the 99th percentile, which caps the batch size every chip can use.

Array size trades these effects against reuse. For TPU v2 Google compared, at the same total FLOPS, one 256 × 256 array, four 128 × 128 arrays and sixteen 64 × 64 arrays. Four 128 × 128 arrays gave 1.6 times the utilization of one 256 × 256 array, with half the operations per operand; sixteen 64 × 64 arrays gained little more utilization (1.7×) for another halving of reuse. They chose 128 × 128.

The fixed cost per fold

For one fold on an R×CR \times C array SCALE-Sim gives τ=2R+C+T−2\tau = 2R + C + T - 2 for OS, WS and IS alike. In OS, the far PE receives its first operands at R+C−2R + C - 2, accumulates for TT cycles and the results drain in RR. In WS, loading the weights takes RR cycles (no skew needed, since nothing computes during the load), the first input reaches the last column after C−1C - 1, streaming takes TT and the reduction down the column takes R−1R - 1. The overhead 2R+C−22R + C - 2 is independent of TT, so for a full fold

U=TT+2R+C−2U = \frac{T}{T + 2R + C - 2}
ArrayT=128T = 128T=1024T = 1024T=4096T = 4096
128 × 12825%73%91%
256 × 25614%57%84%

(Computed from the formula; it assumes no overlap between folds, which double-buffered designs such as TPU v1 partly achieve.)

Fragmentation

With F=⌈SR/R⌉⌈SC/C⌉F = \lceil S_R/R \rceil \lceil S_C/C \rceil folds, the useful share of PE slots is SRSC/(FRC)S_R S_C / (F R C). For TPU v1’s 600 × 600 example: 360,000/(9×65,536)≈61%360{,}000 / (9 \times 65{,}536) \approx 61\% on 256 × 256 versus 360,000/(4×262,144)≈34%360{,}000 / (4 \times 262{,}144) \approx 34\% on 512 × 512, consistent with the reported 18 µs versus 32 µs. Fragmentation is worse in two dimensions than one, which the authors compared to internal fragmentation of large memory pages.

Measured: TPU v1 performance counters

Table 3 of the TPU paper splits the matrix unit’s time across six production workloads (MLP0 and 1, LSTM0 and 1, CNN0 and 1). Mean values: array active 28% of cycles, useful MACs 23% of peak, unused MACs on active cycles 5%, weight stalls 43% and weight shifting 12%. CNN0 reaches 78.2% useful MACs and 86 of 92 peak TOPS; CNN1 reaches only 22.5%, half its active MACs idle from shallow layers and 28.1% of cycles stalled on weights for its fully connected layers. Four of the six workloads sit on the bandwidth-limited slope of the TPU’s , whose ridge is at about 1350 operations per weight byte.

Mitigations, and what they cost

  • Overlap folds. Double-buffer weights so the next tile shifts in during compute (TPU v1). Costs a second weight register per PE.
  • Smaller, more numerous arrays. 128 × 128 instead of 256 × 256 (TPU v2), and more of them per core (two in TPU v3, four in TPU v4). Costs reuse per operand and so buffer bandwidth.
  • Bigger batches raise TT for WS, limited by latency targets and memory capacity.
  • Pick the dataflow by shape: WS amortizes overhead over MM, OS over KK. The sim makes the mirror symmetry visible.
  • Scale out. SCALE-Sim’s follow-up asks whether one big array or several partitions is faster for a given layer, and builds the runtime model above to search that space.
25%50%75%100%1612810244096rows streamed T (log) →utilization ↑128256One passfill, drain25% useful
Array

T = 128 rows streamed on a 128 × 128 array: 128 useful cycles out of 510, so utilization is 25%. Load, fill and drain cost 382 cycles every pass.

Utilization of one pass from SCALE-Sim’s runtime model: the overhead is fixed, so it matters less the longer the stream. Assumes no overlap between passes.Share freely with credit: ‘Figure from chipfieldguide.com’
256 × 2569 tiles · 61% useful9 × 2 µs = 18 µs512 × 5124 tiles · 34% useful4 × 8 µs = 32 µs

600 × 600 weights: 9 tiles on 256 × 256 (61% of slots useful), 4 on 512 × 512 (34%). The TPU v1 paper: 18 µs vs 32 µs, so the bigger array is slower.

Fragmentation: tiles at the edges are partly padding (hatched). The TPU v1 paper’s example is 600 × 600, where the 512 × 512 array is the slower one.Share freely with credit: ‘Figure from chipfieldguide.com’
TPU v1 matrix unit
256 × 256 MACs
TPU v1 useful MACs, mean of 6 workloads
23%
DRAM access energy vs. one MAC (65 nm)
≈ 200×
Utilization gain, 4×(128²) vs 1×(256²)
1.6×

Sources: the TPU v1 paper, Eyeriss and the TPU v2/v3 Hot Chips talk.

What these numbers mean:

  • 256 × 256 is 65,536 tiny calculators in one grid. On the first TPU, each one did a multiply-and-add 700 million times a second.
  • 23% is how much of that grid did useful work, on average, in real use. Much of the rest of the time it was waiting for numbers.
  • 200×: fetching a number from the main memory chips takes about 200 times the energy of one multiply-and-add. That is the cost the whole design tries to avoid.
  • 1.6×: Google later swapped one giant grid for four smaller ones. Together, the small grids stayed busy about 1.6 times as much.

TPU v1 matrix-unit counters

WorkloadMLP0MLP1LSTM0LSTM1CNN0CNN1
Array active cycles12.7%10.6%8.2%10.5%78.2%46.2%
Useful MACs (% of peak)12.5%9.4%8.2%6.3%78.2%22.5%
Weight stall cycles53.9%44.2%58.1%62.1%0.0%28.1%
Weight shift cycles15.9%13.4%15.8%17.1%0.0%7.0%
Delivered TOPS (92 peak)12.39.73.72.886.014.1

Reading the table:

  • The convolutional network CNN0 reuses each weight across many image positions, so weights never stall and the array runs at 78% of peak.
  • The MLPs and LSTMs use each weight only once per request in the batch, so the array spends most of its time waiting for weights from memory and delivers 3–13% of peak.
  • CNN1 shows the other losses: half the MACs idle on active cycles because some layers are too shallow for 256 columns, plus weight stalls in its fully connected layers.

Even so, the paper reports the TPU 15 to 30 times faster on these workloads than the CPU and GPU of its day.

Points worth noting:

  • Useful MACs track active cycles closely for MLP0, MLP1, LSTM0 and CNN0, so there the loss is time (stalls), not space (fragmentation). CNN1 loses about half its active MACs to unused slots, and LSTM1 about 40%.
  • Weight shifting is 13–17% of cycles in the MLPs and LSTMs even with double buffering: when each weight tile is used for few rows, its 256-cycle shift can’t be hidden behind compute.
  • Eyeriss reports its row-stationary dataflow 1.4–2.5× more energy-efficient than WS, OS and NLR in AlexNet’s convolution layers and at least 1.3× in fully connected layers at batch sizes of 16 or more.
  • The TPU v4 authors attribute part of their energy advantage to reuse: a 128 × 128 MXU reuses each 128-entry input 128 times, so the on-chip SRAM is read less per operation.

Building a chip around a grid like this means making some bets:

  • One big grid or several small ones. A big grid reuses each number more, but it is harder to keep busy. Google moved from one big grid to several smaller ones.
  • Which numbers stay put. Keeping weights still suits some jobs. Keeping totals still suits others.
  • Special or flexible. The grid is superb at multiplying big tables and poor at almost everything else. The chip needs other parts for the rest.
  • Memory takes the space. The grid itself is small. Much of the chip goes to the memory that feeds it.

Array size: reuse against utilization

A bigger array reuses each operand more (each activation crosses more columns) but loses more to fill, drain and fit. At the same peak FLOPS, Google’s analysis showed four 128 × 128 arrays got 1.6× the utilization of one 256 × 256, at half the operations per operand read.

Dataflow: what you save and what you pay

  • Weight-stationary saves weight reads but ships partial sums between PEs and to accumulators.
  • Output-stationary keeps sums local but must stream both operands and re-read one of them for every tile of outputs.
  • Row-stationary balances all three at the cost of more complex PEs and mapping.

Speed against area and power

Registers between every pair of PEs keep wires short and the clock fast, but registers cost area and power. Gemmini compared a fully pipelined “TPU-like” array with one where PEs within a tile are chained without registers (“NVDLA-like”). With 256 PEs, the pipelined design ran at 2.7 times the frequency but used 1.8 times the area and 3.0 times the power.

Specialization

TPU v1’s array was designed for dense matrices; support for sparsity was left out to ship sooner. Layers with little reuse, such as depthwise convolutions, map poorly. Activation functions, normalization and data rearrangement need other units: TPU v2 replaced v1’s fixed activation pipeline with a general vector unit and added a transpose and permute unit beside the matrix unit.

Ways designs go wrong

  • Starved arrays. Weights or activations can’t arrive fast enough, as in TPU v1’s MLPs and LSTMs.
  • Shape mismatch. Layers narrower or shallower than the array leave PEs idle.
  • Latency limits. Response-time targets cap the batch size, so reuse stays low.
  • Host overhead. Without on-chip im2col, Gemmini’s convolution speed depends heavily on the host CPU.

Reuse arithmetic

In an R×CR \times C WS array, each activation read is used CC times, each weight read TT times per load, and each partial sum leaving the array represents RR MACs. Operations per operand read therefore scale with the array edge, which is why quartering a 256 × 256 array into four 128 × 128 arrays halves operations per operand (4× to 2× on Google’s chart, normalized to 64 × 64) while improving utilization 1.6×. The buffer must supply the difference: four arrays of edge 128 need twice the operand bandwidth of one array of edge 256 at the same FLOPS.

Wide sums on the wires

Kung noted in 1982 that paths carrying partial results must be wider than paths carrying weights, because results carry more bits for accuracy. With int8 operands and 32-bit accumulation, a WS column carries 32-bit sums PE to PE, four times the width of the activations crossing the row. Tesla’s FSD accelerator takes the other side: it computes 96 output pixels by 96 output channels at a time with the input-channel and kernel loops innermost, accumulating 8-bit × 8-bit products into 30-bit sums held in the MAC array, and lists “in place data reuse vs result movement” among its power choices.

Clocking a big array

Nearest-neighbor wiring removes long data wires but not the clock. Kung warned that large 2-D systolic arrays may need a slower global clock to absorb , while one-dimensional arrays tolerate large skew between their ends. Today that is the job of clock tree synthesis, but the clock remains the one signal that spans the whole array.

Flexibility tax

Supporting several dataflows needs muxes and extra registers in every PE, and SCALE-Sim found fixing one dataflow rarely costs much; Gemmini makes it a build-time choice so designers can measure. For layers whose matrices are small, the shape mismatch matters more than the dataflow: a tiny MM on a WS array is mostly fill and drain. More on batch and decode in The memory wall.

1 × 256 × 256utilization1×ops per operand4×feed bandwidth1×relative; reuse vs 64 × 64
Arrays

One 256 × 256 array: the most reuse per operand, but the most lost to fill, drain and fit.

Equal peak FLOPS split into 1, 4 or 16 arrays: Google’s TPU v2 analysis. Feed bandwidth is the inverse of reuse.Share freely with credit: ‘Figure from chipfieldguide.com’

This part goes deeper, into the math, models and algorithms behind the chapter. It’s written for the Expert level.

The models behind the simulation and the numbers above, step by step, with their assumptions.

1. Dataflow as a loop order

A GEMM is three nested loops. A dataflow chooses which index is spatial (unrolled across PEs) and which is temporal, and the stationary operand is the one whose indices don’t change in the innermost temporal loop. Illustrative pseudo-code for one tile:

dataflows.txt (illustrative)text
# C[M][N] += A[M][K] * B[K][N] on an R x C array
# Weight-stationary: B[k][n] pinned in PE(k, n)
for k0 in 0..K step R:            # fold: load B[k0:k0+R][0:C]
  for m in 0..M:                  # time: stream rows of A
    parallel for k, n in R x C:   # space
      C[m][n] += A[m][k0+k] * B[k0+k][n]

# Output-stationary: C[m][n] pinned in PE(m, n)
for m0 in 0..M step R:            # fold: new tile of outputs
  for k in 0..K:                  # time: stream A columns and B rows
    parallel for m, n in R x C:   # space
      C[m0+m][n] += A[m0+m][k] * B[k][n]
  drain C[m0:m0+R][0:C]
  1. 1L3Each fold loads a new weight tile; partial sums for different k0 add in the accumulators.
  2. 2L4M is the temporal dimension: the longer it is, the more the fold overhead is amortized.
  3. 3L9Each fold re-reads all of B for a new tile of outputs.
  4. 4L10K is temporal: the accumulation happens in place, so partial sums never leave the PE.

Tesla’s slides show the same idea for convolution: the seven-deep loop nest is reordered so output pixels and output channels step by 96 in the outer loops and input channels and kernel positions run innermost.

2. Cycle-exact timing

Weight-stationary, R=C=nR = C = n, one fold, cycles counted from the start of the fold:

  1. Load: weight rows shift in from the top, bottom row first, one row per cycle: nn cycles. Nothing computes, so no skew is needed.
  2. Stream: A[m][k]A[m][k] enters row kk at stream cycle s=m+ks = m + k and meets PE(k,jk, j) at s=m+k+js = m + k + j.
  3. Exit: C[m][j]C[m][j] leaves the bottom at s=m+(n−1)+js = m + (n - 1) + j. The last one, m=M−1m = M - 1, j=n−1j = n - 1, leaves at s=M+2n−3s = M + 2n - 3.
  4. Total: n+(M+2n−2)=3n+M−2n + (M + 2n - 2) = 3n + M - 2, which is SCALE-Sim’s 2R+C+T−22R + C + T - 2 with T=MT = M.

Output-stationary: A[m0+r][k]A[m_0 + r][k] enters row rr at cycle k+rk + r, B[k][j]B[k][j] enters column jj at k+jk + j, so PE(r,jr, j) does its kk-th MAC at cycle k+r+jk + r + j. The last MAC is at K+2n−3K + 2n - 3; then nn cycles shift the results out the bottom: K+3n−2=2R+C+T−2K + 3n - 2 = 2R + C + T - 2 with T=KT = K. The busy count at stream cycle ss is the number of (r,j)(r, j) with 0≤s−r−j<T0 \le s - r - j < T, a diagonal band; it reaches n2n^2 only if T≥2n−1T \ge 2n - 1. That is why the sim’s 4 × 4 default (T=4T = 4) peaks at 12 busy PEs.

3. Runtime and utilization with folds

τ=(2R+C+T−2)⋅⌈SRR⌉⋅⌈SCC⌉\tau = (2R + C + T - 2) \cdot \left\lceil \frac{S_R}{R} \right\rceil \cdot \left\lceil \frac{S_C}{C} \right\rceil

U=MKNR C τU = \frac{MKN}{R\,C\,\tau}

Assumptions: operands always available (no memory stalls), no overlap between folds, outputs leave without stalling, and every fold pays the full overhead even when partly empty. Real designs overlap weight loading with compute (TPU v1 double-buffers) and may pipeline consecutive folds, so τ\tau is a pessimistic bound on overhead and an optimistic one on memory. SCALE-Sim’s authors checked its cycle counts against an in-house RTL model for one case: output-stationary multiplication of matrices the size of the array.

4. Buffer traffic

Counting each operand that enters the array edge as one buffer read (the sim’s model, N=CN = C):

  • WS: reads = KNKN (each weight once) + MKMK (each activation once per column tile); accumulator writes = MN⌈K/R⌉MN \lceil K/R \rceil, of which all but the last fold also need a read-modify-write.
  • OS: reads = MKMK (each activation once) + KN⌈M/R⌉KN \lceil M/R \rceil (BB re-read for every row tile of outputs); result writes = MNMN.
  • Lower bound for either: MK+KNMK + KN, every operand read once. Without reuse a MAC needs two operand reads, so 2MKN2MKN.

Example from the sim: n=4n = 4, M=16M = 16, K=4K = 4. WS reads 16+64=8016 + 64 = 80 for 256 MACs (3.2 MACs per read, the minimum possible); OS needs 4 row tiles and reads 64+64=12864 + 64 = 128 (2.0 MACs per read). Swap to M=4M = 4, K=16K = 16 and the roles reverse in cycles, though here both read 128 because AA and BB are both 64 values.

5. Energy from reuse

Eyeriss models a value’s data-movement energy from how many times it is reused at each level. If a value is read from DRAM aa times, each of those copies from the global buffer bb times, passed across the array cc times and read from the register file dd times, then

E=a EC(DRAM)+ab EC(buffer)+abc EC(array)+abcd EC(RF)\begin{aligned} E = {} & a\,\mathrm{EC}(\mathrm{DRAM}) + ab\,\mathrm{EC}(\mathrm{buffer}) \\ & + abc\,\mathrm{EC}(\mathrm{array}) + abcd\,\mathrm{EC}(\mathrm{RF}) \end{aligned}

with EC=200\mathrm{EC} = 200, 6, 2 and 1 MAC-equivalents. Partial sums are counted the same way for their reads and writes. A worked example with those costs (illustrative): an activation that must meet 128 weights. Read from the buffer for every MAC it costs 128×6=768128 \times 6 = 768. Read once from the buffer and passed PE to PE across a 128-wide WS row it costs 6+128×2=2626 + 128 \times 2 = 262, about 3× less. Read from DRAM every time it would cost 128×200=25,600128 \times 200 = 25{,}600. Sze et al. estimate AlexNet’s 724M MACs would need nearly 3000M DRAM accesses with no reuse and 61M with perfect on-chip reuse.

DRAM every MAC128 × 200 = 25,600buffer every MAC128 × 6 = 768buffer once, pass6 + 128 × 2 = 26211e11e21e31e41e5× MAC

n = 128: 25,600 / 768 / 262 MAC-equivalents. Passing PE to PE is 2.9× cheaper than re-reading the buffer (→ 3× cheaper as n grows), 98× cheaper than DRAM.

Data-movement energy for one activation that meets n weights, with Eyeriss’s 65 nm costs (DRAM 200, buffer 6, inter-PE 2, MAC = 1). Log scale. The text’s worked example is n = 128.Share freely with credit: ‘Figure from chipfieldguide.com’

6. Lowering convolution

Filter tensor FF (KK filters × CC channels × R×SR \times S) reshapes to FmF_m of K×CRSK \times CRS; the data matrix DmD_m is CRS×NPQCRS \times NPQ, built by copying each R×S×CR \times S \times C input patch into a column; Om=Fm⋅DmO_m = F_m \cdot D_m is K×NPQK \times NPQ. Each input value appears up to R⋅SR \cdot S times in DmD_m, so materializing it multiplies activation memory and traffic by up to R⋅SR \cdot S; cuDNN instead forms tiles of DmD_m lazily in on-chip memory, and Gemmini does the equivalent in hardware at the array edge. Sze et al. note that the same redundancy, or a complex access pattern, is the price of treating convolution as plain matrix multiplication, and that its row-stationary dataflow exploits the convolutional reuse directly instead.

7. What the simulation leaves out

  • Memory: buffers deliver every operand on time; there are no DRAM, bandwidth or bank-conflict stalls.
  • Overlap: folds run back to back with the full load, fill and drain each time (no double buffering).
  • Shape: BB always has as many columns as the array (N=nN = n), so column fragmentation isn’t modeled.
  • Numerics: small positive integers with unlimited precision; no 8-bit saturation or accumulator width.
  • Energy and area: reads are counted, not weighted by the cost of each memory level.
Novice · 0 of 4 correct
  1. Q1Kung’s example: memory delivers 10 million bytes per second and each operation needs 2 bytes moved. A chain of six processing elements reuses each fetched value. What rate becomes possible?

  2. Q2A 4 × 4 array (16 PEs) does a 64-MAC job in 14 clock cycles. What is its utilization?

  3. Q3What stays in place in an output-stationary array?

  4. Q4Why does data enter row rr of the array rr cycles later than row 0?

Sources

Show Hide 12 sources
  1. Systolic Arrays (for VLSI)H. T. Kung and Charles E. Leiserson · Carnegie-Mellon University, report CMU-CS-79-103 (copy on H. T. Kung’s Harvard page) · 1978The name from the heart’s systole; processors that rhythmically compute and pass data; a hexagonally connected array for matrix multiplication; simple, regular communication suited to cheap VLSI.
  2. Why Systolic Architectures?H. T. Kung · IEEE Computer 15(1) (author’s copy at Harvard) · 1982Multiple computations per memory access; 10 MB/s and 2 bytes per operation cap one PE at 5 MOPS while a six-PE array reaches 30 MOPS; the family of convolution designs where weights, results or inputs stay; idle half-arrays; wide partial sums; the clock as the only global signal; clock skew in large 2-D arrays.
  3. Efficient Processing of Deep Neural Networks: A Tutorial and SurveyVivienne Sze, Yu-Hsin Chen, Tien-Ju Yang, Joel Emer · arXiv (Proceedings of the IEEE) · 2017Three reads and one write per MAC; AlexNet’s 724M MACs need nearly 3000M DRAM accesses without reuse, 61M at best; relative access energy DRAM 200×, buffer 6×, inter-PE 2×, register file 1×; WS, OS, NLR and RS dataflows; Toeplitz lowering of convolution; resistive crossbars as the ultimate weight-stationary design.
  4. Eyeriss: A Spatial Architecture for Energy-Efficient Dataflow for Convolutional Neural NetworksYu-Hsin Chen, Joel Emer, Vivienne Sze · ISCA 2016 (MIT RLE copy) · 2016Dataflow taxonomy (WS, OS and its variants, NLR) and row stationary; energy per access normalized to a MAC in 65 nm; the reuse-based energy equation; RS 1.4–2.5× more efficient in conv layers; chip spec: 168 PEs, 0.5 kB per PE, 108 kB buffer, 200 MHz, 16-bit fixed point.
  5. SCALE-Sim: Systolic CNN Accelerator SimulatorAnanda Samajdar, Yuhao Zhu, Paul Whatmough, Matthew Mattina, Tushar Krishna · arXiv · 2018Output-, weight- and input-stationary mappings on a systolic array; loading the stationary matrix costs cycles with no compute; WS beats IS when output pixels outnumber weights; no single dataflow wins everywhere.
  6. A Systematic Methodology for Characterizing Scalability of DNN Accelerators using SCALE-SimAnanda Samajdar, Jan Moritz Joseph, Yuhao Zhu, Paul Whatmough, Matthew Mattina, Tushar Krishna · ISPASS 2020 (copy on the Horizon Research Lab site) · 2020Mapping GEMM dimensions to the array’s rows (S_R), columns (S_C) and time (T); skewed inputs; runtime 2S_R + S_C + T − 2 for OS, WS and IS; folds ⌈S_R/R⌉·⌈S_C/C⌉, each 2R + C + T − 2 cycles.
  7. In-Datacenter Performance Analysis of a Tensor Processing UnitNorman P. Jouppi, Cliff Young, Nishant Patil, David Patterson, et al. · arXiv (ISCA 2017) · 2017256×256 8-bit MACs, 92 TOPS peak, 700 MHz, 28 nm, 40 W; 24 MiB Unified Buffer and 4 MiB of 32-bit accumulators; systolic execution to cut buffer reads; diagonal wavefront; double-buffered 64 KiB weight tiles that take 256 cycles to shift in; Table 3 utilization counters; the 600×600 tiling example.
  8. Google’s Training Chips Revealed: TPUv2 and TPUv3 (Hot Chips 32 slides)Thomas Norrie, Nishant Patil, Doe Hyun Yoon, George Kurian, Sheng Li, James Laudon, Cliff Young, Norman P. Jouppi, David Patterson · Hot Chips 32 · 2020128×128 systolic matrix unit with streaming LHS and results and a stationary RHS; bfloat16 multiply, float32 accumulate; ‘Why 128x128?’: at constant FLOPS, 4×128×128 gives 1.6× the utilization of one 256×256 at half the operations per operand; TPUv3 doubles the matrix units per core.
  9. TPU v4: An Optically Reconfigurable Supercomputer for Machine Learning with Hardware Support for EmbeddingsNorman P. Jouppi, George Kurian, Sheng Li, et al. · arXiv (ISCA 2023) · 2023Two TensorCores per chip, each with four 128×128 MXUs; 275 TFLOPS (bf16 or int8) at 1050 MHz in 7 nm; 128 MiB CMEM; TPU v3 at 123 TFLOPS and 940 MHz; twice TPU v3’s matrix multipliers; a 128×128 MXU reuses each 128-entry input 128 times.
  10. Compute and Redundancy Solution for the Full Self-Driving Computer (Hot Chips 31 slides)Pete Bannon, Ganesh Venkataramanan, Debjit Das Sarma, Emil Talpes, Bill McGee and team (Tesla) · Hot Chips 31 · 2019Two neural-network accelerators per chip, each a 96×96 MAC array at 2 GHz+ (36.8 TOPS) with 32 MB of SRAM; 8b × 8b products into 30-bit accumulators held in place; loop order with output pixels and output channels outermost; goal of about 80% utilization at batch size one.
  11. Gemmini: Enabling Systematic Deep-Learning Architecture Evaluation via Full-Stack IntegrationHasan Genc, Seah Kim, Alon Amid, et al. · arXiv (DAC 2021) · 2019Open-source generator; PEs use weight- or output-stationary dataflow; fully pipelined TPU-like vs combinational NVDLA-like arrays (2.7× frequency for 1.8× area and 3.0× power); a 16×16 array is 11.3% of system area beside a 256 KB scratchpad at 52.9%; on-the-fly im2col; low reuse in depthwise convolution.
  12. cuDNN: Efficient Primitives for Deep LearningSharan Chetlur, Cliff Woolley, Philippe Vandermersch, Jonathan Cohen, John Tran, Bryan Catanzaro, Evan Shelhamer · arXiv · 2014Lowering convolution to matrix multiplication: a K × CRS filter matrix times a CRS × NPQ data matrix; each input value duplicated up to R·S times; matrix multiplication is efficient when the matrices are large.