1. 课程导论:ARM与RISC-V架构差异概述、驱动移植面临的挑战、设备树与中断控制器迁移的核心地位
1.1 ARM与RISC-V:两种指令集架构的基因差异
嵌入式系统驱动移植的本质,是从一种处理器架构的硬件抽象层迁移到另一种。ARM(v7-A/v8-A)与RISC-V(RV64GC)虽然都采用加载-存储(Load-Store)架构,但在特权级模型、中断控制器、系统寄存器访问方式上存在根本性差异。下表梳理了驱动移植中最关键的架构差异点:
| 对比维度 | ARM (Cortex-A系列) | RISC-V (RV64GC) | 移植影响 |
|---|---|---|---|
| 特权级模型 | EL0/EL1/EL2/EL3(4级) | U/S/M(3级,可选HS) | 中断入口/退出栈切换逻辑需重写 |
| 中断控制器 | GICv2/v3(分发器+CPU接口) | PLIC/CLINT(平台级+核级) | 中断号映射、优先级、亲和性机制完全不同 |
| 系统寄存器访问 | MRS/MSR 指令 + 协处理器 |
CSRRW/CSRRS 指令 |
上下文保存/恢复代码需逐条替换 |
| 设备树绑定 | arm,gic-* 兼容字符串 |
riscv,plic0 / riscv,clint0 |
中断控制器节点必须重新描述 |
| 内存模型 | 强排序(Device-nGnRnE) | 弱一致性(WMO + fence) | DMA/MMIO 屏障指令需适配 |
核心认知:ARM 的 GIC 是架构强制的中断控制器,所有 Cortex-A 处理器必须实现 GIC 接口;而 RISC-V 的 PLIC/CLINT 是平台定义的,不同 SoC 可能使用不同的中断控制器实现。这意味着设备树中的 interrupt-controller 节点必须从 ARM 的 GIC 绑定完全迁移到 RISC-V 的 PLIC/CLINT 绑定。
1.2 驱动移植面临的三大挑战
从 ARM 到 RISC-V 的驱动移植不是简单的“查找替换”,而是涉及硬件抽象层重构、中断路径重映射和设备树重写的系统工程。以下是实践中最常见的三大挑战:
- 中断控制器语义不兼容:ARM GIC 使用
SPI(共享外设中断)和PPI(私有外设中断)的编号体系,而 RISC-V PLIC 使用context+source的二维中断号。驱动中的request_irq()参数必须重新计算。 - 系统寄存器上下文保存/恢复:ARM 的
DAIF中断掩码、SPSR_EL1状态保存与 RISC-V 的mstatus/sstatus寄存器位域完全不同。中断嵌套、抢占逻辑需要从零实现。 - 设备树绑定与解析差异:ARM 设备树中
interrupt-parent = &gic;配合interrupts = <0 42 4>;的三元组格式,在 RISC-V 中变为interrupts-extended = <&plic0 1 42>;的扩展格式。解析代码必须适配新的of_irq_parse_one()路径。
常见陷阱:许多开发者将 ARM 的 writel_relaxed() 直接替换为 RISC-V 的 writel(),忽略了 RISC-V 弱内存模型下 MMIO 操作需要显式插入 fence iorw, iorw 屏障。这会导致 DMA 描述符更新顺序错误,引发数据损坏。
1.3 设备树与中断控制器迁移的核心地位
在驱动移植的整个工作流中,设备树(Device Tree)和中断控制器(Interrupt Controller)的迁移占据了约 60% 的工作量。设备树是硬件拓扑的“唯一真相源”,而中断控制器是外设与 CPU 交互的“神经中枢”。两者必须协同迁移,否则驱动无法正确响应硬件事件。
下图展示了 ARM 与 RISC-V 在设备树中断路径上的核心差异:
迁移要点:设备树迁移的核心工作包括:① 将 interrupt-parent 从 &gic 改为 &plic0;② 将 interrupts 三元组格式改为 interrupts-extended 扩展格式;③ 在 PLIC 节点中正确描述 riscv,ndev 属性和 interrupts-extended 到 CLINT 的映射。这些改动直接影响 irq_domain 的解析和中断号的最终分配。
1.4 中断控制器迁移的层次模型
中断控制器的迁移可以抽象为三个层次:设备树描述层、irq_chip 驱动层和中断分发层。每个层次在 ARM 和 RISC-V 之间都有对应的映射关系:
| 层次 | ARM GICv2 | RISC-V PLIC + CLINT | 迁移动作 |
|---|---|---|---|
| 设备树描述 | compatible = "arm,cortex-a15-gic" |
compatible = "riscv,plic0" |
替换 compatible 字符串,重写 interrupt-cells |
| irq_chip 驱动 | gic_handle_irq() → 读 GICC_IAR |
plic_handle_irq() → 读 PLIC_CLAIM |
重写中断处理入口,替换寄存器访问指令 |
| 中断分发 | GICD 分发器 → CPU 接口 → 核 | PLIC 优先级仲裁 → context 选择 → CLINT | 重新实现中断亲和性与负载均衡 |