一起学RISC-V汇编第4讲之指令格式

一起学RISC-V汇编第4讲之指令格式

RISC-V指令集的一个特点就是指令是定长的,对于RV32I ISA,一共有47条指令,有6种指令指令格式,分别为R/I/S/B/U/J类型,其中:

  • R型用于寄存器-寄存器间的操作(10条)
  • I型用于短立即数和访存(Load)操作(25条)
  • S型用于访存Store操作(3条)
  • B型用于条件跳转(6条)
  • U型用于长立即数(2条)
  • J型用于无条件跳转(1条)

格式如下图所示:

RISC-V指令格式

从图中可以看出RV32I指令集:

  • 指令的长度固定为32位,以4字节边界对齐;
  • 源寄存器rs1、rs2,以及目标寄存器rd的位置保持不变;
  • RISC-V的指令编码风格是操作码(opcode)放在低位,操作码占7bit;
  • 立即数的符号位放置在最高位(31位),方便进行符号扩展;
  • RISC-V 指令格式为三操作数或者两操作数,操作数只能为立即数或寄存器;
  • B类型分支跳转指令是在S类型基础上将立即数进行了旋转,J类型跳转指令是在U类型基础上将立即数进行了旋转,所以也可以认为RISC-V仅有4种指令类型;
  • B型分支指令和J型跳转指令的地址需要左移1位(相当于乘以2),以获得更大的跳转范围。意味着:B型分支指令和J型跳转指令实际上只能跳转到偶数地址。

下面分别进行讲解:

1 RISC-V指令格式

1.1 R-type

R-type是最常用的指令类型,表示寄存器-寄存器操作,指令的操作由7bit的opcode、3位的func3、7位的func7 共同决定的(func3字段正好3位,func7字段正好7位);R-type包含有三个寄存器(两个源寄存器rs1与rs2,一个目的寄存器rd),寄存器位宽为5bit,可以寻址32个寄存器(指令集定义的通用寄存器个数2^5),由这些小细节可见,RISC-V指令集经过了巧妙的设计。

RV32I一共有10条R-type指令:

10条R-type

其详细编码如下:
10条R-type详细

以上指令的使用形式为:

# 加(算术指令)
ADD rd, rs1, rs2
# 减(算术指令)
SUB rd, rs1, rs2
# 逻辑左移(移位指令)
SLL rd, rs1, rs2
# 小于则置位(比较-置位)
SLT rd, rs1, rs2
# 无符号小于则置位(比较-置位)
SLTU rd, rs1, rs2
# 异或(逻辑指令)
XOR rd, rs1, rs2
# 逻辑右移(移位指令)
SRL rd, rs1, rs2
# 算术右移 (移位指令)
SRA rd, rs1, rs2
# 或(逻辑指令)
OR rd, rs1, rs2
# 与(逻辑指令)
AND rd, rs1, rs2

1.2 I-type

I-type指令有两个寄存器和一个立即数,相当于将R-type指令格式中的一个寄存器改为立即数,立即数一共12位,也即支持有符号数[-2028, 2017],如果是无符号数范围为[0-4096];指令的操作由7bit的opcode、3位的func3来决定。

I-type指令1

I-type指令2

RV32I一共有25条I-type指令,这里列出15条,分为9条短立即数操作,5条load取数操作,以及1条跳转操作。其它10条I-type指令为CSR读写指令以及环境指令等,后续会讲到。

可见:

  • 短立即数操作为1.1中 R-type中的指令中的rs2寄存器换成立即数,表示寄存器与立即数进行计算,结果存到目的寄存器里;
  • load取数操作中,rs1为寄存器,立即数为偏移地址,表示寄存器相对寻址。

9条短立即数操作:

9条短立即数指令

以上指令的使用形式为:

# 加立即数(算术指令)
ADDI rd, rs1, imm
# 小于立即数则置位(比较-置位)
SLTI rd, rs1, imm
# 无符号小于立即数则置位(比较-置位)
SLTIU rd, rs1, imm
# 异或立即数(逻辑指令)
XORI rd, rs1, imm
# 或立即数(逻辑指令)
ORI rd, rs1, imm
# 与立即数(逻辑指令)
ANDI rd, rs1, imm
# 逻辑左移立即数(移位指令)
SLLI rd, rs1, shamt
# 逻辑右移立即数(移位指令)
SRLI rd, rs1, shamt
# 算术右移立即数 (移位指令)
SRAI rd, rs1, shamt

注意:riscv中没有SUBI指令,SUBI可以由ADDI来实现(减一个数等于加一个负数)。

5条load取数操作:
5条load指令

以上指令的使用形式为:

# 取字节(取指令)
LB rd, imm(rs1)
# 取半字(取指令)
LH rd, imm(rs1)
# 取字(取数令)
LW rd, imm(rs1)
# 取无符号字节(取指令)
LBU rd, imm(rs1)
# 取无符号半字(取指令)
LHU rd, imm(rs1)

RV32I为什么没有LWU指令?实际上LWU属于RV64I的指令,LB,LH 都是load数到32位寄存器里,由于寄存器比要取的数宽,所以存在符号扩展,在RV64I架构,寄存器为64bit的,LW才存在符号扩展,所以LWU属于RV64I的指令。

1条跳转操作:

跳转指令

这条指令的使用形式为:

# 寄存器跳转并链接(跳转指令)
JALR rd, imm(rs1)

1.3 S-type

S-type指令操作由7bit的opcode、3位的func3来决定,S-type指令没有目的寄存器,一般表示访存的store操作,如sw,sd等。指令中包含两个源寄存器(rs1,rs2)与一个12位立即数(imm[31:25]与imm[11:7],这么样拆分目的在于尽量固定rs1,rs2 位域的位置,方便译码)

S-type

RV32I一共有3条S-type指令,实现的同样是寄存器相对寻址:
Store指令

以上指令的使用形式为:

# 存字节(存指令)
SB rs2, imm(rs1)
# 存半数(存指令)
SH rs2, imm(rs1)
# 存字(存指令)
SW rs2, imm(rs1)

1.4 B-type

B-type指令操作由7bit的opcode、3位的func3来决定;指令中包含两个源寄存器(rs1,rs2)与一个12位立即数,B-typed 一般表示条件跳转操作指令(分支指令),如相等(beq)、不相等(bne)、大于等于(bge)以及小于(blt)等跳转指令。

B-type

RV32I一共有6条B-type指令:

分支指令

以上指令的使用形式为:

# 相等时分支(分支指令)
BEQ rs1, rs2, imm
# 不等时分支(分支指令)
BNE rs1, rs2, imm
# 小于时分支(分支指令)
BLT rs1, rs2, imm
# 大于等于时分支(分支指令)
BGE rs1, rs2, imm
# 无符号小于时分支(分支指令)
BLTU rs1, rs2, imm
# 无符号大于等于时分支(分支指令)
BGET rs1, rs2, imm

为什么没有小于等于、大于这些分支指令?

实际上if (a <= b),可以转换为 if (b >= a) , 所以只需交换操作数即可,无需专门的指令,riscv中小于等于、大于等分支指令都是伪指令,后续伪指令章会讲到。

1.5 U-type

U-type指令操作仅由7位opcode决定;指令包括一个目的寄存器rd和20位的立即数,U-type一般表示长立即数操作,例如 lui 指令,将立即数左移 12 位,并将低 12 位置零,结果写回目的寄存器中。

U-type

RV32I一共有2条U-type指令:
U-type指令

以上指令的使用形式为:

# 装入高位立即数(算术指令)
LUI rd, imm
# PC加高位立即数(算术指令)
AUIPC rd, imm

看这个指令的描述,可以与I-type的短立即数(12bit)配合,得到一个完整的32bit立即数。

1.6 J-type

J-type指令操作仅由7位opcode决定,与U-type一样只有一个目的寄存器rd和20位的立即数,但是立即数的位域与U-type的组成不同,J-type一般用于无条件跳转,如jal指令,RV32I一共有1条J-type指令。

J-type指令

以上指令的使用形式为:

# 跳转(跳转指令)
JAL rd, imm

2 RISC-V是如何译码的?

通过opcode来得到指令类型,RISC-V 规范中给出了如下图所示的 opcode 表格(适用于 RV32G 和 RV64G):

opcode映射表

opcode一共7位,低两bit固定位1(inst[1:0]=0b11),根据inst[6:5] 与 nst[4: 2]可查表。根据指令类型,可以判断得到是属于R/I/S/B/U/J中的某一类型,然后按照其格式进行拆分译码,例如:如果是R-type,进一步拆分func3和func7字段。

如表中对应关系为:

指令类型 对应opcode map中的类型
R类型 OP
I类型 OP-IMM、LOAD、JALR
S类型 STORE
B类型 BRANCH
U类型 AUIPC、LUI
J类型 JAL

详情可以参考芯来开源的蜂鸟解码实现:

https://github.com/riscv-mcu/e203_hbirdv2/blob/master/rtl/e203/core/e203_exu_decode.v

参考:

  1. 《riscv-spec-20191213.pdf》
  2. 3、寄存器 — RISC-V RT-Thread 编程指南 0.0.1 文档 (riscv-rtthread-programming-manual.readthedocs.io)
  3. RISC-V 指令格式和6种基本整数指令_csrrw-CSDN博客
  4. 计算机系统基础(五)之RISC-V指令集
  5. 从零开始,徒手写一个 RISC-V 模拟器(2)——RISC-V 指令集与 CPU - 泰晓科技 (tinylab.org)
posted @ 2024-07-19 23:54  sureZ_ok  阅读(239)  评论(0编辑  收藏  举报