本人小白一枚,在学习FPGA的过程中偶然刷到了tinyriscv这个开源项目,并且自己对计算机体系结构的知识也很感兴趣,所以想参考这个开源项目做一个基于RISC-V指令集的CPU,下面是tinyriscv这个开源项目的地址,本项目很多思路和设计都参考了tinyriscv开源项目:
从零开始写RISC-V处理器 | liangkangnan的博客
tinyriscv: 一个从零开始写的极简、非常易懂的RISC-V处理器核。
在设计RISC-V处理器之前,我们首先要做的事就是指令集的选取,指令集是一个CPU的基石,要实现CPU 计算和控制功能,就必须定义好一系列与硬件电路相匹配的指令系统。下面是RISC-V官方给出的指令集架构:
因为是第一次做,所以我们选择简单一点的32位基本指令集,也就是RV32I 基本指令集,扩展指令我们到后面再去实现。
RV32I 基本指令集有6种格式,分别是:
目的 | 类型 |
---|---|
用于寄存器-寄存器操作 | R 类型指令 |
用于短立即数和访存 load 操作 | I 型指令 |
用于访存 store 操作 | S 型指令 |
用于条件跳转操作 | SB 类型指令 |
用于长立即数 | U 型指令 |
用于无条件跳转 | UJ 型指令 |
RV32I 基本指令集有47条指令,如下图:
本项目打算先实现前37条(黄色高亮部分)基础指令,下面介绍了一下这些指令的用法,这里给大家先推荐一个好用的工具叫Ripes,可以到上面运行riscv汇编指令,并且可以把对应的指令转成二进制机器码,方便我们后面的仿真:
Releases · mortbopet/Ripes · GitHub
下面我们开始指令的介绍。
1. LUI
语法:lui rd, imm,作用是将立即数imm逻辑左移12位,结果写入rd寄存器(立即数是无符号数)。
我们在ripes运行如下例子来看看结果:
lui x1,1
lui x2,255
如下是运行结果,可以看到寄存器内的数值都是立即数左移12位的结果(16进制):
2. AUIPC
语法:auipc rd, imm,作用是将立即数imm逻辑左移12位,然后加上当前指令PC的值,结果写入rd寄存器(立即数是无符号数)。
我们可以通过将立即数的值设置为0来计算PC的值:
auipc x1,0
auipc x2,0
auipc x3,0
auipc x4,4
运行结果如下:
3. JAL(无条件跳转指令)
语法:jal rd, imm,作用是将PC的值加上4,结果写入rd寄存器,rd默认为x1,同时将PC的值设置为当前jal指令地址加上符号位拓展的imm。
我们可以通过这个指令来跳到指定的指令执行:
auipc x2,0
auipc x3,0
auipc x4,4
jal x1,-12
可以看到上述指令会一直循环执行。
4. JALR(无条件跳转指令,寄存器相对寻址)
语法:jalr rd, rs1,imm,作用是将PC的值加上4,结果写入rd寄存器,rd默认为x1,同时将当前PC值设置为寄存器rs1的值加上符号位拓展的imm。
功能和JAL指令一样,不过计算跳转地址的方式不一样,JAL指令跳转地址为PC+imm,而JALR指令跳转地址为寄存器rs1内容+imm:
addi x1,x0,8
auipc x2,0
auipc x3,0
auipc x4,4
jalr x5,x1,4
因为第4条指令的地址为12,寄存器x1的内容8+立即数4等于12,所以可以看到程序跳转到第四条指令执行:
5. BEQ(分支跳转指令,相等时跳转)
语法:beq rs1, rs2, imm,作用是如果rs1的值等于rs2的值,则将PC设置为符号当前指令beq地址+符号位拓展的立即数imm。
addi x1,x0,1
addi x2,x0,2
add x1,x1,x1
beq x1,x2,-4
执行结果如下,可以看到第三条add指令执行了两次:
6. BNE(分支跳转指令,不等时跳转)
语法:bne rs1, rs2, imm,作用是如果rs1的值不等于rs2的值,则将PC设置为符号当前指令bne地址+符号位拓展的立即数imm。
addi x1,x0,1
addi x2,x0,8
add x1,x1,x1
bne x1,x2,-4
执行结果如下,可以看到第三条add指令执行了三次:
7. BLT(分支跳转指令,小于时跳转)
语法:blt rs1, rs2, imm,作用是如果rs1的值小于rs2的值,则将PC设置为符号当前指令blt地址+符号位拓展的立即数imm。
addi x1,x0,1
addi x2,x0,8
add x1,x1,x1
blt x1,x2,-4
执行结果如下,可以看到第三条add指令执行了三次:
8. BGE(分支跳转指令,大于等于时跳转)
语法:bge rs1, rs2, imm,作用是如果rs1的值大于或等于rs2的值,则将PC设置为符号当前指令bge地址+符号位拓展的立即数imm。
addi x1,x0,10
addi x2,x0,8
addi x1,x1,-1
bge x1,x2,-4
执行结果如下,可以看到第三条addi指令执行了三次:
9. BLTU(分支跳转指令,无符号数比较,小于时跳转)
语法:bltu rs1, rs2, imm,作用是如果rs1的值小于rs2的值(rs1和rs2为无符号数),则将PC设置为符号当前指令bltu地址+符号位拓展的imm。
10. BGEU(分支跳转指令,无符号数比较,大于或等于时跳转)
语法:bgeu rs1, rs2, imm,作用是如果rs1的值大于或等于rs2的值(rs1和rs2为无符号数),则将PC设置为符号当前指令bgeu地址+符号位拓展的imm。
BLTU、BGEU和BLT、BGE的区别在于一个是无符号数比较,一个是有符号数比较。
如下两个示例,执行结果是不一样的:
addi x1,x0,-1
addi x2,x0,8
addi x1,x1,-1
bge x1,x2,-4
addi x1,x0,-1
addi x2,x0,8
addi x1,x1,-1
bgeu x1,x2,-4
11. LB(访存指令,一字节)
语法:lb rd, imm, rs1,作用是从处读rs1加上imm的地址取一个字节的内容,并将该内容经符号位扩展后写入rd寄存器。
addi x1,x0,-1
lb x3,1,x1
执行结果如下,可以看到memory内第一个字节的数据被符号位扩展后送入寄存器x3:
12. LH(访存指令,两字节)
语法:lh rd, imm, rs1,作用是从处读rs1加上imm的地址取两个字节的内容,并将该内容经符号位扩展后写入rd寄存器。
addi x1,x0,0
lh x3,4,x1
执行结果如下,可以看到memory内地址4的两个字节数据被符号位拓展后送入x3:
13. LW(访存指令,四字节)
语法:lw rd, imm, rs1,作用是从处读rs1加上imm的地址取四个字节的内容,并将该内容写入rd寄存器(因为四字节位数和寄存器位数相同,所以无需符号位拓展)。
addi x1,x0,0
lw x3,4,x1
执行结果如下,可以看到memory内地址4的四个字节数据被送入x3:
14. LBU(访存指令,一字节,无需符号位拓展)
语法:lbu rd, imm, rs1,作用是从处读rs1加上imm的地址取一个字节的内容,并将该内容经高位补0后写入rd寄存器。
15. LHU(访存指令,两字节,无需符号位拓展)
语法:lhu rd, imm, rs1,作用是从处读rs1加上imm的地址取两个字节的内容,并将该内容经高位补0后写入rd寄存器。
16. SB(访存指令,一字节)
语法:sb rs2, imm, rs1,作用是将rs2的低八位写入rs1加上imm的地址处。
addi x1,x0,-1
sb x1,0,x0
执行结果如下:
17. SH(访存指令,两字节)
语法:sh rs2, imm, rs1,作用是将rs2的低16位写入rs1加上imm的地址处。
addi x1,x0,-1
sh x1,0,x0
执行结果如下:
18. SW(访存指令,四字节)
语法:sw rs2, imm, rs1,作用是将rs2写入rs1加上imm的地址处。
addi x1,x0,-1
sw x1,0,x0
19. ADDI
语法:addi rd, rs1, imm,作用是将符号扩展的立即数imm的值加上rs1的值,结果写入rd寄存器,忽略算术溢出。
addi x1,x0,-1
addi x2,x1,2
执行结果如下:
20. SLTI(小于置1)
语法:slti rd, rs1, imm,作用是将符号扩展的立即数imm的值与rs1的值比较(有符号数比较),如果rs1 < imm,则向rd寄存器写1,否则写0。
addi x1,x0,-1
slti x2,x1,2
执行结果如下:
21. SLTIU(小于置1,无符号比较)
语法:sltiu rd, rs1, imm,作用是将高位补0的立即数imm的值与rs1的值比较(无符号数比较),如果rs1 < imm,则向rd寄存器写1,否则写0。
addi x1,x0,-1
sltiu x2,x1,2
执行结果如下,可以看到x2结果变为了0:
22. XORI(异或)
语法:xori rd, rs1, imm,作用是将rs1与符号位扩展的imm按位异或,结果写入rd寄存器。
23. ORI
语法:ori rd, rs1, imm,作用是将rs1与符号位扩展的imm按位或,结果写入rd寄存器。
24. ANDI
语法:andi rd, rs1, imm,作用是将rs1与符号位扩展的imm按位与,结果写入rd寄存器。
25. SLLI(逻辑左移)
语法:slli rd, rs1, shamt,作用是将rs1左移shamt位,空出的位补0,结果写入rd寄存器。
26. SRLI(逻辑右移)
语法:srli rd, rs1, shamt,作用是将rs1右移shamt位,空出的位补0,结果写入rd寄存器。
27. SRAI(算术右移)
语法:srai rd, rs1, shamt,作用是将rs1右移shamt位,空出的位用rs1的最高位补充,结果写入rd寄存器。
28. ADD
语法:add rd, rs1, rs2,作用是将rs1寄存器的值加上rs2寄存器的值,然后将结果写入rd寄存器里,忽略算术溢出。
29. SUB
语法:sub rd, rs1, rs2,作用是将rs1寄存器的值减去rs2寄存器的值,然后将结果写入rd寄存器里,忽略算术溢出。
30. SLL
语法:sll rd, rs1, rs2,作用是将rs1左移rs2位(低5位有效),空出的位补0,结果写入rd寄存器。
31. SLT
语法:slt rd, rs1, rs2,作用是将rs1的值与rs2的值比较(有符号数比较),如果rs1的值更小,则向rd寄存器写1,否则写0。
32. SLTU
语法:sltu rd, rs1, rs2,作用是将rs1的值与rs2的值比较(无符号数比较),如果rs1的值更小,则向rd寄存器写1,否则写0。
33. XOR
语法:xor rd, rs1, rs2,作用是将rs1与rs2按位异或,结果写入rd寄存器。
34. SRL
语法:srl rd, rs1, rs2,作用是将rs1右移rs2位(低5位有效),空出的位补0,结果写入rd寄存器。
35. SRA
语法:sra rd, rs1, rs2,作用是将rs1右移rs2位(低5位有效),空出的位用rs1的最高位补充,结果写入rd寄存器。
36. OR
语法:or rd, rs1, rs2,作用是将rs1与rs2按位或,结果写入rd寄存器。
37. AND
语法:and rd, rs1, rs2,作用是将rs1与rs2按位与,结果写入rd寄存器。
以上就是目前所用到的指令了,之后如果添加新的指令会在这里更新。
之后我会还会出新文章更新具体设计部分的内容,大家看了如果觉得有帮助请点个小小的赞吧~