AI Accelerators Explained
← /learn · 08

FPGAs and edge NPUs

A few watts and a phone's memory: why one user's decode is a bandwidth problem, how narrower weights help, and where FPGAs win on latency and custom numerics.

Loading the animation…

Concept

The eighth family is defined less by an architecture than by a budget. An edge NPU sits inside a phone, laptop or camera chip and has a few watts and whatever memory the device has. An FPGA, or an adaptive SoC that pairs programmable logic with arrays of small AI engines, is used when a design needs hardware shaped to one model. Both meet the same wall the animation shows: at batch 1, one user's token costs one read of every weight, and these devices have the least memory bandwidth of any family here.

Apple's M4 is the example NPU. Its 16-core Neural Engine is rated at 38 TOP/s (Apple does not state the format; the model treats it as the 8-bit rate), and it shares the chip's unified memory with the CPU and GPU: up to 32 GB at 120 GB/s. Llama 3.1 8B with 8-bit weights is a 8.03 GB read per token, so the bound is 15.9 tokens per second, and the Neural Engine's arithmetic is busy for 409 µs of each 63.1 ms step. The rated operations per second hardly matter for decode; the bytes do. That is why the animation steps the weight width down: at 4 bits the same model reads half as much and the bound doubles to 31.4 tokens per second.

Narrow formats are where the two halves of this family differ. An NPU runs the formats its hardware and toolchain support: Hailo's 10H module, for example, is rated at 40 TOP/s in INT4 and 20 TOP/s in INT8, at 2.5 W typical. An FPGA can implement any width at all, 6, 5 or 3 bits, with arithmetic built for exactly that width, so a weight costs exactly its bits in memory and in logic. The animation's widths between 8 and 4 bits are the ones an FPGA can serve directly and most NPUs cannot.

 Apple M4 (Neural Engine)Hailo-10H (M.2 module)AMD Alveo V80 (Versal HBM)AMD Alveo V70 (Versal AI Core)(retired)
INT8, dense38 TOP/s[apple-m4]20 TOP/s[hailo-10h]not disclosed[amd-v80]404 TOP/s[amd-v70]
INT4, dense–40 TOP/s[hailo-10h]––
BF16, densenot disclosed[apple-m4]–not disclosed[amd-v80]202 TFLOP/s[amd-v70]
On-chip SRAMnot disclosed[apple-m4]–84.1 MB (derived)[amd-v80]–
Off-chip memory32 GB[apple-mbp-m4]8 GB[hailo-10h]32 GB[amd-v80]not disclosed[amd-v70]
Off-chip bandwidth120 GB/s[apple-mbp-m4]not disclosed[hailo-10h]820 GB/s[amd-v80]76.8 GB/s[amd-v70]
SRAM bandwidthnot disclosed[apple-m4]––47.6 TB/s[amd-v70]
Powernot disclosed[apple-m4]2.5 W[hailo-10h]190 W[amd-v80]75 W[amd-v70]

FPGAs: an HBM card, published LLM designs and the flow

The current part. AMD's Alveo V80 is the HBM FPGA card of this generation: a Versal HBM device (XCV80) with 2,600,000 look-up tables, 10,848 DSP slices and 132 Mb of block RAM plus 541 Mb of UltraRAM (about 84.1 MB on chip), beside 32 GB of HBM2e at 820 GB/s, in a card of up to 190 W (AMD's specifications). Pick it in the animation. Llama 3.1 8B fits in its HBM at any width shown; at 4 bits the decode bound is 215 tokens per second for one user, 0.885 J per token at the TDP, and at 3 bits 285. Two things follow. The on-chip memory, large for an FPGA, holds about a hundredth of the 8B model's 8-bit weights, so an LLM on this card streams its weights from HBM like a GPU does, with a quarter of an H100's bandwidth. And AMD gives no TOPS figure, because there is none to give: the arithmetic is whatever the design builds from those DSPs and LUTs, so the model has no compute term for the FPGA, and the bound is the memory's. The earlier Alveo V70, an AI Engine part with DDR, is discontinued (in the table for comparison: 20.1 tokens per second at 4 bits from 76.8 GB/s of DDR).

What published FPGA LLM designs do with that. FlightLLM (Zeng et al., FPGA 2024) maps whole LLMs onto an Alveo U280 with three FPGA-specific ideas: a configurable chain of DSP48 slices that skips the zeros of several sparsity patterns, an "always-on-chip decode" scheme with mixed-precision support aimed at the decode phase's memory bandwidth, and a "length adaptive" compilation method that cuts the cost of compiling for each sequence length. At batch 1 on LLaMA2-7B the authors report 6.0 times the energy efficiency and 1.8 times the cost efficiency of a V100S, and on a Versal VHK158 1.2 times an A100's throughput (their claims, not this site's runs). Chen et al. go further towards the FPGA's native style: a spatial design, one hardware unit per operator with data streamed between them, built from a library of HLS kernels, with an analytical model of when it beats a GPU; for GPT-2 decode on a U280 they report 1.9 times an A100's speed and 5.7 times its energy efficiency. Both results are for batch 1 and small to mid-size models, which is where the FPGA's fixed pipeline helps most: there is no batch to fill and no kernel launch to wait for.

The flow, and why custom widths pay. An FPGA design for a network is usually written in C++ for high-level synthesis (Vitis HLS) or generated from the trained network by a compiler. hls4ml builds networks into FPGAs for particle-physics triggers, where its example classifier answers in about 100 ns; FINN builds a streaming dataflow accelerator, one hardware stage per layer, with each layer's resources sized to a throughput target. Both rely on the FPGA's freedom to pick any number format per layer. That pays twice. Memory: a weight costs exactly its bits, which is the animation. Logic: a multiplier's size grows about as the square of its width, so a narrow multiply built from LUTs costs a fraction of a wide one, and several narrow multiplies can share one DSP slice. The author's synthesis of a systolic array's PEs (Systolic Arrays Explained, the model page) measures the square law in gates. The cost is the one that has kept FPGAs out of large-model serving: a hardware design flow, a long place-and-route run for every change, and clock speeds well below a fixed-function chip's.

Maths

With NwN_w weights of bb bits, a KV cache of KK bytes per position over a context of cc positions, memory bandwidth β\beta and peak PP:

ttoken=max⁡ ⁣(Nw b/8+c Kβ,2Nw+FattnP),compute termmemory term≈2Nw/PNwb/(8β)=16 βb P.t_{\text{token}} = \max\!\left(\frac{N_w\, b / 8 + c\,K}{\beta}, \frac{2 N_w + F_{\text{attn}}}{P}\right), \qquad \frac{\text{compute term}}{\text{memory term}} \approx \frac{2 N_w / P}{N_w b / (8\beta)} = \frac{16\,\beta}{b\,P}.

For the M4 at 8 bits that ratio is 16×120 GB/s/(8×38 T/s)≈0.00616 \times 120\,\text{GB/s} / (8 \times 38\,\text{T/s}) \approx 0.006: the arithmetic is idle more than 99% of the time at batch 1.

Code

The decode bound at a chosen weight width, cut from src/lib/accel/model.ts:

const wb = bits / 8;
const kvb = wb < 1 ? 1.0 : wb;
const weights = s.params * wb;
const nbytes = s.matmul_params * wb + ctx * s.kv_elems_per_token * kvb;