reference: <riscv-spec.pdf>
1.RV32I指令集格式
| 目的 | 类型 |
|---|---|
| 用于寄存器-寄存器操作 | R 类型指令 |
| 用于短立即数和访存 load 操作 | I 型指令 |
| 用于访存 store 操作 | S 型指令 |
| 用于条件跳转操作 | B 类型指令 |
| 用于长立即数 | U 型指令 |
| 用于无条件跳转 | J 型指令 |
??以下是分支指令(B 类型)立即数字段 S 旋转基于类型 1 位。跳转指令(J类型)的直接字段在 U 旋转基于类型 12 位。因此,RISC-V 事实上,只有四种基本格式,但我们可以保守地认为它有六种格式。 ?? B和 J 格式的分支和跳转地址必须向左移动 1 地址乘以 2.从而给分支和跳转指令更大的跳转范围。 RISC-V 将立即数中的位置从自然排列进行一些移位轮换,将指令信号的风扇和立即数多路复用的成本降低了近两倍, 这也简化了低端实现中的数据通路逻辑。
RISC-V指令有几个特点:
- 指令只有六种格式,所有指令都是 32 位长简化了指令解码.
- RISC-V 指令提供三个寄存器操作数,而不是像 x86-32 同样,让源操作数和目的操作数共享一个字段.
- 在 RISC-V 对于所有指令,要读写的寄存器标识符总是在同一位置,这意味着您可以在解码指令之前访问寄存器.许多其他的 ISA 在某些指令中,一些指令字段被重用为源目的地, 以其他指令为目的操作数(例如, ARM-32 和 MIPS-32) 。因此,为了取出正确的指令字段,我们需要在紧张的解码路径上添加额外的解码逻辑,使解码路径的顺序更加紧张.
- 这些格式的即时数字段总是符号扩展,符号位总是指令中最高的。这意味着可能成为关键路径的即时数字符号扩展可以在指令解码前进行.
2.RV32I指令集清单
??RV32I 指令如下图所示。 从左到右连接带下划线的字母形成 RV32I 指令。花括号{}表示集合中垂直方向上的每同变体。集合下划线_这意味着不包含字母的也是指令名称。例如,左上角附近的符号表示以下六个指令: and, or, xor, andi, ori, xori。 ??可见可分为四类:(设计多优雅!
3.汇编相关寄存器
??下表为寄存器x和寄存器f的汇编助记符,以及 RISC-V 调用协议,各种指针(sp/gp/tp/fp),返回地址(ra),保存寄存器(s0-s11)和临时寄存器(t0-t6).统一使用程序二进制接口(ABI)定义的寄存器名称使其更容易阅读:
4.RV32I相关操作
4.1 RV32I的整数操作
??算术指令简单(add, sub)、逻辑指令(and, or, xor),移位指令(sll, srl, sra) 和其他 ISA 差不多。他们从寄存器读取两个 32 并将 32 将位结果写入目标寄存器。 RV32I 还提供了这些指令的即时版本。
??为了应对这种使用场景,程序可以根据比较结果生成布尔值, RV32I 提供一个"小于时位"的指令。若第一个操作数小于第二个操作数,则将目标寄存器设置为 1,否则为0。不出所料,对这个指令,有一个有符号版本(slt)和无符号版本(sltu),用于处理有符号和无符号的整数比较。因此,上述两个指令也有即时版本(slti, sltiu)。就像我们要看到的,虽然 RV32I 分支指令可以检查两个寄存器之间的所有关系,但有些条件表达式涉及多个寄存器之间的关系。编译器或汇编语言程序员可以使用这些表达式slt 并结合或不同或其他逻辑指令来解决更复杂的条件表达。
??lui和auipc这两个整数计算指令主要用于构建大常量值和链接。 立即加载到高位(lui)将 20 高位常量加载到寄存器 20 位。 然后可以使用标准的立即指令来创建 32位常量。这样子, 仅使用 2 条 32 位 RV32I 指令,便可构造一个 32 位常量。向 PC 高位加立即数(auipc) 让我们只使用两个指令, 以现在为基础 PC 控制流或访问数据以任意偏移量转移。将 auipc 中的 20 位立即数与 jalr 中 12 我们可以将执行转移到任何位立即数的组合中 32 位 PC 相对地址。 而 auipc 加上普通加载或存储指令 12位立即数偏移12位立即数偏移量 32 位 PC 相对地址的数据。
4.2 RV32I的Load和Store操作
??除了提供 32 位字(lw, sw)除加载和储存外,RV32I 支持加载符号、无符号字节和半字符(lb, lbu, lh, lhu)存储字节和半字(sb, sh)。符号字节和半字符号扩展 32 然后写入目的寄存器。即使自然数据类型较窄,低宽数据也会在扩展后进行处理,这使得后续的整数计算指令能够正确处理所有问题 32 位。无符号字节和半字,常用于文本和无符号整数,在写入目标寄存器之前被无符号扩展到 32 位。唯一的加载和存储支持搜索模式是符号扩展 12 立即数到基地址寄存器.
4.3 RV32I分支跳转的控制转移条件
??RV32I 两个寄存器可以比较,分支可以根据比较结果跳转。比较可以是: 相等(beq),不相等 (bne),大于等于(bge),或小于(blt) 。 最后两种比较符号比较, RV32I 还提供相应的无符号版本比较: bgeu 和 bltu。通过简单地交换两个操作数,可以完成剩余的两过简单地交换两个操作数来完成比较。 因为 x < y 表示 y > x 且 x ≥ y表示 y ≤ x。 ??由于 RISC-V 指令长度必须是两个字节的倍数——可选的双字节指令 ,请参考压缩指令-分支指令的寻址方式 12 立即数乘以位 2, 扩展符号,然后增加值PC 作为分支的跳转地址。 PC 可用于位置无关的代码, 简化了链接器和加载器的工作 。
4.4 RV32I无条件跳转控制转移
??跳转并链接指令(jal)具有双重功能。若下一个指令 PC 4 在目标寄存器中保存地址通常是返回地址寄存器 ra,可用于实现过程调用。若使用零寄存器(x0) 替换 ra 作为目标寄存器,可以无条件跳转,因为 x0 不能更改。和分支一样, jal 将其 20 分支地址乘以 2.符号扩展后添加 PC 上,便得到了跳转地址。 ??寄存器版本的跳转和链接指令(jalr)它也是多用途的。它可以调用动态计算的地址函数,也可以调用返回(只需要 ra 作为寄存器,零寄存器(x0)作为目的寄存器)。 Switch 和 case 语句的地址跳转,也可以使用 jalr 指令,目的寄存器设为 x0。
4.5 RV32I的杂项操作
控制状态寄存器指令 (csrrc、 csrrs、 csrrw、 csrrci、 csrrsi、 csrrwi),使我们可以轻松地访问一些程序性能计数器。 对于这些 64 位计数器, 我们一次可以读取 32位。这些计数器包括了系统时间, 时钟周期以及执行的指令数目。
在 RISC-V 指令集中, ecall 指令用于向运行时环境发出请求,例如系统调用。调试器使用 ebreak 指令将控制转移到调试环境。
fence 指令对外部可见的访存请求,如设备 I/O 和内存访问等进行串行化。外部可见指对处理器的其他核心、线程,外部设备或协处理器可见。 fence.i 指令同步指令和数据流。在执行 fence.i 指令之前,对于同一个硬件线程, RISC-V 不保证用存储指令写到内存指令区的数据可以被取指令取到。