设备树之DTS与DTB格式

article/2025/10/25 1:00:11

目录

 

一、设备树

二、DTS格式

2.1 属性

2.2 节点

2.3 引用其他节点

2.4 小总结

三、DTB格式

3.1 结构

3.2 分析


一、设备树

对于点灯字符设备驱动程序可以有三种写法,首先是传统方法,这种方式直接在程序中写死,其次是利用总线设备驱动模型,利用分离的思想,将程序规划为两部分,对于驱动部分一般是稳定的,在设备部分更改资源,最后是利用设备树方法,可以使用设备树直接指明引脚,而驱动写法的核心不变,差别就在于如何指定硬件资源,这三种方法有各自的优缺点

 优点缺点
传统方法简单不易扩展,需重新编译
总线设备驱动模型易扩展,只需要修改资源稍微复杂 ,冗余代码多(如果有多个版本的硬件就会有多个版本的程序),需重新编译
设备树易扩展,无冗余代码,不需重新编译内核或者驱动 ,只需要提供不一样的设备树文件复杂

对于设备树,通过dts文件来指定资源,内核会根据.dts文件分配设置注册platform_device(平台设备),当更改单板时,只需要重新定义dts文件就可以了,dts文件最终会编译为dtb文件,启动单板的时候,既要启动内核,也要传入dtb文件,因此更改单板时,不需要重新编译驱动程序,只需要提供一个不一样的dtb文件

 

二、DTS格式

写设备树我们是写出dts文件,文件写出来之后根据编译器dtc编译为二进制dtb文件,DTS文件布局(layout)如下,第一行表示版本,第二行表示保留的的内存区域,比如板子有64M内存,里面想留下4M给自己使用,不想让内核使用,就可以定义这个选项,如果想让内核使用全部内存就可以省略这个选项

/dts-v1/;
[memory reservations]        /* 格式为:/memreserve/ <address> <length>; */
/{                           /* /表示根,设备树的起点 */[property definitions]   /* 首先有属性来描述硬件 */[child nodes]            /* 子节点,在子节点中还可以有子节点 */
};

 

2.1 属性

对于属性"[property definitions]"的格式有两种,即无值和有值两种方式,属性后需要分号

Property格式1:
[label:] property-name = value;
Property格式2(没有值):
[label:] property-name = value;

如下led节点,可以知道属性中的value值的形式有多种

led {compatible = "jz2440_led";pin = <S3C2410_GPF>;  //S3C2410_GPF为一个宏
};

对于value的值有三种表示:arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示)、string(字符串)、bytestring(1个或多个字节),这些值什么含义,完全取决于驱动程序

示例:

interrupts = <17 0xc>;(里面每一个数据都是32位数据)

clock-frequency = <0x00000001 0x00000000>; (64bit数据使用2个cell来表示 )

compatible = "simple-bus"; (A null-terminated string有结束符的字符串)

A bytestring(字节序列) : local-mac-address = [00 00 12 34 56 78]; 或者local-mac-address = [000012345678]; (每个byte使用2个16进制数来表示 )

可以是各种值的组合, 用逗号隔开: compatible = "ns16550", "ns8250";example = <0xf00f0000 19>, "a strange property format";

 

特殊、默认的属性:

在根节点中

  • #address-cells    在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
  • #size-cells           在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)

如下,若CPU为64位的,则需要两个32位来表示reg,因此可以写为#address-cells = <1>;  对于reg中还可以写多个内存,如reg = <0x30000000 0x40000000 0 4096>; 第一个是地址单元 第二个表示大小,可以写多个address size

compatible:定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备,即这个板子兼容哪些平台, uImage是用来选择单板的,uImage支持很多单板每个单板都会有一个machine_desc结构体 ,如下设备树优先去寻找 “samsung,smdk2440”的machine_desc,如果找不到的话再来找 "samsung,s3c24xx"

chosen节点可以设置内核command line参数, 跟u-boot中设置的bootargs作用一样

cpus节点代表有多个CPU,对于个人的单板只有一个CPU,就可以只写一个子节点,在子节点中需要写device_type = "cpu";上面的例子没有引用cpus节点,cpu子节点中用reg属性用来标明自己是哪一个cpu, 所以 /cpus 中有以下2个属性: #address-cells:在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address),#size-cells:在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size),必须设置为0

/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory {device_type = "memory";       //这是大家约定的,必须写这个reg = <0x30000000 0x4000000>; //reg用来表示内存的起始地址和大小};
/*cpus {cpu {compatible = "arm,arm926ej-s";};};
*/	chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};
};

 

2.2 节点

对于节点"[child nodes]"的格式如下,可以看出节点可以有自己的子节点,节点结束后需要分号

[label:] node-name[@unit-address] {   //label可以写也可以不写,写的时候别人引用该节点就比较方便,而node-name比如上面写的led[properties definitions][child nodes]
};

对于node-name后面可以加上地址,组合起来就是节点的名字,假设有两个memory(内存)节点在同一级别就可以来用区别,用@加上内存的首地址来表示名字的不同,在memory的子目录下可以有同名的memory目录,当然没有这么古怪的节点,这里只是举例

memory@30000000{device_type = "memory";reg = <0x30000000 0x4000000>;       memory{device_type = "memory";reg = <0 4096>;  };
};memory@0{device_type = "memory";reg = <0 4096>;  
};

 

2.3 引用其他节点

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

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

还有另外一种方式,实质也是利用了phandle属性,使用label来引用节点

PIC: pic@10000000 {   //编译器dtc会在父节点插入一个 phandle = <xxx>; 然后会把下面的引用<&PIC>替换为<xxx>interrupt-controller;    
};another-device-node {interrupt-parent = <&PIC>;   // 使用label来引用上述节点, // 使用lable时实际上也是使用phandle来引用, // 在编译dts文件为dtb文件时, 编译器dtc会在dtb中插入phandle属性
};

举例,在dts中会把共同的地方抽出来然后写为dtsi文件

  • dtsi文件
// SPDX-License-Identifier: GPL-2.0
/** SAMSUNG SMDK2440 board device tree source** Copyright (c) 2018 weidongshan@qq.com* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb*/#define S3C2410_GPA(_nr)	((0<<16) + (_nr))
#define S3C2410_GPB(_nr)	((1<<16) + (_nr))
#define S3C2410_GPC(_nr)	((2<<16) + (_nr))
#define S3C2410_GPD(_nr)	((3<<16) + (_nr))
#define S3C2410_GPE(_nr)	((4<<16) + (_nr))
#define S3C2410_GPF(_nr)	((5<<16) + (_nr))
#define S3C2410_GPG(_nr)	((6<<16) + (_nr))
#define S3C2410_GPH(_nr)	((7<<16) + (_nr))
#define S3C2410_GPJ(_nr)	((8<<16) + (_nr))
#define S3C2410_GPK(_nr)	((9<<16) + (_nr))
#define S3C2410_GPL(_nr)	((10<<16) + (_nr))
#define S3C2410_GPM(_nr)	((11<<16) + (_nr))/dts-v1/;/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory {  /* /memory */device_type = "memory";reg =  <0x30000000 0x4000000 0 4096>;		};/*cpus {cpu {compatible = "arm,arm926ej-s";};};
*/	chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};
};
  • dts文件
// SPDX-License-Identifier: GPL-2.0
/** SAMSUNG SMDK2440 board device tree source** Copyright (c) 2018 weidongshan@qq.com* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb*//dts-v1/;#include "jz2440.dtsi"/ {led {pin = <S3C2410_GPF(6)>;};
};

这样后面写的属性会覆盖前面的属性,反汇编"./scripts/dtc/dtc -I dtb -O dts -o tmp.dts arch/arm/boot/dts/jz2440.dtb ",查看tmp.dts可以看到led节点中的pin引脚就被覆盖了

/dts-v1/;/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <0x1>;#size-cells = <0x1>;memory {device_type = "memory";reg = <0x30000000 0x4000000 0x0 0x1000>;};chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <0x50006>;};
};

上面这种方式比较麻烦,若不想重头写led节点可以修改dtsi中的led节点加上label

LED: led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;
};

修改dts中的led节点,反汇编之后一样会看到节点的变化

/dts-v1/;#include "jz2440.dtsi"
&LED {pin = <S3C2410_GPF(7)>;
};

 

2.4 小总结

在内核文档中Documentation\devicetree\usage-model.txt,其中对设备树进行了三部分的总结,首先是平台识别信息,其次是运行时的配置,最后是设备枚举

Linux uses DT data for three major purposes:
1) platform identification,
2) runtime configuration, and
3) device population.

举例

  • 平台信息
model = "SMDK24440";
compatible = "samsung,smdk2440";#address-cells = <1>;
#size-cells = <1>;
  • 运行时的配置,设置bootargs传给内核,还有/memreserve/ 0x33000000 0x10000;
chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};
  • 设备枚举
LED: led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;
};

 

三、DTB格式

用dtc编译器把dts文件转换为dtb文件,首先会将宏展开,其次还有dtsi和dts编译为一个dtb文件,可以根据可读性强的代码进行帮我们找到错误,对于DTB格式,可以阅读官方文档:https://www.devicetree.org/specifications/ ,也可以阅读内核文档:Documentation/devicetree/booting-without-of.txt

 

3.1 结构

dtb文件分为四部分,首先是头部信息,其次是内存保留区、结构区和字符串区,对于保留区:例/memreserve/ 0x33f00000 0x100000;放在了memory reserve map,其中都是64位表示的,在dtb文件中数据存放的格式是以大端保存的,对于小端高位存放在高地址,对于大端低位存放在低地址

在官方文件中头部header的结构体,在文档描述了magic必须是D00DFEED,totalsize为文件大小,查看文件属性占用空间是4096而实际的大小是465个字节,465的16进制是1D1,因此是"01 D1 00 00",而off_dt_struct是structure block在文件中的偏移地址,off_dt_strings、off_mem_rsvmap同理,在strings block存放属性的名称,每一个名字有0字符结尾,属性的名字例如compatible用了多次为了节约空间,把属性的名字单独作为字符串保留在strings block里面

struct fdt__header {uint32_t magic;uint32_t totalsize;uint32_t off_dt_struct ;uint32_t off_dt_strings ;uint32_t off_mem_rsvmap;uint32_t version;uint32_t last_comp_version;uint32_t boot_cpuid_phys;uint32_t size_dt_strings;uint32_t size_dt_struct;
};

对于内存保留区在文档中也有个结构体用来表示 ,并且都是64位的表示地址和大小,在下面例子中"/memreserve/ 0x33f00000 0x100000;"在dts文件中在第四行和五行标红的地方

struct fdt_reserve_entry {uint64_t address;uint64_t size;
};

 

 

3.2 分析

  • dtb文件
// SPDX-License-Identifier: GPL-2.0
/** SAMSUNG SMDK2440 board device tree source** Copyright (c) 2018 weidongshan@qq.com* dtc -I dtb -O dts -o jz2440.dts jz2440.dtb*/#define S3C2410_GPA(_nr)	((0<<16) + (_nr))
#define S3C2410_GPB(_nr)	((1<<16) + (_nr))
#define S3C2410_GPC(_nr)	((2<<16) + (_nr))
#define S3C2410_GPD(_nr)	((3<<16) + (_nr))
#define S3C2410_GPE(_nr)	((4<<16) + (_nr))
#define S3C2410_GPF(_nr)	((5<<16) + (_nr))
#define S3C2410_GPG(_nr)	((6<<16) + (_nr))
#define S3C2410_GPH(_nr)	((7<<16) + (_nr))
#define S3C2410_GPJ(_nr)	((8<<16) + (_nr))
#define S3C2410_GPK(_nr)	((9<<16) + (_nr))
#define S3C2410_GPL(_nr)	((10<<16) + (_nr))
#define S3C2410_GPM(_nr)	((11<<16) + (_nr))/dts-v1/;/memreserve/ 0x33f00000 0x100000;/ {model = "SMDK24440";compatible = "samsung,smdk2440";#address-cells = <1>;#size-cells = <1>;memory {  /* /memory */device_type = "memory";reg =  <0x30000000 0x4000000 0 4096>;		};/*cpus {cpu {compatible = "arm,arm926ej-s";};};
*/	chosen {bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";};led {compatible = "jz2440_led";pin = <S3C2410_GPF(5)>;};
};
  • dts文件

 分析device-tree structure区域,是最复杂的区,用0x00000001表示根节点和子节点的开始,从0x00000002结束,整个根节点结束 0x00000009 

而节点的名字并不是放在strings block里面 而是紧跟着0x00000001后面加上节点名字,根节点没有名字,因此后面都0,直到0x00000003表示属性开始,那怎么表示名字和值呢,在属性后面有一个结构体,然后再加上val 如果val不是向4取整的话就会自动向4取整,对于第二个属性也一致

struct{uint32_t len;  //val长度 uint32_t nameoff; //表示属性名字在strings blosk的偏移
}

对于字符串的偏移值为188h, 第一个属性为model,大小为0x0A,在字符串区的偏移值为0,值为"53 4D 44 4B 32 34 34 34 30 00 00 00",自动向4取整,紧接着从第二属性开始即"00 00 00 03"


http://chatgpt.dhexx.cn/article/PVhINxP6.shtml

相关文章

Android boot.img dtb.img 编译过程

最近做RK3588案子,修改dts后,导致boot.img过大,编译出错,整体分析下boot.img过大的原因是因为在打包boot.img过程中,dbt.img过大导致,所以整体分析下boot.img编译过程,尤其是dbt.img的生成过程. boot.img生成过程 在Andorid跟目录下执行, source build/envsetup.sh 然后lunch x…

Linux 设备树(二) dtc dts/dtsi dtb的关系

在学习设备树之前&#xff0c;我们先来了解一下跟设备树相关的三个对象&#xff0c;分别是dtc、dts/dtsi、dtb。 dtc:用来编译设备树的工具 dts:设备树描述文件 dtsi:设备树头文件 dtb:编译后的二进制文件 dtc设备树编译工具 dtc是用来编译设备树的工具&#xff0c;就像gcc可以…

dtb如何转换到platform_device

分2步&#xff0c;第一步是首先转换为device_node&#xff0c;第二步device_node转换为platform_device。 第一步 /*** unflatten_device_tree - create tree of device_nodes from flat blob** unflattens the device-tree passed by the firmware, creating the* tree of st…

U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明

最近&#xff0c;工作重心要从裸机开发转移到嵌入式 Linux 系统开发&#xff0c;在之前的博文 Linux 之八 完整嵌入式 Linux 环境、&#xff08;交叉&#xff09;编译工具链、CPU 体系架构、嵌入式系统构建工具 中详细介绍了嵌入式 Linux 环境&#xff0c;接下来就是实际动手学…

dts、dtb的那些事儿

笔者最近在支持新的案子&#xff0c;过于忙碌&#xff0c;好久没更新了&#xff0c;勿怪。 1、设备树大变革故事 2011年3月17日的ARM Linux邮件列表有封邮件“this whole ARM thing is a fucking pain in the ass”引起了轩然大波&#xff0c;原来是我们的Linux之父Linus Torv…

DBT工具简介

What is DBT 在将数据加载到集中式数据仓库之前&#xff0c;必须对其进行清理、保持一致并根据需要进行组合。换句话说&#xff0c;必须转换数据&#xff0c;这就是我们所谓的 ETL&#xff08;提取、转换、加载&#xff09;和 ELT 中的“T”。 这是挖掘数据价值的关键一步。 而…

4.2uboot对设备树的支持——dtb的修改原理

本节说明在uboot中修改dtb的原理。 在uboot中&#xff0c;有一些命令支持对dtb文件进行修改。 当我们想要修改dtb文件时&#xff0c;可以直接修改dts文件&#xff0c;然后编译dts文件生成新的dtb文件&#xff0c;再将新的dtb文件载入设备。 或者&#xff0c;我们也可以在ubo…

DBT是什么

关于DBT DBT 是一种数据转换工作流&#xff0c;可帮助您完成更多工作&#xff0c;同时产生更高质量的结果。您可以使用 dbt 来模块化和集中分析代码&#xff0c;同时还为数据团队提供软件工程工作流中常见的护栏。在将数据模型安全部署到生产环境之前&#xff0c;通过监控和可见…

2.1设备树的规范(dts和dtb)——DTS格式

本节学习设备树的规范。 使用设备树时&#xff0c;需要编写dts文件&#xff0c;然后使用dtc编译dts文件&#xff0c;生成dtb文件。 所以本节分为两部分&#xff0c;第一部分讲解dts格式&#xff0c;第二部分讲解dtb格式。 首先看一下dts文件的布局。 DTS文件布局&#xff0…

关于EMUELC适配各种机型,DTB如何修改教程

很多爱好者留言&#xff0c;都想问关于EMUELC的dtb适配机型问题&#xff0c;这里我就出一个教程&#xff0c;如何修改dtb&#xff0c;然后去适配自己的机型&#xff0c;然后启动 。这里我只是提供方法&#xff0c;具体的调试是需要原理图进行配置。 首先&#xff0c;不管是aml…

DTBO简介

1、DTBO简介 设备树 (DT) 是用于描述“不可发现”硬件的命名节点和属性构成的一种数据结构。操作系统&#xff08;例如在 Android 中使用的 Linux 内核&#xff09;会使用 DT 来支持 Android 设备使用的各种硬件配置。硬件供应商会提供自己的 DT 源文件&#xff0c;接下来…

【DTB/DTBO 分区介绍】

如果你的 DTB/DTBO 位于专属的分区&#xff08;例如 dtb 和 dtbo 分区&#xff09;中&#xff0c;请使用以下表格结构和头文件格式&#xff1a; 数据结构 dt_table_header 仅适用于 dtb/dtbo 分区&#xff1b;您不能在kernel( image.gz) 末尾处附加此格式。如果您有一个 DTB/D…

2.2设备树的规范(dts和dtb)——DTB格式

本节讲述设备树的dtb格式。 上节讲述了dts格式。回顾上节&#xff0c;在dts文件和dtsi文件中&#xff0c;可以使用C语言的define和include&#xff0c;使用方法和作用也同C语言相同。 编写dts文件后&#xff0c;需要使用dtc工具将dts文件编译成dtb文件。dtc工具可以检查dts文…

「设备树」dtb给内核的两种工作模式

一&#xff0c;传递dtb给内核 对于传统bootloader提供两种工作模式&#xff1a;一是启动加载模式&#xff08;start loading&#xff09;&#xff0c;一是下载模式&#xff08;downloading&#xff09; 工作在启动加载模式时&#xff0c;bootloader会自动执行bootcmd命令&#…

设备树_dtb文件分析

前言&#xff1a;我之前的原计划是没有打算写设备树dtb文件分析&#xff0c;但是情势所迫啊&#xff01;&#xff0c;学习还是要一步一步来的。 在前面的章节提到过.dts文件以文本方式对系统设备树进行描述&#xff0c;经过Device Tree Compiler(dtc)将dts文件转换成二进制文件…

Linux设备树学习2 - DTB文件格式

一. DTB文件简介 DTB文件是由DTS文件通过dtc命令编译生成的二进制文件。DTS文件不能直接被内核解析&#xff0c;需要编译成DTB文件才可以直接被内核识别并解析使用的。 二. DTB文件内容布局 从上图可以看出&#xff0c;DTB由四个部分组成&#xff0c;分别是struct fdt_header&a…

setUserVisibleHint-- fragment真正的onResume和onPause方法

这个情况仅适合与多个fragment之间切换时统计&#xff0c;而非activity和fragment同时交互&#xff0c;因当时项目为首页4个fargment时长统计&#xff0c;因此适合&#xff0c;经下面网友评论指出&#xff0c;特在这里写出此问题&#xff0c;因最近项目较忙&#xff0c;具体情况…

onCreate与onStart区别,onStart与onResume区别

http://www.cnblogs.com/kofi1122/archive/2011/04/10/2011772.html Activity生命周期之我见 关于Activity生命周期的文章很多&#xff0c;而且大部分也说得很详细&#xff0c;所以作为关于这方面的内容我本来不想多说&#xff0c;但是大家可能跟我之前一样&#xff0c;在看这方…

Flutter BaseWidget 实现onResume、onPause()

熟读唐诗三百首&#xff0c;不会作诗也会吟。——孙洙 最近用Flutter开发的项目算是完成了开发到上线第一阶段了。任何一个项目开始了&#xff0c;若想追求的是更好&#xff0c;那么就需要下功夫对项目用户体验和代码效率深入的研究了。作为用户和产品经理、老板、UI、不懂技术…

Activity生命周期中onStart()和onResume()的区别

Activity生命周期中onStart()和onResume()的区别 在讲onStart()和onResume()的区别之前&#xff0c;必须清楚Activity的四种状态&#xff1a; 1.Running状态&#xff1a;一个新的Activity的启动入栈后&#xff0c;它在屏幕最前端&#xff0c;处于栈的最顶端&#xff0c;此时它…