☰
Zephyr BSP: 06-SoC设备树实战
2026/9/29 8:15:48 网站建设 项目流程

摘要:本文是 Zephyr SoC Porting 系列的第 06 篇,从实战角度讲解如何为假想 SoC 编写 Devicetree。文章以一个虚构芯片的寄存器地址为起点,逐步演示了 SoC DTSI 与 Board DTS 的区别、UART/GPIO 等外设的 DTS 描述方法、compatible与 Driver 的匹配机制、reg/interrupts/clocks/resets等关键属性的含义,并梳理了从硬件 Memory Map → DTS → Binding → Driver 的完整链路。最后给出 SoC BSP 开发时 DTS 的落地步骤与学习建议,帮助读者建立从"会看 DTS"到"能为自家 SoC 建立 Devicetree 硬件描述体系"的完整思维模型。
SoC Devicetree 实战
这一篇建议正式进入Zephyr SoC Porting 的第二个核心环节:Devicetree。

前面几篇的逻辑已经形成了:

01 Hardware Model ↓ 02 Architecture → SoC → Board ↓ 03 BSP / Board 组织 ↓ 04 SoC Porting ↓ 05 SoC Devicetree ↓ 06 SoC Devicetree 实战 ← 现在

这一篇不要再停留在"Devicetree 是什么",而是要真正做一次:
从一个假想 SoC 的寄存器地址开始,逐步把它描述成 Zephyr 可以使用的 Devicetree。

1. 这一篇要解决什么问题?
假设公司有一颗 SoC:

Company SoC │ ├── CPU │ └── Cortex-M4 │ ├── SYS │ ├── Clock │ └── Reset │ ├── UART0 │ └── 0x4000_0000 │ ├── GPIO │ └── 0x4001_0000 │ ├── TIMER0 │ └── 0x4002_0000 │ └── SPI0 └── 0x4003_0000

硬件手册可能告诉你:

UART0 Base Address=0x40000000 GPIO Base Address=0x40010000 TIMER Base Address=0x40020000 SPI0 Base Address=0x40030000

但是 Zephyr 并不知道这些东西。
你需要把:

Hardware Memory Map ↓ Devicetree ↓ Zephyr Device Model ↓ Driver

连接起来。

2. 最重要的理解:Devicetree 不是"配置文件"
很多刚开始做 Zephyr BSP 的人会把 DTS 理解成:
“一个配置硬件参数的文件。”

这个理解不够准确。
对于 SoC BSP 开发来说,更准确的是:
Devicetree 是 Zephyr 对硬件结构的描述。
例如:

UART0 ├── address ├── size ├── interrupt ├── clock ├── status └── compatible

这些信息最终告诉 Zephyr:

这里有一个 UART 设备 ↓ 它位于 0x40000000 ↓ 它使用 IRQ5↓ 它属于某种 UART compatible ↓ 应该匹配某个 driver

3. SoC Devicetree 与 Board Devicetree 的区别
这是做公司 SoC BSP 时必须掌握的。
假设:

Company SoC │ ├── UART0 ├── UART1 ├── GPIO ├── SPI0 └── TIMER0

这些属于SoC 固有硬件。
而:

开发板 │ ├── LED → GPIO0 Pin5├── Button → GPIO0 Pin10├── UART connector → UART0 └── SPI Flash → SPI0

这些属于Board。
因此:

为了更直观地建立两者的边界认知,下面从五个维度对比 SoC DTSI 与 Board DTS:

对比维度SoC DTSI(mychip.dtsi)Board DTS(myboard.dts)
描述对象SoC 固有硬件:CPU、UART、GPIO、SPI、TIMER 等芯片内部外设开发板上的资源:LED、Button、UART connector、SPI Flash 等板级外设
文件位置dts/arm/mycompany/mychip/mychip.dtsiboards/arm/myboard/myboard.dts
典型内容compatible、reg、interrupts、clocks、resets、gpio-controller等外设属性model、aliases、leds、&uart0 { status = "okay"; }等板级配置
status 默认值通常为"disabled",SoC 层默认关闭所有外设通过&uart0 { status = "okay"; }按需打开
修改频率低,芯片定型后基本不变高,随开发板设计、外设增减而频繁调整

一句话总结:SoC DTSI 回答「这颗芯片有什么」,Board DTS 回答「这块板子怎么用」。前者是芯片的「户口本」,后者是板子的「使用说明书」。

核心原则:
SoC DTSI 描述 SoC 有什么。
Board DTS 描述这块板子怎么使用这些硬件。

4. 一个最小 SoC Devicetree
我们先假设公司 SoC 名叫:

mycompany,mychip

创建:

dts/ └── arm/ └── mycompany/ └── mychip/ └── mychip.dtsi

最简单可以从:

/{compatible="mycompany,mychip";cpus{cpu0:cpu@0{compatible="arm,cortex-m4";device_type="cpu";reg=<0>;};};};

开始。
这里最重要的是:

compatible="mycompany,mychip";

它表示:
这是我们的 SoC。

5. 添加 UART
假设硬件手册:

UART0 Base=0x40000000 Size=0x1000 IRQ=5

那么:

uart0: uart@40000000{compatible="mycompany,my-uart";reg=<0x40000000 0x1000>;interrupts=<5>;status="disabled";};

于是:

Hardware │ │ UART0 │ ├── Base=0x40000000 ├── Size=0x1000 └── IRQ=5│ ↓ Devicetree

到这里,我们已经把 UART0 描述进了 Devicetree。把前面第 4 节的 CPU 节点和第 5 节的 UART0 节点组合起来,就得到一份包含 CPU 和 UART0 的 mychip.dtsi:

/{compatible="mycompany,mychip";/* 整个 SoC 的 compatible,标识这是 mycompany 的 mychip */cpus{#address-cells=<1>;#size-cells=<0>;cpu0:cpu@0{compatible="arm,cortex-m4";/* CPU 内核类型,Zephyr 据此匹配内核支持 */device_type="cpu";/* 固定写法,表示这是一个 CPU 节点 */reg=<0>;/* CPU 编号,单核 SoC 通常为 0 */};};soc{compatible="simple-bus";/* 表示该节点下的子节点都是内存映射外设 */ranges;/* 子节点 reg 地址直接映射到父地址空间 */uart0:uart@40000000{compatible="mycompany,my-uart";/* 外设类型,Driver 靠它匹配 */reg=<0x400000000x1000>;/* 基地址 0x40000000,寄存器空间 0x1000 */interrupts=<5>;/* UART0 使用 IRQ 5 */status="disabled";/* SoC 层默认关闭,由 Board 层打开 */};};};

这段代码把「CPU 是什么、UART0 在哪里、用哪个中断」都描述清楚了,是后续添加 GPIO、TIMER 等外设的基础骨架。
对应:

uart0: uart@40000000{compatible="mycompany,my-uart";reg=<0x40000000 0x1000>;interrupts=<5>;status="disabled";};

6. 为什么默认是 disabled?
这里非常重要。
SoC 可能有:

UART0 UART1 UART2

但某个具体 Board 可能只使用:

UART0

因此 SoC 层通常可以定义:

status="disabled";

然后 Board 打开:

&uart0{status="okay";};

于是:

SoC DTSI UART0 disabled │ ↓ Board DTS │ └── status="okay"

这就是SoC → Board override。

7. 添加 GPIO
假设:

GPIO Base=0x40010000 IRQ=6

可以:

gpio0: gpio@40010000{compatible="mycompany,my-gpio";reg=<0x40010000 0x1000>;interrupts=<6>;gpio-controller;# gpio-cells = <2>;status="disabled";};

这里第一次出现:

gpio-controller;

它表示:
这个 node 是一个 GPIO controller。
而:

# gpio-cells = <2>;

表示其他设备引用 GPIO 时,需要提供两个参数。
例如:

led0{gpios=<&gpio05GPIO_ACTIVE_HIGH>;};

现在把 GPIO0 和 TIMER0 也加进来,就得到一份包含 CPU、UART0、GPIO0、TIMER0 四个节点的完整 mychip.dtsi:

/{compatible="mycompany,mychip";/* 整个 SoC 的 compatible,标识这是 mycompany 的 m

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询