QA:dts和dtsi的区别
在嵌入式 Linux 系统中,DTS(Device Tree Source)和 DTSI(Device Tree Source Include)是描述硬件设备树的文件格式。它们本质上是同一种语法的文件,但在使用上有一定区别。以下是两者的主要差异和用途:
1. 文件类型和用途
-
DTS (Device Tree Source)
DTS 文件是设备树的主文件,描述了一个具体设备或板子的硬件配置。每个具体的硬件设备都会有一个对应的 DTS 文件,它是系统用来解析设备硬件结构的主要输入。
例如:board.dts
。 -
DTSI (Device Tree Source Include)
DTSI 文件是设备树的包含文件,通常是一个共享的硬件描述片段,供多个 DTS 文件引用。DTSI 文件中定义的是通用或模块化的硬件配置,目的是实现代码复用。
例如:chip.dtsi
。
2. 内容层次
-
DTS
通常定义特定板级硬件相关的配置,例如 GPIO 引脚分配、外设启用等。/dts-v1/; /include/ "chip.dtsi" / { model = "Custom Board"; compatible = "custom,board"; aliases { serial0 = &uart0; }; chosen { stdout-path = &uart0; }; };
-
DTSI
通常定义 SoC(芯片)级或通用硬件部分的配置,适用于多个设备树文件。/dts-v1/; / { soc { uart0: serial@10000000 { compatible = "ns16550"; reg = <0x10000000 0x1000>; clock-frequency = <24000000>; }; }; };
3. 继承和覆盖
DTS 文件通过 /include/
指令包含 DTSI 文件中的内容,然后可以覆盖其中的属性。
例如:
- 在
chip.dtsi
中定义了 UART 外设; - 在
board.dts
中可以基于chip.dtsi
,启用或修改 UART 的配置。
继承示例
chip.dtsi
:
/include/ "chip.dtsi"
&uart0 {
status = "okay"; // 覆盖为启用
};
board.dts
:
/include/ "chip.dtsi"
&uart0 {
status = "okay"; // 覆盖为启用
};
4. 文件组织
- DTSI 通常由芯片厂商或 SoC 开发人员提供,用于定义通用部分,例如 CPU、外设等。
- DTS 通常由板级开发人员创建,用于描述特定开发板或产品的具体硬件配置。
5. 总结对比
属性 | DTS | DTSI |
---|---|---|
用途 | 定义具体设备或板级配置 | 定义共享或通用硬件配置 |
作用 | 具体化设备树描述 | 模块化、复用 |
包含关系 | 可引用多个 DTSI 文件 | 通常不引用其他文件 |
适用范围 | 针对单个设备/板子 | 针对芯片级或通用配置 |
通过这种分层结构,可以实现设备树文件的模块化和复用,从而简化开发和维护工作。
QA:如果我是板级工程师,那么我不应该修改dtsi文件,而是只能新增或者修改dts文件?
是的,作为板级工程师,一般不建议直接修改 DTSI 文件,而是通过新增或修改 DTS 文件来完成硬件描述。这样做的原因如下:
为什么不建议修改 DTSI 文件?
-
DTSI 文件通常由芯片厂商或 BSP 提供
- DTSI 文件一般描述的是芯片级(SoC 级)的硬件资源和特性,这些内容是通用的,适用于多种不同的开发板或设备。修改 DTSI 可能会影响其他使用相同芯片的项目。
- 如果您修改了 DTSI 文件,在后续升级厂商提供的 BSP(Board Support Package)时,可能会导致冲突,甚至丢失修改内容。
-
分层设计的目的
- DTSI 文件的作用是描述通用硬件资源,而 DTS 文件是为具体板级定制的。通过继承和覆盖,可以避免直接修改通用内容,从而维护良好的分层结构。
-
方便维护和协作
- 当多个工程师协作开发时,如果每个人都能随意修改 DTSI 文件,会导致难以追踪改动来源,增加协作复杂度。
- 如果只修改 DTS 文件,改动是板级特有的,容易维护和调试。
正确的做法:通过 DTS 文件实现定制化
如果需要在板级启用或修改某些硬件资源,可以在 DTS 文件中通过继承和覆盖的方式实现。以下是常见情况的处理方式:
1. 启用某些外设
DTSI 文件中通常将外设状态设置为 "disabled"
,您可以在 DTS 文件中将其改为 "okay"
。
/include/ "chip.dtsi"
&uart0 {
status = "okay"; // 启用 UART0
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
};
2. 添加板级特有的外设
如果板子上有芯片厂商未定义的外设,比如额外的传感器或定制模块,可以直接在 DTS 文件中新增节点,而无需修改 DTSI 文件。
&i2c1 {
status = "okay";
temp_sensor@48 {
compatible = "mycompany,temp-sensor";
reg = <0x48>;
};
};
3. 覆盖特定配置
如果板级需要调整某些硬件资源(例如 GPIO 引脚或时钟频率),可以在 DTS 中覆盖 DTSI 的默认配置:
&spi0 {
status = "okay";
cs-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; // 修改片选引脚
};
如果修改 DTSI 是必需的情况
尽管一般不建议修改 DTSI 文件,但在以下情况下,可以考虑调整:
-
厂商提供的 DTSI 文件存在错误
如果您确认芯片厂商的 DTSI 文件有问题(如硬件地址或属性错误),可以修复后在项目内使用。 -
特定项目的定制需求
如果您确定该 DTSI 文件仅用于当前项目,且不会影响其他设备或板子,可以直接修改,但需要清楚记录原因。 -
实验性调试
如果某些问题需要快速验证,您可以临时修改 DTSI,但在正式实现时应将更改移到 DTS 文件中。
总结建议
- 优先修改 DTS 文件:通过覆盖或新增节点实现板级定制。
- 尽量避免修改 DTSI 文件:保持芯片级硬件描述的通用性。
- 如需修改 DTSI 文件:确保原因充分,并明确记录,以便后续维护。
通过这种方式,可以既满足项目需求,又维持代码的模块化和可维护性。
QA:有人说dts和dtsi的关系就像.c文件和.h文件的include关系。这样的说法准确吗?
网上将 DTS 和 DTSI 的关系类比为 .c 文件和 .h 文件的关系,并不完全准确。这种类比可能有助于初学者快速理解它们的引用方式,但实际上两者的作用和结构存在显著差异。
为什么类比不准确?
1. .h 文件与 .c 文件的分工明确
- .h 文件:主要是接口声明,定义函数、变量或结构体的原型。它不包含具体实现。
- .c 文件:主要是实现具体功能,编写函数的逻辑代码。
这种分工明确的方式在 DTS 和 DTSI 中并不存在,因为 DTSI 文件不仅包含“声明”内容,还包含许多具体配置(属性和硬件节点),可以直接被系统解析和使用。
2. DTSI 文件包含具体实现
DTSI 文件并不是简单的“声明文件”,而是包含大量具体硬件配置。例如:
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
clock-frequency = <24000000>;
status = "disabled";
};
以上代码实际上已经定义了 UART 的硬件地址、寄存器大小和时钟频率,这些是完整的硬件描述,直接参与设备树解析,并非简单的“接口声明”。
3. DTS 和 DTSI 的关系是“继承与覆盖”
DTS 文件从 DTSI 文件中“继承”节点配置,并可以通过覆盖来调整具体的属性值。这种行为更接近面向对象编程中的“继承”关系,而不是简单的“引用”关系。
更贴切的类比是什么?
将 DTSI 比作一个“父类”或“模板”,将 DTS 比作一个“子类”可能更恰当:
- DTSI 定义了通用的硬件节点和默认配置,相当于父类提供的通用功能和默认属性。
- DTS 则在继承 DTSI 内容的基础上,进一步细化或修改配置,相当于子类对父类进行扩展或覆盖。
示例(继承与覆盖关系)
// chip.dtsi
soc {
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
clock-frequency = <24000000>;
status = "disabled"; // 默认禁用
};
};
// board.dts
/include/ "chip.dtsi"
&uart0 {
status = "okay"; // 覆盖为启用
};
在这个例子中:
chip.dtsi
定义了通用的 UART 硬件节点和属性;board.dts
继承了chip.dtsi
中的内容,并根据具体板级需求调整状态。
总结:为什么网上的类比有局限性?
- DTSI 文件不仅仅是“声明”性质,它包含具体实现,而 .h 文件不包含实现。
- DTS 文件不是从零开始实现所有功能,而是基于 DTSI 的内容进行扩展或覆盖。
- 它们的关系更接近“模板(父类)”与“实例(子类)”,而不是“接口与实现”。
因此,用 继承与覆盖 来描述 DTS 和 DTSI 的关系,比简单的 .c
和 .h
文件类比更准确,也更符合设备树的实际用法。