RISC-V指令集中最基本的部分是RV32I,每个RISC-V CPU都要实现这一指令集(当然嵌入式设备可以实现更为精减的RV32E)。

RV32I中共有47条指令,其中SCALLFENCE等可以在阉割了部分功能的条件下通过某些方式用其他指令替代,故真正”最少需要实现”的指令条数是38条。

寻址方式

RISC-V在运算指令中只支持寄存器寻址和立即数,所有和内存的交互都通过loadstore族指令进行。比x86一堆怪异而且根本记不住的寻址方式不知道高到哪里去了。

寄存器

RV32I要求要有:

  • 1个32位零寄存器x0/zero,这个寄存器直接与硬件的0相连。

  • 31个32位通用寄存器x1-x31,和x86的表面通用是不同的,这些寄存器是真正的”通用”寄存器,往里塞啥都可以,用起来也都没有差别,虽然如果遵循标准的调用约定的话,他们还是有各自的用途和名字。
    屏幕快照 2019-06-14 上午1.06.49

  • 1个程序计数器PC,RISC-V的PC指向的是当前执行的指令的地址。

指令格式

屏幕快照 2019-06-14 上午1.22.43

如图,RV32I主要有

  • R(代表register
  • I(代表imm
  • S(代表store
  • U(代表upper

四种指令格式,而根据指令中立即数各个位的布局,又有另外两种指令格式

  • B(代表branchS的变体,虽然和store完全没关系)
  • J(代表jumpU的变体,同样和upper没啥关系)

其中各个指令格式形成的立即数格式如下:

屏幕快照 2019-06-14 上午2.36.53

运算指令

共有19条运算指令,实现完了这些指令集就完成了一半了。

所有运算使用指令func3字段来标记运算的类型,有时会用funct7的左起第6位来进一步指定操作类型(如加/减,移位是算术移位还是逻辑移位)。

func3 助记符 作用
000 ADD/SUB 加减法
001 SL 左移
010 SLT 若带符号比较结果是<,则设置目标寄存器为1
011 SLTU 若无符号比较结果是<,则设置目标寄存器为1
100 XOR 异或
101 SR 右移
110 OR
111 AND

寄存器-立即数运算

这类运算用一个I型指令表示,其opcode0010011

屏幕快照 2019-06-14 上午2.35.03

屏幕快照 2019-06-14 上午2.40.37

移位指令中L代表逻辑移位,A代表算术移位,shamt代表要移动的位数。

寄存器-寄存器运算

这类运算用一个R型指令表示,其opcode0110011

屏幕快照 2019-06-14 上午11.52.33

注意在移位指令中rs2只有低5位有效。

LUI

这是一个U型指令,opcode0110111,这个指令主要用于将32位数的第31-第12位送入寄存器。与ADDI连用能达到将32位立即数送入寄存器的目的,在后面的跳转指令中也用于构造长距离跳转。

屏幕快照 2019-06-14 下午12.27.24

控制转移(跳转)指令

RISC-V中的跳转都是相对跳转。

无条件跳转

JAL

这是一个J型指令,opcode1101111,这个指令将当前pc+4的值存入指定寄存器(可用来进行函数调用),并进行跳转,跳转范围为(向前或向后)1MiB内。

屏幕快照 2019-06-14 下午1.14.20

注意此处的offset没有第0位,这是由于每个指令都是32位的,代码区不可能出现offset第0位为1的情况。

JALR

这是一个I型指令,opcode1100111,和JAL唯一不同之处在于JALR的目标地址是由(imm + rs1) & 0xffff_fffe1的结果。

屏幕快照 2019-06-14 下午1.28.47

其中rs1的值常常是由LUI加载的。

有条件跳转

所有有条件跳转指令都是B型指令,opcode1100011,跳转范围是向前或向后4KiB

屏幕快照 2019-06-14 下午1.39.17

funct3 助记符 作用
000 BEQ rs1 == rs2,则跳转
001 BNE rs1 != rs2,则跳转
100 BLT 带符号比较,rs1 < rs2,则跳转
101 BGE 带符号比较,rs1 > rs2,则跳转
110 BLTU 无符号比较,rs1 < rs2,则跳转
111 BGEU 无符号比较,rs1 > rs2,则跳转

AUIPC

这是一个U型指令,opcode0010111,作用是:rd = pc + imm

屏幕快照 2019-06-14 下午3.49.45的副本

这个指令和JALR结合,可以进行32位的相对跳转,可以用来构建PIC2

内存存取指令

LOAD

这是一组I型指令,opcode0100011,用途是将内存中rs1+imm位置的数据加载到rd中。

屏幕快照 2019-06-14 下午4.11.45

其中funct3指定了要加载的数据的长度:

funct3 助记符 数据长度
000 LB 8位,放入寄存器时符号扩展到32位
001 LH 16位,放入寄存器时符号扩展到32位
010 LW 32位
100 LBU 8位,放入寄存器时0扩展到32位
101 LHU 16位,放入寄存器时0扩展到32位

STORE

这是一组S型指令,opcode0000011,用途是将rs2中的数据放入内存中的rs1+imm位置。

屏幕快照 2019-06-14 下午4.31.54

其中funct3指定了要存入的数据的长度:

funct3 助记符 数据长度(不足32位时均取低位)
000 SB 8位
001 SH 16位
010 SW 32位

控制和状态寄存器相关指令

(暂时不需要,略)

环境(系统)调用和断点指令

(暂时不需要,略)

1. & 0xffff_fffe即将最后一位置0
2. Position Independent Code,位置无关代码