Architecture
Inference Pipeline
Token → Embedding → [Layer ×28] → LM Head → Sample → Token
│
┌───────────────┼───────────────┐
│ │ │
RMS Norm Attention MLP (SiLU)
│ │ │
QKV GEMM Q·K^T softmax GU GEMM
(INT8 xclbin) + V weighted (INT8 xclbin)
│ │ │
└───────────────┼───────────────┘
│
D GEMM
(INT8 xclbin)
│
Residual + Norm
INT8 Quantization
Weights are dequantized from Q4NX (4-bit with per-group BF16 scales) to float32, then re-quantized to symmetric INT8 using per-tensor max-abs scaling:
q(x) = round(x / scale), scale = max(|x|) / 127
This is done once at startup. The INT8 weights are uploaded to pre-allocated per-layer BOs and never copied during inference.
Activations are quantized on-the-fly before each GEMM:
A_int8 = round(A_float * 127/5.0)
The fixed scale of 5.0/127.0 was empirically validated on Strix Halo.
NPU Context Architecture
xrt::device (1 instance)
├── xclbin: QKV (128×1024×4096, group_id_B=4)
│ ├── layerB[0..27] — pre-loaded INT8 weight BOs
│ └── bA, bC — activation/output scratch BOs
├── xclbin: O (128×2048×1024, group_id_B=4)
│ └── layerB[0..27]
├── xclbin: GU (128×1024×6144, group_id_B=4)
│ └── layerB[0..27]
├── xclbin: D (128×3072×1024, group_id_B=4)
│ └── layerB[0..27]
└── xclbin: ATTN ×4 (per-window, optional)
All 4 GEMM contexts are created at startup and kept alive. NPU2 supports multiple concurrent hw_contexts — we've verified 8 alive at once.
Key Fixes
K-Interleaving Bug
The original INT8 MLIR generator used a shared A ObjectFifo with round-robin distribution. With 8 cores and depth=2, each core only saw 1/8 of the K-dimension — producing 394% error on random data.
Fix: Added dataReuse annotations (dimensionsToStream/dimensionsFromStream)
to the A_L2L1 ObjectFifo, switching DMA from round-robin to broadcast mode.
All 8 cores now receive the same A data. Verified correct on random data.
BFP16 Precision Collapse
BFP16 GEMMs with Chess-compiled xclbins produced correct matmul results but accumulated precision loss over 28 layers — the hidden state collapsed to a fixed point and the model repeated the same token.
Fix: Switched to INT8 quantization, which avoids the double-quantization (Q4NX→BFP16→hardware) in the BFP16 pipeline. INT8 weights are directly quantized from float32, preserving per-value precision.
NPU2 Context Limit
Multiple simultaneous hw_contexts were thought to cause ERT state=8 timeouts.
Fix: The limitation was firmware-version-dependent. Our firmware (1.1.2.65) supports 8+ concurrent contexts. XRT hw_context objects can coexist as long as kernels are invoked sequentially.