永久移走到YEWPO/Yprocessor
New Process Core
Writen by Chisel
RISC-V
五级流水线处理器,支持RISCV64IMZicsr
指令集。
访存接口支持AXI-Lite
总线协议。
首先,你需要根据 此处安装mill
.
然后,按照 此处安装verilator
.
测试整个设计:
make test
生成verilog
代码:
make verilog
在verilator
上面仿真:
make sim
查看verilator
仿真时产生的波形文件:
首先需要安装gtkwave
。对于Ubuntu
,你可以通过sudo apt install gtkwave
来安装。
make mv
清理项目中的编译中间文件:
make clean
.
|- docs // 该目录下存放了一些文档
|- utils // 一些辅助工具
|- playgroud
|- src // NPC的硬件描述核心代码部分
|_ test // NPC的测试代码
|- verilator
|- include // 仿真器的头文件
|- script // 仿真器的编译脚本
|- src // 仿真器的源代码
|_ tools // 仿真器的辅助工具
|- build.sc // scala环境配置文件
|- README.md
|_ Makefile
在整个微架构设计当中,整个设计分为三个层面:数据通路层面,控制层面以及冒险层面。
- 数据通路层面:表示着正常数据流通过情况。该部分在顶层模块以及各个流水线阶段中体现。
- 控制层面:根据指令生成控制信号,控制相应的逻辑单元的运算结果。该设计提供译码控制模块以及控制状态寄存器指令的控制模块。
- 冒险层面:通过冒泡,转发,暂停等手段,防止流水线发生结构冒险,数据冒险和控制冒险。该部分未单独列为一个模块。
数据冒险部分使用转发辅以暂停来实现。我们保存了每个阶段的rd
信息,所以我们直接通过比较译码阶段的两个源数据的地址是否与在执行阶段或者是访存阶段甚至是写回阶段的rd
冲突。可能的冲突情况如下:
- 与执行阶段冲突
- 需要访存的数据
- 不需要访存的数据
- 与访存阶段冲突
- 需要访存的数据
- 不需要访存的数据
- 与写回阶段冲突
需要转发的情况,当需要源寄存器以及和相应的阶段的目的寄存器发生冒险时,才需要转发。为了记录指令是否需要源寄存器,我们使用了rs1_tag
和rs2_tag
两个标记,这两个标记会在控制单元译码时生成。是否需要目的寄存器可以通过寄存器使能控制信号来判断。
若冲突,则选择合适阶段的转发值。具体的转发选择如下:
- 若与执行阶段发生冲突,且不需要访存的数据,直接选择执行阶段所生成的数据即可;若需要访存的数据,则暂停一拍。
- 若与访存阶段发生冲突,且不需要访存的数据,直接选择执行阶段所生成的数据即可;若需要访存的的数据,则暂停一拍。
- 若与写回阶段发生冲突,直接选择写回阶段的数据即可。
控制冒险,即无条件跳转和分支跳转指令的下一条指令的地址判断冒险。我们采取先继续执行,待跳转结果得出之后,再考虑是否冲刷执行过的指令。冲刷指令的方法是,通过响应阶段的有效信号置0。
对于CSR
的数据冒险,由于只涉及到一个值,所以对每个阶段进行对比,看看是否存在与译码阶段所需的CSR
相同。如果相同且需要写回,则选择离译码最近的阶段的冲突转发值。
ecall
指令属于一种异常指令,如果遇到该指令,则将该阶段的cause
值设置为0xb
,表示自陷。然后在写回阶段判断是否存在异常,若存在则将下一跳的pc
值设置为tvec
的值,同时设置epc
,否则,正常执行指令。
对于mret
当作跳转指令来处理。
控制采用集中式控制方法,在译码阶段产生所有的模块所需要的控制信号,然后通过流水线寄存器逐级传递。
控制信号文档,在该文档中详细介绍了各个信号的具体设计含义以及它们的作用。
同时,在项目中,使用chisel
的语言特性,通过BitPat
来匹配指令,再通过查表的方式获得这些控制信号。
控制信号表,目前暂时记录了每个指令具体产生的信号。
在utils/decodegen
目录下,已经编写好了main.py
的python
程序,该程序会将表格中的内容生成对应的chisel
代码。使用时无需安装其他库。使用方法如下:
python3 utils/decodegen/main.py
该程序会将代码复制到playground/src/control/DecodeTable.scala
中的decode_map
的数组中,并格式化代码样式。
测试大纲,记录了测试说明,测试用例和实际的测试结果。