3.2 RISC-V指令编码格式

RISC-V每条指令的宽度为32位(不考虑压缩扩展指令),包括RV32指令集以及RV64指令集。指令编码格式大致可分成如下6类。

R类型:寄存器与寄存器算术指令。

I类型:寄存器与立即数算术指令或者加载指令。

S类型:存储指令。

B类型:条件跳转指令。

U类型:长立即数操作指令。

J类型:无条件跳转指令。

RISC-V指令集编码格式如图3.1所示。

图3.1 RISC-V指令集编码格式

指令编码可以分成如下几个部分。

opcode(操作码)字段:位于指令编码Bit[6:0],用于指令的分类。

funct3和funct7(功能码)字段:常常与opcode字段结合在一起使用,用来定义指令的操作功能。

rd字段:表示目标寄存器的编号,位于指令编码的Bit[11:7]。

rs1字段:表示第一源操作寄存器的编号,位于指令编码的Bit[19:15]。

rs2字段:表示第二源操作寄存器的编号,位于指令编码的Bit[24:20]。

imm:表示立即数。RISC-V中使用的立即数大部分是符号扩展(sign-extended)的立即数。

RV64指令集支持64位宽的数据和地址寻址,为什么指令的编码宽度却只有32位[1]


[1]RISC-V通常使用32位定长指令,不过为了减少代码量,也支持16位的压缩扩展指令。

因为RV64指令集是基于寄存器加载和存储的体系结构设计,所以所有的数据加载、存储以及处理都是在通用寄存器中完成的。RISC-V一共有32个通用寄存器(通用寄存器都是64位宽的,可以处理64位宽的数据),即x0~x31,其中,x0寄存器的编号为0,以此类推。因此,指令编码使用5位宽(25 = 32),即索引32个通用寄存器。

对于精简指令集来说,大部分运算指令最多采用3个寄存器来表示,例如加法指令add rd, rs1, rs2,那么在指令编码中只需要15个位宽就可以索引3个通用寄存器,剩余的位宽可用于指令分类等。因此32位宽的指令编码已经绰绰有余,使用32位宽指令编码比使用64位宽指令编码更有利于提高指令密度,减小代码尺寸,从而提高指令高速缓存的命中率,提升程序执行效率。

加载和存储指令通常采用12位立即数加上2个通用寄存器的方式来表示,下面以lw加载指令为例说明其指令编码的布局,如图3.2所示。

图3.2 lw加载指令的编码

第0~6位为opcode字段,用于指令的分类。

第7~11位为rd字段,用来描述目标寄存器rd的编号,可以从x0~x31通用寄存器中选择。

第12~14位为功能码字段,在加载指令中表示加载数据的位宽。

第15~19位为基地址rs1,可以从x0~x31通用寄存器中选择。

第20~31位为offset字段,表示偏移量。

RV64指令集中常用的符号说明如下。

rd:表示目标寄存器的编号,可以从x0~x31通用寄存器中选择。

rs1:表示源寄存器1的编号,可以从x0~x31通用寄存器中选择。

rs2:表示源寄存器2的编号,可以从x0~x31通用寄存器中选择。

():通常用来表示寻址模式,例如,(a0)表示以a0寄存器的值为基地址进行寻址。这个前面还可以加offset,表示偏移量,可以是正数或负数。例如,8(a0)表示以a0寄存器的值为基地址,偏移8字节进行寻址。

{ }:表示可选项。

imm:表示有符号立即数。