MIPS微系统设计方案
概述
刚刚拿到实验教程的时候真的感到无从下手,不过在学长博客和gxpPPT的指引下还是解决了。这里贴出我用到的三个博客以示感谢。
- 北航CO 2024 P7课下部分个人理解分享 | Lazyfish & chilly_river
- [BUAA-CO-Lab] P7 MIPS 微体系 | ROIFE BLOG
- 「BUAA-CO」P7_MIPS微系统(异常中断) | Hyggge’s Blog
有一说一我觉得助教是刻意把教程弄成这样的。因为教程里都写了P7注重考验同学们的自主设计,并且在设计上给了同学们相当大的自由。不过我觉得还是改了为好。
搭建思路
P7要做的事情有些琐碎,因此选择一个良好的操作顺序非常重要,以下是我的工作流程。
- 搭建CP0,建议放M级。
- 产生异常,设计出
ExcCode
信号并随流水线传递到M级,注意课程组给出的条件,注意一条指令多个异常和多条指令同时异常的情况。 - 根据CP0的需求爆改流水线,包括但不限于
- 将CP0需要的已知信号统统流水传递过来,原本没有的(如表示当前指令是否为延迟槽指令的
BDIn
)就生成出来。 - 在
Controller
模块中加入四条新指令即mfc0
,mtc0
,eret
,syscall
。选择合适的控制信号,个人建议syscall和eret单独拉出来。 - 在CPU中以合适的方法建立四条新指令的数据通路。做这里的时候加吐我了,因为之前没有使用宏定义,导致很多之前不需要传递的信号都要传递。
- 处理冒险,
mfc0
转发直接用原有的数据通路。阻塞则只需要加一条eret相关阻塞,处理mtc0对EPC进行写入的问题,
- 将CP0需要的已知信号统统流水传递过来,原本没有的(如表示当前指令是否为延迟槽指令的
- 通过修改流水级寄存器和NPC完成异常中断的跳转和返回过程。这里我们把CP0产生的异常中断信号
req
传给这些模块,将流水级寄存器除了PC
以外的内容置0,PC
置为32'h4180
,并让NPC跳转到32'h4180
处,注意对实验教程中提到的对乘除模块的处理。返回则是当eret产生时,NPC跳回EPC同时清空延迟槽(操作想必大家都知道,注意阻塞的情况)。 - 调整模块结构,规范化CPU的输入输出端口,并建立新的mips顶层模块,将CPU和课程组给的Timer放进去。
- 设计Bridge模块,这个模块在P7的作用仅仅是判断当前读写的是哪个外设并处理对应的信号,实现相当简单。
- 把线都连好,大功告成。
功能模块设计
挑出比较重要的新增模块。
CP0
值得一提的是内部,许多学长是直接定义了IM
,EXL
,IE
等寄存器,在写入时则用拼接的方式这是合理的,因为SR
,Cause
中的绝大部分位数没有用。当然,还可以使用在模块内用宏定义,具体怎么实现看个人喜好。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
clk | input | 时钟信号 | 1 |
reset | input | 复位信号 | 1 |
en | input | 启用信号,控制CP0模块的操作 | 1 |
CP0Add | input | 控制寄存器选择信号,指定访问的寄存器地址(5位) | 5 |
CP0In | input | 往CP0寄存器写入的数据 | 32 |
VPC | input | 异常发生时的程序计数器值 | 32 |
BDIn | input | 异常时的返回地址标志 | 1 |
ExcCodeIn | input | 异常代码 | 5 |
HWInt | input | 硬件中断请求信号(6位) | 6 |
EXLClr | input | 清除异常级别标志(用于清除SR[1]) | 1 |
EPCOut | output | 异常程序计数器(EPC)输出 | 32 |
CP0Out | output | 读取的CP0寄存器值,根据CP0Add选择 | 32 |
Req | output | 异常或中断请求信号 | 1 |
Bridge
连个线,没啥可说的。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
Addr | input | 地址输入,决定访问的外设区域 | 32 |
byteen | input | 字节使能信号,用于指示操作的字节 | 4 |
DM_RD | input | 数据存储区读取数据 | 32 |
T0_RD | input | 定时器0读取数据 | 32 |
T1_RD | input | 定时器1读取数据 | 32 |
RD | output | 输出数据,根据地址选择不同外设的读取数据 | 32 |
DM_WE | output | 数据存储区写使能信号,根据地址选择是否写入数据存储区 | 4 |
T0_WE | output | 定时器0写使能信号 | 1 |
T1_WE | output | 定时器1写使能信号 | 1 |
int_WE | output | 中断控制寄存器写使能信号 | 4 |
关于BUG
P7真的很容易写出各种各样的BUG,所以自行测试的时候就很考验数据点编写的能力(虽然我大部分都是找别人要的)。但如果要自己编写测试点,建议先把每种类型的异常全部构造出对应的点,先检验有没有漏判的问题。随后认真观察波形图,分析一下进入异常处理程序后各寄存器的情况,以及是怎么跳转回去的,跳转回去各寄存器的情况又如何。理解好异常中断的处理过程对于找bug好处很大。
此外,评测机真的是好东西,cokiller帮我解决了大部分BUG,虽然最后还是漏了就是了。还有就是学长博客,以及多跟同学交流,很多BUG是共通的,例如空泡的问题。
测试方案
之前P3的时候想着P4再搓评测机,到后面每天疲于奔命也没有自己搓了。 于是我就自己写了一点巨弱的测试点,然后又找大佬要了一堆强力测试点。
思考题
-
请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的? 答:鼠标和键盘的输入信号通过硬件接口(如PS/2、USB或蓝牙)传输到计算机。当按下键盘的某个按键时,它会发送一个扫描码;鼠标则发送位置和点击信号。这些信号通过设备驱动程序转化为操作系统可以理解的格式,操作系统通过中断或轮询方式接收这些信号,并将其传递给相应的应用程序。最终,CPU根据这些输入信号作出响应,例如显示字符或移动鼠标指针。整个过程依赖硬件接口、驱动程序、操作系统和中断机制的协作。
-
请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法) 答:因为这样可以简化设计,增加系统的稳定性和可靠性。可以提供,但是可能会出现增加安全性问题,因为用户可能会选择不够安全的入口。此外,不同程序采用不同的入口可能导致冲突,出现故障时也不容易排查Bug.
-
为何与外设通信需要 Bridge? 答:因为在我们的设计中,CPU不需要知道外设的具体信息,只需要对程序指定的地址进行访问。这样符合高内聚低耦合的思想。
-
请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并分别针对每一种模式绘制状态移图。 答:区别是enable是否是自动赋值。
-
倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息? 答:那么EPC会变成0,这样跳回时会引发错误。所以应该保留PC信息和本条指令是否是延迟槽的信息。
-
为什么 jalr 指令为什么不能写成 jalr $31, $31? 答:因为我们处理延迟槽指令发生异常时EPC会指向跳转指令,因此从异常处理程序返回后跳转指令实际上总共被执行了两次,而
jalr
具有累加效应,第二次执行的jalr和第一次是不一样的,所以会出错。