计组P5课下作业总结
我的设计思路
我在P3和P4的单周期CPU设计中思路是非常固定的:那就是首先将单个元件设计好,然后再进行接线,是一种元件驱动式的设计方法。但是这种方法经过我的思考在P5中似乎并不适用:因为在P3和P4中我都可以根据元件功能快速的写出每个元件的端口情况,但是在流水线设计中难以做到,因为控制信号过多和转发暂停判断会让端口非常复杂。
因此本P中我打算使用元件和功能共同驱动的思路:即首先搭出最基本的用来实现功能的元件,例如PC,GRF,ALU,DM等,然后构建集中式译码的控制器(我打算使用集中式译码),然后进一步实现四级寄存器的数据传输,最后构建控制转发暂停的冒险控制单元,最后的最后进行接线。这样我们就可以不断根据功能添加端口,最后进行合成就获得了最终的元件。
总体架构
基本功能元件构建
F级基本元件
PC
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
F_PC_DI[31:0] | I | 下一条指令地址输入 |
F_PC_DO[31:0] | O | 当前指令地址输出 |
本题同样要求PC的初始地址为0x3000,因此由于我们依然会设定IM从0地址开始读入,所以我们需要对每一条PC输出的地址减0x3000来取得正确的指令。
IM
端口 | 方向 | 功能 |
---|---|---|
F_IM_PC[31:0] | I | 当前要执行的指令所在地址 |
F_IM_Instr[31:0] | O | 当前指令的二进制机器码 |
利用reg阵列构建ROM来存储所有指令的16进制机器码,通过$readmemh指令实现读取(从0x0地址开始读取即可,这样不浪费空间)。
正如PC元件中提到的,本体需要在PC给出的指令初始地址基础上减0x3000才能取到真正的对应指令。
D级基本元件
GRF
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
D_GRF_A1[4:0] | I | 读取的第一个寄存器编号(rs) |
D_GRF_A2[4:0] | I | 读取的第二个寄存器编号(rt) |
D_GRF_A3[4:0] | I | 写入的寄存器编号 |
D_GRF_Wr | I | GRF中寄存器写入的写使能控制信号 |
D_GRF_WD[31:0] | I | GRF中需要被写入A3的值 |
D_GRF_PC[31:0] | I | 当前执行写入操作的指令地址 |
D_GRF_RD1[31:0] | O | 从A1号寄存器中读取的值 |
D_GRF_RD2[31:0] | O | 从A2号寄存器中读取的值 |
同样利用32个32位reg寄存器构成的阵列来模拟CPU中的寄存器堆。同时由于和P4一样需要展示寄存器的写入情况,因此需要将当前正在执行的写入指令的地址值PC传入(我认为它既然是写入指令,那么应该是从W级寄存器中得到的而不能是从F级传入的。这点我们之后再进行设计)
EXT0
端口 | 方向 | 功能 |
---|---|---|
D_EXT_Input[15:0] | I | 机器码的0-15位作为立即数进行扩展 |
D_EXT_EXTOp[1:0] | I | 确定扩展方式的控制信号:00:0扩展 01:符号扩展 10:低位补0(lui) |
D_EXT_Output[31:0] | O | 输出32位扩展结果 |
由于流水线CPU我们希望尽量减少数据冒险以减少转发和暂停情况,所以我们希望Tnew尽可能小,那就要让计算都尽可能前移,因此扩展模块在D级中就可以完成即将其归为D级。
同时,我希望能够将跳转相关的操作全部放入NPC,而计算相关操作不经过NPC,所以这里EXT中的扩展仅针对后续计算有关的扩展,而beq和jal指令中的扩展操作都放在NPC模块中完成。
CMP
端口 | 方向 | 功能 |
---|---|---|
D_CMP_RD1[31:0] | I | A1号寄存器读出的32位二进制数 |
D_CMP_RD2[31:0] | I | A2号寄存器读出的32位二进制数 |
D_CMP_Result | O | GPR[rs]和GPR[rt]的比较结果,如果相等输出1,否则输出0 |
和EXT放到D级中的理由相同,我们希望能够将指令的完成尽量前移。在P3和P4中,我们的rs和rt对应寄存器值的比较都是在ALU模块中完成的,而ALU模块只可能在E级中。因此我们可以在这两个值流出GRF后就立刻用一个比较模块进行比较,就可以将beq等指令的完成前移到D级。这就是我构建一个新的比较模块的原因。
NPC
端口 | 方向 | 功能 |
---|---|---|
D_NPC_PC[31:0] | I | 输入当前指令的地址 |
D_NPC_NPCOp[2:0] | I | 控制NPC的计算方式的信号:000:PC+4 001:beq 010:jal 011:jr |
D_NPC_Zero | I | CMP模块中GPR[rs]和GPR[rt]的比较结果,相等输入1,否则输入0 |
D_NPC_Imm26[25:0] | I | 针对jal指令取的二进制机器码的26位立即数 |
D_NPC_Imm16[15:0] | I | 针对beq指令取的二进制机器码的16位立即数 |
D_NPC_RA[31:0] | I | 针对jr指令输入的GPR[rs]值 |
D_NPC_NPC[31:0] | O | 计算所得下一条指令的32位二进制地址 |
后续可能要考虑跳转的延迟槽问题,同时PC信息应该会随着DEMW四级寄存器不断转移,所以PC8信息是否需要从这里给出也可以后续再进行决定,如果决定从其他位置引入就可以省略这个接口。
这里的控制信号选择三位是为了后续如果还有其他跳转方式可以直接进行控制信号编码而不需要让控制信号的位数变化。
E级基本元件
ALU
端口 | 方向 | 功能 |
---|---|---|
E_ALU_A[31:0] | I | ALU模块第一个读入数GPR[rs] |
E_ALU_B[31:0] | I | ALU模块第二个读入数GPR[rt]或立即数的扩展EXT |
E_ALU_MSel[2:0] | I | ALU模块进行什么运算的控制信号:000:A+B 001:A-B 010:A 011:B |
E_ALU_C[31:0] | O | ALU模块的计算结果输出 |
由于比较模块已经前移至D级中的比较器中,因此这里不需要有P4中的Zero输出,同时这里MSel的位数为3也是为了后续的指令添加。
M级基本元件
DM
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
M_DM_Addr[31:0] | I | ALU计算所得需要被写入内存或从内存读取的地址 |
M_DM_WD[31:0] | I | 需要被写入Addr地址的32位二进制数据 |
M_DM_PC[31:0] | I | 这条写入指令在IM中的地址 |
M_DM_Wr | I | 控制DM执行读取功能还是写入功能的控制信号,0:读取 1:写入 |
M_DM_RD[31:0] | O | 将A地址中的数据从RD读出 |
这里也是使用reg的阵列模拟RAM的效果。同时由于每次写入内存时也需要输出写入信息,其中包括本条写入指令的地址,因而也需要PC的输入。
W级基本元件
GRF
由于本题GRF在元件内进行内部转发,因此不需要在W级重新创建新原件,GRF情况见上。
控制器CU(在D级集中式译码)
端口 | 方向 | 功能 |
---|---|---|
D_CU_opcode[5:0] | I | 输入当前指令opcode |
D_CU_funct[5:0] | I | 输入当前指令funct |
D_CU_NPCOp[2:0] | O | 当前指令的跳转方式:000:PC+4 001:beq 010:jal 011:jr |
D_CU_WRSel[1:0] | O | 当前指令回写寄存器的选择:00:rt 01:rd 10:31 |
D_CU_WDSel[2:0] | O | 当前指令回写寄存器的内容选择:000:ALU 001:DM 010:PC8 |
D_CU_GRFWr | O | 当前指令是否需要回写寄存器:0:不允许回写 1:允许回写 |
D_CU_EXTOp[2:0] | O | 当前指令中立即数的扩展方式:00:0扩展 01:符号扩展 10:低位补0(lui) |
D_CU_BSel | O | 选择输入ALU的B口的数据:0:RD2 1:EXT |
D_CU_MSel[2:0] | O | 选择ALU的计算方式:000:A+B 001:A-B 010:A |
D_CU_DMWr | O | 选择内存是否可以写入:0:不可写入 1:可以写入 |
这里的控制信号是和单周期CPU一致的,后续还应该判断此处的Tnew和Tuse并流水,这些到暂停和转发的相关设计处再进行添加。
以下附上各个指令的机器码和对应控制信号。
指令 | 机器码 |
---|---|
add | opcode:000000,funct:100000 |
sub | opcode:000000,funct:100010 |
ori | opcode:001101 |
lw | opcode:100011 |
sw | opcode:101011 |
lui | opcode:001111 |
beq | opcode:000100 |
jal | opcode:000011 |
jr | opcode:000000,funct:001000 |
nop | opcode:000000,funct:000000 |
指令 | NPCOp | WRSel | WDSel | RFWr | EXTOp | BSel | MSel | DMWr |
---|---|---|---|---|---|---|---|---|
nop | 000 | 000 | 000 | 0 | 000 | 0 | 000 | 0 |
add | 000 | 001 | 000 | 1 | 000 | 0 | 000 | 0 |
sub | 000 | 001 | 000 | 1 | 000 | 0 | 001 | 0 |
ori | 000 | 000 | 000 | 1 | 000 | 1 | 010 | 0 |
lw | 000 | 000 | 001 | 1 | 001 | 1 | 000 | 0 |
sw | 000 | 000 | 000 | 0 | 001 | 1 | 000 | 1 |
lui | 000 | 000 | 000 | 1 | 010 | 1 | 011 | 0 |
beq | 001 | 000 | 000 | 0 | 000 | 0 | 000 | 0 |
jal | 010 | 010 | 010 | 1 | 000 | 0 | 000 | 0 |
jr | 011 | 000 | 000 | 0 | 000 | 0 | 000 | 0 |
四级流水线寄存器的构建
D级寄存器(IF/ID)
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
D_F_PC[31:0] | I | F级取得的PC地址 |
D_F_Instr[31:0] | I | F级通过PC地址从IM中取得的机器码 |
D_D_PC[31:0] | O | 需要被传输进入D级的PC地址 |
D_D_Instr[31:0] | O | 需要被传输进入D级的机器码 |
对于后续需要写入的PC4和需要输出的写入指令的PC进行合并,只传输每条信息的PC,然后再根据具体情况传入计算元件。
E级寄存器(ID/EX)
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
E_D_PC[31:0] | I | 当前指令的地址 |
E_D_RD1[31:0] | I | GRF输出的第一个寄存器中的内容 |
E_D_RD2[31:0] | I | GRF输出的第二个寄存器中的内容 |
E_D_EXTOut[31:0] | I | EXT输出的立即数扩展内容 |
E_D_GRFA3[4:0] | I | 回写GRF的寄存器地址 |
E_D_WRSel[2:0] | I | 当前指令回写寄存器的选择控制信号 |
E_D_WDSel[2:0] | I | 当前指令回写寄存器的内容选择控制信号 |
E_D_GRFWr | I | 当前指令是否需要回写寄存器的控制信号 |
E_D_BSel | I | 选择输入ALU的B口的数据控制信号 |
E_D_MSel[2:0] | I | 选择ALU的计算方式控制信号 |
E_D_DMWr | I | 选择内存是否可以写入的控制信号 |
E_E_PC[31:0] | O | 当前指令的地址 |
E_E_RD1[31:0] | O | GRF输出的第一个寄存器中的内容 |
E_E_RD2[31:0] | O | GRF输出的第二个寄存器中的内容 |
E_E_EXTOut[31:0] | O | EXT输出的立即数扩展内容 |
E_E_GRFA3[4:0] | O | 回写GRF的寄存器地址 |
E_E_WRSel[2:0] | O | 当前指令回写寄存器的选择控制信号 |
E_E_WDSel[2:0] | O | 当前指令回写寄存器的内容选择控制信号 |
E_E_GRFWr | O | 当前指令是否需要回写寄存器的控制信号 |
E_E_BSel | O | 选择输入ALU的B口的数据控制信号 |
E_E_MSel[2:0] | O | 选择ALU的计算方式控制信号 |
E_E_DMWr | O | 选择内存是否可以写入的控制信号 |
这些寄存器需要存储和传输的东西的本质就是:要传输这条机器码的所有相关信息,直到它在这一级被使用了,就不需要继续传递。例如在E级寄存器上就不需要储存CMP元件的比较结果,因为CMP的比较结果在F和D中就使用完毕。所以我们只需要对需要使用的信号进行无脑传输即可。
同时有输入就有输出,所以有来自上一级的指令相关内容,也就有传递到下一级的指令相关内容。所以需要让这些流水线寄存器输入输出完全对称。
M级寄存器(EX/MEM)
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
M_E_PC[31:0] | I | 当前指令的所在地址 |
M_E_RD2[31:0] | I | GRF输出的第二个寄存器中的内容RD2 |
M_E_ALUAns[31:0] | I | ALU计算出的结果 |
M_E_GRFA3[4:0] | I | 回写GRF的寄存器地址 |
M_E_WRSel[2:0] | I | 当前指令回写寄存器的选择控制信号 |
M_E_WDSel[2:0] | I | 当前指令回写寄存器的内容选择控制信号 |
M_E_GRFWr | I | 当前指令是否需要回写寄存器的控制信号 |
M_E_DMWr | I | 选择内存是否可以写入的控制信号 |
M_M_PC[31:0] | O | 当前指令的所在地址 |
M_M_RD2[31:0] | O | GRF输出的第二个寄存器中的内容RD2 |
M_M_ALUAns[31:0] | O | ALU计算出的结果 |
M_M_GRFA3[4:0] | O | 回写GRF的寄存器地址 |
M_M_WRSel[2:0] | O | 当前指令回写寄存器的选择控制信号 |
M_M_WDSel[2:0] | O | 当前指令回写寄存器的内容选择控制信号 |
M_M_GRFWr | O | 当前指令是否需要回写寄存器的控制信号 |
M_M_DMWr | O | 选择内存是否可以写入的控制信号 |
和D级寄存器的端口设计方式是相似的,不再赘述。
W级寄存器(MEM/WB)
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 时钟信号输入 |
Reset | I | 同步复位信号输入 |
W_M_PC[31:0] | I | 当前指令的所在地址 |
W_M_DMOut[31:0] | I | DM输出的内存中的数据 |
W_M_ALUAns[31:0] | I | ALU计算出的结果 |
W_M_GRFA3[4:0] | I | 回写GRF的寄存器地址 |
W_M_WRSel[2:0] | I | 当前指令回写寄存器的选择控制信号 |
W_M_WDSel[2:0] | I | 当前指令回写寄存器的内容选择控制信号 |
W_M_GRFWr | I | 当前指令是否需要回写寄存器的控制信号 |
W_W_PC[31:0] | O | 当前指令的所在地址 |
W_W_DMOut[31:0] | O | DM输出的内存中的数据 |
W_W_ALUAns[31:0] | O | ALU计算出的结果 |
W_W_GRFA3[4:0] | O | 回写GRF的寄存器地址 |
W_W_WRSel[2:0] | O | 当前指令回写寄存器的选择控制信号 |
W_W_WDSel[2:0] | O | 当前指令回写寄存器的内容选择控制信号 |
W_W_GRFWr | O | 当前指令是否需要回写寄存器的控制信号 |
和以上寄存器的设计相似,不再赘述。
流水线冒险的避免
设计思路
- 冒险的种类
冒险的种类分为:结构冒险,控制冒险和数据冒险。
结构冒险是指不同时间使用同一个数据位置的数据所造成的冲突。因为本题的IM和DM分开,因此不存在内存中的结构冒险。但是可能出现GRF中的结构冒险,即在D级读取和W级写入相同寄存器的数据,此时需要让GRF内部转发。
控制冒险是指分支指令(beq)的判断影响之后的指令运行的情况,由于判断不及时会让后续指令流入流水线。我们可以利用延迟槽解决这一问题。(pc+4变为pc+8)
数据冒险是指后面指令需求的数据正好是前面指令提供的数据,但是在后续指令需求时前面指令提供的数据还没有存入,导致后续指令读取的数据不正确。我们可以利用AT法建模,比较Tuse和Tnew值来决定转发和暂停来解决。
- 结构冒险
如上,我们唯一要考虑的情况就是D级读取与W级写入操作都针对同一个寄存器,此时导致D级无法读取正确的寄存器信息。此时我们就在GRF内部让D级指令中那个冲突的寄存器输出正在从W级存入的寄存器内容即可。
- AT法建模
我们需要首先针对每一级寄存器都给出当前该级寄存器中指令的Tnew和Tuse。所谓Tnew,就是该指令还有几个周期会生成新的数据并存储到流水线寄存器中。例如add指令,当它在E级时,它已经通过ALU算出了值,但是还没有存入M级寄存器中,因此其Tnew就是1,但是在M和W级中Tnew就是0。因此我们发现不同指令在不同寄存器中的Tnew全都不同,我们需要分指令分寄存器计算。最终储存在流水线寄存器中
*上下级寄存器针对同一条指令的Tnew计算一般是:Tnew’=max{Tnew,0}
所谓Tuse,就是流水线什么时候需要结果数据。这个值我们是站在D级进行分析的,并且同一条指令在不同流水线级的Tuse是定值。例如beq指令在D级就需要寄存器的值进行比较,因此Tuse=0。同时一个指令可能有两个不同的Tuse值,例如sw指令,在E级中需要用到rs寄存器中的值计算内存地址,在M级需要用到rt寄存器中的值确定存入数据。
对于A,就是寄存器所在地址,因为即使对于指令顺序可能出现冒险,但是如果他们读写针对的不是同一个寄存器,那就不可能存在冒险。所以只有A相等且不为0时我们才需要考虑冒险情况。
- 暂停
我们需要在D级就优先判断暂停。所以每一条指令进入D级流水线寄存器时,我们都需要判断前面E,M,W级寄存器的Tnew和D级指令的Tuse。当Tuse<Tnew时,就需要人为插入Nop气泡。
此处附上相关暂停判断的策略矩阵(S为暂停情况,F为转发情况):
Tuse\Tnew | E_Tnew | E_Tnew | E_Tnew | M_Tnew | M_Tnew | W_Tnew |
---|---|---|---|---|---|---|
Tuse\Tnew | 2 | 1 | 0 | 1 | 0 | 0 |
0 | S | S | F | S | F | F |
1 | S | F | F | F | F | F |
2 | F | F | F | F | F | F |
以及不同指令的Tuse和Tnew:
指令 | Tuse_rs | Tuse_rt | Tnew(E) | A |
---|---|---|---|---|
add | 1 | 1 | 1 | rd |
sub | 1 | 1 | 1 | rd |
ori | 1 | x | 1 | rt |
lw | 1 | x | 2 | rt |
sw | 1 | 2 | x | x |
lui | x | x | 0 | rt |
beq | 0 | 0 | x | x |
jal | x | x | 0 | 31 |
jr | 0 | x | x | x |
则出现满足暂停情况的AT组合时,我们需要做到:
冻结 PC 的值;
冻结 D 级流水线寄存器的值;
将 E 级流水线寄存器清零(这等价于插入了一个 nop 指令)。
为了冻结PC和D级流水线,我们要为其增加使能信号,并且只有使能信号为1时我们才能让他们的值改变。
为了清除E级流水线寄存器,我们需要增加一个清空信号,这样只要需要暂停就可以用这个信号清空E级流水线寄存器。
设置的时候如果没有Tuse就设定为最大值,没有Tnew就设定为最小值。
当某个指令的Tuse为x(不需要使用寄存器值),则人为将其Tuse调到最大(本题我调到3),当Tnew=x,人为将其调到0,并且让其A为0(这样肯定不会写入)。
- 转发
我们首先弄清楚可能需要转发信息的寄存器:D级中的RD1,RD2(CMP,NPC);E级中的A,B(ALU);M级中的RD(DM)。可能提供转发信息的寄存器:E、M级中的ALU答案以及W中的最终回写值。具体如表格:
M级ALU计算结果 | W级回写结果 | |
---|---|---|
D级被转发 | RS寄存器值、RT寄存器值 | RS寄存器值、RT寄存器值 |
E级被转发 | ALU的A、ALU的B、RT寄存器值 | ALU的A、ALU的B、RT寄存器值 |
M级被转发 | DM的WD |
新增端口
PC
端口 | 方向 | 功能 |
---|---|---|
F_PC_EN | I | 为PC元件赋能,如果为0则被冻结 |
添加赋能信号是为了配合暂停时冻结PC的存储值。
CU
端口 | 方向 | 功能 |
---|---|---|
D_CU_Instr[31:0] | I | 当前指令机器码 |
CU_D_Tuse_rs[1:0] | O | 寄存器传输的当前指令的D级Tuse_rs |
CU_D_Tuse_rt[1:0] | O | 寄存器传输的当前指令的D级Tuse_rt |
CU_D_Tnew[1:0] | O | 寄存器传输的当前指令的D级Tnew |
CU_D_ATarget[4:0] | O | 寄存器传输的当前指令的输入寄存器 |
CU_D_Read1[4:0] | O | 寄存器传输的当前指令要读取的第一个寄存器 |
CU_D_Read2[4:0] | O | 寄存器传输的当前指令要读取的第一个寄存器 |
CU_D_PC8EXTSel | O | 若Tnew(E)为0,则要选择读取EXT出的lui还是jal的PC8 |
CU_D_PC8ALUSel | O | 若Tnew(M)为0,则要选择读取ALUOut还是PC+8 |
在控制器中计算出当前指令的T情况
D级寄存器(IF/ID2
端口 | 方向 | 功能 |
---|---|---|
D_EN | I | 为D级寄存器赋能,如果为0则被冻结 |
添加赋能信号是为了配合暂停时冻结D级寄存器的存储值。
E级寄存器(EX/MEM)
端口 | 方向 | 功能 |
---|---|---|
E_Clr | I | E级寄存器的清空信号,当暂停时需要激活来清空E级寄存器模拟插入NOP |
E_CU_Instr[31:0] | I | 需要被传输进入E级的机器码 |
E_CU_Tuse_rs[1:0] | I | 上一级传入的当前指令的D级Tuse_rs |
E_CU_Tuse_rt[1:0] | I | 上一级传入的当前指令的D级Tuse_rt |
E_CU_Tnew[1:0] | I | 上一级传入的当前指令的D级Tnew |
E_CU_Read1[4:0] | I | 上一级传入的当前指令的D级要读取的第一个寄存器 |
E_CU_Read2[4:0] | I | 上一级传入的当前指令的D级要读取的第二个寄存器 |
E_CU_PC8EXTSel | I | 选择需要转发的是jal的PC8还是lui的EXTOut |
E_CU_PC8ALUSel | O | 若Tnew(M)为0,则要选择读取ALUOut还是PC+8 |
E_E_Instr[31:0] | O | 需要被传输进入M级的机器码 |
E_E_Tuse_rs[1:0] | O | 寄存器传输的当前指令的E级Tuse_rs |
E_E_Tuse_rt[1:0] | O | 寄存器传输的当前指令的E级Tuse_rt |
E_E_Tnew[1:0] | O | 寄存器传输的当前指令的E级Tnew |
E_E_Read1[4:0] | O | 当前指令的E级要读取的第一个寄存器 |
E_E_Read2[4:0] | O | 当前指令的E级要读取的第二个寄存器 |
E_E_PC8EXTSel | O | 选择需要转发的是jal的PC8还是lui的EXTOut |
E_E_PC8ALUSel | O | 若Tnew(M)为0,则要选择读取ALUOut还是PC+8 |
添加清空信号是为了配合暂停时清空E级寄存器的存储值。同时需要传输当前指令机器码来确定寄存器地址,来确定是否存在冒险可能。
M级寄存器(EX/MEM)
端口 | 方向 | 功能 |
---|---|---|
M_E_Instr[31:0] | I | 需要被传输进入M级的机器码 |
M_E_Tuse_rt[1:0] | I | 上一级传入的当前指令的E级Tuse_rt |
M_E_Tnew[1:0] | I | 上一级传入的当前指令的E级Tnew |
M_E_Read2[4:0] | I | 上一级传入的当前指令的E级要读取的第二个寄存器 |
M_E_PC8ALUSel | O | 若Tnew(M)为0,则要选择读取ALUOut还是PC+8 |
M_M_Instr[31:0] | O | 需要被传输进入W级的机器码 |
M_M_Tuse_rt[1:0] | O | 寄存器传输的当前指令的M级Tuse_rt |
M_M_Tnew[1:0] | O | 寄存器传输的当前指令的M级Tnew |
M_M_Read2[4:0] | O | 当前指令的M级要读取的第二个寄存器 |
M_M_PC8ALUSel | O | 若Tnew(M)为0,则要选择读取ALUOut还是PC+8 |
由于在M级中只可能需求rt寄存器中的值(用来当作内容存入DM),因此不需要再传入Tuse_rs进行判断了。
W级寄存器(MEM/WB)
端口 | 方向 | 功能 |
---|---|---|
W_M_Instr[31:0] | I | 需要被传输进入W级的机器码 |
W_W_Instr[31:0] | O | 需要被传输进入冒险控制单元的W级的机器码 |
同时,由于W级寄存器不可能作为数据的需求者,所以不需要判断T_use,且其T_new一定为0(一定已经完成了数据的产生),所以不需要新增定位T相关端口。
HCU(冒险控制单元)(新增)
端口 | 方向 | 功能 |
---|---|---|
HCU_D_Tuse_rs[1:0] | I | D级Tuse_rs |
HCU_D_Tuse_rt[1:0] | I | D级Tuse_rt |
HCU_D_Tnew[1:0] | I | D级Tnew |
HCU_D_ATarget[4:0] | I | D级指令需要输入的寄存器位置 |
HCU_D_Read1[4:0] | I | D级指令读取的第一个寄存器编号 |
HCU_D_Read2[4:0] | I | D级指令读取的第二个寄存器编号 |
HCU_D_GRFWr | I | D级指令是否允许写入寄存器的控制信号 |
HCU_E_Tuse_rs[1:0] | I | E级Tuse_rs |
HCU_E_Tuse_rt[1:0] | I | E级Tuse_rt |
HCU_E_Tnew[1:0] | I | E级Tnew |
HCU_E_ATarget[4:0] | I | E级指令需要输入的寄存器位置 |
HCU_E_Read1[4:0] | I | E级指令读取的第一个寄存器编号 |
HCU_E_Read2[4:0] | I | E级指令读取的第二个寄存器编号 |
HCU_E_GRFWr | I | E级指令是否允许写入寄存器的控制信号 |
HCU_M_Tuse_rt[1:0] | I | M级Tuse_rt |
HCU_M_Tnew[1:0] | I | M级Tnew |
HCU_M_ATarget[4:0] | I | M级指令需要输入的寄存器位置 |
HCU_M_Read2[4:0] | I | M级指令读取的第二个寄存器编号 |
HCU_M_GRFWr | I | M级指令是否允许写入寄存器的控制信号 |
HCU_W_ATarget[4:0] | I | W级指令需要输入的寄存器位置 |
HCU_W_GRFWr | I | W级指令是否允许写入寄存器的控制信号 |
HCU_PC_EN | O | 判断是否应该冻结PC |
HCU_D_EN | O | 判断是否应该冻结D级寄存器 |
HCU_E_Clr | O | 判断是否应该清空E级寄存器 |
HCU_D_RD1MUX[1:0] | O | D级中RD1的转发信号 |
HCU_D_RD2MUX[1:0] | O | D级中RD2的转发信号 |
HCU_E_RD1MUX[1:0] | O | E级中RD1的转发信号 |
HCU_E_RD2MUX[1:0] | O | E级中RD2的转发信号 |
HCU_M_WDMUX | O | M级中WD的转发信号 |
思考题:
1.例如lw+beq,ori+beq,由于beq的Tnew变成0了,就更有可能需要暂停。
2.因为这样才能回到延迟槽下面的指令,否则延迟槽指令将被重复执行。
3.可能需要考虑PC8的选择问题,同时这样比较规整。(idk)
4.可以避免W级转发的复杂操作判断,就是当读寄存器和写寄存器指向相同的非零寄存器并且Wr=1允许写入时,直接将写入值读给读取值。
5.供给者可能是EXT(lui),PC8,DM,ALU,需求者是D_RD1,D_RD2,E_RD1,E_RD2,M_RD2。
6.首先需要修改CU让它的信息能被读取,然后如果有新的不属于前面的供给者情况需要进行多路选择。
7.译码器采取集中式译码,在D级将Instr输入译码器并且将全部控制信号输出给E级寄存器,然后只要该信号还有效果就需要顺着流水线向后传递。优点是不需要动脑子,想要新增加控制信号就在控制器中翻译然后无脑向后传递即可,劣势是增加控制信号时需要增加很多端口。