当前位置:网站首页> 体育知识 > csr赛车t2(我们一起学RISC-V——05-RV32I指令集)

csr赛车t2(我们一起学RISC-V——05-RV32I指令集)

更新时间:2022-10-14 11:04:26

csr赛车t2(我们一起学RISC-V——05-RV32I指令集)

本期内容如下:

  1. RISC-V指令格式
  2. RV32I指令命名规则
  3. RV32I指令集
  4. 重点指令详解

一、RISC-V指令格式


RISC-V按照32bit的指令不同字符的具体分布共分为6种基本格式,分别是R类型,I类型,S类型,U类型,J类型,B类型。具体32bit的数据功能划分如图1所示。

csr赛车t2(我们一起学RISC-V——05-RV32I指令集)

图1 RISC-V的六种基本指令格式

  • R类型——寄存器间操作指令;
  • I类型 ——短立即数和访存Load操作指令;
  • S类型——访存Store操作指令;
  • U类型——用于操作长立即数的指令;
  • J类型——用于无条件跳转的指令;
  • B类型——用于有条件跳转的指令;

在图1中rd为目标寄存器;rs1,rs2为源寄存器1,2;imm[11:0]为12位立即数。opcode和func3共同决定了不同类型的指令;

二、RV32I指令命名规则


csr赛车t2(我们一起学RISC-V——05-RV32I指令集)

图2 指令集全称和缩写

图2 位RV32I指令集的全称,在图2中有下划线"_"的部分为指令的关键词,中括号为可选组合部分,单独的"_"表示什么也不加,举个例子:

  • add在RISC-V中是一个加法指令,该指令是一个R类型的操作指令参见图1,该指令还有一个立即数操作(I类型)的版本也就是addi。
add a0,a1,a2  //这是一个将寄存器a1和a2相加,之后把结果保存到a0中的指令
addi a0,a1,12  //这是一个将寄存器a1和立即数12相加,之后再把结果保存到a0中的指令
  • and为“逻辑与”指令,该指令是一个R类型的操作指令,该指令也有一个立即数操作(I类型)的版本也就是andi。
and t0,t1,t2  //这是一个将寄存器a1和a2按位相与,之后把结果保存到t0中的指令
andi t0,t1,0x01  //这是一个将寄存器t1和立即数0x01相与,之后再把结果保存到t0中的指令

其他指令可以参照图2,进行理解学习。

三、RV32I指令集


本部分暂时仅介绍机器指令,由于很多伪指令容易让学习者混淆一些概念,和编译器相关的伪指令将在今后介绍。

在寄存器功能表述中rs1,rs2,rd均表示寄存器的名称,(rs1)表示rs1寄存其中的数值,

(rs2)表示rs2寄存其中的数值,(rd)表示rd寄存其中的数值,其他寄存器的表述(rx)表示rx寄存器中的数值。

M(xxxx)表示内存地址xxxx的数值。

3.1 算术运算

本部分将介绍加法指令add 、addi和减法指令sub

rd,rs1,rs2可以是RISC-V寄存器的X0~X31任意寄存器中的数据

  • add rd , rs1, rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现(rd)=(rs1) (rs2),忽略算术溢出。

  • addi rd, rs1, imm

rd为目标寄存器,rs1为源寄存器1,imm为立即数带有符号,该指令实现(rd)=(rs1) imm,忽略算术溢出。

  • sub rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现(rd)=(rs1)-(rs2),忽略算术溢出。

3.2 逻辑运算

本部分将介绍逻辑运算

逻辑“与”and、andi;

逻辑“或”运算or、ori;

逻辑“异或”xor、xori

  • and rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现按位“与”操作,即(rd)=(rs1)&(rs2)。

  • andi rd, rs1, imm

rd为目标寄存器,rs1为源寄存器1,imm为立即数带有符号,该指令实现按位“与”,即(rd)=(rs1)&imm。

  • or rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现按位或,即(rd)=(rs1)|(rs2)。

  • ori rd, rs1, imm

rd为目标寄存器,rs1为源寄存器1,imm为立即数带有符号,该指令实现按位或,即(rd)=(rs1)|imm。

  • xor rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现按位异或,

即(rd)=(rs1)^(rs2)。

  • xori rd, rs1, imm

rd为目标寄存器,rs1为源寄存器1,imm为立即数带有符号,该指令实现按位异或,

即(rd)=(rs1)^imm。

3.3 移位操作

算术右移sra,srai:最高位填充符号位。正数填充0,负数填充1
逻辑右移srl,srli:最高位填充0
逻辑左移sll,slli:最高位补0

  • sll rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现逻辑左移动,

即(rd)=(rs1)<<(rs2)。

  • slli rd, rs1, shamt

rd为目标寄存器,rs1为源寄存器1,shamt为立即数,该指令实现立即数逻辑左移动,

即(rd)=(rs1)<<shamt。

  • sra rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现算术右移动,

即(rd)=(rs1)>>rs2。

  • srai rd, rs1, shamt

rd为目标寄存器,rs1为源寄存器1,shamt为立即数,该指令实现立即数算术右移动,

即(rd)=(rs1)>>shamt。

  • srl rd,rs1,rs2

rd为目标寄存器,rs1为源寄存器1,rs2为源寄存器2,该指令实现逻辑右移动,

即(rd)=(rs1)<<(rs2)。

  • srli rd, rs1, shamt

rd为目标寄存器,rs1为源寄存器1,shamt为立即数,该指令实现立即数逻辑右移动,

即(rd)=(rs1)>>shamt。


3.4 内存操作

内存加载指令主要是实现从内存地址读取数据到处理器寄存器(Load),存储寄存器中的数据到指定地址的内存单元中(Store)

  • lui rd,imm

加载有符号立数到高20位,即imm到rd的高20bit,imm取值范围为0-1048575。

C伪代码表示为,(rd)=(imm<<12)&0xfff。

例如:

lui  a0,0x23     //此时a0的数值为0x23000
  • auipc rd,imm

加载有符号立即数imm的到PC寄存器高20位,同时保存结果到rd中,C伪代码表示为(rd)=(pc) (imm<<12)&0xfff。

例如:

auipc a1, 0x06  //假设当前pc数值为0x04,执行该指令后,a1的数值为0x6004
  • lb rd,rs1,imm

rs1为立即数,rd为目标寄存器,imm为立即数,读取((rs1) imm)地址的一字节数据经符号扩展后保存到rd中。

(rd)=((rs1) imm)&0xff

该指令通常被写成lb rd,offset(rs),其中offset也就是立即数,否则gnu编译器不能识别。

  • lbu rd,rs1,imm

rs1为立即数,rd为目标寄存器,imm为立即数,读取((rs1) imm)地址的一字节数据经零扩展后保存到rd中。

(rd)=((rs1) imm)&0xff

该指令通常被写成bu rd,offset(rs),其中offset也就是立即数,否则gnu编译器不能识别。

  • lh rd,rs1,imm

rs1为立即数,rd为目标寄存器,imm为立即数,读取((rs1) imm)地址的两字节数据经符号扩展后保存到rd中。

rd=((rs1) imm)&0xffff

该指令通常被写成lh rd,offset(rs),其中offset也就是立即数,否则gnu编译器不能识别。

  • lhu rd,rs1,imm

rs1为立即数,rd为目标寄存器,imm为立即数,读取((rs1) imm)地址的两字节数据经零扩展后保存到rd中。

rd=((rs1) imm)&0xffff

该指令通常被写成lhu rd,offset(rs),其中offset也就是立即数,否则gnu编译器不能识别。

  • lw rd,rs1,imm

rs1为立即数,rd为目标寄存器,imm为立即数,读取((rs1) imm)地址的四字节数据经符号扩展后保存到rd中。

rd=((rs1) imm)&0xffffffff

该指令通常被写成lw rd,offset(rs),其中offset也就是立即数,否则gnu编译器不能识别。

  • sb rs1,rs2,imm

rs1为目标地址寄存器,rs2为源寄存器,imm为立即数,读取rs2寄存器的一字节数据保存到地址((rs1) imm)地址中。

M((rs1) imm)=(rs2)&0xff

该指令通常被写成sb rs2,offset(rs1),其中offset也就是立即数,否则gnu编译器不能识别。

  • sh rs1,rs2,imm

rs1为目标地址寄存器,rs2为源寄存器,imm为立即数,读取rs2寄存器的两字节数据保存到地址((rs1) imm)中。

M((rs1) imm)=(rs2)&0xffff

该指令通常被写成sh rs2,offset(rs1),其中offset也就是立即数,否则gnu编译器不能识别。

  • sw rs1,rs2,imm

rs1为目标地址寄存器,rs2为源寄存器,imm为立即数,读取rs2寄存器的四字节数据保存到地址((rs1) imm)中。

M((rs1) imm)=(rs2)&0xffffffff

该指令通常被写成sw rs2,offset(rs1),其中offset也就是立即数,否则gnu编译器不能识别。

3.5 比较指令

比较指令主要用于比较两个寄存器的数值,以及寄存器和立即数的比较。

  • slt rd, rs1, rs2

rd为目标寄存器,rs1为比较数1,rs2为比较数2, 本指令表示如果rs1中的数据比rs2中的数据小就设置rd为1,否则设置rd为0.rs1,rs2为有符号数。

用C伪代码表示如下:

if((rs1)<(rs2))

rd=1;

else

rd=0;


  • slti rd,rs1,imm

rd为目标寄存器,rs1为比较数1,imm为比立即数, 本指令表示如果rs1中的数据比imm小就设置rd为1,否则设置rd为0..rs1,imm为有符号数。

用C伪代码表示如下:

if((rs1)<imm)

rd=1;

else

rd=0;

  • sltu rd,rs1,rs2

rd为目标寄存器,rs1为比较数1,rs2为比较数2, 本指令表示如果rs1中的数据比rs2中的数据小就设置rd为1,否则设置rd为0.rs1,rs2被视为无符号数。

用C伪代码表示如下:

if((rs1)<(rs2))

rd=1;

else

rd=0;

  • sltiu rd,rs1,imm

rd为目标寄存器,rs1为比较数1,imm为比较立即数。 本指令表示如果rs1中的数比立即数imm小就设置rd为1,否则设置rd为0。rs1,imm为被视为无符号数。

用C伪代码表示如下:

if((rs1)<imm)

rd=1;

else

rd=0;

3.6 控制和状态寄存器操作

本部分指令主要是为了操作处理器本身的一些控制和状态寄存器而设定的。

  • csrrw rd,csr,rs1

rd为目标寄存器,csr为状态寄存器,rs1为源寄存器1,该指令为先读取csr寄存器的数据t,之后将rs1内的数据写入csr,最后再将t数据写入rd。


  • csrrs rd,csr,rs1

rd为目标寄存器,csr为状态寄存器,rs1为源寄存器1,该指令为先读取csr寄存器的数据t,之后将rs1与csr按位“或”后的数据写入csr,最后再将t数据写入rd。

  • csrrc rd,csr,rs1

rd为目标寄存器,csr为状态寄存器,rs1为源寄存器1,该指令为先读取csr寄存器的数据t,之后将rs1与csr按位“与”后的数据写入csr,最后再将t数据写入rd。

  • csrrwi rd,csr,imm

rd为目标寄存器,csr为状态寄存器,imm为立即数,该指令先读取csr寄存器的数据并写入rd中,之后5bit零扩展的立即数写入csr。

  • csrrci rd,csr,imm

rd为目标寄存器,csr为状态寄存器,imm为立即数,该指令先读取csr寄存器的数据t,之后将t与5bit零扩展的立即数imm位“与”后写入csr,再把t写入rd。

  • csrrsi rd,csr,imm

rd为目标寄存器,csr为状态寄存器,imm为立即数,该指令先读取csr寄存器的数据t,之后与5bit零扩展的立即数imm位“或”后写入csr,再把t写入rd。


3.7 条件跳转指令

  • beq rs1,rs2,imm

rs1比较数值1,rs2为比较数值2,imm为立即数,该指令表示当rs1和rs2数值相同时,设置pc数值为(pc) imm的,在这里imm就是offset地址偏移。

  • bne rs1,rs2,imm

rs1比较数值1,rs2为比较数值2,imm为立即数,该指令表示当rs1和rs2数值不相同时,设置pc数值为(pc) imm的,在这里imm就是offset地址偏移。

  • bge rs1,rs2,imm

rs1比较数值1,rs2为比较数值2,imm为立即数,该指令表示当rs1大于等于rs2数值时,设置pc数值为(pc) imm的,在这里imm就是offset地址偏移。

  • blt rs1,rs2,imm

rs1比较数值1,rs2为比较数值2,imm为立即数,该指令表示当rs1小于rs2数值时,设置pc数值为(pc) imm的,在这里imm就是offset地址偏移。

  • bgeu rs1,rs2,imm

rs1比较数值1,rs2为比较数值2,imm为立即数,该指令表示当无符号数rs1大于等于无符号数rs2数值时,设置pc数值为(pc) imm的,在这里imm就是offset地址偏移。

  • bltu rs1,rs2,imm

rs1比较数值1,rs2为比较数值2,imm为立即数,该指令表示当无符号数rs1小于无符号数rs2数值时,设置pc数值为(pc) imm的,在这里imm就是offset地址偏移。

3.8 无条件跳转指令

  • jal rd,imm

把rd的值设置为当前(pc) 4,pc的值设为pc当前数值 imm,imm为有符号扩展的立即数,imm也即是offset。

  • jalr rd,rs1,imm

读取(pc) 4为t,把pc数值设为((rs1) imm并设置0bit为0),将t值保存到rd中, imm也即是offset。

该指令通常也被写为jalr rd,offset(rs1)

四、重点指令详解


在基本指令集中,如下指令需要认真理解其功能,这样可以防止今后的学习中,混淆概念。

  1. lui rd,imm

该指令所加载的imm立即数是一个20bit的数据,取值范围为0-1048575,rd只有高20bit用于保存imm数据,低12bit被清0了。

由于risc-v的立即数并不能32bit,实际中我们期望直接写一个32bit的常量数值到RISC-V的寄存器中,我们应该如何实现呢?

在risc-v的汇编指令格式中,U类型指令有20bit立即数,I类型指令有12bit立即数,我们可以借助相关类型的操作指令来完成,32bit常量数据的加载。

例如:

lui  t0,(0x12345678>>12)                //此时t0=0x12345000
addi t0,t0,(0x12345678&0xfff)     //此时t0=0x12345000 0x678=0x12345678,这样我们就把0x12345678放到了t0中

看了上面的例子,有些同学可能会产生疑问,为啥我这么费劲呢,直接一条指令加载32bit不就可以了吗?想法是好的,但是RISCV没有这种功能的指令,如果有自然是可以的了。

上述代码通常也被写成如下形式:

lui  t0,%hi(0x12345678)               //此时t0=0x12345000,%hi()是编译器支持的求取32bit的高20bit的数值,
addi t0,t0,%lo(0x12345678)     //此时t0=0x12345000 0x678=0x12345678,这样我们就把0x12345678放到了t0中
                                                                 //%lo()是编译器支持的求取32bit的低12bit的数值,
  1. auipc rd,imm

该指令加载的立即数是一个20bit的数据,取值范围为0-1048575,但是该指令rd中保存的数据是

(pc) (立即数<<12)

例如:

//例1--------------------------
auipc a0,0   //如果pc当前值为0x18,执行该指令将a0数值=pc数值 0,该条指令通常用于读取pc当前数值。

//例2--------------------------
auipc t0,(0x12345678>>12)     //t0数值=0x12345000 pc数值
addi t0,t0,(0x12345678&0xfff)//t0数值=0x12345000 pc数值 0x678=0x12345678 pc数值

//以上两条指令配合其jalr指令便可以实现从pc当前地址跳转到32bit的地址空间
jalr  ra,0(t0)         //  ra数值当前pc数值 4,pc数值=t0=(0x12345678 pc数值)&~0x1,这就3条就实现了相对pc的
                                  //   32bit地址空间的跳转

以上两条lui,auipc指令,仅仅是将数据保存到rd中,并不会对pc数值产生影响

3. jal rd,imm和jalr rd,rs1,imm

1) 这两条指令都会改变PC的数值,前者pc=pc当前值 imm,后者(pc)=((rs1) imm)&~0x1;

2) rd中保存的都是(pc) 4;

3)如将rd指定为ra(返回)寄存器,那么就可实现函数的跳转返回。

因为在调用指令跳转到子函数执行完毕后,在子程序中执行MRET等操作时,ra数据会被存入pc,从而实现了从子函数,返回继续执行程序。


4.beq,bne,bge,blt,bgeu,bltu

以上指令均未显示地对PC进行操作,但它们的执行终将影响pc的数值,pc的数值为当前(pc) imm。