3. 开发环境搭建与交叉编译

在 i.MX 平台上做 NPU 推理部署,第一步不是写模型代码,而是把“宿主机—工具链—目标板”这条链路打通。i.MX 系列(如 i.MX 8M Plus、i.MX 93)的 NPU 驱动、固件、用户态库(如 libethosu、OpenVX、ONNX Runtime EP)通常由 NXP 的 Yocto BSP 层提供,因此最稳妥的方式是用 Yocto/OpenEmbedded 构建与目标板一致的根文件系统,再基于同一套 BSP 生成 SDK 做交叉编译。这样能避免“宿主机编译的库版本与板端不匹配”这类最常见的问题。

一、Yocto/OpenEmbedded 构建:从 BSP 到可启动镜像

Yocto 的核心是“层(layer)+ 配方(recipe)+ 配置(conf)”的组合。NXP 官方发布的是 imx-manifest(repo 清单),它把 poky、meta-openembedded、meta-freescale、meta-imx 等层按固定版本锁定。典型流程如下:

# 1. 安装依赖(Ubuntu 22.04 示例)
sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential \
  chrpath socat cpio python3 python3-pip python3-pexpect xz-utils \
  debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa \
  libsdl1.2-dev pylint xterm zstd liblz4-tool

# 2. 拉取 NXP BSP 清单
mkdir imx-yocto-bsp && cd imx-yocto-bsp
repo init -u https://github.com/nxp-imx/imx-manifest \
  -b imx-linux-kirkstone -m imx-5.15.71-2.2.0.xml
repo sync -j8

# 3. 初始化构建目录(选择 i.MX 8M Plus EVK)
DISTRO=fsl-imx-xwayland MACHINE=imx8mp-lpddr4-evk \
  source imx-setup-release.sh -b build-npu

# 4. 构建带 NPU 支持的镜像
bitbake imx-image-full

这里有两个关键点。第一,MACHINE 必须与目标板一致,否则内核设备树、NPU 固件路径都会错。第二,imx-image-full 默认包含 TensorFlow Lite、ONNX Runtime、OpenVX 等组件,但 NPU 相关的 ethos-u 驱动和固件是否启用,取决于 local.conf 中的 DISTRO_FEATURES 与 MACHINE_FEATURES。建议在 conf/local.conf 中显式加入:

DISTRO_FEATURES:append = " opengl vulkan"
IMAGE_INSTALL:append = " ethos-u-driver ethos-u-firmware"

构建完成后,产物位于 tmp/deploy/images/imx8mp-lpddr4-evk/,其中 .wic.gz 是可直接烧录到 SD 卡的完整镜像,.dtb 是设备树,Image 是内核。用 bmaptool 或 dd 写入 SD 卡后,插板启动,串口应能看到 U-Boot 和内核日志。

二、SDK 安装:把“板端环境”搬到宿主机

Yocto 构建完成后,用 bitbake imx-image-full -c populate_sdk 生成 SDK 安装包,通常是一个 .sh 自解压脚本。安装时指定路径:

./fsl-imx-xwayland-glibc-x86_64-imx-image-full-cortexa53-crypto-imx8mp-lpddr4-evk-toolchain-5.15-kirkstone.sh \
  -d /opt/fsl-imx-xwayland/5.15-kirkstone
source /opt/fsl-imx-xwayland/5.15-kirkstone/environment-setup-cortexa53-crypto-poky-linux

执行 environment-setup 后,环境变量 CC、CXX、SDKTARGETSYSROOT 会被设置。此时 $CC 已经指向 aarch64-poky-linux-gcc,并自动带上 --sysroot=$SDKTARGETSYSROOT。这意味着你编译时链接的头文件和库,与目标板根文件系统中的完全一致,这是交叉编译不“翻车”的根本保障。

三、交叉编译工具链配置与目标板连接

SDK 安装后,交叉编译一个 NPU 推理程序的标准写法是:

$CC -O2 -march=armv8-a+crc -o npu_demo npu_demo.c \
  -lrt -lpthread -lOpenCL -lVX -lNNRT

其中 -lVX 对应 OpenVX,-lNNRT 对应 NXP 的 NNRT 运行时。若使用 ONNX Runtime,则链接 -lonnxruntime,并确保 libonnxruntime.so 已随镜像部署到板端 /usr/lib。编译产物用 file npu_demo 检查,应显示 ARM aarch64。

目标板连接有三种常用方式:串口(调试)、SSH(部署)、ADB(快速推送)。串口用 picocom -b 115200 /dev/ttyUSB0 查看启动日志;SSH 用 ssh root@192.168.1.100(板端默认 IP 视网络配置而定);文件传输用 scp npu_demo root@192.168.1.100:/home/root/。若板端没有 SSH 服务,可在 Yocto 的 local.conf 中加入 IMAGE_INSTALL:append = " openssh-sftp-server" 重新构建。

i.MX NPU 开发环境链路:Yocto 构建 → SDK → 交叉编译 → 目标板 宿主机(x86_64 Ubuntu) Yocto / OpenEmbedded 构建 repo init -u imx-manifest -b kirkstone MACHINE=imx8mp-lpddr4-evk bitbake imx-image-full SDK 安装 populate_sdk → .sh 自解压包 -d /opt/fsl-imx-xwayland/5.15-kirkstone source environment-setup-cortexa53-crypto 交叉编译 $CC -O2 -march=armv8-a+crc -lOpenCL -lVX -lNNRT -lonnxruntime file npu_demo → ARM aarch64 --sysroot=$SDKTARGETSYSROOT 目标板(i.MX 8M Plus EVK) 根文件系统(与 SDK 同源) /usr/lib/libethosu.so · libVX.so · libNNRT.so /lib/firmware/ethosu_firmware.bin 目标板连接方式 串口:picocom -b 115200 /dev/ttyUSB0 SSH:ssh root@192.168.1.100 SCP:scp npu_demo root@192.168.1.100:/home/root/ ADB:adb push npu_demo /home/root/ 板端运行与验证 ./npu_demo model.tflite dmesg | grep ethosu → 驱动加载成功 cat /sys/kernel/debug/ethosu/status 推理耗时 / 帧率 / NPU 占用率 部署 关键原则:SDK 与目标板根文件系统必须来自同一次 Yocto 构建,否则库版本不一致会导致 NPU 运行时链接失败

四、常见问题与判断依据

交叉编译后运行报 error while loading shared libraries: libNNRT.so: cannot open shared object file,说明板端缺少对应库,需检查 IMAGE_INSTALL 是否包含 nnrt 或 onnxruntime。若报 Illegal instruction,通常是编译时 -march 与目标 CPU 不匹配,i.MX 8M Plus 的 Cortex-A53 应使用 armv8-a+crc。若 NPU 无响应,先确认 dmesg | grep ethosu 是否有固件加载日志,再检查 /dev/ethosu0 设备节点是否存在。这些判断依据能帮助你在环境搭建阶段快速定位问题,而不是把时间耗在模型本身。

建议把 source environment-setup 写入 ~/.bashrc 或单独的 env.sh,避免每次打开终端都要重新设置。同时用 echo $CC 确认工具链已生效,再开始编译。