皋陶 发表于 2020-8-23 20:35:06

RISC-V RV32I 基本6种指令集

本帖最后由 皋陶 于 2020-8-26 12:56 编辑

RISC-V 的RV32I是最基本的指令集,包括6种,其中4种核心的类型(R/I/S/U)。本文是参考文档:The RISC-V Instruction Set Manual Volume I: Unprivileged ISA Document Version 20191213


RISC-V的指令集有以下几个特点:

[*]非常的规整,可以看到rs1和rs2都作为数据源寄存器,同时rd一直作为目标寄存器。然后这三个寄存器在6种类型指令中的位置是固定的!这使得硬件解码非常简单。
[*]立即数的最高位一直在最左边,这方便了做符号位的拓展。
[*]相比与arm,将指令放不下的立即数放进常量池然后用load去读。risc v用的是两条指令,先用U type读高20位的数据,然后再用加法加上低12位。这使得不用去访问外部存储也能处理高位宽的常数。
[*]立即数默认都是有符号数的,都需要符号拓展。
[*]B类型和S类型的差别,是B的立即数是S的立即数*2,但是为了保证第2个特点,同时保持最多的位的位置保持不变,比如指令的6到11位,仅仅改变了第6位从S的imm变为imm。这也是方便了硬件解码,但是苦逼了编译器。
[*]U类型和J类型同第五点。
[*]讲多一个,常用寄存器有一个x0,他的值一直为0,这个当你指令不需要一个寄存器就用它,或者用x0实现一些特殊的指令,比如ADDI x0, x0, 0,就是啥也不干。



接下来讲几种整数运算指令
整体就是寄存器-立即数用I类型指令,寄存器-寄存器用R类型指令。
寄存器-立即数运算

比如
ADDI rd, rs1, imm:就是REG = REG + $signed(imm);
SLTI (set less than immediate) rd, rs1, imm:就是当REG比$signed(imm)小的时候,REG为1,否则为0,有符号比较。
SLTIU跟SLTI差不多,不过是无符号数比较。
ANDI, ORI, XORI是逻辑操作,(有符号数)。ANDI rd, rs1, imm:REG = REG & imm


移位操作也是编译成I type的。移位的位数是imm,所以一次指令最多可以移动32位,跟寄存器的位宽一样。
SLLI:是逻辑左移,低位补零
SRLI:是逻辑右移, 高位补零
SRAI:是算术右移,高位补原来高位。
(这里我有个问题,原文是说The right shift type is encoded in bit 30,所以按说应该右移类型的第30位应该是1,左移是0吼,但又好像不太是。anyway)


LUI (load upper immediate): LUI rd imm。就是REG = imm。
REG = 0。就是把imm赋给rd的高20位,然后低12位补零。
一般后面加一个addi的指令,把低12位的值给上。
这样子,通过两条指令,就把一个32位的imm赋给了寄存器啦。
AUIPC (add upper immediate to pc):就是在LUI的基础上,再加上这个命令的pc地址。同样后面再加一个12位的加法,就可以寻址到任何位置的指令的地址啦。
整数的寄存器-寄存器运算指令
这种就比较简单了,都是把rs1,rs2寄存器里面的值运算一下,然后将结果给rd就完事了。


ADD就是加,SUB就是减,都不考虑溢出的哦。
SLT和SLTU分别是有符号比较和无符号数比较,rs1<rs2的话,rd为1,否则为0。
SLL, SRL, and SRA就是逻辑左,逻辑右,算术右的运算,是rs1进行移位,移动的位数是rs2的低5位。
控制指令
控制指令就是if else这种分支跳转的指令了,这个玩意复杂可以很复杂,我这里就是简单介绍一下指令。
非条件跳转

JAL:就是pc指针跳转到当前位置加上{$signed(imm),0},这是J型,你软件给说JAL rd 16,其实实现的是pc = pc + 32。然后将pc + 4这个值赋给rd存放好到时候要返回来的地址。imm是2个字节对齐的,20位就是2MB的寻址空间了,imm有符号数的哦。


JALR功能跟JAL差不多,比如JALR rd rs1 imm,那就是pc = REG +imm。然后把最低有效的位置0。
条件跳转指令

所有的条件跳转都是B类型的指令。B类型的imm都是2字节对齐的。
BEQ and BNE分别当rs1和rs2相等和不相等时,跳转。否则就不跳。
BLT and BLTU 分别是有符号数比较和无符号数比较,当rs1<rs2时候就跳,否则不跳
BGE and BGEU 分别是有符号数比较和无符号数比较,当rs1>=rs2时候就跳,否则不跳
再强调哦,跳转分支我这里只是说明一下指令,具体有一些预测分支啊,应用啊,link啊啥的。
load and store 指令
RISC V的load 和store指令也是一个特色哦,就是load和store只能通过寄存器来操作,就是一定要把地址放在在寄存器里面,然后load这个寄存器的地址读取memory里面的数据。


LOAD从mem读取一个32位的数据到rd。地址是REG + imm
STORE 是把rs2里面的数据放到地址是REG + imm
LH 从mem读取一个16位的数据,然后符号拓展到32位,再放到rd中
LHU 从mem读取一个16位的数据,然后高位补0到32位,再放到rd中
LB and LBU 同理处理8位的数据
SW, SH, and SB同理分别sore 32,16,8位的数据。
总结
RV32I是RISC V最基本的指令集类型,还有一些低功耗的呀,高性能的呀啥的,RV32I指令集的特点是特别规整,这加大了额外的编辑器的工作量,比如B指令啥的,但是却简化的硬件设计,应该还是非常值得的呀。
RV32I一共有40条指令,我这里一共介绍了33条指令,后面还有一些memory order,environment call and breakpoints,HINT的,稍稍复杂,怕讲的太误人了。我这里也不一定正确,请多指教。
本篇完,感谢关注:RISC-V单片机中文网
页: [1]
查看完整版本: RISC-V RV32I 基本6种指令集