Skip to content

P7 中断异常设计文档

一、中断和异常

中断异常的处理在我看来是整个 P7 设计中最重要和复杂的任务。这一部分主要帮助我理清这些设计思路。

1.1 内部异常

内部异常主要指的是因为流水线 CPU 执行指令时出现不合规范的情况,在发现指令异常时,我们需要将异常的信息告诉协处理器 CP0。这里涉及到需要把这个处理器放在哪一级的问题。这没有最好的答案,依我个人来看,放在 M 级比较合适,主要是因为所有类型的指令异常都会在 M 级确定,M 级流水寄存器已经完成对所有异常的响应。当然放在 M 级也产生其他问题,我们这里先不讨论。

我们总结所有可能产生内部异常的情况,见下表。

流水线级异常类型检测部件异常原因涉及指令处理方式
FetchAdELIFUPC未对齐,超出范围ALL置 ExcCode 00100;修改 instr 记为nop
DecodeRICU出现无法识别的指令---置 ExcCode 01010;修改 instr 记为nop
DecodeSyscallCU系统调用syscall置 ExcCode 01000;修改 instr 记为 nop
ExecuteOvALU计算类指令溢出add, addi, sub置 ExcCode 01100;清除 GRF 写使能
ExecuteAdELALU取数地址溢出load 类指令置 ExcCode 00100;清零 GRF 写使能
ExecuteAdESALU存数地址溢出store 类指令置 ExcCode 00101;清零 DM 写使能信号
MemoryAdELDM_CU取数地址未对齐、非法取数load 类指令置 ExcCode 00100;清零 GRF 写使能
MemoryAdESDM_CU存数地址未对齐、非法存数store 类指令置 ExcCode 00101;清零 DM 字节使能信号

处理内部异常的做法首先是要保证宏观 PC 以及延迟槽标志 BD 不能出错。比如当 D 级被阻塞时, E 级的气泡 nop 的 PC 应为 D_PC,BD 应为 D_BD。其次要在发现指令异常时及时做出响应,保证异常指令及其后面的指令不对微处理器做修改。

这里比较麻烦的是乘除指令,因为大多数指令只能在 M 级以后才对微处理器有修改,而乘除指令在 E 级就有了。虽然乘除指令没有异常的情况,但如果其在异常指令之后,而 M 级刚刚识别出异常时,此时 MDU 开始计算,这种情况就要消除乘除指令的影响。

1.2 外部中断

中断是指 CPU 的外设对 CPU 内部发出的请求,此时的宏观 PC 就是中断开始的指令,处理方法同异常。需要注意的是,如果乘除指令流水到 M 级时刚好中断了,课程要求保证不需要再修改 MDU,减少设计难度。

中断优先级高于异常优先级,即当有异常提交至 CP0 寄存器时,若有中断发生,则硬件应先响应中断,并重新执行受害指令及其后续指令;若没有中断发生,则处理异常。

1.3 异常码

异常码用于记录异常中断类型,其中由于中断信号直接给到 CP0,所以在 CPU 中可以认为异常码是 0 时表示指令没有内部异常。

一种指令只可能发生一种内部异常,但由于流水线部件中可能会产生新的异常码,因此异常码的优先级是 F > D > E > M。各功能模块产生的异常码都需要经过判断才能用于更新 ExcCode。

这里有个需要关注的问题也是我设计时遇到的一个 bug,即如果 M 级出现异常以后,通过 Req 修改每个流水线寄存器的值(比如 PC 改成 0x4180),这些被修改的指令其实是无效的,仅仅为了维护宏观 PC,也就是说这些指令不应该出现异常,你需要保证这些指令流到 M 级时 ExcCode 是 5'b00000。否则会出现 CP0 写入的 EPC 为 0x4180 的错误

CPU 对中断异常的响应都是将执行一段异常处理程序,最后通过 eret 指令返回记录的受害指令继续执行。


二、模块设计

2.1 F 级模块

该级主要包含 IFU 模块。

IFU:增强版 PC 模块,直接和 tb 的 IM 交互。

端口方向功能
clkinput时钟控制信号
resetinput同步复位信号
Reqinput微处理器中断异常请求,用于修正 F_PC
F_IFU_ENinput使能信号,支持阻塞功能
F_IFU_PCnextinput[31:0]下一条指令,来自 NPC 输出,是正常程序执行时产生的下一条指令地址
F_PCoutput[31:0]F 级指令地址(此时不考虑 eret 的情况)
F_IFU_ExcCodeoutput[4:0]IFU 得到的异常码

为了实现 eret 清空延迟槽操作,须在 CPU 中对该模块的 F_PC 输出依情况修正。

延迟槽判断不放在该模块,是希望完全分离组合逻辑和时序逻辑。

2.2 D 级模块

该级包括 EXT、NPC、GRF、CMP 、CU、F_D_REG 模块。

EXT:用于进行立即数扩展。

端口方向功能
D_EXT_imm16input[15:0]用于被扩展的16位立即数
D_EXTopinput[3:0]EXT 模块功能选择控制信号: 0000:符号扩展;0001:无符号扩展; 0010: 低位补0;0011:低位补 1
D_EXT_imm32output[31:0]扩展得到的32位结果

CMP:得到两个源操作数的比较结果。

端口方向功能
D_CMP_opAinput[31:0]32位源操作数A
D_CMP_opBinput[31:0]32位源操作数B
D_CMPopinput[3:0]CMP模块功能选择控制信号:0000:A=?=B; 0001;A<?B有符号; 0010: A>?B有符号; 0011:A<?B无符号; 0100: A>?B无符号; 0101:A!=B?
D_CMP_resultoutput[31:0]比较结果

为了提高 CPU 吞吐率,将比较功能从 ALU 中分离出来,这是为了实现在 D级就能够判断下一条指令地址而做的改变。

GRF:寄存器文件,存储寄存器数据。

端口方向功能
clkinput时钟控制信号
resetinput同步复位信号
GRF_writeinput写控制信号,高电平有效
GRF_A1input[4:0]第一个读寄存器地址
GRF_A2input[4:0]第二个读寄存器地址
GRF_A3input[4:0]写寄存器的地址
GRF_WDinput[31:0]写寄存器的32位数据
GRF_RD1output[31:0]第一个读寄存器的数据
GRF_RD2output[31:0]第二个读寄存器的数据

在 GRF 中,支持内部转发,即在模块内自行判断 A3 和 A1, A2 相同的情况。

NPC:根据指令译码结果,计算下一条进入流水线的指令地址。

端口方向功能
D_NPC_PCinput[31:0]NPC 模块计算的基地址
D_NPCopinput[3:0]NPC 模块功能控制信号:0000:PC+4; 0001: PC+4+signed[imm16]+00; 0010: PC[31:28]+imm26+00; 0011:regdata; 0100: EPC
D_NPC_imm16input[15:0]指令16位立即数
D_NPC_imm26input[25:0]指令26位立即数
D_CMP_resultinput[31:0]CMP 的比结果
D_NPC_RegDatainput[31:0]寄存器存储的跳转地址
D_NPC_EPCinput[31:0]经过转发后得到的EPC值
D_NPC_PCnextoutput[31:0]计算得到的新PC

F_D_REG:F 到 D级流水线寄存器。

端口方向功能
clkinput时钟控制信号
resetinput高电平有效,同步复位信号,用于清空寄存器
Reqinput微处理器异常处理请求
F_D_GRF_ENinput高电平有效,寄存器使能信号
F_BDinputF 级指令的延迟槽标志
F_ExcCodeinput[4:0]F 级指令异常码
F_PCinput[31:0]F 级的指令地址
F_instrinput[31:0]F 级的指令机器码
D_BDoutputD 级指令的延迟槽标志
D_PCoutput[31:0]D 级的指令地址
D_instroutput[31:0]D 级的指令机器码
D_ExcCodeoutput[4:0]D 级指令异常码

注意流水线寄存器流出的异常码需要在该级做进一步修正,并非最终结果,这里仅为命名方便

CU:集中式译码,产生指令流水过程的各种控制信号,不包括冲突控制信号。

端口方向功能
D_CU_opcodeinput[5:0]指令的opcode字段
D_CU_funcinput[5:0]指令
D_EXTopoutput[3:0]D 级指令产生的 EXT 控制信号:0000:符号扩展;0001:无符号扩展; 0010: 低位补0;0011:低位补1
D_NPCopoutput[3:0]D 级指令产生的 NPC 控制信号:0000:PC+4; 0001: PC+4+signed[imm16]+00; 0010: PC[31:28]+imm26+00; 0011:regdata; 0100: EPC
D_CMPopoutput[3:0]D 级指令产生的 CMP 控制信号:0000:A=?=B; 0001;A<?B有符号; 0010: A>?B有符号; 0011:A<?B无符号; 0100: A>?B无符号; 0101:A!=B?
D_GRF_writeoutputD 级指令产生的寄存器写信号
D_ALUopoutput[4:0]D 级指令产生的 ALU 控制信号: 00000:A+B; 00001:A-B; 00010:A或B; 00011:A与B; 00100:B逻辑右移C; 00101: B逻辑左移C; 00110: B算数右移C; 00111;A<?B有符号; 01000: A>?B有符号; 01001:A<?B无符号; 01010: A>?B无符号
D_DM_writeoutputD 级指令产生的 DM 写信号
D_GRF_DatatoRegoutput[3:0]D 级指令写入寄存器的数据选择信号:0000:ALUout; 0001:DMout; 0010:PC+8; 0011:写入CMPresult; 0100:MDUout; 0101:CP0out
D_GRF_A3_seloutput[2:0]D 级指令目的寄存器选择控制信号:000:rd;001:rt;010:31号寄存器
D_ALU_Bseloutput[2:0]D 级指令产生的 ALU B端口数据选择信号:000:RD2 001:扩展后的32位立即数
D_rs_Tuseoutput[3:0]D 级指令rs段对应的寄存器使用所需时间
D_rt_Tuseoutput[3:0]D 级指令rt段对应的寄存器使用所需时间
D_Tnewoutput[3:0]D 级指令产生写入寄存器的数据所需时间
D_DMopoutput[1:0]D 级指令产生的 DM 控制信号:W(00)、H(01)、B(10)
D_BEopoutput[2:0]D 级指令产生的 BE 控制信号:000:无扩展;001:无符号字节数据扩展;010:符号字节数据扩展;011:无符号半字数据扩展;100:符号半字数据扩展
D_MDU_startoutputD 级指令的 MDU 开始工作信号
D_MDUopoutput[3:0]D 级指令的 MDU 功能选择信号:0000:无操作;0001:符号乘 A*B;0010:无符号乘 A*B;0011:符号除 A/B; 0100:无符号除 A/B;0101:写 HI 寄存器;0110:写 LO 寄存器
D_MDUout_seloutputD 级指令 MDU 输出结果选择信号:0:HI 寄存器;1:LO 寄存器
D_CP0_writeoutputD 级指令 CP0 写使能信号
D_instr_typeoutput[3:0]指令类型,用于异常码判断 0000:不会发生异常指令;0001:计算类指令,可能发生Ov;0010:store类指令,可能发生 AdEs 异常;0011:load类指令,可能发生 AdEL 异常;0100:跳转指令,判断 F 级是否为延迟槽
D_eretoutputD 级指令是否为 eret
D_CU_ExcCodeoutput[4:0]CU 译码出指令的异常码,可能是 SyscallRI

由于采用无脑转发的冒险解决方式,如果某一个字段的寄存器不被用到,那么其 Tuse 设置为 7,避免被 AT 法误判产生阻塞信号。

2.3 E 级模块

该级主要包括 ALU、D_E_REG、MDU 模块。

ALU:有计算功能的模块。

端口方向功能
E_ALU_opAinput[31:0]ALU 模块的第一个操作数
E_ALU_opBinput[31:0]ALU 模块的第二个操作数
E_ALU_opCinput[4:0]ALU 模块的第三个操作数,对应 R 型指令的shamt 字段
E_ALUopinput[4:0]ALU 功能选择控制信号: 00000:A+B; 00001:A-B; 00010:A或B; 00011:A与B; 00100:B逻辑右移C; 00101: B逻辑左移C; 00110: B算数右移C
E_instr_typeinput[3:0]E 级指令的类型,用于判定异常码
E_ALU_resultoutput[31:0]32位计算结果
E_ALU_ExcCodeoutput[4:0]ALU 部件得到的异常码

D_E_REG:D 到 E级流水线寄存器。

端口方向功能
clkinput时钟控制信号
resetinput高电平有效,同步复位信号,用于清空寄存器
Reqinput中断异常请求信号
flushinput阻塞时的清空信号,优先级最低
D_E_GRF_ENinput高电平有效,使能信号
D_PCinput[31:0]D 级的指令地址
D_instrinput[31:0]D 级的指令机器码
D_ALUopinput[4:0]D 级指令产生的 ALU 控制信号
D_DM_writeinputD 级指令产生的 DM 写信号
D_CP0_writeinputD 级指令的 CP0 写使能信号
D_GRF_writeinputD 级指令产生的 GRF 写信号
D_RD1input[31:0]从寄存器读出的第一个数据,已经过一次转发
D_RD2input[31:0]从寄存器读出的第二个数据,已经过一次转发
D_instr_shamtinput[4:0]D 级指令 shamt 段数据
D_EXT_imm32input[31:0]D 级 EXT 扩展的32位立即数
D_GRF_A3input[4:0]D 级指令的目的寄存器地址
D_CMP_resultinput[31:0]D 级指令 CMP 比较结果
D_GRF_DatatoReginput[3:0]D 级指令写入寄存器的数据选择信号
D_ALU_Bselinput[2:0]D 级指令产生的 ALU B端口数据选择信号
D_DMopinput[1:0]D 级指令的 DM 控制信号
D_MDU_startinputD 级指令的 MDU 开始信号
D_MDUopinput[3:0]D 级指令的 MDU 功能选择信号
D_MDUout_selinputD 级指令 MDU 输出结果选择信号
D_BEopinput[2:0]D 级指令产生的 BE 控制信号
D_rs_Tuseinput[3:0]D 级指令rs段对应的寄存器使用所需时间
D_rt_Tuseinput[3:0]D 级指令rt段对应的寄存器使用所需时间
D_Tnewinput[3:0]D 级指令得到写入寄存器的数据所需时间
D_BDinputD 级指令的延迟槽标志
D_eretinputD 级指令eret标记
D_ExcCodeinput[4:0]D 级指令的异常码
D_instr_typeinput[3:0]D 级指令的类型
E_RD1output[31:0]从 D 级流水至 E 级的第一个寄存器数据
E_RD2output[31:0]从 D 级流水至 E 级的第二个寄存器数据
E_instr_shamtoutput[4:0]E 级指令 shamt 段数据
E_EXT_imm32output[31:0]从 D 级流水至 E 级的扩展后的32位立即数
E_GRF_A3output[4:0]E 级指令的目的寄存器地址
E_PCoutput[31:0]E 级的指令地址
E_instroutput[31:0]E 级的指令机器码
E_ALUopoutput[31:0]E 级指令产生的 ALU 控制信号
E_DM_writeoutputE 级指令产生的 DM 写信号
E_GRF_writeoutputE 级指令产生的 GRF 写信号
E_CP0_writeoutputE 级指令的 CP0 写信号
E_CMP_resultoutput[31:0]E 级指令 CMP 比较结果
E_GRF_DatatoRegoutput[3:0]E 级指令写入寄存器的数据选择信号
E_ALU_Bseloutput[2:0]E 级指令产生的 ALU B端口数据选择信号
E_MDU_startoutputE 级指令的 MDU 开始信号
E_MDUopoutput[3:0]E 级指令的 MDU 功能选择信号
E_MDUout_seloutputE 级指令 MDU 输出结果选择信号
E_BEopoutput[2:0]E 级指令产生的 BE 控制信号
E_DMopoutput[1:0]E 级指令的 DM 控制信号
E_BDoutputE 级指令的延迟槽标志
E_eretoutputE 级指令eret标记
E_ExcCodeoutput[4:0]E 级指令的异常码
E_instr_typeoutput[3:0]E 级指令的类型
E_rs_Tuseoutput[3:0]E 级指令rs段对应的寄存器使用所需时间
E_rt_Tuseoutput[3:0]E 级指令rt段对应的寄存器使用所需时间
E_Tnewoutput[3:0]E 级指令产生写入寄存器的数据所需时间

MDU:乘除模块。

端口方向功能
clkinput时钟控制信号
resetinput同步复位信号
startinput表示 MDU 即将工作的信号,维持一周期
Reqinput中断异常请求
E_MDU_opAinput[31:0]MDU 模块第一个操作数
E_MDU_opBinput[31:0]MDU 模块第二个操作数
E_MDUopinput[3:0]MDU 功能选择信号:0000:无操作;0001:符号乘 A*B;0010:无符号乘 A*B;0011:符号除 A/B; 0100:无符号除 A/B;0101:写 HI 寄存器;0110:写 LO 寄存器
busyoutputMDU 工作信号,高位表示正在使用
HIoutput[31:0]HI 寄存器的数据
LOoutput[31:0]LO 寄存器的数据

乘除模块只会阻塞需要用到该模块的指令即 MULT、 MULTU、 DIV、 DIVU、MFHI、MFLO、MTHI、MTLO,其他指令可以从 D 级流水至 E 级,这就要求增加 HCU 的功能。

2.4 M 级模块

该级主要包括 DM、E_M_REG、DM_CU、BE、CP0 模块。

DM:数据存储器。

端口方向功能
m_inst_addrinput[31:0]M 级 PC
m_data_byteeninput[3:0]数据存储器功能选择信号,具体见下表
m_data_addrinput[31:0]待写入/读出的数据存储器相应地址
m_data_wdatainput[31:0]待写入数据存储器相应数据
m_data_rdataoutput[31:0]数据存储器存储的相应数据
byteen功能
1111m_data_wdata[31:24] 写入 byte3m_data_wdata[23:16] 写入 byte2m_data_wdata[15:8] 写入 byte1m_data_wdata[7:0] 写入 byte0
0011m_data_wdata[15:8] 写入 byte1m_data_wdata[7:0] 写入 byte0
1100m_data_wdata[31:24] 写入 byte3m_data_wdata[23:16] 写入 byte2
0001m_data_wdata[7:0] 写入 byte0
0010m_data_wdata[15:8] 写入 byte1
0100m_data_wdata[23:16] 写入 byte2
1000m_data_wdata[31:24] 写入 byte3

P7 实验中该模块已外置。

DM_CU:DM 控制信号译码器。

端口方向功能
M_DM_writeinput数据存储器写使能信号
M_instr_typeinput[3:0]M 级指令的类型
M_RD2_newinput[31:0]M 级转发后的 RD2
M_ALUoutinput[31:0]M 级指令产生的 ALU 计算结果
M_DMopinput[1:0]M 级指令的 DM 控制信号,用于判断字,半字,字节指令
M_byteenoutput[3:0]四位字节使能
M_DM_WDoutput[31:0]经过处理的 DM 写数据,主要是配合DM规格的使用
M_DM_CU_ExcCodeouput[4:0]DM_CU 得到的指令异常码

BE:数据存储器的数据扩展模块。

端口方向功能
M_BE_addrinput[31:0]从数据存储器读出的数据地址,连接ALUout
M_BE_ininput[31:0]从数据存储器读出的数据
M_BEopinput[2:0]BE 模块功能选择信号:000:无扩展;001:无符号字节数据扩展;010:符号字节数据扩展;011:无符号半字数据扩展;100:符号半字数据扩展
M_BEoutoutput[31:0]扩展得到的32位数据,连接 M_DMout

E_M_REG:E 到 M 级流水线寄存器。

端口方向功能
clkinput时钟控制信号
resetinput同步复位信号,高电平有效
Reqinput中断异常请求信号
E_M_REG_ENinput高电平有效,使能信号
E_PCinput[31:0]E 级指令的地址
E_instrinput[31:0]E 级指令的32位机器码
E_RD2input[31:0]E 级指令读出的第二个寄存器数据
E_DM_writeinputE 级指令的 DM 写控制信号
E_CP0_writeinputE 级指令的 CP0 写信号
E_DMopinput[1:0]E 级指令的 DM 控制信号
E_BEopinput[2:0]E 级指令产生的 BE 控制信号
E_MDUoutinput[31:0]E 级指令的MDU计算结果
E_ALUoutinput[31:0]E 级指令的 ALU 计算结果
E_GRF_A3input[4:0]E 级指令的目的寄存器
E_GRF_writeinputE 级指令的寄存器堆写信号
E_GRF_DatatoReginput[3:0]E 级指令写入寄存器的数据选择信号
E_CMP_resultinput[31:0]E 级指令 CMP 比较结果
E_BDinputE 级指令的延迟槽标志
E_eretinputE 级指令eret标记
E_instr_typeinput[3:0]E 级指令类型
E_ExcCodeinput[4:0]E 级指令的异常码
E_rs_Tuseinput[3:0]E 级指令rs段对应的寄存器使用所需时间
E_rt_Tuseinput[3:0]E 级指令rt段对应的寄存器使用所需时间
E_Tnewinput[3:0]E 级指令产生写入寄存器的数据所需时间
M_PCoutput[31:0]M 级指令的地址
M_instroutput[31:0]M 级指令的32位机器码
M_RD2output[31:0]M 级指令读出的第二个寄存器数据
M_ALUoutoutput[31:0]M 级指令的 ALU 计算结果
M_DM_writeoutputM 级指令的 DM 写控制信号
M_DMopoutput[1:0]M 级指令的 DM 控制信号
M_BEopoutput[2:0]M 级指令产生的 BE 控制信号
M_MDUoutoutput[31:0]M 级指令的MDU计算结果
M_GRF_A3output[4:0]M 级指令的目的寄存器
M_GRF_writeoutputM 级指令的寄存器堆写信号
M_CP0_writeoutputM 级指令的 CP0 写信号
M_GRF_DatatoRegoutput[3:0]M 级指令写入寄存器的数据选择信号
M_CMP_resultoutput[31:0]M 级指令 CMP 比较结果
M_BDoutputM 级指令的延迟槽标志
M_eretoutputM 级指令eret标记
M_instr_typeoutput[3:0]M 级指令类型
M_ExcCodeoutput[4:0]M 级指令的异常码
M_rs_Tuseoutput[3:0]M 级指令rs段对应的寄存器使用所需时间
M_rt_Tuseoutput[3:0]M 级指令rt段对应的寄存器使用所需时间
M_Tnewoutput[3:0]M 级指令产生写入寄存器的数据所需时间

CP0:中断异常协处理器。

端口方向功能
clkinput时钟控制信号
resetinput复位信号
CP0_writeinput写使能信号
CP0_addrinput[4:0]CP0寄存器地址
CP0_ininput[31:0]CP0的写入数据
EPC_ininput[31:0]受害指令地址
BD_ininput标记受害指令是否是延迟槽指令
ExcCodeIninput[4:0]记录中断异常类型
HWIntinput[5:0]中断信号
EXL_clrinputEXL 复位信号
CP0_outoutput[31:0]CP0 读出数据
EPC_outoutput[31:0]返回的 EPC
Reqoutput中断处理信号

2.5 W 级模块

该级主要包括 GRF、M_W_REG 模块。

M_W_REG:M 到 W 级流水线寄存器。

端口方向功能
clkinput时钟控制信号
resetinput同步复位信号,高电平有效
Reqinput中断异常信号
M_W_REG_ENinput高电平有效,使能信号
M_PCinput[31:0]M 级指令的地址
M_instrinput[31:0]M 级指令的32位机器码
M_ALUoutinput[31:0]M 级指令的 ALU 计算结果
M_GRF_A3input[4:0]M 级指令的目的寄存器
M_DMoutinput[31:0]M 级指令从 DM 读出的数据
M_GRF_writeinputM 级指令的寄存器堆写控制信号
M_GRF_DatatoReginput[3:0]M 级指令写入寄存器的数据选择信号
M_CMP_resultinput[31:0]M 级指令 CMP 比较结果
M_MDUoutinput[31:0]M 级指令的MDU计算结果
M_CP0_EPCinput[31:0]M 级指令 CP0 的 EPC 寄存器输出
M_CP0outinput[31:0]M 级指令 CP0 读出的寄存器数据
M_rs_Tuseinput[3:0]M 级指令rs段对应的寄存器使用所需时间
M_rt_Tuseinput[3:0]M 级指令rt段对应的寄存器使用所需时间
M_Tnewinput[3:0]M 级指令产生写入寄存器的数据所需时间
W_PCoutput[31:0]W 级指令的地址
W_instroutput[31:0]W 级指令的32位机器码
W_GRF_writeoutputW 级指令的寄存器堆写控制信号
W_GRF_A3output[4:0]W 级指令的目的寄存器
W_GRF_DatatoRegoutput[3:0]W 级指令写入寄存器的数据选择信号
W_ALUoutoutput[31:0]W 级指令的 ALU 计算结果
W_DMoutoutput[31:0]W 级指令从 DM 读出的数据
W_MDUoutoutput[31:0]W 级指令的MDU计算结果
W_CMP_resultoutput[31:0]W 级指令 CMP 比较结果
W_CP0_EPCoutput[31:0]W 级指令 CP0 的 EPC 寄存器输出
W_CP0outoutput[31:0]W 级指令 CP0 读出的寄存器数据
W_rs_Tuseoutput[3:0]W 级指令rs段对应的寄存器使用所需时间
W_rt_Tuseoutput[3:0]W 级指令rt段对应的寄存器使用所需时间
W_Tnewoutput[3:0]W 级指令产生写入寄存器的数据所需时间

HCU:冒险(hazard)控制单元,在下一部分重点分析。


三、冒险控制单元设计

在流水线 CPU 中最大最重要的问题就是冒险问题。为了解决这一问题,我专门设置了一个冒险控制单元,产生冒险控制信号。在我的设计中,冒险控制单元的逻辑基于 AT 法,属于无脑转发的方式,同时,为了处理乘除指令的冲突,还需要根据 busy,start信号以及对 mflo,mfhi 作特判。接下来从几个问题的回答来辅助设计冒险控制单元。

在流水线 CPU 中冒险的种类可以细分为:

  • 结构冒险:在 CO 实验体系结构中,结构冒险指寄存器文件需要在 D 级和 W 级同时被使用(读写)时并且读和写的寄存器为同一个寄存器时。本质还是一种数据冒险,我们采用 GRF 内部转发解决。
  • 控制冒险:指分支指令(如 beq )的判断结果会影响接下来指令的执行流的情况。在判断结果产生之前,我们无法预测分支是否会发生。然而,此时流水线还会继续取指,让后续指令进入流水线。这时就有可能导致错误的产生,即不该被执行的指令进入到了指令的执行流中。课程选择了延迟槽来解决这一冒险。
  • 数据冒险:流水线之所以会产生数据冒险,就是因为后面指令需求的数据,正好就是前面指令供给的数据,而后面指令在需要使用数据时,前面供给的数据还没有存入寄存器堆,从而导致后面的指令不能正常地读取到正确的数据,这也是我们重点需要关注的冒险种类。

冒险的解决方案:

  • 转发:简而言之,转发就是前方流水中已经得到结果而当前阶段有需要,不等该结果写回寄存器而直接重定向到当前阶段,提高吞吐率。不难发现,需要转发的地方是需要寄存器数据的地方,主要在于 D 级和 E 级。能够提供转发数据的地方主在 E 级,M 级和 W 级。
  • 阻塞:阻塞是指当发生数据依赖时,只让前一条指令执行,而后一条指令被阻塞在流水线的某个阶段,并不向下执行,等待前一条指令执行完成(或者执行到没有冲突的时候),再解除后一条的阻塞状态。显然阻塞降低 CPU 的性能,所以只在必要时刻才阻塞。

什么时候转发和阻塞:

  • 转发的需求者应该在当前阶段需要用到寄存器的数据,有 D 级(CMP,NPC)、E 级(ALU)、E 级(DM)。
  • 转发的供给者应当在当前阶段的流水线寄存器中保存了会写入寄存器堆的数据。(注意,本实验要求解决数据冒险而设计的转发数据来源必须是某级流水线寄存器,不允许对功能部件的输出直接进行转发。
  • 可能写入寄存器堆的数据有 E_CMP_result、E_PC+8、M_PC+8、M_ALUout、M_CMP_result、M_MDUout、W_PC+8、W_ALUout、W_DMout、W_CMP_result、W_MUDout、W_CP0out
  • 当前指令的所在阶段的 Tuse >= 之后流水线中指令的 Tnew 时,可以继续流水,反之则必须阻塞(实验要求阻塞必须在 D级)。这也意味着我们必须对处在 D 级的指令做好判断,如果阻塞,需要同时清空 E 级流水线寄存器(插入一个气泡)。

至此,我们就可以进行模块的设计:

端口方向功能
D_instrinput[31:0]用于解析 D 级指令的两个源寄存器编号 rs rt
D_rs_Tuseinput[3:0]D 级指令 rs 寄存器使用时钟周期
D_rt_Tuseinput[3:0]D 级指令 rt 寄存器使用时钟周期
E_instrinput[31:0]用于解析 E 级指令的两个源寄存器编号 rs rt
E_rs_Tuseinput[3:0]E 级指令 rs 寄存器使用时钟周期
E_rt_Tuseinput[3:0]E 级指令 rt 寄存器使用时钟周期
E_Tnewinput[3:0]E 级指令产生写入结果的时钟周期
M_instrinput[31:0]用于解析 M 级指令的一 个源寄存器编号 rt
M_rt_Tuseinput[3:0]M 级指令 rt 寄存器使用时钟周期
M_Tnewinput[3:0]M 级指令产生写入结果的时钟周期
W_Tnewinput[3:0]W 级指令产生写入结果的时钟周期
E_GRF_A3input[4:0]E 级指令的目的寄存器
E_GRF_writeinput用于判断 E 级指令是否为写寄存器指令
E_GRF_DatatoReginput[3:0]E 级指令写入寄存器的数据选择信号:0000:ALUout; 0001:DMout; 0010:PC+8; 0011:写入CMPresult; 0100:MDUout;0101:CP0out
M_GRF_A3input[4:0]M 级指令的目的寄存器
M_GRF_writeinput用于判断 M 级指令是否为写寄存器指令
M_GRF_DatatoReginput[3:0]M 级指令写入寄存器的数据选择信号:0000:ALUout; 0001:DMout; 0010:PC+8; 0011:写入CMPresult; 0100:MDUout;0101:CP0out
W_GRF_A3input[4:0]W 级指令的目的寄存器
W_GRF_writeinput用于判断 W 级指令是否为写寄存器指令
W_GRF_DatatoReginput[3:0]W 级指令写入寄存器的数据选择信号:0000:ALUout; 0001:DMout; 0010:PC+8; 0011:写入CMPresult; 0100:MDUout;0101:CP0out
E_busyinputE 级指令的 MDU 使用信号
E_startinputE 级指令的 MDU 开始使用信号
D_startinputD 级指令的 MDU 开始使用信号
stalloutput阻塞信号,高电平有效,如果阻塞,则 stall 接到D_E_REG复位端,取反接到 PC 使能端,F_D_REG 使能端
D_FW_EPC_seloutput[2:0]D 级的 NPC 的 EPC 选择转发信号: 000 :W_CP0_EPC; 001: M_RD2; 010: E_RD2
D_FW_rs_seloutput[4:0]D 级指令 rs 端寄存器转发选择信号:00000: keep; 00001: E_PC+8; 00010: E_CMP_result; 00011: M_PC+8; 00100: M_ALUout; 00101: M_CMP_result; 00110: M_MDUout
D_FW_rt_seloutput[4:0]D 级指令 rt 端寄存器转发选择信号:00000: keep; 00001: E_PC+8; 00010: E_CMP_result; 00011: M_PC+8、00100: M_ALUout; 00101: M_CMP_result; 00110: M_MDUout
E_FW_rs_seloutput[4:0]E 级指令 rs 端寄存器转发选择信号:00000: keep; 00001: M_ALUout; 00010: M_PC+8; 00011: M_CMP_result; 00100: M_MDUout; 00101: W_ALUout; 00110: W_DMout; 00111: W_PC+8; 01000: W_CMP_result; 01001: W_MDUout; 01010: W_CP0out
E_FW_rt_seloutput[4:0]E 级指令 rt 端寄存器转发选择信号:00000: keep; 00001: M_ALUout; 00010: M_PC+8; 00011: M_CMP_result; 00100: M_MDUout; 00101: W_ALUout; 00110: W_DMout; 00111: W_PC+8; 01000: W_CMP_result; 01001: W_MDUout; 01010: W_CP0out
M_FW_rt_seloutput[4:0]M 级指令 rt 端寄存器转发选择信号: 00000: keep; 00001: W_ALUout; 00010: W_DMout; 00011: W_PC+8; 00100: W_CMP_result; 00101: W_MDUout; 00110: W_CP0out
E_flushoutputE 级流水线寄存器的清空信号

四、指令实现

ori 指令

功能描述 : GPR[rt] <- GPR[rs] OR unsignedextend(immediate)

opCP0_writeGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
001101010001000000010001000000000000000000001X04'd14'd74'd2

lui 指令

功能描述:GPR[rt] <- immediate||16'd0

opCP0_writeGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
00111101000100000010000000000000000000XXXX001004'd14'd74'd2

jal 指令

功能描述:PC <- (PC[31:28] || instr_immediate || 00); GPR[31] <- PC + 4

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000011100100010XXXXXXXXX0000000000010XXXX000004'd74'd74'd1

jr 指令

功能描述:PC <- GPR[rs]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
00000000100000XXXXXXXXXXXXXXXX0000000000011XXXX000004'd04'd74'd0

add 指令: 功能描述:GPR[rd] <- GPR[rs]+GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000100000100000000XXXX000000000000000000XXXX000004'd14'd14'd2

sub 指令

功能描述 GPR[rd] <- GPR[rs] - GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000100010100000000XXXX000010000000000000XXXX000004'd14'd14'd2

beq 指令

功能描述:if (GPR[rs] == GPR[rt]) PC <- PC + 4 + sign_extend(offset||00) else PC <- PC + 4

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
00010000XXXXXXXXXXXXXXXX00000000000010000XXX004'd04'd04'd0

lw 指令

功能描述:GPR[rt] <= memory[GPR[rs]+offset]

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
10001110001000100000000000000000000000000001004'd14'd74'd3

sw 指令

功能描述:memory[GPR[rs]+offset] <- GPR[rt]

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
10101101XXXXXXX0000000000000000000000XXXX001004'd14'd24'd0

mult 指令

功能描述:(HI, LO) <- GPR[rs]×GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000011000000000000XXXX000000000000010000XXXX000014'd14'd14'd0

div 指令

功能描述:(HI, LO) <- GPR[rs] / GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000011010000000000XXXX000000000000110000XXXX000014'd14'd14'd0

multu 指令

功能描述:(HI, LO) <- GPR[rs]×GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000011001000000000XXXX000000000000100000XXXX000014'd14'd14'd0

divu 指令

功能描述:(HI, LO) <- GPR[rs] / GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000011011000000000XXXX000000000001000000XXXX000014'd14'd14'd0

mfhi 指令

功能描述:GPR[rd] <- HI

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000010000100000100XXXX000000000000000000XXXX000004'd74'd74'd2

mflo 指令

功能描述:GPR[rd] <- LO

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000010010100000100XXXX000000000000000000XXXX000104'd74'd74'd2

mthi 指令

功能描述:HI <- GPR[rs]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000010001000000000XXXX000000000001010000XXXX000014'd14'd74'd0

mtlo 指令

功能描述:LO <- GPR[rs]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000010011000000000XXXX000000000001100000XXXX000014'd14'd74'd0

and 指令

功能描述:GPR[rd] <- GPR[rs] and GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000100100100000000XXXX000110000000000000XXXX000004'd14'd14'd2

or 指令

功能描述:GPR[rd] <- GPR[rs] or GPR[rt]

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000100101100000000XXXX000100000000000000XXXX000004'd14'd14'd2

slt 指令

功能描述:GPR[rd] <- (GPR[rs] < GPR[rt])

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000101010100000000XXXX0011100000000000000000000004'd14'd14'd2

sltu 指令

功能描述:GPR[rd] <- (0||GPR[rs]< 0||GPR[rt])

opfuncGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
000000101011100000000XXXX0100100000000000000000000004'd14'd14'd2

addi 指令

功能描述: GPR[rt] <- GPR[rs] + signed_extend(immediate)

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
00100010001000000000000000000000000000000001004'd14'd74'd2

andi 指令

功能描述: GPR[rt] <- GPR[rs] AND zero_extend(immediate)

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
00110010001000000010001100000000000000000001004'd14'd74'd2

bne 指令

功能描述:if (GPR[rs] != GPR[rt]) PC <- PC + 4 + sign_extend(offset||00) else PC <- PC + 4

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
00010100XXXXXXXXXXXXXXXX00000000000010101XXX004'd04'd04'd0

sh 指令

功能描述:memory[Addr]15+16byte..16byte <- GPR[rt]15:0

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
10100101XXXXXXX0000000000100000000000XXXX001004'd14'd24'd0

sb 指令

功能描述:memory[Addr]7+8byte..8byte <- GPR[rt]7:0

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
10100001XXXXXXX0000000001000000000000XXXX001004'd14'd24'd0

lh 指令

功能描述:GPR[rt] <- sign_ext(memword15+16byte..16byte)

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
1000011000100010000000000110000000000XXXX001004'd14'd74'd3

lb 指令

功能描述:GPR[rt] <- sign_ext(memword7+8byte..8byte)

opGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
1000001000100010000000001001000000000XXXX001004'd14'd74'd3

eret 指令

功能描述:PC <- CP0[epc]

opCP0_writeGRF_writeDM_writeGRF_A3sel[2:0]DatatoReg[3:0]EXTop[3:0]ALUop[4:0]DMop[1:0]BEop[2:0]MDUop[3:0]NPCop[3:0]CMPop[3:0]ALU_Bsel[2:0]MDUout_selMDU_startrs_Tuse[3:0]rt_Tuse[3:0]Tnew[3:0]
10000000000000000000000000000000000100XXXX000004'd74'd74'd0

五、测试方案

根据指令产生写数据,以及使用寄存器数据的情况可以把指令分类:

----按 Tnew 分类-----
S1.Tnew = 0: j, jr, beq, bne, sw, sh, sb, mult, multu, div, divu, mthi, mtlo, syscall, mtc0, eret
S2.Tnew = 1: jal
S3.Tnew = 2: ori, lui, add, sub, mfhi, mflo, and, or, slt, sltu, addi, andi
S4.Tnew = 3: lw, lh, lb, mfc0 

----按 Min_Tuse 分类-----
K1.Tuse = 0: beq, bne, jr, eret
K2.Tuse = 1: add, sub, and, or, slt, sltu, lui, addi, andi, ori, lui, lb, lh, lw, sw, sh, sb, mult, multu, div, divu, mthi, mtlo
K3.Tuse = 2: mtc0
K4.无 Tuse : mfhi, mflo, jal, mfc0, syscall, eret

----按乘除部件使用情况分类------
T1: mult, multu, divu, div
T2: mflo, mfhi
T3:mthi, mtlo
T4:others

测试阻塞

DecodeExecuteMemoryWriteBack
K1S3/S4**
K1*S4*
K2S4**

测试转发: 转发的数据可能来自 PC+8,ALUout,DMout。

DecodeExecuteMemoryWriteBack
K1S2**
K2S2\S3**
K2*S2\S3\S4*
K2**s2\s3\s4

对乘除指令单独测试,保证没有内部异常情况

DecodeExecuteMemoryWriteBack
T1T1**
T2T1**
T3T1**
T2T3**
T4T1**

除了保证乘除指令间可以正确阻塞以外,还应测试当乘除部件 busy 时,是否可以继续正确执行其他无关指令,保证流水线效率。需要额外关注寄存器为 $0 的情况。

测试各类异常指令是否能够正确识别

assembly
// 计算类指令溢出
lui $1, 0x8000
lui $2, 0x8000
add $0, $1, $2
lui $2, 0x4000
sub $0, $2, $1
addi $0, $2, 0x40000000
// 指令地址溢出和未对齐
jr $2
nop
addi $2, $0, 0x3001
jr $2
nop
// 未知指令
jalr $0, $1
// syscall
syscall
// 存数地址加法溢出,未对齐
lui $1, 0x8000
sw $1, -4($1)
sh $1, -2($1)
sb $1, -1($1)
sw $1 , 1($0)
sw $1, 2($0)
sw $1, 3($0)
sh $1, 1($0)
sh $1, 3($0)
// 取数地址加法溢出, 未对齐
lui $1, 0x8000
lw $0, -4($1)
lh $0, -2($1)
lb $0, -1($1)
lw $0, 1($0)
lw $0, 2($0)
lw $0, 3($0)
lh $0, 1($0)
lh $0, 3($0)
// lb, lh 地址为 Timer
lh $0, 0x7f00($0)
lh $0, 0x7f04($0)
lh $0, 0x7f08($0)
lb $0, 0x7f10($0)
lb $0, 0x7f14($0)
lb $0, 0x7f18($0)
// sb,sh 地址为 Timer
sh $1, 0x7f00($0)
sh $1, 0x7f04($0)
sh $1, 0x7f08($0)
sb $1, 0x7f10($0)
sb $1, 0x7f14($0)
sb $1, 0x7f18($0)
// load, store 地址溢出
sw $1, 0x3000($0)
sw $1, 0x4180($0)
sw $1, 0xffff($0)
sh $1, 0x3000($0)
sh $1, 0x4180($0)
sh $1, 0xffff($0)
sb $1, 0x3000($0)
sb $1, 0x4180($0)
sb $1, 0xffff($0)
// store 向 count 存值
sw $1, 0x7f08($0)
sw $1, 0x7f18($0)
sh $1, 0x7f08($0)
sh $1, 0x7f18($0)
sb $1, 0x7f08($0)
sb $1, 0x7f18($0)

测试各种位置指令异常是否可以正常识别:1. 跳转指令错误;2. 跳转指令的目标指令;3. 乘除指令前一条;4. 乘除指令后一条;5. 延迟槽;6. 其他。


六、思考题解答

Q1:请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的?

A:键盘、鼠标这类的低速设备是通过中断请求的方式进行IO操作的。即当键盘上按下一个按键的时候,键盘会发出一个中断信号,中断信号经过中断控制器传到CPU,然后CPU根据不同的中断号执行不同的中断响应程序,然后进行相应的IO操作,把按下的按键编码读到寄存器(或者鼠标的操作),最后放入内存中。

Q2:请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法)

A:指定好中断异常处理程序的入口,就可以从硬件层面实现这个部分。让硬件实现进入中断处理,软件实现中断处理的内容,各司其职,更加清晰。如果自定义入口地址,也可以提供我们需要的功能,但是在程序运行开始之前需要先将自定义的地址取出来记录,用作后续程序运行时中断异常处理入口。而且每次发生异常时,就要从保存地址中取出这个入口,导致效率低下。

Q3:为何与外设通信需要 Bridge?

A:CPU 的外设往往有很多种,而且每一种外设都有自己独特的地址和数据。CPU 不能为每个设备都提供一套地址/数据,否则 CPU 的设计就会非常复杂,因此通过 Bridge 这个模块来实现 CPU 的数据与外设的数据之间的转换,从而使CPU 的设计不需要做过多改变,增加外设也只需要在 Bridge 内部做修改即可。这样的分工减少了设计 CPU 的难度,也让微处理器的设计更加清晰。

Q4:请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并分别针对每一种模式绘制状态移图。

A:中断模式0:将计数器写使能变为0,并且保持中断信号持续有效,直到计数器写使能为1。该模式通常用于定时中断。

中断模式1:只保持一周期的中断信号,并重新载入计数器初值,再次倒计时。该模式通常用于产生周期性脉冲。

状态转移图

Q5:倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息?

A:第一,宏观指令出错,第二指令的延迟槽标记 BD 是 0,如果被阻塞的是延迟槽指令,那么会导致写入的 EPC 出错。清空流水线时,产生的空泡应保留被阻塞指令的 PC 和 BD 信号。

Q6:为什么 jalr 指令为什么不能写成 jalr $31, $31

A:如果 jalr 的两个寄存器相同,那么当他的延迟槽指令出现异常后,执行完异常处理程序返回时,jalr 的跳转 PC 已经变了,这样改变了原先 jalr 的行为,导致指令错误执行。