Zynq设备树详细教程(实际操作指南)
1. 设备树基础概念
1.1 什么是设备树
设备树(Device Tree)是一种描述硬件资源的数据结构,它通过一种树状结构来描述系统中的硬件设备信息。在Zynq系统中,设备树主要用来描述:
- 处理器特性
- 内存布局
- 外设寄存器地址范围
- 中断连接
- 时钟信息
- 其他硬件特定参数
1.2 设备树的作用
设备树(Device Tree)用于描述硬件配置信息,将硬件描述与驱动代码分离。通过.dts
(设备树源文件)编译为.dtb
(二进制设备树),供Linux内核在启动时解析硬件信息。
1.3设备树文件类型
- DTS:设备树源文件,需手动编辑(如
system-top.dts
)。 - DTSI:设备树头文件,类似C语言的
.h
文件,供DTS文件包含(如pl.dtsi
、pcw.dtsi
)。 - DTB:编译后的二进制文件,由内核加载。
- DTC:设备树编译器,用于编译
.dts
为.dtb
。 - DTG:Xilinx设备树生成工具,自动生成设备树文件。
1.4 关键节点与属性
compatible
:驱动匹配的关键属性,格式为" manufacturer,device-type"
。reg
:描述硬件寄存器地址和大小。status
:设备状态(okay
或disabled
)。#address-cells
和#size-cells
:定义地址和大小的单元数。
2. Zynq设备树开发环境搭建
2.1 所需工具
- Xilinx SDK/Vitis - 包含设备树编译器(dtc)
- Petalinux工具 (可选) - 用于基于Linux的系统
- 文本编辑器 - 如VSCode、Vim等
2.2 设备树生成步骤
方法1:使用Vitis生成
- 导出XSA文件
在Vivado中完成硬件设计后,导出.xsa
文件(File → Export → Export hardware)。 - 生成设备树
- 打开Vitis,创建新工程,选择
File → New → Board Support Package
。 - 选择设备树模板,配置BSP参数(如控制台端口、启动参数)。
- 点击
Apply and Close
,生成设备树文件(system-top.dts
、pl.dtsi
、pcw.dtsi
等)。
- 打开Vitis,创建新工程,选择
方法2:使用SDK生成(传统方法)
- 配置设备树资源包
- 下载设备树资源包(如
device-tree-xlnx
),解压到SDK安装目录(如/Xilinx/SDK/data embeddedsw
)。 - 在SDK中,通过
Xilinx → repositories
添加资源包路径。
- 下载设备树资源包(如
- 创建BSP并生成设备树
- 新建BSP时选择
device_tree
选项,配置启动参数(如console=ttyPS0,115200 root=/dev/mmcblk0p2
)。 - 生成设备树文件后,将其放入Petalinux工程的
project-spec/meta-user/recipes-bsp/devicetree/files
目录。
- 新建BSP时选择
3. Zynq设备树结构解析
3.1 基本结构
一个典型的Zynq设备树文件结构如下:
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "xlnx,zynq-7000";
model = "Xilinx Zynq";
cpus {
/* CPU信息 */
};
memory {
/* 内存信息 */
};
amba {
/* 外设信息 */
};
};
3.2 关键节点详解
3.2.1 CPU节点
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0>;
clocks = <&clkc 3>;
clock-latency = <1000>;
cpu0-supply = <®ulator_vccpint>;
operating-points = <
/* kHz uV */
666667 1000000
333334 1000000
>;
};
};
3.2.2 内存节点
memory {
device_type = "memory";
reg = <0x0 0x40000000>;
};
3.2.3 AMBA总线节点
amba {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
ranges;
/* 外设定义在这里 */
};
3.3 设备树配置与修改
- 关键文件说明
system-top.dts
:主设备树文件,包含所有硬件配置。pl.dtsi
:描述PL(可编程逻辑)端的外设。pcw.dtsi
:描述PS(处理系统)端的外设。system-user.dtsi
:用户自定义修改的优先级最高。
- 修改设备树
- 启用外设:将
status
属性从disabled
改为okay
(如SD卡控制器)。 - 添加自定义外设:在
system-user.dtsi
中添加节点,例如SPI控制器:&spiflash { compatible = "xlnx,spiflash"; reg = ; status = "okay"; #address-cells = ; #size-cells = ; };
- 调整时钟与中断:配置
clocks
和interrupts
属性,确保与硬件设计一致。
- 启用外设:将
- 编译设备树
- 使用
dtc
工具编译:dtc -I dts -O dtb -o output.dtb input.dts
- 在Petalinux中,通过
petalinux-config
配置设备树路径,重新编译内核。
- 使用
4. 为自定义硬件添加设备树节点
4.1 查找硬件地址信息
在Vivado中完成设计后:
- 导出硬件定义(XSA文件)
- 在"Address Editor"标签页查看外设基地址和范围
4.2 添加自定义IP节点示例
假设我们有一个自定义的AXI GPIO IP核:
amba {
my_gpio: gpio@41200000 {
compatible = "xlnx,xps-gpio-1.00.a";
reg = <0x41200000 0x10000>;
interrupts = <0 29 4>;
interrupt-parent = <&intc>;
#gpio-cells = <2>;
gpio-controller;
xlnx,all-inputs = <0>;
xlnx,all-inputs-2 = <0>;
xlnx,all-outputs = <1>;
xlnx,all-outputs-2 = <0>;
xlnx,dout-default = <0x0>;
xlnx,dout-default-2 = <0x0>;
xlnx,gpio-width = <8>;
xlnx,gpio2-width = <32>;
xlnx,interrupt-present = <1>;
xlnx,is-dual = <0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
};
4.3 中断处理
Zynq中的中断定义:
intc: interrupt-controller@f8f01000 {
compatible = "arm,gic-400", "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0xF8F01000 0x1000>,
<0xF8F00100 0x100>;
};
中断说明:
- 第一个数字:中断类型(0=SPI, 1=PPI)
- 第二个数字:中断号
- 第三个数字:触发类型(1=上升沿, 4=高电平)
5 设备树与驱动绑定
- 驱动匹配规则
- 驱动通过
compatible
属性匹配设备树节点。例如,VDMA驱动需与设备树中的compatible
字段一致。 - 示例:
&vdma { compatible = "xlnx,vdma-1.00.a"; reg = ; status = "okay"; };
- 驱动通过
- 驱动代码适配
- 在驱动代码中,通过
of匹配表
(如of_match_table
)声明支持的compatible
字符串。 - 使用
of_getproperty
读取设备树中的属性值(如寄存器地址、中断号)。
- 在驱动代码中,通过
6. 编译和部署设备树
6.1 手动编译设备树
# 编译dts为dtb
dtc -I dts -O dtb -o devicetree.dtb system.dts
# 反编译dtb为dts
dtc -I dtb -O dts -o decompiled.dts devicetree.dtb
6.2 使用Petalinux工具
# 创建Petalinux项目
petalinux-create --type project --name myproject --template zynq
# 进入项目目录
cd myproject
# 更新设备树
petalinux-config --get-hw-description=<path_to_xsa>
# 编译
petalinux-build
生成的设备树文件位于images/linux/
目录下。
5.3 在U-Boot中加载设备树
# 从TFTP加载
tftp 0x1000000 devicetree.dtb
setenv fdtaddr 0x1000000
# 启动内核时指定设备树
bootm 0x3000000 - 0x1000000
7. 调试设备树
7.1 查看系统设备树
# 查看当前设备树
cat /proc/device-tree/
# 查看特定节点
cat /proc/device-tree/amba/my_gpio/reg
7.2 设备树覆盖
对于动态配置,可以使用设备树覆盖:
# 加载覆盖
mkdir /config/device-tree/overlays
echo my_overlay.dtbo > /config/device-tree/overlays/load
8. 实际案例:为Zynq添加自定义外设
8.1 案例描述
假设我们在PL部分实现了一个PWM控制器,通过AXI-Lite总线连接到PS,基地址为0x43C00000,中断号为61(SPI 29)。
8.2 设备树节点
amba {
pwm: pwm@43c00000 {
compatible = "mycompany,pwm-controller";
reg = <0x43c00000 0x10000>;
interrupts = <0 29 4>;
interrupt-parent = <&intc>;
#pwm-cells = <2>;
clocks = <&clkc 15>;
clock-names = "pwm_clk";
status = "okay";
};
};
8.3 驱动中使用设备树
在驱动代码中,可以通过以下方式获取设备树信息:
static int pwm_probe(struct platform_device *pdev)
{
struct resource *res;
void __iomem *base;
int irq;
// 获取内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
// 获取中断
irq = platform_get_irq(pdev, 0);
// 获取时钟
struct clk *clk = devm_clk_get(&pdev->dev, "pwm_clk");
// 其他初始化...
}
9. 常见问题解决
9.1 设备树语法错误
错误示例:
Error: system.dts:10.1-9 syntax error
FATAL ERROR: Unable to parse input tree
解决方法:
- 检查括号是否匹配
- 检查分号是否遗漏
- 使用
dtc
的-v
选项获取更详细错误信息
9.2 外设无法识别
可能原因:
- 寄存器地址不正确
- 中断号或中断父节点错误
- compatible字符串不匹配
调试方法:
# 检查设备是否成功注册
ls /sys/bus/platform/devices/
# 查看内核日志
dmesg | grep your_device
9.3 内存映射错误
症状:
Unable to handle kernel paging request at virtual address xxxxxxxx
解决方法:
- 检查设备树中的reg属性是否正确
- 确保地址范围不与其他设备重叠
9.4 启动失败或设备未识别
- 检查设备树中
status
是否为okay
。 - 验证
reg
地址与硬件设计是否一致。 - 使用
dtc
反编译.dtb
文件,对比修改前后的差异。
9.5 设备树语法错误
- 使用
dtc
检查语法:dtc -@ -I dts -O dtb -o test.dtb test.dts 2>&1
- 确保括号、引号和逗号闭合正确。
10. 最佳实践
- 模块化设计:将通用部分放在.dtsi文件中,板级特定配置放在.dts中
- 版本控制:将设备树文件纳入版本控制系统
- 注释:为每个节点添加详细注释
- 验证:在修改前后都进行完整系统测试
- 备份:保留工作版本的设备树文件
11. 进阶主题
11.1 设备树覆盖(DTO)
对于动态可重构系统,可以使用设备树覆盖:
// 覆盖片段
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&amba>;
__overlay__ {
new_device@43c10000 {
compatible = "xlnx,new-device";
reg = <0x43c10000 0x1000>;
};
};
};
};
11.2 使用DTB反推DTS
当原始DTS丢失时:
dtc -I dtb -O dts -o recovered.dts system.dtb
11.3 设备树与U-Boot
可以在U-Boot中修改设备树:
# 查看设备树
fdt print /amba
# 修改节点
fdt set /amba/serial@e0001000 status "disabled"
结论
Zynq设备树是连接硬件和软件的重要桥梁。通过本教程,您应该能够:
- 理解设备树的基本结构和语法
- 为自定义硬件添加设备树节点
- 编译和部署设备树
- 调试设备树相关问题
- 应用最佳实践进行设备树开发
随着经验的积累,您将能够更高效地使用设备树来配置和管理Zynq系统的硬件资源。