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" 重新构建。
四、常见问题与判断依据
交叉编译后运行报 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 确认工具链已生效,再开始编译。