Chisel实战之单周期RISC-V处理器实现(上)——需求分析和初步设计

需求分析

首先明确我们要做的是什么,这个在标题里面已经说明了,我们要做的是一个单周期RISC-V处理器

但光是个短语不足以支撑我们开展项目,我们需要对项目目标做进一步的明确,也就是需求分析。

关于指令集架构(ISA)

设计一个处理器的依据是指令系统规范,也就是ISA的规范,不严谨地来说就是该指令集架构的机器语言的规范,即计算机软件和硬件的接口。而设计处理器是在ISA规范的基础上,对微体系结构进行设计,所以经典的教材《计算机体系结构:量化研究方法》中就将计算机体系结构描述为指令集架构(ISA)和微体系结构的结合。

因此,我们第一步就是要明确,我们这个项目支持的指令系统规范是什么!

既然要做一个RISC-V处理器,那必然是要支持RISC-V指令集,我们可以在官网找到该指令集的规范文件:

Specifications - RISC-V International (riscv.org)

指令集规范中包含了非特权指令集(当前版本规范为Unprivileged Spec v. 20191213)、特权指令集(当前版本规范为Privileged Spec v. 20211203),以及一些仍然处于其他阶段的扩展规范。

我们这个项目的目标很简单,不需要支持特权指令,更不需要支持拓展指令(比如向量拓展、位操作拓展等),仅需要支持非特权指令集就行了,而且是它的一个子集

非特权指令集有以下基本子集:

基本集 说明 版本 状态
RVWMO WMO即Weak Memory Ordering,是RISC-V的内存一致性模型 2.0 正式批准
RV32I 最基本的RISC-V 32位整数指令集 2.1 正式批准
RV64I 最基本的RISC-V 64位整数指令集,可以看作是RV32I到64位的拓展 2.1 正式批准
RV32E 面向嵌入式微控制器的基本的RISC-V 32位整数指令集,可以看作是RV32I的精简 1.9 草案
RV128I 最基本的RISC-V 128位整数指令集,可以看作是RV32I、RV64I到128位的拓展 1.7 草案

既然是基本子集,那就必须得有一个,我们的单周期处理器是单线程的、顺序执行的,因此不需要考虑内存一致性模型。我们作为例子也只需要实现最简单的32位版本,因此考虑将RV32I作为我们项目的基本指令集。而RV32E虽然是RV32I的精简,但仍然处于草案阶段,就也不考虑了。

注意:这里的xx位指的是地址空间的位数。

还有一些拓展指令子集:

拓展集 说明 版本 状态
M 整数乘法、除法拓展 2.0 正式批准
A 原子指令拓展 2.1 正式批准
F 单精度浮点数拓展 2.2 正式批准
D 双精度浮点数拓展 2.2 正式批准
Q 四精度浮点数拓展 2.2 正式批准
C 压缩指令拓展 2.0 正式批准
Counters 计数器、定时器、性能计数器拓展 2.0 草案
L 十进制浮点数拓展 0.0 草案
B 位操作拓展 0.0 草案
J 对动态转译语言的支持拓展,动态转译也叫JIT,此拓展用于支持动态检查和垃圾回收 0.0 草案
T 事务性内存操作拓展 0.0 草案
P Packed-SIMD指令拓展 0.2 草案
V 向量拓展 0.7 草案
Zicsr 控制和状态寄存器拓展 2.0 正式批准
Zifencei 指令抓取栅栏拓展 2.0 正式批准
Zam 非对齐原子内存操作拓展 0.1 草案
Ztso RVTSO(Total Store Ordering)内存一致性模型拓展,是RVWMO的变体 0.1 草案

RV32I加上A拓展就可以支持操作系统了,但我们不需要,其他拓展更是不需要了。

我们可以根据需求选取需要的基本子集和拓展集,实现符合应用场景的处理器。由于我们的需求很简单,那么自然我们经过上面的分析,就可以得到结论:

我们只需要支持RV32I基本指令集!不需要其他任何拓展!

当然了RV32I中的指令我们也并非都需要,在后续的实现中我们还会进行少许的取舍。

关于微体系结构(Microarchitecture)

CPU的设计无非只有两部分,一个是数据通路,另一个就是逻辑控制,不管如何我们先确定我们在微体系结构上的需求。

如果你学习过《计算机体系结构:量化研究方法》或其他类似的教材,那肯定知道很多现代处理器中常见的技术,比如Cache、流水线、分支预测、SIMD等等,想想就让人害怕。不过好消息是我们这个项目暂时不涉及这些,不信我们先捋一捋:

  1. 内存层级方面:现代处理器都有Cache之类的东西,用来构成内存层级,然后用一些复杂的策略来保证Cache的命中率,而我们的项目中不需要内存层级,直接从内存访问指令、数据啥的就行;
  2. 指令集并行(ILP)方面:这里引入了流水线技术,紧接着为了解决数据冒险,引入了转发技术和动态调度技术(比如记分牌算法和Tomasulo算法),跟着一起出现的还有分支预测那些,另一方面引入了多发射技术,似乎越来越超纲了,但是我们不需要,我们是单周期CPU,没有流水线,而且我们一次只执行一条指令,也没有多发射,根本就没有指令集并行;
  3. 数据级并行(DLP)方面:显然我们不需要,因为我们不支持向量拓展,跳过;
  4. 线程级并行(TLP)方面:我们是单核CPU,不支持多线程,更不存在线程之间共享内存,也没有Cache啥的,因此我们同样也不需要这个,复杂的内存一致性模型完全不用考虑;

还有什么地址转换啥的,我们也不需要

捋完了可以发现,我们什么优化技术都不需要用上,简简单单就实现一个朴素的单周期RISC-V处理器就行了!

初步设计

需求分析结束之后,就可以开始我们的初步设计了!

RV32I指令集分析

通过上面的分析,我们只需要把RV32I中我们需要的指令给支持了就行了,那么我们从分析RV32I中的指令开始

RV32I指令集指令有6种类型:

其中:

  1. R类型即寄存器(Register)类型,有三个操作数,两个源操作数均来自寄存器(rs1和rs2),目的操作数为寄存器(rd);
  2. I类型即立即数(Immediate)类型,有三个操作数,两个源操作数分别来自立即数(imm)和寄存器(rs1),目的操作数为寄存器(rd);
  3. S类型即存储(Store)类型,有三个操作数,均为源操作数,其中寄存器rs1和立即数imm运算得到存储的地址,rs2寄存器的值为被存储的数;
  4. B类型即分支(Branch)类型,有三个操作数,均为源操作数,其中两个寄存器(rs1和rs2)的值用于比较,立即数imm的值为分支目的地址的偏移量;
  5. U类型即无符号立即数(Unsigned immediate)类型,有两个操作数,立即数imm为源操作数,rd为目的操作数,此指令用于将立即数加载到指定寄存器rd;
  6. J类型即跳转(Jump)类型,有两个操作数,立即数imm为源操作数,用于计算跳转目的地址,rd为目的操作数,用于记录跳转前指令的下一条指令的地址;

进一步地,我们分析RV32I中的所有指令,共计四十条,如下表所示:

指令格式是比较规整的,除了ECALLEBREAK以外,均显然符合上面的六种指令格式类型。

可以按照指令的功能对指令进行分类:

  1. 直接跳转类:包括JALJALR
  2. 条件分支类:包括BEQBNEBLTBGEBLTUBGEU
  3. 加载/存储类:包括LBLHLWLBULHUSBSHSW
  4. 算术逻辑运算和位运算类:包括所有加法、减法,按位与、或、异或,逻辑左移、逻辑右移、算术右移相关指令;
  5. 比较指令类:包括SLTISLTIUSLTSLTU
  6. 其他指令类:FENCEECALLEBREAK

再依次对这几个类指令的行为进行分析:

  1. 直接跳转类需要对PC寄存器的值进行直接修改,同时写一个寄存器;
  2. 条件分支类首先需要进行比较,然后根据比较结果选择是否修改PC寄存器;
  3. 加载/存储类需要访问数据存储,加载只读取数据,存储只写入数据;
  4. 算术逻辑运算和位运算类会对操作数进行运算,然后将结果写入目的寄存器;
  5. 比较指令类与算术逻辑运算和位运算类一致,但操作变成了比较;
  6. 其他指令中,由于不需要维护内存一致性和连续性,因此我们不需要实现FENCE,同样,由于不涉及环境调用中断和调试调试中断,所以我们暂时也不需要实现ECALLEBREAK

数据通路和控制逻辑的初步设计

根据上面的分析,我们设计的CPU中应该至少需要包含以下组件:

  1. 指令内存(MemInst):接收一个32位的指令地址,读取出指令;
  2. PC寄存器(PCReg):为指令内存提供指令地址,每个时钟周期地址都会+4,当前指令为跳转时,下一条指令为跳转目的地址,当前指令为分支指令且分支成功时,下一条指令为分支目标地址;
  3. 通用寄存器堆(Registers):可读可写的寄存器,接收寄存器号,为运算单元提供操作数,接收运算结果或从数据内存读取到的值;
  4. 数据内存(MemData):根据加载/存储地址,加载或存储数据,加载或存储依赖于译码器的译码;
  5. 指令译码器(Decoder):对指令进行译码,解析得到立即数、操作码、寄存器号等信息;
  6. 运算单元(ALU):根据操作数和操作码进行运算,运算结果写到寄存器,分支指令时将比较结果发送给PC,加载存储指令时计算地址;

这些组件只描述了数据通路,要使得CPU能正常运行,还需要良好的逻辑控制

控制逻辑需要根据译码结果对数据通路进行控制,可能需要以下几个方面:

  1. ctrlJump:指令是否为跳转指令?如果是,需要给控制信号到PC,要求在下一时钟周期修改为跳转目的地址;
  2. ctrlBranch:指令是否为分支指令?如果是,根据运算单元的比较结果(分支与否),决定是否让PC在下一个时钟周期跳转的分支目标地址;
  3. ctrlRegWrite:指令是否需要写寄存器?如果是,将运算单元的结果或从数据内存中读取的值写入寄存器;
  4. ctrlLoad:指令是否为加载指令?如果是,写入寄存器的值来源应该是数据内存;
  5. ctrlStore:指令是否为存储指令?如果是,将寄存器中的值写入数据内存;
  6. ctrlALUSrc:指令的操作数2是立即数还是寄存器值?根据此选择操作数2的值;
  7. ctrlJAL:指令是否为JAL指令?如果是,操作数1的值应当为PC寄存器的值;
  8. ctrlOP:为ALU指定具体的操作,加?减?或者其他啥?

这些控制信号的生成和传输我们统一由控制器(Controller)完成。

上面的说明并不详尽,只作为初步设计,但我们也很难在开始的时候就考虑到所有细节,更多的细枝末节需要在设计、调试、修改的迭代中完善,但至少上面的内容足够我们开始实现了。

最后放上初步设计的草图:

再次说明,上面的设计是不完备的,比如目前还未考虑到加载/存储时是字节、半字还是字,虽然只是一个信号的问题,但足以体现还有很多不完善的地方,在实现中迭代设计是很有必要的。接下来,我们就将基于这个不完备的设计开始我们的项目开发!

吃透Chisel语言.39.Chisel实战之单周期RISC-V处理器实现(上)——需求分析和初步设计相关推荐

  1. 31条指令单周期cpu设计(Verilog)-(十)上代码→顶层模块设计总结

    说在前面 开发环境:Vivado 语言:Verilog cpu框架:Mips 控制器:组合逻辑 设计思路 按照预先设计好的数据通路图将各个模块连接起来 `timescale 1ns / 1ps mod ...

  2. 吃透Chisel语言.36.Chisel实战之以FIFO为例(一)——FIFO Buffer和Bubble FIFO的Chisel实现

    Chisel实战之以FIFO为例(一)--FIFO Buffer 这一部分,我们将以FIFO以及FIFO的各种变体为例,进行Chisel数字设计的实战.这一部分的实战会以小规模的数字设计为例,比如一个 ...

  3. 吃透Chisel语言.32.Chisel进阶之硬件生成器(一)——Chisel中的参数化

    Chisel进阶之硬件生成器(一)--Chisel中的参数化 Chisel区别于其他硬件描述语言的最强大的地方在于,我们可以用Chisel写硬件生成器.对于老一点的硬件描述语言,比如VHDL和Veri ...

  4. 吃透Chisel语言.38.Chisel实战之以FIFO为例(三)——几种FIFO的变体的Chisel实现

    Chisel实战之以FIFO为例(三)--几种FIFO的变体的Chisel实现 上一篇文章对实现串口通信的最小系统做了简单阐述,然后从发送端开始,实现了基于单字节FIFO缓冲区的发送端,接着类似地实现 ...

  5. 吃透Chisel语言.33.Chisel进阶之硬件生成器(二)——Chisel组合逻辑电路生成:以BCD编码表为例

    Chisel进阶之硬件生成器(二)--Chisel组合逻辑电路生成:以BCD编码表为例 上一篇文章我们学习了两种类型的变量在Chisel之中的使用,然后分别介绍了Chisel中四种参数化的方法,对于我 ...

  6. 吃透Chisel语言.31.Chisel进阶之通信状态机(三)——Ready-Valid接口:定义、时序和Chisel中的实现

    Chisel进阶之通信状态机(三)--Ready-Valid接口:定义.时序和Chisel中的实现 上一篇文章以Popcount为例,介绍了带数据通路的有限状态机FSMD的写法与实现,对于后面写复杂的 ...

  7. 吃透Chisel语言.29.Chisel进阶之通信状态机(一)——通信状态机:以闪光灯为例

    Chisel进阶之通信状态机(一)--通信状态机:以闪光灯为例 上一部分我们学习了单个有限状态机的Chisel描述方法,但是单个有限状态机通常很难描述稍微有点复杂的数字设计.在这种情况下,可以把问题划 ...

  8. 吃透Chisel语言.18.Chisel模块详解(五)——Chisel中使用Verilog模块

    Chisel模块详解(五)--Chisel中使用Verilog模块 上一篇文章讲述了用函数实现轻量级模块的方法,可以大幅度提升编码效率.Chisel中也提供了一些好用的函数,方便我们编写代码,也方便C ...

  9. 吃透Chisel语言.15.Chisel模块详解(二)——Chisel模块嵌套和ALU实现

    Chisel模块详解(二)--Chisel模块嵌套和ALU实现 稍微复杂点的硬件设计就需要用嵌套的模块层级来构建了,上一篇文章中实现的计数器其实就是个例子,计数器内部嵌套了一个寄存器.一个Mux和一个 ...

最新文章

  1. WebBrowserProgramming - Python Wiki
  2. 浅析响应式网站建设的注意要点
  3. python中清除海龟图的代码_Python海龟绘图——常用方法指令
  4. ES6~ES12——Array Includes、Object values、Object entries、Object fromEntries、flat、flatMap、空值合并运算符、可选链等
  5. 最新版 | 李沐《动手学深度学习》中文版pdf重磅开源!
  6. Linux中echo 返回值的意思
  7. DXUT框架剖析(10)
  8. 【TWVRP】基于matlab A_star算法求解带时间窗和任务软时间窗多车场多车型车辆路径问题【含Matlab源码 1561期】
  9. webpack 图像压缩_基于webpack的图片压缩最佳方案
  10. zabbix监控打印机并自动更新监控项
  11. R语言缺失值判断与处理mice包-3
  12. 关于训练误差、测试误差、泛化误差
  13. Spring 整合Hibernate 开发实例
  14. 知行合一ArduPilot | ArduPilot控制器框架简述与PID参数整定
  15. excel运行时出现正在等待其他应用程序的OKE操作完了
  16. UITextField实现过滤选中状态拼音
  17. 辽宁启迪电商:拼多多提升店铺流量的几个技巧
  18. 【BI学习心得13-淘宝定向广告演化与天猫用户复购预测】
  19. 修改Win7开机登录界面背景图片
  20. java判断字符串st6_是否包含st5_第 18 章 在机器指令级调试

热门文章

  1. 中国农业大学2022年法学硕士研究生拟录取名单
  2. mysql 绕过addslashes,SQL注入:Bypass addslashes()
  3. Python 最大公约数/最小公倍数算法
  4. 第十六天前端HTML、CSS、JavaScript详细总结(内置VSCode安装教程)
  5. python算法二:迭代法
  6. JVM优化--垃圾回收
  7. 海康VM联合C#开发时点击快速匹配报错处理
  8. oracle更改字段类型 有数据无数据的时候,修改字段长度,增加字段
  9. X协议调整大小、旋转和反射的扩展(The X Resize, Rotate and Reflect Extension )
  10. SpringBoot 分布式Session共享