Architecture

The top level is tiny_attn_top. A host loads Q, K and V into a model of off-chip memory, starts the engine, and reads back the output. Two interchangeable controllers drive one tile core. One selects the FLASH schedule, and the other the NAIVE schedule.

host hbm_model words, byte counters dma bursts, latency flash_ctrl tiles, prefetch naive_ctrl four passes K/V buffers double-buffered attn_tile MM1 S = Q·Kᵀ (4×4 PEs) scale, mask, online softmax MM2 P·V (4×4 PEs) O accumulator, normalize sram_bank output staging perf_counters cycles, bytes, tiles

Memory

Controllers

flash_ctrl loops over Q blocks and, inside each block, over the K/V tiles. It issues the DMA loads, the weight loads and the group tiles, skips tiles above the causal diagonal, and normalizes and stores each block. naive_ctrl runs the four passes of the standard softmax over each row block, writing and reading the score and probability tiles through HBM.

Tile core

attn_tile processes one 4-row group against one K/V tile. The two 4×4 systolic arrays are the matrix products. Between them sit the scale-and-mask unit, the row maximum, the exp2 unit and the sum. The O accumulator rescales the running output by α and adds the new product. Five operations share the datapath: FLASH (online), and the four NAIVE passes.

Timing

The schedule is exact: busy cycles follow the model in sim/golden/schedule.py, and the end-to-end test checks them for every configuration it runs. Overlap changes the schedule, so its cycle counts are pinned as regression baselines in sim/perf_baseline.json.

The design synthesizes for an ECP5 FPGA (LFE5U-85F): 40,416 of 83,640 LUT4 positions, 51 of 156 DSP blocks, no block RAM. The timing estimate is 11.5 MHz, below the 25 MHz board clock, so timing is not closed yet. The numbers on the rest of this site come from simulation. See ARCHITECTURE section 15.