2月技术报告-周雨
目标与问题
主要目标
- 阅读 RKNPU 驱动相关源码,学习 NPU/AI 相关知识,将 RKNPU 硬件手册总结为 markdown 文档
- 实现NPU驱动异步中断支持
- 实现NPU驱动多核多任务并发
主要问题
- 环境与工具链问题:开发板 U‑Boot 缺少网卡驱动,只能通过串口加载内核,调试效率低;U-boot网卡驱动编译困难,依赖太多,将会逐步解决
- 驱动集成冲突:尝试将 NPU 驱动集成到 StarryOS 主线时,HAL 层架构
axhal与somehal存在冲突,RK3588 平台级代码集成涉及整个架构的大修改。当前阶段将继续在当前已有驱动基础上开发。暂时不会集成到主线
方案或思路
面对上述问题,我制定了以下开发路线:
-
分阶段学习与验证:
- 第一阶段:学习 NPU 原理、AI 模型推理流程,利用 RKNN Toolkit2 仿真器在 x86 上验证推理链条。
- 第二阶段:在真实硬件(OrangePi 5 Plus RK3588)上部署 StarryOS(NPU 版本),验证驱动可行性。
- 第三阶段:逆向闭源用户态库
librknnrt.so,整理完整的寄存器语义和任务提交流程,形成社区参考文档。
-
驱动架构设计:
- 采用
rdrive框架实现设备树(DTB)自动探测,避免硬编码设备地址。 - 创建独立的
axdrmcrate 实现 DRM 框架,提供 GEM 内存管理和 ioctl 编码解析/分发机制,保持用户态接口兼容性。 - 将驱动模块放在 StarryOS 根目录独立开发,规避 workspace 循环依赖问题,待功能稳定后再集成回 ArceOS。
- 采用
-
寄存器访问安全:
- 使用
svd2rust工具从 SVD 描述文件生成类型安全的寄存器库rknpu-regs,覆盖 PC、CNA、CORE、DPU、PPU、DDMA 等所有功能块。 - 通过编译期类型检查杜绝寄存器偏移量写错、位域宽度混淆等错误。
- 使用
-
异步中断处理:
- 利用 WFI(Wait For Interrupt)指令实现低功耗等待,NPU 核心完成任务后触发中断,CPU 被唤醒并读取中断状态寄存器。
- 为后续多核并发任务提交奠定基础——每个 NPU 核心可独立触发中断,CPU 侧可并行等待多个核心的完成通知。
-
多核并发任务调度:
- 扩展驱动数据结构,支持多套寄存器命令缓冲区(regcmd)。
- 重构
submit_ioctrl函数,实现批量任务分配,一次 ioctl 可向多个核心提交任务。 - 新增
wait_all_npucore并行等待机制,循环检查各核心中断状态,直到所有核心完成任务。
-
问题规避与迂回:
- 暂时放弃将驱动集成到 StarryOS 主线,专注于在 NPU 版本上开发核心功能,减少架构冲突带来的干扰。
- 通过刷写干净的 SPI 镜像解决 U‑Boot 环境变量残留问题,明确使用 eMMC 作为存储介质。
实现情况
1. 基础知识学习与仿真验证(第一周)
- NPU 原理学习:理解了 NPU 作为神经网络处理单元的本质,掌握了 AI 模型训练与推理的基本流程,以及驱动在推理链条中的角色(用户态库通过 ioctl 与内核驱动通信,驱动负责任务调度、DMA 搬运、寄存器操作)。
- RKNN Toolkit2 仿真验证:在 x86 主机上利用 RKNN Toolkit2 的仿真器跑通测例,梳理出 RKNN 推理的 API 调用流程:
rknn_init→rknn_inputs_set→rknn_run→rknn_outputs_get→rknn_destroy。
2. 开发板验证与文档整理(第二周)
- 硬件验证:在 OrangePi 5 Plus(RK3588)开发板上成功部署 StarryOS(NPU 版本),并跑通 RKNN 推理测例,验证了 NPU 驱动在真实硬件上的可行性。
- 闭源库逆向与文档:系统逆向分析了
librknnrt.so用户态库,结合官方 TRM 手册和开源内核驱动,整理了完整的寄存器语义、任务提交流程、状态机与 DMA Fence 路径,形成 GitHub Pages 文档,为社区提供参考。
3. 驱动骨架与 DRM 框架搭建(第二周)
- 设备探测:编写
dtbparse.rs,利用rdrive框架通过设备树(DTB)自动探测 NPU 设备(compatible = "rockchip,rk3588-rknn"),提取 MMIO 基址(0xfdab0000,大小0x9000)和中断号(SPI 110‑112)。 - DRM 框架:创建
axdrmcrate,实现 GEM 内存对象管理(handle 分配/回收、物理地址映射)和 ioctl 编码解析/分发机制,为 NPU 驱动提供兼容 Linux DRM 的用户态接口。
4. 寄存器库重构与 AI 辅助注释(第三周)
- svd2rust 寄存器库:基于 SVD 描述文件生成类型安全的寄存器库
rknpu-regs,覆盖 PC、CNA、CORE、DPU、PPU、DDMA 等所有功能块。将原有的手动偏移+裸指针访问改为类型安全、带自动补全和文档的 API,例如:#![allow(unused)] fn main() { let status = core.pc().interrupt_status().read().bits(); core.pc().interrupt_clear().write(|w| unsafe { w.bits(INT_CLEAR_ALL) }); } - AI 辅助注释:利用 AI 为
axnpu驱动代码添加详细的中文注释,明确关键函数的调用流程和参数含义,降低后续开发和协作门槛。
5. 异步中断处理实现(第三周)
- WFI 等待机制:实现基于 WFI(Wait For Interrupt)指令的低功耗异步等待。NPU 核心完成任务后触发中断,CPU 进入休眠状态;中断到来时 CPU 被唤醒,读取中断状态寄存器并清除标志位,返回任务执行结果。
- 多核中断基础:该机制为多核并发任务提交打下基础,每个 NPU 核心可独立触发中断,CPU 侧可并行等待多个核心的完成通知。
6. 多核并行矩阵乘法实现(第五周)
- 三核并行 QKV 计算:扩展
Transformer结构体,支持 3 套 regcmd 缓冲区,实现matmul_npu_3core_qkv函数,将 Transformer 中的 Q、K、V 三个矩阵乘法并行提交到 3 个 NPU 核心。 - 批量任务分配:重构
submit_ioctrl函数,支持将用户空间的任务数组自动分配到多个 NPU 核心,一次 ioctl 可向多个核心提交任务。 - 并行等待机制:新增
wait_all_npucore函数,循环检查各核心中断状态,利用 WFI 等待所有核心完成,实现真正的多核并行等待。
7. 板端验证结果
板端运行日志显示多核并行成功:
[261.640936] Total tasks to submit: 3, active cores: 3, max batch size: 4095
[261.650710] Total tasks to submit: 1, active cores: 1, max batch size: 4095
[262.295035] Total tasks to submit: 3, active cores: 3, max batch size: 4095
...
Once upon a time...
3 tasks, 3 cores表示 QKV 三核并行成功。1 tasks, 1 core表示后续的wo矩阵乘法(单核)。- 模型推理输出 "Once upon a time..." 验证结果正确。
8. 其他问题修复(第三周)
- Axvisor 文件系统同步问题:修复了 rsext4 缓存未同步导致的磁盘数据不一致问题,在写操作后增加
sync_to_disk调用,确保文件系统一致性(PR #368)。
下一步的计划/建议
基于 1 月末和 2 月份的开发进展,后续工作可围绕以下方向展开:
-
驱动稳定性与健壮性
- 当前驱动还有很多unimpliment函数和特性,我将会继续完善它
- 完善错误处理机制,增强驱动健壮性
-
多核调度优化
- 实现动态负载均衡,根据各 NPU 核心的利用率自动分配任务。(当前多核并发是简单的任务切片)
- 支持任务优先级调度,确保高优先级任务优先执行。
- 探索任务依赖关系(DAG)支持,实现复杂模型层的流水线并行。
-
主线集成与代码重构
- 待 NPU 驱动功能稳定后,重新评估集成到 StarryOS 主线的可行性,解决 HAL 层冲突。
- 将
axnpu-rknn驱动模块迁移回 ArceOS 的modules/目录,遵循项目模块化规范。
-
文档与社区建设
- 持续更新 GitHub Pages 文档,补充更多寄存器细节、性能调优指南和故障排查案例。