RISC-V 指令集分析

2022/05/04 RISC-V CA 共 8855 字,约 26 分钟
硅星球

RISC-V 指令集分析

背景

RISC-V指令集作为当前最新最流行的开源指令集,吸收了之前出现的指令集的优点,并极力避免了一些不足,创造性地使用了模块化指令集的概念,使得基于RISC-V指令集设计的芯片既可以用于极低功耗的嵌入式环境,也可用于高性能高带宽的业务场景,所以RISC-V具有广阔的应用市场和发展前景。本项目希望设计一个基于RV64I指令集的CPU,加强对于RISC-V的认知和理解。RV64I是64位架构的整数指令集,其是整个RISC-V指令集的一个子集,也是RV32I指令集的超集,也就是说RV64I是在RV32I的基础上发展而来,在后面具体的指令集分析时候,也可以看出来。设计CPU的第一步就是对于指令集的分析,本篇文章就是对于RV64指令集的一个简单分析。

RV64I

首先需要明确一点,RV64I和RV32I的指令宽度都是32bit。RV64I共有64条指令,包括59条基本指令,5条特权指令。59条基本指令中包括47条从RV32I指令发展而来的基本指令,这些指令大部分RV64I和RV32I通用,有小部分有细微差别;还包括12条RV64I独有的指令。总的指令分类有R型、I型、S型、B型、U型、J型六种。下图是从RISC-V unprivilege spec中截取的指令分类图。

具体的指令分析如下所示。

注意 sext即符号位扩展;shamt即shift amount即移位数量;u代表视为无符号数,s代表视为有符号数;tmp都是32位的

R型指令

R型指令的格式如下所示:

31~2524~2019~1514~1211~76~0
func7rs2rs1func3rdopcode

具体指令格式则如下所示:

RV32I & RV64I

指令func7rs2rs1func3rdopcode操作
add0000000rs2rs1000rd0110011加法,x[rd]=x[rs1] + x[rs2],忽略算术溢出
sub0100000rs2rs1000rd0110011减法,x[rd]=x[rs1] - x[rs2],忽略算术溢出
sll0000000rs2rs1001rd0110011逻辑左移,x[rd]=x[rs1] « x[rs2],低位填0,RV64I中rs2低6位代表移位个数,忽略高位
srl0000000rs2rs1101rd0110011逻辑右移,x[rd]=x[rs1] » u x[rs2],高位填0,RV64I中rs2低6位代表移位个数,忽略高位
sra0100000rs2rs1101rd0110011算术右移,x[rd]=x[rs1] » s x[rs2],高位填rs1最高位,RV64I中rs2低6位代表移位个数,忽略高位
slt0000000rs2rs1010rd0110011有符号小于置位,若x[rs1] < s x[rs2],x[rd]为1,否则为0
sltu0000000rs2rs1011rd0110011无符号小于置位,若x[rs1] < u x[rs2],x[rd]为1,否则为0
and0000000rs2rs1111rd0110011x[rd]=x[rs1] & x[rs2],位与
or0000000rs2rs1110rd0110011x[rd]=x[rs1] | x[rs2],位或
xor0000000rs2rs1100rd0110011x[rd]=x[rs1] ^ x[rs2],位异或

RV64I

以下指令得到的结果都是32bit的,将32bit符号扩展之后再写入64位的xd中

指令func7rs2rs1func3rdopcode操作
addw0000000rs2rs1000rd0111011tmp=rs1[31:0]+rs2[31:0], rd=sext(tmp[31:0]);先截取rs1和rs2低32位,将其和符号扩展写入xd
subw0100000rs2rs1000rd0111011tmp=rs1[31:0]-rs2[31:0], rd=sext(tmp[31:0]);先截断rs1和rs2低32位,将其差符号扩展写入xd
sllw0000000rs2rs1001rd0111011tmp=rs1[31:0] « rs2[4:0], rd=sext(tmp[31:0]);rs1低32bit逻辑左移,低位填0,rs2低5位为移动位数
srlw0000000rs2rs1101rd0111011tmp=rs1[31:0] » u rs2[4:0], rd=sext(tmp[31:0]);rs1低32bit逻辑右移,高位填0,rs2低5位为移动位数
sraw0100000rs2rs1101rd0111011tmp=rs1[31:0] « s rs2[4:0], rd=sext(tmp[31:0]);rs1低32bit算术右移,高位填rs1[31],rs2低5位为移动位数

U型指令

属于RV31I和RV64I的U型指令只有两条,其格式如下所示:

31~1211~76~0
imm[19:0]rdopcode

具体指令格式则如下所示:

RV32I & RV64I

指令imm[19:0]rdopcode操作
luiimm[19:0]rd0110111x[rd]=sext(imm[19:0] « 12),20bit立即数逻辑左移12位,低位填0,符号扩展之后写入rd
auipcimm[19:0]rd0010111x[rd]=pc+sext(imm[19:0] « 12),20bit立即数逻辑左移12位,低位填0,符号扩展到64位,然后加上pc写入rd

J型指令

属于RV31I和RV64I的J型指令只有一条,其格式如下所示:

3130~212019~1211~76~0
imm[20]imm[10:1]imm[11]imm[19:12]rdopcode

具体指令格式则如下所示:

RV32I & RV64I

指令imm[20]imm[10:1]imm[11]imm[19:12]rdopcode操作
jalimm[20]imm[10:1]imm[11]imm[19:12]rd1101111x[rd]=pc+4, pc+=sext(imm20); 跳转到立即数加pc的地址,并将pc+4数值存入rd,rd默认为x1,imm20符号扩展到64位

jal指令需要注意偏移量是带符号扩展的,并且偏移量是2字节对齐的(imm[20:1]),虽然RV32I和RV64I中所有指令地址都是4字节对齐的,但是jal指令还可能被用到用于兼容C扩展指令集,所以就默认imm[0]位为0,即2字节对齐。因此jal跳转的地址范围有+/-1MB的范围(2^21=2MB=+/-1MB)。

S型指令

S型指令格式如下所示:

31~2524~2019~1514~1211~76~0
imm[11:5]rs2rs1func3imm[4:0]opcode

具体指令格式则如下所示:

RV32I & RV64I

指令imm[11:5]rs2rs1func3imm[4:0]opcode操作
sbimm[11:5]rs2rs1000imm[4:0]0100011M[x[rs1]+sext(imm)]=x[rs2][7:0],将rs2的低8位存入到内存
shimm[11:5]rs2rs1001imm[4:0]0100011M[x[rs1]+sext(imm)]=x[rs2][15:0],将rs2的低16位存入到内存
swimm[11:5]rs2rs1010imm[4:0]0100011M[x[rs1]+sext(imm)]=x[rs2][31:0],将rs2的低32位存入到内存

RV64I

指令imm[11:5]rs2rs1func3imm[4:0]opcode操作
sdimm[11:5]rs2rs1011imm[4:0]0100011M[x[rs1]+sext(imm)]=x[rs2][63:0],将rs2的64位存入到内存

B型指令

B型指令格式如下所示:

3130~2524~2019~1514~1211~876~0
imm[12]imm[10:5]rs2rs1func3imm[4:1]imm[11]opcode

B型指令同jal指令类似,偏移量都是2字节对齐,也就是意味着imm[0]默认为0。

具体指令格式则如下所示:

RV32I & RV64I

指令imm[12][10:5]rs2rs1func3[4:1][11]opcode操作
beqimm[12][10:5]rs2rs1000[4:1][11]1100011if (rs1 == rs2), pc += sext(imm)
bneimm[12][10:5]rs2rs1001[4:1][11]1100011if (rs1 != rs2), pc += sext(imm)
bltimm[12][10:5]rs2rs1100[4:1][11]1100011if (rs1 < rs2), pc += sext(imm),x[rs1]和x[rs2]为有符号数
bltuimm[12][10:5]rs2rs1110[4:1][11]1100011if (rs1 < rs2), pc += sext(imm),x[rs1]和x[rs2]为无符号数
bgeimm[12][10:5]rs2rs1101[4:1][11]1100011if (rs1 >= rs2), pc += sext(imm),x[rs1]和x[rs2]为有符号数
bgeuimm[12][10:5]rs2rs1111[4:1][11]1100011if (rs1 >= rs2), pc += sext(imm),x[rs1]和x[rs2]为无符号数

I型指令

I型指令格式如下:

31~2019~1514~1211~76~0
imm[11:0]rs1func3rdopcode

RV32I & RV64I

指令imm[11:0]rs1func3rdopcode操作
addiimm[11:0]rs1000rd0010011x[rd]=x[rs1] + sext(imm),忽略算术溢出
sltiimm[11:0]rs1010rd0010011x[rd]=x[rs1] < s sext(imm),x[rs1]小于符号位扩展的imm,rd置1,否则置0,两者都为有符号数
sltiuimm[11:0]rs1011rd0010011x[rd]=x[rs1] < u sext(imm),x[rs1]小于符号位扩展的imm,rd置1,否则置0,两者都为无符号数
andiimm[11:0]rs1111rd0010011x[rd]=x[rs1] & sext(imm),位与
oriimm[11:0]rs1110rd0010011x[rd]=x[rs1] | sext(imm),位或
xoriimm[11:0]rs1100rd0010011x[rd]=x[rs1] ^ sext(imm),位异或
slli{000000,shamt6}rs1001rd0010011x[rd]=x[rs1] « shamt6,逻辑左移,移位数为shamt6,视为无符号数;RV32I中移位数为shamt低5位
srli{000000,shamt6}rs1101rd0010011x[rd]=x[rs1] » u shamt6,逻辑右移,移位数为shamt6,视为无符号数;RV32I中移位数为shamt低5位
srai{010000,shamt6}rs1101rd0010011x[rd]=x[rs1] » s shamt6,算术右移,移位数为shamt6,视为无符号数;RV32I中移位数为shamt低5位
指令imm[11:0]rs1func3rdopcode操作
csrrwimm[11:0]rs1001rd1110011t=CSRs[imm]; CSRs[imm]=x[rs1]; x[rd]=t,imm[11:0]指明是哪个csr,将csr旧值0扩展写入到rd,rs1的值写入到csr
csrrsimm[11:0]rs1010rd1110011t=CSRs[imm]; CSRs[imm]=t|x[rs1]; x[rd]=t,imm[11:0]指明是哪个csr,将csr旧值0扩展写入到rd,rs1的值位或上csr旧值写入到csr
csrrcimm[11:0]rs1011rd1110011t=CSRs[imm]; CSRs[imm]=t&~x[rs1]; x[rd]=t,imm[11:0]指明是哪个csr,将csr旧值0扩展写入到rd,rs1的值先按位取反,再位与上csr旧值写入到csr
csrrwiimm[11:0]zimm[4:0]101rd1110011x[rd]=CSRs[imm]; CSRs[imm]=zimm,imm[11:0]指明是哪个csr,将csr旧值0扩展写入到rd,5bit的zimm0扩展写入到csr
csrrsiimm[11:0]zimm[4:0]110rd1110011t=CSRs[imm]; CSRs[imm]=t|zimm; x[rd]=t,imm[11:0]指明是哪个csr,将csr旧值0扩展写入到rd,5bit的zimm0扩展和csr位或写入到csr
csrrciimm[11:0]zimm[4:0]111rd1110011t=CSRs[imm]; CSRs[imm] =t&∼zimm; x[rd]=t,imm[11:0]指明是哪个csr,将csr旧值0扩展写入到rd,5bit的zimm0扩展先安慰取反,在和和csr位与写入到csr
指令imm[11:0]rs1func3rdopcode操作
lbimm[11:0]rs1000rd0000011x[rd]=sext(M[x[rs1]+sext(imm)][7:0]),从内存中读取一个字节,地址是rs1和imm符号扩展后的加和,字节符号扩展后写入rd
lhimm[11:0]rs1001rd0000011x[rd]=sext(M[x[rs1]+sext(imm)][15:0]),从内存中读取一个半字,地址是rs1和imm符号扩展后的加和,半字符号扩展后写入rd
lwimm[11:0]rs1010rd0000011x[rd]=sext(M[x[rs1]+sext(imm)][31:0]),从内存中读取一个字,地址是rs1和imm符号扩展后的加和,字符号扩展后写入rd
lbuimm[11:0]rs1100rd0000011x[rd]=M[x[rs1]+sext(imm)][7:0],从内存中读取一个字节,地址是rs1和imm符号扩展后的加和,字节0扩展后写入rd
lhuimm[11:0]rs1101rd0000011x[rd]=M[x[rs1]+sext(imm)][15:0],从内存中读取一个半字,地址是rs1和imm符号扩展后的加和,半字0扩展后写入rd
指令imm[11:0]rs1func3rdopcode操作
jalrimm[11:0]rs1000rd1100111t=pc+4, pc=(x[rs1]+sext(imm))&∼1, x[rd]=t; 将计算出的pc值最低有效位设为0,将pc+4值写入rd,rd默认x1

jalr指令是将rs1值与符号位扩展的imm[11:0]加起来,并将最后一位设为0(1取反并这个相加结果位与,就是将最后一位设为0)作为当前的pc值(最后一位为0是为了字节对齐),而(pc+4)的值写入到rd中。

指令31~2019~1514~1211~76~0操作
ecall00000000000000000000000001110011环境调用,通过引发环境调用异常来请求执行环境
ebreak00000000000100000000000001110011环境断点,通过抛出断点异常的方式请求调试器
指令31~2019~1514~1211~76~0操作
fence{0000,pred,succ}00000000000000001111 
fence.i00000000000000000001000000001111 

RV64I

指令imm[11:0]rs1func3rdopcode操作
addiwimm[11:0]rs1000rd0011011tmp=rs1[31:0]+sext(imm)[31:0], rd=sext(tmp); rs1低32位和imm符号扩展后的低32位相加,将和进行符号扩展写入xd,忽略算术溢出
slliw{0000000,shamt5}rs1001rd0011011tmp=(rs1[31:0] « shamt5)[31:0], rd=sext(tmp); rs1截断低32位进行逻辑左移再截断低32位,左移位数由5bit的shamt决定,二次截断后符号位扩展写入rd
srliw{0000000,shamt5}rs1101rd0011011tmp=(rs1[31:0] » shamt5)[31:0], rd=sext(tmp); rs1截断低32位进行逻辑右移再截断低32位,右移位数由5bit的shamt决定,二次截断后符号位扩展写入rd
sraiw{0100000,shamt5}rs1101rd0011011tmp=(rs1[31:0] » s shamt5)[31:0], rd=sext(tmp); rs1截断低32位进行算术右移再截断低32位,右移位数由5bit的shamt决定,二次截断后符号位扩展写入rd
指令imm[11:0]rs1func3rdopcode操作
lwuimm[11:0]rs1110rd0000011x[rd]=M[x[rs1]+sext(imm)][31:0],rs1加上符号扩展的imm作为内存地址,取该地址低32位,0扩展后写入rd
ldimm[11:0]rs1011rd0000011x[rd]=M[x[rs1]+sext(imm)][63:0],rs1加上符号扩展的imm作为内存地址,取该地址64位数据写入rd

特权指令

指令31~2019~1514~1211~76~0操作
uret00000000001000000000000001110011 
mret00110000001000000000000001110011 
sret00010000001000000000000001110011 
指令31~2524~2019~1514~1211~76~0操作
sfence.vma0001001rs2rs1000rd1110011 
wfi00010000010100000000000001110011 

RV64M

RV64M指令集共有13条指令,其指令格式如下所示:

31~2524~2019~1514~1211~76~0
func7rs2rs1func3rdopcode

具体指令集描述如下所示:

指令名称31~2524~2019~1514~1211~76~0操作
mul有符号乘法0000001rs2rs1000rd0110011rd=(rs1*rs2)[63:0]
mulh有符号乘法取高位0000001rs2rs1001rd0110011rd=(rs1*rs2)[127:64]
mulhu无符号乘法取高位0000001rs2rs1011rd0110011rd=(rs1*rs2)[127:64]
mulhsu有符号无符号乘法取高位0000001rs2rs1010rd0110011rd=(rs1*rs2)[127:64]; rs1有符号数,rs2无符号数
div有符号除法0000001rs2rs1100rd0110011rd=rs1/rs2; 除数为0,结果为0xffffffffffffffff; 产生overflow,结果为0x8000000000000000
divu无符号除法0000001rs2rs1101rd0110011rd=rs1/rs2; 除数为0,结果为0xffffffffffffffff
rem有符号取余0000001rs2rs1110rd0110011rd=rs1%rs2; 除数为0,求余结果为被除数; 产生overflow,余数结果为0x0
remu无符号取余0000001rs2rs1111rd0110011rd=rs1%rs2; 除数为0,求余结果为被除数
指令名称31~2524~2019~1514~1211~76~0操作
mulw低32位有符号乘法0000001rs2rs1000rd0111011tmp=(rs1[31:0]*rs2[31:0])[31:0], rd=sext(tmp[31:0])
divw低32位有符号除法0000001rs2rs1100rd0111011tmp=(rs1[31:0]/rs2[31:0])[31:0], rd=sext(tmp[31:0]); 除数为0,结果为0xffffffffffffffff; 产生overflow,结果为0xffffffff80000000
divuw低32位无符号除法0000001rs2rs1101rd0111011tmp=(rs1[31:0]/rs2[31:0])[31:0], rd=sext(tmp[31:0]); 除数为0,结果为0xffffffffffffffff
remw低32位有符号取余0000001rs2rs1110rd0111011tmp=(rs1[31:0]%rs2[31:0])[31:0], rd=sext(tmp[31:0]); 除数为0,求余结果为被除数[31]位符号位扩展后的结果,产生overflow时,余数结果为0x0
remuw低32位无符号取余0000001rs2rs1111rd0111011tmp=(rs1[31:0]%rs2[31:0])[31:0], rd=sext(tmp[31:0]) ; 除数为0,求余结果为被除数[31]位符号位扩展后的结果

文档信息

Search

    Table of Contents