verilog惯例:记得给reg型变量(计数器等)初始化和清零哦!
1 从单周期到流水线
1.1 单周期和流水线的区别
-
不难发现,单周期CPU在任何一个时间点都只有一条指令在运行。当上一条指令完成了取指令、取操作数和译码、执行、访存、回写等步骤之后,下一条指令才会开始运行。而流水线CPU为了加快指令运行效率,在同一个时间点会有若干条指令在运行。
-
所以,我们不难发现,因为同时有若干条指令在运行,所以模块的输出信息(比如GRF的输出可能是这几条指令中某一条的)无法保存,就会乱套。所以我们将一条指令的运行切分为我们已经很熟悉的五个步骤,在同一时间点,只有一条指令在完成该步骤。在步骤与步骤间安插寄存器,保存完成当前步骤的指令的所有需要的信息。
-
寄存器的意义就是保存当前步骤正在执行的指令的信息。
1.2 步骤划分和模块布局
-
步骤划分为:取指令、取操作数和译码、执行、访存、回写五个步骤
-
基本模块
-
PC,IM,NPC,GRF,CMP,ALU,DM
-
各级的控制器和寄存器(采用分布式控制器,即每级各一个流水级控制器)
-
暂停控制模块Stall_CTRL
-
-
由此,我们可以得到一张模块布局图:
1.3 各级寄存器中需要保存什么信息
-
该指令在该级需要的所有信息,以及在下一级要用的信息,用于转发和暂停判断的信息。
1.4 采用分布式控制器
-
在D,E,M,W级均设置一个控制器,在每个流水级都进行一遍译码,每一级产生当前该级指令的控制信号。
-
分布式控制器的优势是可以减少级模块之间传递的信号,每个模块的逻辑性更强,更好理解,不易乱,但是缺点是有点呆,没有集中式控制器的简约。
2 教程中的设计和测试说明
2.1 设计要求
-
处理器应支持{addu,subu,ori,lw,sw,beq,lui,j,jal,jr,nop}指令
-
处理器为五级流水线设计
-
必须最大可能支持转发以解决数据冒险
-
对于b类和j类指令,流水线设计必须支持延迟槽,因此设计需要使用D_PC_8或I_PC_4
-
IM和DM的容量变化
-
IM:容量为32bit*4096word 4096=2^12
-
DM:容量为32bit*3072word
-
-
PC的初始地址为0x0000_3000
-
支持同步复位
-
支持延迟槽,针对跳转指令
-
将CMP比较模块提前至D级
3 什么是延迟槽
-
延迟槽就是所有跳转类指令,不论是条件转移满足转移条件(即beq等)还是非条件转移(即j,jr,jal等),它的下一条指令都被写mips汇编程序者写成了一条程序中必须会完成的指令,必须进入流水线,完成所有的步骤。换言之,对于条件转移类指令,无论是否满足转移条件,它的下一条指令都需要执行;对于非条件转移类指令,它的下一条指令也必须执行,所以,在F级不需要设置NPC模块,只需要ADD4模块,而在D级设置NPC模块,它的PC+8指令才是转移后的指令。
-
总结来讲,支持延迟槽就是编写mips汇编语言的人用他们的智慧给搭建流水线CPU的人提供便利。
-
延迟槽只针对跳转指令
4 转发和暂停
4.1 什么时候需要转发?
-
当需要使用到在D级取到的GRF寄存器的值的时候,由于写回GRF步骤是最后一步,所以可能此时的值不是最新的,需要依靠转发来获得更新后的寄存器的值。
-
需求者简而言之就是需要使用GRF_RD1或者GRF_RD2的地方
-
在流水线中需要使用到寄存器中保存的数据的地方都可能需要转发
-
-
供给者简而言之就是写入GRF的最终值,就是GRF_WD
-
ALU_C(ALU计算结果,计算类指令)
-
DM_out(DM的输出数据)
-
PC+8(jal)
-
-
每一级的转发数据来源都有什么特点呢?
-
首先必须来源于后级的寄存器。且一定是流水级寄存器中已经保存了的值(不能是刚刚产生的)。比如说:
-
D级的转发mux数据来源:EREG,MREG(WREG采用内部转发)
-
E级的转发mux数据来源:MREG,WREG
-
M级的转发mux数据来源:WREG
-
-
为什么呢?因为转发的目的是为了获得还没有更新的GRF寄存器中的值,所以一定是在该指令前面执行的指令产生的值,所以是来源于后面的级。
-
-
在每一级都要更新转发后的TR_GRF_RD1/TR_GRF_RD2并将该值当作GRF_RD1/GRF_RD2传入下一流水级寄存器。
内部转发
-
在GRF模块内部判断
外部转发
-
在每一级都用一个mux选择器更新当前流水级GRF_RD1和GRF_RD2的值
-
数据来源是后级可能回写入GRF的值,比如E_PC+8,M_PC+8,M_ALU_C,W_PC+8,W_ALU_C,W_DM_out
-
-
4.2 什么时候需要暂停?
-
主要利用Tuse和Tnew
当某级指令需要使用寄存器中的数据,但该指令的后级指令要回写该寄存器但还回写数据还没保存在流水级寄存器中时(即转发无效时),需要喊暂停。
5 模块定义
5.1 F级
5.1.1 PC
-
PC的主要功能是完成输出位于F级指令的地址
-
同步复位后,PC指向0x0000_3000
端口名 | 端口输入/输出信号名 | 端口描述 |
---|---|---|
input clk | clk | 时钟信号 |
input reset | reset | 同步复位信号,复位到0x0000_3000 |
input PC_en | Stall | 用于暂停操作,PC的使能端 当PC_en为1时,PC不工作,在时钟上升沿不改变值 |
input [31:0] F_NextPC | F_NextPC | 下一条指令所在地址 |
output reg [31:0] F_PC | F_PC | 当前F级指令所在地址 |
5.1.2 IM
-
IM功能是保存所有指令,并输出F级指令
信号名 | 描述 |
---|---|
input [31:0] F_PC | 当前F级指令所在的IM地址 |
output [31:0] F_IM_instr | 当前F级指令,为IM_ROM[PC[13:2]] |
5.1.3 MUX_F_NextPC选择器(其实没什么用,懒得删了)
-
同步复位时选择32'h0000_3004
模块端口名 | 端口对应信号名 | 对输入信号的描述 |
---|---|---|
input [31:0] A | D_NextPC | |
input [31:0] B | 32'h0000_3004 | |
input Sel | reset | |
output [31:0] Y | F_NextPC |
5.2 D级
5.2.1 GRF(支持内部转发)
-
端口定义
信号名 | 描述 |
---|---|
input clk | 时钟信号 |
input reset | 同步复位信号 |
input [4:0] D_GRF_A1 | D级指令的5位地址输入信号,指定32个寄存器中的一个,将其中的数据输出到RD1 |
input [4:0] D_GRF_A2 | D级指令的5位地址输入信号,指定32个寄存器中的一个,将其中的数据输出到RD2 |
input [31:0] W_PC | 当前W级指令所在的IM地址 |
input [4:0] W_GRF_A3 | W级指令的5位地址输入信号,指定32个寄存器中的一个作为写入目标寄存器 |
input [31:0] W_GRF_WD | W级指令的32位写入数据 |
input W_RegWrite | W级指令的GRF写使能信号 |
output reg [31:0] D_GRF_RD1 | D级指令输出A1指定寄存器的32位数据 |
output reg [31:0] D_GRF_RD2 | D级指令输出A2指定寄存器的32位数据 |
-
功能定义
功能 | 描述 |
---|---|
读出数据 | 组合逻辑:读出D级指令D_GRF_A1和D_GRF_A3地址所对应的寄存器的值通过D_GRF_RD1和D_GRF_RD2读出。支持内部转发:将W级即将在下一个时钟周期写入的数据读出。(数据转发) |
写入数据 | 时序逻辑:当W_RegWrite为1且时钟上升沿来临时,将W级指令的W_GRF_WD写入W_GRF_A3(非0)所对应的寄存器中。0号寄存器的值始终为0。 |
同步复位 | 当reset为1且时钟上升沿来临时,将所有寄存器的值清零。 |
5.2.2 EXT
-
EXT功能是位扩展,将16位立即数扩展为32位立即数。
信号名 | 描述 |
---|---|
input [15:0] D_EXT_imm16 | D级指令的16位立即数输入 |
input [4:0] D_EXTop | EXT功能选择信号 |
output reg [31:0] D_EXT_imm32 | D级指令的32位扩展后的立即数输出 |
5.2.3 mux_D_TR_GRF_RD1
-
GRF_RD1数据转发选择器
-
EREG,MREG
-
没有WREG(WREG采用GRF内部转发)
模块端口名 | 端口对应信号名 | 对输入信号的描述 |
---|---|---|
input [31:0] A | 32'd0 | |
input [31:0] B | D_GRF_RD1 | |
input [31:0] C | M_ALU_C | |
input [31:0] D | M_PC+8 | |
input [31:0] E | E_PC+8 | |
input Sel | D_TR_GRF_RD1_Sel | |
output [31:0] Y | D_TR_GRF_RD1 |
5.2.4 mux_D_TR_GRF_RD2
-
GRF_RD2数据转发选择器
模块端口名 | 端口对应信号名 | 对输入信号的描述 |
---|---|---|
input [31:0] A | 32'd0 | |
input [31:0] B | D_GRF_RD2 | |
input [31:0] C | M_ALU_C | |
input [31:0] D | M_PC+8 | |
input [31:0] E | E_PC+8 | |
input Sel | D_TR_GRF_RD2_Sel | |
output [31:0] Y | D_TR_GRF_RD2 |
5.2.5 mux_CMP_A
-
D_CMP_A的数据来源选择器
模块端口名 | 端口对应信号名 | 描述 |
---|---|---|
input [31:0] A | D_TR_GRF_RD1 | |
input [2:0] Sel | D_CMP_A_Sel | |
output [31:0] Y | D_CMP_A |
5.2.6 mux_CMP_B
-
D_CMP_B的数据来源选择器
模块端口名 | 端口对应信号名 | 描述 |
---|---|---|
input [31:0] A | D_TR_GRF_RD2 | |
input [2:0] Sel | D_CMP_B_Sel | |
output [31:0] Y | D_CMP_B |
5.2.7 CMP
-
CMP的功能是输出条件比较的结果,可用于NPC跳转控制信号。
信号名 | 描述 |
---|---|
input [31:0] D_CMP_A | D级指令的32位比较数A的输入 |
input [31:0] D_CMP_B | D级指令的32位比较数B的输入 |
input [4:0] D_CMPop | |
output [31:0] D_cmp_sign | D级指令的比较结果输出 |
5.2.8 mux_D_TR_reg_data
-
D_reg_data数据转发选择器
-
和D_GRF_RD1保持一致
5.2.9 NPC
-
NPC的作用是计算转移指令的下一条指令的地址,当前转移指令(b类或j类)在D级
信号名 | 描述 |
---|---|
input [31:0] D_NPC_PC | NPC输入 确定跳转的指令为D_PC 不跳转的指令为F_PC |
input [15:0] D_imm16 | 当前D级指令的16位立即数 |
input [25:0] D_imm26 | 当前D级指令的26位立即数 |
input [31:0] D_reg_data | 当前D级指令跳转寄存器地址的寄存器数据 |
input [4:0] D_NPCop | 当前D级指令NPC控制信号 |
output reg [31:0] D_NextPC | 当前D级指令的下一条指令地址 |
5.2.10 DREG
-
D级寄存器
-
保存当前D级指令(正在取操作数的指令)的信息
端口名 | 端口输入/输出信号名 | 描述 |
---|---|---|
input clk | clk | 时钟信号 |
input reset | reset | 同步复位信号,清空寄存器 |
input DREG_en | Stall | 用于暂停操作 |
input [31:0] F_IM_instr | F_IM_instr | 当前F级32位指令,在时钟上升沿写入DREG |
input [31:0] F_PC | F_PC | 当前F级指令的PC,在时钟上升沿写入DREG |
output [31:0] D_IM_instr | D_IM_instr | 当前D级32位指令 |
output [31:0] D_PC | D_PC | 当前D级指令的PC |
5.2.11 DCTRL
-
D级控制器
-
输出当前D级指令需要的控制信号
信号名 | 描述 |
---|---|
input [5:0] D_op | 当前D级指令的6位op码 |
input [5:0] D_func | 当前D级指令的6位func码 |
input [5:0] D_cmp_sign | 当前D级指令的1位比较输出信号 |
input [4:0] D_GRF_A1 | 用于生成转发选择信号 |
input [4:0] D_GRF_A2 | 用于生成转发选择信号 |
input [4:0] E_GRF_A3 | 用于生成转发选择信号 |
input [4:0] M_GRF_A3 | 用于生成转发选择信号 |
input [3:0] E_order | 当前E级指令编号,用于判断E级是什么指令,用于生成转发选择信号 |
input [3:0] M_order | 当前M级指令编号,用于判断M级是什么指令,用于生成转发选择信号 |
output [4:0] D_EXTop | 当前D级指令EXT模块功能选择信号 00000:符号扩展 00001:0扩展 00010:加载至最高位,低位补0 |
output [4:0] D_CMPop | 当前D级指令CMP模块功能选择信号 00000:相等判断 |
output [4:0] D_NPCop | 当前D级指令NPC模块功能选择信号 00000:PC+4 00001:PC+4+sign_extend(imm16) 00010:PC[31:28]||imm26||00 00011:reg_data |
output [2:0] D_GRF_A1_Sel | 当前D级指令GRF_A1数据来源选择信号 000:D_rs |
output [2:0] D_GRF_A2_Sel | 当前D级指令GRF_A2数据来源选择信号 000:D_rt |
output [2:0] D_GRF_A3_Sel | 当前D级指令GRF_A3数据来源信号(本来应该在W级输出,但是由于转发需求) 000:D_rt 001:D_rd 010:31(jal) |
output [2:0] D_CMP_A_Sel | 当前D级指令CMP_A数据来源选择信号 000:D_TR_GRF_RD1 |
output [2:0] D_CMP_B_Sel | 当前D级指令CMP_B数据来源选择信号 000:D_TR_GRF_RD2 |
output [2:0] D_TR_GRF_RD1_Sel | 转发数据来源选择信号 000:32'd0 001:D_GRF_RD1 010:M_ALU_C 011:M_PC+8 100:E_PC+8 |
output [2:0] D_TR_GRF_RD2_Sel | 转发数据来源选择信号 000:32'd0 001:D_GRF_RD2 010:M_ALU_C 011:M_PC+8 100:E_PC+8 |
output [1:0] D_Tuse_GRF_A1 | 当前D级指令的GRF_A1的Tuse信号 |
output [1:0] D_Tuse_GRF_A2 | 当前D级指令的GRF_A2的Tuse信号 |
output D_NPC_PC_Sel | 当前D级NPC的PC输入选择信号 确定要跳转的指令为1(D_PC);顺序执行的指令为0(F_PC) |
5.3 E级
5.3.1 mux_E_TR_GRF_RD1
-
GRF_RD1数据来源转发选择器
模块端口名 | 端口对应信号名 | 对输入信号的描述 |
---|---|---|
input [31:0] A | 32'd0 | 摆烂 |
input [31:0] B | E_GRF_RD1 | 当前E级的GRF_RD1(在D级转过更新过的RD1) |
input [31:0] C | W_DM_out | |
input [31:0] D | W_ALU_C | |
input [31:0] E | W_PC+8 | |
input [31:0] F | M_ALU_C | |
input [31:0] G | M_PC+8 | |
input [31:0] H | W_new_WD | WREG可能保存的为新指令预留的回写数据,优先级在M级下面。 |
input [2:0] Sel | E_TR_GRF_RD1_Sel | |
output [31:0] Y | E_TR_GRF_RD1 |
5.3.2 mux_E_TF_GRF_RD2
-
GRF_RD2数据来源转发选择器
模块端口名 | 端口对应信号名 | 对输入信号的描述 |
---|---|---|
input [31:0] A | 32'd0 | |
input [31:0] B | E_GRF_RD2 | 当前E级的GRF_RD2(在D级转过更新过的RD2) |
input [31:0] C | W_DM_out | |
input [31:0] D | W_ALU_C | |
input [31:0] E | W_PC+8 | |
input [31:0] F | M_ALU_C | |
input [31:0] G | M_PC+8 | |
input [31:0] H | W_new_WD | WREG可能保存的为新指令预留的回写数据,优先级在M级下面。 |
input [2:0] Sel | E_TR_GRF_RD2_Sel | |
output [31:0] Y | E_TR_GRF_RD2 |
5.3.3 ALU
-
ALU的功能是执行当前E级指令32位加、减、或运算等功能
信号名 | 描述 |
---|---|
input [31:0] E_ALU_A | 当前E级指令的第一个32位操作数 |
input [31:0] E_ALU_B | 当前E级指令的第二个32位操作数 |
input [4:0] E_ALU_S | 当前E级指令的5位补充操作数 |
input [4:0] E_ALUop | 当前E级指令的ALU模块功能选择信号 |
output [31:0] E_ALU_C | 当前E级指令的ALU模块计算32位结果输出 |
5.3.4 EREG
-
E级寄存器
-
保存当前E级指令(正在执行)的信息(该指令需要保存的信息)
端口名 | 端口输入/输出信号名 | 描述 |
---|---|---|
input clk | clk | |
input reset | reset|Stall | |
input [31:0] D_IM_instr | D_IM_instr | |
input [31:0] D_PC | D_PC | |
input [4:0] D_GRF_A1 | D_GRF_A1 | |
input [4:0] D_GRF_A2 | D_GRF_A2 | |
input [31:0] D_GRF_RD1 | D_TR_GRF_RD1 | 在D级更新(转发)过的GRF_RD1信号 |
input [31:0] D_GRF_RD2 | D_TR_GRF_RD2 | 在D级更新(转发)过的GRF_RD2信号 |
input [4:0] D_GRF_A3 | D_GRF_A3 | |
input [31:0] D_EXT_imm32 | D_EXT_imm32 | |
input D_cmp_sign | D_cmp_sign | |
output [31:0] E_IM_instr | E_IM_instr | |
output [31:0] E_PC | E_PC | |
output [4:0] E_GRF_A1 | E_GRF_A1 | |
output [4:0] E_GRF_A2 | E_GRF_A2 | |
output [31:0] E_GRF_RD1 | E_GRF_RD1 | |
output [31:0] E_GRF_RD2 | E_GRF_RD2 | |
output [4:0] E_GRF_A3 | E_GRF_A3 | |
output [31:0] E_EXT_imm32 | E_EXT_imm32 | |
output E_cmp_sign | E_cmp_sign |
5.3.5 ECTRL
-
E级控制器
-
输出当前E级指令所需要的控制信号
信号名 | 描述 |
---|---|
input [5:0] E_op | |
input [5:0] E_func | |
input [4:0] E_GRF_A1 | 用于生成转发控制信号 |
input [4:0] E_GRF_A2 | 用于生成转发控制信号 |
input [4:0] M_GRF_A3 | 用于生成转发控制信号 |
input [4:0] W_GRF_A3 | 用于生成转发控制信号 |
input [3:0] M_order | 当前M级指令编号,用于判断当前M级是什么指令,用于生成转发控制信号 |
input [3:0] W_order | 当前W级指令编号,用于判断当前W级是什么指令,用于生成转发控制信号 |
output [4:0] E_ALUop | ALU模块功能选择信号 00000:A&B 00001:A|B 00010:A+B 00011:A-B 00100:B 00101:新运算 |
output [2:0] E_ALU_A_Sel | 000:E_TR_GRF_RD1 |
output [2:0] E_ALU_B_Sel | 000:E_TR_GRF_RD2 001:E_EXT_imm32 |
output [2:0] E_ALU_S_Sel | 000:E_shamt |
output [2:0] E_TR_GRF_RD1_Sel | 转发数据来源选择信号 000:32'd0 001:E_GRF_RD1 010:W_DM_out 011:W_ALU_C 100:W_PC+8 |
output [2:0] E_TR_GRF_RD2_Sel | 转发数据来源选择信号 000:32'd0 001:E_GRF_RD2 010:W_DM_out 011:W_ALU_C 100:W_PC+8 |
output [1:0] E_Tnew | 当前E级指令的Tnew信号 |
output [3:0] E_order |
5.4 M级
5.4.1 mux_M_GRF_RD2
-
GRF_RD2数据来源转发选择器
模块端口名 | 端口对应信号名 | 对输入信号的描述 |
---|---|---|
input [31:0] A | 32'd0 | |
input [31:0] B | M_GRF_RD2 | |
input [31:0] C | W_DM_out | |
input [31:0] D | W_ALU_C | |
input [31:0] E | W_PC+8 | |
input [31:0] F | W_new_WD | |
input Sel | M_TR_GRF_RD2_Sel | |
output [31:0] Y | M_TR_GRF_RD2 |
5.4.2 DM
-
端口定义
信号名 | 描述 |
---|---|
input clk | 时钟信号 |
input reset | 同步复位信号 |
input [31:0] M_PC | 当前M级指令的PC地址 |
input M_MemWrite | DM的写入使能 |
input [31:0] M_DM_A | DM地址数据 |
input [31:0] M_DM_data | 32位DM输入待处理数据 |
input [4:0] M_DMop | DM功能选择信号 word?half word?byte? |
output reg [31:0] M_DM_out | 32位DM输出 |
-
功能实现
功能 | 描述 |
---|---|
读出数据 | 组合逻辑:将M_DM_A地址对应的DM_RAM的值通过M_DM_out读出 |
写入数据 | 时序逻辑:当M_MemWrite为1且时钟上升沿来临时,将M_DM_data的值写入到M_DM_A地址所对应的主存中。 |
同步复位 | 当reset为1且时钟上升沿来临时,将所有寄存器DM_RAM的值清零。 |
5.4.3 MREG
-
M级寄存器
-
保存M级指令执行所需要的所有信息
端口名 | 端口对应信号名 | |
---|---|---|
input clk | clk | |
input reset | reset | |
input [31:0] E_IM_instr | E_IM_instr | |
input [31:0] E_PC | E_PC | |
input [4:0] E_GRF_A2 | E_GRF_A2 | |
input [31:0] E_GRF_RD2 | E_TR_GRF_RD2 | |
input [4:0] E_GRF_A3 | E_GRF_A3 | |
input [31:0] E_ALU_C | E_ALU_C | |
input E_cmp_sign | E_cmp_sign | |
output [31:0] M_IM_instr | M_IM_instr | |
output [31:0] M_PC | M_PC | |
output [4:0] M_GRF_A2 | M_GRF_A2 | |
output [31:0] M_GRF_RD2 | M_GRF_RD2 | |
output [4:0] M_GRF_A3 | M_GRF_A3 | |
output [31:0] M_ALU_C | M_ALU_C | |
output M_cmp_sign | M_cmp_sign |
5.4.4 MCTRL
信号名 | 描述 |
---|---|
input [5:0] M_op | |
input [5:0] M_func | |
input [4:0] M_GRF_A2 | 用于生成转发选择信号 |
input [4:0] W_GRF_A3 | 用于生成转发选择信号 |
input [3:0] W_order | 当前W级指令编号,用于判断当前W级是什么指令,用于生成转发选择信号 |
output M_MemWrite | |
output [4:0] M_DMop | 00000:word 00001:符号扩展byte 00010:符号扩展half word |
output [2:0] M_DM_A_Sel | 000:M_ALU_C |
output [2:0] M_DM_data_Sel | 000:M_TR_GRF_RD2 |
output [2:0] M_TR_GRF_RD2_Sel | 000:32'd0 001:M_GRF_RD2 010:W_DM_out 011:W_ALU_C 100:W_PC+8 |
output [1:0] M_Tnew | 当前M级指令的Tnew信号 |
output [3:0] M_order | 当前M级指令编号 |
5.5 W级
5.5.1 WREG
信号名 | 描述 |
---|---|
input clk | |
input reset | |
input [31:0] M_IM_instr | |
input [31:0] M_PC | |
input [4:0] M_GRF_A3 | |
input [31:0] M_ALU_C | |
input [31:0] M_DM_out | |
input M_cmp_sign | |
input [31:0] M_new_WD | |
output [31:0] W_IM_instr | |
output [31:0] W_PC | |
output [4:0] W_GRF_A3 | |
output [31:0] W_ALU_C | |
output [31:0] W_DM_out | |
output W_cmp_sign | |
output [31:0] W_new_WD |
5.5.2 WCTRL
信号名 | 描述 |
---|---|
input [5:0] W_op | |
input [5:0] W_func | |
output W_RegWrite | |
output [2:0] W_GRF_WD_Sel | 000:W_ALU_C 001:W_DM_out 010:W_PC+8 |
output [1:0] W_Tnew | 当前W级指令的Tnew |
output [3:0] W_order | 当前W级指令编号 |
6 Stall_CTRL
-
使用AT法控制流水线是否需要暂停的控制器
6.1 D_Tuse
-
由于在D级判断是否需要暂停操作,所以D_Tuse信号来源是D级的控制器,是个静态值,只来源于D级!!!
-
D_Tuse_GRF_A1:还需要几个cycle需要使用到从GRF中取得的编号为A1的寄存器的值
-
假设不需要使用该寄存器中的值:则为11
-
-
D_Tuse_GRF_A2:还需要几个cycle需要使用到从GRF中取得的编号为A2的寄存器的值
-
假设不需要使用该寄存器中的值:则为00
-
指令 | D_Tuse_GRF_A1 | D_Tuse_GRF_A2 |
---|---|---|
addu | 1(01) | 1(01) |
subu | 1(01) | 1(01) |
ori | 1(01) | 3(11)(不需要) |
lui | 3(11)(不需要) | 3(11)(不需要) |
lw | 1(01) | 3(11)(不需要) |
sw | 1(01) | 2(10) |
beq | 0 | 0 |
j | 3(11)(不需要) | 3(不需要) |
jal | 3(11)(不需要) | 3(不需要) |
jr | 0 | 3(不需要) |
nop | 3(11)(不需要) | 3(不需要) |
6.2 Tnew
-
当前该流水级指令还需要几个cycle该流水级的寄存器中才会保存写回新的编号为GRF_A3的寄存器的值,比如ALU_C,DM_out,PC_4
-
来源于E级控制器,M级控制器和W级控制器
-
如果该指令位于的级寄存器中已经保存了写回的值,则Tnew为0
-
-
如果该指令没有写回操作,该级Tnew为0
-
可以写回的值必须保存在级寄存器中!!!!
-
指令 | 产生写回值 | 最先保存该写回值的级寄存器 | E_Tnew | M_Tnew | W_Tnew |
---|---|---|---|---|---|
addu | E_ALU_C | MREG | 1 | 0 | 0 |
subu | E_ALU_C | MREG | 1 | 0 | 0 |
ori | E_ALU_C | MREG | 1 | 0 | 0 |
lui | E_ALU_C | MREG | 1 | 0 | 0 |
lw | M_DM_out | WREG | 2 | 1 | 0 |
sw | 无需写回 | 无 | 0(无需写回) | 0(无需写回) | 0(无需写回) |
beq | 无需写回 | 无 | 0(无需写回) | 0(无需写回) | 0(无需写回) |
j | 无需写回 | 无 | 0(无需写回) | 0(无需写回) | 0(无需写回) |
jal | D_PC_4 | DREG | 0 | 0 | 0 |
jr | 无需写回 | 无 | 0(无需写回) | 0(无需写回) | 0(无需写回) |
nop | 无需写回 | 无 | 0(无需写回) | 0(无需写回) | 0(无需写回) |
6.3 Stall_CTRL暂停控制器设计
信号名 | 描述 |
---|---|
input [1:0] D_Tuse_GRF_A1 | 当前D级指令到使用GRF_RD1还需要几个周期 |
input [1:0] D_Tuse_GRF_A2 | 当前D级指令到使用GRF_RD2还需要几个周期 |
input [1:0] E_Tnew | 当前E级指令到写入GRF的值存入当前级寄存器还需要几个周期 |
input [1:0] M_Tnew | 当前M级指令到写入GRF的值存入当前级寄存器还需要几个周期 |
input [1:0] W_Tnew | 当前W级指令到写入GRF的值存入当前级寄存器还需要几个周期 |
input [4:0] D_GRF_A1 | |
input [4:0] D_GRF_A2 | |
input [4:0] E_GRF_A3 | |
input [4:0] M_GRF_A3 | |
input [4:0] W_GRF_A3 | |
output Stall | 暂停信号 |
5 转发思路
内部转发
-
在GRF模块内部判断
外部转发
-
在每一级都用一个mux选择器更新当前流水级GRF_RD1和GRF_RD2的值
-
数据来源是后级可能回写入GRF的值,比如E_PC+8,M_PC+8,M_ALU_C,W_PC+8,W_ALU_C,W_DM_out
-
6 指令分类
课上题型一般分为三种:
6.1 计算类指令
-
计算类指令重点在ALU模块的计算
6.2 条件跳转类指令
-
条件转移类指令重点在CMP模块的新比较的添加以及清空延迟槽的操作
-
清空延迟槽就是:如果D_cmp_sign==0且Stall==0的时候,产生一个DREG_clr信号,清空DREG。
6.3 条件存储类指令
-
这个指令比较有难度,GRF_A3直到在M级才产生。所以需要改造暂停控制器。
7 如何添加一条新指令
7.1 三个重要表格
7.1.1 指令编号列表
编号 | 指令 |
---|---|
0 | addu |
1 | subu |
2 | ori |
3 | lui |
4 | lw |
5 | sw |
6 | beq |
7 | j |
8 | jal |
9 | jr |
10 | nop |
7.1.2 D_Tuse表格
7.1.3 Tnew表格
7.2 大致思路
修改分为数据通路和控制器两个维度:数据通路为mips顶层和模块内部功能添加、控制器为各流水级控制器
-
step1:赋予新指令的指令编号,在macro_CTRL.v中添加新指令;在Decode_CTRL模块中define该指令的op码和func码,在下方if_else 中添加该指令的order
-
step2:根据在哪个步骤使用GRF_RD1和GRF_RD2计算新指令的D_Tuse_GRF_A1和D_Tuse_GRF_A2(两个值会不太一样)。如果不使用,则为11。在DCTRL模块中,添加新指令标签(修改D_Tuse)。根据在哪个步骤的级寄存器会保存回写入GRF的数据,计算E _Tnew,M_Tnew和W_Tnew。分别在ECTRL,MCTRL和WCTRL模块中添加新指令标签(修改Tnew)。
步骤二目的:DCTRL生成D_Tuse_GRF_A1和D_Tuse_GRF_A2;ECTRL生成E_Tnew;MCTRL生成M_Tnew;WCTRL生成W_Tnew
注意:step1和step2一定要填写7.1的三个表格!!!
-
step3:根据五个步骤修改数据通路和控制器。Decode_CTRL只用改一次(step1中已经修改了)。
每个步骤都需要考虑什么呢?
-
首先分为各个模块。
-
思考模块的控制信号。(控制信号列表)
-
是否工作?(使能信号)
-
选择什么数据输入?(数据来源选择信号)
-
模块怎么工作?(模块功能选择信号)
-
-
思考数据通路。(分为mips层和模块内部)
-
如果是已存在mux有新的数据来源,就在新的端口添加新的变量。在mips顶层模块。
-
如果是需要添加新的mux。首先在mips顶层模块wire该mux的输出信号。然后在端口添加输入变量,和控制信号。
-
模块如果有新的功能,就在模块内部加新功能。
-
-
-
其次是转发。
-
思考转发的数据通路。如果有某流水级保存了新的回写进GRF的变量。就添加进该流水级前面所有流水级的转发mux选择器中。要注意优先级。注意转发来源一定是流水级寄存器。
-
D级转发不用考虑W级的GRF_WD(有内部转发)。E级优先级最高。
-
E级转发M级和W级流水级寄存器的数据。M级寄存器优先级高于W级寄存器。
-
M级转发W级寄存器数据。
-
W级不考虑转发。
-
-
思考转发的控制信号。和数据通路输入端口序号相对应。某级寄存器出现回写入GRF的新的信号。
-
在该流水级之前的CTRL中都添加进该信号。和数据通路相对应。
-
-
-
如果某个步骤需要一个在前面步骤的模块产生的新的值。就需要修改流水级寄存器(DREG,EREG,MREG,WREG)。从产生该值的流水级的下一级流水线级寄存器开始加新的信号,直到把数据传到该新值需求的流水级。输出信号要wire新的变量mips。数据通路。
-
修改级寄存器要注意修改mips层(添加实例化输入输出端口)和级寄存器模块内部。
-
7.3 步骤一:F级
-
和单周期不同,NPC移到了D级所以不用修改F级的部分
7.4 步骤二:D级
D级涉及的模块:GRF(只考虑取操作数的部分),EXT,CMP,NPC,mux_D_GRF_A3
GRF(取操作数):
-
数据来源选择(需要考虑的控制信号)
-
D_GRF_A1_Sel
-
D_GRF_A2_Sel
-
EXT(扩展器):
-
数据来源一般为D_imm16
-
如何扩展
-
D_EXTop
-
CMP:
-
数据来源选择
-
D_CMP_A_Sel
-
D_CMP_B_Sel
-
-
如何比较
-
D_CMPop
-
NPC:
-
D_NPC_PC数据选择信号
D_NPC_PC_Sel(如果有一定会跳转的情况就为1)
-
NPC功能选择信号
-
D_NPCop
-
mux_D_GRF_A3
-
回写寄存器地址来源选择信号:D_GRF_A3_Sel (提前选择指令写入的寄存器编号,不回写则为0)
7.5 步骤三:E级
E级涉及的模块:ALU
ALU:
-
运算数据来源选择
-
E_ALU_A_Sel
-
E_ALU_B_Sel
-
E_ALU_S_Sel
-
-
做什么运算
-
E_ALUop
-
7.6 步骤四:M级
M级涉及的模块:DM(分为访和存)
DM:
存(写入数据)
-
是否写入DM
-
M_MemWrite
-
-
写入地址来源和写入数据来源选择
-
M_DM_A_Sel
-
M_DM_data_Sel
-
-
写入什么大小的数据?word?byte?half word?
-
M_DMop
-
访(读出数据)
-
读出数据的地址来源
-
M_DM_A_Sel
-
-
读出什么大小的数据?word?byte?half word?
-
M_DMop
-
7.7 步骤五:W级
W级涉及的模块:位于D级的GRF(写入)
-
是否写入
-
W_RegWrite
-
-
写入寄存器地址来源和写入数据来源
-
W_GRF_A3已经产生(不用考虑)
-
W_GRF_WD_Sel
-
7.8 按需修改所有转发(同一修改关于转发的数据通路和控制信号)
思考转发有两个很重要的角度:供给者和需求者
7.8.1 供给者
供给者角度问两个很重要的问题:
-
该指令是否需要回写?
如果不需要,直接跳过供给者修改部分。
-
如果需要回写的话,是否产生了新的回写GRF的数据? 就是除了ALU_C,PC+8,DM_out之外的新数据
-
如果没有产生新数据,判断该指令回写数据属于哪个类别,将它的指令标签添加到各需要的流水级的转发控制信号中
-
如果产生了新数据,可能稍稍有点麻烦了。首先要判断新数据最先出现在哪个流水级寄存器中,然后在mips顶层中的转发mux中增加新的端口,并添加相应的控制信号。
-
最先保存回写数据的流水级前面所有的转发mux都要添加该数据
-
举例:M_ALU_C会成为mux_D_TR_GRF_RD1,mux_D_TR_GRF_RD2,mux_E_TR_GRF_RD1和mux_E_TR_GRF_RD2的数据来源
-
-
只要在某流水级寄存器产生了新的回写入GRF的数据,那么这个数据会在后级寄存器中出现,并加到所有前级的转发mux中
-
举例:M_ALU_C,会有W_ALU_C;D_PC+8,会有E_PC+8,M_PC+8以及W_PC+8
-
-
如果是最先保存在WREG中,D级采用内部转发
-
要注意该新回写数据的优先级(在写控制信号的时候)
-
-
7.8.2 需求者
需求者需要考虑的问题:
-
该指令是否有新的使用寄存器值的地方?
如果没有,那就直接跳过。
-
如果有的话,添加新的转发mux,(mips中加mux以及控制器中添加控制信号)。
7.8.3 需要考虑的控制信号
-
D_TR_GRF_RD1_Sel
-
D_TR_GRF_RD2_Sel
-
E_TR_GRF_RD1_Sel
-
E_TR_GRF_RD2_Sel
-
M_TR_GRF_RD2_Sel
7.9 暂停控制
-
暂停控制器只有在条件存储类指令时才会修改,大部分情况是不需要考虑修改暂停控制器的。
-
由于条件存储类指令的GRF_A3知道M级才产生,最先保存在WREG中,所以暂停控制器中会产生误判。
-
所以要在该指令产生确定的GRF_A3之后才取消暂停。
添加指令步骤表
8 思考题
8.1 流水线冒险
-
在采用本节所述的控制冒险处理方式下,PC 的值应当如何被更新?请从数据通路和控制信号两方面进行说明。
将分为发生跳转和不发生跳转两中情况考虑
-
发生跳转(j,jal,jr以及D_cmp_sign==1的beq指令)
-
由于在D级检测到发生跳转,会有延迟槽产生,则在NPC模块中对D级地址做运算,即将写入F级PC模块的地址是,延迟槽指令的下一条指令,也就是当前D级指令要跳转到的指令。
-
-
不发生跳转(其他指令以及D_cmp_sign==0的beq指令)
-
则F级的指令的下一条指令为F级指令的地址加4,正常顺序执行,则在NPC模块中对F级地址直接做加4运算
-
-
控制信号:用一个信号D_NPC_PC_Sel来控制选择F_PC还是D_PC
-
不发生跳转:选择F_PC D_NPC_PC_Sel=0
-
发生跳转:选择D_PC D_NPC_PC_Sel=1
-
-
对于 jal 等需要将指令地址写入寄存器的指令,为什么需要回写 PC+8 ?
-
由于jal等指令为跳转指令,所以会有延迟槽产生。在jal指令的下一条指令也就是PC+4会有一条无关指令,以提高流水线效率。所以真正的jal指令的下一条指令是PC+8。
8.2 数据冒险的分析
-
为什么所有的供给者都是存储了上一级传来的各种数据的流水级寄存器,而不是由 ALU 或者 DM 等部件来提供数据?
-
电路具有延迟性,不论是ALU的计算结果还是取出的DM中的数据都是发生在时钟周期的后半段,如果直接从ALU或者DM部件取数据,取出来的大概率是错误数据。
8.3 AT 法处理流水线数据冒险
-
“转发(旁路)机制的构造”中的 Thinking 1-4;
-
Thinking 1:为了实现转发机制,我们对这些输入前加上一个 MUX。这些 MUX 的默认输入来源是上一级中已经转发过的数据。如果不采用已经转发过的数据,而采用上一级中的原始数据,会出现怎样的问题?试列举指令序列说明这个问题。)下面,我们继续分析这些 MUX 的其他输入来源和选择信号的生成。
-
比如:lw $7,0($0)
nop
sw $7,0($0)
-
在lw指令回写寄存器的周期,sw在M级向DM中存数据,此时由于采用的是未转发的数据,lw的新值已经不在转发数据来源中了(只存在GRF中),所以会存入错误的值。假如采用的是上级已经转发过的数据,M级的GRF_RD2已经是lw的值,就不会发生错误。
-
-
Thinking2:GPR 是一个特殊的部件,它既可以视为 D 级的一个部件,也可以视为 W 级之后的流水线寄存器。基于这一特性,我们将对 GPR采用内部转发机制。也就是说,当前 GPR 被写入的值会即时反馈到读取端上。我们为什么要对 GPR 采用内部转发机制?如果不采用内部转发机制,我们要怎样才能解决这种情况下的转发需求呢?
-
采用内部转发机制,则可以将还没有写入(在下一个时钟上升沿写入)的W级GRF_RD数据读出。如果不采用内部转发,则需要在mux加入W级的转发数据。
-
-
Thinking3:选择信号的生成规则是:只要当前位点的读取寄存器地址和某转发输入来源的写入寄存器地址相等且不为 0为。什么 0 号寄存器需要特殊处理?
-
因为0号寄存器的值始终是0,不能改变。
-
-
Thinking4:在有多个转发输入来源都满足条件时,最新产生的数据优先级最高。什么是“最新产生的数据”?为了获取生成选择信号所需的信息,我们需要对指令的读取寄存器和写入寄存器在 D 级进行译码并流水(指令的“ A 信息”)。
-
就是离当前指令所在级最近的下一级流水线寄存器所产生的值。
-
-
在 AT 方法讨论转发条件的时候,只提到了“供给者需求者的A相同,且不为 0”,但在 CPU 写入 GRF 的时候,是有一个 we 信号来控制是否要写入的。为何在 AT 方法中不需要特判 we 呢?为了用且仅用 A 和 T 完成转发,在翻译出 A 的时候,要结合 we 做什么操作呢?
-
如果翻译出的we(W_RegWrite)信号为0,则代表着GRF_A3为0,不对寄存器做任何改变。
-
9 可能出现的bug和注意的点
-
当只有一种情况的时候,直接使用assign语句,千万不要使用always@(*)块
-
直接assign M_DMop=5'd0;
-
-
区分output和output reg
-
output对应assign语句赋值的变量
-
output reg对应always @(*)块赋值的变量
-
如果将assign改为always@(*)块,记得将output改为output reg
-
-
记得所有if_else语句最后要加else begin_end,避免产生锁存器
-
信号优先级:顶层reset>stall>延迟操清零
-
清空延迟槽的优先级在Stall后面
-
-
reg型变量计数器是否清零