摘要:本文是 Zephyr 系列后半部分(20~40 篇)的总纲。前半部分(01~19 篇)解决「Zephyr 是怎么工作的」,从第 20 篇起,学习主线正式切换为「如何把公司自研 SoC 接入 Zephyr,做成一套可维护、可扩展、可 upstream 的 BSP」。文章先建立 Company SoC → Zephyr BSP 的全局地图,再给出 20~40 篇的完整路线图:从最小 SoC Port、CPU/Architecture 边界、Startup、中断控制器、时钟/复位,到 Devicetree、Binding、各外设驱动、Board、Kconfig、构建系统、链接脚本、烧录调试与 BSP 验证,最后讨论 Vendor HAL 边界、多芯片家族架构与 BSP 完整生命周期。
本文是 Zephyr 系列后半部分(20~40 篇)的总纲。如果说前 19 篇解决的是「Zephyr 是怎么工作的」,那么从第 20 篇起,学习主线将正式切换为「设计 Zephyr BSP」——把公司自研 SoC 接入 Zephyr,最终交付一套可维护、可扩展、可 upstream 的 BSP。这一转变不仅是技术栈的延伸,更是思维方式的升级:从读懂源码,到亲手搭建 SoC Port、Board、Driver、HAL、Devicetree、Kconfig 与 Build System 的完整链路。本文将先建立 Company SoC → Zephyr BSP 的全局地图,再给出 20~40 篇的完整路线图,并附上贯穿始终的核心架构图与统一的工程化学习模板,帮助你从「读者」成长为真正的 BSP Engineer。
目录导航
本文作为 Zephyr 系列后半部分的总纲,将按以下主线展开,你可以根据兴趣直接跳转到对应章节:
- 学习主线切换:从「读 Zephyr」到「设计 Zephyr BSP」 —— 对比两个阶段在目标、核心问题、产出物、验证方式上的差异
- 第一张核心图 —— Zephyr App → Drivers → HAL → SoC → Board 的全局架构图
- 20~30 篇路线图 —— 从 BSP 全局架构到 Board Support Package 的逐篇推进
- BSP 目录树 —— Company SoC Zephyr BSP 的完整目录结构与职责说明
- BSP 验证清单 —— 各外设对应的 Zephyr sample、验证命令与通过标准
- 学习方法模板 —— 每篇统一的工程化推进模板(①~⑧)
- 最终能力目标 —— 从「会写 driver」到「看到新 SoC 能独立判断归属层」
后半部分的学习方法也从「读 Zephyr」转变为「设计 Zephyr BSP」,每篇采用统一的工程化模板推进。
20 篇开始,学习主线应该正式切换。
前 01~19 篇解决的是:
“Zephyr 是怎么工作的?”
从 20 篇开始解决:
“如果公司有一颗自己的 SoC,我如何把它接入 Zephyr,最终让公司内部开发者可以像使用 STM32/NXP 一样使用它?”
这两个阶段的目标完全不同。
20 篇开始:Company SoC → Zephyr BSP
我建议把后面的系列重新定义为:
01 ~19━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 理解 Zephyr Hardware / Driver / Device Model ↓ “知道 Zephyr 怎么工作” ↓20~ ? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 把 Company SoC 做成 Zephyr BSP ↓ Architecture ↓ SoC ↓ Board ↓ Devicetree ↓ Kconfig ↓ Drivers ↓ HAL ↓ Build System ↓ Zephyr Applications ↓ “公司 SoC 成为 Zephyr 平台”而且你真正的目标不是:
做一个能跑 blinky 的 demo。
而是:
建立一套可维护、可扩展、可 upstream 的 Company SoC Zephyr BSP。
20 — 从一个"假想公司 SoC"开始
我建议不要马上拿真实公司 SoC 开始改代码。
先建立一个非常重要的概念:
Company SoC │ ├── CPU │ ├── Clock / Reset │ ├── Interrupt Controller │ ├── UART │ ├── GPIO │ ├── Timer │ ├── SPI │ ├── I2C │ ├── DMA │ ├── Flash Controller │ └──...然后问:
Zephyr 到底需要从公司 SoC 手里拿到什么?
答案可以先浓缩为:
SoC │ ├── CPU architecture support │ ├── SoC description │ ├── startup │ ├── interrupt │ ├── clock │ ├── system initialization │ └── peripheral drivers │ ↓ Board │ ↓ Application所以第 20 篇最重要的不是写代码。
而是建立:
Zephyr BSP 的全局地图
**20 — 从理解 Zephyr 到打造自己的 SoC BSP:Company SoC → Zephyr 的完整路线图
**
学习主线切换:从「读 Zephyr」到「设计 Zephyr BSP」
为了更清楚地理解 20 篇之后学习主线的变化,下面用一张表格横向对比两个阶段在目标、核心问题、产出物、验证方式四个维度上的差异:
| 维度 | 读 Zephyr(01~19 篇) | 设计 Zephyr BSP(20~40 篇) |
|---|---|---|
| 目标 | 理解 Zephyr 的 Hardware / Driver / Device Model,搞清楚「Zephyr 是怎么工作的」 | 把公司自研 SoC 接入 Zephyr,建立一套可维护、可扩展、可 upstream 的 Company SoC Zephyr BSP |
| 核心问题 | Zephyr 内部机制是什么?DeviceTree、Driver、Device Model 之间如何协作? | 公司 SoC 需要提供什么?Zephyr 要求什么?两者哪里不匹配?BSP 应该放哪一层解决? |
| 产出物 | 知识体系、阅读笔记、对 Zephyr 源码的理解 | 可编译、可烧录、可验证的 SoC Port / Board / Driver / HAL / Devicetree / Kconfig / Build System 代码 |
| 验证方式 | 读懂源码、跑通官方 sample、能解释机制 | 用west build编译、west flash烧录,并让每一个 Zephyr Hardware Model 层(UART / GPIO / SPI / I2C / Timer / Interrupt / DMA)都得到验证 |
一句话总结:前 19 篇是「读 Zephyr」,目标是理解;20 篇以后是「设计 Zephyr BSP」,目标是交付一套真正能支撑公司产品开发的 SoC 平台。
这一篇作为整个后半部分的总纲。
第一张核心图
你后面所有文章都可以围绕这张图展开:
┌─────────────────────┐ │ Zephyr App │ │ │ │ main()│ │ device_is_ready()│ │ uart / gpio / spi │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Zephyr Drivers │ │ │ │ UART / GPIO / SPI │ │ I2C / Timer / PWM │ └──────────┬──────────┘ │ Driver API │ ▼ ┌─────────────────────┐ │ Company SoC HAL │ │ │ │ Register Access │ │ Clock │ │ IRQ │ │ Reset │ └──────────┬──────────┘ │ ▼ ┌────────────────────────────────┐ │ Company SoC │ │ │ │ CPU / NVIC-like IRQ │ │ UART / GPIO / SPI / I2C │ │ Timer / DMA / Clock / Reset │ └───────────────┬────────────────┘ │ │ ┌─────────▼─────────┐ │ Board │ │ │ │ SoC + Crystal │ │ Flash + LED │ │ UART + Connector │ └───────────────────┘然后再往下增加:
Zephyr BSP │ ┌───────────┼────────────┐ │ │ │ ▼ ▼ ▼ Architecture SoC Board │ │ │ │ │ ├── board.dts │ │ ├── board.yaml │ │ ├── Kconfig.board │ │ └── board.cmake │ │ │ ├── soc.c │ ├── soc.h │ ├── Kconfig │ ├── CMakeLists.txt │ └── dtsi │ └── CPU / ABI / IRQ / exception然后 20~30 篇,我建议这样走
20 — BSP 全局架构
目标:
建立:
Architecture ↓ SoC ↓ Board ↓ Devicetree ↓ Driver ↓ Application下面是一份完整的 Company SoC Zephyr BSP 目录树,覆盖了从 SoC 描述、Board 定义、外设驱动到 Devicetree Binding 的全部关键文件,并标注了每个目录/文件的职责:
zephyr/ ├── soc/ │ └── company/ │ └── my_soc/# SoC 层:描述一颗具体的 Company SoC│ ├── Kconfig.soc# SoC 选择入口,定义 CONFIG_SOC_COMPANY_MY_SOC│ ├── Kconfig.defconfig# SoC 默认配置(默认使能时钟、UART 等)│ ├── CMakeLists.txt# 声明该 SoC 需要编译的源文件│ ├── soc.c# SoC 初始化:时钟、复位、系统上电配置│ ├── soc.h# SoC 寄存器基址、IRQ 号、内存映射等宏定义│ ├── linker.ld# 该 SoC 的链接脚本(FLASH/SRAM 布局)│ └── dts/ │ └── company/ │ └── my_soc.dtsi# SoC 级 Devicetree:CPU、UART、GPIO 等外设节点│ ├── boards/ │ └── company/ │ └── my_board/# Board 层:基于该 SoC 的具体开发板│ ├── board.dts# 板级 Devicetree:使能外设、配置引脚、LED 等│ ├── board.yaml# Board 元数据:名称、SoC、兼容性声明│ ├── Kconfig.board# Board 选择入口,定义 CONFIG_BOARD_MY_BOARD│ ├── Kconfig.defconfig# Board 默认配置(默认使能板载外设)│ ├── board.cmake# 烧录/调试 runner 配置(openocd/jlink 等)│ └── CMakeLists.txt# Board 层编译声明│ ├── drivers/ │ ├── serial/ │ │ └── uart_company.c# Company UART 驱动:实现 struct uart_driver_api│ ├── gpio/ │ │ └── gpio_company.c# Company GPIO 驱动│ ├── spi/ │ │ └── spi_company.c# Company SPI 驱动│ ├── i2c/ │ │ └── i2c_company.c# Company I2C 驱动│ └── timer/ │ └── timer_company.c# Company Timer 驱动│ ├── dts/ │ └── bindings/ │ ├── serial/ │ │ └── company,my-uart.yaml# UART Binding:描述 compatible 与属性│ ├── gpio/ │ │ └── company,my-gpio.yaml# GPIO Binding│ ├── spi/ │ │ └── company,my-spi.yaml# SPI Binding│ └── i2c/ │ └── company,my-i2c.yaml# I2C Binding│ ├── include/ │ └── dt-bindings/ │ └── company/ │ ├── my_soc_irq.h# IRQ 号宏定义(供 dtsi 与驱动共用)│ ├── my_soc_clk.h# 时钟源/分频宏定义│ └── my_soc_pinctrl.h# 引脚复用宏定义│ └── modules/ └── hal/ └── company/# Vendor HAL 层(可选):寄存器访问、底层驱动├── CMakeLists.txt ├── Kconfig └── include/ └── company_hal.h# HAL 头文件:寄存器结构体、位域定义说明:
soc/与boards/是 Zephyr 构建系统的核心入口,drivers/与dts/bindings/负责把外设能力接入 Zephyr Hardware Model,include/dt-bindings/提供跨层共享的宏定义,modules/hal/则用于隔离公司已有的底层 HAL 代码,避免与 Zephyr 驱动层耦合。
回答:
一个新的 SoC 加进 Zephyr,到底需要增加哪些东西?
21 — Zephyr SoC Port 的最小骨架
开始真正写代码。
建立一个假想:
company/ └── my_soc/然后研究:
soc/ arch/ boards/ drivers/ dts/之间的关系。
最终得到:
Company SoC
↓
最小 Zephyr SoC Port
↓
能够编译
注意:
先编译,再运行。
22 — CPU / Architecture 到底由谁负责?
这是非常关键的一篇。
很多人会把:
SoC CPU Architecture Board混在一起。
实际上应该理解成:
Architecture │ │ defines ▼ CPU execution model │ ▼ Company SoC │ ▼ Board例如你的公司 SoC 如果是:
Cortex-M4那么你不是重新实现 ARM Cortex-M4 architecture。
你主要是在:
ARM Cortex-M + Company SoC上做 Zephyr SoC integration。
但如果未来是:
Company CPU Core那么事情就完全不一样了。
这篇会把:
arch/arm/ soc/之间的边界讲清楚。
23 — Startup:SoC 上电以后,Zephyr 第一行代码在哪里?
这一篇开始真正接触:
Reset ↓ Reset_Handler ↓ startup ↓ CPU initialization ↓ Zephyr kernel ↓ device initialization ↓ main()你最终要能回答:
我的 Company SoC 上电以后,Zephyr 是怎么跑起来的?
24 — Interrupt Controller:自己的 SoC 如何接入 Zephyr IRQ?
这是 BSP 的核心之一。
从:
Hardware IRQ ↓ Interrupt Controller ↓ ISR ↓ Zephyr IRQ API ↓ Driver一路追。
然后自己实现一个:
Company SoC IRQ25 — Clock / Reset:SoC BSP 真正开始「像 SoC」
开始进入 SoC-specific 内容:
Clock │ ├── CPU clock ├── Bus clock ├── Peripheral clock └── PLL以及:
Reset │ ├── UART reset ├── GPIO reset ├── SPI reset └──...然后研究:
Clock Control Driver Reset Controller DeviceTree怎么连接。
26 — Devicetree:把 Company SoC 描述给 Zephyr
这一篇非常重要。
你已经学过:
DT_NODELABEL dependency ordinal device handle struct device现在反过来:
轮到你描述自己的 SoC。
例如:
soc{uart0:uart@40000000{compatible="company,my-uart";reg=<0x400000000x1000>;interrupts=<50>;status="okay";};};然后追:
company,my-uart ↓ binding ↓ DTnode↓ driver ↓ DEVICE_DT_DEFINE()↓ struct device这就是你前面 01~19 篇知识的第一次真正落地。
27 — Binding:告诉 Zephyr “我的硬件是什么”
研究:
dts/bindings/例如:
company,my-uart.yaml然后理解:
compatible properties required reg interrupts clocks resets最终形成:
Company SoC Hardware ↓ DeviceTree Binding ↓ DeviceTree ↓ Driver28 — Company UART Driver
这是第一个真正的:
Company SoC Driver
例如:
drivers/serial/ uart_company.c实现:
struct uart_driver_api然后:
UART Hardware ↓ Company UART Driver ↓ Zephyr UART API ↓ Application最终:
const struct device\*uart=DEVICE_DT_GET(DT_NODELABEL(uart0));就能工作。
29 — GPIO / SPI / I2C / Timer
这一篇开始建立:
Company Peripheral Drivers例如:
drivers/ ├── serial/ ├── gpio/ ├── spi/ ├── i2c/ ├── timer/ └──...重点不再是"怎么写一个 driver"。
而是:
如何建立一套 Company SoC driver architecture。
30 — Board Support Package
终于进入:
Board
例如:
boards/ └── company/ └── my_board/包含:
board.yml board.cmake Kconfig.board Kconfig.defconfig my_board.dts my_board.yaml然后:
Company SoC + Company Board ↓ west build ↓ Zephyr firmware31 — Kconfig:把 Company SoC 变成可配置平台
开始处理:
CONFIG_COMPANY_SOC CONFIG_COMPANY_UART CONFIG_COMPANY_GPIO CONFIG_COMPANY_SPI最终形成:
Kconfig │ ├── SoC selection ├── Driver selection ├── Hardware features └── Build options这一步非常重要,因为公司真正做 BSP 后,不可能所有东西都硬编码。
32 — CMake / Build System
这一篇回答:
Zephyr 为什么知道应该编译我的 Company SoC?
追:
west build ↓ CMake ↓ BOARD ↓ SOC ↓ Kconfig ↓ CMakeLists.txt ↓ drivers ↓link↓ elf你会真正理解:
BOARD=my_boardSOC=company_my_soc到底是怎么影响整个 build system 的。
33 — Linker / Memory Map
这篇非常重要。
因为 SoC BSP 最终必须知道:
FLASH SRAM ROM TCM MMIO VECTOR TABLE例如:
0x00000000 ┌───────────────┐ │ Boot / Vector │ ├───────────────┤ │ Flash │ │ │ ├───────────────┤ │ SRAM │ │ │ ├───────────────┤ │ Peripheral │ └───────────────┘然后连接:
DeviceTree + Linker Script + SoC Memory Map34 — Flash / Debug / Runner
你的 BSP 最终不能只:
build还必须:
flash debug所以研究:
board.cmake runner openocd jlink pyocd vendor programmer形成:
west flash west debug35 — 从 blinky 到真正的 BSP Validation
建立 BSP validation:
hello_world ↓ blinky ↓ UART ↓ GPIO ↓ Timer ↓ Interrupt ↓ SPI ↓ I2C ↓ DMA下面是一张 BSP 验证清单,把每个外设对应的 Zephyr sample、验证命令和通过标准整理成表,方便逐项核对:
| 外设 | Zephyr Sample | 验证命令 | 通过标准 |
|---|---|---|---|
| UART | samples/hello_world | west build -b my_board samples/hello_worldwest flash | 串口输出Hello World!,无乱码、无丢字符 |
| GPIO | samples/basic/blinky | west build -b my_board samples/basic/blinkywest flash | LED 按预期频率闪烁,gpio_pin_toggle()生效 |
| SPI | samples/drivers/spi/spi_bitbang(或spi_flash) | west build -b my_board samples/drivers/spi/spi_bitbangwest flash | SPI 收发数据一致,回环测试通过,无 CRC 错误 |
| I2C | samples/drivers/i2c/i2c_scan | west build -b my_board samples/drivers/i2c/i2c_scanwest flash | 能扫描到挂载设备地址,读写寄存器返回预期值 |
| Timer | samples/drivers/timer(或samples/synchronization) | west build -b my_board samples/drivers/timerwest flash | 定时中断按设定周期触发,计数误差在可接受范围内 |
| Interrupt | samples/drivers/interrupt_controller(或samples/philosophers) | west build -b my_board samples/drivers/interrupt_controllerwest flash | 外部/定时中断能正确触发 ISR,IRQ 优先级与嵌套行为符合预期 |
| DMA | samples/drivers/dma | west build -b my_board samples/drivers/dmawest flash | DMA 搬运数据与源数据完全一致,完成回调正常触发 |
说明:
-b my_board需替换为你的实际 board 名称;若某个外设没有官方 sample,可基于samples/drivers/下最接近的示例改写,或参考tests/drivers/下的测试用例自行构造最小验证工程。
不是"demo 越多越好"。
而是:
每一个 Zephyr Hardware Model 层都得到验证。
36 — Company HAL 怎么放进 Zephyr?
这一步对公司项目尤其重要。
现实中你可能已经有:
Company SDK │ ├── HAL ├── CMSIS-like layer ├── register definitions ├── startup ├── drivers └── middleware问题来了:
这些东西全部推倒重写成 Zephyr driver 吗?
答案通常不是简单的 yes/no。
需要研究:
Zephyr Driver │ ├────────── Company HAL │ └────────── Registers什么时候:
Zephyr → HAL → Hardware什么时候:
Zephyr Driver → Registers → Hardware这是非常实际的 BSP architecture 问题。
37 — Vendor HAL / Zephyr Driver 的边界
进一步解决:
HAL Driver Subsystem Application边界问题。
例如:
Application ↓ Zephyr API ↓ Zephyr Driver ↓ Company HAL ↓ Register而不是:
Application ↓ Company HAL ↓ Register否则最终你的"Zephyr BSP"只是把 Zephyr 当成启动器。
38 — Multi-Board / Multi-Chip
当公司有:
MySoC-A MySoC-B MySoC-C以及:
Evaluation Board Reference Board Customer Board以后怎么办?
建立:
SoC family │ ├── SoC A ├── SoC B └── SoC C │ ├── EVK ├── Reference └── Customer这篇开始讨论:
如何避免 BSP 最后变成一坨复制粘贴。
39 — SoC Family Architecture
最终形成真正公司的结构:
soc/company/ │ ├── soc_a/ ├── soc_b/ ├── soc_c/ │ └── common/ ├── clock/ ├── reset/ ├── irq/ ├── pinctrl/ └──...然后讨论:
common code SoC-specific code board-specific code应该分别放在哪里。
40 — 公司 BSP 的完整生命周期
最后把整个流程串起来:
更重要的是:20 篇以后学习方法要改变
前 19 篇你是在:
读 Zephyr。
20 篇以后你应该开始:
设计 Zephyr BSP。
所以每篇文章都最好采用同一个工程化模板:
① Zephyr 要求什么? ↓ ② Company SoC 提供什么? ↓ ③ 两者哪里不匹配? ↓ ④ BSP 应该放哪一层解决? ↓ ⑤ Zephyr 源码在哪里? ↓ ⑥ 最小代码是什么? ↓ ⑦ 怎么 build? ↓ ⑧ 怎么验证?这个方法非常适合你最终的公司 SoC。
最终你要达到的能力
不是:
“我会写 Zephyr driver。”
而应该是看到一颗新的 SoC:
Company SoC │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ CPU Memory Peripherals │ │ │ ▼ ▼ ▼ Architecture Linker Drivers/HAL │ │ │ └────────────────┼────────────────┘ ▼ SoC Port │ ▼ Board │ ▼ Devicetree │ ▼ Kconfig │ ▼ Build System │ ▼ Zephyr Firmware你能够自己判断:
这个东西应该属于 Architecture、SoC、Board、Driver、HAL、Devicetree、Kconfig 还是 Build System?
这才是BSP Engineer / SoC Zephyr Porting真正需要掌握的能力。