RV32I指令格式
用于寄存器—寄存器操作的R类型指令
用于短立即数和访存LOAD操作的I型指令
用于访存store操作的S型指令
用于条件跳转操作的B型指令
用于长立即数的U型指令
用于无条件跳转的J型指令
RISC_V实际上只有四种基本格式,分支指令B类型的立即数字段是在S类型的基础上调转了一位而来,而跳转指令J类型则是将直接字段在U类型的技术上调转了十二位而得来的
31 25 24 20 19 15 14 12 11 7 6 0 | |||||||
imm[31:12] | rd | 0110111 | U lui | ||||
imm[31:12] | rd | 0010111 | U auipc | ||||
imm[20|10:1|11|19:12] | rd | 1101111 | J jal | ||||
imm[11:0] | rs1 | 000 | rd | 1100111 | I jalr | ||
imm[12|10:5] | rs2 | rs1 | 000 | imm[4:1|11] | 1100011 | B beq | |
imm[12|10:5] | rs2 | rs1 | 001 | imm[4:1|11] | 1100011 | B bne | |
imm[12|10:5] | rs2 | rs1 | 100 | imm[4:1|11] | 1100011 | B blt | |
imm[12|10:5] | rs2 | rs1 | 101 | imm[4:1|11] | 1100011 | B bge | |
imm[12|10:5] | rs2 | rs1 | 110 | imm[4:1|11] | 1100011 | B bltu | |
imm[12|10:5] | rs2 | rs1 | 111 | imm[4:1|11] | 1100011 | B bgeu | |
imm[11:0] | rs1 | 000 | rd | 0000011 | I lb | ||
imm[11:0] | rs1 | 001 | rd | 0000011 | I lh | ||
imm[11:0] | rs1 | 010 | rd | 0000011 | I lw | ||
imm[11:0] | rs1 | 100 | rd | 0000011 | I lbu | ||
imm[11:0] | rs1 | 101 | rd | 0000011 | I lhu | ||
imm[11:5] | rs2 | rs1 | 000 | imm[4:0] | 0100011 | S sb | |
imm[11:5] | rs2 | rs1 | 001 | imm[4:0] | 0100011 | S sh | |
imm[11:5] | rs2 | rs1 | 010 | imm[4:0] | 0100011 | S sw | |
imm[11:0] | rs1 | 000 | rd | 0010011 | I addi | ||
imm[11:0] | rs1 | 010 | rd | 0010011 | I slti | ||
imm[11:0] | rs1 | 011 | rd | 0010011 | I sltiu | ||
imm[11:0] | rs1 | 100 | rd | 0010011 | I xori | ||
imm[11:0] | rs1 | 110 | rd | 0010011 | I ori | ||
imm[11:0] | rs1 | 111 | rd | 0010011 | I andi | ||
0000000 | shamt | rs1 | 001 | rd | 0010011 | I slli | |
0000000 | shamt | rs1 | 101 | rd | 0010011 | I srli | |
0100000 | shamt | rs1 | 101 | rd | 0010011 | I srai | |
0000000 | rs2 | rs1 | 000 | rd | 0110011 | R add | |
0100000 | rs2 | rs1 | 000 | rd | 0110011 | R sub | |
0000000 | rs2 | rs1 | 001 | rd | 0110011 | R sll | |
0000000 | rs2 | rs1 | 010 | rd | 0110011 | R slt | |
0000000 | rs2 | rs1 | 011 | rd | 0110011 | Rsltu | |
0000000 | rs2 | rs1 | 100 | rd | 0110011 | R xor | |
0000000 | rs2 | rs1 | 101 | rd | 0110011 | R srl | |
0100000 | rs2 | rs1 | 101 | rd | 0110011 | R sra | |
0000000 | rs2 | rs1 | 110 | rd | 0110011 | R or | |
0000000 | rs2 | rs1 | 111 | rd | 0110011 | R and | |
0000 | pred | succ | 00000 | 000 | 00000 | 0001111 | I fence |
0000 | 0000 | 0000 | 00000 | 001 | 00000 | 0001111 | I fence.i |
000000000000 | 00000 | 00 | 00000 | 1110011 | I ecall | ||
000000000000 | 00000 | 000 | 00000 | 1110011 | I ebreak | ||
csr | rs1 | 001 | rd | 1110011 | I csrrw | ||
csr | rs1 | 010 | rd | 1110011 | I csrrs | ||
csr | rs1 | 011 | rd | 1110011 | I csrrc | ||
csr | zimm | 101 | rd | 1110011 | I csrrwi | ||
csr | zimm | 110 | rd | 1110011 | I cssrrsi | ||
csr | zimm | 111 | rd | 1110011 | I csrrci |
表 2.3:RV32I带有指令布局,操作码格式类型和名称 有指令布局,操作码格式类型和名称 有指令布局,操作码格式类型和名称 有指令布局,操作码格式类型和名称 有指令布局,操作码格式类型和名称 有指令布局,操作码格式类型和名称 的操作码映射。(此图 基于 [Waterman and Asanovi'c 2017]的表 19.2。)
RV32I寄存器与整数计算
简单的算术指令(add, sub)、逻辑指令(and, or, xor)以及移位指令(sll, srl, sra)和其他ISA差不多。他们从寄存器读取两个32位的值,并将32位结果写入目标寄存器。RV32I还提供了这些指令的立即数版本,RV32I的立即数会进行符号扩展,如果需要,可以用立即数表示负数
程序可以根据比较结果生成布尔值。为应对这种使用场景下,RV32I提供一个当小于时置位的指令。如果第一个操作数小于第二个操作数,它将目标寄存器设置为1,否则为0。不出所料,对这个指令,有一个有符号版本(slt)和无符号版本(sltu),分别用于处理有符号和无符号整数比较。相应的,上述两条指令也有立即数版本的(slti,sltiu)。正如我们将要看到的,虽然RV32I分支指令可以检查两个寄存器之间的所有关系,但一些条件表达式涉及多对寄存器之间的关系。对于这些表达式,编译器或汇编语言程序员可以将slt以及与或异或等逻辑指令组合使用来解决更复杂的条件表达式。
剩下的两条整数计算指令主要用于构造大的常量数值和链接。加载立即数到高位(lui)将20位常量加载到寄存器的高20位。接着便可以使用标准的立即指令来创建32位常量。这样子,仅使用2条32位RV32I指令,便可构造一个32位常量。向PC高位加上立即数(auipc)让我们仅用两条指令,便可以基于当前PC以任意偏移量转移控制流或者访问数据。将auipc中的20位立即数与jalr(参见下面)中12位立即数的组合,我们可以将执行流转移到任何32位PC相对地址。而auipc加上普通加载或存储指令中的12位立即数偏移量,使我们可以访问任何32位PC相对地址的数据。
RV32I的寄存器
RV32I的Load和Store
除了提供32位字(lw,sw)的加载和存储外,图2.1中说明,RV32I 支持加载有符号和无符号字节和半字(lb,lbu,lh,lhu)和存储字节和半字(sb,sh)。有符号字节和半字符号扩展为32位再写入目的寄存器。即使是自然数据类型更窄,低位宽数据也是被扩展后再处理,这使得后续的整数计算指令能正确处理所有的32位。在文本和无符号整数中常用的无符号字节和半字,在写入目标寄存器之前都被无符号扩展到32位。 加载和存储的支持的唯一寻址模式是符号扩展12位立即数到基地址寄存器,RISC-V没有特殊的堆栈指令。将31个寄存器中的某一个作为堆栈指针,标准寻址模式使用起来和压栈(push)和出栈(pop)类似,并且不增加ISA的复杂性。RISC-V不支持延迟加载(delayed load)。
RV32I条件分支
RV32I可以比较两个寄存器并根据比较结果上进行分支跳转。比较可以是:相等(beq),不相等 (bne),大于等于(bge),或小于(blt)。最后两种比较有符号比较,RV32I也提供相应的无符号版本比较的:bgeu和bltu。剩下的两个比较关系(大于和小于等于)可以通过简单地交换两个操作数,即可完成比较。因为x < y表示y > x且x ≥ y表示y ≤ x。 由于RISC-V指令长度必须是两个字节的倍数——关于可选的双字节指令 ,分支指令的寻址方式是12位的立即数乘以2,符号扩展它,然后将得到值加到PC上作为分支的跳转地址。
补充说明:不使用条件码实现大位宽数据的加法
在 RV32I中是通过 中是通过 sltu计算进位来实现的: 计算进位来实现的:
add a0,a2,a4 # 加低 32 位: a0 = a2 + a4
sltu a2,a0,a2 # 若 (a2+a4) < a2那么 a2’ = 1, 否则 a2’ = 0
add a5,a3,a5 # 加高 32位: a5 = a3 + a5
add a1,a2,a5 # 加上低 32位的进位
补充说明:获取 PC
当前的 PC可以通过将 auipc的 U立即数字段设置为 0来获得。对于 来获得。对于 来获得。对于 x86-32,要想读取 ,要想读取 PC, 你需要先进行函数调用,(这样子可以将 你需要先进行函数调用,(这样子可以将 你需要先进行函数调用,(这样子可以将 PC推入堆栈) ;然后被调用的函数可以从堆栈中读 取刚被压栈的 PC,最后将 PC值返回给调用者(需要再弹出堆栈)。因此,或许当前的 值返回给调用者(需要再弹出堆栈)。因此,或许当前的 PC至少需要 1个 store,2个 load和 2个跳转
补充说明: 软件检查溢出
大部分(但不是所有)程序都忽略整数算术溢出,因此 大部分(但不是所有)程序都忽略整数算术溢出,因此 大部分(但不是所有)程序都忽略整数算术溢出,因此 大部分(但不是所有)程序都忽略整数算术溢出,因此 大部分(但不是所有)程序都忽略整数算术溢出,因此 RISCRISCRISCRISC-V依赖于软件溢出检查 。依赖于软件溢出检查 。无符号加法的溢出只需要在指令 后添一个额外分支:addu t0,t1,t2; bltu t0, t1,overflow。
对于带符号的加法,如果已知一个操作数则溢出检查只需要在后添条分支 对于带符号的加法,如果已知一个操作数则溢出检查只需要在后添条分支 对于带符号的加法,如果已知一个操作数则溢出检查只需要在后添条分支 指令: addi t0,t1,+ imm; blt t0,t1,overflow。
这覆盖了常见的加立即数情况。对于一般带符号法,我们需要在指令后添三个 这覆盖了常见的加立即数情况。对于一般带符号法,我们需要在指令后添三个 这覆盖了常见的加立即数情况。对于一般带符号法,我们需要在指令后添三个 这覆盖了常见的加立即数情况。对于一般带符号法,我们需要在指令后添三个 附加指令,当且仅一个操作数为负时结果才能小于另否则就是溢出。
add t0, t1, t2
slti t3, t2, 0 # t3 = (t2<0)
slt t4, t0, t1 # t4 = (t1+t2<t1)
bne t3, t4, overflow # 若 (t2<0) && (t1+t2>=t1)
# || (t2>=0) && (t1+t2<t1)则为溢出
RV32I无条件跳转
跳转并链接指令(jal)具有双重功能。若将下一条指令PC + 4的地址保存到目标寄存器中,通常是返回地址寄存器ra(见上表),便可以用它来实现过程调用。如果使用零寄存器(x0)替换ra作为目标寄存器,则可以实现无条件跳转,因为x0不能更改。像分支一样,jal将其20位分支地址乘以2,进行符号扩展后再添加到PC上,便得到了跳转地址。 跳转和链接指令的寄存器版本(jalr)同样是多用途的。它可以调用地址是动态计算出来的函数,或者也可以实现调用返回(只需ra作为源寄存器,零寄存器(x0)作为目的寄存器)。Switch和case语句的地址跳转,也可以使用jalr指令,目的寄存器设为x0。
RV32I杂项
控制状态寄存器指令 (csrrc、csrrs、csrrw、csrrci、csrrsi、csrrwi),使我们可以轻松地访问一些程序性能计数器。对于这些64位计数器, 我们一次可以读取32位。这些计数器包括了系统时间, 时钟周期以及执行的指令数目。
在RISC-V指令集中,ecall指令用于向运行时环境发出请求,例如系统调用。调试器使用ebreak指令将控制转移到调试环境。
fence指令对外部可见的访存请求,如设备I / O和内存访问等进行串行化。外部可见指对处理器的其他核心、线程,外部设备或协处理器可见。fence.i指令同步指令和数据流。在执行fence.i指令之前,对于同一个硬件线程,RISC-V不保证用存储指令写到内存指令区的数据可以被取指令取到。
RISC-V使用内存映射I / O,不需要inout等指令来进行I/O状态控制,为支持字符串处理,RISC-V还可以进行字节存取
那里,继承了如下这些特性: 那里,继承了如下这些特性: 那里,继承了如下这些特性: 那里,继承了如下这些特性: 那里,继承了如下这些特性:
⚫ 32位字节可寻址的地址空间
⚫ 所有指令均为 32位长
⚫ 31个寄存器,全部 32位宽,寄存器 0硬连线为零
⚫ 所有操作都在寄存器之间(没有寄存器到内存的操作 )
⚫ 加载 /存储字加上有符号和无符号加载、存储字节和半字
⚫ 所有算术,逻辑和移位指令都有立即数版本
⚫ 立即数总是符号扩展
⚫ 仅提供一种数据寻址模式(寄存器 +立即数)和 PC相对分支
⚫ 无乘法或除法指令
⚫ 一个指令,用于将大立即数加载到寄存器的高位,这样加载32位常量到寄存器只需要两条指令