跳转到主要内容

如何为现有节点、子节点和可编程逻辑(PL)自定义添加或修改设备树

本文作者:AMD 工程师 Aravind Babu

下文讨论如何在构建时和运行时为处理器系统 (PS) 的各组成部分或各可编程逻辑 (PL) IP 添加或修改设备树更改,以及如何调试与设备树相关的问题。

1.修改设备树

  • 如何更新现有节点并为可编程逻辑 (PL) 添加自定义节点。
  • 这在集成新 IP 块或调整硬件描述时很有用。

2.设备树属性

  • 在 U-Boot 和 Linux 中读取和写入属性,这对运行时配置至关重要。

3.调试技巧

  • 用于对设备树问题进行故障排除的技术,可以帮助解决启动或外设初始化问题。

4.HSI 命令

  • 使用 Xilinx Hardware Server Interface(赛灵思硬件服务器接口)命令调试特定 .xsa 文件的设备树,该文件将硬件设计与软件配置联系起来。

1.为现有 PL 节点和自定义 PL 节点修改设备树

 1.1 了解设备树结构

Zynq/ZynqMP/Versal 的设备树是使用 PetaLinux 工具从 .xsa 文件自动生成的。其中包括:

  • SoC 外设:UART、SPI、I2C 等
  • 存储器节点
  • PL 节点(如果在 .xsa 中存在)

1.2 设备树文件的位置

  • 在 PetaLinux 中:
    project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
  • 在 Yocto 中:
    <layer>/recipes-bsp/device-tree/files/*.dtsi
  • 独立设备树仓库:
    从以下位置克隆:https://github.com/Xilinx/device-tree-xlnx

1.3 编辑现有节点示例

要修改 dwc3 节点,只需在 project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi 文件中添加以下节点。

&dwc3_0 {
   status = "okay";
   dr_mode = "peripheral";
};

1.4 添加自定义 PL 节点

如果您的 PL 中有自定义 IP 块(例如 axi_gpio_0),其节点可能已经存在于从 .xsa 生成的 pl.dtsi 文件中。

要修改或添加属性:

  • 请勿直接编辑 pl.dtsi;它是从 .xsa 自动生成的。
  • 在 system-user.dtsi 中覆盖 PL 节点:
&axi_gpio_0 {
    compatible = "xlnx,xps-gpio-1.00.a";
    gpio-controller;
    #gpio-cells = <2>;
    status = "okay";
};

注释:检查是否在此路径中自动生成该节点。仅限该节点存在时才从 system-user.dtsi 修改或添加节点,以避免构建错误。

要手动添加新的 PL 节点:

axi_custom_ip: custom_ip@a0000000 {
    compatible = "vendor,custom-ip";
    reg = <0x0 0xa0000000 0x0 0x1000>;
    clocks = <&zynqmp_clk 71>;
    status = "okay";
};

如何添加子节点

&spi0 {
    status = "okay";
    spidev@0 {
        compatible = "spidev";
        reg = <0>;
        spi-max-frequency = <1000000>;
    };
};

注释:在更新之前,检查子节点的现有节点并将其用作参考。

删除设备树节点属性

示例 1:您需要移除以下节点中的 phys:

usb0 {
    #address-cells = <0x2>;
    #size-cells = <0x2>;
    status = "okay";
    compatible = "xlnx,zynqmp-dwc3";
    reg = <0x0 0xff9d0000 0x0 0x100>;
    clock-names = "bus_clk", "ref_clk";
    #stream-id-cells = <0x1>;
    iommus = <0x8 0x860>;
    power-domains = <0x31>;
    ranges;
    nvmem-cells = <0x20>;
    nvmem-cell-names = "soc_revision";
    clocks = <0x3 0x20 0x3 0x22>;
    pinctrl-names = "default";
    pinctrl-0 = <0x32>;
    dwc3@fe200000 {
        compatible = "snps,dwc3";
        status = "okay";
        reg = <0x0 0xfe200000 0x0 0x40000>;
        interrupt-parent = <0x4>;
        interrupts = <0x0 0x41 0x4 0x0 0x45 0x4>;
        snps,quirk-frame-length-adjustment = <0x20>;
        snps,refclk_fladj;
        dr_mode = "host";
        snps,usb3_lpm_capable;
        phy-names = "usb3-phy";
        phys = <0x33 0x4 0x0 0x2 0x18cba80>;
    };
};

解决方案:在 system-user.dtsi 中添加以下内容

&dwc3 {
    /delete-property/ phy-names;
    /delete-property/ phys;
};

 

在设备树中移除 iic_main 节点。

# pl.dtsi 内容

iic_main: i2c@40800000 {
    #address-cells = <1>;
    #size-cells = <0>;
    clock-frequency = <100000000>;
    clocks = <&clk_bus_0>;
    compatible = "xlnx,xps-iic-2.00.a";
    interrupt-names = "iic2intc_irpt";
    interrupt-parent = <µblaze_0_axi_intc>;
    interrupts = <5 2>;
    reg = <0x40800000 0x10000>;
};

# system-top.dts 内容

aliases {
    ethernet0 = &axi_ethernet;
    i2c0 = &iic_main;
    serial0 = &rs232_uart;
    spi0 = &axi_qspi;
};

解决方案:在 system-user.dtsi 中添加以下内容

注释:在删除该节点之前,请确保已删除别名和其他引用。

解决方案

/include/ "system-conf.dtsi"
/ {
    aliases {
        /delete-property/ i2c0;
    };
};

&amba_pl {

    /delete-node/ i2c@40800000;
};

2.在 U-Boot 和 Linux 中读取/写入设备树属性

U-Boot 使用扁平设备树 (FDT)。您可以使用 fdt 命令对其进行操作。 

读取属性

fdt addr ${fdtcontroladdr}

(fdt get value <var> <path> <property> )

例如:fdt get value val /axi/ethernet@ff0c0000 mac-address

写入属性

fdt set <path> <property> <value>

例如:fdt set /axi/ethernet@ff0c0000 mac-address [00 0a 35 00 01 02]

列出节点

fdt list /

fdt list /axi

使用 FDT 从 U-boot 更改存储器节点。

存储器配置设备树:

方法 1:在 <plnx-proj-root>/project-spec/meta-user/recipes-bsp/devcie-tree/files/system-user.dtsi 中添加以下内容

/* 4.0GB configurations */

/ {

    memory {

    device_type = "memory";

    reg = <0x0 0x0 0x0 0x80000000>, <0x8 0x0 0x0 0x80000000>;

    };

}

ZynqMP> fatload mmc 0 0x1000000 image.ub

reading image.ub

24222740 bytes read in 3131 ms (7.4 MiB/s)

ZynqMP> iminfo 0x1000000

## Checking Image at 01000000 ...

   FIT image found

   FIT description: U-Boot fitImage for plnx_aarch64 kernel

....  

    Image 1 (fdt@0)

     Description: Flattened Device Tree blob

     Type: Flat Device Tree

     Compression: uncompressed

     Data Start: 0x01c6dbd0

     Data Size: 41389 Bytes = 40.4 KiB

     Architecture: AArch64

     Hash algo: sha1

     Hash value: 90651e85c42e7316fc710b9d7dc23f66fbf055d3

....

ZynqMP> fdt addr 0x01c6dbd0

ZynqMP> fdt

fdt - flattened device tree utility commands

Usage:

fdt addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>

fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active

fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed

fdt print  <path> [<prop>]          - Recursive print starting at <path>

fdt list   <path> [<prop>]          - Print one level starting at <path>

fdt get value <var> <path> <prop>   - Get <property> and store in <var>

fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>

fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>

fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>

fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]

fdt mknode <path> <node>            - Create a new node after <path>

fdt rm     <path> [<prop>]          - Delete the node or <property>

fdt header                          - Display header info

fdt bootcpu <id>                    - Set boot cpuid

fdt memory <addr> <size>            - Add/Update memory node

fdt rsvmem print                    - Show current mem reserves

fdt rsvmem add <addr> <size>        - Add a mem reserve

fdt rsvmem delete <index>           - Delete a mem reserves

fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree

ZynqMP> fdt move 0x01c6dbd0 0x04000000

ZynqMP> fdt addr 0x04000000

ZynqMP> fdt print /memory

memory {

        device_type = "memory";

        reg = <0x00000000 0x00000000 0x00000000 0x80000000 0x00000008 0x00000000 0x00000000 0x80000000>;

};

ZynqMP> fdt set /memory reg <0x0 0x0 0x0 0x60000000 0x00000008 0x00000000 0x0 0x80000000>

ZynqMP> fdt print /memory

memory {

        device_type = "memory";

        reg = <0x00000000 0x00000000 0x00000000 0x60000000 0x00000008 0x00000000 0x00000000 0x80000000>;

};

从 Linux 读取设备树

# 读取属性

cat /proc/device-tree/<node>/<property>

例如:cat /proc/device-tree/axi/ethernet@ff0c0000/mac-address

# 反编译 dtb 用于调试

dtc -I dtb -O dts -o extracted.dts /proc/device-tree

注释:/proc/device-tree 是当前设备树的实时表示法。

3.设备树问题的调试技巧

常见症状

  • 器件节点不执行探测(未绑定任何驱动程序)。
  • 内核 dmesg 显示“OF: no match for compatible.”
  • /sys/class/、/dev/ 等中未出现器件

调试检查表

  1. 检查节点中的 status 是否为“okay”。
  2. 确保 compatible 字符串与驱动程序的 of_match_table 匹配。
  3. 验证 reg、interrupts 和 clocks 的值。
  4. 确保节点未被覆盖;后面的 include 可以覆盖先前的定义。
  5. 在内核中启用 CONFIG_OF_ 选项。
  6. 重新编译 DTS 并更新 DTB。

反编译并检查 DTB

dtc -I dtb -O dts -o out.dts system.dtb

在 Linux 中检查驱动程序绑定

dmesg | grep -i <compatible-string>

ls /sys/bus/platform/devices/

4.通过 HSI 从 XSA 文件调试设备树

  hsi 用于从 .xsa 中提取硬件信息,以进行设备树生成/调试。

使用 hsi 查询 PL 地址映射、时钟、外设:

open_hw_design design.xsa

get_cells -hier

report_property [get_cells axi_gpio_0]

有助于确保设备树中地址和时钟信息的准确性。

 4.1 基本 HSI 脚本示例

打开 XSA:

open_hw_design <path_to_xsa_file>

set cpu [get_cells -hier -filter {IP_TYPE == "psu_cortexa53"}]

#2.  列出可用外设(列出 IP):

get_cells -hier

获取 IP 属性

get_property IP_NAME [hsi get_cells]

# 检查 PL IP 的属性

get_property CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ [get_os]

4.2 导出设备树

open_hw_design my_design.xsa

set_repo_path <path_to_device_tree_repo>

create_sw_design my_dts dts

set_property CONFIG.dt_overlay true [get_os]

generate_target -dir ./output_dir

这样会生成:

  • system.dts
  • pl.dtsi
  • pcw.dtsi

4.3 确认设备树绑定

您可以编写 Tcl 脚本来执行以下操作:

  • 交叉检查寄存器地址
  • 检查时钟分配
  • 列出存储器映射
  • 列出状态为“disabled”的 IP