RISC-V 非特权基础指令集学习资料:RV32I、RV64I、Zifencei、Zicsr
本文严格限定在 RISC-V Volume I 非特权规范中的基础入口部分:RV32I Base Integer Instruction Set、RV64I Base Integer differences/additions、Zifencei、Zicsr。不会展开到 M、A、F、D、C、V 等其他扩展,也不会把伪指令当成正式 ISA 指令。
参考入口:<https://docs.riscv.org/reference/isa/unpriv/unpriv-index.html>
这类资料为什么要先学?因为 I 才是 RISC-V 的地基。你后面看到的函数调用、编译器生成代码、操作系统陷入、JIT 代码刷新,最终都会落回这些基础指令的语义上。把这里学稳,后面的扩展只是“往上加功能”,不是重新学一门 ISA。
为什么先学非特权基础整数指令集
RISC-V 的设计不是“先记一堆助记符”,而是“先理解一套最小但完整的整数执行模型”。RV32I 提供的是可以直接编译程序、表达控制流、读写内存、处理异常入口的最基础能力;RV64I 只是把这个模型扩展到 64 位世界,并补上更适合 64 位环境的 W 类指令和 64 位访存指令。
如果你一开始就跳去看乘除法、原子指令、浮点或者向量,很容易忽略最关键的东西:寄存器怎么用、立即数怎么编码、分支偏移为什么按 2 字节计、为什么 LW 在 RV64I 上会符号扩展、为什么 FENCE.I 不是普通 FENCE。这些细节恰恰是后续理解编译器和操作系统的基础。
先把 RV64I 的边界说清楚
这篇文章里和 RV64I 相关的内容,其实分成两类。第一类不是“新增助记符”,而是 RV32I 里已经存在、但到了 XLEN=64 时语义要重新留意的正式指令,例如 LUI、AUIPC、LW、SW、ADD/SUB/SLL/SRL/SRA,以及 SLLI/SRLI/SRAI。它们名字没变,但结果宽度、符号扩展、移位量取值都要按 64 位规则理解。
第二类才是 RV64I 真正新增出来的正式指令名字:LWU、LD、SD,以及 ADDIW、SLLIW、SRLIW、SRAIW、ADDW、SUBW、SLLW、SRLW、SRAW。前者解决 64 位地址空间下的访存问题,后者解决“在 64 位寄存器里维持 32 位结果语义”的问题。
尤其要单独记住一点:SLLI、SRLI、SRAI 在 RV64I 里不是新指令,只是同名正式指令在 XLEN=64 下改为从 I-immediate 的低 6 位取得移位量,所以可表示的移位范围是 0-63;而 SLLIW/SRLIW/SRAIW 才是 RV64I 新增的 W 类移位指令,它们固定只处理低 32 位结果,并把结果符号扩展回 64 位。
先把模型讲清楚
RISC-V 基础整数架构有 32 个通用整数寄存器,名字是 x0 到 x31。其中 x0 永远是 0,写进去也不会改变它;pc 是程序计数器,指向当前执行位置。ABI 名称只是方便人类阅读的别名,例如 ra=x1、sp=x2、a0-a7=x10-x17、t0-t6=x5-x7/x28-x31、s0-s11=x8-x9/x18-x27。
指令格式先记住六种就够了:R、I、S、B、U、J。R 型负责寄存器到寄存器运算,I 型负责带立即数的运算和很多加载指令,S 型负责存储,B 型负责条件分支,U 型负责高位立即数,J 型负责无条件跳转。RISC-V 的一个核心习惯是:立即数大多默认按符号扩展,且符号位固定在指令的 bit 31,方便硬件并行扩展。
这里必须顺手提醒一句:mv、nop、j、jr、ret、seqz、snez、not、sext.w 这些常见名字多数是伪指令或汇编器别名,不是正式 ISA 指令。学习时要把“汇编语法便利”和“架构定义”分开。
先学哪几类最划算
真正高频、最值得先掌握的是这几组:
- 上高位立即数和 PC 相关地址构造:
LUI、AUIPC - 控制流:
JAL、JALR、六条分支 - 访存:
LB、LH、LW、LBU、LHU、SB、SH、SW,以及 RV64I 的LD、LWU、SD - 立即数算术逻辑:
ADDI、SLTI、SLTIU、XORI、ORI、ANDI、SLLI、SRLI、SRAI - 寄存器算术逻辑:
ADD、SUB、SLL、SLT、SLTU、XOR、SRL、SRA、OR、AND - 系统入口和内存顺序:
FENCE、FENCE.I、ECALL、EBREAK - RV64I 的
W类:ADDIW、SLLIW、SRLIW、SRAIW、ADDW、SUBW、SLLW、SRLW、SRAW - CSR 指令:
CSRRW、CSRRS、CSRRC、CSRRWI、CSRRSI、CSRRCI
按类别讲解
1. 上高位立即数与 PC 相关地址
LUI 的用途是把一个 20 位 U 立即数放到高位,低 12 位补 0,常用于构造常量的上半部分。关键点是:它不是“加载一个完整 32 位常量”,而是先形成 32 位模式,再在 RV64I 中把结果符号扩展到 64 位,所以它在 64 位环境里不是简单的零扩展。示例:lui t0, 0x12345。
AUIPC 的用途是把当前 pc 与一个高位立即数结合,最常见的场景是构造 PC 相对地址,或者作为长距离调用/取址的前半段。关键点是:它用的是当前指令地址本身,不是“下一条指令地址”,并且在 RV64I 中同样是先构造 32 位偏移再符号扩展。示例:auipc t0, 0x10。
2. 无条件跳转
JAL 的用途是无条件跳转并把返回地址写入 rd,函数调用通常从这里开始理解。关键点是:J 立即数按 2 字节为单位编码,目标相对偏移是有符号的,范围约为正负 1 MiB;写入 rd 的是 pc+4,而不是跳转目标。示例:jal ra, func。
JALR 的用途是寄存器间接跳转,常用于函数返回、函数指针调用和间接分支。关键点是:目标地址先做 rs1 + signext(imm12),然后把结果最低位清零;它的立即数不是像 JAL/分支那样按 2 字节缩放,而是直接参与加法;如果 rd=x0,就只跳转不保存返回地址。示例:jalr x0, 0(ra)。
3. 条件分支
分支指令全部使用 B 型格式,偏移量按 2 字节计,范围约为正负 4 KiB。关键点是:只有“分支成立”时才会检查目标是否对齐并可能触发指令地址错位异常;不跳转时不会因为目标不对齐而报错。
BEQ 的用途是做相等比较,最适合零值判断、循环退出和标志位分支。关键点是:它只看两个寄存器是否相等,不关心有符号或无符号。示例:beq t0, zero, done。
BNE 的用途是做不等比较,常见于循环继续条件和状态轮询。关键点是:它是 BEQ 的反面,很多条件判断都可以先写成“相等则跳过”。示例:bne t0, t1, retry。
BLT 的用途是有符号“小于”比较,适合处理可能为负的整数。关键点是:它按照二补码有符号大小关系比较,而不是按位模式排序。示例:blt a0, a1, smaller。
BGE 的用途是有符号“大于等于”比较,通常是 BLT 的反向条件。关键点是:它同样按二补码语义判断,和 BLTU/BGEU 不是一回事。示例:bge a0, a1, ge_case。
BLTU 的用途是无符号“小于”比较,数组边界检查尤其常见。关键点是:负数在无符号比较里会变成非常大的数,所以它常能把“下界检查”和“上界检查”合并成一次比较。示例:bltu t0, t1, in_range。
BGEU 的用途是无符号“大于等于”比较,和 BLTU 成对出现。关键点是:它最适合地址、长度、索引这类本来就应该按无符号解释的值。示例:bgeu t0, t1, out_of_range。
4. 访存指令
RISC-V 是典型的 load/store 架构,算术指令只处理寄存器,内存必须通过专门的 load/store 指令访问。关键点是:有效地址由 rs1 + signext(imm12) 得到,x0 作为 load 目的寄存器时,指令仍然必须真正完成访存并触发应有异常和副作用。
LB 的用途是从内存读 8 位有符号值。关键点是:它会把最高位当成符号位并扩展到 XLEN,因此很适合读取 char 或小整数。示例:lb t0, 0(a0)。
LH 的用途是从内存读 16 位有符号值。关键点是:它和 LB 一样会做符号扩展,只是宽度换成了半字。示例:lh t0, 2(a0)。
LW 的用途是从内存读 32 位值。关键点是:在 RV32I 中它就是自然宽度;在 RV64I 中它会把 32 位结果符号扩展到 64 位,这一点非常容易踩坑。示例:lw t0, 4(a0)。
LBU 的用途是从内存读 8 位无符号值。关键点是:它零扩展到 XLEN,适合读字节标志和小型无符号字段。示例:lbu t0, 0(a0)。
LHU 的用途是从内存读 16 位无符号值。关键点是:它同样零扩展,和 LH 的差别只在于是否保留符号。示例:lhu t0, 2(a0)。
SB 的用途是把寄存器低 8 位写回内存。关键点是:它只存低位,不会替你做任何扩展,常用于写字节数组和标志位。示例:sb t0, 0(a0)。
SH 的用途是把寄存器低 16 位写回内存。关键点是:它适合半字数组、结构体字段和小整数存储。示例:sh t0, 2(a0)。
SW 的用途是把寄存器低 32 位写回内存。关键点是:在 RV64I 上它仍然只写 32 位,和 SD 不同;读回时是否符号扩展取决于对应的 load 指令。示例:sw t0, 4(a0)。
LD 是 RV64I 新增的 64 位加载指令。关键点是:它一次读 64 位,不做额外扩展,最适合读指针、64 位计数器和 64 位数据结构字段。示例:ld t0, 8(a0)。
LWU 是 RV64I 新增的 32 位无符号加载指令。关键点是:它把 32 位值零扩展到 64 位,因此它和 LW 的差别正好在“是否保留符号位”。示例:lwu t0, 4(a0)。
SD 是 RV64I 新增的 64 位存储指令。关键点是:它把寄存器低 64 位完整写回内存,和 SW 的 32 位截断不同。示例:sd t0, 8(a0)。
5. 立即数算术与逻辑
这一组的共同点是:立即数大多按 12 位有符号数处理,先符号扩展到 XLEN,再参与运算。关键点是:很多人第一次学 RISC-V 会误以为立即数是“无符号小常量”,但实际上默认规则正好相反。
ADDI 的用途是给寄存器加一个小常量,或者做栈指针调整、地址偏移。关键点是:它忽略溢出,结果只取低 XLEN 位;addi rd, rs1, 0 也常被汇编器拿来实现 mv。示例:addi sp, sp, -16。
SLTI 的用途是有符号“小于立即数”比较,常用于范围判断和条件标记。关键点是:它把立即数符号扩展后按有符号数比较。示例:slti t0, t1, 0。
SLTIU 的用途是无符号“小于立即数”比较。关键点是:立即数依然先符号扩展,但比较时按无符号解释,所以 sltiu rd, rs1, 1 可以用来判断 rs1 是否为 0。示例:sltiu t0, t1, 1。
XORI 的用途是按位异或,常用于掩码翻转或位切换。关键点是:xori rd, rs1, -1 等价于把每一位取反,但这仍然是正式指令语义,不是伪指令。示例:xori t0, t0, 0xff。
ORI 的用途是按位或,常用于设置标志位或合并低位字段。关键点是:立即数会先符号扩展,所以做掩码时要确认高位不会被意外带入。示例:ori t0, t0, 1。
ANDI 的用途是按位与,常用于保留低位、截断标志或提取小字段。关键点是:它同样使用符号扩展后的立即数,所以大掩码要小心。示例:andi t0, t0, 0xff。
SLLI 的用途是逻辑左移,最常见于乘以 2 的幂、构造字段和位域拼接。关键点是:RV32I 里移位量只看立即数低 5 位,RV64I 里则看低 6 位。示例:slli t0, t0, 3。
SRLI 的用途是逻辑右移,适合无符号除以 2 的幂或提取高位字段。关键点是:高位补 0,不保留符号。示例:srli t0, t0, 4。
SRAI 的用途是算术右移,适合有符号除以 2 的幂。关键点是:高位会复制原始符号位,所以负数右移时仍保持负号。示例:srai t0, t0, 4。
6. 寄存器算术与逻辑
这一组和立即数版本的区别只是第二个操作数来自寄存器。关键点是:ADD/SUB/SLL/SRL/SRA 在 RV64I 上默认按 64 位做运算,而不是继续沿用 32 位语义。
ADD 的用途是寄存器加法。关键点是:溢出被忽略,结果只保留低 XLEN 位。示例:add t0, t1, t2。
SUB 的用途是寄存器减法。关键点是:它计算的是 rs1 - rs2,不是反过来。示例:sub t0, t1, t2。
SLL 的用途是寄存器指定的逻辑左移。关键点是:RV32I 只看 rs2 低 5 位,RV64I 只看低 6 位。示例:sll t0, t1, t2。
SLT 的用途是有符号比较两个寄存器。关键点是:如果 rs1 < rs2,结果为 1,否则为 0。示例:slt t0, t1, t2。
SLTU 的用途是无符号比较两个寄存器。关键点是:它非常适合地址、长度、计数器和位模式比较。示例:sltu t0, t1, t2。
XOR 的用途是按位异或两个寄存器。关键点是:它常用于翻转标志位、差异检测和简单加密/解密原语的底层步骤。示例:xor t0, t1, t2。
SRL 的用途是逻辑右移寄存器中的值。关键点是:高位补 0,不保留符号。示例:srl t0, t1, t2。
SRA 的用途是算术右移寄存器中的值。关键点是:负数右移时会补符号位,和 SRL 的语义不同。示例:sra t0, t1, t2。
OR 的用途是按位或两个寄存器。关键点是:它通常和掩码、标志组合、字段合并一起使用。示例:or t0, t1, t2。
AND 的用途是按位与两个寄存器。关键点是:它是最常见的字段提取和标志清理工具之一。示例:and t0, t1, t2。
7. FENCE、FENCE.I、ECALL、EBREAK
FENCE 的用途是对设备 I/O 和内存访问建立顺序关系。关键点是:它通过 predecessor/successor 位集描述哪些 I/O 输入输出和哪些内存读写要被排序,含义可以粗暴理解成“后继集合里的访问,不能被其他 hart 或外部设备观察成先于前驱集合里的访问”;它不处理指令取指一致性,如果你要同步自修改代码或 JIT 生成代码,靠的是 FENCE.I,不是 FENCE。示例:fence rw, rw。
FENCE.I 的用途是同步本 hart 的指令流和数据流。关键点是:它保证同一个 hart 上,FENCE.I 之前已经可见的数据写入,会被后续取指看到;换句话说,它把“显式内存访问在前,之后的 instruction fetch 在后”这个顺序钉住了。它不自动解决多 hart 的全局取指一致性问题,因此常见于自修改代码、JIT 或代码补丁场景。示例:fence.i。
ECALL 的用途是向执行环境发起精确的环境调用。关键点是:它是一个陷入点,不是普通函数调用;参数怎么传由执行环境和 ABI 约定决定。示例:ecall。
EBREAK 的用途是向调试环境发起精确断点陷入。关键点是:它通常用于调试器停住程序,也常被编译器或运行时拿来表示“这里不应该执行到”。示例:ebreak。
8. RV64I 的 W 类指令
RV64I 的一个核心思想是:真实程序里经常要处理 32 位值,而这些值在 64 位寄存器中通常保持“低 32 位有效、并且符号扩展到 64 位”的不变式。W 类指令就是为这个场景准备的。
ADDIW 的用途是对 32 位值做立即数加法并得到 32 位结果,再符号扩展到 64 位。关键点是:它忽略高 32 位,addiw rd, rs1, 0 还能实现“把低 32 位符号扩展到 64 位”。示例:addiw t0, t1, 1。
SLLIW 的用途是对 32 位值做立即数左移。关键点是:它只看低 32 位,结果再符号扩展到 64 位;在 RV64I 中这类 *W 立即数移位的 imm[5] 不是普通可自由使用位,非零时是保留编码。示例:slliw t0, t1, 3。
SRLIW 的用途是对 32 位值做逻辑右移。关键点是:它先在低 32 位范围内做逻辑右移并在 32 位内部高位补 0,再把得到的 32 位结果按 bit 31 做符号扩展写回 64 位寄存器。示例:srliw t0, t1, 3。
SRAIW 的用途是对 32 位值做算术右移。关键点是:它保留 32 位符号位,再把结果符号扩展到 64 位。示例:sraiw t0, t1, 3。
ADDW 的用途是做 32 位加法并返回 64 位符号扩展结果。关键点是:它和 ADD 很像,但语义明确限定为“只看低 32 位”;这是很多 32 位整数运算在 RV64 上保持正确语义的关键。示例:addw t0, t1, t2。
SUBW 的用途是做 32 位减法并返回 64 位符号扩展结果。关键点是:和 ADDW 一样,溢出按模 2^32 忽略。示例:subw t0, t1, t2。
SLLW 的用途是对 32 位值做寄存器指定左移。关键点是:移位量只看 rs2[4:0],结果再符号扩展到 64 位。示例:sllw t0, t1, t2。
SRLW 的用途是对 32 位值做寄存器指定逻辑右移。关键点是:它和 SRL 的区别在于操作宽度固定为 32 位。示例:srlw t0, t1, t2。
SRAW 的用途是对 32 位值做寄存器指定算术右移。关键点是:它保留 32 位符号位,然后符号扩展到 64 位。示例:sraw t0, t1, t2。
9. Zicsr:CSR 读写指令
Zicsr 把 CSR 读写从基础整数指令中单独拆了出来。关键点是:CSR 指令对单个 CSR 做原子读-改-写,CSR 编号放在指令的 csr 字段里,而立即数形式把 rs1 字段解释成 5 位零扩展的 uimm。
CSRRW 的用途是“交换”:先把 CSR 的旧值读到 rd,再把 rs1 的值写入 CSR。关键点是:如果 rd=x0,就不读 CSR,也不会触发任何读副作用;如果 rs1=x0,就是往 CSR 写 0。示例:csrrw t0, somecsr, t1。
CSRRS 的用途是读 CSR 并把 rs1 指定的位集合到 CSR 中。关键点是:它只对 rs1 中为 1 的位做置位;如果 rs1=x0,就不会写 CSR,只相当于读,因此读取只读 CSR 时也常用这种形式。示例:csrrs t0, somecsr, t1。
CSRRC 的用途是读 CSR 并把 rs1 指定的位从 CSR 中清掉。关键点是:它和 CSRRS 对称;rs1=x0 时同样不会写,因此也会退化成只读访问。示例:csrrc t0, somecsr, t1。
CSRRWI 的用途是 CSRRW 的立即数版。关键点是:uimm 是 5 位零扩展立即数,rd=x0 时不读 CSR;它适合写入小型控制位组合。示例:csrrwi t0, somecsr, 3。
CSRRSI 的用途是 CSRRS 的立即数版。关键点是:uimm=0 时不会写 CSR,但仍然会读 CSR;这使它可以安全地退化成只读访问,也不会因为目标 CSR 是只读而触发写相关异常。示例:csrrsi t0, somecsr, 1。
CSRRCI 的用途是 CSRRC 的立即数版。关键点是:uimm=0 时同样不会写 CSR,但仍然会读 CSR;它常用于清除某个小位集合,也能安全退化成只读访问。示例:csrrci t0, somecsr, 1。
学习顺序建议与易错点
最稳妥的学习顺序是:先记寄存器和 ABI 名字,再记 U/J/B/I/R/S 六种格式,随后优先掌握 LUI、AUIPC、JAL、JALR、六条分支、ADDI、LW/SW、FENCE、ECALL/EBREAK,最后再补 RV64I 的 W 类和 Zicsr。这样你会先掌握“数据怎么走”和“控制流怎么跳”,再处理更细的系统能力。
最容易出错的点有这几类:
JAL和JALR写回的是pc+4,不是跳转目标地址- 分支和跳转的偏移量按 2 字节计,不是按 1 字节计
LW在RV64I上会符号扩展,LWU才是零扩展SLLI/SRLI/SRAI在RV64I的移位量宽度和RV32I不同SLTIU先把立即数符号扩展,再按无符号比较FENCE不是FENCE.ICSRRW只有在rd!=x0时才读 CSR;CSRRS/CSRRC只有在rs1字段不是x0时才会执行 CSR 写回,不能把“寄存器当前值为 0”误当成“rs1=x0”;CSRRSI/CSRRCI的uimm=0会退化成只读- 伪指令只是汇编器方便写法,不要和正式 ISA 指令混为一谈
完整检索表
下面这张表把本文范围内的全部正式指令放进一张总表里。RV64I 中“沿用但语义受 XLEN 影响”的条目,我也单独在备注里标出来,方便和“RV64I 新增指令”区分。
| 指令 | 类别 | 格式 | 作用 | 典型场景/备注 |
|---|---|---|---|---|
LUI | 高位立即数 | U | 装载高 20 位立即数 | 常量构造;RV64I 中结果符号扩展到 64 位 |
AUIPC | PC 相关 | U | pc 加高位立即数偏移 | 位置无关代码、长距离取址 |
JAL | 无条件跳转 | J | 跳转并写回返回地址 | 偏移按 2 字节计,范围约 ±1 MiB |
JALR | 间接跳转 | I | rs1+imm 间接跳转并写回返回地址 | bit0 清零;立即数不按 2 字节缩放 |
BEQ | 条件分支 | B | 相等则跳转 | 零判断、循环退出 |
BNE | 条件分支 | B | 不等则跳转 | 循环继续、重试 |
BLT | 条件分支 | B | 有符号小于则跳转 | 负数参与比较 |
BGE | 条件分支 | B | 有符号大于等于则跳转 | 与 BLT 配对 |
BLTU | 条件分支 | B | 无符号小于则跳转 | 地址、长度、数组边界 |
BGEU | 条件分支 | B | 无符号大于等于则跳转 | 与 BLTU 配对 |
LB | Load | I | 读 8 位并符号扩展 | 有符号字节 |
LH | Load | I | 读 16 位并符号扩展 | 有符号半字 |
LW | Load | I | 读 32 位并符号扩展 | RV64I 上仍是 32 位读,但结果扩展到 64 位 |
LBU | Load | I | 读 8 位并零扩展 | 标志位、字节数组 |
LHU | Load | I | 读 16 位并零扩展 | 无符号半字 |
SB | Store | S | 存低 8 位 | 字节写入 |
SH | Store | S | 存低 16 位 | 半字写入 |
SW | Store | S | 存低 32 位 | RV64I 中仍只写 32 位 |
ADDI | 立即数算术 | I | 加符号扩展 12 位立即数 | 栈调整、地址偏移 |
SLTI | 立即数比较 | I | 有符号小于比较 | 范围判断 |
SLTIU | 立即数比较 | I | 无符号小于比较 | imm=1 常用作零测试 |
XORI | 立即数逻辑 | I | 按位异或 | 位翻转、取反技巧 |
ORI | 立即数逻辑 | I | 按位或 | 置位、合并标志 |
ANDI | 立即数逻辑 | I | 按位与 | 掩码截取 |
SLLI | 立即数移位 | I | 逻辑左移 | RV32I 取 5 位移位量;RV64I 沿用同名指令但取 6 位 |
SRLI | 立即数移位 | I | 逻辑右移 | RV32I 取 5 位移位量;RV64I 沿用同名指令但取 6 位 |
SRAI | 立即数移位 | I | 算术右移 | RV32I 取 5 位移位量;RV64I 沿用同名指令但取 6 位 |
ADD | 寄存器算术 | R | 寄存器加法 | 忽略溢出;RV64I 默认做 64 位加法 |
SUB | 寄存器算术 | R | 寄存器减法 | 忽略溢出;RV64I 默认做 64 位减法 |
SLL | 寄存器移位 | R | 寄存器指定左移 | RV32I 看 rs2[4:0];RV64I 看 rs2[5:0] |
SLT | 寄存器比较 | R | 有符号小于比较 | 条件生成 |
SLTU | 寄存器比较 | R | 无符号小于比较 | 地址、长度比较 |
XOR | 寄存器逻辑 | R | 按位异或 | 差异检测 |
SRL | 寄存器移位 | R | 逻辑右移 | RV64I 看 rs2[5:0] |
SRA | 寄存器移位 | R | 算术右移 | RV64I 看 rs2[5:0] |
OR | 寄存器逻辑 | R | 按位或 | 置位、字段合并 |
AND | 寄存器逻辑 | R | 按位与 | 掩码、清位 |
FENCE | 内存顺序 | I | 约束 I/O 与内存访问顺序 | 通过 predecessor/successor 集合描述排序关系 |
ECALL | 系统入口 | I | 发起环境调用陷入 | 系统调用、运行时服务 |
EBREAK | 调试入口 | I | 发起断点陷入 | 调试器、不可达路径 |
LWU | RV64I 新增 Load | I | 读 32 位并零扩展到 64 位 | 和 LW 的区别在最终扩展方式 |
LD | RV64I 新增 Load | I | 读 64 位 | 指针、64 位整数、64 位字段 |
SD | RV64I 新增 Store | S | 存 64 位 | 64 位数据结构写回 |
ADDIW | RV64I 新增算术 | I | 32 位加立即数并符号扩展 | 保持 32 位结果语义;addiw rd, rs1, 0 可做 sext.w |
SLLIW | RV64I 新增移位 | I | 32 位左移并符号扩展 | imm[5] != 0 为保留编码 |
SRLIW | RV64I 新增移位 | I | 32 位逻辑右移并符号扩展 | 逻辑右移发生在低 32 位内,之后统一符号扩展 |
SRAIW | RV64I 新增移位 | I | 32 位算术右移并符号扩展 | imm[5] != 0 为保留编码 |
ADDW | RV64I 新增算术 | R | 32 位加法并符号扩展 | 低 32 位结果写回后扩展到 64 位 |
SUBW | RV64I 新增算术 | R | 32 位减法并符号扩展 | 低 32 位结果写回后扩展到 64 位 |
SLLW | RV64I 新增移位 | R | 32 位左移并符号扩展 | 移位量来自 rs2[4:0] |
SRLW | RV64I 新增移位 | R | 32 位逻辑右移并符号扩展 | 移位量来自 rs2[4:0] |
SRAW | RV64I 新增移位 | R | 32 位算术右移并符号扩展 | 移位量来自 rs2[4:0] |
FENCE.I | Zifencei | I | 同步数据写入与后续取指 | 自修改代码、JIT、代码补丁;只保证本 hart |
CSRRW | Zicsr CSR 读写 | I | 读旧值并写新值 | rd=x0 时不读 CSR |
CSRRS | Zicsr CSR 读写 | I | 读旧值并按位集合 | rs1=x0 时退化成只读 |
CSRRC | Zicsr CSR 读写 | I | 读旧值并按位清除 | rs1=x0 时退化成只读 |
CSRRWI | Zicsr CSR 立即数 | I | 立即数版读写交换 | uimm 为 5 位零扩展;rd=x0 时不读 |
CSRRSI | Zicsr CSR 立即数 | I | 立即数版按位置位 | uimm=0 时不写但仍读 |
CSRRCI | Zicsr CSR 立即数 | I | 立即数版按位清除 | uimm=0 时不写但仍读 |
参考入口
如果你想回到原始权威文本,最该保存的入口就是这一个:<https://docs.riscv.org/reference/isa/unpriv/unpriv-index.html>。从那里进入 RV32I、RV64I、Zifencei、Zicsr 四个章节,基本就能把本文所有内容一一对上。
京公网安备11011202100605号