cd /news/artificial-intelligence/axera-ax8850-llm-running-ggufs Β· home β€Ί topics β€Ί artificial-intelligence β€Ί article
[ARTICLE Β· art-111682] src=github.com β†— pub= topic=artificial-intelligence verified=true sentiment=Β· neutral

Axera AX8850 LLM running ggufs

A custom llama.cpp backend (ggml-axcl) now runs Qwen3-0.6B directly from GGUF files on an Axera AX8850 NPU accelerator card (M5Stack LLM-8850) hosted on a Raspberry Pi 5, achieving 1.3-2.7 tokens per second decode versus the vendor's 13.5-16.9 t/s with baked weights. The developer, woolcoxm, reports 13 of 14 automated tests pass, with the GGUF as the only model artifact and Q8_0 and Q4_K_M quantizations supported. The performance gap is attributed to the NPU idling about 80% of each token due to host-side glue work and per-op scheduler fragmentation.

read4 min views1 publishedAug 26, 2026
Axera AX8850 LLM running ggufs
Image: Michielbdejong (auto-discovered)

A custom llama.cpp backend (ggml-axcl

) that runs Qwen3-0.6B directly from GGUF on an Axera AX8850 NPU accelerator card (M5Stack LLM-8850: 24 TOPS INT8, 8 GB LPDDR4x) hosted on a Raspberry Pi 5.

The GGUF is the only model artifact β€” weights stream from the GGUF into NPU engines at load time. Q8_0 and Q4_K_M quants both work from the same code path.

  • Current speed: ~1.3-2.7 t/s decode (see "Architecture" for why)
  • Vendor reference, same card + model: 13.5-16.9 t/s (baked weights, closed runtime)
  • Code: branch Axera-8850-GGUF-support-PoC-qwen3-0.9b-Q4KM-Q8

ongithub.com/woolcoxm/llama.cpp

LLMTest/
β”œβ”€β”€ README.md                   this file
β”œβ”€β”€ NOTES-DYNAMIC-WEIGHTS.md    research log: whole-layer engines (weight layout cracked)
β”œβ”€β”€ llama.cpp/                  llama.cpp fork with the ggml-axcl backend
β”‚   └── ggml/src/ggml-axcl/ggml-axcl.cpp   THE backend
β”œβ”€β”€ gemm/                       NPU engine lab (harnesses, layout research, test scripts)
β”‚   β”œβ”€β”€ layout_artifacts/       captured QuantAxModel builds
β”‚   β”œβ”€β”€ mk_code_marker.py       code-encoded checkpoints (layout extraction)
β”‚   └── e2e_test.sh             the E2E test matrix (run on the Pi)
β”œβ”€β”€ pulsar2/                    Axera compiler toolchain (x86_64)
β”œβ”€β”€ ax-llm-build/               vendor LLM-builder configs
β”œβ”€β”€ Qwen3-0.6B/                 HF checkpoint (ground truth for builds)
└── vendor/                     vendor reference packages

On the Pi (kram@10.0.0.81): ~/build-axcl/

(build), ~/models/

(qwen3-q8.gguf, qwen3-q4km.gguf), /usr/local/share/ggml-axcl/

(compiled NPU engines), /usr/lib/axcl/

(card runtime, axcl-smi

at /usr/bin/axcl/axcl-smi

).

Each generated token executes ~120-140 NPU engine calls (7 matmuls Γ— 28 layers + vocab head). Between every call the Pi's CPU does glue work: RMSNorm, RoPE, softmax, masking, adds, GLU. Per call: ~0.6 ms NPU exec wrapped in ~2-3 ms of host staging + PCIe DMA + scheduler overhead β€” llama.cpp's scheduler splits the graph into per-op fragments, so the NPU idles ~80% of every token waiting for the host (visible as ~21% NPU utilization with CMM at 7 GB).

The vendor's engine fuses the entire layer into one NPU call (norm, qkv, rope, attention with on-card KV cache, FFN, GLU): 28 calls/token, no host round-trips, 1.5 ms/layer. That's the 5-10Γ— gap. Our path there is staged (see "Offload roadmap").

The weight path is already solved: Pulsar2's AxQuantizedMatMul

custom op accepts int8 weights as runtime tensor inputs, so GGUF weights are quantized once at load, uploaded, and bound per call β€” no conversion step, 4Γ— less traffic than f32.

13/14 automated tests PASS: prompt sizes 1 β†’ 3000 tokens, unicode, emoji, shell metacharacters, 2000-char word, single char, long generation. Fixed during the pass: two empty-prompt crashes in llama-simple (n_batch=0

assert; zero-token batch β†’ BOS fallback). Verified: SIGINT shutdown clean with full CMM release; 5 back-to-back runs leak-free; 2 concurrent runs correct; NPU activity confirmed live via axcl-smi.

Device-resident chain mode (GGML_AXCL_CHAIN=1

): verified coherent including 180-token prompts (the old corruption is fixed) β€” norm/add/glu run as NPU engines on device-resident activations.

Cross-fragment QKV fusion: implemented, engine output verified bit-exact vs CPU reference, 3 calls β†’ 1 per layer β€” but default-off because the scheduler interleaves q_norm/rope CPU fragments before the fusion's writeback (corrupts KV cache). Enable for experiments with GGML_AXCL_QKV_X=1

; needs op-claiming/scheduler work to be safe.

Pi:

cmake -B build-axcl -S llama.cpp -DGGML_AXCL=ON
cmake --build build-axcl -j4
~/build-axcl/bin/llama-simple -m ~/models/qwen3-q8.gguf -n 48 "Your prompt here"

NOTE: llama-simple takes the prompt as a positional argument (not -p

), and -n

must come before the prompt.

Engines (dev machine, x86): compiled with Pulsar2 from gemm/

ONNX sources (pulsar2/p7p/.../bin

on PATH), installed to the Pi under /usr/local/share/ggml-axcl/

.

Var Effect
GGML_AXCL_CHAIN
device-resident chain mode (norm/add/glu on NPU)
GGML_AXCL_CHAIN_OPS
gate chain routes (norm,add,glu )
GGML_AXCL_QKV_X
cross-fragment QKV fusion (default off β€” see above)
GGML_AXCL_QKV_SWAP
swap k/v bindings (diagnostics)
GGML_AXCL_WPOOL_MB
device weight pool size (default 2560)
GGML_AXCL_NO_OVERRIDE
disable activation-source override
GGML_AXCL_NO_FUSION
disable all fusions
GGML_AXCL_ASYNC
async engine execute + stream sync
GGML_AXCL_ATTN_MODEL
attention engine path override
GGML_AXCL_DEBUG
verbose engine load/bind logging
  • NPU3-compiled engines measured neutral vs NPU1 for our shapes (DRAM-bound); the vendor ships NPU1-default builds for this model class. - Multi-core opportunity: VNPU partitioning ( axclrtEngineInit

VNPU kinds) + concurrent engine execution β€” untapped. - Full card utilization comes from the whole-layer engines, not multi-core compilation.

Chain mode everywhere(done, default off): elementwise ops as NPU engines.** Matmul device-pipelining**: bind X from device-resident chain buffers (theg_chain_x_override

mechanism) for all projections β€” removes H2D per call.Attention engine for all context lengths: device KV cache with watermark uploads (exists; activation gate needs fixing β€” currently requires seq > 128).** Whole-layer engines**: vendor-class 28 calls/token. Weight layout fully reverse-engineered and verified (int4 nibble pairs, complete position table β€”gemm/layer_layout_v3.pkl

); remaining: scale-table mapping, weight-patching viaaxclrtEngineLoadFromMem

, backend integration. Full log:NOTES-DYNAMIC-WEIGHTS.md

.

  • Engines fail to load β†’ sudo chmod 777 /tmp/axcl

(runtime log sink), check driver. - Garbage output β†’ unset GGML_AXCL_CHAIN

/GGML_AXCL_QKV_X

experiment flags. - Card busy β†’ check axcl-smi

for stale processes; CMM baseline is ~18 MiB. - Periodic memory api ... return fail

log lines from the card runtime are non-fatal.

── more in #artificial-intelligence 4 stories Β· sorted by recency
── more on @llama.cpp 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/axera-ax8850-llm-run…] indexed:0 read:4min 2026-08-26 Β· β€”