文章目录

RISC-V处理器的设计与实现(一)—— 基本指令集_Patarw_Li的博客-CSDN博客

RISC-V处理器的设计与实现(二)—— CPU框架设计_Patarw_Li的博客-CSDN博客

RISC-V处理器的设计与实现(三)—— 上板验证_Patarw_Li的博客-CSDN博客


本人小白一枚,在学习FPGA的过程中偶然刷到了tinyriscv这个开源项目,并且自己对计算机体系结构的知识也很感兴趣,所以想参考这个开源项目做一个基于RISC-V指令集的CPU,下面是tinyriscv这个开源项目的地址,本项目很多思路和设计都参考了tinyriscv开源项目:

从零开始写RISC-V处理器 | liangkangnan的博客

tinyriscv: 一个从零开始写的极简、非常易懂的RISC-V处理器核。

在设计RISC-V处理器之前,我们首先要做的事就是指令集的选取,指令集是一个CPU的基石,要实现CPU 计算和控制功能,就必须定义好一系列与硬件电路相匹配的指令系统。下面是RISC-V官方给出的指令集架构:

因为是第一次做,所以我们选择简单一点的32位基本指令集,也就是RV32I 基本指令集(目前增加了RV32M扩展)

RV32I 基本指令集有6种格式,分别是:

目的 类型
用于寄存器-寄存器操作 R 类型指令
用于短立即数和访存 load 操作 I 型指令
用于访存 store 操作 S 型指令
用于条件跳转操作 SB 类型指令
用于长立即数 U 型指令
用于无条件跳转 UJ 型指令

RV32I 基本指令集有47条指令,如下图:

本项目打算先实现前37条(黄色高亮部分)基础指令,下面介绍了一下这些指令的用法,这里给大家先推荐一个好用的工具叫Ripes,可以到上面运行riscv汇编指令,并且可以把对应的指令转成二进制机器码,方便我们后面的仿真:

Releases · mortbopet/Ripes · GitHub

下面我们开始指令的介绍。

RV32I 基本指令集

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指令地址PC加上符号位拓展的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,-1
sb x1,0,x0

 执行结果如下,可以看到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寄存器。

RV32M 扩展指令集

1. MUL

语法:mul rd, rs1, rs2,作用是将rs1与rs2相乘,结果的低32位写入rd寄存器。

2. MULH

语法:mulh rd, rs1, rs2,作用是将rs1与rs2相乘,结果的高32位写入rd寄存器。

3. MULHSU

语法:mulhsu rd, rs1, rs2,作用是将rs1与rs2相乘,其中rs1为有符号数,rs2为无符号数,结果的高32位写入rd寄存器。

4. MULHU

语法:mulhu rd, rs1, rs2,作用是将rs1与rs2相乘,其中rs1、rs2均为无符号数,结果的高32位写入rd寄存器。

5. DIV

​​​​​​​语法:div rd, rs1, rs2,作用是将rs1与rs2相除,结果的写入rd寄存器。

6. DIVU

语法:divu rd, rs1, rs2,作用是将rs1与rs2相除,其中rs1、rs2均为无符号数,结果的写入rd寄存器。

7. REM

​​​​​​​语法:rem rd, rs1, rs2,作用是将rs1与rs2相除,结果的余数写入rd寄存器。

8. REMU

​​​​​​​语法:remu rd, rs1, rs2,作用是将rs1与rs2相除,其中rs1、rs2均为无符号数,结果的余数写入rd寄存器。

以上就是目前所用到的指令了,之后如果添加新的指令会在这里更新。

之后我会还会出新文章更新具体设计部分的内容,大家看了如果觉得有帮助请点个小小的赞吧~

如果遇到问题也欢迎加群 892873718 交流~