RV32I是最基本的32位Base指令集,它支持32位寻址空间,支持字节地址访问,仅支持小端格式(little-endian,高地址高位,低地址地位),寄存器也是32位整数寄存器。RV32I指令集的目的是尽量简化硬件的实施设计,所以它只有47条指令。
在RV32I指令集架构中,包括32个通用目的寄存器,其中x0被预留为常数0,其它31个寄存器(x1-x31)是普通的通用整数寄存器。在Risc-V汇编语言中,每个通用寄存器都有一个对应的ABI名字,也就是说在汇编语言中,x1等价于ra,它们都会编译成相同的机器码。
在Risc-V架构中,要得到当前指令pc(指令在存储器中的位置,instruction program counter),可以通过AUIPC指令,把它读入到一个通用寄存器中。
寄存器 |
ABI名字(别名) |
注释 |
Saver |
x0
|
zero |
Hard-wired zero,常数0 |
|
x1 |
ra |
Return address |
caller,调用函数的指令pc |
x2 |
sp |
Stack pointer |
callee,被调用的函数指令pc |
x3 |
gp |
Global pointer |
|
x4 |
tp |
Thread pointer |
|
x5 |
t0 |
Temporary/alternate link register |
caller |
x6 |
t1 |
Temporaries |
caller |
x7 |
t2 |
Temporaries |
caller |
x8 |
s0/fp |
Saved register/frame pointer |
caller |
x9 |
s1 |
Saved register |
caller |
x10 |
a0 |
Function arguments/return values |
caller |
x11 |
a1 |
Function arguments/return values |
caller |
x12 |
a2 |
Function arguments |
caller |
x13 |
a3 |
Function arguments |
caller |
x14 |
a4 |
Function arguments |
caller |
x15 |
a5 |
Function arguments |
caller |
x16 |
a6 |
Function arguments |
caller |
x17 |
a7 |
Function arguments |
caller |
x18 |
s2 |
Saved registers |
caller |
x19 |
s3 |
Saved registers |
caller |
x20 |
s4 |
Saved registers |
caller |
x21 |
s5 |
Saved registers |
caller |
x22 |
s6 |
Saved registers |
caller |
x23 |
s7 |
Saved registers |
caller |
x24 |
s8 |
Saved registers |
caller |
x25 |
s9 |
Saved registers |
caller |
x26 |
s10 |
Saved registers |
caller |
x27 |
s11 |
Saved registers |
caller |
x28 |
t3 |
Temporaries |
caller |
x29 |
t4 |
Temporaries |
caller |
x30 |
t5 |
Temporaries |
caller |
x31 |
t6 |
Temporaries |
caller |
Base指令格式:
RV32I指令格式包括以下6种,每种指令格式都是固定的32位指令,所以指令在内存中必须4字节对齐,否则将触发异常。其中rd表示目的寄存器,rs1是源操作数寄存器1,rs2是源操作数寄存器2。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028095904793-1285678799.png)
imm表示指令中的立即数,比如imm[11:0],表示一个12位的立即数,它的高20位会符号位扩展,imm[31:12]表示一个32位的立即数,它的低12位会补0。
下图是各种指令格式扩展后的32位立即数。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028095905779-446756063.png)
RV32I整数指令集
RV32I整数指令集分为几个种类:
1.Load和store指令
Category |
Fmt |
RV32I base |
machine code(bin) |
comment |
Loads |
load byte |
I |
lb rd, rs1, imm |
[31-20,imm][19-15,rs1]000[11-7,rd]0000011
|
rd=mem[rs1+imm], 8bit数据符号位扩展后返回rd |
load half word |
I |
lh rd, rs1, imm |
[31-20,imm][19-15,rs1]001[11-7,rd]0000011
|
rd=mem[rs1+imm],16bit数据符号扩展后返回rd,应该保证地址对齐,否则产生异常 |
load word |
I |
lw rd, rs1, imm |
[31-20,imm][19-15,rs1]010[11-7,rd]0000011
|
rd=mem[rs1+imm],32bit数据返回rd,应该保证地址对齐,否则产生异常 |
load byte unsinged |
I |
lbu rd, rs1, imm |
[31-20,imm][19-15,rs1]100[11-7,rd]0000011
|
rd=mem[rs1+imm], 8bit数据高位补0后返回rd |
load half unsinged |
I |
lhu rd, rs1, imm |
[31-20,imm][19-15,rs1]101[11-7,rd]0000011
|
rd=mem[rs1+imm], 16bit数据高位补0后返回rd |
stores |
store byte |
S |
sb rs1, rs2, imm |
[31-25,imm[11-5]][24-20,rs2,][19-15,rs1]000[11-7,imm[4-0]]0100011
|
mem[rs1+imm]=rs2的低8bit数据 |
store half word |
S |
sh rs1, rs2, imm |
[31-25,imm[11-5]][24-20,rs2,][19-15,rs1]001[11-7,imm[4-0]]0100011
|
mem[rs1+imm]=rs2的低16bit数据 |
store word |
S |
sw rs1, rs2, imm |
[31-25,imm[11-5]][24-20,rs2,][19-15,rs1]010[11-7,imm[4-0]]0100011
|
mem[rs1+imm]=rs2的32bit数据 |
RV32I是一个load /store架构,所有的memory访问都是通过load/store指令,其它指令都是在寄存器之间进行运算,比如加法指令,减法指令等等。注意,装入目的寄存器如果为x0,将会产生一个异常。Load/Store指令在memory和寄存器之间传输数据,Load指令编码为I型,store指令编码为S型。计算memory地址时候,imm都会符号扩展成32位,然后和rs1相加,得到memory地址。为了提高性能,load/store指令应该尽量对齐地址,比如lw指令,应该4字节对齐访问地址,lh应该双字节对齐访问地址。根据微架构实现的不同,不对齐地址的访问可能会比较慢,而且地址对齐访问,能够确保是原子操作,不对齐的话为了读取和存储数据正确,还有进行额外的同步操作。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028105230554-886441500.png)
2.整数计算指令(算术,逻辑指令,比较指令以及移位指令)
Category |
Fmt |
RV32I base |
machine code(bin) |
comment |
arithmetric |
add |
R |
add rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]000[11-7,rd]0110011
|
rd=rs1+rs2,如果溢出,舍弃高位,保留低32bit |
add immediate |
I |
addi rd, rs1, imm |
[31-20,imm][19-15,rs1]000[11-7,rd]0010011
|
rd=rs1+imm,12bit立即数会符号位扩展,结果写回rd时如果溢出,则舍弃高位,保留低32bit |
subtract |
R |
sub rd, rs1, rs2 |
0100000[24-20,rs2][19-15,rs1]000[11-7,rd]0110011
|
rd=rs1-rs2,如果溢出,舍弃高位,保留低32bit |
load upper imm |
U |
lui rd, imm |
[31-12,imm][11-7,rd]0110111
|
load imm to high 20 bits of rd, and place 0 in low 12 bits |
add upper imm to PC |
U |
auipc rd, imm |
[31-12,imm][11-7,rd]0010111
|
pc=pc+high 20 bits imm&low 12 bits 0, then result save to rd. |
logical |
xor |
R |
xor rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]100[11-7,rd]0110011
|
rd=rs1^rs2, bitwise operation |
xor immediate |
I |
xori rd, rs1, imm |
[31-20,imm][19-15,rs1]100[11-7,rd]0010011
|
rd=rs1^imm, i符号扩展12bit数imm,结果放在rd。 |
or |
R |
or rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]110[11-7,rd]0110011
|
rd=rs1|rs2, bitwise operation |
or immediate |
I |
ori rd, rs1, imm |
[31-20,imm][19-15,rs1]110[11-7,rd]0010011
|
rd=rs1|imm, 符号扩展12bit数imm,结果放在rd。 |
and |
R |
and rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]111[11-7,rd]0110011
|
rd=rs1&rs2, bitwise operation |
and immediate |
I |
andi rd, rs1, imm |
[31-20,imm][19-15,rs1]111[11-7,rd]0010011
|
rd=rs1&imm, i符号扩展12bit数imm,结果放在rd。 |
shifts |
shift left |
R |
SLL rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]001[11-7,rd]0110011
|
rd=rs1<<rs2,rs1左移值为rs2的低5bit,低位补0 |
shift left immediate |
I |
SLLI rd, rs1, shamt |
0000000[24-20,imm][19-15,rs1]001[11-7,rd]0010011
|
rd=rs1<<shamt,rs1移动5bit立即数,低位补0 |
shift right |
R |
SRL rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]101[11-7,rd]0110011
|
rd=rs1>>rs2,高位补0,右移值为rs2中的低5位 |
shift right immediate |
I |
SRLI rd, rs1, shamt |
0000000[24-20,imm][19-15,rs1]101[11-7,rd]0010011
|
rd=r1>>shamt,高位补0,右移值为5bit立即数 |
shift right arithmetirc |
R |
SRA rd, rs1, rs2 |
0100000[24-20,rs2][19-15,rs1]101[11-7,rd]0110011
|
rd=rs1>>rs2,高位补符号位,右移值为rs2中的低5位 |
shift right arith imm |
I |
SRAI rd, rs1, shamt |
0100000[24-20,imm][19-15,rs1]101[11-7,rd]0010011
|
右移时,右边补符号位 |
compare |
set <
|
R |
slt rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]010[11-7,rd]0110011
|
if rs1< rs2, write 1 to rd, 有符号数比较 |
set < immediate
|
I |
slti rd, rs1, imm |
[31-20,imm][19-15,rs1]010[11-7,rd]0010011
|
if rs1<imm, write 1 to rd,imm会符号位扩展,所以进行有符号数比较 |
set < unsigned |
R |
sltu rd, rs1, rs2 |
0000000[24-20,rs2][19-15,rs1]011[11-7,rd]0110011
|
if rs1< rs2, write 1 to rd, 无符号数比较 |
set < imm unsigned |
I |
sltiu rd, rs1, imm |
[31-20,imm][19-15,rs1]011[11-7,rd]0010011
|
if rs1<imm, write 1 to rd,imm会符号位扩展,所以进行有符号数比较 |
计算指令在寄存器和寄存器之间,或者在寄存器和立即数之间进行算术或逻辑运算。指令格式为I,R或者U型。整数计算指令不会产生异常。我们能够通过ADDI x0, x0, 0来模拟NOP指令,该指令除了改变pc值外,不会改变其它任何用户状态。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028105231005-247232269.png)
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028105231552-1047505397.png)
3. 控制指令,包括无条件跳转指令和条件跳转指令
Category |
Fmt |
RV32I base |
machine code(bin) |
comment |
branch |
branch = |
SB |
beq rs1, rs2,imm |
[31-25, imm[12][10:5]][24-20, rs2][19-15, rs1]000[11-7, imm[4:1][11]]1100011 |
if(rs1==rs2) pc=pc+imm*2,跳转指令 |
branch <> |
SB |
bne rs1, rs2,imm |
[31-25, imm[12][10:5]][24-20, rs2][19-15, rs1]001[11-7, imm[4:1][11]]1100011 |
if(rs1!=rs2) pc=pc+imm*2,跳转指令 |
branch < |
SB |
blt rs1, rs2,imm |
[31-25, imm[12][10:5]][24-20, rs2][19-15, rs1]100[11-7, imm[4:1][11]]1100011 |
if(rs1<rs2) pc=pc+imm*2,跳转指令 , 有符号数 |
branch >= |
SB |
bge rs1, rs2,imm |
[31-25, imm[12][10:5]][24-20, rs2][19-15, rs1]101[11-7, imm[4:1][11]]1100011 |
if(rs1>rs2) pc=pc+imm*2,跳转指令,有符号数 |
branch < unsigned |
SB |
bltu rs1, rs2,imm |
[31-25, imm[12][10:5]][24-20, rs2][19-15, rs1]110[11-7, imm[4:1][11]]1100011 |
if(rs1<rs2) pc=pc+imm*2,跳转指令,无符号数 |
branch >=unsigned |
SB |
bgeu rs1, rs2,imm |
[31-25, imm[12][10:5]][24-20, rs2][19-15, rs1]111[11-7, imm[4:1][11]]1100011 |
if(rs1>rs2) pc=pc+imm*2,跳转指令,无符号数 |
jump and link |
J&L |
UJ |
JAL rd, imm |
[31-12, imm[20][10:1][11][19:12]][11-7,rd]1101111 |
pc=pc+imm*2, 现在的pc+4写入rd寄存器 |
Jump and link register |
UJ |
JALR, rd, rs1, imm |
[31-20,imm[11:0]][19-15,rs1]000[11-7,rd]1100111
|
pc=rs1+imm, pc+4写入到rd寄存器 |
JAL指令使用20位的立即数作为偏移,所以它仅可以跳转到前后1M的地址空间。JALR使用12位的立即数,其值和rs1中的值相加,得到偏移量。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028142618500-67915275.png)
4. 同步指令
Category |
Fmt |
RV32I base |
machine code(bin) |
comment |
Synch |
synch thread |
I |
FENCE iorw,iorw |
0000[27-24,pred][23-20,succ]00000000000000001111
|
pred/succ分别表示fence之前和之后的 iorw |
synch instr and data |
I |
FENCE.i |
00000000000000000001000000001111
|
用于同步指令和数据流 |
Risc-V在多个hart(硬件线程)之间使用的是松散一致性模型,所以需要存储器fence指令。
fence指令能够保证存储器访问的执行顺序。在fence指令之前的所有存储器访问指令,比该fence之后的所有数据存储器访问指令先执行。
Risc-V架构将数据存储器的地址空间分为设备IO(device IO)和普通存储器空间,因此其读写访问分为四种类型:
I:设备读(device-input)
O:设备写(device-ouput)
R:存储器读(memory-reads)
W:存储器写(memory-writes)
PI/PO/PR/PW,分别f表示ence指令之前的四种读写访问类型,SI/SO/SR/SW分别表示fence指令之后的四种读写访问类型。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028184756811-1109210682.png)
fence.i指令用于同步指令和数据流。如果程序中添加一个fence.i,则该指令能够保证fence.i之前所有指令的访存结果能被fence.i之后的所有指令访问到。通常说来,处理器的微架构硬件实现时,一旦遇到一条fence.i指令,便会先等到之前的所有访存指令执行完,然后冲刷流水线,包括Icache,使其后的所有指令,能够重新取指,从而得到最新的值。
注意:fence.i只能保证同一个hart(硬件线程)执行的指令流和数据流顺序,不能保证多个hart之间的指令流和数据流访问。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028142620362-337557247.png)
5.控制状态寄存器指令
Category |
Fmt |
RV32I base |
machine code(bin) |
comment |
CSR |
CSRRW |
I |
CSRRW |
[31-20,csr][19-15, rs1]001[11-7,rd]1110011 |
csrrw rd, csr, rs1, 将csr索引的寄存器值读出,写到rd,将rs1中的值写入csr |
CSRRS |
I |
CSRRS |
[31-20,csr][19-15, rs1]010[11-7,rd]1110011 |
csrrs rd, csr, rs1, 将csr索引寄存器值读出写到rd,以rs1为参考,如果rs1中某位为1,则置位对应的csr寄存器位,其他位不受影响。 |
CSRRC |
I |
CSRRC |
[31-20,csr][19-15, rs1]011[11-7,rd]1110011 |
csrrc rd, csr, rs1, 将csr索引寄存器值读出写到rd,以rs1为参考,如果rs1中某位为1,则清零对应的csr寄存器位,其他位不受影响。 |
CSRRWI |
I |
CSRRWI |
[31-20,csr][19-15, zimm]101[11-7,rd]1110011 |
csrrwi rd, csr, imm[4:0], 将csr索引寄存器值读出写到rd,5位立即数的值写入csr,高位补0 |
CSRRSI |
I |
CSRRSI |
[31-20,csr][19-15, zimm]110[11-7,rd]1110011 |
csrrsi rd, csr, imm[4:0], 将csr索引寄存器值读出写到rd,5位立即数的值(高位补0)逐位比较,如果为1,则置位csr相应位 |
CSRRCI |
I |
CSRRCI |
[31-20,csr][19-15, zimm]111[11-7,rd]1110011 |
csrrci rd, csr, imm[4:0], 将csr索引寄存器值读出写到rd,5位立即数的值(高位补0)逐位比较,如果为1,则清零csr相应位 |
CSR指令包括上面表中的6条指令,通常在用户层,不会访问写csr的指令。
这些CSR指令实现都是原子操作。对于CSRRW和CSRRWI来说,如果rd为x0,则不会有任何写操作。对于CSRS和CSRRC指令,如果rs1为x0,则不会有任何写操作。对于CSRRSI和CSRRCI来说,如果立即数为0,则不会有任何写操作。
用CSR指令可以实现一些伪指令,例如读取始终频率等。详细见伪指令列表。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028175944687-1878007520.png)
6.环境调用和断点指令
Category |
Fmt |
RV32I base |
machine code(bin) |
comment |
System |
system call |
I |
ECALL |
00000000000000000000000001110011
|
用于生成环境调用异常,产生异常时候,mepc寄存器将保存当前指令pc值。 |
system break |
I |
EBREAK |
00000000000100000000000001110011
|
生成断点,产生异常时候,mepc寄存器将保存当前指令pc值。 |
这两条指令能够产生环境调用异常和生成断点异常,产生异常时候,当前指令的pc值被写入mepc寄存器。
这两条指令在调试代码时候有用。
![image image](https://img2018.cnblogs.com/blog/361409/201810/361409-20181028142621475-1097816978.png)