设备树是什么?
设备树简称DT,是用来描述硬件的数据结构。它可以由操作系统(如,Linux)读取,这样可以将操作系统与机器硬件分离,而不必对机器硬件的细节进行硬编码。
Linux上使用DT的基本功能包括:平台识别、运行时配置(如bootargs)和设备节点填充。
设备树的基础知识
设备树中的每个驱动程序或模块都由节点定义,其所有属性都在该节点下定义。基于驱动程序,它可以有子节点或父节点。
例如,由SPI总线连接的设备将以SPI总线控制器作为其父节点,而该设备将是SPI节点的一个子节点。根节点是所有节点的父节点。
典型的根节点通常包括如下的数据信息:
-
cpu节点信息
-
内存信息
-
可以选择配置的数据,如内核参数字符串和initrd映像的位置
-
别名
-
定义总线信息的节点
设备树的语法示例
文件名:zynqmp-example.dtsi
/ {
compatible = "xlnx,zynqmp";
#address-cells = <2>;
#size-cells = <2>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortexa53", "arm,armv8";
device-type = "cpu";
enable-method = "psci";
operating-points-v2 = <&cpu_opp_table>;
reg = <0x0>;
cpu-idle-states = <&CPU_SLEEP_0>;
};
cpu1: cpu@1 {
compatible = "arm,cortexa53", "arm,armv8";
device-type = "cpu";
enable-method = "psci";
operating-points-v2 = <&cpu_opp_table>;
reg = <0x1>;
cpu-idle-states = <&CPU_SLEEP_0>;
};
};
chosen {
bootargs = "earlycon clk_ignore_unused";
};
memory {
device-type = "memory";
reg = <0x0 0x0 0x0 0x80000000>, <0x00000008 0x0 0x0 0x80000000>;
};
amba_apu: amba_apu@0 {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0 0 0xffffffff>;
gic: interrupt-controller@f9010000 {
compatible = "arm,gic-400", "arm,cortex-a15-gic";
#interrupt-cells = <3>;
reg = <0x0 0xf9010000 0x10000>,
0x0 0xf9020000 0x20000>,
0x0 0xf9040000 0x20000>,
0x0 0xf9060000 0x20000>,
interrupt-controller;
interrupt-parent = <&gic>;
interrupts =<1 9 0xf04>;
};
};
amba: amba {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <2>;
ranges;
can0: can@ff060000 {
compatible = "xlnx,zynq-can-1.0";
clock-names = "can_clk", "pclk";
reg =<0x0 0xff060000 0x0 0x1000>;
interrupts = <0 23 4>;
interrupt-parent = <&gic>;
tx-fifo-depth = <0x40>;
rx-fifo-depth = <0x40>;
power-domains = <&pd_can0>;
};
};
设备树的属性
-
compatible:顶层的compatible属性定义了兼容的板卡,然后定义了兼容的SOC。
-
#address-cells:该属性表示用多少字长的地址来表示设备寄存器的基地址。如#address-cells
= <1>,表示该设备的基地址为32bit,即1个cell。 -
#size-cells:表示设备寄存器属性所占的大小(以地址单元来表示),如cpu也有地址,在4核的处理器中,它们可能被称为0、1、2和3。这可以看作是一个没有深度的一维数组,因此大小为0,即#
size -cells = <0>。 -
interrupt-controller:该属性为一个布尔属性,用来表示该节点是不是一个中断控制器。
-
#interrupt-cells:它表示中断源的属性的数量。这里为1,中断源只用IRQ数来表示。有些中断控制器可能还需要定义中断源的触发方式:边沿触发或电平触发等。这时#interrupt-cells属性的值就不再是1了。
-
interrupt-parent:定义了设备连接到的中断控制器。phandle指向当前节点的中断控制器。通常有一个顶层的interrupt-parent定义,指向主中断控制器。
MSS文件
MSS文件,即Microprocessor software specification
file,包含了定制操作系统、库和驱动的操作指令。
MDD文件
MDD文件,即Microprocessor Driver Definition file,包含了定制软件驱动的指令。
每一个设备驱动都有一个MDD文件和一个TCL文件与之关联。TCL文件根据MSS文件中不同的配置选项,使用MDD文件定制驱动。
每个驱动的源文件和MDD文件必须放在特定的目录中,以便能够找到这些文件和驱动。
驱动定义文件
驱动的定义包括定义一个数据定义文件MDD,和一个数据生成文件TCL。
-
数据定义文件:MDD文件(<driver_name>.mdd)包含了可配置的参数。
-
数据生成文件:TCL文件(<driver_name>.tcl)具有与MDD相同的文件名,使用MSS文件中的驱动的配置参数生成数据。
如何添加一个新的驱动到DTG中?
-
同步源码库repo https://github.com/xilinx/device-tree-xlnx
-
创建一个驱动名的文件夹,如device-tree-xlnx/axi_iic/
-
在该文件夹中添加一个数据文件夹,如device-tree-xlnx/axi_iic/data/
-
在该数据文件夹中创建一个MDD文件和TCL文件,如,axi_iic.tcl和axi_iic.mdd。
-
axi_iic.mdd文件的内容和语法如下:
device-tree-xlnx/axi_iic/data/axi_iic.mdd文件。
OPTION psf_version = 3.0;
BEGIN driver axi_iic
OPTION supported_peripherals = (axi_iic);--> the axi_iic is the IP_NAME which we get from the HDF file.
OPTION supported_os_types = (DTS);
OPTION driver_state = ACTIVE;
OPTION NAME = axi_iic;
END drive
- axi_iic.tcl文件的内容和语法如下,该文件包含了一个需要基于某些条件来设置的属性,而这些条件是我们使用HIS
APIs来升级节点属性时定义的。如下所示,我们更新了axi_iic调用泛型函数的时钟属性。
device-tree-xlnx/axi_iic/data/axi_iic.tcl文件。
if {[string match -nocase $proctype "psu_cortexa53"] } {
update_clk_node $drv_handle "s_axi_aclk"
}
在pl.dtsi中生成的节点如下:
pl.dtsi文件。
io_bd_iic_0: i2c@a1200000 {
#address-cells = <1>;
#size-cells = <0>;
clock-names = "s_axi_aclk";
clocks = <&misc_clk_0>;
compatible = "xlnx,xps-iic-2.00.a";
interrupt-names = "iic2intc_irpt";
interrupt-parent = <&gic>;
interrupts = <0 2 4>;
reg = <0x0 0xa1200000 0x0 0x10000>;
};
DTG中支持的驱动列表和在Linux设备树中的绑定
参考:
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842279/Build+Device+Tree+Blob
设备树的说明文档在:
https://github.com/Xilinx/linux-xlnx/blob/master/Documentation/devicetree/bindings/
设备树的生成
一般来说对于SOC这类芯片,都有一个静态的dts/dtsi文件,但是对于FPGA来说情况就复杂的多,因为FPGA的情况要复杂很多,PL的IP设备可能多种多样,并由许多不同的配置。
一旦我们生成设备树文件,将输出多个各种不同的DT文件,例如,dt/pl.dtsi pcw.dtsi
system-top.dts zynqmp.dtsi zynqmp-clk-ccf.dtsi。
-
pl.dtsi:该文件中包含了所有可用的内存映射的PL IP节点。
-
pcw.dtsi:包含PS外设所需的动态属性,包含我们在FPGA工程创建时,在原理框图设计中进行IO配置的外设接口。
-
system-top.dts:该文件以include的方式包含了pcw.dtsi和zynq-7000.dtsi,包含内存信息和早期控制台和启动参数。
-
zynqmp.dtsi:包含了所有的PS外设信息以及CPU信息。
-
zynqmp-clk-ccf.dtsi:包含了所有外设IP的时钟信息。
此外对于特定的板卡将生成一个特定于该板卡的dtsi文件。如<skeleton>.dtsi。其包含了板卡特定的属性,如I2C,其可能连接了一些从设备。
如何使用上层的DT覆盖底层的DTG?
使用HIS命令
-
克隆设备树源码库repo。
https://github.com/Xilinx/device-tree-xlnx -
在SDK软件中选择菜单Xilinx Launch Shell,启动SDK
Shell,输入his,进入HIS命令提示符。 -
打开硬件描述文件。his% open_hw_design design_1_wrapper.hdf。
-
运行his% set_repo_path <DTG repo的绝对路径>。
-
构建设备树文件,his% create_sw_design –proc psu_crotexa53_0 sd22 –os
device_tree -
his% set_property CONFIG.dt_overlay true [get_os]
-
查看生成的文件
hsi% ls dt/
pcw.dtsi pl.dtsi sd22.mss system-top.dts zynqmp-clk-ccf.dtsi zynqmp.dtsi -
可以看到生成了一个MSS文件。
使用XSCT命令提示符
开发XSCT命令提示符。
-
hsi open_hw_design design_1_wrapper.xsa
-
hsi set_repo_path <DTG repo path>
-
hsi create_sw_design -proc psu_cortexa53_0 sd22 -os device_tree
-
hsi generate_target -dir dt
编译pl.dtsi
dtc –O dtb –o pl.dtbo –b 0 -@ pl.dtsi
设备树二进制文件的比较
可以通过dtx_diff程序来比较两个设备树Blob(dtb)之前的差异。
依赖性/局限性
-
zynqmp-clk-ccf.dtsi具有静态时钟节点配置,如果用户需要更改任何时钟信息,请更新system-user.dtsi中的信息。
-
DTG不支持多concat中断模块。
-
中断端口宽度不支持超过1。
-
当为MAC IPs启用多核(如果MAC
IPs大于1)时,DTG中的标签就会出现问题,并且会失败。但是,如果MAC
IP是一个,并且启用了多核,就不会有问题。 -
DTG不支持生成私有外围中断(PPI)。
-
DTG支持基于内部TRD设计的视频管道生成,如wiki中所述
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/25329832/Zynq+UltraScale+MPSoC+VCU+TRD+2018.3 -
如果有任何自定义的ip连接在视频管道IP之间,DTG不支持这些情况,用户可能需要添加输入和输出端口。
-
对于广播IP,输出可以连接到多个输出端口,DTG无法知道哪个输出端口对于正确的管道是有效的。如果在设计中有多个类似的视频管道,用户需要在节点中添加输入和输出端口信息。下面的wiki给出了一些关于如何添加输入和输出端口的信息。
构建设备树
在开始构建设备树之前,需要做如下的准备:
-
获取源码(Device Tree Generator源码和Linux源码)
-
硬件工程
需要的工具有:
-
Xilinx Vivado
-
Xilinx SDK
输入的文件
-
硬件工程目录
-
Linux源码目录
输出的文件
*.dts, *.dtsi, *.dtb
任务描述
创建设备树源文件(.dts/.dtsi)
-
生成硬件描述HDF文件;
-
打开SDK;
-
克隆设备树Generator Git repo;
https://github.com/Xilinx/device-tree-xlnx.git -
在SDK中添加BSP repo:点击Xilinx Repositories New… (<bsp repo>) OK。
-
创建设备树板级支持包(BSP):SDK Menu: File > New > Board Support Package
> Board Support Package OS: device-tree > Finish。 -
设置BSP窗口打开(也可以通过system.mss文件打开),修改设置BSP配置,在bootargs和console
device参数中填入合适的启动参数。
.dts /.dtsi文件现在位于<SDK工作空间>/device_tree_bsp_0/文件夹中。
***例如:console=<tty>, <baudrate> root=/dev/ram rw ip=::: eth0:dhcp
earlyprintk
*** <tty>的一些示例值是当使用Zynq时ttyPS0,当使用UART
Lite软ip时ttyUL0,或者当使用UART16550软ip时ttyS0。
从DTS中编译设备树Blob(.dtb)文件
一个称为设备树编译器(DTC)的实用程序用于将DTS文件编译成DTB文件。DTC是Linux源代码目录的一部分。linux-xlnx/scripts/dtc/包含dtc的源代码,需要编译才能使用。编译DTC的一种方法是构建Linux树。DTC也可以通过OS的包管理器获得。
一旦DTC可用,该工具将可能被调用用来生成DTB。
./scripts/dtc/dtc -I dts -O dtb -o <devicetree name>.dtb <devicetree
name>.dts
DTC也可以反过来将DTB转换为DTS。
./scripts/dtc/dtc -I dtb -O dts -o <devicetree name>.dts <devicetree
name>.dtb
可选的方法(只针对ARM)
在Linux源码目录中,make ARCH=arm
dtbs,这将从linux-xlnx/arch/arm/boot/dts编译所有的DTS文件将其转换为DTB文件。
编译好的DTB文件与DTS文件在相同的目录中。
自定义IP
对于自定义IP,DTG将生成带有兼容属性和中断(如果连接的话)的节点。
参考文献:
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842279/Build+Device+Tree+Blob