Zephyr Device Tree简介
本文说明了Zephyr对device tree的使用方法,并分析了Qemu_cortex_m3的device tree和对应生成的#define. 本文旨在帮助理解Zephyr的代码,不说明如何添加Zephyr的Device Tree和yaml,不分析python script.
Zephyr device tree概述 链接到标题
linux下DTS被DTC编译为DTB,DTB被放到Flash内,启动时DTB由bootloader送给kernel使用。但是这套机制太过庞大,不适合Zephyr作为iot终端节点的目标。 Zephyr采用buildin的方法来使用DTS,主要步骤如下:
- 使用DTC将DTS和C头文件一起预编译组合成.dts_compiled文件。
- python脚本按照YAML文件指定的一组规则从.dts_compiled文件中提取信息转换为define,合并板级描述的dts.fixup内的define放置在头文件generated_dts_board.h中
- Zephyr编译时头文件generated_dts_board.h参与编译被buildin入zephyr

Zephyr device tree基本元素说明 链接到标题
Zephyr Device Tree 链接到标题
Zephyr使用Device Tree来描述架构,板子,驱动信息,Device的文件分别放在
boards/<ARCH>/<BOARD>/
dts/
dts/common/
dts/<ARCH>/
dts/<ARCH>/<MANUFACTURER>/
Device Tree语法简述 链接到标题
Zephyr device tree遵循EPAPR document. 简要说明如下:
Device Tree 链接到标题
Device tree由node组成,包含根节点和子节点 节点node由节点名和节点内容组成,节点内容用{}扩起来
/{
node-name@unit-address{
property-name=property-value;
node-name@unit-address{
property-name=property-value;
};
}
}
节点 链接到标题
节点node在Device tree中以下面形式出现:
node-name@unit-address
只能有一个根节点root node, 可以有多个子节点sub node。 一个node的内容有属性和自己的sub node
节点名 链接到标题
根节点名是/ 子节点名node-name为长度小于31的字符串。对不同的device,EPAPR document有推荐的node-name(非强制) 当node有寄存器时,unit-address是第一个寄存器的地址。如果node没有寄存器,则无@unit-address
节点属性 链接到标题
属性用表达式以下面形式出现,分为属性名和属性值
property-name=property-value
属性名 链接到标题
属性名分为标准属性名和非标属性名,对于非标属性名EPAPR document建议加前缀以示区分,例如
fsl,channel-fifo-len
ibm,ppc-interrupt-server#s
linux,network-index
注意以上只有3个属性名,属性名当中是允许出现 , 的
属性值 链接到标题
属性值有7种类型
- <empty> 空,没有值
- <u32>
big-endian 32bit符号整形
property = <0x11223344> - <u64>
big-endian 64bit符号整形,由两个32bit组成
property = <0x11223344 0x55667788> - <string>
字符串
device_type = "memory"; - <prop-encoded-array>
任意数量的array
reg = <0x3000 0x20 0xFE00 0x100>; - <phandle>
节点引用
uart_0 = &uart0; - <stringlist>
字符串列表
compatible = "ti,lm3s6965evb-qemu", "ti,lm3s6965";
标准属性 链接到标题
常用标准属性共12种:
- compatible
- model
- phandle
- status
- #address-cells
- #size-cells
- reg
- virtual-reg
- ranges
- dma-ranges
- name
- device_type
另外有6种中断用的属性
- interrupts
- interrupt-parent
- #interrupt-cells
- interrupt-controller
- interrupt-map
- interrupt-map-mask
属性名内容繁多,具体参考EPAPR document,后文QEMU_CORTEX_M3 Device Tree分析会做一些简要说明
YAML 链接到标题
YAML它是一种直观的能够被电脑识别的数据序列化格式,是一个可读性高并且容易被人类阅读,容易和脚本语言交互,用来表达资料序列的编程语言. Zephyr 使用YAML来定义Device tree转化为#define的生成规则,dts/bindings/device_node.yaml.template 是一个yaml的模板, zephyr的yaml放在
dts/bindings
boards/<ARCH>/<BOARD>/
Python Script 链接到标题
将Dts转化为头文件的脚本放在scripts\dts下。目前也还不计划阅读这部分。
Qemu_Cortex_m3 device tree分析 链接到标题
DTS文件组成 链接到标题
由下面文件include组成
- boards/arm/qemu_cortex_m3/qemu_cortex_m3.dts #include <ti/lm3s6965.dtsi>
- dts/arm/ti/lm3s6965.dtsi #include <arm/armv7-m.dtsi>
- dts/arm/armv7-m.dtsi #include “skeleton.dtsi”
- dts/common/skeleton.dtsi
将上面4个文件合并后如下分析:
/ { //根节点的属性,如果子节点有重新定义相同属性,子节点内属性使用重新定义后的值
model = "QEMU Cortex-M3"; //板制造商设备型号,一般会用model = "st,stm32f0"说明制造商(st)和型号(stm32f0),这里是Qemu因此没有带制造商
compatible = "ti,lm3s6965evb-qemu", "ti,lm3s6965"; // 对于root node,compatible属性用来匹配machine type,用于指出板子的制造商和设备型号
#address-cells = <1>;
#size-cells = <1>;
aliases { //一个节点以全路径被引用太过复杂,aliase用短别名取代
uart_0 = &uart0; //例如“/soc/uart@4000C000”就可以用uart_0来取代
uart_1 = &uart1;
uart_2 = &uart2;
};
chosen { //chosen节点为硬件和操作系统数据传输服务,如:启动参数。Zephyr是build in形式,因此在产生header的时候用该节点指明驱动设备的配置
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,console = &uart0;
zephyr,bt-uart = &uart2;
zephyr,uart-pipe = &uart1;
zephyr,bt-mon-uart = &uart2;
};
cpus {
#address-cells = <1>; //cpu子节点reg地址长度为32bit,中address-cells表示子节点中reg中address字段长度单位为32bit
#size-cells = <0>; //cpu子节点reg数据为0,中address-cells表示子节点中reg中数据字段长度单位为32bit,联合address-cells表示子节点reg 值为一个没有size的uint32地址
cpu@0 { //cpu寻址被id表示第0个CPU,如果节点有reg属性,节点名必须包含unit-address,并且取reg属性的第一address值(0)
device_type = "cpu"; //只有memory和cpu两种device type,为了兼容IEEE 1275
compatible = "arm,cortex-m3"; //cpu的制造商和设备型号
reg = <0>; //reg属性第一个address值为0
};
};
sram0: memory@20000000 { //内存地址在0x20000000
device_type = "memory"; //只有memory和cpu两种device type,为了兼容IEEE 1275
compatible = "mmio-sram"; //设备型号mmio-sram
reg = <0x20000000 (64*1024)>; //地址在0x20000000大小64K
};
flash0: flash@0 { //flash地址在0x00000000
reg = <0x00000000 (256*1024)>; //flash地址在0x00000000大小256K
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&nvic>; //指定节点设备所依附的中断控制器的phandle,若没有此参数,则从父节点继承
ranges; //空属性, ranges本身是做地址映射,让设备地址可以被CPU访问
nvic: interrupt-controller@e000e100 { //中断控制器
compatible = "arm,v7m-nvic";
reg = <0xe000e100 0xc00>; //中断控制器的寄存器地址在0xe000e100,长度0xc00
interrupt-controller; //空属性标示是interrupt-controller
#interrupt-cells = <2>; //用2个32bit reg标示一个中断源
arm,num-irq-priority-bits = <3>;
};
systick: timer@e000e010 { //硬件timer
compatible = "arm,armv7m-systick";
reg = <0xe000e010 0x10>; //中断控制器的寄存器地址在0xe000e010,长度0x10
status = "disabled";
};
uart0: uart@4000C000 { //uart0,
compatible = "ti,stellaris-uart";
reg = <0x4000C000 0x4c>; //寄存器起始地址在0x4000c000, 大小0x4C(19个)
interrupts = <5 3>; //中断号5,触发方式3
label = "UART_0"; //绑定device描述字符串为UART_0
status = "ok"; //status指示device状态,值可以选"okay" "disabled" "fail" "fail-sss"
current-speed = <115200>;
};
uart1: uart@4000D000 { //uart1
compatible = "ti,stellaris-uart";
reg = <0x4000D000 0x4c>; //寄存器起始地址在0x4000d000, 大小0x4C(19个)
interrupts = <6 3>; //中断号6,触发方式3
label = "UART_1";
status = "ok"; //status指示device状态,值可以选"okay" "disabled" "fail" "fail-sss"
current-speed = <115200>;
};
uart2: uart@4000E000 { //uart2
compatible = "ti,stellaris-uart";
reg = <0x4000E000 0x4c>; //寄存器起始地址在0x4000e000, 大小0x4C(19个)
interrupts = <33 3>; //中断号33,触发方式3
label = "UART_2";
status = "ok"; //status指示device状态,值可以选"okay" "disabled" "fail" "fail-sss"
current-speed = <115200>;
};
};
};
Yaml组成 链接到标题
Qemu_cortex_m3使用的yaml有,生成规则待分析(可能要分析python script才能知道),目前能知道转换结果就不会影响Zephyr代码的分析了
- boards/arm/qemu_cortex_m3/qemu_cortex_m3.yaml
- dts/bindings/interrupt-controller/arm,v7m-nvic.yaml
- dts/bindings/serial/ti,stellaris-uart.yaml
- dts/bindings/serial/uart.yaml
转换结果 链接到标题
基本上可以看出就是将dts内的信息转换成device信息的#define,供Zephyr的代码使用
/**************************************************
* Generated include file for ti,lm3s6965evb-qemu
* DO NOT MODIFY
*/
#ifndef _DEVICE_TREE_BOARD_H
#define _DEVICE_TREE_BOARD_H
/* flash@0 */
#define CONFIG_FLASH_BASE_ADDRESS_0 0x0
#define CONFIG_FLASH_LOAD_OFFSET 0
#define CONFIG_FLASH_LOAD_SIZE 0
#define CONFIG_FLASH_SIZE_0 256
#define CONFIG_FLASH_BASE_ADDRESS CONFIG_FLASH_BASE_ADDRESS_0
#define CONFIG_FLASH_SIZE CONFIG_FLASH_SIZE_0
/* memory@20000000 */
#define CONFIG_SRAM_BASE_ADDRESS_0 0x20000000
#define CONFIG_SRAM_SIZE_0 64
#define CONFIG_SRAM_BASE_ADDRESS CONFIG_SRAM_BASE_ADDRESS_0
#define CONFIG_SRAM_SIZE CONFIG_SRAM_SIZE_0
/* interrupt-controller@e000e100 */
#define ARM_V7M_NVIC_E000E100_ARM_NUM_IRQ_PRIORITY_BITS 3
#define ARM_V7M_NVIC_E000E100_BASE_ADDRESS_0 0xe000e100
#define ARM_V7M_NVIC_E000E100_SIZE_0 3072
#define ARM_V7M_NVIC_E000E100_BASE_ADDRESS ARM_V7M_NVIC_E000E100_BASE_ADDRESS_0
#define ARM_V7M_NVIC_E000E100_SIZE ARM_V7M_NVIC_E000E100_SIZE_0
/* uart@4000C000 */
#define CONFIG_UART_CONSOLE_ON_DEV_NAME "UART_0"
#define TI_STELLARIS_UART_4000C000_BASE_ADDRESS_0 0x4000c000
#define TI_STELLARIS_UART_4000C000_CURRENT_SPEED 115200
#define TI_STELLARIS_UART_4000C000_IRQ_0 5
#define TI_STELLARIS_UART_4000C000_IRQ_0_PRIORITY 3
#define TI_STELLARIS_UART_4000C000_LABEL "UART_0"
#define TI_STELLARIS_UART_4000C000_SIZE_0 76
#define TI_STELLARIS_UART_4000C000_BASE_ADDRESS TI_STELLARIS_UART_4000C000_BASE_ADDRESS_0
#define TI_STELLARIS_UART_4000C000_SIZE TI_STELLARIS_UART_4000C000_SIZE_0
/* uart@4000D000 */
#define CONFIG_UART_PIPE_ON_DEV_NAME "UART_1"
#define TI_STELLARIS_UART_4000D000_BASE_ADDRESS_0 0x4000d000
#define TI_STELLARIS_UART_4000D000_CURRENT_SPEED 115200
#define TI_STELLARIS_UART_4000D000_IRQ_0 6
#define TI_STELLARIS_UART_4000D000_IRQ_0_PRIORITY 3
#define TI_STELLARIS_UART_4000D000_LABEL "UART_1"
#define TI_STELLARIS_UART_4000D000_SIZE_0 76
#define TI_STELLARIS_UART_4000D000_BASE_ADDRESS TI_STELLARIS_UART_4000D000_BASE_ADDRESS_0
#define TI_STELLARIS_UART_4000D000_SIZE TI_STELLARIS_UART_4000D000_SIZE_0
/* uart@4000E000 */
#define CONFIG_BT_MONITOR_ON_DEV_NAME "UART_2"
#define CONFIG_BT_UART_ON_DEV_NAME "UART_2"
#define TI_STELLARIS_UART_4000E000_BASE_ADDRESS_0 0x4000e000
#define TI_STELLARIS_UART_4000E000_CURRENT_SPEED 115200
#define TI_STELLARIS_UART_4000E000_IRQ_0 33
#define TI_STELLARIS_UART_4000E000_IRQ_0_PRIORITY 3
#define TI_STELLARIS_UART_4000E000_LABEL "UART_2"
#define TI_STELLARIS_UART_4000E000_SIZE_0 76
#define TI_STELLARIS_UART_4000E000_BASE_ADDRESS TI_STELLARIS_UART_4000E000_BASE_ADDRESS_0
#define TI_STELLARIS_UART_4000E000_SIZE TI_STELLARIS_UART_4000E000_SIZE_0
/* Following definitions fixup the generated include */
/* SoC level DTS fixup file */
#define CONFIG_NUM_IRQ_PRIO_BITS ARM_V7M_NVIC_E000E100_ARM_NUM_IRQ_PRIORITY_BITS
/* End of SoC Level DTS fixup file */
#endif
参考 链接到标题
http://docs.zephyrproject.org/devices/dts/device_tree.html http://events17.linuxfoundation.org/sites/events/files/slides/Zephyr%20Device%20Tree%20-%20ELC2017.pdf https://elinux.org/images/b/b8/DTWorkshop2017_Zephyr.pdf https://www.devicetree.org/downloads/devicetree-specification-v0.1-20160524.pdf