2. RISC-V体系结构概览

RISC-V 不是某一颗具体芯片的名字,而是一套开放、可自由实现的指令集架构(ISA)。它最显著的特征是“精简核心 + 模块化扩展”:基础指令集只定义最必要的整数运算、访存、分支和跳转,其余能力通过标准扩展按需拼装。理解这一结构,是后续编写汇编、分析启动代码和移植操作系统的前提。

一、ISA 与微架构:先分清两个层次

初学者最容易混淆的是“指令集架构”和“微架构实现”。ISA 是软件与硬件之间的契约,规定有哪些指令、寄存器、特权级和内存访问规则;微架构则是某款处理器如何用流水线、缓存、乱序执行去实现这份契约。同一份 RISC-V 汇编,既可以在简单的三级流水线 MCU 上运行,也可以在带 MMU 的应用处理器上运行,只要它们遵循相同的 ISA 约定。

RISC-V 的核心设计哲学:
1. 基础指令集小而稳定,避免历史包袱;
2. 扩展模块化,用字母后缀组合出不同应用场景;
3. 显式寄存器操作,load/store 架构,运算不直接访问内存;
4. 开放规范,任何人可免费实现,无需授权费。

二、模块化扩展:字母后缀就是能力清单

RISC-V 用字母表示标准扩展,常见组合如 RV32IMAC、RV64GC。基础整数指令集有 RV32I 与 RV64I 两种位宽,分别对应 32 位和 64 位地址空间。下表列出工程中最常遇到的扩展及其作用。

扩展名称典型用途
I基础整数指令所有实现必备,含算术、逻辑、分支、load/store
M整数乘除法乘法、除法、取余,算法与驱动常用
A原子操作自旋锁、无锁队列、多核同步
F / D单/双精度浮点信号处理、控制算法、科学计算
C压缩指令16 位短指令,减小代码体积,提升取指效率
G通用组合等价于 IMAFD,应用处理器常见基础
V向量扩展并行数据计算、AI 推理、多媒体

命名规则上,RV32IMAC 表示 32 位基础整数加 M、A、C 扩展;RV64GC 表示 64 位通用组合加压缩指令。启动代码中若使用了浮点寄存器保存上下文,就必须确认目标核确实实现了 F/D 扩展,否则会出现非法指令异常。

三、寄存器组织:汇编程序员的工作台

RISC-V 整数寄存器共 32 个,记为 x0~x31。其中 x0 恒为零,写入被忽略,读取永远为 0,这个设计让很多伪指令得以简化。ABI(应用程序二进制接口)为寄存器规定了别名和调用约定,汇编代码应优先使用别名,提高可读性与工具链兼容性。

# 常用寄存器别名与职责
zero  x0   恒零,不可写
ra    x1   返回地址,call/ret 使用
sp    x2   栈指针,指向栈顶
gp    x3   全局指针,用于访问全局数据
tp    x4   线程指针,TLS 常用
t0-t2 x5-x7  临时寄存器,调用者保存
s0/fp x8   保存寄存器/帧指针
s1    x9   保存寄存器
a0-a7 x10-x17 参数与返回值,a0/a1 返回结果
s2-s11 x18-x27 保存寄存器,被调用者保存
t3-t6 x28-x31 临时寄存器,调用者保存

调用约定是理解函数栈帧的关键:a0~a7 传参,a0/a1 返回;s0~s11 由被调用者负责保存和恢复;t0~t6 可被随意覆盖。编写启动代码时,在跳转到 C 语言入口前通常要把 sp 设置到合法栈顶,并按需清零 bss 段、搬运 data 段。

四、特权级与内存模型

RISC-V 定义了三个特权级:机器模式(M)、监管模式(S)和用户模式(U)。M 模式权限最高,负责最底层硬件控制与异常入口;S 模式通常运行操作系统内核;U 模式运行普通应用程序。嵌入式裸机程序往往只运行在 M 模式,而 Linux 这类系统会使用 M→S→U 的完整层级。

内存模型方面,RISC-V 采用弱内存序(RVWMO),默认不保证不同地址的访存顺序,需要程序员在共享数据场景显式使用 fence 指令建立顺序。这与 x86 的强内存序不同,是多核驱动和锁实现中必须注意的差异。地址空间上,指令与数据通过同一地址空间访问,但取指与访存路径在微架构中可能分离。

记忆口诀:M 管硬件,S 管系统,U 管应用;跨级访问必须通过异常或 ecall 陷入,不能随意跳转。

五、典型场景:从复位到 C 入口

以一颗 RV32IMAC 的 MCU 为例,上电后 CPU 从复位向量取指,此时处于 M 模式。启动代码通常先设置栈指针,再初始化时钟与内存,然后清零 bss、搬运 data,最后跳转到 main。若启用了压缩指令,链接脚本和反汇编输出中会看到 16 位指令与 32 位指令混排,这是正常现象。

# 简化启动片段(RV32)
_start:
    la   sp, _stack_top      # 设置栈顶
    la   t0, _bss_start
    la   t1, _bss_end
clear_bss:
    bgeu t0, t1, done_bss
    sw   zero, 0(t0)
    addi t0, t0, 4
    j    clear_bss
done_bss:
    call main                # 跳转 C 入口
    j    .                   # main 返回后原地循环

六、常见误区与复盘

  • 误区一:把扩展当成可选装饰。若工具链按 RV64GC 编译,而目标核只有 RV64I,浮点和压缩指令会触发非法指令异常。选型时必须核对 ISA 字符串。
  • 误区二:忽略 ABI 寄存器约定。在汇编函数中随意破坏 s 寄存器,会导致调用者数据错乱,且问题往往在深层调用后才暴露。
  • 误区三:认为内存序天然有序。多核共享标志位时未加 fence,可能读到旧值,表现为偶发死锁或数据竞争。
  • 误区四:混淆特权级入口。把 S 模式代码直接跳到 U 模式地址,或未设置异常向量就触发 ecall,都会导致不可预期的陷入。

复盘建议:拿到一块 RISC-V 板卡,先确认三件事——ISA 字符串、复位后所处特权级、栈指针初始值。这三项决定了启动代码能否正确运行,也决定了后续汇编与 C 混合编程的边界。

RISC-V 体系结构概览 基础整数 ISA RV32I / RV64I 算术·逻辑·分支·访存 模块化扩展(按需拼装) M 乘除 A 原子 F/D 浮点 C 压缩 V 向量 G = IMAFD 寄存器组织(x0–x31) zero(x0) 恒零 · ra(x1) 返回地址 · sp(x2) 栈指针 a0–a7(x10–x17) 参数/返回值 s0–s11(x8,x9,x18–x27) 被调用者保存 t0–t6(x5–x7,x28–x31) 调用者保存 gp(x3) 全局指针 · tp(x4) 线程指针 特权级与内存模型 M 机器模式 S 监管模式 U 用户模式 弱内存序 RVWMO:跨地址访存需 fence 保序 ecall / 异常 → 陷入更高特权级 启动路径:复位 → 设置 sp → 初始化 → 清零 bss / 搬运 data → 跳转 main 复位取指 设置栈指针 时钟/内存初始化 bss/data 处理 main