SFC2021
SFC2021 总版主

267枚
铜币

334点
威望

0个
银元

计算机体系架构基础-RISC-V 非特权架构

2021-03-26 10:45

2870

本文开始将介绍RISC-V 的非特权架构和特权架构,希望能够帮助读者理解,并掌握RISC-V的指令集架构

RISC-V 非特权架构主要描述了RISC-V指令集指令的功能,在上一篇文章计算机体系架构基础-RISC-V 指令集现状介绍中已经简单介绍了RISC-V包含IMACFD等指令集,本文将简单介绍这些指令功能,关于RISC-V 指令集指令功能的描述,可以参考文档https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf


I 指令集指令功能


I指令集指令功能描述了基本的整型操作指令,按照指令功能可分为算术指令,控制指令,加载/存储指令,内存模型指令,以及环境调用指令等

I指令集指令使用了六种不同的指令格式进行编码,包括R-type, I-type, S-type, B-type, U-type 和J-type,如下图所示:

图片:计算机体系架构基础-RISC-V 非特权架构 1.jpg



从编码格式中,可以知道:
  • R-type 主要用于读取两个寄存器数据(rs1,rs2)进行操作,并将结果更新到目标寄存器(rd)的指令,主要编码算术指令
  • I-type 一个操作数是立即数(imm[11:0]),另一个操作数是寄存器(rs1) ,并将结果更新到目标寄存器(rd)的指令, 主要编码带立即数的算术指令和加载指令
  • S-type 使用了三个源操作数,rs1,rs2,和imm[11:0], 可用于编码存储指令
  • B-type 与S-type的区别在于立即数是imm[12:1],而不是imm[11:0], 这是因为对于RISC-V指令来说,PC[0]总是为零,因此B-type指令可用于编码比较两个源操作数结果,并控制程序跳转的分支控制指令。
  • U-type主要用于编码加载32-bit 大立即数的高20位
  • J-type 主要用于编码Jump and Link 等无条件跳转指令


算术指令


I指令集的算术指令包含了常用的算术逻辑操作,例如加、减、与、或、非、移位等,如下表:
       
指令
       
描述
       
操作符
       
汇编
       
add/addi
       
       
rd = rs1+rs2
       
add rd, rs1, rs2
addi rd, rs1, imm
       
sub
       
       
rd = rs1 - rs2
       
sub rd, rs1, rs2
       
slt, slti
       
小于时置1
       
rd = rs1< rs2 ? 1 : 0
       
slt rd, rs1, rs2
slti rd, rs1, imm
       
and, andi
       
按位与
       
rd = rs1 & rs2
       
and rd, rs1, rs2
andi rd, rs1, imm
       
or, ori
       
按位或
       
rd = rs1 | rs2
       
or rd, rs1, rs2
ori rd, rs1, imm
       
xor, xori
       
按位异或
       
rd = rs1 ^ rs2
       
xor rd, rs1, rs2
xori rd, rs1, imm
       
sll, slli
       
左移
       
rd = rs1<< rs2
       
sll rd, rs1, rs2
slli rd, rs1, imm
       
srl, srli
       
逻辑右移
       
rd = rs1 >> rs2
       
srl rd, rs1, rs2
srli rd, rs1, imm
       
sra, srai
       
算术右移
       
rd = rs1 >>> rs2
       
sra rd, rs1, rs2
srai rd, rs1, imm
       
lui
       
加载立即数高20位
       
rd = {imm[31:12], 12'b0}
       
lui rd, up-imm
       
auipc
       
立即数高20位与PC相加
       
rd = {imm[31:12], 12'b0} + PC
       
auipc rd, up-imm




















需要注意的是
  • 1.nop 指令为addi x0, x0, 0
  • sll/slli, srl/srli, sra/srai 的rs2/imm 作为移位数,会根据操作数据的长度(xLen)进行截断,例如RV32I 中, 移位数截断为低5位, 即rs2[4:0]/imm[4:0]


控制指令


控制指令可以分为无条件跳转指令(jump and link)和条件分支指令(branch).

无条件跳转指令主要用于函数的调用(jal)与返回(jalr), 条件分支指令主要用于if/else, switch 和while等程序结构。
  • jal rd, signedOffset指令将使程序跳转到地址为PC + signedOffset * 2, 并且将下一条指令的PC存储到目标寄存器rd中。
  • jalr rd, signedOffset(rs1)指令将使程序跳转到地址为((rs1 + signedOffset) << 1) >>1,  并且将下一条指令的PC存储到目标寄存器rd中。
  • beq/bne rs1, rs2, signedOffset指令判断rs1 是否与rs2 相等(不相等),如果满足条件,则将使程序跳转到地址为PC + signedOffset * 2。
  • blt/bltu/bge/bgeu rs1, rs2, signedOffset指令也是判断对应的条件。如果满足条件,则将使程序跳转到地址为PC + signedOffset * 2。


加载/存储指令


加载/存储指令负责将数据在寄存器与内存之间进行搬移,加载指令将数据从内存中加载到寄存器中,存储指令将数据从寄存器中,存回到内存当中。

在软件层次上,数据可以分为字节(Byte), 半字(HalfWord), 字(Word)以及双字(DoubleWord)

因此,对于RV32I 来说,加载指令包括(lb,lbu,lh,lhu,lw),存储指令包括(sb,sh,sw);对于RV64I 来说,加载指令包括(lb, lbu,lh,lhu,lw,lwu,ld),存储指令包括(sb,sh,sw,ld)。


内存模型指令


在基于RISC-V 指令集实现的处理器中,只有I指令集是必须实现的,其他指令都是可选择实现的,因为在没有实现原子指令集(A)的情况下,如何保证多核之间在临界区的同步,就成为了一个问题,因此,I 指令集定义了fence指令,来保证内存访问的顺序性。

fence 指令的指令编码如下:

图片:计算机体系架构基础-RISC-V 非特权架构 2.png



fence指令将存储的载体简单的分为内存(Memory)和设备(Device),两者的区别为,内存(Memory)具有读写一致性,即读取的值,一定是之前最近写入的值,而设备读写不一致,即读取的值,与之前最近写入的值没有关系。

因为对存储载体的访问操作可分为:
  • 对内存的读(R)写(W)操作
  • 设备的输入(I)输出(O)

同时,fence指令也将内存操作分成两个集合:
  • predecessor:fence指令之前的内存操作,由指令的PI,PO,PR,PW位编码
  • successor:fence指令之后的内存操作,由指令的SI,SO,SR,SW 位编码

而fence指令的功能则是保证在fence 指令之前的内存操作完成之前,不会执行fence指令之后的内存操作

例如:fence rw, r //fence 指令之前的rw操作完成之前,不会执行fence 指令之后的r操作


环境调用指令和断点指令


在现代操作系统中,会对处理器不同级别的程序赋予不同的特权模式,RISC-V 指令集架构暂时定义了三种特权模式: M-Mode, S-Mode 和 U-Mode, 在当前RSIC-V 处理器运行Linux操作系统中,OpenSBI 运行在M-Mode用于处理最高特权模式的操作,内核调度程序运行在S-Mode中,用户程序运行在U-Mode.

而处理器实现模式切换的途径主要包括中断/异常,以及环境调用指令ecall

例如,在Linux操作系统中,内核程序使用ecall 指令进入到OpenSBI(M-Mode) 中, OpenSBI 定义了一系列M-Mode下,处理器的服务请求操作,详细可以访问网址https://github.com/riscv/opensbi, 一般编译opensbi时,都需要将内核镜像放置于opensbi中,后续会有相应的文档介绍。

而断点指令(ebreak)主要用于对程序的调试。


乘除法指令集(M)


M指令集实现了整型的乘除法操作,如下表所示
       
指令
       
格式
       
功能描述
       
mul
       
mul rd, rs1,rs2
       
rs1,rs2相乘, 并乘法结果的低xLen-bit更新到rd
       
mulhu
       
mulhu rd, rs1, rs2
       
无符号的rs1,rs2 相乘,并将乘法结果的高xLen-bit更新到rd
       
mulh
       
mulh rd, rs1,rs2
       
有符号的rs1,rs2相乘,并将结果的高xLen-bit更新到rd
       
mulhsu
       
mulhsu rd, rs1,rs2
       
有符号的rs1, 无符号的rs2相乘,并将结果的高xLen-bit更新到rd
       
div
       
div rd, rs1, rs2
       
有符号的rs1,rs2相除,并将商更新到rd
       
divu
       
divu rd, rs1, rs2
       
无符号的rs1, rs2 相除,并将商更新到rd
       
rem
       
rem rd, rs1,rs2
       
有符号的rs1,rs2相除,并将余数更新到rd
       
remu
       
remu rd, rs1, rs2
       
无符号的rs1, rs2 相除,并将余数更新到rd


















总结


本文简单介绍了RISC-V 的I/M指令集指令功能,一般情况下,基本指令集(I)定义的指令功能已经可以保证了操作系统的运行,这降低了硬件实现的难度,但是一定程度上影响了处理器的性能,以及代码密度。

本文转载自SoC设计基础,目的在于传播更多信息,版权归原作者所有。


返回顶部