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 个通用寄存器,编号为 x0
到 x31
,每个寄存器为 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
扩展指令集的指令 csrrw
、csrrs
、csrrc
等进行读写。 - 按照特权级别分类,CSR 分为用户态(u)、监督态(s)、机器态(m*)等。 - 在简化实现中,仅支持机器态(M-mode)的 CSR 是常见情况。
(3)浮点寄存器¶
RISC-V 架构在支持浮点扩展(如 F
和 D
扩展)时,提供了一组专用的浮点寄存器,用于浮点数的计算和数据存储。
RISC-V 定义了 32 个浮点寄存器,编号为 f0
到 f31
,每个寄存器通常为 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 < x2 ,x5=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)
参数大小约束¶
- 小于指针大小(如
char
、short
):放在寄存器的低位 - 超过一个指针大小(如
long long
):需要偶数对齐寄存器 -
例如 RV32 中:
void foo(int, long long)
int
→a0
long long
→a2
和a3
(跳过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
等寄存器不属于这个范畴。