实验 8: 具有异常处理的 RISC-V 处理器
实验8截止日期:11月25日星期五晚上11:59:59 EST。
你在实验8中的(极少量的)交付物包括:
- 在
ExcepProc.bsv
中完成的练习1的答案- 在
discussion.txt
中完成的讨论问题1的答案
引言
在本实验中,你将为一个单周期RISC-V处理器添加异常处理功能。有了异常支持,我们将能够做到以下两件事:
- 实现
printInt()
、printChar()
和printStr()
函数作为系统调用。 - 在软件异常处理程序中模拟不支持的乘法指令(
mul
)。
我们使用单周期处理器,这样你可以专注于异常处理的工作方式,而不需要考虑流水线带来的复杂性。
你已经得到了所有必需的程序来测试你的处理器。你只需要添加硬件支持来运行异常。以下部分涵盖了处理器中发生了哪些变化以及你需要做什么。
控制状态寄存器(CSRs)
src/includes/CsrFile.bsv
中的mkCsrFile
模块已经扩展了一些新的CSRs,用于实现异常处理。
以下是mkCsrFile
模块中新增CSRs的总结。你的软件可以使用csrr
、csrw
和csrrw
指令操作这些CSRs。
控制寄存器名称 | 描述 |
---|---|
mstatus | 该寄存器的低12位存储了一个包含特权/用户模式(PRV)和中断使能(IE)位的4元素栈。每个栈元素宽3位。例如,mstatus[2:0] 对应于栈顶,包含当前的PRV和IE位。具体来说,mstatus[0] 是IE位,如果IE=1,则中断被使能。mstatus[2:1] 包含PRV位。如果处理器处于用户模式,则应设置为2'b00 ;如果处理器处于机器(特权)模式,则应设置为2'b11 。其他栈元素(例如mstatus[5:3], ..., mstatus[11:9] )具有相同的构造。当发生异常时,栈将通过左移3位“推”;结果,新的PRV和IE位(例如机器模式和中断禁用)现在存储在mstatus[2:0] 中。相反,当我们使用eret 指令从异常中返回时,栈通过右移3位“弹出”。mstatus[2:0] 将包含它们原来的值,mstatus[11:9] 被分配给(用户模式,中断使能)。 |
mcause | 当异常发生时,原因存储在mcause 中。ProcTypes.bsv 包含了我们将在本实验中实现的两个异常原因值:excepUnsupport :不支持的指令异常。excepUserECall :系统调用。 |
mepc | 当异常发生时,导致异常的指令的PC存储在mepc 中。 |
mscratch | 它存储了一个“安全”数据段的指针,可以在发生异常时用来存储所有通用目的寄存器(GPR)的值。这个寄存器在本实验中完全由软件操作。 |
mtvec | 陷阱向量(trap vector) 是一个只读寄存器,它存储异常处理程序的起始地址。当发生异常时,处理器应将PC设置为mtvec 。 |
mkCsrFile
模块还包含了一些额外的接口方法,应该是不言自明的。
解码逻辑
解码逻辑也已扩展以支持异常。以下三个新指令的功能总结如下:
指令 | 描述 |
---|---|
eret | 这条指令用于从异常处理中返回。它被解码为新的iType 为ERet ,其他一切都无效且不被执行。 |
ecall (或scall ) | 这条指令是系统调用指令。它被解码为新的iType 为ECall ,其他一切都无效且不被执行。 |
csrrw rd, csr, rs1 | 这条指令将csr 的值写入rd ,并将rs1 的值写入csr 。也就是说,它执行rd <- csr; csr <- rs1 。rd 和rs1 都是GPR,而csr 是CSR。这条指令取代了我们之前使用的csrw 指令,因为csrw 只是csrrw 的一个特例。这条指令被解码为新的iType 为Csrrw 。由于csrrw 将写入两个寄存器,ProcTypes.bsv 中的ExecInst 类型增加了一个新字段“Data csrData ”,其中包含要写入csr 的数据。 |
eret
和csrrw
指令仅在机器(特权)模式下允许。为了检测在用户模式下非法使用这些指令,Decode.bsv
中的decode
函数接受第二个参数“Bool inUserMode
”。如果处理器处于用户模式,则该参数应设置为True
。如果解码函数检测到在用户模式下非法使用eret
和csrrw
指令,则指令的iType
将被设置为新的值NoPermission
,处理器稍后将报告此错误。
处理器
我们已经提供了大部分处理器代码在ExcepProc.bsv
中,你只需要填写四个标有“TODO
”注释的地方:
- 为
decode
函数添加第二个参数。 - 处理“不支持的指令”异常:设置
mepc
和mcause
,将新的PRV和IE位推入mstatus
的栈中,并更改PC到mtvec
。你可能需要使用mkCsrFile
的startExcep
方法。 - 处理系统调用:系统调用可以像不支持的指令异常一样处理。
- 处理
eret
指令:弹出mstatus
的栈并更改PC到mepc
。你可能需要使用mkCsrFile
的eret
方法。
测试程序
测试程序可以分为三类:我们之前见过的汇编测试和基准测试,以及一组新的测试处理器异常处理功能的程序。
旧程序
汇编测试和基准测试在机器模式下运行(这些被称为“裸机运行”),不会触发异常。它们可以通过进入programs/assembly
和programs/benchmarks
文件夹并运行make
来编译。
新程序
第三类程序涉及异常。这些程序从机器模式开始,但立即降至用户模式。所有打印函数都实现为系统调用,不支持的乘法指令(mul
)可以在软件异常处理程序中模拟。这些程序的源代码也位于programs/benchmarks
文件夹下,但它们链接到programs/benchmarks/excep_common
文件夹中的库(而不是programs/benchmarks/common
)。
编译这些程序,你可以使用以下
命令:
cd programs/benchmarks
make -f Makefile.excep
编译结果将出现在programs/build/excep
文件夹中。(如果你忘记了,你会收到一个错误消息,如"ERROR: ../../programs/build/excep/vmh/median.riscv.vmh does not exit [sic], you need to first compile"。)
这些程序不仅包括我们之前看到的原始基准测试,还包括两个新程序:
mul_inst
:这是原始multiply
基准的一个替代版本,直接使用mul
指令。permission
:这个程序在用户模式下执行csrrw
指令,并应该失败!
实现异常
练习1(40分):如上所述,在
ExcepProc.bsv
中的处理器上实现异常。你可以通过运行build -v excep
在
scemi/sim
中构建处理器。我们提供了以下脚本在仿真中运行测试程序:
run_asm.sh
:在机器模式下运行汇编测试(无异常)。run_bmarks.sh
:在机器模式下运行基准测试(无异常)。run_excep.sh
:在用户模式下运行基准测试(有异常)。run_permit.sh
:在用户模式下运行permission
程序。你的处理器应该通过前三个脚本(
run_asm.sh
、run_bmarks.sh
和run_excep.sh
)中的所有测试,但应该在最后一个脚本(run_permit.sh
)中报告错误并终止。注意,在运行run_permit.sh
时看到bsim_dut
输出的错误消息后,软件测试台tb
仍在运行,因此你需要按Ctrl-C
来终止它。
讨论问题1(10分):在即将到来的感恩节假期的精神中,列举一些你感激只需在单周期处理器上做这个实验的理由。为了帮助你开始:如果你在处理流水线实现,异常会引入哪些新的危险?
讨论问题2(可选):你完成这个实验花了多长时间?
完成后记得提交你的代码并git push
。
© 2016 麻省理工学院。版权所有。