本文作者: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 treeZynqMP> 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/ 等中未出现器件
调试检查表
- 检查节点中的 status 是否为“okay”。
- 确保 compatible 字符串与驱动程序的 of_match_table 匹配。
- 验证 reg、interrupts 和 clocks 的值。
- 确保节点未被覆盖;后面的 include 可以覆盖先前的定义。
- 在内核中启用 CONFIG_OF_ 选项。
- 重新编译 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