实验 8: 具有异常处理的 RISC-V 处理器

实验8截止日期:11月25日星期五晚上11:59:59 EST。

你在实验8中的(极少量的)交付物包括:

  • ExcepProc.bsv中完成的练习1的答案
  • discussion.txt中完成的讨论问题1的答案

引言

在本实验中,你将为一个单周期RISC-V处理器添加异常处理功能。有了异常支持,我们将能够做到以下两件事:

  1. 实现printInt()printChar()printStr()函数作为系统调用。
  2. 在软件异常处理程序中模拟不支持的乘法指令(mul)。

我们使用单周期处理器,这样你可以专注于异常处理的工作方式,而不需要考虑流水线带来的复杂性。

你已经得到了所有必需的程序来测试你的处理器。你只需要添加硬件支持来运行异常。以下部分涵盖了处理器中发生了哪些变化以及你需要做什么。

控制状态寄存器(CSRs)

src/includes/CsrFile.bsv中的mkCsrFile模块已经扩展了一些新的CSRs,用于实现异常处理。

以下是mkCsrFile模块中新增CSRs的总结。你的软件可以使用csrrcsrwcsrrw指令操作这些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这条指令用于从异常处理中返回。它被解码为新的iTypeERet,其他一切都无效且不被执行。
ecall(或scall这条指令是系统调用指令。它被解码为新的iTypeECall,其他一切都无效且不被执行。
csrrw rd, csr, rs1这条指令将csr的值写入rd,并将rs1的值写入csr。也就是说,它执行rd <- csr; csr <- rs1rdrs1都是GPR,而csr是CSR。这条指令取代了我们之前使用的csrw指令,因为csrw只是csrrw的一个特例。这条指令被解码为新的iTypeCsrrw。由于csrrw将写入两个寄存器,ProcTypes.bsv中的ExecInst类型增加了一个新字段“Data csrData”,其中包含要写入csr的数据。

eretcsrrw指令仅在机器(特权)模式下允许。为了检测在用户模式下非法使用这些指令,Decode.bsv中的decode函数接受第二个参数“Bool inUserMode”。如果处理器处于用户模式,则该参数应设置为True。如果解码函数检测到在用户模式下非法使用eretcsrrw指令,则指令的iType将被设置为新的值NoPermission,处理器稍后将报告此错误。

处理器

我们已经提供了大部分处理器代码在ExcepProc.bsv中,你只需要填写四个标有“TODO”注释的地方:

  1. decode函数添加第二个参数。
  2. 处理“不支持的指令”异常:设置mepcmcause,将新的PRV和IE位推入mstatus的栈中,并更改PC到mtvec。你可能需要使用mkCsrFilestartExcep方法。
  3. 处理系统调用:系统调用可以像不支持的指令异常一样处理。
  4. 处理eret指令:弹出mstatus的栈并更改PC到mepc。你可能需要使用mkCsrFileeret方法。

测试程序

测试程序可以分为三类:我们之前见过的汇编测试和基准测试,以及一组新的测试处理器异常处理功能的程序。

旧程序

汇编测试和基准测试在机器模式下运行(这些被称为“裸机运行”),不会触发异常。它们可以通过进入programs/assemblyprograms/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中构建处理器。我们提供了以下脚本在仿真中运行测试程序:

  1. run_asm.sh:在机器模式下运行汇编测试(无异常)。
  2. run_bmarks.sh:在机器模式下运行基准测试(无异常)。
  3. run_excep.sh:在用户模式下运行基准测试(有异常)。
  4. run_permit.sh:在用户模式下运行permission程序。

你的处理器应该通过前三个脚本(run_asm.shrun_bmarks.shrun_excep.sh)中的所有测试,但应该在最后一个脚本(run_permit.sh)中报告错误并终止。注意,在运行run_permit.sh时看到bsim_dut输出的错误消息后,软件测试台tb仍在运行,因此你需要按Ctrl-C来终止它。

讨论问题1(10分):在即将到来的感恩节假期的精神中,列举一些你感激只需在单周期处理器上做这个实验的理由。为了帮助你开始:如果你在处理流水线实现,异常会引入哪些新的危险?

讨论问题2(可选):你完成这个实验花了多长时间?

完成后记得提交你的代码并git push


© 2016 麻省理工学院。版权所有。