0. 基本整数指令集的分类
RISC-V指令集分为基础指令集和扩展指令集,此外又根据不同的处理器位宽分为32位、64位和128位指令集,比如RV32I就表示32位处理器的基本整数指令集,此外,为了进一步的支持嵌入式处理器,还拓展了RV32R指令集,上边这些各类指令的具体含义和意义都可以在RISC-V的官方网站上找到:Specifications – RISC-V International (riscv.org)
下边本文主要学习和介绍RV32I的基础指令集,其他指令集与之仅有微小的差异。
前段时间动手写了一个基于RV32I的五级流水线RISC-V处理器,犹豫了一段时间要不要专门分享以下设计流程这样子,但又觉得现在RISC-V的开源项目已经太多了,即使写完也不一定会有用,这个处理器的详细架构图在我的另一个博客RISC-V指令集_努力学习的小英的博客-CSDN博客https://blog.csdn.net/qq_38798111/article/details/129695667?spm=1001.2014.3001.5502
是一个比较经典的五级流水,然后做了一些小小的优化,看如果感兴趣的人多的话,就再写一下,目前还是先把指令集更新完成。
1. 基本整数指令集
RV32I指令集中包括算术、逻辑、位移、比较、分支、存储和加载运算指令,可以完成一个处理器的所有基本功能,是一个最小的处理器。但如果想要在其基础上增加性能或增加对其他场景的兼容能力,可以可选的增加一些拓展指令如F型浮点运算等。
RV32I中共包括六种类型的指令:
- R型指令:寄存器类型指令,用于在寄存器之间执行算术、逻辑和比较运算。这些指令使用三个寄存器参数。
- I型指令:立即数类型指令,用于在寄存器和立即数(常数)之间执行算术、逻辑、移位和分支等操作。这些指令使用两个寄存器参数和一个立即数参数。
- S型指令:存储类型指令,用于将寄存器中的数据存储到存储器中。这些指令使用两个寄存器参数和一个偏移量参数。
- B型指令:分支类型指令,用于根据条件跳转到不同的代码段。这些指令使用两个寄存器参数和一个偏移量参数。
- U型指令:无条件跳转类型指令,用于无条件跳转到指定的地址。这些指令使用一个立即数参数。
- J型指令:跳转类型指令,用于跳转到指定的地址。这些指令使用一个寄存器参数和一个偏移量参数。
这六种类型指令的指令格式如下:
其中:
- opcode:操作码,每个指令类型都有只属于自己的编码值,用于区分不同的指令类型;
- rd:目标寄存器,需要写入的通用寄存器,该Bit域是寄存器的地址;
- func7,func3:表示指令的功能,同一指令类型中通过这几个bit域来区分具体功能(数字“7”代表是占用7-bit位宽,数字“3”同样);
- rs1:源寄存器1,需要读取的通用寄存器,该Bit域是寄存器的地址;
- rs2:源寄存器2,需要读取得通用寄存器,该Bit域是寄存器的地址;
- imm:立即数,该Bit域的数值可直接用于计算,是常数。
2. RV32I的寄存器
可以看到上述的六种类型寄存器一方面可以将存储器的数据读取到寄存器中,或将寄存器中数据写入到存储器,另一方面也可以操作寄存器与寄存器、寄存器与立即数间进行各类运算。所以需要使用一定的寄存器堆来缓存中间数据,这类寄存器堆的速度要求极高,需要与处理器主频一致,所以其大小也受到了限制,对于RV32I来说,一共有32个32bits的寄存器:
其中x0,为固定的常数0寄存器,其余的寄存器理论上可以互相通用,但为了方便起见,不同的寄存器尝尝会有一些特定的场合,比如一些寄存器就会专门用来保存函数调用时的返回地址等,这里不做过于详细的讨论(其实我也不特别懂,等到真的去编写编译器应该就会更深入的研究一下,哈哈哈)。
3. RV32I的指令概述
上边说到RV32I一共有六种类型的指令,先把所有的指令列出来,然后下一章详细的对每一个指令的作用和流程进行介绍:
4. RV32I指令集的详细介绍
4.1 R型指令
4.1.1 ADD指令(ADD rd,rs1,rs2):
- 把寄存器x[rs2]加到寄存器x[rs1]上,结果写入x[rd],忽略算数溢出。
4.1.2 SUB指令(SUB rd,rs1,rs2)
- x[rs1]减去x[rs2],将结果写入x[rd],忽略算数溢出。
4.1.3 XOR指令(XOR rd,rs1,rs2)
- 异或,x[rs1]和x[rs2]按位异或,将结果写入x[rd]
4.1.4 OR指令(OR rd,rs1,rs2)
- 或,x[rs1]和x[rs2]按位或后,将结果写入x[rd]
4.1.5 AND指令(AND rd,rs1,rs2)
- 与,将寄存器x[rs1]和x[rs2]按位相与后,将结果写入x[rd]
4.1.6 SLL指令(SLL rd,rs1,rs2)
- 逻辑左移(Shift Left Logical),把寄存器x[rs1]左移x[rs2]位,空的位置填0后,将结果写入x[rd]。x[rs2]的低5位(如果是RV64I则是低6位)代表移动位数,其余高位则忽略。
4.1.7 SRL指令(SRL rd,rs1,rs2)
- 逻辑右移,把寄存器x[rs1]右移x[rs2]位,空的位置填0后,将结果写入x[rd]。x[rs2]的低5位(如果是RV64I则是低6位)代表移动位数,其余高位则忽略。
4.1.8 SRA指令(SRA rd,rs1,rs2)
- 算数右移(Shift Right Arithmetic),把寄存器x[rs1]右移x[rs2]位,空位用x[rs1]最高位填充,结果写入x[rd]。x[rs2]的低5位(如果是RV64I则是低6位)代表移动位数,其余高位则忽略。
4.1.9 SLT指令(SLT rd,rs1,rs2)
- 小于则置位(Set if Less Than),比较x[rs1]和x[rs2]中的数,如果x[rs1]更小,则像x[rd]写入1,否则写入0。
4.1.10 SLTU指令(SLTU rd,rs1,rs2)
- 无符号小于则置位(Set if Less Than,Unsigned),比较x[rs1]和x[rs2],比较时视为无符号数,如果s[rs1]更小,则向x[rd]写入1,否则写入0。
4.2 I型指令
4.2.1 ADDI指令(ADDI rd,rs1,immediate)
- 加立即数(Add Immediate),吧符号位拓展的立即数加到寄存器x[rs1]上,结果写入x[rd],忽略算数溢出。
4.2.2 XORI指令(XORI rd,rs1,immediate)
- 立即数异或(Exclusive-OR Immediate),x[rs1]和有符号位扩展的immediate按位异或,结果写入x[rd]。
4.2.3 ORI指令(OR rd,rs1,immediate)
- 立即数或(OR immediate),将寄存器x[rs1]和有符号位扩展的立即数immediate按位或,结果写入x[rd]。
4.2.4 ANDI指令(ANDI rd,rs1,immediate)
- 立即数与(And immediate),把符号位扩展的立即数和寄存器x[rs1]上的值按位与,结果写入x[rd]。
4.2.5 SLLI指令(SLLI rd,rs1,shamt)
- 立即数逻辑左移(Shift Left Logical Immediate),把寄存器x[rs1]左移shamt位,空出的位置填入0,结果写入x[rd]。对于RV32I,仅当shamt[5]=0时,指令才是有效的
4.2.6 SRLI指令(SRLI rd,rs1,shamt)
- 立即数逻辑右移(Shift Right Logical Immediate),把寄存器x[rs1]右移shamt位,空出的位置填入0,结果写入x[rd]。对于RV32I,仅当shamt[5]=0时,指令才是有效的。
4.2.7 SRAI指令(SRAI rd,rs1,shamt)
- 立即数算数右移(Shift Right Arithmetic Immediate),把寄存器x[rs1]右移shamt位,空位用x[rs1]的最高位填充,结果写入x[rd]。对于RV32I,仅当shamt[5]=0时指令是有效的。
4.2.8 SLTI指令(SLTI rd,rs1,immediate)
- 小于立即数则置位(Set if Less Than Immediate),比较x[rs1]和有符号扩展的immediate,如果x[rs1]更小,向x[rd]写入1,否则写入0。
4.2.9 SLTIU指令(SLTIU rd,rs1,immediate)
- 无符号小于立即数则置位(Set if Less Than Immediate, Unsigned),比较x[rs1]和有符号扩展的immediate,比较时视为无符号数,如果x[rs1]更小,向x[rd]写入1,否则写入0。
4.2.10 LB指令(LB rd,offset(rs1))
- 字节加载(Load Byte),从地址x[rs1]+sign-extend(offset)读取一个字节,经符号位扩展后写入x[rd]。
4.2.11 LH指令(LH rd,offset(rs1))
- 半字节加载(Load Halfword),从地址x[rs1]+sign-extend(offset)读取两个字节,经符号位扩展后写入x[rd]。
4.2.12 LW指令(LW rd,offset(rs1))
- 字加载(Load Word),从地址x[rs1]+sign-extend(offset)读取四个字节,写入x[rd]。对于RV64I,需要进行符号位扩展。
4.2.13 LBU指令(LBU rd,offset(rs1))
- 无符号字节加载(Load Byte,Unsigned),从地址x[rs1]+sign-extend(offset)读取一个字节,经零扩展后写入x[rd]。
4.2.14 LHU指令(LHU rd,offset(rs1))
- 无符号半字加载(Load Halfword,Unsigned),从地址x[rs1]+sign-extend(offset)读取两个字节,经零扩展后写入x[rd]。
4.2.15 JALR指令(JALR rd,offset(rs1))
- 跳转并寄存器链接(Jump and Link Register),把pc寄存器设置为x[rs1]+sign-extend(offset),把计算出的地址的最低有效位设为0 ,并将源pc+4的值写入x[rd]。
4.2.16 ECALL指令
- 环境调用(Environment Call),通过引发环境调用异常来请求执行环境。
4.2.17 EBREAK指令
- 环境端点(Environment Breakpoint),通过抛出端点异常的方式请求调试器。
4.3 S型指令
4.3.1 SB指令(SB rs2,offset(rs1))
- 存字节(Store Byte)将x[rs2]的低字节存入内存地址x[rs1]+sign-extend(offset)。
4.3.2 SH指令(SH rs2,offset(rs1))
- 存半字(Store Halfword),将x[rs2]的低位2字节存入内存地址x[rs1]+sign-extend(offset)。
4.3.3 SW指令(SW rs2,offset(rs1))
- 存字(Store Word),将x[rs2]的低4个字节存入内存地址x[rs1]+sign-extend(offset)。
4.4 B型指令
4.4.1 BEQ指令(beq rs1,rs2,offset)
- 相等时跳转分支(Branch if Equal),如果寄存器x[rs1]和寄存器x[rs2]的值相等,把pc的值设置为当前值加上符号位扩展的偏移offset。
4.4.2 BNE指令(bne rs1,rs2,offset)
- 不相等时分支(Branch if Not Equal),如果寄存器x[rs1]和寄存器x[rs2]的值不相等,把pc的值设为当前值加上符号位扩展的偏移offset。
4.4.3 BLT指令(blt rs1,rs2,offset)
- 小于时分支(Branch if Less Than),如果寄存器x[rs1]的值小于寄存器x[rs2]的值(均视为二进制补码),那么把pc的值设为当前值加上符号位扩展的偏移offset。
4.4.4 BGE指令(bge rs1,rs2,offset)
- 大于等于时分支(Branch if Greater Than or Equal),如果寄存器x[rs1]的值大于等于寄存器x[rs2]的值(均视为二进制补码),那么把pc值设置为当前值加上符号位扩展的偏移offset。
4.4.5 BLTU指令(bltu rs1,rs2,offset)
- 无符号小于时分支(Branch if Less Than,Unsigned),如果寄存器x[rs1]的值小于寄存器x[rs2]的值(均视为无符号数),那么把pc的值设置为当前值加上符号位扩展的偏移offset。
4.4.6 BGEU指令(bgeu rs1,rs2,offset)
- 无符号大于等于时分支(Branch if Greater Than or Equal,Unsigned),如果寄存器x[rs1]的值大于等于寄存器x[rs2]的值(均视为无符号数),那么把pc值设为当前值加上符号位扩展的偏移offset。
4.5 U型指令
4.5.1 LUI指令(lui rd,immediate)
- 高位立即数加载(Load Upper Immediate),将符号位扩展的20位立即数immediate左移12位,并将低12位置零,写入x[rd]中。
4.5.2 AUIPC指令(auipc rd,immediate)
- PC加立即数(Add Upper Immediate to PC),把符号位扩展的20位立即数加到pc上,结果写入x[rd]。
4.6 J型指令
4.6.1 JAL指令(jal rd,offset)
- 跳转并链接(Jump and Link),把下一条指令的地址(pc+4)存储到x[rd]寄存器,然后把pc设置为当前值加上符号位扩展的offset。