☰
Zephyr BSP: 15-Zephyr Devicetree Dependency Graph
2026/9/29 9:48:11 网站建设 项目流程

摘要:本文从clocks = <&clock0>这一行出发,系统讲解 Zephyr Devicetree 依赖图的完整链路。首先通过真实例子认识phandle如何表达设备间的依赖关系;接着解释 Zephyr 为什么需要dependency ordinal作为 Devicetree node 的内部编号;随后说明device handle如何描述设备依赖,并最终通过DEVICE_DT_DEFINE()生成struct device、通过DEVICE_DT_GET()获取设备对象。文章还厘清了dependency ordinal与初始化优先级、DEVICE_DT_GET()与依赖关系等易混概念,最后给出编写公司 SoC BSP 时应遵循的「三层模型」原则:让 DTS 描述依赖、让 Driver 描述操作。

Zephyr Devicetree Dependency Graph
1. 先看一个真实的例子
假设我们有:

uart0:uart@40004000{compatible="mycompany,my-uart";reg=<0x400040000x1000>;clocks=<&clock0>;interrupts=<5>;status="okay";};

同时:

clock0:clock@40000000{compatible="mycompany,my-clock";reg=<0x400000000x1000>;status="okay";};

这里最重要的一行:

clocks=<&clock0>;

&clock0 就是一个phandle。
它表达的是:
UART0 依赖 clock0。

所以设备之间实际上已经形成了一个图:

┌──────────────┐ │ clock0 │ │ Clock Dev │ └──────▲───────┘ │ clocks │ ┌──────┴───────┐ │ uart0 │ │ UART Dev │ └──────────────┘

这就是:
Dependency Graph**

2. phandle 到底是什么?
很多人第一次看 Devicetree 会误以为:

clocks=<&clock0>;

相当于:

clocks=&clock0;

不是。
Devicetree 本身并不是 C。
&clock0 是 Devicetree 中的:
节点引用
也就是:

&clock0 │ ▼ clock0node

例如:

clock0: clock@40000000

这里:

clock0

是 label。
于是:

&clock0

就可以引用这个 node。

3. 那 Zephyr 为什么需要 dependency ordinal?
因为最终 C 程序需要一种方式,把:

clock0node

和:

struct device

关联起来。
但是 Zephyr 不能简单地直接生成:

struct device\*uart_clock=&clock0_device;

因为:

  • Devicetree node 不是 C symbol
  • node 名字不是 C linker symbol
  • 不同 instance 可能有相同的 node name
  • Devicetree 是一个编译期硬件描述系统
    所以 Zephyr 建立了一层非常重要的映射:
Devicetreenode│ ▼ dependency ordinal │ ▼ device handle │ ▼ struct device

4. Dependency ordinal 是什么?
可以把它简单理解成:
Zephyr 给每个 Devicetree node 分配的一个内部编号。
例如:

clock0node↓ ordinal12uart0node↓ ordinal27

那么:

clock0 →12uart0 →27

这个数字不是:

UART 的地址

也不是:

IRQ number

也不是:

device ID

而是:
Zephyr Devicetree dependency graph 中的节点编号。

5. 为什么需要这个编号?
因为 Zephyr 最终需要把:

clocks=<&clock0>;

变成某种机器可处理的信息。
可以想象生成阶段得到:

uart0 dependency: clock0

转换成:

uart0 dependency ordinal:27clock0 dependency ordinal:12

然后可以进一步表达:

UART0 device │ ├── dependency#12│ └──...

6. Device Handle 是什么?
这里就进入 Zephyr Device Model 最关键的地方。
Zephyr 的:

struct device

并不是孤立存在的。
在内部,它可以携带一组:
device handles

用来描述设备之间的关系。
概念上可以理解成:

UART0 struct device │ │ handles ▼ ┌───────────┐ │ clock0 │ └───────────┘

6.1 实战:一个完整的依赖图生成示例

下面用一个包含clock0、pinmux0、uart0三个节点的 DTS 文件,完整展示 Zephyr 构建系统如何为它们分配 dependency ordinal、生成 device handles,并最终映射到struct device。

第一步:DTS 文件

/ { soc { clock0: clock-controller@40000000 { compatible = "mycompany,my-clock"; reg = <0x40000000 0x1000>; status = "okay"; }; pinmux0: pinmux@40001000 { compatible = "mycompany,my-pinmux"; reg = <0x40001000 0x1000>; status = "okay"; }; uart0: serial@40002000 { compatible = "mycompany,my-uart"; reg = <0x40002000 0x1000>; clocks = <&clock0>; pinctrl-0 = <&pinmux0>; interrupts = <10>; status = "okay"; }; }; };

这里uart0通过clocks = <&clock0>和pinctrl-0 = <&pinmux0>声明了对clock0和pinmux0的依赖。

第二步:Zephyr 构建系统分配 dependency ordinal

Zephyr 在编译期扫描整个 Devicetree,为每个 node 分配一个全局唯一的 dependency ordinal(编号越小,依赖越靠前):

clock0node→ dependency ordinal=12pinmux0node→ dependency ordinal=15uart0node→ dependency ordinal=27

注意:ordinal 不是初始化优先级,它只是 Devicetree dependency graph 中的节点编号。

第三步:生成 device handles

每个struct device内部会携带一组 device handles,用来描述它依赖哪些设备。uart0的 device 会持有指向clock0和pinmux0的 handle:

┌──────────────────────────────┐ │ uart0 struct device │ │ │ │ handles: │ │ ├──► handle#12 (clock0) ││ └──► handle#15 (pinmux0)│└──────────────────────────────┘

第四步:device handles 关系图

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

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

立即咨询