CS61C CPU PROJECT
用时4天,体验CPU设计
关于如何Debug
相信前面的lab5
和lab6
已经奠定好了使用logisim
的基础,前面相关内容同样奠定好了RISC-V ISA的知识基础。接下来Project中的一系列的task可以通过参考:
61C Reference card
ROM
表- 对应测试文件中的汇编代码和测试电路
- 通过
change value
来对电路中的bit位进行赋值,查看输入输出的值
需要将所有这些结合起来构建完整的big picture
。
ALU
根据文档中给的功能类别通过门级电路来构造基本的ALU原件,个别功能的实现所用到的元器件还需要查看手册。
RegFile
需要用到Multiplexer
和Demultiplexer
,以及一些基本的逻辑,比较废鼠标…
Immediate Generator
这一部分跟着61C绿卡里的指令编码表。以下显示的是所有含有imm部分的指令的实现。
Control Store(Implemented by ROM)
通过分析绿卡上或者是文档里给的指令的功能,来初始化每条指令对应的微指令,需要结合数据通路中每个部分的实现以及PartA中我们已经构建好的ALU。Immsel以及个别控制信号可以自行设计(后面会有介绍修改.csv
文件即可, 我这边是修改过后的),建议PartB部分一开始就完善ROM表。
下面给出我的数据:
完善ROM表后,将ROM Output
复制到控制逻辑的ROM中。需要在控制逻辑电路中根据指令编码中的opcode
, fun3
, fun7
字段来区分每个类型,甚至每条指令,再通过优先级别编码器来索引控存中的微指令, 产生对应的控制信号。
因为优先级别编码器最多只能支持32bit, 但我们的指令有36条, 需拆拆分成两部分输入。剩下四条指令可以通过指令编码格式来各自区分,然后再找到指令编码格式中与前32条指令相异的bit作为控制信号来控制二选一MUX。
Branch and Jump Instruction
通过BrUn
信号来实现分支比较器产生对应BrEq
和BrLt
的控制信号决定分支是否发生跳转。
B类指令和J类指令产生PCsel
控制信号来决定是否修改PC
, 同样也通过指令编码格式中的bit来区分jump发生还是branch发生
Loading and Storing
这部分需要细心一些,部分load和部分store无论是字节还是半字都要符号位拓展成字的大小。部分load相对部分store来说比较简单,因为部分load只需要考虑从存储器中读出来的数据位置不同即可,而部分store不仅需要将从寄存器中读来的数据分割,还需要将分割的数据存到对应存储器中字的偏移位置(这部分是由Mask来确定写入的位置的)。需要通过低两位来判断具体load的是当前字的哪一个部分。
Partial Load
如果不是取一个字的数据,则要根据当前指令lw来访问存储器的地址字段的低两位来产生控制信号,决定取当前数据字的哪一个部分的数据到寄存器中(通过splitter来划分)。由指令编码中的bit来作为控制信号区分,选择具体是lw
还是lh
, lb
。
Partial Store
同样有指令编码格式来区分三种类型的store指令决定store到存储器中的数据。MemWriteMask有四个二进制bit组成,分别表示store到存储器中数据的位置, 实验中并未提到mask实现的细节。但是需要完成有指令编码和访问存储器的地址来生成mask。生成的mask需要通过指令编码格式中的bit和地址字段的低两位来区分, 额外在利用上优先级别编码器来索引。 生成的mask有lb
的0001, 0010, 0100, 1000, 和lh
的0011, 1100以及lw
的1111, 七种组合。
Datapath
下面是流水过后的数据通路, 两级流水线挺好实现的,看了一下不需要考虑Structure Hazards
以及Data Hazards
,省去了很多数据通路上的麻烦。需要考虑的只有Control Hazards
。当Branch或者Jump发生时,紧随其后进入流水线的只有一条指令,实验文档里解决的方案是插入一条NOP指令来防止冲突。什么时候插入NOP指令根据PCsel
控制信号来决定即可。同时注意两个不同stage之间的差异(导致后面利用到PC的指令执行结果不正确),需要通过在两个stage之间加流水线寄存器来存储一个时钟周期之内的信息(包括指令和PC值)。
参考书目
- Introduction to Computing Systems: From Bits & Gates to C & Beyond [3ed.]
- P&H [RISC-V 2ed.]
- CAAQA [6ed.]