Skip to content

Risc-V

Warning

The current page still doesn't have a translation for this language.

You can read it through Google Translate.

Besides, you can also help to translate it: Contributing.

介绍 Risc-V 基础内容。

0x01 架构特点

RISC-V 是一种新兴的开放指令集架构(ISA),具有高度的灵活性和可扩展性。它以模块化设计为核心理念,允许开发者根据实际需求选择不同的扩展模块来构建处理器。例如,最基本的指令集包括 RV32I 和 RV64I,而可选的扩展则覆盖了乘除法(M)、原子操作(A)、浮点运算(F、D)、压缩指令(C)以及适用于高性能计算的向量指令集(V)等。

该架构支持多种位宽,包括 32 位(RV32)、64 位(RV64)以及未来预留的 128 位(RV128)模式,从而满足从嵌入式系统到高性能服务器等多样化应用场景。RISC-V 还明确区分了处理器的特权级别,定义了用户态(U-mode)、监督态(S-mode)和机器态(M-mode),为操作系统的运行和安全机制提供了良好的支撑。

以下表格列出了一些常见的指令集扩展及其简要说明:

名称 描述
RV32I / RV64I 基础整数指令集,构建最小系统的核心
M 扩展 支持整数乘除法运算
A 扩展 提供原子操作能力,适用于多核系统
F / D 扩展 支持浮点数运算:F 为单精度,D 为双精度
C 扩展 16 位压缩指令,提升代码密度
V 扩展 向量处理能力,用于数据并行计算
Zicsr / Zifencei 控制状态寄存器操作与指令同步控制

0x02 寄存器

RISC-V 处理器架构定义了一组通用寄存器和控制状态寄存器(CSR),用于处理数据、控制流程以及存储状态信息。以下是对这两类寄存器的简要介绍。

(1)通用寄存器

RISC-V 一共有 32 个通用寄存器,编号为 x0x31,每个寄存器为 32 位(RV32)或 64 位(RV64)。这些寄存器也有对应的 ABI 名称,常用于汇编语言编程。

寄存器编号 ABI 名称 用途说明
x0 zero 恒为 0 的只读寄存器
x1 ra 返回地址寄存器
x2 sp 栈指针
x3 gp 全局指针
x4 tp 线程指针
x5-x7 t0-t2 临时寄存器
x8 s0/fp 保存寄存器/帧指针
x9 s1 保存寄存器
x10-x17 a0-a7 函数参数/返回值
x18-x27 s2-s11 保存寄存器
x28-x31 t3-t6 临时寄存器

注意事项: - x0 是特殊的,只读寄存器,总是返回 0,写入无效。 - 临时寄存器(t0~t6)在函数调用过程中不保证保留。 - 保存寄存器(s0~s11)在函数调用之间必须被保存和恢复。

(2)CSR 寄存器

CSR(Control and Status Register)是用于控制、状态监测和配置处理器运行的重要寄存器。RISC-V 中 CSR 的地址空间为 12 位,共支持 4096 个 CSR 寄存器。

常见的 CSR 寄存器包括:

名称 地址 功能说明
mstatus 0x300 机器级状态寄存器,保存全局中断使能、特权级等信息
misa 0x301 指示当前支持的指令集架构
mie 0x304 中断使能寄存器
mtvec 0x305 中断向量表地址
mepc 0x341 异常发生时的程序计数器保存寄存器
mcause 0x342 异常/中断原因寄存器
mtval 0x343 出错地址信息
mcycle 0xB00 机器模式下的周期计数器

注意事项: - CSR 可通过特殊隶属 zicsr 扩展指令集的指令 csrrwcsrrscsrrc 等进行读写。 - 按照特权级别分类,CSR 分为用户态(u)、监督态(s)、机器态(m*)等。 - 在简化实现中,仅支持机器态(M-mode)的 CSR 是常见情况。

(3)浮点寄存器

RISC-V 架构在支持浮点扩展(如 FD 扩展)时,提供了一组专用的浮点寄存器,用于浮点数的计算和数据存储。

RISC-V 定义了 32 个浮点寄存器,编号为 f0f31,每个寄存器通常为 32 位(单精度,RV32F)或 64 位(双精度,RV64D),具体取决于所实现的浮点扩展。

寄存器编号 ABI 名称 用途说明
f0-f7 ft0-ft7 临时寄存器
f8-f9 fs0-fs1 保存寄存器
f10-f17 fa0-fa7 函数参数/返回值
f18-f27 fs2-fs11 保存寄存器
f28-f31 ft8-ft11 临时寄存器

注意事项: - 浮点寄存器的 ABI 命名与整数寄存器类似,但前缀为 f。 - 根据 ABI 约定,函数参数通过 fa0~fa7 传递,返回值通常使用 fa0。 - ft* 为调用者保存(Caller-saved),fs* 为被调用者保存(Callee-saved)。 - 使用浮点寄存器需要处理器支持相应的浮点扩展指令集,如 F(单精度)或 D(双精度)。

0x03 常见指令

(1)常见整数指令(R 型)

R 型指令用于寄存器之间的算术或逻辑操作,格式:rd = rs1 op rs2

指令 含义 示例 说明
add 加法 add x5, x1, x2 x5 = x1 + x2
sub 减法 sub x5, x1, x2 x5 = x1 - x2
sll 逻辑左移 sll x5, x1, x2 x2 位左移 x1
srl 逻辑右移 srl x5, x1, x2 x2 位右移 x1
sra 算术右移 sra x5, x1, x2 保留符号位的右移
and 位与 and x5, x1, x2 位级 AND 运算
or 位或 or x5, x1, x2 位级 OR 运算
xor 位异或 xor x5, x1, x2 位级 XOR 运算
slt 小于置位(有符号) slt x5, x1, x2 x1 < x2x5=1 否则为 0
sltu 小于置位(无符号) sltu x5, x1, x2 同上但比较无符号值

(2)常见立即数指令(I 型)

I 型指令对寄存器和立即数执行运算,适用于快速计算和常数赋值。

指令 含义 示例 说明
addi 加立即数 addi x5, x1, 10 x5 = x1 + 10
andi 与立即数 andi x5, x1, 0xF 位级 AND,掩码操作常用
ori 或立即数 ori x5, x1, 1 设置某一位常用
xori 异或立即数 xori x5, x1, 0x1 反转特定位
slti 小于立即数 slti x5, x1, 5 有符号立即数比较
sltiu 无符号小于立即数 sltiu x5, x1, 5 无符号立即数比较
slli 左移立即数 slli x5, x1, 3 最多移位 31(RV32)
srli 右移立即数 srli x5, x1, 2 无符号逻辑右移
srai 算术右移立即数 srai x5, x1, 2 有符号右移保留符号位
lui 加载高位立即数 lui x5, 0x12345 x5 = 0x12345000
auipc 加偏移到 PC auipc x5, 0x10 x5 = PC + 0x10000

(3)加载指令(I 形)

用于从内存地址读取数据到寄存器。地址由 rs1 + offset 计算得出。

指令 含义 示例 说明
lb 加载字节(有符号) lb x5, 0(x1) x1 指向的地址读 1 字节,符号扩展到 x5
lbu 加载字节(无符号) lbu x5, 0(x1) 零扩展
lh 加载半字(16 位) lh x5, 2(x1) 符号扩展 16 位整数
lhu 加载半字(无符号) lhu x5, 2(x1) 零扩展
lw 加载字(32 位) lw x5, 4(x1) 常用加载指令
lwu 加载字(无符号,RV64) lwu x5, 4(x1) 32 位零扩展用于 RV64
ld 加载双字(64 位) ld x5, 8(x1) RV64 架构下的主力指令

(4)存储指令(S 型)

用于将寄存器中的值写入内存中。地址仍由 rs1 + offset 计算。

指令 含义 示例 说明
sb 存储字节 sb x5, 0(x1) 仅存 1 字节
sh 存储半字(16 位) sh x5, 2(x1)
sw 存储字(32 位) sw x5, 4(x1) RV32 和 RV64 共用
sd 存储双字(64 位) sd x5, 8(x1) 仅 RV64 可用

(5)控制流指令(B 型)

用于实现条件跳转,配合标签使用,跳转偏移为 12 位立即数(±4KB)。

指令 含义 示例 说明
beq 相等跳转 beq x1, x2, label x1 == x2,跳转到 label
bne 不等跳转 bne x1, x2, label x1 != x2,跳转
blt 小于跳转(有符号) blt x1, x2, label x1 < x2,跳转
bge 大于等于跳转(有符号) bge x1, x2, label x1 >= x2,跳转
bltu 小于跳转(无符号) bltu x1, x2, label 无符号小于比较跳转
bgeu 大于等于跳转(无符号) bgeu x1, x2, label 无符号大于等于比较跳转

(6)跳转与链接指令(J 型、I 型)

用于过程调用和函数跳转,保留返回地址到 ra

指令 含义 示例 说明
jal 跳转并链接(J 型) jal x1, label x1 = PC+4; PC = label(用于调用)
jalr 寄存器间接跳转(I 型) jalr x1, x2, 0 x1 = PC+4; PC = x2 + 0(常返回,调用函数指针)
  • 若目标寄存器为 x0,表示不保存返回地址(纯跳转)。
  • jal 通常用于调用函数,jalr 用于从函数返回。

(7)特殊系统调用指令(环境交互)

指令 含义 示例 说明
ecall 环境调用(系统调用) ecall 触发环境调用异常
ebreak 调试断点 ebreak 触发调试异常

0x04 调用规约

栈帧结构图(RISC-V ABI 栈帧)

高地址
│
│  +-----------------------------+
│  | 需要保存的寄存器             |
│  +-----------------------------+
│  | 超过寄存器传参的参数 > a7/fa7 |
│  +-----------------------------+
│  | 返回地址 (ra)                │
│  +-----------------------------+
│  | 局部变量                     |
│  +-----------------------------+
│  | (16 字节对齐空间填充)        |
└──► 栈指针 (sp)

1. 栈增长

  • 向低地址方向增长
  • sp(x2)总是保持 16 字节对齐
  • 常用 sp 为指针访问栈的内容(例如: ld t0, 16(sp)

2. 保存寄存器内容

callee 保存: - s0–s11(x8–x9, x18–x27) → 用于长期保存的数据 - fs0–fs11(f8–f9, f18–f27) → 浮点保存寄存器 - ra(x1) → 返回地址(call伪指令修改ra后由被调用函数保存)

caller 保存: - t0–t6(x5–x7, x28–x31) - ft0–ft11(f0–f7, f28–f31)

参数大小约束

  • 小于指针大小(如 charshort):放在寄存器的低位
  • 超过一个指针大小(如 long long):需要偶数对齐寄存器
  • 例如 RV32 中:

    void foo(int, long long)
    

    • inta0
    • long longa2a3(跳过 a1,偶数对齐)
  • 超过两个指针大小(如 struct { long x[3]; }) → 按引用传递

返回值传递规则

  • 返回值使用:
  • 整数:a0, a1
  • 浮点数:fa0, fa1
  • 结构体返回:
  • ≤2 pointer-word:用 a0/a1
  • 2 pointer-word:调用者分配内存,并通过隐藏指针作为第一个参数传递

寄存器使用一览

类型 名称 用途 由谁保存
x1 ra 返回地址 调用者(caller)
x2 sp 栈指针 被调用者(callee)
x8 s0 / fp 帧指针 / 保存寄存器 被调用者
x10–x17 a0–a7 参数 / 返回值寄存器 调用者
x5–x7, x28–x31 t0–t6 临时变量 调用者
x9, x18–x27 s1, s2–11 保存变量 被调用者
f10–f17 fa0–fa7 浮点参数 / 返回值 调用者
f0–f7, f28–f31 ft0–11 浮点临时变量 调用者
f8–f9, f18–f27 fs0–fs11 浮点保存变量 被调用者

注意 sp, ra 寄存器严格来说不属于 草稿/保留 寄存器分类,对这两个寄存器的处理多以callee保存为实现。与此同时还有 gp 等寄存器不属于这个范畴。

参阅