本篇是《Drone OS 深度实战:Rust 异步实时操作系统全解析》系列第 29 篇。上一篇:扩展体系:PSL 与 VSL 分层,下一篇:收官:横向对比与进阶路线,目录见文末。

移植与生态:RISC-V、RP2040 与新芯片

导读

第 28 篇给了分层地图,本篇走一遍真实地形:Drone 生态里除了 Blue Pill 之外还有哪些正在生长的分支?我们把视野放到五个对象上:

  1. drone-riscv——跨出 ARM 的实验性平台层(Longan Nano 的 GD32VF103 是首个落地目标);
  2. drone-raspberrypi-pico——RP2040 双核 Cortex-M0+ 模板:双核双栈双线程池的特殊形态;
  3. drone-nrf-map——nRF 系列厂商映射与 nRF9160 的 DSO 日志路线;
  4. drone-svd——VSL 自动化的起点工具;
  5. longan-blink / nrf9160dk-blink 两个示例仓库——移植成果的最小可运行证明。

先给一个诚实的现状声明:与本系列前 28 篇基于 0.14/0.15 稳定 API 不同,本篇的部分仓库处于实验性或早期阶段(drone-riscv 尤甚),API 与支持面可能变化,代码以仓库当前 master 为准。本篇的定位是"读懂分支结构 + 建立移植方法论",而非逐行 API 手册。

为什么需要关注移植与生态

三个理由。生存理由:单一芯片/单一板卡的支持面限制了 RTOS 的适用半径,Drone 的核心资产(fiber 调度、定容堆、寄存器 tag 体系)是平台无关的,生态分支决定这些资产能触达多少硬件。学习理由:移植是检验架构理解的最深题型——读懂 drone-cortexm 与"能在 RISC-V 上重写一个 drone-cortexm"之间隔着第 9-14 篇所有知识的总装。选型理由:第 30 篇的横向对比里,"支持哪些硬件"是 Drone 与 FreeRTOS/Tock 这类多平台 RTOS 比拼的显性维度,知道分支的现状才知道自己的项目能不能上这班车。

代码案例:三种移植形态剖析

形态一:跨架构 —— drone-riscv 与 Longan Nano

Longan Nano 是 Sipeed 出品的 GD32VF103CBT6 板卡:RISC-V RV32IMAC(Bumblebee 核,Nuclei Systems),32 KB RAM / 128 KB flash,板载 160x80 LCD 与 RGB LED——规格与 Blue Pill 惊人相似,是 RISC-V 阵营的"Blue Pill 平替",也是 drone-riscv 的验证平台(longan-blink 仓库即最小示例)。

drone-riscv 作为 PSL 要替换的四件套(对照第 28 篇表格):

core 侧契约 Cortex-M 实现 RISC-V 实现(drone-riscv)
线程触发 NVIC 异常进入 trap 向量(中断/异常入口)
原子底座 LDREX/STREX RISC-V A 扩展(AMOSWAP 等 AMO 指令)
栈切换 SVC 指令 + 双栈 ecall + 手工换栈指针
复位/睡眠 SCB_AIRCR / WFI 自定义复位流程 / wfi 指令

应用层的写法不变——thr::pool!(软池)或 trap 版线程宏、heap!、stream!、reg! 全部照用,只是 drone_cortexm:: 前缀换成 drone_riscv::。这正是第 28 篇"core 对平台一无所知"承诺的兑现:fiber 链、spsc、堆池在 RV32 上逐字同一份代码。

RISC-V 移植的特殊难点不在寄存器(SVD 照常解析 GD32 的 SVD),而在中断模型:Cortex-M 的 NVIC 自动向量化 + 自动压栈让"每个中断一个线程"几乎是免费的,而 GD32VF103 的中断控制器(CLIC/PLIC 混合形态)需要 PSL 手工做向量分发与上下文保存——drone-riscv 的 trap 处理代码承担了 NVIC 硬件原本自动完成的工作。这也是它标"实验性"的主要原因:中断路径的每一处细节都要人肉正确。

0.15 变化:drone-riscv 不在 crates.io 0.14 稳定版里,仅存在于源码仓库。本节代码骨架为示意,以仓库实际 master 为准。

形态二:同架构双核 —— drone-raspberrypi-pico

RP2040 是两个 Cortex-M0+ 并排(264 KB RAM,无 FPU/MPU),drone-raspberrypi-pico 模板展示了 Drone 在多核形态下的答案。0.15 模板的文件结构直接说明了模型:

src/
├── thr0.rs    // 核 0 的线程池定义(thr::nvic!)
├── thr1.rs    // 核 1 的线程池定义
├── main.rs    // 双核启动:核 0 跑 main,核 1 由启动代码带起
└── tasks/├── root0.rs   // 核 0 的 root 任务└── root1.rs   // 核 1 的 root 任务

加上 boot2.ld(RP2040 的第二阶段引导镜像链接节),这个模板表达的事实是:每核一套独立的线程池、栈与 root 任务,drone-core 的 Thread trait 在两核上各实例化一次。核间通信复用第 16 篇的消息传递原语——spsc 通道的无锁单生产者单消费者语义天然匹配"核 0 生产、核 1 消费"的分工,跨核的 add_fib 原子入链也是安全路径(fiber 链本身 lock-free)。

M0+ 的移植难点与 M3 相反:NVIC 一样存在("线程即中断"免费),但 M0+ 没有 LDREX/STREX,原子 CAS 要靠位带别名区模拟(RP2040 无位带,用 RP2040 自己的 atomic hardware/回退方案)。drone-cortexm 的 cortexm0plus_r0p0/r0p1 flag(第 2 篇的核版本表)就是这个形态的入口,软原子回退(第 28 篇 Q1)在这里从"可选项"变成"必选项"。

形态三:加一个厂商系列 —— drone-nrf-map 与 nRF9160

nRF9160 是 Nordic 的 LTE-M/NB-IoT SoC:Cortex-M33F(ARMv8-M 主线,带 FPU/MPU/TrustZone),target thumbv8m.main-none-eabihf,核修订 cortexm33f_r0p2(第 28 篇的 Device 注册示例原文)。它没有 SWO,日志走 DSO——drone-nrf91-dso 用 UARTE(feature 声明里那一个 features: &["uarte"] 就是"这个芯片的绑定只需要 uarte 外设映射"的意思)。

drone-nrf-map 与 drone-stm32-map 的结构同构(第 28 篇 VSL 三段式):drone-nrf-map-pieces-1..12 并行生成寄存器绑定,drone-nrf-map-periph-uarte 等 opt-in crate 手写外设映射,顶层 re-export。nrf9160dk-blink 仓库是开发板(nRF9160 DK)上的最小示例——对标 bluepill-blink 的生态位。

工具层:drone-svd

drone-svd 是 CMSIS-SVD 文件解析器,VSL 自动化的源头。它的输入是厂商 SVD(drone-stm32-map 仓库里 svd_files/STM32F103.svd 那样的文件),输出是寄存器/字段/访问类型的中间表示,供 map crate 的 generate_regs 消费。作为独立 crate 的价值:SVD 方言杂乱(各厂商导出质量参差),解析层的脏活集中一处;map crate 只关心"中间表示 -> reg! 宏调用"的干净翻译。本系列核对寄存器字段名时反复使用的 SVD 文件,就是这条流水线的原料。

逐段解析

移植工作量矩阵

把三种形态的清单并排,工作量分布一目了然:

工作项 同系列新型号(STM32WB55) 新系列同架构(nRF9160) 新架构(GD32VF103) 多核(RP2040)
PSL 运行时 零 零 重写 扩展(双核启动)
PSL 线程后端 零 零 重写(trap 分发) 扩展(两套池)
PSL Waker 零 零 重写 复用 x2
VSL 寄存器绑定 加 SVD 文件 新 map crate(pieces 骨架照抄) 新 map crate 新 map crate
VSL 外设映射 按需补 新写 新写 新写
日志 SWO 现成 写 DSO crate 看核(GD32 有 SWD/串口,需适配) 看探针支持
CLI 注册 一条 Device 一条 Device Platform 枚举 + Device Platform 扩展 + Device
示例仓库 可选 nrf9160dk-blink 型 longan-blink 型 模板本身

规律:越往左越便宜,右端是新世界。同系列新型号是"声明工作",新架构是"系统编程工作"。这个矩阵也是给想贡献者的路线图:从矩阵左侧入手(给 drone-stm32-map 补 SVD、补外设映射)是最低门槛的贡献方式。

核版本 flag 的完整谱系

drone-cortexm 支持的核与 flag(第 2 篇表格的复习 + 本篇视角):

  • ARMv6-M:cortexm0plus_r0p0 / r0p1(RP2040 属于这里);
  • ARMv7-M:cortexm3_r0p0 到 r2p1(Blue Pill 是 cortexm3_r1p1);
  • ARMv7E-M:cortexm4_r0p0/r0p1 与带 FPU 的 cortexm4f_*;
  • ARMv8-M:cortexm33(f)_r0p2/r0p3/r0p4(nRF9160 是 cortexm33f_r0p2)。

flag 在应用侧设置(0.14 由 drone new 写入 .cargo/config,0.15 写入 flake.nix 的 rustFlags),PSL 源码用 #[cfg(drone_cortexm = "cortexm3_r1p1")] 消费。这个设计的含义:同一份 drone-cortexm 源码为每颗核的精确修订版生成特化代码(修订版之间的指令时序、errata 差异都吃进去),应用侧必须声明得精确到 rXpY——比 --target 更细一层的硬件契约。

双核模板的线程模型细节

RP2040 模板的两套线程池引出几个值得想清楚的问题:

  • 两核的 Thr 类型不同(thr0/thr1 各自的 nvic! 展开),fiber 不能跨池移动——跨核协作只能走消息传递(spsc 的 send/recv 各在一核)或共享内存 + 原子;
  • 堆是共享的:heap! 生成的 Pool 分配器基于 lock-free 链表,两核并发分配安全且 O(1)——定容堆设计在多核下的又一个红利;
  • stream 是共享的:Drone Stream 的环形缓冲写入是原子事务,两核的 println! 交错但不串包;
  • 启动不对称:RP2040 的核 1 由核 0 的启动代码唤醒(boot2 与主程序决定),模板里 root1 的启动时机在核 0 的初始化路径上。

对比传统双核 RTOS(往往一个核跑内核一个核裸跑,或 SMP 加自旋锁),Drone 的"双核 = 两台单核 Drone + 无锁信道"是单栈哲学的自然延伸:每个核一个栈、一套严格优先级,跨核不共享执行状态只共享内存——避免了 SMP 调度的复杂性与锁风暴。

示例仓库的定位

三个官方系示例仓库构成"移植成熟度"的标尺:

  • bluepill-blink(book 三章配套):STM32F103 全功能验证——时钟、GPIO、SysTick、SWO,一切稳定 API 的基准;
  • longan-blink:RISC-V 移植的存在性证明——GD32VF103 上跑通 blink,验证 drone-riscv 的 trap/原子/线程链路;
  • nrf9160dk-blink:新厂商系列 + DSO 日志的整合验证——M33F、J-Link、DSO 三条新路径各就位。

模仿这三个仓库的"最小闭环"思路:新移植的第一个里程碑不是功能丰富,而是 blink 能跑 + 日志能看——第 4 篇与第 20 篇的体验在每个新平台上重演一遍,就说明移植通了。

0.15 变化:RP2040 模板(boot2.ld、双 root 任务)是 0.15 project-templates 的新成员;0.14 时代没有双核模板。drone-riscv、drone-raspberrypi-pico、drone-nrf-map 均为独立仓库(crates.io 上版本节奏与主仓库不同步),使用前查各自 README。

运行现象

以 longan-blink 的假想流程走一遍跨架构移植的"编译现象"(长an Nano 实机流程与 Blue Pill 类似,探针换成 GD-Link/Sipeed 调试器):

$ git clone --recurse-submodules https://github.com/drone-os/drone-riscv
# 平铺目录:drone-core/drone-riscv/drone-gd32-map(若有)/项目 同级
$ cd longan-blink
$ cargo build --release    # nightly + riscv32imac-unknown-none-elf
$ # 探针烧写,串口或板载 LED 验证

编译成功的关键标志:rustFlags 里的 --cfg drone_riscv="..."(架构 flag)与 map crate 的芯片 flag 被正确消费;失败则通常是五仓库未平铺(path 依赖断裂)或 nightly 缺 riscv32 target 组件。

nrf9160dk-blink 侧:drone new --device nrf9160(0.14)生成项目后,just flash 走 J-Link(registry 里 probe_jlink 的 device 名),just log 走 DSO 解析器——终端里看到的 Hello, world! 与 Blue Pill 上无异,但背后是 UART 承载的 DSO 协议。三个平台、三种探针、两种日志协议,应用代码却都是同一个模板改出来的——"生态多样性不影响开发体验"就是现象本身。

RP2040 侧的现象更特别:drone stream 能同时看到两个 root 任务各自的输出(共享 stream 通道),逻辑分析仪在两个核的 GPIO 上能观察到真正的并行翻转——不是快速交替,是同一纳秒级窗口内各自独立。这是单核 RTOS 无法产生的现象,也是双核模板存在的意义。

常见问题

Q1:想给 Drone 加支持的新芯片在 CLI 的 registry 里没有,但平台与系列都有现成 crate,具体怎么动手?

按第 28 篇的"确认 -> 声明 -> 注册"三步,落到文件级:(1) 确认核修订版在 drone-cortexm 的 flag 表里(不在则 PSL 要先加 flag,参考同架构相邻修订版的 cfg 分支);(2) map crate:把芯片的 SVD 文件放进 svd_files/,在 svd/src/lib.rs 的 cfg 匹配表加一行,若有外设缺失/多余按 RM 微调生成器 exclude 列表;外设映射按需在对应 periph crate 加 f 系列模块;(3) CLI:drone/src/devices/registry.rs 加 Device 记录(target、origins、flags、探针、日志);(4) 跑通模板 blink 后提交 PR。注意 0.15 master 的 drone new 已移除,注册工作主要服务版本管理;自己复制 project-templates 也能开工,不等 CLI。

Q2:M0+ 没有硬件 CAS,drone-core 的"原子 CAS 假设"在 RP2040 上怎么成立?

drone-core 的原子要求是"逻辑上的原子读改写",不是"必须是 LDREX"。无硬件 CAS 的核上有两条路径:位带/外设辅助的原子位操作(Cortex-M0+ 经典技巧:位带别名写是单访存原子读改写,RP2040 无位带则用芯片自己的硬件原子外设或临界区模拟);软原子回退(关中断 + 重试序列,drone-core 的 sync::soft_atomic 模块,第 7/28 篇讲过)。代价是原子操作的时延变高且关中断窗口可见——影响的是最坏中断延迟,不是正确性。drone-cortexm 的 cortexm0plus_* flag 会选择对应的实现路径,应用无感知。选型含义:M0+ 上 Drone 可用但中断延迟预算更紧,硬实时要求极高的场景优先 M3/M4/M33。

Q3:drone-riscv 实验性到什么程度?拿它做产品靠谱吗?

诚实的评估是"研究/学习阶段"。证据面:无 crates.io 稳定发布(仓库源码级使用)、示例仓库只有 longan-blink 一个最小闭环、中断分发路径需要大量手工汇编(Cortex-M 的 NVIC 自动向量化优势不存在)、PSL 文档与测试覆盖薄于 cortexm。它适合:研究 RISC-V RTOS 移植、验证 drone-core 的平台无关性承诺、给 GD32VF103 写实验固件。不适合:量产项目。如果产品定位在 RISC-V 实时系统,短期建议 embassy 的 riscv 支持或裸机 + RTIC 式框架做风险评估,同时关注 drone-riscv 的演进。这个判断也示范了第 30 篇要讲的"生态成熟度"评估维度。

Q4:双核模板里两个线程池的优先级如何协调?核 0 的高优先级能抢占核 1 吗?

不能,也不应该。两核各自独立调度:核 0 的严格优先级抢占只发生在核 0 的中断集内,核 1 同理——这是"两台单核"模型的必然。跨核的"抢占"语义由任务分配解决:把高实时任务放哪个核是设计期决策(类似 AMP 架构的职责划分),不是运行期调度决策。运行期的跨核协调手段:spsc 信道的优先级信号(核 1 消费慢时核 0 的发送会 backpressure)、共享原子状态。避免的手段:不要用自旋锁跨核保护长临界区——两核各自的中断延迟会被对方的临界区拉爆。Drone 的设计哲学(无锁、消息传递、编译期拓扑)在双核形态下反而比 SMP 方案更少锁与竞态面。

Q5:为什么 drone-svd 解析 SVD 而不是直接解析芯片手册生成寄存器?SVD 质量差怎么办?

SVD(CMSIS 标准 XML)是厂商机器可读的寄存器描述,虽不完美但结构化、可 diff、可版本化;芯片手册是 PDF,机器解析等于二次 NLP 工程,成本与出错率不可控。SVD 质量参差的对策:drone-stm32-map 的生成器有补丁层(patch_stm32f102/f401/... 函数在生成前修 SVD 的错误字段),外加"与 RM 不一致视为 bug 可修"的承诺(第 3 篇讲过)——SVD 是初始事实源,RM 是终审事实源,两层校验。这也是它比社区通用 SVD 绑定生成器(如 svd2rust 系列)多出的一层工程保障:patch 能力与 RM 校验纪律。

面试考点

Q1:对比 Drone 在 RP2040(双核 M0+)与在 STM32F103(单核 M3)上的调度模型差异,并说明跨核通信原语的选型理由。

参考答案:F103:单核严格优先级全抢占,线程 = NVIC 中断,原子 = LDREX/STREX;RP2040:两核各一套独立线程池与 root 任务,核内同样是严格优先级抢占(NVIC),核间互不抢占。跨核通信选 spsc:单生产者单消费者的无锁语义与"核 0 生产/核 1 消费"的 AMP 分工同构,无锁实现避免了核间自旋锁与中断屏蔽;堆与 stream 是全局共享的(lock-free 保证并发正确)。不能跨核移动 fiber(不同 Thr 类型),实时任务的核分配是设计期决策。这个模型规避了 SMP 调度的复杂性与锁风暴,代价是负载均衡要人工规划。

Q2:drone-riscv 需要重写 PSL 四件套的哪几件?哪一件的工作量最大,为什么?

参考答案:四件全要重写:运行时(rt.rs 的 BSS/data 初始化换成 RISC-V 链接约定)、线程后端(NVIC 换成 trap 向量分发)、Waker(换成 RISC-V 中断软件挂起/SEV 类机制)、栈式 fiber(SVC 换成 ecall + 手工换栈)。工作量最大的是线程后端:Cortex-M 的 NVIC 把"向量查找、压栈、返回"全部硬件化,RISC-V 的 trap 入口是一个统一入口,PSL 要手工保存/恢复上下文并按 mcause 分发到对应线程——相当于用软件实现 NVIC 的一部分。此外 GD32VF103 的中断控制器是 CLIC/PLIC 混合形态,进一步增加分支复杂度。这就是 drone-riscv 实验性的技术根源。

Q3:nRF9160 的日志为什么走 DSO 而不是 SWO?说明 DSO 与 SWO 在协议层的同构性。

参考答案:nRF9160 无 ITM/SWO 硬件(Cortex-M33 上 Nordic 未引出该调试通路),硬件日志缺失,只能软件实现。DSO(Drone Serial Output)与 SWO 的同构点:都是多端口流(stream 编号语义一致:0=stdout、1=stderr、31=heaptrace)、都提供原子包(SWO 是硬件包封装,DSO 是软件包格式)、CLI 侧都渲染为统一日志视图。实现差异:SWO 靠 ITM 硬件 FIFO 与探针解码,DSO 靠软件 FIFO + 通用 UART。应用层 println! 经 stream! 抽象后对两种后端无感知——log_swo/log_dso 的 CLI 字段切换即完成迁移。这是"日志也是可替换后端"的分层设计。

Q4:给 drone-stm32-map 添加一颗新型号(如 STM32F105)的完整变更集是什么?哪一步最容易出错?

参考答案:变更集:(1) SVD 文件入库 svd_files/;(2) svd/src/lib.rs 的设备 cfg 匹配表加分支(必要时写 patch 函数修 SVD 缺陷);(3) 各 periph crate 的 lib.rs cfg 列表把新型号归入正确的系列分支(f1/f3/f4/l4),并按该型号的外设可用性调整 feature 支持面;(4) README/文档更新。最容易错的是 (3):型号到系列分支的归类——F105 是互联网连接型(以太网 MAC、CANx2),外设集合与 F103 不完全重合,照抄 f103 分支会产生"生成了不存在的寄存器"或"漏了存在的寄存器";SVD patch(2)次之,厂商 SVD 的字段 access 标错会传导成错误的 API 形态。验证手段:模板 blink 编译通过 + reg token 抽查若干寄存器地址与 RM 对照。

Q5:从生态视角评估:Drone 的多平台现状与 RTIC/embassy 相比处于什么位置?这对芯片选型意味着什么?

参考答案:RTIC 与 embassy 依托 cortex-m 生态(svd2rust/stm32-rs 等)覆盖几乎全部主流 Cortex-M 芯片,选芯片即选映射,社区生成的绑定矩阵大;Drone 的 VSL 是自建体系(drone-svd + 手写映射 + 12 pieces),覆盖面小(STM32 F1/F3/F4/L4、nRF91、RP2040 模板、RISC-V 实验),但每颗被覆盖芯片的支持深度(核修订 flag、位带自动化、两阶段链接、stream/heap 工具链)高于平均水平。选型含义:目标芯片在 Drone 覆盖面内且项目吃"确定性/工具链"红利(堆可证、日志全链路、host 测试)时,Drone 是强选;芯片在覆盖面外而团队无 VSL 扩展意愿时,选 RTIC/embassy 更务实。这是"生态广度 vs 单平台深度"的经典权衡,第 30 篇展开。

小结

  1. 三种移植形态与成本梯度:新型号(声明级)< 新系列(map crate 级)< 新架构(PSL 重写级);RP2040 是第四形态(多核扩展);
  2. drone-riscv 验证了 core 的平台无关性,但 trap 分发的软件化让它停留在实验阶段;GD32VF103/Longan Nano 是其验证平台;
  3. RP2040 双核模型:"两台单核 Drone + 无锁信道",每核独立线程池与 root 任务,跨核协作走 spsc,堆与 stream 全局共享;
  4. drone-nrf-map 与 nrf9160dk-blink 展示了新厂商系列接入的完整样板:M33F + J-Link + DSO 三条新路径;
  5. drone-svd 是 VSL 自动化的源头,patch 层 + RM 终审纪律对抗 SVD 质量波动。

下一篇收官:把 Drone 与 RTIC、embassy、FreeRTOS、Tock 放上同一张桌子,横向比较调度模型、内存模型、async 支持与 unsafe 边界,并给出本系列的进阶路线。

系列目录

  1. 系列总览:Drone OS 设计哲学与生态全景
  2. 环境搭建:nightly 工具链与 drone CLI
  3. 硬件准备:Blue Pill 与 Black Magic Probe
  4. Hello, world!:项目模板与 SWO 打印
  5. Drone.toml 与构建体系:layout、vectors 与两阶段链接
  6. 内存映射寄存器(上):tag 体系与读写字段
  7. 内存映射寄存器(下):原子性、字段修饰与位操作
  8. Token 抽象与外设映射:map! 宏与零成本泛型
  9. 线程模型(上):thr::pool! 与硬件线程
  10. 线程模型(下):软线程池与 pending 位图
  11. Fiber 深入:栈式协程与 RootFib
  12. 中断线程:isr!、tick! 与 sv!
  13. SVCall 与上下文切换:sv::pool! 服务
  14. 异步运行时(上):async/await 与 fiber 调度
  15. 异步运行时(下):Future 组合与 Stream
  16. 消息传递并发:spsc、Mutex 与无锁链表
  17. 动态内存:heap! 宏与 O(1) 无锁 Pool
  18. 堆追踪与确定性分析
  19. 启动流程:vectors、单栈模型与初始化序列
  20. 实战:Blink at Full Speed 与 RCC 时钟配置
  21. SysTick 与定时体系:tick 线程与内置驱动
  22. GPIO 实战:外设 opt-in 与寄存器级控制
  23. TIM 定时器实战:PWM 与输入捕获
  24. UART 与 DMA:流式收发实战
  25. SPI 与 I2C 实战:传感器通信
  26. EXTI 外部中断与低功耗设计
  27. 测试策略:host 测试与硬件在环
  28. 扩展体系:PSL 与 VSL 分层
  29. 移植与生态:RISC-V、RP2040 与新芯片
  30. 收官:横向对比与进阶路线