实验 4:MIPS 异常与中断
参考资料: MIPS 手册 Ⅲ
异常种类
实现以下几种类型的异常:
- 读地址错和写地址错。
- 溢出、
break
指令和syscall
指令(Execution Exceptions)。 - 保留指令(Instruction Validity Exceptions)。
- 中断。
中断包括软件中断和硬件中断。软件中断是由软件通过执行某些指令来产生的。硬件中断包括外部的中断(CPU 的接口 ext_int
)和时钟中断。MIPS 支持 6 个硬件中断和 2 个软件中断。
CP0 寄存器
为了支持中断,MIPS 添加了协处理器(coprocessor)CP0。
CP0 是一组 32 位的寄存器,用 5 位 index 和 3 位 sel 来进行索引。本实验中只需实现 sel 为 0 的一些寄存器。
需要实现的寄存器:
- BadVAddr(8 号寄存器,地址错异常时记录该虚拟地址)。
- Count(9 号寄存器,是一个计时器,每两个时钟周期加一)。
- 该寄存器是软件可写的,软件写入的优先级高于硬件自增。
- Compare(11 号寄存器,和 Count 寄存器比较以产生时钟中断)。
- 当 Compare 寄存器被设置过,且 Compare 和 Count 寄存器值相等时,产生时钟中断信号并保持。
- 当 Compare 寄存器被软件修改时,清除时钟中断信号。
- Status(12 号寄存器,记录处理器的运行状态)。
- Cause(13 号寄存器,记录最近一次异常的原因)。
- EPC(14 号寄存器,用于异常处理结束后的恢复)。
cp0.Cause
和 cp0.Status
有很多个 field,本实验中需要实现的 field 将在下文中提到,没提到的部分可实现为只读恒为 0。
CP0 寄存器不能作为算术指令的源寄存器和目的寄存器,软件需要通过 mfc0
和 mtc0
来读取或写入它们。
CP0 寄存器的写入,包括两种情况:
- 软件通过
mtc0
指令写入 CP0 寄存器。需要注意 CP0 寄存器和通用寄存器不同,有些寄存器不是每一位都允许软件进行写入,手册上的R/W
权限描述了各个寄存器每一位的软件读写权限。 - 硬件写入。
对于它们的优先级,请先用单周期 CPU 模型进行讨论,再思考五级流水线中的做法。
处理异常
异常的处理是由软件和硬件共同完成的。硬件主要处理异常检测与异常恢复。
异常检测
五级流水线的前四个阶段都可能产生异常,但不一定要在当个流水段就响应,可以产生一个异常使能信号,通过流水线寄存器传到 M 阶段再进行统一处理。
中断产生的条件包括:
cp0.Status.IE
为 1,全局硬件中断使能为有效。cp0.Status.EXL
为 0,CPU 在执行异常处理程序时,不允许中断。- 中断源产生中断(包括
ext_int[5:0]
,cp0.Cause.IP[7:0]
和时钟中断),且对应的中断使能cp0.Status.IM[7:0]
为有效。时钟中断对应的 mask 为IM[7]
,外部硬件中断对应的 mask 为IM[7:2]
。可用以下代码来表示:interrupt_info = ({exception.ext_int, 2'b00} | cp0_cause.IP | {cp0.timer_interrupt, 7'b0}) & cp0_status.IM;
。
分析中断信号与各个异常信号,只要有一个为有效,则产生异常。检测到异常后,硬件需要做以下几件事:
- 清空流水线,并把下一条指令的 PC 设置为
0xbfc00380
。本实验中,这是统一的异常处理入口。 - 根据异常优先级(手册 page 55~56),得到产生异常的原因,按照手册 138 的对应关系生成异常代码
exccode
,写入cp0.Cause.ExcCode
。本实验需要实现的 code 包括:Int
、AdEL
、AdES
、Sys
、BP
、RI
、Ov
。 - 如果是地址错异常,将出错的虚拟地址写入
cp0.BadVAddr
。 - 如果
cp0.Status.EXL
为 0,设置cp0.EPC
和cp0.Cause.BD
。如果产生异常的指令不在分支延迟槽中,将cp0.EPC
设为该指令的 PC,并将cp0.Cause.BD
设为 0;否则,将cp0.EPC
设置为分支/跳转指令的 PC(即当前 PC 减 4),并将cp0.Cause.BD
设为 1。 - 将
cp0.Status.EXL
设置为 1。
异常恢复
软件通过 eret
指令来恢复异常。
此时,硬件需要:
- 清空流水线,并将下一条指令的 PC 置为
cp0.EPC
。 cp0.Status.EXL
← 0。
有些教科书上说,对于 syscall
类型的异常,当异常返回时,应该返回到下一条指令。在 MIPS 里,这件事情由软件完成(cp0.EPC
是软件可写的)。
添加指令
添加以下指令:
add
、addi
、sub
(可能产生溢出异常)。break
、syscall
。mfc0
、mtc0
(读取/写入 CP0 寄存器)。eret
。
实验提交
18307130024/
├── report/ (报告所在目录)
├── source/ (源文件所在目录)
└── verilate/ (仿真代码所在目录)
用 zip -r 18307130024.zip 18307130024/
打包。用 unzip 18307130024.zip
检查,应在当前目录下有学号目录。
通过标准
上板通过 test1~test5,并且 make verilate
编译通过。
test4 是异常与中断的测试,仅测试了所要求的异常与软件中断。
test5 包含了十个测试,通过调整开关来执行这十个测试。test5 的默认时钟频率是 50MHz。如果生成 bit 文件时产生时序违例(比如当你的除法器是单周期实现时),请调整时钟频率至 7MHz。助教在测试时会避开时序违例。
调试建议
test5 的十个测试很难用 Vivado 的仿真进行调试,可以用 Verilator 进行调试。不像 test1~test4,测试参数为 TEST=test1
这种,测试 test5 是对每个小测试运行一次 make vsim
,参数为 TEST=sha
之类,各个测试的名字在 misc/nscscc
里可以查看到。这些测试没有 trace 文件,无法直接定位到寄存器写入错误的位置,出错了只能看波形图。
你也可以用 Verilator 运行一遍 func_test
测试,它集成了 test1~test4 的全部测试。
截止时间
2021 年 5 月 17 日 12:00