IMX93 运行NPU demo,解决1G内存下的运行问题
前言
IMX93在2G内存情况下NPU是正常的,在1G的情况下就会推理超时,然后卡住。解决下这个问题以及记录下NPU的操作步骤。使用的是NXP yocto自带的NPU demo。
步骤
我是 6.6.36_2.1.0 的yocto release版本,主要参考L6.6.36_2.1.0-DOCS.zip里面的UG10166_i.MX_Machine_Learning_User's_Guide.pdf文档。
如何下载可以参考我写的 https://www.cnblogs.com/allalonewithyou/p/23223977
先说IMX93 的 2G内存 正常情况,把步骤说清楚:
1. 转化模型
cd /usr/bin/tensorflow-lite-2.16.2/examples

要用Dedicated_Sram模式:
vela --accelerator-config ethos-u65-256 --system-config Ethos_U65_High_End --memory-mode Dedicated_Sram --config /usr/lib/python3.12/site-packages/ethosu/config_files/Arm/vela.ini mobilenet_v1_1.0_224_quant.tflite
可以使用默认的配置,命令简化:
# 把CPU的模型转为NPU可以运行的模型
vela mobilenet_v1_1.0_224_quant.tflite
会输出到output文件夹下。
2. 运行
# ethosu delegate + vela 模型
./label_image -m output/mobilenet_v1_1.0_224_quant_vela.tflite \
-i grace_hopper.bmp -l labels.txt \
--external_delegate_path=/usr/lib/libethosu_delegate.so
对比
识别demo的图片,类型是军装。

CPU 推理:
cd /usr/bin/tensorflow-lite-2.16.2/examples
./label_image -m mobilenet_v1_1.0_224_quant.tflite -i grace_hopper.bmp -l labels.txt

NPU 推理:

127ms 对 3.7ms ,30倍的速度
测试模型
# 测试模型
./benchmark_model --graph=mobilenet_v1_1.0_224_quant.tflite
1G的情况
内存保留配置之前是官方写的:

现在改为:
- ethosu_mem: ethosu_region@C0000000 {
+ /*
+ * 必须落在 M33/NPU 的 Non-Secure DRAM 段内(i.MX93 RM "M33 memory map"):
+ * NS : 0x8000_0000-0x8FFF_FFFF / 0xA000_0000-0xAFFF_FFFF / 0xC000_0000-0xCFFF_FFFF
+ * S别名: 0x9000_0000 / 0xB000_0000 / 0xD000_0000 ← 绝不能用
+ * A55 视角里 0xB000_0000 只是普通 DRAM,但在 M33/NPU 视角它是第 2 段(0xA000_0000)的
+ * Secure 别名 ⇒ 固件按该地址取命令流/权重会读到别的物理内存或被拒 ⇒ 推理失败。
+ * 原值 0xB0000000 就是踩了这个坑(1G 板必失败;2G 板换该 DTB 同样失败,已实测)。
+ * 这里取第 1 段 NS 内的 0x88000000(2MB 对齐、避开内核 0x80410000-0x8300d000),1G/2G 通用。
+ */
+ ethosu_mem: ethosu_region@88000000 {
compatible = "shared-dma-pool";
reusable;
- reg = <0x0 0xB0000000 0x0 0x8000000>;
+ reg = <0x0 0x88000000 0x0 0x8000000>;
};
发现是内存大小的问题,是通过对比发现的,使用相同的1G的保留内存的设备树配置到2G的板子上,运行NPU也不行,就说明是这个设备树配置的问题。
看IMX93的RM手册中system memory map:

NS/S 只是 TrustZone 视图,且只有 0x8000_0000 以下才有 NS/S 别名,所以 DRAM 段全是 (NS) —— 和 NPU 的问题无关。
看IMX93的RM手册,M33部分的system memory map:

上面代码注释部分写的很清楚。
参考
https://github.com/nxp-imx/ethos-u-firmware/tree/lf-6.6.36_2.1.0
IMX93RM.pdf
额外
调试记录
libtensorflow-lite + libethosu* + libethosu_delegate.so + ethosu_firmware 是必须兼容的,同一个yocto release里面即可。
root@imx93-11x11-lpddr4x-evk:/usr/bin/tensorflow-lite-2.16.2/examples# strings output/mobilenet_v1_1.0_224_quant_vela.tflite | grep -iE "ethos-u|vela|OfflineMemory"
OfflineMemoryAllocation
vela_version
Vela 3.12.0 Optimised
ethos-u
固件位置:
https://github.com/nxp-imx/ethos-u-firmware/tree/lf-6.6.36_2.1.0
相关的APP可以下载源码编译,查看UG10166_i.MX_Machine_Learning_User's_Guide.pdf文档:

# 开启关闭ethos
echo start > /sys/class/remoteproc/remoteproc0/state
echo stop > /sys/class/remoteproc/remoteproc0/state
保留内存:
# 查看保留内存
ls /sys/firmware/devicetree/base/reserved-memory/ 2>/dev/null || ls /proc/device-tree/reserved-memory/
# 查看寄存器 注意是小端的
od -An -tx4 /sys/firmware/devicetree/base/reserved-memory/rsc-table@2021e000/reg 2>/dev/null
网上有6.19+ mainline 的 accel/ethosu + Mesa/Teflon 可以让 A55 直连 NPU、完全不经 M33,还是开源的。
日志相关的:
# 内核需要开启 CONFIG_DYNAMIC_DEBUG 选项
# 全开动态调试(重启会丢)+ 清空日志
echo -n '+p' > /sys/kernel/debug/dynamic_debug/control
dmesg -C
可以交给AI,让AI反编译固件和so文件。
然后可以open下ethos节点:
/*
* ethosu_caps_probe.c —— 不接 M33 串口,直接从内核 ethоsu 驱动读 NPU 硬件能力
*
* 原理:Linux 驱动的 ETHOSU_IOCTL_CAPABILITIES_REQ 会让 CM33 固件去读 Ethos-U 的
* ID 寄存器(product / arch / macs_per_cc / cmd_stream_version / custom_dma),
* 再把结果回给用户态。所以这个程序能回答两件事:
* 1) NPU 到底有没有响应(读回来全是 0 ⇒ 供电/时钟/NPU 没起来);
* 2) 硬件自报的配置是什么(用来和模型里编进去的配置对比)。
*
* 交叉编译(主机上):
* aarch64-linux-gnu-gcc -O2 -Wall -o ethosu_caps_probe ethosu_caps_probe.c
* 拷到板子,重启后运行(需要 root):
* ./ethosu_caps_probe
*
* QY 板(i.MX93 = Ethos-U65-256)实测正确的值:
* version_status=1, product_major=6 (NPU_ID 的 part 号;U65 的 NPU_CONFIG[31:28]=1),
* macs_per_cc=8 (=2^8=256 MACs 的原始编码), cmd_stream_version=0, custom_dma=0,
* arch=1.0.6 (= Vela 的 ARCH_VER), driver 0.16.0。
* 硬件 NPU_CONFIG 读数为 0x10003008,与 Vela 编进模型的 optimizer config 字完全相同。
*/
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
/* ---- 与 drivers/staging/ethosu/uapi/ethosu.h 保持一致的 UAPI 定义 ---- */
struct ethosu_uapi_device_hw_id {
uint32_t version_status;
uint32_t version_minor;
uint32_t version_major;
uint32_t product_major;
uint32_t arch_patch_rev;
uint32_t arch_minor_rev;
uint32_t arch_major_rev;
};
struct ethosu_uapi_device_hw_cfg {
uint32_t macs_per_cc;
uint32_t cmd_stream_version;
uint32_t custom_dma;
};
struct ethosu_uapi_device_capabilities {
struct ethosu_uapi_device_hw_id hw_id;
struct ethosu_uapi_device_hw_cfg hw_cfg;
uint32_t driver_patch_rev;
uint32_t driver_minor_rev;
uint32_t driver_major_rev;
};
#define ETHOSU_IOCTL_BASE 0x01
#define ETHOSU_IOR(nr, type) _IOR(ETHOSU_IOCTL_BASE, nr, type)
#define ETHOSU_IOCTL_VERSION_REQ \
_IO(ETHOSU_IOCTL_BASE, 0x01)
#define ETHOSU_IOCTL_CAPABILITIES_REQ \
ETHOSU_IOR(0x02, struct ethosu_uapi_device_capabilities)
int main(void)
{
struct ethosu_uapi_device_capabilities c;
int fd, i, ret;
/* open() 会顺带把 CM33 固件拉起来(并等待 rpmsg 通道就绪) */
fd = open("/dev/ethosu0", O_RDWR | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "open /dev/ethosu0 failed: %s\n", strerror(errno));
return 1;
}
printf("/dev/ethosu0 opened\n");
/* 和官方 delegate 一样先发一次 version 请求(只为对齐,无返回数据) */
(void)ioctl(fd, ETHOSU_IOCTL_VERSION_REQ, NULL);
/* 内核里 capabilities 请求最多等 5s,失败会打 "Capabilities response timeout" */
for (i = 0; i < 3; i++) {
memset(&c, 0, sizeof(c));
ret = ioctl(fd, ETHOSU_IOCTL_CAPABILITIES_REQ, &c);
if (ret == 0)
break;
fprintf(stderr, "try %d: ioctl CAPABILITIES_REQ failed: %s\n",
i + 1, strerror(errno));
sleep(1);
}
if (ret != 0) {
fprintf(stderr,
"CAPABILITIES_REQ 一直失败 —— 看 dmesg 里 'Capabilities response timeout'\n");
close(fd);
return 2;
}
printf("hw_id : version_status=%u version=%u.%u product_major=%u arch=%u.%u.%u\n",
c.hw_id.version_status,
c.hw_id.version_major, c.hw_id.version_minor,
c.hw_id.product_major,
c.hw_id.arch_major_rev, c.hw_id.arch_minor_rev, c.hw_id.arch_patch_rev);
printf("hw_cfg: macs_per_cc=%u cmd_stream_version=%u custom_dma=%u\n",
c.hw_cfg.macs_per_cc, c.hw_cfg.cmd_stream_version, c.hw_cfg.custom_dma);
printf("driver: %u.%u.%u\n",
c.driver_major_rev, c.driver_minor_rev, c.driver_patch_rev);
printf("\n---- 解读提示 ----\n");
if (c.hw_id.version_status == 0 || c.hw_cfg.macs_per_cc == 0) {
printf("!! version_status/macs_per_cc 为 0:固件没读到 NPU 的 ID 寄存器,\n"
" 基本可以判定 NPU 没上电/没时钟(板级问题),而不是模型问题。\n");
} else {
printf("NPU 有响应。QY 板(i.MX93=Ethos-U65-256)正确的实测值:\n"
" version_status=1 product_major=6(NPU_ID) macs_per_cc=8(=256 MACs)\n"
" cmd_stream_version=0 custom_dma=0 arch=1.0.6 driver=0.16.0\n"
"硬件 NPU_CONFIG=0x10003008 与 Vela 编进模型的 config 字一致 => 配置不匹配已排除。\n");
}
close(fd);
return 0;
}
本文来自博客园,作者:杨旭0324,转载请注明原文链接:https://www.cnblogs.com/allalonewithyou/p/23240852,个人邮箱:yaonie4444@foxmail.com

浙公网安备 33010602011771号