【设备树笔记整理3】设备树的规范(dts和dtb)

1 dts 文件格式

1.1 DTS文件布局(layout):

cpp 复制代码
/dts-v1/;
[memory reservations] 
/ {
    [property definitions]
    [child nodes]
};

(1)第一行表示当前设备树文件的版本

(2)第二行用来定义保留的内存区域,该区域不给内核使用,例如:

cpp 复制代码
/memresersve/  0x33000000  0x10000

(3)第三行的 "/" 定义根节点

(4)根节点中包含根节点属性(property definitions)和子节点(child nodes)

注意: 大括号和属性都要在结尾处加 ";"

1.2 节点属性格式

(1)Property格式2(没有值):

cpp 复制代码
[label:] property-name;

(2)Property格式1:

cpp 复制代码
[label:] property-name = value;

(3)Property取值有3种情况:

① arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示)

示例1:Arrays of cells : cell就是一个32位的数据

cpp 复制代码
interrupts = <17 0xc>;

示例2:64bit数据使用2个cell来表示:

cpp 复制代码
clock-frequency = <0x00000001 0x00000000>;

② string(字符串)

示例:A null-terminated string (有结束符的字符串):

cpp 复制代码
compatible = "simple-bus";

③ bytestring(1个或多个字节)

示例:A bytestring(字节序列) :

cpp 复制代码
local-mac-address = [00 00 12 34 56 78];  // 每个byte使用2个16进制数来表示
local-mac-address = [000012345678];       // 每个byte使用2个16进制数来表示(空格可以省略)

补充: Property的取值还可以是各种值的组合(该用法不常见), 用逗号隔开:

cpp 复制代码
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";

1.3 设备节点的格式

Devicetree node格式:

cpp 复制代码
[label:] node-name[@unit-address] {
    [properties definitions]
    [child nodes]
};

1.4 特殊的、默认的属性

(1)根结点的属性

cpp 复制代码
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible       // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备
                 // 即这个板子兼容哪些平台 
                 // uImage : smdk2410 smdk2440 mini2440     ==> machine_desc         
                 
model            // 咱这个板子是什么
                 // 比如有2款板子配置基本一致, 它们的compatible是一样的
                 // 那么就通过model来分辨这2款板子

补充: 关于 #address-cells 和 #size-cells

cpp 复制代码
/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
}

由于#address-cells和#size-cells的值都为1,所以上述例子reg的值中0x30000000表示地址,而0x4000000就表示地址的长度。已知#address-cells和#size-cells,reg可以区分多组地址段:

cpp 复制代码
/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000 0 4096>;
	};
}

上述例子中的reg表示两组数据,第一组是地址为0x30000000,长度为0x4000000的地址段;而第二组数据是地址为0,长度为4096的地址段。

(2)/memory 结点

cpp 复制代码
device_type = "memory";
reg             // 用来指定内存的地址、大小

(3)/chosen 结点

cpp 复制代码
bootargs        // 内核command line参数, 跟u-boot中设置的bootargs作用一样

(4)/cpus 结点

/cpus节点下有1个或多个cpu子节点, cpu子节点中用reg属性用来标明自己是哪一个cpu

所以 /cpus 中有以下2个属性:

cpp 复制代码
#address-cells   // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)

#size-cells      // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
                 // 必须设置为0

(5)/cpus/cpu* 结点(多个cpu中的每个cpu结点)

cpp 复制代码
device_type = "cpu";
reg             // 表明自己是哪一个cpu

1.5 引用其他节点

(1)phandle : // 节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)

cpp 复制代码
pic@10000000 {
    phandle = <1>;
    interrupt-controller;
};

another-device-node {
    interrupt-parent = <1>;   // 使用phandle值为1来引用上述节点
};

(2)label:

cpp 复制代码
PIC: pic@10000000 {
    interrupt-controller;
};

another-device-node {
    interrupt-parent = <&PIC>;   // 使用label来引用上述节点, 
                                 // 使用lable时实际上也是使用phandle来引用, 
                                 // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};

1.6 dtsi 文件

dtsi文件的格式和dts文件的格式一样,可以将公共的部分放到dtsi文件,并让dts文件包含该dtsi文件。在dts中包含dtsi文件的方法:

cpp 复制代码
#include "jz2440.dtsi"

下面举两个dtsi文件相关的例子:

(1)示例1:覆盖结点

① 文件:jz2440.dtsi

cpp 复制代码
/dtsi-v1/;

/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	
	led {
		compatible = "jz2440_led";
		reg = <5>;
	};
};

② 文件:mytree.dts

cpp 复制代码
/dts-v1/;

#include "jz2440.dtsi"

/ {
    led {
	    reg = <6>;
	};
};

说明: 在mytree.dts中包含jz2440.dtsi文件,然后按照jz2440.dtsi中led的结点路径,重写led结点,并修改其reg属性为6。这样在最终编译生成的dtb文件中,led的reg属性就为6。也就是说在dts文件中可以覆盖dtsi文件的结点和属性。

(2)示例2:标签引用

① 文件:jz2440.dtsi

cpp 复制代码
/dtsi-v1/;

/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	
	LED: led {
		compatible = "jz2440_led";
		reg = <5>;
	};
};

② 文件:mytree.dts

cpp 复制代码
/dts-v1/;

#include "jz2440.dtsi"

&LED {
    reg = <5>;
};

说明: 依旧让mytree.dts中包含jz2440.dtsi文件。但是这里通过led结点的标签LED来引用该结点,注意通过标签引用结点时不要从根路径开始写结点,否则会报错。

(3)dtc工具

  • dtc其实就是device-tree-compiler,也就是设备树文件dts的编译器。
  • dtc可以将dts和dtsi文件编译成dtb二进制文件,也可以将dtb文件反编译成dts文件。
  • dtc工具在Linux内核的scripts/dtc目录下,因此需要编译完内核源码后才会生成。

2 dtb 文件格式

2.1 dtb文件的布局

cpp 复制代码
             ------------------------------
     base -> |  struct boot_param_header  |
             ------------------------------
             |      (alignment gap) (*)   |
             ------------------------------
             |      memory reserve map    |
             ------------------------------
             |      (alignment gap)       |
             ------------------------------
             |                            |
             |    device-tree structure   |
             |                            |
             ------------------------------
             |      (alignment gap)       |
             ------------------------------
             |                            |
             |     device-tree strings    |
             |                            |
      -----> ------------------------------
      |
      |
      --- (base + totalsize)

2.2 dtb二进制文件的分析

2.3 dtb文件采用大端存储模式

(1)大端存储模式和小端存储模式

(2)补充

大端模式和小端模式是针对数值存储来说的,字符串存储则没有该规则。例如字符串"ab","b"总是存储在比"a"高的地址中。

2.4 查看二进制文件的工具

  • UltraEdit
  • Free-Hex-Editor-Neo

3 参考

(1)官方文档: Specifications - DeviceTree

(2)内核文档1: Documentation/devicetree/usage-model.txt

(3)内核文档2: Documentation/devicetree/booting-without-of.txt

相关推荐
A小辣椒2 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao3 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠4 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush44 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5204 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩4 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言