2. i.MX NPU硬件与软件栈解析

在 i.MX 系列应用处理器上部署神经网络,第一步不是写推理代码,而是把“硬件到底提供了什么、软件栈如何把它暴露给用户态”这两件事弄清楚。i.MX 8M Plus、i.MX 93 等型号集成了不同类型的 NPU 加速单元,它们在算力、数据精度、内存访问方式上差异明显,直接决定了模型能否落地、以什么方式落地。本节围绕 NPU 核心特性、内存层次、驱动接口与 Linux BSP 集成四条线索展开,帮助读者建立从硅片到应用进程的完整认知。

NPU 核心:Ethos-U 与 NPU IP 的定位差异

i.MX 平台上的 NPU 大致分为两类。一类是 Arm 的 Ethos-U 系列微 NPU,典型代表是 i.MX 93 中的 Ethos-U65,它本身不直接连接 DDR,而是作为 Cortex-A 或 Cortex-M 的协处理器,通过 AXI 总线访问系统内存,算力通常在 0.5 TOPS 量级,面向关键词唤醒、人脸检测、简单分类等轻量任务。另一类是集成度更高的独立 NPU IP,例如 i.MX 8M Plus 中的 2.3 TOPS 神经处理单元,它拥有自己的内部缓存和更宽的数据通路,可承担 MobileNet、YOLO 系列等中等规模视觉模型。

两者在编程模型上高度一致:都通过算子图(graph)描述网络,由编译器把图切分为可在 NPU 上执行的子图,其余算子回退到 CPU 或 GPU。理解这一点很关键——NPU 不是“整网加速器”,而是“算子加速器”,部署时的性能瓶颈往往出现在回退算子和数据搬运上,而非 NPU 本身。

内存层次:决定推理延迟的隐形主角

NPU 推理的延迟由计算和访存共同决定,而访存常常是主导项。i.MX NPU 的内存层次自上而下大致为:NPU 内部寄存器与 MAC 阵列、片上 SRAM/缓存(几十 KB 到数 MB)、L3 或系统级缓存、最后是 DDR。Ethos-U65 没有大容量内部 SRAM,权重和激活值主要驻留在 DDR,因此对 DDR 带宽和访问模式非常敏感;而 i.MX 8M Plus 的 NPU 带有较大的内部缓存,能显著减少对 DDR 的重复访问。

这带来两个工程结论。第一,模型量化到 INT8 不仅减少计算量,更直接把权重和激活的访存字节数降到 FP32 的四分之一,对带宽受限的 NPU 收益极大。第二,张量布局(NHWC 与 NCHW)会影响访存连续性,NPU 编译器通常偏好 NHWC,若模型导出时布局不匹配,会引入额外的转置开销。实际部署中,建议先用编译器的分析报告确认各层的数据驻留位置,再决定是否需要调整模型结构。

i.MX NPU 软硬件栈与内存层次 硬件层次(自顶向下) NPU 计算核心 MAC 阵列 / 激活单元 / 量化单元 片上 SRAM / 内部缓存 Ethos-U65:小容量;8M Plus NPU:较大 系统级缓存 / AXI 总线 连接 NPU、CPU、DDR 控制器 DDR 主存 权重、激活、输入输出张量 外设 / 摄像头 / 传感器 通过 DMA 或 V4L2 送入数据 软件栈(自底向上) Linux BSP / 内核驱动 设备树节点、时钟、电源、DMA 通道 NPU 内核驱动 /dev/ethosu 或厂商字符设备 用户态运行时库 libethosu / OpenVX / TFLite Delegate 模型编译器 Vela / eIQ 工具链,生成 .tflite 或 .bin 应用层 推理进程、预处理、后处理、业务逻辑 硬件提供算力与存储,软件栈负责调度、编译与接口暴露

驱动接口:从设备节点到推理调用

在 Linux 用户态,NPU 通常以字符设备形式出现。以 Ethos-U 为例,内核驱动会注册 /dev/ethosu,用户态运行时库通过 ioctl 提交命令流(command stream),驱动再把命令流写入 NPU 的寄存器队列并启动执行。一次典型的推理调用链是:应用调用 TFLite Interpreter 的 Invoke(),Ethos-U Delegate 拦截可加速算子,把编译好的命令流交给 libethosu,libethosu 打开设备节点并下发,驱动完成 DMA 配置与中断处理,最后把结果写回内存。

理解这条链路的价值在于排障。如果推理结果全错,问题可能在模型量化;如果推理极慢,可能是命令流频繁提交导致 ioctl 开销过大;如果直接报设备打开失败,则要检查设备树中 NPU 节点是否使能、时钟和电源域是否被正确引用。下面是一段查看 NPU 设备与驱动状态的示例命令,输出为合理示例,实际以所用 BSP 为准:

# 查看 NPU 设备节点
ls -l /dev/ethosu
# crw------- 1 root root 240, 0 Jan  1 00:00 /dev/ethosu

# 查看内核日志中 NPU 驱动加载情况
dmesg | grep -i ethos
# [    2.134567] ethosu 38500000.npu: Ethos-U65 device registered
# [    2.135001] ethosu 38500000.npu: firmware version 1.0.6

# 查看设备树中 NPU 节点
find /proc/device-tree -name "*npu*" -maxdepth 2
# /proc/device-tree/soc@0/npu@38500000

Linux BSP 集成:设备树、时钟与电源

NPU 能否正常工作,取决于 BSP 是否把它正确“接”进系统。在设备树中,NPU 节点需要声明寄存器基地址、中断号、时钟源、电源域以及 DMA 一致性属性。以 i.MX 93 为例,NPU 节点通常挂在 soc@0 下,使用 clocks 引用 IMX93_CLK_NPU,并通过 power-domains 关联到对应的电源域。若这些引用缺失或写错,驱动 probe 会失败,表现为设备节点不存在或打开返回 ENODEV。

此外,NPU 与 CPU 共享 DDR 带宽,BSP 中的 DDR 频率调节策略、总线优先级配置都会影响推理延迟。在 i.MX 8M Plus 上,若同时运行摄像头采集和显示输出,NPU 可用带宽会被压缩,此时可通过调整 busfreq 或使用 devfreq 接口观察带宽占用。工程上建议在部署前先用 dd 或带宽测试工具测出 DDR 实际可用带宽,再与模型每层所需的访存量对比,判断是否存在带宽瓶颈。

Ethos-U65 的权重通常需要按编译器要求对齐到 16 字节边界,若手动搬运权重到自定义内存池,务必检查对齐,否则会出现难以定位的推理错误。

部署前的检查清单

综合以上内容,在 i.MX NPU 上开始部署前,建议按以下顺序确认:确认 SoC 型号与 NPU 类型,明确算力与精度支持;确认 BSP 版本与内核中 NPU 驱动是否已编译并加载;检查设备树节点、时钟、电源域是否完整;确认用户态运行时库与编译器版本匹配;用一个小型模型跑通端到端推理,观察设备节点访问、命令流提交和结果正确性。这五步能把大部分“环境问题”挡在模型调优之前,让后续的性能优化建立在可靠的基础之上。