欢迎光临
我们一直在努力

轻松学习Zephyr BSP: 02-Zephyr架构组织解析

摘要:本文从 Zephyr 的目录结构出发,系统梳理 Architecture(CPU 架构)→ SoC(芯片)→ Board(开发板) 三个层次的职责划分与协作关系。你将理解:arch/ 回答「CPU 怎么运行」,soc/ 回答「这颗芯片有什么」,boards/ 回答「这块板子怎么把芯片用起来」,而 dts/ 则用 Devicetree 把三者串成完整的硬件描述。文章以 STM32F303RE / NUCLEO-F303RE 为例,逐步拆解 SoC 与 Board 的区别、Devicetree 与 Kconfig 的分工,并给出公司 SoC 接入 Zephyr 时「SoC 只实现一次、Board 可多个」的 BSP 设计原则,为后续学习 SoC / Board Porting 打下基础。

这一篇非常关键。

如果你的最终目标是把公司的 SoC 芯片加入 Zephyr 开发生态,那么你现在不能只把 boards/ 当成"开发板目录"来看。你需要建立一个非常清晰的概念:

Architecture 决定 CPU 怎么跑 → SoC 决定这颗芯片有什么 → Board 决定这块板子怎么把芯片用起来。

Zephyr 官方的 SoC Porting Guide 和 Board Porting Guide 也是按照这个层次组织的。

Zephyr:Architecture → SoC → Board 到底怎么组织

先记住这一张图:
在这里插入图片描述
但是这里有一个非常重要的细节:

这不是简单的"目录包含关系"。

例如:

ARCH

└── SoC

└── Board

更准确地说是:
在这里插入图片描述
而 Devicetree 又把它们串起来了。

1. 先看 Zephyr 的三个目录

在你的 ~/zephyrproject/zephyr:

zephyr/
├── arch/
├── soc/
├── boards/
├── dts/
├── drivers/
├── kernel/
├── subsys/
├── include/
└── ...

其中今天最重要的是:

arch/
soc/
boards/
dts/

可以把它们理解成:

目录回答的问题
arch/ CPU 怎么运行?
soc/ 这颗芯片是什么?
boards/ 这块开发板是什么?
dts/ 硬件实际上怎么连接?

这四个东西共同构成 Zephyr 的硬件适配层。

2. Architecture:CPU 架构

例如你的 STM32F303RE。
它使用:

ARM Cortex-M4

所以它首先属于:

ARM
└── ARM Cortex-M

Zephyr 的 arch/ 负责的是:

CPU architecture

├── reset
├── interrupt
├── exception
├── context switch
├── CPU register
├── stack
└── thread switching

也就是说:
arch/ 不关心你的 STM32F303RE 板子上 LED 接在哪个 GPIO。
它甚至不应该关心:

UART1
SPI2
I2C1
GPIOA

这些属于更下面的 SoC / DeviceTree / Driver 层。

3. 为什么 Architecture 要独立出来?

这是 Zephyr 能够支持大量芯片的关键。
假设:

STM32F303
STM32F407
STM32H743

都是:

ARM Cortex-M

那么它们不应该分别实现:

thread switching
interrupt entry
exception handling
stack initialization

否则会产生大量重复代码。
所以: 在这里插入图片描述
这些 SoC 可以共享 Architecture 层。
这就是:
Architecture 是 CPU 共性。

4. SoC:真正开始进入"芯片世界"
然后到了:

soc/

这里才真正开始描述:
我的这颗 SoC 到底是什么。

官方 SoC Porting Guide 给出的基本结构类似:

soc/<VENDOR>/<soc-name>/
├── soc.yml
├── soc.h
├── CMakeLists.txt
├── Kconfig
├── Kconfig.soc
└── Kconfig.defconfig

比如可以抽象成:

soc/
└── st/
└── stm32/
├── common/
├── stm32f0/
├── stm32f1/
├── stm32f3/
├── stm32f4/
└── ...

这里开始出现:

STM32

而不是:

Cortex-M4

5. SoC 层到底负责什么?

例如你的 STM32F303。
SoC 层可能涉及:

STM32F303

├── CPU
├── NVIC
├── Clock
├── RCC
├── Flash
├── SRAM
├── UART
├── SPI
├── I2C
├── GPIO
├── Timer
├── ADC
└── ...

所以:

ARCH

回答:

Cortex-M4 怎么运行?

而:

SoC

回答:

STM32F303 这颗芯片有哪些硬件资源,以及这些资源如何初始化?

这就是两个非常不同的层次。

6. Board:开发板又是什么?
假设芯片是:

STM32F303RE

但是你有:

NUCLEO-F303RE

那么:

STM32F303RE ≠ NUCLEO-F303RE

因为:

STM32F303RE

是芯片。

而:

NUCLEO-F303RE

是:
一块使用 STM32F303RE 的具体 PCB。

因此:

boards/
└── st/
└── nucleo_f303re/

描述的是板子。

7. Board 层负责什么?

例如:

NUCLEO-F303RE

板子上可能有:
在这里插入图片描述
这些东西:
不是 STM32F303RE 芯片本身决定的。

而是:
NUCLEO-F303RE 这块板子怎么把芯片的资源连接出来。

所以 Board 层描述:

– 这个 LED 接哪里?
– 这个 Button 接哪里?
– 这个 UART 用哪组 Pin?
– 这个板子的默认 console 是哪个 UART?

这就是 Board。

8. 一个非常重要的区别
你以后做公司 SoC 时,一定要区分:

SoC

和:

Board

例如公司有一颗:

ABC123

然后公司有:

ABC123-EVK
ABC123-DK
ABC123-Reference

那么应该是:
在这里插入图片描述
SoC 只实现一次。
Board 可以有很多个。
这就是你以后做 BSP 最重要的设计原则之一。

9. Devicetree 是怎么把三者串起来的?

这里才是整个 Zephyr 硬件模型最关键的地方。

例如:

boards/.../nucleo_f303re/nucleo_f303re.dts

可能会包含:

STM32F303 SoC .dtsi

最终形成:

nucleo_f303re.dts

├── stm32f303xx.dtsi

├── ARM/common DTSI

└── Board-specific hardware

官方文档明确说明,Board 的 .dts 通常会 include SoC 的 .dtsi,SoC .dtsi 描述 CPU/SoC 的硬件资源,而 Board .dts 再描述板级硬件。

所以最终可以画成:

Board DTS

│ include

SoC DTSI

│ include

ARCH DTSI

这非常重要。

10. 举一个 UART 的完整例子

假设:

STM32F303

里面有:

USART1

那么 SoC 层可以描述:

usart1: serial@40013800 {
compatible = "st,stm32-usart";
reg = &lt;0x40013800 0x400&gt;;
interrupts = &lt;37&gt;;
...
};

这表达的是:
芯片里面存在 USART1。

但是:
NUCLEO-F303RE

可能把它连接到了某些板级接口。
所以 Board DTS 再决定:

chosen {
zephyr,console = &usart1;
};

于是:

Application

printk()

Zephyr Console


USART1 Driver


STM32 USART1


NUCLEOF303RE


STLINK / UART


macOS Terminal

这就是你之前做的:

NUCLEO-F303RE → ST-LINK → macOS → UART terminal

背后的 Zephyr 硬件模型。

11. 所以不要把 DTS 当成"配置文件"

这是我建议你现在重点改变的一个认识。
很多初学者认为:

DTS = 配置文件

不够准确。

更准确的是:
**Devicetree 是 Zephyr 对硬件结构的描述语言。**

它描述:

CPU
Memory
Bus
Peripheral
Interrupt
GPIO
Clock
Pin
Device

然后 Zephyr 根据这些硬件描述生成对应的配置和代码连接。

12. Kconfig 又在干什么?
这时候再把:

Kconfig

放进来。

你可以先简单理解:

Devicetree
\\=
"硬件有什么、在哪里、怎么连接"

Kconfig
\\=
"软件要不要启用它、怎么编译它"

例如:

DTS:
USART1 存在
USART1 address = ...
USART1 interrupt = ...

而 Kconfig:

CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y

表示:

我要把 UART 驱动和 UART console 编译进来。

所以:

Hardware description

DeviceTree


Hardware


Software options ─ Kconfig


Zephyr Build System

13. 把整个关系画完整

这张图你建议直接记住:

14. 你以后做公司 SoC,应该怎么思考?

这才是这篇对你真正重要的地方。
假设你公司的芯片叫:

ACME123

CPU 是:

RISC-V RV32

那么第一层:

arch/
└── riscv/

你通常不需要重新做 RISC-V architecture。

然后:

soc/
└── acme/
└── acme123/

这里做:

ACME123
├── CPU configuration
├── interrupt controller
├── clock
├── timer
├── UART
├── GPIO
├── SPI
├── I2C
├─ DMA
├── SRAM
├── Flash
└── ...

然后:

boards/
└── acme/
├── acme123_evk/
├── acme123_dk/
└── acme123_reference/

每块板子只描述:

EVK
├── LED → GPIOx
├── Button → GPIOy
├── UART → UART0
└── sensor → I2C1

DK
├── LED → GPIOz
├── Button → GPIOa
└── UART → UART1

这就形成:
在这里插入图片描述
这才是一个真正可扩展的 BSP 架构。

15. 为什么 Zephyr 不把 SoC 放到 Board 里面?

这是你下一阶段特别容易问到的问题。

错误理解:

boards/
└── acme123_evk/
└── 把所有 SoC 代码都放这里

这样做的问题是:

ACME123 SoC

├── EVK
├── DK
├── Reference
└── Customer Board

你就要复制四份:

clock
UART
GPIO
interrupt
timer

非常痛苦。

正确方式: 在这里插入图片描述
SoC 共用。
Board 差异放 Board。
这也是 Zephyr 官方建议将 SoC 家族/系列中的共性功能抽出来避免重复的原因。

16. 你现在学习 Zephyr,目录应该这样读

我建议你不要从:

zephyr/

一层一层全部看。
那会非常容易迷路。
而应该用一块具体芯片追踪。

你已经有:

NUCLEO-F303RE

所以现在就用它作为"教材"。
按照这个顺序:

NUCLEO-F303RE


boards/


nucleo_f303re


BOARD.dts


STM32F303 SoC DTSI


ARM Cortex-M architecture


arch/arm/

然后再反过来:

BOARD

SoC

ARCH

去看:

Kconfig
CMakeLists.txt
DeviceTree
Linker
Startup
Interrupt
Clock
Drivers

17. 对你来说,真正应该建立的是这张"BSP 心智模型"

以后拿到公司 SoC Spec,不要马上问:
“我要创建哪个 board?”
而应该按照这个顺序问: 在这里插入图片描述
这实际上已经开始进入你真正需要学习的:
Zephyr BSP / SoC Porting。

18. 最后给你一句最重要的话

你之前的 Zephyr 学习路线更像:

Application

GPIO

Interrupt

Driver

Project

现在应该开始升级成: 在这里插入图片描述
而你公司的 SoC 加入 Zephyr,真正的核心工作就是:
在这里插入图片描述
官方现在也明确把 Architecture Porting、SoC Porting、Board Porting 分成三个独立的硬件移植层次。

所以我建议你的下一篇 03-Zephyr BSP 不要马上写"怎么创建一个 Board"。
18.5 west build 到底做了什么?

既然你已经理解了 Board → SoC → Architecture 的层次关系,那我们就顺着这条线,把 west build -b nucleo_f303re 这条命令背后发生的事情完整走一遍。

第一步:west 找到 Board 目录

当你执行:

west build -b nucleo_f303re -p always

west 会先在 Zephyr 的 boards/ 目录里搜索名为 nucleo_f303re 的板子。搜索路径不止一个,它会按顺序查找:

boards/
├── st/
│ └── nucleo_f303re/
│ ├── nucleo_f303re.dts
│ ├── nucleo_f303re_defconfig
│ ├── Kconfig.board
│ ├── Kconfig.defconfig
│ ├── board.cmake
│ └── CMakeLists.txt

找到 nucleo_f303re 之后,Zephyr 构建系统就拿到了这块板子的入口。

第二步:从 Board 反查 SoC

打开 nucleo_f303re_defconfig,你会看到类似:

CONFIG_SOC_SERIES_STM32F3X=y
CONFIG_SOC_STM32F303XE=y

这两行告诉构建系统:这块板子用的是 STM32F303 这颗 SoC。于是构建系统立刻转向:

soc/
└── st/
└── stm32/
├── stm32f3/
│ ├── soc.c
│ ├── soc.h
│ ├── Kconfig.soc
│ ├── Kconfig.defconfig
│ └── CMakeLists.txt
└── common/

第三步:从 SoC 反查 Architecture

继续往下,soc/st/stm32/stm32f3/Kconfig.soc 里会声明:

config SOC_STM32F303XE
select ARM
select CPU_CORTEX_M4

于是构建系统又知道:这颗 SoC 的 CPU 是 ARM Cortex-M4,属于 arch/arm/。它接着去加载:

arch/
└── arm/
├── core/
│ └── cortex_m/
│ ├── CMakeLists.txt
│ ├── Kconfig
│ └── linker.ld
└── include/

第四步:DTS 的 include 链

构建系统在编译前,会先把 Devicetree 源文件展开。nucleo_f303re.dts 会 include SoC 的 .dtsi,SoC 的 .dtsi 又会 include ARM 架构的公共 DTSI:

nucleo_f303re.dts

#include

stm32f303xe.dtsi

#include

armv7-m.dtsi

最终展开成一份完整的、描述整块板子硬件结构的 Devicetree,再交给 Zephyr 的 devicetree 生成器,产出 devicetree_generated.h 等头文件。

第五步:Kconfig 的合并

与此同时,构建系统会把所有 Kconfig 按层次合并:

Board Kconfig

├── SoC Kconfig

└── Architecture Kconfig

最终生成 .config,决定哪些驱动、子系统、内核功能被编译进来。

第六步:CMake 与 Linker 汇合

CMake 根据上面收集到的信息,把以下内容组织进构建:

Kconfig → 决定编译哪些源文件
Devicetree → 生成硬件描述头文件
CMakeLists.txt → 决定编译顺序与链接选项
Linker Script → 决定代码段、数据段、堆栈的布局

最后链接生成:

build/zephyr/zephyr.elf

完整流程图

#mermaid-svg-KCIol28eJxBZ74rA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KCIol28eJxBZ74rA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KCIol28eJxBZ74rA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KCIol28eJxBZ74rA .error-icon{fill:#552222;}#mermaid-svg-KCIol28eJxBZ74rA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KCIol28eJxBZ74rA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KCIol28eJxBZ74rA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KCIol28eJxBZ74rA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KCIol28eJxBZ74rA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KCIol28eJxBZ74rA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KCIol28eJxBZ74rA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KCIol28eJxBZ74rA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KCIol28eJxBZ74rA .marker.cross{stroke:#333333;}#mermaid-svg-KCIol28eJxBZ74rA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KCIol28eJxBZ74rA p{margin:0;}#mermaid-svg-KCIol28eJxBZ74rA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-KCIol28eJxBZ74rA .cluster-label text{fill:#333;}#mermaid-svg-KCIol28eJxBZ74rA .cluster-label span{color:#333;}#mermaid-svg-KCIol28eJxBZ74rA .cluster-label span p{background-color:transparent;}#mermaid-svg-KCIol28eJxBZ74rA .label text,#mermaid-svg-KCIol28eJxBZ74rA span{fill:#333;color:#333;}#mermaid-svg-KCIol28eJxBZ74rA .node rect,#mermaid-svg-KCIol28eJxBZ74rA .node circle,#mermaid-svg-KCIol28eJxBZ74rA .node ellipse,#mermaid-svg-KCIol28eJxBZ74rA .node polygon,#mermaid-svg-KCIol28eJxBZ74rA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KCIol28eJxBZ74rA .rough-node .label text,#mermaid-svg-KCIol28eJxBZ74rA .node .label text,#mermaid-svg-KCIol28eJxBZ74rA .image-shape .label,#mermaid-svg-KCIol28eJxBZ74rA .icon-shape .label{text-anchor:middle;}#mermaid-svg-KCIol28eJxBZ74rA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KCIol28eJxBZ74rA .rough-node .label,#mermaid-svg-KCIol28eJxBZ74rA .node .label,#mermaid-svg-KCIol28eJxBZ74rA .image-shape .label,#mermaid-svg-KCIol28eJxBZ74rA .icon-shape .label{text-align:center;}#mermaid-svg-KCIol28eJxBZ74rA .node.clickable{cursor:pointer;}#mermaid-svg-KCIol28eJxBZ74rA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KCIol28eJxBZ74rA .arrowheadPath{fill:#333333;}#mermaid-svg-KCIol28eJxBZ74rA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KCIol28eJxBZ74rA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KCIol28eJxBZ74rA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KCIol28eJxBZ74rA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KCIol28eJxBZ74rA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KCIol28eJxBZ74rA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KCIol28eJxBZ74rA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KCIol28eJxBZ74rA .cluster text{fill:#333;}#mermaid-svg-KCIol28eJxBZ74rA .cluster span{color:#333;}#mermaid-svg-KCIol28eJxBZ74rA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KCIol28eJxBZ74rA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KCIol28eJxBZ74rA rect.text{fill:none;stroke-width:0;}#mermaid-svg-KCIol28eJxBZ74rA .icon-shape,#mermaid-svg-KCIol28eJxBZ74rA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KCIol28eJxBZ74rA .icon-shape p,#mermaid-svg-KCIol28eJxBZ74rA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KCIol28eJxBZ74rA .icon-shape .label rect,#mermaid-svg-KCIol28eJxBZ74rA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KCIol28eJxBZ74rA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KCIol28eJxBZ74rA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KCIol28eJxBZ74rA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

west build -b nucleo_f303re

查找 boards/st/nucleo_f303re/

读取 nucleo_f303re_defconfig

定位 SoC: STM32F303

读取 soc/st/stm32/stm32f3/

定位 Architecture: ARM Cortex-M4

读取 arch/arm/core/cortex_m/

展开 nucleo_f303re.dts

include stm32f303xe.dtsi

include armv7-m.dtsi

生成 devicetree_generated.h

合并 SoC Kconfig

合并 Architecture Kconfig

合并 Board Kconfig

生成 .config

CMake 组织编译

链接 zephyr.elf

实际目录查找顺序总结

以 west build -b nucleo_f303re 为例,Zephyr 的查找顺序是:

1. boards/st/nucleo_f303re/
├── nucleo_f303re.dts
├── nucleo_f303re_defconfig
└── Kconfig.board

2. soc/st/stm32/stm32f3/
├── soc.c
├── Kconfig.soc
└── Kconfig.defconfig

3. arch/arm/core/cortex_m/
├── CMakeLists.txt
├── Kconfig
└── linker.ld

4. dts/arm/st/f3/
├── stm32f303xe.dtsi
└── armv7-m.dtsi

一句话总结:west build 的本质,就是从 Board 出发,沿着 Board → SoC → Architecture 这条链,把 Kconfig、DTS、CMake、Linker 一层层收集起来,最终拼装出 zephyr.elf。你以后拿到公司 SoC SDK,只要顺着这条链反推,就能知道该往哪个目录里放什么文件。

下一篇应该专门搞懂:
west build -b xxx 到底发生了什么?Zephyr 是如何根据 Board → SoC → Architecture 找到 Kconfig、DTS、CMake、Linker,最后生成 zephyr.elf 的?

这个搞懂之后,你以后拿到公司 SoC SDK,基本就能开始反推"这个 SoC 要接入 Zephyr,应该改哪些地方"。

赞(0)
未经允许不得转载:171主机测评 » 轻松学习Zephyr BSP: 02-Zephyr架构组织解析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址