2. RISC-V架构基础

RISC-V 是一套开放、模块化、可扩展的指令集架构(ISA)。它不像 x86 那样背负历史包袱,也不像 ARM 那样需要授权才能实现,而是以“基础指令集 + 可选扩展”的方式组织。对于多核 Linux 系统开发而言,理解 RISC-V 的指令集特点、寄存器组织、特权级、内存模型与多核扩展,是后续编写启动代码、移植内核、调试并发问题的基础。

一、指令集特点:模块化与规整编码

RISC-V 的核心思想是“小基础、大扩展”。基础整数指令集只有 RV32I / RV64I,指令长度固定为 32 位,编码规整,译码简单。常用扩展包括:

  • M:整数乘除法;
  • A:原子操作,多核同步的关键;
  • F/D:单/双精度浮点;
  • C:压缩指令,降低代码体积;
  • V:向量扩展,用于高性能计算;
  • H:虚拟化扩展,支持 Hypervisor。

Linux 通常要求 RV64GC,即 RV64I + M + A + F + D + C。这种模块化让同一套软件栈可以运行在从 MCU 到服务器级处理器的不同实现上。规整的编码也意味着流水线设计更简单,但代价是代码密度需要靠 C 扩展弥补。

二、寄存器组织:32 个通用寄存器与调用约定

RISC-V 定义 32 个通用寄存器 x0~x31,其中 x0 恒为 0。寄存器没有隐式含义,但 ABI 为其规定了角色:a0~a7 传参,a0/a1 返回结果,sp 栈指针,ra 返回地址,s0~s11 被调用者保存,t0~t6 调用者保存。这种约定直接影响函数调用、中断处理和上下文切换。

寄存器   ABI名   用途
x0      zero    恒为0
x1      ra      返回地址
x2      sp      栈指针
x10-x11 a0-a1   参数/返回值
x12-x17 a2-a7   参数
x8      s0/fp   保存寄存器/帧指针
x18-x27 s2-s11  被调用者保存
x5-x7   t0-t2   临时寄存器
x28-x31 t3-t6   临时寄存器

在 Linux 内核中,进程切换必须保存和恢复这些寄存器;中断入口也要根据 ABI 判断哪些寄存器需要压栈。理解寄存器角色,才能看懂 entry.S 和 switch_to 的汇编逻辑。

三、特权级:M/S/U 三级与 Trap 机制

RISC-V 定义三个特权级:M(Machine)、S(Supervisor)、U(User)。M 级最高,负责启动、时钟中断和底层控制;S 级运行 Linux 内核;U 级运行用户进程。Linux 通常运行在 S 级,通过 SBI 调用 M 级固件完成关机、IPI 等操作。

特权级切换由 Trap 机制完成。发生异常、中断或 ecall 时,CPU 保存 mepc/sepc,跳转到 mtvec/stvec 指定的入口,并根据 mcause/scause 判断原因。返回时执行 mret/sret。这套机制是系统调用、缺页异常和中断处理的基础。

调试提示:若内核启动后立即陷入 M 级,优先检查 stvec 是否设置、S 级中断是否在 sie 中使能,以及 sstatus.SIE 是否打开。

四、内存模型与多核扩展

RISC-V 采用弱内存模型(RVWMO),允许硬件在不破坏单核语义的前提下重排访存。多核环境下,必须使用 fence 指令或 A 扩展的原子指令(lr/sc、amoswap)建立顺序。Linux 中的 smp_mb()、smp_rmb() 最终会落到这些指令上。

多核扩展方面,RISC-V 通过 CLINT/PLIC 或 ACLINT/AIA 管理中断,通过 IPI 实现核间中断。每个 hart 有独立的寄存器上下文,但共享内存和外设。启动时通常由一个 hart 执行初始化,再通过 sbi_send_ipi 唤醒其他 hart。

RISC-V 多核系统结构:特权级、寄存器与内存模型 特权级 M 级 Machine 启动 / SBI / 时钟 mtvec / mcause / mret S 级 Supervisor Linux 内核 stvec / scause / sret U 级 User 用户进程 ecall 进入内核 Trap:异常/中断/ecall 保存 sepc → 跳 stvec 寄存器组织 x0 zero 恒为0 x1 ra / x2 sp a0-a7 参数/返回 x10-x17 s0-s11 被调用者保存 x8-x9, x18-x27 t0-t6 临时寄存器 x5-x7, x28-x31 上下文切换保存/恢复 entry.S / switch_to 内存模型与多核 RVWMO 弱内存模型 fence / lr.sc / amo* smp_mb() → fence rw,rw 中断控制器 CLINT / PLIC ACLINT / AIA 核间中断 IPI sbi_send_ipi(hart) 唤醒其他 hart 共享内存 + 外设 每 hart 独立寄存器上下文 Trap 同步

五、案例:从启动到多核并行的关键路径

以一颗 4 核 RV64GC 处理器为例。上电后 hart0 进入 M 级,执行 BootROM,初始化 CLINT 和内存,然后跳转到 S 级内核入口。内核在 S 级完成页表、中断控制器和调度器初始化后,通过 SBI 向 hart1~hart3 发送 IPI。其他 hart 收到 IPI 后从 sbi_hart_start 指定的地址开始执行,进入 smp_callin,最终加入调度。整个过程涉及特权级切换、寄存器上下文建立、fence 保证初始化顺序,以及 IPI 唤醒,是 RISC-V 多核 Linux 启动的典型路径。

六、常见误区与复盘

误区 正确理解
认为 RISC-V 只有一种指令集 基础 RV32I/RV64I 加多种扩展,Linux 常用 RV64GC
忽略 A 扩展和 fence 多核同步必须依赖原子指令和内存屏障
把 M 级和 S 级混为一谈 Linux 运行在 S 级,M 级由固件管理,通过 SBI 交互
认为寄存器可以随意使用 ABI 约定影响函数调用、中断和上下文切换
启动时只初始化一个 hart 需通过 IPI 唤醒其他 hart,并保证内存顺序
掌握 RISC-V 架构基础的关键,是把“指令集—寄存器—特权级—内存模型—多核”串成一条线:指令集决定能做什么,寄存器决定怎么传数据,特权级决定谁能做什么,内存模型决定多核之间如何正确同步,多核扩展决定系统如何并行启动与调度。