Fixed point
The engine is integer hardware. Every value has a fixed format, and the formats were chosen by a numerics study before any RTL was written.
The golden model in sim/golden/fixed.py uses the same integers, so the RTL can be checked bit for bit.
Formats
| Value | Format | Notes |
|---|---|---|
| Q, K, V | int8 | Symmetric per-tensor scale, set on the host (see below) |
| S = Q·Kᵀ | int17 | Signed dot product of int8 values |
| cfg_scale | UQ4.20, 24 bits | Folds s_Q · s_K · log₂e / √d into one multiplier |
| Z, m | Q8.8, signed | Scaled scores in log₂ units, and their running maximum |
| Z − m | Q9.8, signed | Always ≤ 0; the input of exp2 |
| 2^(Z − m), α | UQ1.15 | exp2 output and the rescale factor |
| P | UQ1.8, 9 bits | Probabilities, 0 to 256 |
| P·V (one tile) | int18, signed | Matrix product in the second array |
| l (running sum) | 18 bits, unsigned | Real value × 2¹² |
| O (accumulator) | 26 bits, signed | Real value × 2⁴, in V units |
| Reciprocal | UQ1.16 mantissa, 5-bit exponent | Used once per row, at the end |
| Output | Q8.8, signed | Dequantize with s_V: value = out / 256 · s_V |
Quantization
The host picks one scale per tensor: s = max|x| / 127, then int8 = round(x / s) with ties to
even, clipped to ±127. The scales of Q and K go into cfg_scale, so the datapath never multiplies by a
scale. Try it:
The exp2 unit
2^(x/256) for x ≤ 0 is split into an integer shift and a fraction. The fraction indexes a
256-entry table of 16-bit values. The integer part is a right shift. Results that shift past 16 bits are exactly zero,
and the output is truncated, not rounded. The table is generated by sim/golden/gen_luts.py, and CI checks
that the committed table is up to date.
The unit is tested exhaustively over its input range, so there is no sampling gap.
Measured error
Errors are against float attention on the same inputs, in float units. The first table is for each preset, with the
engine's output after dequantization. The second is from the recorded 1000-seed study at N = 32, in V units (one int8
step of V). The study separates the datapath error (dp, on the INT8 inputs) from the total error
(tot, which includes input quantization).
| Preset | N | Causal | Max |engine − float| |
|---|---|---|---|
| toy_sentence | 12 | yes | 0.0049 |
| previous_token | 16 | yes | 0.0105 |
| attention_sink | 16 | yes | 0.0254 |
| uniform | 16 | no | 0.0028 |
| self | 16 | no | 0.0260 |
| random | 16 | no | 0.0163 |
| Study, N = 32, peaky inputs | dp max | dp mean | tot max | tot mean |
|---|---|---|---|---|
| FLASH | 1.260 | 0.0691 | 11.904 | 0.4722 |
| NAIVE | 1.744 | 0.0989 | 11.806 | 0.4798 |
For the default format at N = 32 (peaky inputs), the datapath error averages 0.07 of a V step for FLASH and 0.10 for
NAIVE. The largest errors are 1.26 steps for FLASH and 1.74 for NAIVE. The total error is larger because it also includes the rounding of
the inputs to INT8. The full study output is recorded in sim/golden/study_results_1000seeds.txt.