由于课程政策,CS61C的所有代码均不会在网上公开,以下仅记录完成project时的具体思路。
PartA,为一个基础的RISC-V CPU实现ALU和RegFile部分,以及补充CPU执行addi指令的数据路径;
PartB,使用这些组件连接成一个能运行实际RISC-V指令的CPU。
Task 1: Arithmetic Logic Unit (ALU)
根据提供的表格设计ALU
具体思路是补充完对应的模块后,根据ALUSel的值结合多路选择器实现。
第十个指令,mulh可能有一定难度,了解二进制补码乘法的原理后就很容易实现了。
Task 2: Register File (RegFile)
按部就班的完成即可。
Task 3: The addi Instruction
补充完立即数模块、控制模块之后连接上CPU的各个组件即可,算是增强了对取指、读指、执行、写回这些指令执行阶段的理解吧。
Task 4: More Instructions
这一部分的难点在于control logic,补充思路如下:
先根据opcode区分指令类型,倒数两位bit都是11,可以忽略。根据倒数3、4bit将指令分为两大部分
再根据opcode中前三位bit并结合funct3和funct7细分其他指令,使用多路复用器级联实现最后指令信号的输出。
我将这个模块总结为RISC-V世界杯(哈哈,紧跟时事):来自各个大洲的40支参赛队伍(对应40条指令的控制字)在control logic足球场相聚一堂,角逐出最后的“大力神”杯获得者(控制字被control logic输出)。在这其中,决定每条指令的opcode、funct3和funct7就类似于各场比赛的裁判,和真实球赛不同的是,control logic中只有符合裁判口味的球队(控制字)才会胜出。
不太满意的地方在于使用的多路复用器太多了,连线很复杂,解决方案是使用logisim自带的tunnel代替连线,更美观。下面是针对各条指令设计的控制字,其中标黄的部分是控制字中有决定性作用的信号,白色部分则为无关信号:
Task 5: Custom Tests
这个task应该和task4同步进行,在task4补充完每一条指令的control logic后,同时写出对应的测试单元对其进行测试。我们需要做的便是写出使用对应指令的汇编代码即可,然后用项目中为我们提供的代码生成测试电路。
运行项目提供的生成电路的命令的时候,出现了和网络有关的错误,应该是tools文件夹的安装配置问题,回到lab3重新配置环境,问题解决。
项目进行测试的思路如下,
1.编写测试汇编指令;
2.将指令生成为对应的机器指令;
3.将机器指令存入测试电路中的IMEM,设置对应的时钟停止周期;
4.运行测试电路,对比测试输出和标准输出对应寄存器的值,如果两者不一致则测试失败。
以项目中提供的sanity测试中的branch指令为例记录一下debug的思路:
1.首先观察到的现象是自己写的常规测试案例中branch指令都能通过,但项目中自带的branch测试失败,从理性的角度来看应该是我电路的实现细节中出现了bug。下面是branch测试的源代码:
2.对比测试输出和标准输出
标准输出:
测试输出:
发现测试输出中PC从1018开始指令都为空了,最后一条执行的指令是PC为0028的位置,对应于汇编代码中第十行位置的指令:
blt s1, s0, label2 #10
从测试电路的单步调试中也可以看到,CPU执行完这一条指令后PC的跳转出现错误,读取的下一条指令为空。奇怪的是前面执行的代码中同样存在跳转指令,但没有出现问题。该指令与前面已经执行过的指令不同的地方在于,它将要跳转的label2部分位于该指令的上方,也即是PC将减去对应的值进行跳转,而在前面的跳转中,PC都是加上对应的值。
3.可能存在问题的模块
(1)ALU的输入选择错误
(2)ALU的计算逻辑出现错误
(3)立即数提取错误
最后通过排查,发现是立即数提取模块中SB类型指令的立即数提取出现错误,修改电路连接后成功解决问题。其他bug都大同小异,大多是连线出现了问题,调试思路和上述步骤一致,最后成功通过项目中提供的完整性测试:
Task 6: Pipelining
在前面的单周期CPU的基础上补充电路,实现二阶段流水线CPU,具体思路就是在各个阶段间添加额外的寄存器保存好当前阶段执行的指令的PC值、指令值。
除了普通指令外,分支和跳跃指令还需要控制其可能会出现的控制隐患(执行阶段采用了新分支后取值阶段已经取出了下一条无用的指令)。因此需要在采用新分支后向执行阶段注入nop空指令,比如像addi x0, x0, 0这样不会有任何影响的指令。下面是课程中介绍的5阶段流水线CPU,此项目中只需要前两个阶段即可:
最终成功通过了项目中提供的完整性测试,end:
1.增强了对CPU流水线各个阶段操作的理解:
RISC-V CPU流水线的五个阶段:
1.Instruction Fetch(IF)
2.Instruction Decode(ID)
3.Execute(EX)
4.Memory(MEM)
5.Write Back(WB)
2.CPU中各个模块,ALU、control logic、regFile的设计和程序设计的模式很像,预先设计好输入输出接口及其模块的功能,模块内部的架构在CPU层面是不用考虑的,我们只需要该模块能够按照设计规范运行即可。
3.流水线操作CPU和单周期CPU不同的地方在哪里呢?我们需要额外添加什么电路使得它能够进行流水线操作呢?
流水线操作CPU每个周期的时间缩短,一个周期只能完成一条指令的某个阶段,而单周期CPU周期时间长,能完整的运行一条指令。也就是说,流水线操作的CPU一个周期内执行的是多个指令的某个阶段,因此需要额外添加寄存器在各个阶段间,维持执行当前指令所需的环境。此项目中实现的是2阶段的CPU,在更多阶段的CPU中,还需要存储当前指令对应的控制信号。
对于分支跳跃等指令,我们还需要注入nop空指令,以免出现可能的控制隐患。