Dv505r/ggml-axera
GitHub: Dv505r/ggml-axera
面向 AXERA AX650N / Radxa AX-M1 的实验性 ggml/llama.cpp 动态后端插件,旨在实现边缘 NPU 上的大模型推理桥接与底层协议互操作研究。
Stars: 0 | Forks: 0
# ggml-axera: 面向 llama.cpp 的 AX-M1 backend
[](LICENSE)
[](DISCLAIMER.md)
这是一个实验性的动态 backend,旨在通过 AXCL 在 x86_64 或 ARM64 主机(包括 Raspberry Pi 5)上使用 Radxa AICore AX-M1 / AX8850。
本仓库仅包含源代码、文档和可复现的数值结果。它不分发 AXERA SDK、驱动程序、固件、Pulsar2 镜像、Gemma 权重或 tokenizer,也不包含 `.axmodel` 文件。这些组件必须从授权来源单独获取,并受其各自条款的约束。
## 当前状态
首个实验基础已在文档记录的条件下得到验证:
- 与当前的 GGML/llama.cpp Backend API 兼容的动态插件;
- AXCL device 枚举;
- device buffer 的分配、memset 和拷贝;
- 用于在没有 Axera 硬件的情况下进行编译和测试的 mock runtime;
- 原生构建和 ARM64 交叉编译;
- 在 Raspberry Pi 5 + AX-M1 上加载、内省和同步执行真实的 `.axmodel`;
- GGML 插件中的 sidecar 桥接:使用 `axmodel=...` 进行初始化,I/O 元数据并在 AXCL buffer 上执行,该过程已在微型模型和真实的 Gemma 4 layer 上得到验证;
- 完整的 Gemma 4 prefill 和 greedy decode:tokenizer/chat template、35 层 network、PLE、mask、hidden state、共享 K/V routing 和 post-model,在 AX650N 上生成了 `HowHowHow` 序列,且无需调用 Pulsar2;
- 首批在硬件上针对均匀 bias 和 one-hot/fan-out 16x16 权重进行验证的开放式 encoder,且无需调用 Pulsar2。
该插件**目前尚未声明任何可自动支持的 GGML NPU operator**。sidecar executor 可显式使用,但在 llama.cpp 的 tensor mapping 得到验证之前,`supports_op` 仍将返回 false。这种选择避免了错误的结果:AXCL 执行 `.axmodel` 图,而作为参考的 Rockchip backend 具有直接的 matmul 原语。子图的执行桥接在 [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) 中进行了说明。
`HowHowHow` 序列是相对于由参考 AXLLM runtime 执行的相同包的保真度结果:它证明了 buffer 和编排已被复现,但并不意味着模型的输出在语义上是正确的。关于端到端质量和已编译包缺陷的调查仍然与推理路径的逆向工程分开进行。
## Mock 构建(主机开发)
```
cmake -S . -B build-mock -G Ninja \
-DLLAMA_CPP_SOURCE_DIR=/percorso/llama.cpp \
-DAXERA_USE_AXCL=OFF
cmake --build build-mock --parallel
ctest --test-dir build-mock --output-on-failure
```
要使 mock device 对加载器可见:
```
GGML_AXERA_MOCK=1 ./build-mock/bin/axera-probe
```
## 在 Raspberry Pi 5 上原生构建
前置条件:AArch64 系统、AXCL 3.6.x、位于 `include/axcl` 的头文件、位于 `lib/axcl` 的库、Ninja、CMake 以及 llama.cpp 的 checkout。
```
./scripts/build-arm64.sh /percorso/llama.cpp
```
添加一个简短的 AXCL 分配和拷贝测试(4096 字节):
```
AXERA_MEMORY_TEST=1 ./scripts/build-arm64.sh /percorso/llama.cpp
```
该脚本使用 `/usr` 作为 SDK 根目录;如果头文件位于 Radxa 用户 SDK 中,它会自动检测 `${HOME}/opt/axcl-sdk-3.6.5/usr`。结果为:
```
build-arm64/bin/libggml-axera.so
```
插件和 llama.cpp 必须针对同一个 GGML revision 进行编译。要加载它,请将 `libggml-axera.so` 放在 llama.cpp 构建的其他动态 backend 旁边,或者设置该 revision 预期的 backend 路径。
在经过验证的 revision 中,`GGML_BACKEND_PATH` 必须指向单个插件文件(而不是其目录):
```
GGML_BACKEND_PATH=/percorso/libggml-axera.so llama-cli --list-devices
```
在配置好的 Raspberry 上,可以直接使用:
```
./scripts/run-llama-with-axera.sh --list-devices
```
要检查真实的 Pulsar2 子图而不执行它:
```
./build-arm64/bin/axera-model-inspect /percorso/layer.axmodel
```
生成机器可读的 JSON manifest:
```
./build-arm64/bin/axera-model-inspect --json /percorso/layer.axmodel > layer.manifest.json
```
该命令会加载模型、创建 AXCL context、打印 shape group 和 I/O,然后释放所有资源。
使用清零的 I/O 在 shape group 0 上进行简短的 NPU 冒烟测试:
```
./build-arm64/bin/axera-model-inspect --execute-zero 0 /percorso/layer.axmodel
```
推荐的变体使用非零的确定性 BF16 激活:
```
./build-arm64/bin/axera-model-inspect --execute-pattern 0 /percorso/layer.axmodel
```
集成在动态插件中的路径可以通过以下方式测试:
```
./build-arm64/bin/axera-backend-model-run \
./build-arm64/bin/libggml-axera.so /percorso/layer.axmodel 0
```
runner 使用 `ggml_backend_load` 加载 backend,将 `axmodel=/percorso/layer.axmodel` 传递给 `ggml_backend_dev_init`,分配 I/O 并调用扩展 `ggml_backend_axera_model_execute`。微型模型和 Gemma layer 的测试 manifest 为 [`manifests/ax650-backend-executor-hardware.json`](manifests/ax650-backend-executor-hardware.json)。
全部 35 个 sidecar 的完整路径使用了由开放工具生成的语义 buffer:
```
python3 tools/analyze_gemma4_sidecar_inputs.py MODEL_DIR \
--token-ids 2,105,9731,107,3048,659,496,11045,16326,236761,106,107,105,2364,107,150917,106,107,105,4368,107 \
--emit-dir captures/open-inputs
build-arm64/bin/axera-sidecar-stack-run \
build-arm64/bin/libggml-axera.so MODEL_DIR 35 1 captures/open-inputs 21
```
在验证 prompt 上,argmax 是 token 3910 (`How`),与 AXLLM 相同。公式、cache routing 和分配约束记录在 [`docs/GEMMA4_SIDECAR_CONTRACT.md`](docs/GEMMA4_SIDECAR_CONTRACT.md) 中;硬件 hash 位于 [`manifests/ax650-gemma4-open-prefill-hardware.json`](manifests/ax650-gemma4-open-prefill-hardware.json) 中。
runner 还可以根据刚刚采样的 token 在内存中生成 decode 输入,并在多次传递之间维护 device cache:
```
build-arm64/bin/axera-sidecar-stack-run \
build-arm64/bin/libggml-axera.so MODEL_DIR 35 1 captures/open-inputs 21 \
--generate-decode MODEL_DIR 2
```
此测试生成三个连续的 argmax `3910,3910,3910`,即 `HowHowHow`。`axera-gemma4-input-gen` 单独公开了相同的 C++ 生成器,用于检查或比较单个 token/位置的 buffer。
实际路径直接接受一个 prompt,使用 llama.cpp 进行 template/tokenization/sampling,并在持久引擎中维护模型和 KV cache:
```
AXERA_MODEL_DIR=/percorso/gemma-4-E2B-it-GPTQ-INT4 \
scripts/run-gemma4-axera.sh "Ciao" --n-predict 32 --temperature 0
```
硬件上的 greedy 测试在不使用中间输入文件的情况下生成了 `HowHowHow` 文本。架构、选项和限制详见 [`docs/LLAMA_CPP_PRACTICAL_INTEGRATION.md`](docs/LLAMA_CPP_PRACTICAL_INTEGRATION.md)。
首次真实测试的结果见 [`docs/HARDWARE_VALIDATION.md`](docs/HARDWARE_VALIDATION.md)。
所用 layer 的 manifest 位于 [`manifests/gemma4-e2b-layer0.json`](manifests/gemma4-e2b-layer0.json)。
## 协议逆向工程
该仓库包含非侵入式工具,用于映射从文件到 CMM 的路径,而无需发送手动构造的 ioctl:
```
python3 tools/axmodel_analyze.py diff modello-a.axmodel modello-b.axmodel
python3 tools/axmodel_analyze.py locate-u32 modello-a.axmodel 0x34888 0x34040
python3 tools/protobuf_wire_analyze.py axmodel-blobs modello-a.axmodel
python3 tools/protobuf_wire_analyze.py axmodel-neu-cmm modello-a.axmodel \
--trace capture/ioctl-memory.stdout.txt
python3 tools/flatbuffer_wire_analyze.py modello-a.axmodel \
--offset 0x17e8cdc --size 0x34888 --axera-neu-cmm --expected-cmm 0x34040
python3 tools/flatbuffer_wire_analyze.py modello-a.axmodel \
--offset 0x17e8cdc --size 0x34888 --axera-neu-cmdq
python3 tools/axmodel_cmdq_diff.py compare modello-a.axmodel modello-b.axmodel
python3 tools/axmodel_blob_diff.py modello-a.axmodel modello-b.axmodel
python3 tools/analyze_weight_probe_params.py experiments/ax650-micro-models \
--output manifests/ax650-weight-probe-params.json
python3 tools/axmodel_patch_uniform_bias.py BASE.axmodel OUT.axmodel --bias 0.09375
python3 tools/axmodel_patch_weight_onehot.py BASE_ONEHOT.axmodel OUT.axmodel \
--target-output 2
./scripts/capture-axcl-trace.sh captures/run-001 modello-a.axmodel 0
```
外部容器已被识别为 Protocol Buffers;每个 NEU payload 都是 FlatBuffer。`axmodel-neu-cmm` 命令在十个子图上验证了将 `root.field[5]/table.field[1]` 向量(解释为 8 字节的元素)解释时,与驱动程序返回的 CMM 大小完全一致。它还验证了 15 个 cmdq descriptor 在没有间隙和重叠的情况下对整个 CMM 映像进行了分区。协议和验证标准详见 [`docs/REVERSE_ENGINEERING.md`](docs/REVERSE_ENGINEERING.md)。
生成并分析受控的 AX650 语料库(各种形状的 Add、Mul、Sigmoid、SiLU 和 MatMul):
```
python3 tools/generate_onnx_micro_models.py experiments/ax650-micro-models
python3 tools/build_micro_model_corpus.py experiments/ax650-micro-models \
--repetitions 3 --warmups 1
python3 tools/analyze_micro_model_corpus.py experiments/ax650-micro-models \
--output manifests/ax650-micro-model-analysis.json
```
比较首先测量相同编译之间非确定性的 word,并将它们排除在 operator 之间的差异之外。过程、Pulsar2 映像和限制详见 [`experiments/AX650_MICRO_MODELS.md`](experiments/AX650_MICRO_MODELS.md)。
在 Raspberry/AX650N 上进行批量验证使用确定性的 FP32 输入,并为每个模型保存日志:
```
scripts/validate-axera-micro-models.sh \
build-arm64/bin/axera-model-inspect DIRECTORY_MODELLI DIRECTORY_RISULTATI 0
python3 tools/verify_micro_model_hardware.py DIRECTORY_RISULTATI/summary.tsv \
--max-abs-error 0.03 \
--output manifests/ax650-micro-model-hardware.json
```
当前语料库已完成 39/39 次执行。MatMul 1x16 系列使用相同的逐字节校准存档,包含从 `-0.25` 到 `+0.25` 的均匀扫描、one-hot bias 以及六个受控的权重探测。所有 624 个输出与 NumPy 参考的绝对误差均在 0.03 以内;最大值 `0,02423` 出现在全一矩阵中,该矩阵累加了 16 个乘积。排除该累加情况,最大值保持在 0.02 以下。
包括从未编译的 bias、一个移位的 one-hot 以及一个作用于所有 16 个输出的 fan-out 在内的 11 个混合或合成模型,已完成 11/11 次执行,并且 176/176 个值均在 0.02 以内。报告分别为 [`manifests/ax650-bias-hybrid-hardware.json`](manifests/ax650-bias-hybrid-hardware.json)、[`manifests/ax650-open-weight-onehot-0-2.json`](manifests/ax650-open-weight-onehot-0-2.json) 和 [`manifests/ax650-open-weight-fanout-row0-all.json`](manifests/ax650-open-weight-fanout-row0-all.json)。
观察到的两个 FP32 区域以及 `npu_params` 中相关的 CMM 立即数可以通过以下方式进行分析:
```
python3 tools/analyze_bias_probe_params.py experiments/ax650-micro-models \
--output manifests/ax650-bias-probe-params.json
```
worker 的内存抓取可以在不打开 `/dev/npu` 的情况下进行解码和比较:
```
python3 tools/ioctl_memory_analyze.py summary capture/ioctl-memory.stdout.txt
python3 tools/ioctl_memory_analyze.py compare \
capture-g1/ioctl-memory.stdout.txt capture-g9/ioctl-memory.stdout.txt
python3 tools/ioctl_memory_analyze.py compare-controlled \
capture-matmul-run1/ioctl-memory.stdout.txt \
capture-matmul-run2/ioctl-memory.stdout.txt \
capture-matmul-add/ioctl-memory.stdout.txt
```
比较过程会归一化用户态和 device 指针,并将观察到的 stack 前缀限制为反汇编重构出的 72 字节(16 字节的 wrapper + 56 字节的 descriptor 前缀)。它分别报告 handle/shape group、十个 blob 的布局和残余差异。头文件 [`include/axera-npu-abi.h`](include/axera-npu-abi.h) 仅记录观察到的 offset,目前尚未用于发送 ioctl。
2026 年 7 月 30 日的硬件结果汇总于 [`docs/RE_FINDINGS_2026-07-30.md`](docs/RE_FINDINGS_2026-07-30.md)。
## ARM64 交叉编译
```
export AXCL_ROOT=/percorso/sdk-axcl-aarch64/usr
export AXERA_SYSROOT=/percorso/sysroot-rpi # opzionale
./scripts/build-arm64-cross.sh /percorso/llama.cpp
```
需要在 `PATH` 中有 `aarch64-linux-gnu-gcc/g++` 工具链。
## 参考
- [ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp) — Backend API、tokenizer、chat template 和 sampler;MIT 许可证。
- [invisiofficial/rk-llama.cpp](https://github.com/invisiofficial/rk-llama.cpp) — 动态 GGML backend 的架构参考;MIT 许可证。
- [AXERA-TECH/ax-llm](https://github.com/AXERA-TECH/ax-llm) — AXERA LLM runtime 的行为参考;BSD-3-Clause 许可证。
- [AXERA-TECH/Pulsar2 5.2](https://huggingface.co/AXERA-TECH/Pulsar2/tree/main/5.2) 和 [Pulsar2 文档](https://github.com/AXERA-TECH/pulsar2-docs-en)。
- [Radxa AX-M1 文档](https://docs.radxa.com/en/aicore/ax-m1)。
- [AXCL 文档](https://axcl-docs.readthedocs.io/en/latest/)。
- [Gemma 官方条款](https://ai.google.dev/gemma/terms)。
- [LocalLLaMA 上关于 Rockchip backend 的讨论](https://www.reddit.com/r/LocalLLaMA/comments/1p4t5ix/i_created_a_llamacpp_fork_with_the_rockchip_npu/) — 社区背景,非权威来源。
归属、依赖关系以及所包含代码与外部材料之间的区别在 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 中有详细说明。
## 许可证与安全
本仓库的原始代码根据 [MIT](LICENSE) 许可证分发。外部项目和工件的许可证不受此许可证的修改。使用前请另请参阅 [DISCLAIMER.md](DISCLAIMER.md) 和 [SECURITY.md](SECURITY.md)。
## 引用
对于学术论文、报告或衍生项目,请使用 [CITATION.cff](CITATION.cff) 中的元数据。GitHub 也通过 **Cite this repository** 按钮公开这些信息。
标签:Bash脚本, DLL 劫持, llama.cpp, NPU, 大语言模型, 嵌入式AI, 硬件加速, 边缘计算, 逆向工具