指令系统:计算机语言中的单词成为指令,而这个词汇表就是指令系统。
指令集架构(ISA)
x86:传统pc市场的主流,善于处理大数据。
ARM:移动端设备(手机)市场,处理快数据为主。
RISC-V:当需要同时兼顾数据传输速度和传输量时,RISC-V更有优势。
更重要的是RISC-V是开源的。
主要包括:算术运算、逻辑运算、数据传输、移位操作、条件分支、无条件跳转。
数据存储模式:大端存储,小端存储。
大端:高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
小端:低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
设计原则:简单源于规整、优秀的设计需要适当的折中、更少则更快(寄存器数量太多可能会增加时钟周期)。
RISC-V常见指令表示:R型,I型,S型,B型,J型指令
R型:一般用于寄存器-寄存器操作。
I型:一般用于立即数访问和load操作。
S型:用于访存store操作。
B型:用于条件跳转操作。
J型:用于无条件操作。
funct7(7bit) | rs2(5bit) | rs1(5bit) | funct3(3bit) | rd(5bit) | opcode(7bit) |
funct7:操作码字段。
funct3:操作码字段。
rs2:第二个源操作数寄存器。
rs1:第一个源操作数寄存器。
rd:目的操作数寄存器,存放操作结果。
opcode:(操作码)指令的基本操作。
下面以add x9,x20,x21为例介绍。
0000000 | 10101 | 10100 | 000 | 01001 | 0110011 |
01001:9 10100:20 10101:21
通过funct7,funct3,opcode来识别是一个R型加法指令
将x20寄存器中的值加上x21寄存器中的值相加的结果存放到x9寄存器中。
immediate(12bit) | rs1(5bit) | funct3(3bit) | rd(5bit) | opcode(7bit) |
注意:计算机中是以补码形式存储immediate;12位立即数。立即数范围-2^11到((2^11)-1)(-2048到2047)
下面以lb x9,64(x22)介绍
000001000000 |
10110 |
000 |
01001 |
0000011 |
000001000000 :64 10110:22 01001:9
解释:将rs1地址偏移立即数位空间中的值存储到rd所指的寄存器空间中。
immediate[11:5](7bit) | rs2(5bit) | rs1(5bit) | funct3(3bit) | immediate[4:0](5bit) | opcode(7bit) |
个人理解这里的立即数为何这样存储:为了规整,将原本的rd用来存储立即数的低5位。
下面以sb x5,40(x6)介绍
0000001 |
00101 |
00110 |
000 | 01000 |
0100011 |
000000101000:40 00101:5 00110:6
解释:将rs2寄存器中的值存储到rs1寄存器偏移立即数位地址中去。
immediate[12,[10:5]](7bit) | rs2(5bit) | rs1(5bit) | funct3(3bit) | immediate[[4:1],11](5bit) | opcode(7bit) |
注意:13位立即数,末尾始终为0;
imm[12] | imm[11] | imm[10:5] | imm[4:1] | 0 |
下面以beq x5,x6,100为例
0000011 |
00110 |
00101 |
000 |
00100 |
1100011 |
0 | 0 | 000011 | 0010 | 0 |
注意在使用b型指令时,代码末尾别忘了补个0(或者获取的立即数别忘了左移一位)。解释:如果rs1和rs2寄存器中的值相等,则pc跳转到pc+immediate位置去,这是要重建流水线的。
immediate[20,[10:1],11,[19:12]] (20bit) |
rd (5bit) |
opcode (7bit |
J型立即数(20位)
immediate[20] immediate[19:12] immediate[11] immediate[10:1] 0
下面以jal x1,100为例
jal x1,100
00000110010000000000
00001
1101111
0 | 00000000 | 0 | 0000110010 | 0 |
写代码时,末尾补0别忘了
解释:将pc+4存储到rd寄存器中,同时pc跳转到pc+立即数位置去。
immediate[31:12] | rd | opcode |
vfd
与J型区别在于立即数放的位置
下面以lui x1,0xf0000为例
11110000000000000000 |
00001 |
0110111 |
解释:rd寄存器存储立即数 x1=0xf0000;
包括addi,slti,sltiu,add,sub,slt,sltu。
slt比较指令 指令后边加i 一般表示是指令中有立即数,而u表示有无符号。
immediate[11:0] | rs1 | 000 | rd | 0010011 | addi(I型) | |
immediate[11:0] | rs1 | 010 | rd | 0010011 | slti(I型) | |
immediate[11:0] | rs1 | 011 | rd | 0010011 | sltiu(I型) | |
0000000 | rs2 | rs1 | 000 | rd | 0110011 | add(R型) |
0100000 | rs2 | rs1 | 000 | rd | 0110011 | sub(R型) |
0000000 | rs2 | rs1 | 010 | rd | 0110011 | slt(R型) |
0000000 | rs2 | rs1 | 011 | rd | 0110011 | sltu(R型) |
包括slli,srli,srai,sll,srl,sra。
注意立即数移位指令中立即数(shamt)的范围:(这里立即数是5位的立即数)具体如下表:
0000000 | shamt | rs1 | 001 | rd | 0010011 | slli(I型) |
0000000 | shamt | rs1 | 101 | rd | 0010011 | srli(I型) |
0100000 | shamt | rs1 | 101 | rd | 0010011 | srai(I型) |
0000000 | rs2 | rs1 | 001 | rd | 0110011 | sll(R型) |
0000000 | rs2 | rs1 | 101 | rd | 0110011 | srl(R型) |
0100000 | rs2 | rs1 | 101 | rd | 0110011 | sral(R型) |
注意逻辑右移和算术右移是由区别的。
逻辑右移:数据向右移立即数位,空位填0。
算术右移:数据向右移立即数位,空位填最高位(符号位)。
为了区别这两个机器码的第30位用来作为判断。具体仔细观察上表。
包括xori ,ori,andi,xor,or,and。
imm[11:0] | rs1 | 100 | rd | 0010011 | xori(I型) | |
imm[11:0] | rs1 | 110 | rd | 0010011 | ori(I型) | |
imm[11:0] | rs1 | 111 | rd | 0010011 | andi(I型) | |
0000000 | rs2 | rs1 | 100 | rd | 0110011 | xor(R型) |
0000000 | rs2 | rs1 | 110 | rd | 0110011 | or(R型) |
0000000 | rs2 | rs1 | 111 | rd | 0110011 | and(R型) |
小总结:算术运算,逻辑运算,移位运算这些基本运算指令一般都为R型或者I型指令
包括lb,ln,lw,lbu,lhu,sb,sh,sw。
作用就是将数据从寄存器中取出来,或者存储到寄存器中去,不同在于每次取出多少(半字,字等)。
imm[11:0] | rs1 | 000 | rd | 0000011 | lb(I型) | |
imm[11:0] | rs1 | 001 | rd | 0000011 | lh(I型) | |
imm[11:0] | rs1 | 010 | rd | 0000011 | lw(I型) | |
imm[11:0] | rs1 | 100 | rd | 0000011 | lbu(I型) | |
imm[11:0] | rs1 | 101 | rd | 0000011 | lhu(I型) | |
imm[11:5] | rs2 | rs1 | 000 | imm[4:0] | 0100011 | sb(S型) |
imm[11:5] | rs2 | rs1 | 001 | imm[4:0] | 0100011 | sh(S型) |
imm[11:5] | rs2 | rs1 | 010 | imm[4:0] | 0100011 | sw(S型) |
主要是一些跳转指令:beg,bne,blt,bge,bltu,bgeu,jalr,jal。
imm[12] | imm[10:5] | rs2 | rs1 | 000 | imm[4:1] | imm[11] | 1100011 | beq(B型) |
imm[12] | imm[10:5] | rs2 | rs1 | 001 | imm[4:1] | imm[11] | 1100011 | bne(B型) |
imm[12] | imm[10:5] | rs2 | rs1 | 100 | imm[4:1] | imm[11] | 1100011 | blt(B型) |
imm[12] | imm[10:5] | rs2 | rs1 | 101 | imm[4:1] | imm[11] | 1100011 | bge(B型) |
imm[12] | imm[10:5] | rs2 | rs1 | 110 | imm[4:1] | imm[11] | 1100011 | bltu(B型) |
imm[12] | imm[10:5] | rs2 | rs1 | 111 | imm[4:1] | imm[11] | 1100011 | bgeu(B型) |
imm[11:0] | rs1 | 000 | rd | 1100011 | jalr(I型) | |||
imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd | 1100011 | jal(J型) |
注意上述立即数存储方式,在上面的B型指令讲解中有提到。
资源里有我使用FPGA写的一个基于RISC-V的单周期CPU。目前不具备流水线,后续准备写一个5级流水的单周期处理器,同样会发布到博客上。希望大家评论给出意见。