摘要:本文从 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 = <0x40013800 0x400>;
interrupts = <37>;
...
};
这表达的是:
芯片里面存在 USART1。
但是:
NUCLEO-F303RE
可能把它连接到了某些板级接口。
所以 Board DTS 再决定:
chosen {
zephyr,console = &usart1;
};
于是:
Application
│
│ printk()
▼
Zephyr Console
│
▼
USART1 Driver
│
▼
STM32 USART1
│
▼
NUCLEO–F303RE
│
▼
ST–LINK / 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,应该改哪些地方"。



