RISC-V 芯片 Zephyr RTOS 移植说明文档
基于 nRF54L15 (ARM Cortex-M33 + RISC-V VPR 双核) 移植案例的详细分析
目录
1. Zephyr RTOS 整体架构
1.1 分层架构总览
┌─────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ main.c / Shell / 自定义业务逻辑 │
├─────────────────────────────────────────────────────┤
│ 子系统层 (Subsystem) │
│ BLE / 网络 / 文件系统 / 电源管理 / 日志 / Shell │
├─────────────────────────────────────────────────────┤
│ 驱动层 (Drivers) │
│ GPIO / UART / SPI / I2C / PWM / ADC … │
├─────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌──────────────────┐ │
│ │ 内核 │ │ 设备树 (DTS) │ │
│ │ Kernel │ │ 硬件描述与配置 │ │
│ └────┬────┘ └────────┬─────────┘ │
│ │ │ │
│ ┌──────────────┼───────────────┼──────────┐ │
│ │ ▼ ▼ │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ 架构层 (arch/) │ │ │
│ │ │ ARM / RISC-V / x86 / Xtensa │ │ │
│ │ └─────────────────────────────────┘ │ │
│ │ 架构抽象层 (HAL) │ │
│ └────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ SoC 层 (soc/) │
│ 芯片系列 / 具体型号 / 初始化代码 / 链接脚本 │
├─────────────────────────────────────────────────────┤
│ Board 层 (boards/) │
│ 开发板定义 / 引脚配置 / 外设使能 / 烧录配置 │
└─────────────────────────────────────────────────────┘
1.2 核心子系统说明
| 内核 | kernel/ | 线程调度、同步原语、内存管理、中断框架 |
| 架构层 | arch/riscv/ | CPU 上下文切换、中断处理、FPU、PMP、SMP |
| SoC 层 | soc/<vendor>/ | 芯片初始化、时钟/电源配置、Kconfig 特性声明 |
| Board 层 | boards/<vendor>/ | 开发板外设配置、DTS、defconfig、烧录脚本 |
| 驱动层 | drivers/ | 统一驱动模型,通过设备树实例化 |
| 设备树 | dts/riscv/, dts/vendor/ | 硬件描述、内存布局、外设定义 |
| 构建系统 | CMake + Kconfig + west | 条件编译、配置选项、依赖管理 |
1.3 Zephyr RISC-V 架构层结构
Zephyr 已内置完整的 RISC-V 架构支持(arch/riscv/),核心文件如下:
arch/riscv/
├── CMakeLists.txt # 构建入口,设置 elf32/elf64 输出格式
├── Kconfig # 架构级配置(PMP/FPU/SMP/中断控制器)
├── Kconfig.isa # ISA 扩展配置(RV32I/RV64I/M/A/F/D/C/Zicsr…)
├── core/
│ ├── reset.S # 复位入口,读 mhartid,初始化 FPU/中断栈,跳转 z_prep_c
│ ├── prep_c.c # C 运行环境准备(BSS清零/数据段拷贝/SOC hook → z_cstart)
│ ├── switch.S # 上下文切换(callee-saved 寄存器保存/恢复/FPU/TLS)
│ ├── isr.S # 中断/异常处理(寄存器保存/嵌套/ISR 分发/CLIC 支持)
│ ├── thread.c # 线程创建(arch_new_thread 设置初始栈帧 mstatus/mepc)
│ ├── irq_manage.c # 中断管理(动态连接/PLIC/CLIC 优先级/共享中断)
│ ├── cpu_idle.c # WFI 空闲
│ ├── fatal.c # 致命错误(mcause/mtval 分析)
│ ├── fpu.c / fpu.S # Lazy FPU 上下文保存/恢复
│ ├── pmp.c / pmp.S # PMP 物理内存保护(栈保护/用户态隔离)
│ ├── smp.c # SMP 多核启动
│ ├── ipi_clint.c # CLINT 核间中断
│ ├── reboot.c # 重启(弱定义,SoC 覆盖)
│ └── … # coredump/stacktrace/semihost/tls/userspace/elf
└── include/
├── kernel_arch_data.h # 架构私有数据结构
├── kernel_arch_func.h # arch_kernel_init/arch_switch 声明
├── offsets_short_arch.h # 汇编偏移量宏
└── pmp.h # PMP 函数声明
关键点:RISC-V 架构层已经处理了大部分 CPU 级别的通用逻辑。SoC 移植的核心工作是填入芯片特定的配置和初始化代码,而非重写架构层。
2. Zephyr 移植的层次模型
2.1 三层移植模型
Zephyr 的 SoC 移植采用严格的三层模型,以 nRF54L15 为例:
┌───────────────────────────────────────────────────────┐
│ SoC Family (芯片厂商/系列) │
│ soc/nordic/ │
│ ├── Kconfig.soc → SOC_FAMILY_NORDIC_NRF │
│ ├── Kconfig → 全局 Nordic 选项 │
│ ├── Kconfig.defconfig → 全局默认值 │
│ ├── CMakeLists.txt → add_subdirectory(${SOC_SERIES}) │
│ └── soc.yml → SoC 拓扑(cpuclusters 定义) │
├───────────────────────────────────────────────────────┤
│ SoC Series (芯片子系列) │
│ soc/nordic/nrf54l/ │
│ ├── Kconfig.soc → SOC_NRF54L15_CPUAPP/FLPR │
│ ├── Kconfig → CPU 特性 select 链 │
│ ├── Kconfig.defconfig → NUM_IRQS 等默认值 │
│ ├── CMakeLists.txt → 编译 soc.c │
│ ├── soc.c / soc.h → SoC 初始化代码 │
│ └── kmu_push_area_section.ld → 特殊内存段 │
├───────────────────────────────────────────────────────┤
│ Board (开发板) │
│ boards/nordic/nrf54l15dk/ │
│ ├── board.yml → 声明板卡变体和 SoC 映射 │
│ ├── Kconfig.nrf54l15dk → Board→SoC 符号映射 │
│ ├── Kconfig.defconfig → 板级默认配置 │
│ ├── *.dts / *.dtsi → 硬件描述(CPU/内存/外设/引脚) │
│ ├── *_defconfig → 最小默认配置 │
│ ├── board.cmake → 烧录器配置 │
│ └── *.yaml → 仿真/测试支持 │
└───────────────────────────────────────────────────────┘
2.2 三条关联路径
三层之间通过三条路径相互关联:
路径一:Kconfig select 链(自底向上级联)
Board: BOARD_NRF54L15DK_NRF54L15_CPUAPP
→ select SOC_NRF54L15_CPUAPP (nrf54l/Kconfig.soc)
Series: SOC_NRF54L15_CPUAPP
→ select SOC_NRF54L15 (nrf54l/Kconfig.soc)
→ select SOC_NRF54L_CPUAPP_COMMON (nrf54l/Kconfig)
Series: SOC_NRF54L_CPUAPP_COMMON
→ select ARM
→ select ARMV8_M_DSP
→ select CPU_CORTEX_M33
→ select CPU_HAS_ARM_MPU
→ select CPU_HAS_FPU
… (nrf54l/Kconfig)
Series: SOC_NRF54L15
→ select SOC_SERIES_NRF54LX (nrf54l/Kconfig.soc)
Family: SOC_SERIES_NRF54LX
→ select SOC_FAMILY_NORDIC_NRF (nordic/Kconfig.soc)
→ select HAS_NRFX
→ select HAS_NORDIC_DRIVERS (nrf54l/Kconfig)
RISC-V 对应:将 ARM/CPU_CORTEX_M33 替换为 RISCV + 具体 ISA 扩展(如 RISCV_ISA_RV32I, RISCV_ISA_EXT_M 等)。
路径二:CMake 构建链
soc/nordic/CMakeLists.txt
→ add_subdirectory(${SOC_SERIES}) # 当 SOC_SERIES=nrf54l 时进入 nrf54l/
→ add_subdirectory(common) # 编译公共代码
soc/nordic/nrf54l/CMakeLists.txt
→ zephyr_library_sources(soc.c) # 编译 SoC 初始化
→ zephyr_include_directories(.) # 添加 soc.h 路径
→ 条件链接 kmu_push_area_section.ld # 特殊内存段
路径三:DTS include 链
Board DTS: nrf54l15dk_nrf54l15_cpuapp.dts
→ <nordic/nrf54l15_cpuapp.dtsi> (dts/arm/nordic/)
→ <nordic/nrf54l15.dtsi> (dts/vendor/nordic/) — 内存大小
→ "nrf54l_05_10_15.dtsi" (dts/vendor/nordic/) — 完整外设描述
→ "nrf54l_05_10_15_cpuapp.dtsi" (dts/arm/nordic/) — ARM 核心视角
→ "nrf54l_05_10_15_cpuapp_common.dtsi" — 板级公共
→ <nordic/nrf54l15_partition.dtsi> — RRAM 分区布局
RISC-V 对应:核心视角 DTSI 放在 dts/riscv/<vendor>/ 而非 dts/arm/<vendor>/。
3. nRF54L15 移植案例深度分析
3.1 双核架构的特殊性
nRF54L15 是一颗异构双核芯片,这对理解 Zephyr 移植非常具有参考价值:
| cpuapp | ARM Cortex-M33F | 128MHz | dts/arm/nordic/ | SOC_NRF54L15_CPUAPP → select ARM |
| cpuflpr | RISC-V VPR (rv32emc) | 128MHz | dts/riscv/nordic/ | SOC_NRF54L15_CPUFLPR → select RISCV_CORE_NORDIC_VPR |
关键启示:同一颗芯片的不同核心可以运行不同架构的 Zephyr,它们共享 SoC 基础 DTS(nrf54l15.dtsi),但各自有架构特定的 DTSI 文件。
3.2 ARM 核心视角 vs RISC-V 核心视角的 DTS 对比
ARM cpuapp 视角(dts/arm/nordic/nrf54l_05_10_15_cpuapp.dtsi):
- 删除 cpuflpr 节点
- 添加 NVIC 中断控制器
- 添加 SysTick 定时器
- 中断父节点指向 NVIC
RISC-V cpuflpr 视角(dts/riscv/nordic/nrf54l_05_10_15_cpuflpr.dtsi):
- 删除 cpuapp 节点及相关内存区域
- 设置 CLIC 中断控制器为中断父节点
- 配置 VPR mailbox 通信
- 调整 GRTC/GPIOTE 中断号
3.3 SoC 初始化代码分析
nRF54L15 的 soc.c 实现了以下初始化流程:
// 1. SYS_INIT 在 PRE_KERNEL_1 阶段执行,优先级 0
SYS_INIT(nordicsemi_nrf54l_init, PRE_KERNEL_1, 0);
int nordicsemi_nrf54l_init(void)
{
// 2. 从设备树获取 CPU 频率,更新 SystemCoreClock
SystemCoreClock = NRF_PERIPH_GET_FREQUENCY(DT_NODELABEL(cpu));
// 3. 启用指令缓存
sys_cache_instr_enable();
// 4. 安全域内执行电源和时钟配置
power_and_clock_configuration();
// – LFXO/HFXO 内部负载电容校准(从 FICR 读取 trim 值)
// – DCDC 稳压器配置
// – 常延迟模式配置
return 0;
}
// 5. 架构相关的忙等待实现
void arch_busy_wait(uint32_t time_us)
{
nrfx_coredep_delay_us(time_us);
}
RISC-V 对应:
- SystemCoreClock 更新逻辑相同,但获取频率的设备树节点不同
- 缓存操作可能不同(RISC-V 使用 fence.i 指令或 SoC 特定缓存操作)
- 电源/时钟配置完全取决于芯片硬件
- arch_busy_wait() 可以使用 RISC-V 的 rdtime 或 SoC 定时器实现
3.4 Kconfig 特性声明对比
ARM cpuapp 的特性声明(soc/nordic/nrf54l/Kconfig):
config SOC_NRF54L_CPUAPP_COMMON
bool
select ARM # 选择 ARM 架构
select ARMV8_M_DSP # ARMv8-M DSP 指令
select CPU_CORTEX_M33 # Cortex-M33 核心
select CPU_HAS_ARM_MPU # ARM MPU
select CPU_HAS_FPU # 浮点单元
select CPU_HAS_ICACHE # 指令缓存
…
RISC-V cpuflpr 的特性声明:
config SOC_NRF54L15_CPUFLPR
select RISCV_CORE_NORDIC_VPR # Nordic 自定义 RISC-V 核心
通用 RISC-V SoC 的特性声明(以 WCH CH32V 为例):
config SOC_FAMILY_CH32V
select RISCV # 选择 RISC-V 架构
select BUILD_OUTPUT_HEX
select ATOMIC_OPERATIONS_C # 使用 C 实现的原子操作
imply XIP # 默认 XIP 执行
config SOC_SERIES_QINGKE_V4F
select RISCV_ISA_RV32I # RV32I 基础整数指令集
select RISCV_ISA_EXT_M # 乘除法扩展
select RISCV_ISA_EXT_A # 原子扩展
select RISCV_ISA_EXT_C # 压缩指令扩展
select RISCV_ISA_EXT_F # 单精度浮点
select RISCV_ISA_EXT_ZICSR # CSR 指令
select RISCV_ISA_EXT_ZIFENCEI # 指令缓存同步
3.5 链接脚本
ARM SoC 通常使用 Zephyr 内置的 Cortex-M 链接脚本:
# soc/nordic/CMakeLists.txt
set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld)
RISC-V SoC 使用通用的 RISC-V 链接脚本:
# soc/wch/ch32v/CMakeLists.txt
set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/riscv/common/linker.ld)
如果芯片有特殊的内存布局需求,可以在 SoC 目录下提供自定义链接脚本(如 default.ld)。
4. RISC-V 芯片移植实施步骤
4.0 移植前准备
在开始移植前,需要准备以下信息:
| CPU 核心 | 具体的 RISC-V ISA(如 rv32imac) | 芯片手册 |
| 中断控制器 | PLIC / CLIC / 自定义 | 芯片手册 |
| 定时器 | CLINT mtime / SysTick / 自定义 | 芯片手册 |
| 内存映射 | SRAM/Flash 基地址和大小 | 芯片手册 |
| 外设列表 | UART/SPI/I2C/GPIO 等地址和中断号 | 芯片手册 |
| 调试接口 | JTAG / SWD / 自定义 | 芯片手册 |
| 工具链 | GCC RISC-V 交叉编译器 | 工具链供应商 |
4.1 步骤一:创建 SoC Family 层
目录结构:
soc/<vendor>/
├── Kconfig.soc # 定义 SOC_FAMILY_<VENDOR>
├── Kconfig # Family 级选项(select RISCV 等)
├── Kconfig.defconfig # Family 级默认值
├── CMakeLists.txt # add_subdirectory(${SOC_SERIES})
└── soc.yml # SoC 拓扑描述
4.1.1 Kconfig.soc — 定义 SoC Family
# soc/<vendor>/Kconfig.soc
# 版权声明
config SOC_FAMILY_<VENDOR>
bool
config SOC_FAMILY
default "<vendor>" if SOC_FAMILY_<VENDOR>
rsource "*/Kconfig.soc"
对照 nRF54L15:
# soc/nordic/Kconfig.soc
config SOC_FAMILY_NORDIC_NRF
bool
config SOC_FAMILY
default "nordic_nrf" if SOC_FAMILY_NORDIC_NRF
4.1.2 Kconfig — Family 级特性声明
# soc/<vendor>/Kconfig
# 版权声明
config SOC_FAMILY_<VENDOR>
select RISCV # ★ 关键:选择 RISC-V 架构
select BUILD_OUTPUT_HEX # 输出 hex 格式
select ATOMIC_OPERATIONS_C # C 实现原子操作(若无 A 扩展)
imply XIP # 默认 XIP 执行
if SOC_FAMILY_<VENDOR>
# Family 级公共选项可以在此定义
# 例如自定义中断控制器、特殊定时器等
rsource "*/Kconfig"
endif # SOC_FAMILY_<VENDOR>
对照 nRF54L15:
# soc/nordic/Kconfig
config SOC_FAMILY_NORDIC_NRF
select SOC_COMPATIBLE_NRF
select SOC_RESET_HOOK
4.1.3 CMakeLists.txt — 构建入口
# soc/<vendor>/CMakeLists.txt
# RISC-V 使用通用链接脚本
set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/riscv/common/linker.ld CACHE INTERNAL "")
# 进入具体 SoC Series 子目录
add_subdirectory(${SOC_SERIES})
# 如果有公共代码
# add_subdirectory(common)
对照 nRF54L15:
# soc/nordic/CMakeLists.txt
# ARM 使用 Cortex-M 链接脚本
if(ARM)
set(SOC_LINKER_SCRIPT ${ZEPHYR_BASE}/include/zephyr/arch/arm/cortex_m/scripts/linker.ld)
endif()
add_subdirectory(${SOC_SERIES})
add_subdirectory(common)
4.1.4 soc.yml — SoC 拓扑
# soc/<vendor>/soc.yml
family:
– name: <vendor>_<series>
series:
– name: <series_name>
socs:
– name: <soc_name>
# 如果多核,添加 cpuclusters
# cpuclusters:
# – name: cpu0
# – name: cpu1
对照 nRF54L15:
# soc/nordic/soc.yml
family:
– name: nordic_nrf
series:
– name: nrf54l
socs:
– name: nrf54l15
cpuclusters:
– name: cpuapp # ARM Cortex-M33
– name: cpuflpr # RISC-V VPR
4.2 步骤二:创建 SoC Series 层
目录结构:
soc/<vendor>/<series>/
├── Kconfig.soc # 定义 SOC_<SOC_NAME> / SOC_<SOC_NAME>_<CORE>
├── Kconfig # CPU 特性 select 链 + 系列级选项
├── Kconfig.defconfig # NUM_IRQS 等默认值
├── CMakeLists.txt # 编译 soc.c
├── soc.c # SoC 初始化代码
├── soc.h # SoC 特定常量和宏
└── default.ld # 可选:自定义链接脚本
4.2.1 Kconfig.soc — 定义 SoC 和核心变体
# soc/<vendor>/<series>/Kconfig.soc
# 版权声明
config SOC_<SERIES_UPPER>
bool
select SOC_FAMILY_<VENDOR>
config SOC_<SOC_NAME>
bool
select SOC_<SERIES_UPPER>
config SOC_<SOC_NAME>_<CORE>
bool
select SOC_<SOC_NAME>
config SOC
default "<soc_name>" if SOC_<SOC_NAME>
对照 nRF54L15:
# soc/nordic/nrf54l/Kconfig.soc
config SOC_NRF54L15
bool
select SOC_SERIES_NRF54LX
config SOC_NRF54L15_CPUAPP
bool
select SOC_NRF54L15
config SOC_NRF54L15_CPUFLPR
bool
select SOC_NRF54L15
config SOC
default "nrf54l15" if SOC_NRF54L15
4.2.2 Kconfig — CPU 特性 select 链
这是最关键的文件,声明了芯片的所有硬件特性:
# soc/<vendor>/<series>/Kconfig
# 版权声明
config SOC_<SERIES_UPPER>
select SOC_COMPATIBLE_<SERIES>
# 如果有厂商 HAL 库
# select HAS_<VENDOR>_DRIVERS
config SOC_<SOC_NAME>_COMMON
bool
# ★ RISC-V 基础架构选择
select RISCV
# ★ ISA 扩展声明(根据芯片实际支持的扩展选择)
select RISCV_ISA_RV32I # RV32I 基础整数指令集
select RISCV_ISA_EXT_M # 乘除法扩展
select RISCV_ISA_EXT_A # 原子扩展(可选)
select RISCV_ISA_EXT_C # 压缩指令扩展
select RISCV_ISA_EXT_ZICSR # CSR 指令(几乎必选)
# ★ 中断控制器
select RISCV_HAS_PLIC # 或 RISCV_HAS_CLIC
# ★ 其他特性
select BUILD_OUTPUT_HEX
# 如果有 FPU
# select RISCV_ISA_EXT_F # 单精度浮点
# select RISCV_ISA_EXT_D # 双精度浮点(依赖 F)
# select CPU_HAS_FPU
# 如果有 PMP
# select RISCV_PMP
# 如果需要 SMP
# select SMP
config SOC_<SOC_NAME>_<CORE>
select SOC_<SOC_NAME>_COMMON
if SOC_<SERIES_UPPER>
# 系列级自定义选项
config <VENDOR>_CUSTOM_OPTION
bool "Custom option description"
default n
help
Description of this custom option.
endif # SOC_<SERIES_UPPER>
对照 nRF54L15 ARM 核心的特性声明:
config SOC_NRF54L_CPUAPP_COMMON
select ARM
select ARMV8_M_DSP
select CPU_CORTEX_M33
select CPU_CORTEX_M_HAS_DWT
select CPU_HAS_ARM_MPU
select CPU_HAS_ICACHE
select CPU_HAS_ARM_SAU
select CPU_HAS_FPU
select HAS_HW_NRF_RADIO_IEEE802154
select HAS_POWEROFF
对照 WCH CH32V303 RISC-V 的特性声明:
config SOC_SERIES_QINGKE_V4F
select RISCV_ISA_RV32I
select RISCV_ISA_EXT_M
select RISCV_ISA_EXT_A
select RISCV_ISA_EXT_C
select RISCV_ISA_EXT_F
select RISCV_ISA_EXT_ZICSR
select RISCV_ISA_EXT_ZIFENCEI
4.2.3 Kconfig.defconfig — 默认配置值
# soc/<vendor>/<series>/Kconfig.defconfig
# 版权声明
if SOC_<SOC_NAME>
# 中断数量(根据芯片手册填写)
config NUM_IRQS
default <number>
# 系统时钟频率(从设备树获取)
config SYS_CLOCK_HW_CYCLES_PER_SEC
default $(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency)
# 其他默认值
config CLOCK_CONTROL
default y
endif # SOC_<SOC_NAME>
对照 nRF54L15:
if SOC_NRF54L05_CPUAPP || SOC_NRF54L10_CPUAPP || SOC_NRF54L15_CPUAPP
config NUM_IRQS
default 271
endif
if SOC_NRF54L05_CPUFLPR || SOC_NRF54L10_CPUFLPR || SOC_NRF54L15_CPUFLPR
config NUM_IRQS
default 287
endif
4.2.4 CMakeLists.txt — 编译 SoC 代码
# soc/<vendor>/<series>/CMakeLists.txt
zephyr_library_sources(
soc.c
)
zephyr_include_directories(.)
# 如果需要自定义中断处理(见 4.2.6)
# zephyr_library_sources(
# soc_irq.S
# vector.S
# )
# 如果有自定义链接脚本
# set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/default.ld CACHE INTERNAL "")
4.2.5 soc.c — SoC 初始化代码
/*
* 版权声明
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief System/hardware module for <Vendor> <SoC> processor
*/
#include <zephyr/autoconf.h>
#include <zephyr/devicetree.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
/**
* @brief SoC 硬件初始化
*
* 在 PRE_KERNEL_1 阶段执行,优先级 0(最早执行)
* 主要完成:
* 1. 时钟和电源配置
* 2. 缓存使能
* 3. 其他硬件 errata workaround
*/
static int <vendor>_<soc>_init(void)
{
/* TODO: 根据芯片手册实现以下初始化 */
/* 1. 时钟配置
* – 配置主时钟源(内部/外部晶振)
* – 配置 PLL(如果有)
* – 设置系统时钟频率
*/
/* 2. 电源配置
* – 配置电压调节器
* – 配置低功耗模式相关寄存器
*/
/* 3. 缓存配置(如果有)
* – 使能指令缓存
* – 使能数据缓存(如果支持)
*/
/* 4. 硬件 errata workaround
* – 根据芯片勘误手册实现必要的 workaround
*/
return 0;
}
/**
* 架构相关的忙等待实现
* 如果芯片有专用定时器可用于精确延时,可以覆盖此弱定义
*/
void arch_busy_wait(uint32_t time_us)
{
/* 方案1:使用 RISC-V rdtime 指令(如果可用) */
/* 方案2:使用 SoC 专用定时器 */
/* 方案3:简单循环延时(不推荐,功耗高) */
}
SYS_INIT(<vendor>_<soc>_init, PRE_KERNEL_1, 0);
对照 nRF54L15:
int nordicsemi_nrf54l_init(void)
{
SystemCoreClock = NRF_PERIPH_GET_FREQUENCY(DT_NODELABEL(cpu));
sys_cache_instr_enable();
power_and_clock_configuration(); // LFXO/HFXO 电容校准、DCDC 配置
return 0;
}
void arch_busy_wait(uint32_t time_us)
{
nrfx_coredep_delay_us(time_us); // 使用 Nordic nrfx 库
}
SYS_INIT(nordicsemi_nrf54l_init, PRE_KERNEL_1, 0);
4.2.6 soc_irq.S — 自定义中断处理(可选)
如果芯片使用非标准的中断控制器或需要自定义中断入口,需要提供此文件:
/*
* 版权声明
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief 自定义中断入口
*
* 当 RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING=y 时,
* Zephyr 的 isr.S 会调用 __soc_handle_irq
*/
#include <zephyr/toolchain.h>
GTEXT(__soc_handle_irq)
SECTION_FUNC(TEXT, __soc_handle_irq)
/* 从中断控制器获取中断号 */
/* 具体实现取决于中断控制器类型 */
ret
对照 WCH CH32V:
# soc/wch/ch32v/qingke_v4f/soc_irq.S
# WCH 使用自定义 PFIC 中断控制器,需要特殊处理
4.3 步骤三:创建设备树文件
设备树文件是 Zephyr 硬件描述的核心,分为三层:
dts/vendor/<vendor>/ # SoC 基础描述(共享外设、内存布局)
<soc>.dtsi
dts/riscv/<vendor>/ # RISC-V 核心视角(CPU、中断控制器)
<soc>.dtsi
boards/<vendor>/<board>/ # 板级描述(引脚、LED、按钮等)
<board>.dts
4.3.1 SoC 基础 DTSI(dts/vendor/<vendor>/<soc>.dtsi)
这是最核心的文件,描述了芯片的完整硬件:
/* 版权声明 */
#include <mem.h>
#include <zephyr/dt-bindings/interrupt-controller/riscv.h>
/ {
#address-cells = <1>;
#size-cells = <1>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
device_type = "cpu";
compatible = "<vendor>,<core-name>";
reg = <0>;
riscv,isa = "rv32imac_zicsr"; /* ★ 根据实际 ISA 填写 */
/* 如果有自定义中断控制器 */
/* interrupt-controller; */
/* #interrupt-cells = <1>; */
clocks = <&clk>; /* 关联时钟节点 */
};
};
clocks {
clk: clock-controller@<addr> {
compatible = "<vendor>,<soc>-clock";
reg = <0x<addr> 0x<size>>;
#clock-cells = <1>;
clock-frequency = <DT_FREQ_M(XXX)>; /* 主时钟频率 */
};
};
sram0: memory@<addr> {
compatible = "mmio-sram";
reg = <0x<addr> DT_SIZE_K(XXX)>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
/* ★ 中断控制器 */
plic: interrupt-controller@<addr> {
compatible = "sifive,plic-1.0.0";
reg = <0x<addr> 0x<size>>;
#address-cells = <0>;
#interrupt-cells = <2>;
interrupt-controller;
interrupts-extended = <&cpu0 11>; /* 外部中断 */
riscv,max-priority = <7>;
};
/* ★ 系统定时器(CLINT mtime) */
clint: clint@<addr> {
compatible = "sifive,clint0";
reg = <0x<addr> 0x<size>;
interrupts-extended = <&cpu0 7>; /* 定时器中断 */
};
/* UART0 */
uart0: uart@<addr> {
compatible = "<vendor>,<soc>-uart";
reg = <0x<addr> 0x<size>>;
interrupts = <IRQ_NUM IRQ_PRIORITY>;
interrupt-parent = <&plic>;
clocks = <&clk CLK_UART0>;
status = "disabled";
};
/* GPIO */
gpio0: gpio@<addr> {
compatible = "<vendor>,<soc>-gpio";
reg = <0x<addr> 0x<size>>;
interrupts = <IRQ_NUM IRQ_PRIORITY>;
interrupt-parent = <&plic>;
gpio-controller;
#gpio-cells = <2>;
status = "disabled";
};
/* SPI */
spi0: spi@<addr> {
compatible = "<vendor>,<soc>-spi";
reg = <0x<addr> 0x<size>>;
interrupts = <IRQ_NUM IRQ_PRIORITY>;
interrupt-parent = <&plic>;
clocks = <&clk CLK_SPI0>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
/* I2C */
i2c0: i2c@<addr> {
compatible = "<vendor>,<soc>-i2c";
reg = <0x<addr> 0x<size>>;
interrupts = <IRQ_NUM IRQ_PRIORITY>;
interrupt-parent = <&plic>;
clocks = <&clk CLK_I2C0>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
/* Flash 控制器 */
flash: flash-controller@<addr> {
compatible = "<vendor>,<soc>-flash-controller";
reg = <0x<addr> 0x<size>>;
#address-cells = <1>;
#size-cells = <1>;
flash0: flash@0 {
compatible = "soc-nv-flash";
reg = <0x0 DT_SIZE_K(XXX)>;
erase-block-size = <4096>;
write-block-size = <4>;
};
};
};
};
对照 nRF54L15(dts/vendor/nordic/nrf54l_05_10_15.dtsi):
cpus {
cpuapp: cpu@0 {
compatible = "arm,cortex-m33f";
reg = <0>;
device_type = "cpu";
clocks = <&hfpll>;
};
cpuflpr: cpu@1 {
compatible = "nordic,vpr";
reg = <1>;
device_type = "cpu";
riscv,isa = "rv32emc"; /* ★ RISC-V 核心 */
nordic,bus-width = <32>;
};
};
对照 WCH CH32V303(dts/riscv/wch/qingke-v4f.dtsi):
cpus {
cpu0: cpu@0 {
device_type = "cpu";
compatible = "wch,qingke-v4f";
reg = <0>;
riscv,isa = "rv32imacf_zicsr_zifencei";
};
};
soc {
pfic: interrupt-controller@e000e000 {
compatible = "wch,pfic"; /* ★ 自定义中断控制器 */
#interrupt-cells = <1>;
interrupt-controller;
reg = <0xe000e000 0x10>;
};
systick: systimer@e000f000 {
compatible = "wch,systick";
reg = <0xe000f000 0x10>;
interrupt-parent = <&pfic>;
interrupts = <12>;
};
};
4.3.2 RISC-V 核心视角 DTSI(dts/riscv/<vendor>/<soc>.dtsi)
此文件从 RISC-V 核心的角度描述硬件,通常 include 基础 DTSI 并添加核心特定配置:
/* 版权声明 */
#include <vendor/<soc>.dtsi> /* 包含 SoC 基础描述 */
/* RISC-V 核心特定配置 */
/ {
soc {
interrupt-parent = <&plic>; /* 设置默认中断父节点 */
};
};
对照 nRF54L15(dts/riscv/nordic/nrf54l_05_10_15_cpuflpr.dtsi):
cpu: &cpuflpr {};
clic: &cpuflpr_clic {};
/delete-node/ &cpuapp; /* 删除 ARM 核心 */
/delete-node/ &cpuapp_rram;
/delete-node/ &cpuapp_sram;
/ {
soc {
compatible = "simple-bus";
interrupt-parent = <&cpuflpr_clic>; /* RISC-V 使用 CLIC */
ranges;
};
};
4.4 步骤四:创建 Board 层
目录结构:
boards/<vendor>/<board>/
├── board.yml # 板卡声明和 SoC 映射
├── board.cmake # 烧录器配置
├── Kconfig.<board> # Board→SoC 符号映射
├── Kconfig # 板级选项
├── Kconfig.defconfig # 板级默认配置
├── <board>.dts # 板级设备树
├── <board>-pinctrl.dtsi # 引脚配置
├── <board>_defconfig # 最小默认配置
└── <board>.yaml # 仿真/测试支持
4.4.1 board.yml — 板卡声明
# boards/<vendor>/<board>/board.yml
board:
name: <board>
full_name: <Board Full Name>
vendor: <vendor>
socs:
– name: <soc_name>
# 如果有变体(如 ns/xip)
# variants:
# – name: ns
# cpucluster: cpu0
对照 nRF54L15:
board:
name: nrf54l15dk
full_name: nRF54L15 DK
vendor: nordic
socs:
– name: nrf54l15
variants:
– name: xip
cpucluster: cpuflpr
– name: ns
cpucluster: cpuapp
4.4.2 Kconfig.<board> — Board→SoC 映射
# boards/<vendor>/<board>/Kconfig.<board>
# 版权声明
config BOARD_<BOARD_UPPER>
select SOC_<SOC_NAME>_<CORE>
对照 nRF54L15:
config BOARD_NRF54L15DK_NRF54L15_CPUAPP
select SOC_NRF54L15_CPUAPP
config BOARD_NRF54L15DK_NRF54L15_CPUFLPR
select SOC_NRF54L15_CPUFLPR
4.4.3 Board DTS — 板级硬件描述
/* 版权声明 */
/dts-v1/;
#include <vendor/<soc>.dtsi> /* SoC 基础描述 */
/ {
model = "<Vendor> <Board> <SoC>";
compatible = "<vendor>,<board>-<soc>";
chosen {
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,code-partition = &slot0_partition;
};
/* LED */
leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
label = "LED 0";
};
};
/* 按钮 */
buttons {
compatible = "gpio-keys";
button0: button_0 {
gpios = <&gpio0 1 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Button 0";
};
};
aliases {
led0 = &led0;
sw0 = &button0;
};
};
/* 使能板载外设 */
&uart0 {
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&uart0_default>;
pinctrl-names = "default";
};
&gpio0 {
status = "okay";
};
对照 nRF54L15(nrf54l15dk_nrf54l15_cpuflpr.dts):
/dts-v1/;
#include <nordic/nrf54l15_cpuflpr.dtsi>
#include "nrf54l15dk_common.dtsi"
/ {
model = "Nordic nRF54L15 DK nRF54L15 FLPR MCU";
compatible = "nordic,nrf54l15dk_nrf54l15-cpuflpr";
chosen {
zephyr,console = &uart30;
zephyr,shell-uart = &uart30;
zephyr,code-partition = &cpuflpr_code_partition;
zephyr,flash = &cpuflpr_rram;
zephyr,sram = &cpuflpr_sram;
};
};
4.4.4 defconfig — 最小默认配置
# boards/<vendor>/<board>/<board>_defconfig
# 版权声明
# 使能 UART 驱动
CONFIG_SERIAL=y
# 使能控制台
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
# 使能 GPIO
CONFIG_GPIO=y
# 如果使用 PLIC
# CONFIG_RISCV_HAS_PLIC=y
# 如果使用 CLINT 定时器
# CONFIG_RISCV_MACHINE_TIMER=y
对照 nRF54L15:
# nrf54l15dk_nrf54l15_cpuflpr_defconfig
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_GPIO=y
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_XIP=n
4.5 步骤五:驱动适配
SoC 和 Board 层搭建完成后,需要确保关键驱动可用。驱动适配有两种方式:
方式一:使用 Zephyr 内置驱动
如果芯片使用标准 IP(如 SiFive PLIC、CLINT、DesignWare UART 等),可以直接使用 Zephyr 内置驱动:
/* 使用标准 PLIC 中断控制器 */
plic: interrupt-controller@<addr> {
compatible = "sifive,plic-1.0.0"; /* Zephyr 已有驱动 */
…
};
/* 使用标准 CLINT 定时器 */
clint: clint@<addr> {
compatible = "sifive,clint0"; /* Zephyr 已有驱动 */
…
};
方式二:编写自定义驱动
如果芯片使用自定义 IP,需要编写驱动:
drivers/
├── interrupt_controller/
│ └── <vendor>_plic.c # 自定义中断控制器驱动
├── serial/
│ └── uart_<vendor>.c # 自定义 UART 驱动
├── gpio/
│ └── gpio_<vendor>.c # 自定义 GPIO 驱动
└── …
对照 ez_middleware 的适配器模式:
ez_api/ez_bsp_api/ezbsp_uart.h → 接口声明
ez_services/ez_adapt/ez_bsp_adapt/ → Zephyr UART API 封装
ez_services/ez_common/ez_bsp_common/ → 参数校验/日志
4.6 步骤六:构建与验证
4.6.1 构建命令
# 基础构建
west build -b <board>/<soc>/<core> samples/hello_world
# 详细输出
west build -b <board>/<soc>/<core> samples/hello_world -v
# 清理重建
west build -b <board>/<soc>/<core> samples/hello_world -p always
4.6.2 验证顺序
建议按以下顺序逐步验证:
5. 关键文件模板与示例
5.1 最小 RISC-V SoC 移植文件清单
以下是一个最小 RISC-V SoC 移植所需的全部文件:
# SoC Family 层(3 个文件)
soc/<vendor>/
├── Kconfig.soc
├── Kconfig
└── CMakeLists.txt
# SoC Series 层(5 个文件)
soc/<vendor>/<series>/
├── Kconfig.soc
├── Kconfig
├── Kconfig.defconfig
├── CMakeLists.txt
└── soc.c
# 设备树(2 个文件)
dts/vendor/<vendor>/
└── <soc>.dtsi
dts/riscv/<vendor>/
└── <soc>.dtsi
# Board 层(5 个文件)
boards/<vendor>/<board>/
├── board.yml
├── Kconfig.<board>
├── <board>.dts
├── <board>_defconfig
└── board.cmake
总计约 15 个文件即可完成最小移植。
5.2 RISC-V ISA 扩展 Kconfig 速查
| RISCV_ISA_RV32I | RV32I | 32位基础整数指令集 |
| RISCV_ISA_RV32E | RV32E | 嵌入式(16寄存器) |
| RISCV_ISA_RV64I | RV64I | 64位基础整数指令集 |
| RISCV_ISA_EXT_M | M | 乘除法 |
| RISCV_ISA_EXT_A | A | 原子操作 |
| RISCV_ISA_EXT_F | F | 单精度浮点 |
| RISCV_ISA_EXT_D | D | 双精度浮点(依赖 F) |
| RISCV_ISA_EXT_C | C | 压缩指令 |
| RISCV_ISA_EXT_Q | Q | 四精度浮点 |
| RISCV_ISA_EXT_ZICSR | Zicsr | CSR 指令 |
| RISCV_ISA_EXT_ZIFENCEI | Zifencei | 指令缓存同步 |
| RISCV_ISA_EXT_ZAAMO | Zaamo | 原子内存操作子集 |
| RISCV_ISA_EXT_ZLRSC | Zlrsc | LR/SC 子集 |
| RISCV_ISA_EXT_ZBA | Zba | 地址生成 |
| RISCV_ISA_EXT_ZBB | Zbb | 基本位操作 |
| RISCV_ISA_EXT_ZBC | Zbc | 乘法位操作 |
| RISCV_ISA_EXT_ZBS | Zbs | 单位操作 |
| RISCV_ISA_EXT_ZMMUL | Zmmul | 乘法子集 |
5.3 中断控制器选择
| PLIC | RISCV_HAS_PLIC | sifive,plic-1.0.0 | 标准平台级中断控制器 |
| CLIC | RISCV_HAS_CLIC | riscv,clic | 核本地中断控制器 |
| 自定义 | RISCV_SOC_HAS_CUSTOM_IRQ_HANDLING | 自定义 | 非标准中断控制器 |
5.4 常见 RISC-V 中断控制器 DTS 模板
PLIC(最常用):
plic: interrupt-controller@<addr> {
compatible = "sifive,plic-1.0.0";
reg = <0x<addr> 0x<size>>;
#address-cells = <0>;
#interrupt-cells = <2>; /* 第二个 cell 是优先级 */
interrupt-controller;
interrupts-extended = <&cpu0 11>; /* 外部中断 */
riscv,max-priority = <7>;
};
CLIC(低延迟场景):
clic: interrupt-controller@<addr> {
compatible = "riscv,clic";
reg = <0x<addr> 0x<size>>;
#address-cells = <0>;
#interrupt-cells = <2>;
interrupt-controller;
interrupts-extended = <&cpu0 11>;
};
6. 常见问题与调试
6.1 编译阶段
| SOC_SERIES not defined | soc.yml 或 Kconfig.soc 配置错误 | 检查 soc.yml 中的 series name 与 Kconfig.soc 中的 SOC_SERIES 默认值是否一致 |
| RISCV not selected | Kconfig select 链断裂 | 从 Board Kconfig 开始逐级检查 select 链是否到达 select RISCV |
| ISA extension not selected | Kconfig 缺少 ISA 扩展声明 | 在 SoC Series Kconfig 中添加对应的 RISCV_ISA_EXT_* select |
| linker script not found | CMakeLists.txt 未设置链接脚本 | 在 SoC Family CMakeLists.txt 中设置 SOC_LINKER_SCRIPT |
| DTS include not found | DTS 路径不正确 | 检查 include 路径是否匹配 dts/riscv/<vendor>/ 或 dts/vendor/<vendor>/ |
6.2 运行阶段
| 无串口输出 | UART 驱动未使能或时钟未配置 | 检查 defconfig 中 CONFIG_SERIAL=y、DTS 中 UART 节点 status = "okay"、时钟配置 |
| 卡在启动阶段 | SoC 初始化代码错误 | 在 soc.c 中逐步注释初始化代码定位问题 |
| Hard Fault / 异常 | 栈溢出或 PMP 配置错误 | 增大 CONFIG_MAIN_STACK_SIZE,检查 PMP 配置 |
| 中断不工作 | 中断控制器 DTS 或 Kconfig 配置错误 | 检查 NUM_IRQS、DTS 中 interrupt-parent 和 interrupts 属性 |
| 调度不工作 | 定时器未配置 | 确认 CLINT/mtime 或自定义定时器的 DTS 和驱动正确 |
6.3 调试技巧
7. 验证清单
7.1 编译验证
- west build -b <board> samples/hello_world 编译无错误
- 生成的 .config 文件中 CONFIG_RISCV=y
- 生成的 .config 文件中 ISA 扩展正确
- devicetree_generated.h 中所有外设节点正确生成
- 链接脚本中内存布局正确(SRAM/Flash 地址和大小)
7.2 启动验证
- 串口输出 “Hello World”
- Shell 命令可交互(如果启用)
- kernel 命令显示正确的 SoC 和 Board 名称
- 系统时钟频率正确(k_cyc_to_ns_floor64 结果合理)
7.3 功能验证
- 多线程创建和调度正常
- GPIO 输入/输出正常
- UART 收发正常
- 定时器中断正常
- 外部中断正常
- 睡眠/唤醒正常
- Flash 读写正常
7.4 ez_middleware 适配验证
如果需要在移植的 RISC-V 芯片上运行 ez_middleware,还需要验证:
- ez_sys_adapt 层的内存管理(k_malloc/k_free)正常
- ez_sys_adapt 层的线程管理(k_thread_create)正常
- ez_sys_adapt 层的定时器(k_timer)正常
- ez_sys_adapt 层的加密操作(PSA Crypto)正常
- ez_ble_adapt 层的 BLE 协议栈初始化正常
- ez_bsp_adapt 层的各外设驱动正常
- 事件回调注册/分发机制正常
附录 A:nRF54L15 与通用 RISC-V 移植对比
| 架构选择 | cpuapp→ARM, cpuflpr→RISCV_CORE_NORDIC_VPR | 单核→RISCV + RISCV_ISA_* |
| DTS 路径 | ARM: dts/arm/nordic/, RISC-V: dts/riscv/nordic/ | dts/riscv/<vendor>/ + dts/vendor/<vendor>/ |
| 中断控制器 | ARM→NVIC, RISC-V→CLIC | PLIC/CLIC/自定义 |
| 链接脚本 | ARM→cortex_m/linker.ld, RISC-V→riscv/common/linker.ld | riscv/common/linker.ld |
| SoC 初始化 | 电源/时钟/缓存/DCDC 配置 | 根据芯片手册实现 |
| 双核通信 | VEVIF mailbox | N/A(单核)或自定义 IPC |
| TrustZone | ARM SAU + MPC | RISC-V PMP(如果支持) |
附录 B:参考路径
| RISC-V 架构层 | zephyr/arch/riscv/ |
| RISC-V Kconfig | zephyr/arch/riscv/Kconfig, Kconfig.isa |
| RISC-V 链接脚本 | zephyr/include/zephyr/arch/riscv/common/linker.ld |
| WCH CH32V 移植示例 | zephyr/soc/wch/ch32v/ |
| SiFive Freedom 移植示例 | zephyr/soc/sifive/sifive_freedom/ |
| nRF54L15 SoC 实现 | zephyr/soc/nordic/nrf54l/ |
| nRF54L15 Board 实现 | zephyr/boards/nordic/nrf54l15dk/ |
| nRF54L15 DTS (ARM) | zephyr/dts/arm/nordic/nrf54l15_cpuapp.dtsi |
| nRF54L15 DTS (RISC-V) | zephyr/dts/riscv/nordic/nrf54l15_cpuflpr.dtsi |
| nRF54L15 SoC 基础 DTS | zephyr/dts/vendor/nordic/nrf54l_05_10_15.dtsi |
| QEMU RISC-V 板 | zephyr/boards/qemu/riscv32_virt/ |







