4. Kria Ubuntu系统定制与设备树配置

Kria SOM 出厂通常预装 Kria Ubuntu(基于 Canonical 的 Ubuntu 22.04 LTS 与 Xilinx/AMD 内核),但产品化时几乎不可能直接沿用默认镜像:根文件系统需要裁剪与固化,外设需要按载板重新使能,引脚复用需要与硬件原理图对齐,启动链与固件也要能随产品版本迭代。本节围绕这四件事展开,重点讲清“改什么、在哪改、改完怎么验证”。

一、Ubuntu rootfs 定制:从通用镜像到产品镜像

Kria 的 Ubuntu rootfs 由 Canonical 维护,通过 ubuntu-image 或 AMD 提供的 kria-ubuntu 构建脚本生成。产品化定制一般分三层:

  • 包裁剪:移除 snapd、桌面组件、示例应用,保留 systemd、网络管理、SSH 与必要的运行时库。可用 --snap 与 --extra-ppas 控制,或在 chroot 中执行 apt purge。
  • 文件注入:把产品应用、systemd 服务、udev 规则、网络配置写入 rootfs。推荐用 overlay/ 目录 + rsync 注入,避免直接改镜像。
  • 只读与 A/B 升级:量产设备建议将 rootfs 挂载为只读,配合 overlayfs 或 A/B 双分区,通过 rauc 或 swupdate 做原子升级。

一个可复现的构建流程示例(示例参数,非实测结果):

# 1. 准备构建环境
sudo apt install ubuntu-image qemu-user-static
# 2. 使用 Kria 官方模型
ubuntu-image snap kria-ubuntu-22.04.model \
  --channel=stable \
  --snap=kria-config \
  -O kria-product.img
# 3. 挂载并注入产品文件
sudo losetup -Pf kria-product.img
sudo mount /dev/loop0p2 /mnt
sudo rsync -a overlay/ /mnt/
sudo chroot /mnt systemctl enable product-agent.service
sudo umount /mnt && sudo losetup -d /dev/loop0

验证要点:镜像能启动到登录提示;systemctl list-units --failed 无失败单元;产品服务随系统自启;df -h 显示 rootfs 占用符合预期(Kria KV260 上裁剪后通常可控制在 2 GB 以内)。

二、设备树覆盖(Device Tree Overlay)原理与编写

Kria 的 PL(可编程逻辑)与 PS(处理系统)外设通过设备树描述。由于载板差异大,AMD 采用 Device Tree Overlay 机制:基础设备树 system.dtb 描述 SOM 固定部分,overlay 以 .dtbo 形式在启动时叠加,动态添加或修改节点。Kria 的 kria-config 工具通过 /boot/firmware/config.txt 中的 dtoverlay= 行选择要加载的 overlay。

一个典型的 overlay 源文件结构如下(示例:使能 PL 端 AXI GPIO):

/dts-v1/;
/plugin/;

/ {
    compatible = "xlnx,zynqmp-sk-kv260", "xlnx,zynqmp";

    fragment@0 {
        target-path = "/";
        __overlay__ {
            axi_gpio_0: gpio@a0000000 {
                compatible = "xlnx,axi-gpio-2.0";
                reg = <0x0 0xa0000000 0x0 0x10000>;
                xlnx,all-inputs = <0x0>;
                xlnx,gpio-width = <0x4>;
                interrupts = <0 104 4>;
                interrupt-parent = <&gic>;
                clocks = <&clk 0>;
                status = "okay";
            };
        };
    };
};

编译与部署:

dtc -@ -I dts -O dtb -o axi-gpio.dtbo axi-gpio.dts
sudo cp axi-gpio.dtbo /boot/firmware/overlays/
echo "dtoverlay=axi-gpio" | sudo tee -a /boot/firmware/config.txt
sudo reboot

启动后验证:ls /proc/device-tree/ | grep gpio 应出现 gpio@a0000000;dmesg | grep axi-gpio 应显示驱动 probe 成功;gpiodetect 能看到对应 gpiochip。若节点未出现,优先检查 compatible 是否与驱动匹配、reg 地址是否与 PL 地址分配一致、status 是否为 okay。

三、外设使能与引脚复用配置

Kria 的 PS 外设(I2C、SPI、UART、以太网、USB)在基础设备树中默认可能处于 disabled。使能方式有两种:一是修改基础设备树,二是通过 overlay 覆盖 status。推荐后者,便于版本管理。

引脚复用(pinmux)由 pinctrl 节点描述。以 KV260 的 I2C1 为例,需在 overlay 中同时使能控制器并配置 MIO 引脚:

fragment@1 {
    target = <&i2c1>;
    __overlay__ {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_i2c1_default>;
    };
};

fragment@2 {
    target = <&pinctrl>;
    __overlay__ {
        pinctrl_i2c1_default: i2c1-default {
            mux {
                groups = "i2c1_0_grp";
                function = "i2c1";
            };
            conf {
                groups = "i2c1_0_grp";
                bias-pull-up;
                slew-rate = <0>;
            };
        };
    };
};

关键点:groups 名称必须与 SoC pinctrl 驱动中定义的一致(ZynqMP 的 pinctrl 定义在 drivers/pinctrl/pinctrl-zynqmp.c);bias-pull-up 对 I2C 是必需的;slew-rate 影响信号完整性,高速外设需按硬件手册调整。验证:i2cdetect -y 1 能扫描到从设备地址;cat /sys/kernel/debug/pinctrl/*/pinmux-pins 可确认引脚已绑定到 i2c1。

四、启动流程与固件更新

Kria 的启动链为:BootROM → FSBL(boot.bin)→ PMU Firmware → ATF(bl31.elf)→ U-Boot → Linux Kernel → rootfs。其中 boot.bin、bl31.elf、U-Boot 属于固件层,设备树与内核属于系统层。产品化时,固件更新必须与 rootfs 更新解耦,避免“刷一半变砖”。

Kria 提供 xkrtool 与 bootgen 用于生成 boot.bin;Ubuntu 侧可用 flash-kernel 或 fwupd 管理。推荐做法:

  • 固件(boot.bin、bl31、U-Boot)放在 QSPI 或 eMMC 的独立分区,升级时先写备份分区再切换。
  • 设备树与内核放在 /boot/firmware,随 rootfs 一起 A/B 升级。
  • 使用 rauc 时,system.conf 中定义 bootloader=uboot,通过 fw_setenv 切换 bootslot。

验证启动链是否完整:串口日志应依次出现 FSBL、PMU、ATF、U-Boot、Kernel 版本;cat /proc/device-tree/chosen/u-boot,version 可确认 U-Boot 版本;fw_printenv 可查看当前 bootslot 与升级状态。

Kria Ubuntu 启动链与设备树叠加关系 启动链(固件层 → 系统层) BootROM 片内固化 FSBL boot.bin PMU + ATF bl31.elf U-Boot bootslot 切换 Kernel Image + dtb rootfs Ubuntu 设备树叠加(U-Boot 阶段) system.dtb SOM 基础设备树 PS 外设默认状态 overlay.dtbo 载板差异 / PL 外设 pinmux / status 覆盖 合并后设备树 内核解析并 probe /proc/device-tree 配置入口与验证点 /boot/firmware/config.txt dtoverlay=axi-gpio /boot/firmware/overlays/ *.dtbo 文件存放 dmesg | grep probe 驱动加载确认 /proc/device-tree/ 节点存在性检查 固件更新策略(A/B 双分区) Slot A(当前) boot.bin + rootfs Slot B(备份) 待升级 / 回滚 rauc / fw_setenv 切换 bootslot 验证启动 串口日志确认
设备树 overlay 的调试优先级:先确认 compatible 与驱动匹配,再确认 reg 地址与 PL 地址分配一致,最后检查 pinctrl 的 groups 名称是否与 SoC 驱动定义一致。三者中任一不匹配,节点都不会 probe 成功。
固件更新时不要同时写 QSPI 与 eMMC 的同一分区;A/B 切换前务必确认备份分区内容完整。Kria 的 BootROM 只认 QSPI 或 SD 的 boot.bin,若该文件损坏,设备将无法进入 U-Boot,只能通过 JTAG 恢复。

把上述四件事串起来,产品化流程可以概括为:先用 ubuntu-image 生成裁剪后的 rootfs,再按载板编写 overlay 使能外设与 pinmux,接着用 bootgen 生成含 FSBL/ATF/U-Boot 的 boot.bin,最后通过 A/B 分区与 rauc 实现可回滚的固件更新。每一步都应在串口日志与 /proc/device-tree 中留下可验证的证据,而不是仅凭“能启动”就判定成功。