2. CI基础回顾

设备树(Device Tree),说白了就是一份硬件描述清单。Linux内核通过它才知道你板子上接了哪些外设、内存多大、中断怎么连的。我刚接触Jacinto平台时,第一件事就是翻它的设备树——嗯,不看这个,你连GPIO都点不亮。

2.1 设备树语法与绑定规则

设备树的语法其实不复杂。一个节点就是一个设备,属性就是它的配置参数。我习惯把设备树想象成JSON的树形版本,只不过它用文本文件写,编译后变成二进制。

核心语法三要素:

  • 节点(Node):用 / { } 定义,根节点是 /
  • 属性(Property):键值对,值可以是字符串、整数、字节数组
  • 标签(Label):用 &label 引用,方便覆盖或追加
/dts-v1/;

/ {
    model = "TI Jacinto 7 EVM";
    compatible = "ti,j721e-evm", "ti,j721e";

    memory@80000000 {
        device_type = "memory";
        reg = <0x00000000 0x80000000>;
    };

    chosen {
        stdout-path = &main_uart0;
    };
};

绑定规则(Binding)是设备树的“契约”。每个外设驱动都会声明它需要哪些属性。比如I2C控制器,必须有 #address-cells 和 #size-cells,否则驱动解析地址时会出错。我在项目中遇到过有人漏写 #address-cells,结果I2C设备一个都枚举不出来——查了两天才发现。

属性 含义 必填
compatible 驱动匹配字符串 是
reg 寄存器地址/长度 是
interrupts 中断号与触发类型 视设备而定
clocks 时钟句柄 视设备而定

我的习惯:写设备树前,先去内核源码 Documentation/devicetree/bindings/ 下翻对应厂商的YAML绑定文件。TI的绑定文档写得还算清楚,照着填不容易翻车。

2.2 Jacinto平台设备树结构分析

Jacinto 7(DRA829/TDA4VM)的设备树结构,我总结为“三层嵌套+两大片区”。

  • 第一层:SoC dtsi(如 k3-j721e.dtsi)——定义芯片内部所有IP,包括Cortex-A72、Cortex-R5F、C7x DSP、各种外设控制器。这一层基本不动。
  • 第二层:SOM dtsi(如 k3-j721e-som-p0.dtsi)——定义板级模块,比如DDR颗粒型号、eMMC、PMIC。这一层偶尔改。
  • 第三层:Board dts(如 k3-j721e-evm.dts)——最终板级配置,GPIO复用、外设使能、引脚mux全在这里。这一层天天改。

两大片区指的是 主域(Main Domain) 和 MCU域(MCU Domain)。Jacinto把安全关键的外设放在MCU域,由R5F管理;主域跑Linux。设备树里用 &main_* 和 &mcu_* 区分。

// 主域UART
&main_uart0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&main_uart0_pins_default>;
};

// MCU域I2C
&mcu_i2c0 {
    status = "okay";
    clock-frequency = <400000>;
};

注意:Jacinto的引脚复用(Pinmux)必须在设备树里配好,否则外设不工作。我曾经漏配了一组SPI的引脚,结果SPI通信一直超时——不是驱动问题,是引脚根本没连上。

2.3 设备树编译与反编译

设备树源文件(.dts)需要编译成二进制(.dtb)才能被内核加载。工具链很简单:

# 编译
dtc -I dts -O dtb -o k3-j721e-evm.dtb k3-j721e-evm.dts

# 反编译(调试时常用)
dtc -I dtb -O dts -o k3-j721e-evm.dts k3-j721e-evm.dtb

# 查看dtb内部结构
fdtdump k3-j721e-evm.dtb | less

我个人调试时最常用的是反编译。内核启动后,/sys/firmware/devicetree/base 下能看到实际生效的设备树。但有时候内核加载的dtb和源码不一致——反编译一下,一对比就知道问题出在哪。

避坑指南:dtc版本不同,编译结果可能有差异。我建议用内核自带的dtc(scripts/dtc/),别用系统自带的。曾经有个bug,系统dtc 1.5.0编译出来的dtb,内核解析中断时顺序错了——升级到1.6.0才解决。

2.4 设备树调试技巧:Overlay与动态加载

设备树覆盖(Overlay)是调试利器。你不需要重新编译整个dtb,只需要写一个overlay片段,动态加载到内核里。我经常用它来快速验证外设配置。

// my_spi_test.dts
/dts-v1/;
/plugin/;

&main_spi0 {
    status = "okay";
    #address-cells = <1>;
    #size-cells = <0>;

    spidev@0 {
        compatible = "rohm,dh2228fv";
        reg = <0>;
        spi-max-frequency = <10000000>;
    };
};

加载方法:

  1. 编译overlay:dtc -@ -I dts -O dtb -o my_spi_test.dtbo my_spi_test.dts
  2. 复制到 /lib/firmware/
  3. 通过configfs加载:mkdir /sys/kernel/config/device-tree/overlays/spi_test
    cat my_spi_test.dtbo > /sys/kernel/config/device-tree/overlays/spi_test/dtbo

动态加载的典型场景:

  • 调试新外设时,先overlay验证,确认无误再合入主dts
  • 运行时切换引脚复用功能(比如GPIO和PWM互切)
  • 热插拔FPGA子卡,动态加载对应设备树

卸载也很简单:rmdir /sys/kernel/config/device-tree/overlays/spi_test。内核会自动移除对应设备。

我曾经踩过的坑:overlay里引用的标签(&main_spi0)必须在基础dtb中存在。如果基础dtb里这个节点被删了(status = "disabled"且没有保留phandle),overlay加载时会报“symbol not found”。解决办法:基础dtsi里给所有可能被覆盖的节点加上标签,哪怕暂时不用。

知识结构总览

下面这张图是我自己整理的设备树调试知识体系,你可以把它当作快速索引。

设备树调试知识体系 设备树(DT) 语法与绑定规则 Jacinto平台结构 编译与反编译 节点/属性 compatible reg/中断 SoC dtsi SOM dtsi Board dts dtc编译 反编译 fdtdump 调试技巧:Overlay 编译 .dtbo configfs加载 rmdir卸载 核心原则:先overlay验证 → 再合入主dts → 最后提交代码 避免反复编译整个dtb,提升调试效率