P6
1 P5 流水线CPU设计方案
1.1 设计概述
本文所设计的为Verilog实现的流水线MIPS架构CPU,该CPU支持28条指令,为此,笔者设计了PC,D_Reg, CMP,GRF,EXT,NPC,E_Reg,MDU,ALU,M_Reg,Load,Store,W_Reg,HCU模块,其中IM和DM通过外设实现。整体搭建采用自下而上的方式完成,该CPU的译码方式是分布式译码。
顶层模块设计图如下
1.2 实现指令说明
- cal_R : add,sub,and,or,slt,sltu,lui
- cal_I : addi,andi,ori
- load : lb,lh,lw
- store : sb,sh,sw
- B类指令 : beq,bne
- J类指令 : jal,jr
- md类 :mult,multu,div,divu
- mf类 : mfhi,mflo
- mt类 : mthi,mtlo
所有指令均不考虑运算溢出
2 工程模块定义
2.1 顶层模块
- 端口定义
信号名 方向 位宽 描述 clk I 1 时钟信号 reset I 1 同步复位信号 i_inst_rdata I 32 F级读入指令F_instr m_data_rdata I 32 M级读入数据 i_inst_addr O 32 F_pc m_data_addr O 32 M_AR DM取数据地址 m_data_wdata O 32 M_WD 写入DM数据 m_data_byteen O 4 DM写入数据的字节使能 m_inst_addr O 32 M_pc w_grf_we O 1 W_RegWrite w_grf_addr O 5 W_A3 w_grf_wdata O 32 W_Data 写入GRF的数据 w_inst_addr O 32 W_pc
2.2 功能模块定义
2.2.1 PC
- 端口定义
信号名 方向 位宽 描述 clk I 1 时钟信号 reset I 1 同步复位信号 stall I 1 暂停信号 F_npc I 32 下一条指令地址 F_pc8 O 32 F级pc+8 F_pc4 O 32 F级pc+4 F_pc O 32 F级pc - 功能定义
序号 功能名称 功能描述 1 同步复位 时钟上升沿到来且reset信号有效,PC寄存器中的值置为0x00003000 2 停止 时钟上升沿到来且stall信号有效,PC保持当前的值不变 3 写PC寄存器 时钟上升沿到来且reset和stall信号均失效,将npc写入PC寄存器
2.2.2 CMP
该模块通过比较两个输入,输出信号是否执行B类指令的跳转。
- 端口定义
信号名 方向 位宽 描述 来源 r1 I 32 需要比较的第一个数据 cmp1_Fwd转发后所得 r2 I 32 需要比较的第二个数据 cmp2_Fwd转发后所得 D_Branch I 2 00:非B类指令
01:beq
10:bneD_MCU输出 cmp_out O 1 是否执行B类跳转
2.2.3 GRF
该模块包含32个具有写使能的32位寄存器,对应MIPS架构的$0~$31。
该模块可以实现同步复位和内部转发。
- 端口定义
信号名 方向 位宽 描述 clk I 1 时钟信号 reset I 1 同步复位信号 A1 I 5 需要读取的第一个寄存器编号 A2 I 5 需要读取的第二个寄存器编号 A3 I 5 需要写入的寄存器编号,来自W级 WriteData I 32 需要写入的数据,来自W级 RegWrite I 1 寄存器堆写使能信号,来自W级 pc I 32 当前D级执行的指令 RD1 O 32 从A1寄存器中读取的数据 RD2 O 32 从A2寄存器中读取的数据 - 功能定义
序号 功能名称 功能描述 1 同步复位 时钟上升沿道到来且reset信号有效,GRF中数据清零 2 读数据 读出A1,A2编号对应的寄存器中的数据并将它分别加载到RD1和RD2 3 写数据 时钟上升沿到来且RegWrite信号有效,将WriteData写入A3对应寄存器 4 内部转发 当(A1==A3) && RegWrite && A1 != 0,将WriteData直接加载至RD1
2.2.4 EXT
该模块根据EXTCtrl将指令中的16位立即数扩展位32位
- 端口定义
信号名 方向 位宽 描述 imm16 I 16 需要位扩展的16位立即数 EXTCtrl I 3 000:符号扩展
001:0扩展
010:将立即数加载到高位
011:signExt(imm + 00)E32 O 32 位扩展结果
2.2.5 NPC
该模块通过控制信号和F_pc计算下一条指令的地址
- 端口定义
信号名 方向 位宽 描述 E32 I 32 位扩展后的32位立即数,B类指令使用 imm26 I 26 指令中的26位立即数,jal指令使用 pc I 32 D_pc ra I 32 cmp1_Fwd转发后得到的D_V1,jr指令使用 JCCtrl I 2 00:pc+4或者B类指令
01:jal
10:jrD_npc I 32 跳转指令生效下一条指令地址
2.2.6 MDU
该模块实现乘除法相关运算
- 端口定义
信号名 方向 位宽 描述 clk I 1 时钟信号 reset I 1 同步复位信号 E_start I 1 start为1表示当前E级为乘除运算指令 SrcA I 32 参与运算的第一个数据 SrcB I 32 参与运算的第二个数据 MDCtrl I 4 乘除槽运算控制
0000:mult
0001:multu
0010:div
0011:divu
0100:mfhi
0101:mflo
0110:mthi
0111:mtloMDR O 32 mf类指令输出结果 busy O 1 当前乘除槽正在进行md类指令,表示模拟延迟
2.2.7 ALU
该模块实现算术运算
- 端口定义
信号名 方向 位宽 描述 SrcA I 32 参与运算的第一个数据 SrcB I 32 参与运算的第二个数据 ALUCtrl I 3 000:A&B
001: A或B
010: A+B
110: A-B
011:slt
100:sltu
2.2.8 Load
该模块实现load类指令
- 端口定义
信号名 方向 位宽 描述 m_data_rdata I 32 从外部输入的DM读取初始数据 byte_Addr I 2 M_AR[1:0],字节地址 loadOp I 2 00:lw
01:lh
10:lb
2.2.9 Store
该模块实现store类指令
- 端口定义
信号名 方向 位宽 描述 WD_temp I 32 准备向DM写入的初始数据 byteen I 4 向DM写入数据的字节使能 WD O 32 向DM写入的处理好的数据
2.3 流水寄存器模块定义
2.3.1 D级流水寄存器(IF/ID)
- 端口定义
信号名 方向 位宽 描述 来源 F_instr I 32 F级instr输入 IM F_instr F_pc I 32 F级pc输入 PC F_pc F_pc8 I 32 F级pc8输入 PC F_pc8 reset I 1 同步复位 stall I 1 暂停信号,时钟上升沿到来且stall有效,D级流水寄存器数据保持不变 HCU stall clk I 1 时钟信号 D_instr O 32 D级instr输出 D_pc8 O 32 D级pc8输出 D_pc O 32 D级pc输出
2.3.2 E级流水寄存器(ID/EX)
- 端口定义
信号名 方向 位宽 描述 来源 clk I 1 时钟信号 reset I 1 同步复位信号 stall I 1 暂停信号,时钟上升沿到来且stall信号有效,E级流水寄存器清空 HCU stall D_instr I 32 D级instr输入 D_Reg D_instr D_A1 I 5 D级A1输入 D_Reg D_instr[25:21] D_A2 I 5 D级A2输入 D_Reg D_instr[20:16] D_A3 I 5 D级A3输入 经RegDst信号选择后的D_A3 D_V1 I 32 D级的V1输入 经cmp1_Fwd转发后的数据 D_V2 I 32 D级的V2输入 经cmp2_Fwd转发后的数据 D_pc I 32 D级的pc输入 D_Reg D_pc D_pc8 I 32 D级的pc8输入 D_Reg D_pc8 D_E32 I 32 D级的E32输入 EXT E32 E_instr O 32 E级的instr输出 E_A1 O 5 E级的A1输出 E_A2 O 5 E级的A2输出 E_A3 O 5 E级的A3输出 E_V1 O 32 E级的V1输出 E_V2 O 32 E级的V2输出 E_E32 O 32 E级的E32输出 E_pc8 O 32 E级的pc8输出 E_pc O 32 E级的pc输出
2.3.3 M级流水寄存器(EX/MEM)
- 端口定义
信号名 方向 位宽 描述 来源 clk I 1 时钟信号 reset I 1 同步复位信号 E_instr I 32 E级instr输入 E_Reg E_instr E_A2 I 5 E级A2输入 E_Reg E_A2 E_A3 I 5 E级A3输入 E_Reg E_A3 E_AR I 32 E级ALU计算结果 ALU AR E_Data I 32 E级当前指令的有效数据(MDR/AR) E_V2 I 32 E级V2输入 经ALUb_Fwd转发后得到的E_ALUSrcB_temp E_pc8 I 32 E级pc8输入 E_Reg E_pc8 E_pc I 32 E级pc输入 E_Reg E_pc M_instr O 32 M级instr输出 M_A2 O 5 M级的A2输出 HCU模块确定转发信号DM_Fwd时会使用 M_A3 O 5 M级的A3输出 M_AR O 32 M级的AR输出 M_Datae O 32 E级的Data输出到M级 M_V2 O 32 M级的V2输出 M_pc8 O 32 M级的pc8输出 M_pc O 32 M级的pc输出
2.3.4 W级流水寄存器(MEM/WB)
- 端口定义
信号名 方向 位宽 描述 来源 clk I 1 时钟信号 reset I 1 同步复位信号 M_instr I 32 M级instr输入 M_Reg M_instr M_A3 I 5 M级的A3输入 M_Reg M_A3 M_AR I 32 M级的AR输入 M_Reg M_AR M_RD I 32 M级的RD输入 Load RD M_pc8 I 32 M级的pc8输入 M_Reg M_pc8 M_Data I 32 M级指令对应的有效数据 M_Datae/M_pc8 M_pc I 32 M级的pc输入 M_Reg M_pc W_instr O 32 W级instr输出 W_A3 O 5 W级A3输出 W_AR O 32 W级AR输出 W_RD O 32 W级RD输出 W_pc8 O 32 W级pc8输出 W_Datam O 32 M级Data输出 W_pc O 32 W级pc输出
2.4 控制模块定义
2.4.1 MCU
在主控制模块通过解码opcode和func,分为and逻辑和or逻辑,输出控制信号。本CPU采用分布式译码,即在每一个流水级将MCU实例化一次,选择不同的输出信号。
- 端口定义
信号名 方向 位宽 描述 instr I 32 当前指令,流水级寄存器输出 RegDst O 2 选择D_A3 Branch O 2 B类指令控制信号 EXTCtrl O 3 立即数扩展控制信号 JCtrl O 2 NPC模块跳转控制信号 ALUCtrl O 3 ALU模块算术运算控制信号 MDCtrl O 4 MDU模块乘除运算控制信号 start O 1 表示当前是md类指令 mf O 1 表示当前是mf类指令 ALUSrcBSel O 1 ALU_Srcb选择信号 MemWrite O 1 DM写使能信号 RegWrite O 1 GRF写使能信号 loadOp O 1 Load模块load指令控制信号 m_data_byteen O 4 store类指令字节使能信号 jal O 1 当前是否是jal信号,从而选择需要转发的data MemtoReg O 1 选择需要写入GRF的数据 Tuse_rs O 2 只在D级输出,当前D级指令还有多久需要使用使用rs寄存器的值 Tuse_rt O 2 只在D级输出,当前D级指令还有多久需要使用使用rt寄存器的值 D_Tnew O 2 只在D级输出,当前D级指令还有多久产生需要写入GRF的数据 E_Tnew O 2 只在E级输出,当前E级指令还有多久产生需要写入GRF的数据 M_Tnew O 2 只在M级输出,当前M级指令还有多久产生需要写入GRF的数据 2.4.2 HCU
在冒险控制模块通过“A-T”法分析转发还是暂停 - 端口定义
信号名 方向 位宽 描述 Tuse_rs I 2 当前D级指令还有多久需要使用rs寄存器的值 Tuse_rt I 2 当前D级指令还有多久需要使用rt寄存器的值 E_Tnew I 2 当前在E级的指令还有多久产生写入GRF的数据 M_Tnew I 2 当前在M级的指令还有多久产生写入GRF的数据 E_RegWrite I 1 当前E级指令的RegWrite信号 M_RegWrite I 1 当前M级指令的RegWrite信号 W_RegWrite I 1 当前W级指令的RegWrite信号 D_A1 I 5 D_A2 I 5 E_A1 I 5 E_A2 I 5 E_A3 I 5 M_A2 I 5 M_A3 I 5 W_A3 I 5 stall O 1 暂停信号
冻结PC的值
冻结D级流水寄存器的值
将E级流水寄存器清空cmp1_Fwd O 选择D_V1 00:RD1
10:M_Datacmp2_Fwd O 选择D_V2 00:RD2
10:M_DataALUa_Fwd O 选择ALU_SrcA 00:E_V1
01:W_out
10:M_DataALUb_Fwd O 选择ALU_SrcB 00:E_V2
01:W_out
10:M_DataDM_Fwd O 选择写入DM的数据 0:M_V2
01: W_out
2.5 选择模块
2.5.1 功能MUX
MUX名 | 描述 | 输出信号 | 控制信号 |
---|---|---|---|
MUX_npc | 对PC模块F_npc输入信号进行选择 | 0:F_pc4 1: D_npc |
npcSel |
MUX_A3 | 对D级中A3输入信号进行选择 00:D_instr[20:16] 01:D_instr[15:11] 5’b11111 |
D_A3 | E_RegDst |
MUX_ALUB | 对E级ALU模块SrcB接口的信号进行选择 0:E_ALUSrcB_temp 1:E_E32 |
E_SrcB | ALUSrcBSel |
MUX_E_Data | 对E级的有效数据进行选择 0:E_AR 1:E_MDR |
E_Data | E_mf |
MUX_M_Data | 对M级data进行选择 0:M_V2 1:W_out |
M_Data | M_jal |
MUX_W_Out | 对W级data进行选择 00:W_AR 01:W_RD 10;W_pc8 |
W_out | MemtoReg |
2.5.2 转发MUX
MUX名 | 描述 | 输出信号 | 控制信号 |
---|---|---|---|
HMUX_cmp1 | 将数据转发到D_V1端口 | D_V1 | cmp1_Fwd |
HMUX_cmp2 | 将数据转发到D_V2端口 | D_V2 | cmp2_Fwd |
HMUX_ALUa | 将数据转发到ALUSrcA端口 | E_SrcA | ALUa_Fwd |
HMUX_ALUb | 将数据转发到ALUSrcBtemp端口 | E_SrcB_temp | ALUb_Fwd |
HMUX_DM | 将数据转发到DM_WD端口 | M_WD | DM_Fwd |
3 重要实现方法
3.1 分支转移实现
3.1.1 B类指令
为了减少因控制冲突导致的暂停(stall),我们将B类指令的判断进行前置,单独使用CMP模块进行判断。如果cmp_out==1 && Branch==1,进行跳转。
3.1.2 jal
当jal进入D级后(此时F级的指令为编译优化调度的指令),D_instr中imm26域的数据进入NPC进行处理,如果当前JCCtrl信号为2’b01(说明当前指令为jal指令),NPC输出转移的地址npc,并进入PC的输入端,在下一时钟沿上升时进入F级,实现转移。
jal指令在实现跳转的同时,还需要将下一条指令的地址存入31号寄存器中,因此我们需要在F级中计算出改地址,并随着jal指令进行流水,最终在W级写入GRF的31号寄存器。由于存在延迟槽,pc+4地址中的指令是编译优化机制调度过来的,因此我们要保存的地址应该为pc+8。
3.1.3 jr
当jr进入D级后(此时F级的指令为编译优化调度的指令),D_V1(经过转发后的D_V1值)进入NPC,如果当前JCtrl为2’b10(说明当前指令为jr指令),NPC输出转移的地址npc,并进入PC的输入端,在下一时钟沿上升时进入F级,实现转移。
3.2 冒险Harzard
“A-T”法
3.2.1 暂停Stall
当一个指令到达D级后,我们将它的Tuse与后面每一级Tnew比较以及A值校验,当Tuse < Tnew时,阻塞流水线。
阻塞在D级:
- 冻结PC的值
- 冻结D级流水寄存器的值
- 将E级流水寄存器清空
3.2.2 转发Forward
当一个指令到达D级后,我们需要将它的Tuse与后面每一级Tnew比较以及A值校验,当Tuse >= Tnew,进行转发。
其中GRF实现内部转发。
3.3 乘除槽
设置MDU模块以支持乘除法以及mf类和md类指令。
3.3.1 mf类指令
直接从HI,LO寄存器读取即可
1 | assign MDR = (MDCtrl == 4'b0100) ? HI : |
3.3.2 mt类指令
将数据写入HI,LO寄存器即可
1 | case (MDCtrl) |
3.3.3 md类指令
对于md指令需要做到以下三点:
- 当md类指令到达E级时,start信号置高,表示要开始处理md类指令
- start信号置高后,乘法max赋值为5,除法max赋值为10,busy信号置高,cnt置1,运算结果存入temp寄存器
- cnt每个周期自增,当达到max时,将temp寄存器的值写入HI,LO寄存器,同时cnt和busy_reg寄存器清零
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42else if (E_start) begin //E级需要进行乘除法运算
case (MDCtrl)
4'b0000 : begin
{HI_temp,LO_temp} <= $signed (SrcA) * $signed (SrcB) ;
max <= 5;
end
4'b0001 : begin
{HI_temp,LO_temp} <= $unsigned (SrcA) * $unsigned(SrcB) ;
max <= 5;
end
4'b0010 : begin
LO_temp <= $signed (SrcA) / $signed (SrcB) ;
HI_temp <= $signed (SrcA) % $signed (SrcB) ;
max <= 10;
end
4'b0011 : begin
LO_temp <= $unsigned (SrcA) / $unsigned (SrcB);
HI_temp <= $unsigned (SrcA) % $unsigned (SrcB);
max <= 10;
end
default: ;
endcase
end
# cnt busy
always @(posedge clk ) begin
if (E_start) begin
busy_reg <= 1'b1;
cnt <= 1;
end
else if ((!E_start) && (cnt == max) && busy_reg) begin
busy_reg <= 0;
cnt <= 0;
HI <= HI_temp;
LO <= LO_temp;
end
else if((!E_start) && ((cnt >= 1) && (cnt < max)) && busy_reg) begin
cnt <= cnt + 1;
end
else begin
cnt <= 0;
end
end
3.3.4 冒险
当E级正在进行md类指令时,会把乘除指令均阻塞在D级
1 | assign stall_MD = D_MD && (E_busy || E_start); |
4 思考题
为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?
答:①因为乘除法计算有延迟,如果把乘除法部件整合进ALU就会影响到后面每一条涉及计算的指令,大大降低CPU的效率;而将乘除法部件单独设置,只会对后面涉及乘除法的指令产生影响
②乘除法指令涉及读写HI,LO寄存器,这与其他指令不同,根据“高内聚,低耦合”的原则,单独设立乘除法部件真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。
答:①乘法:如图,将每个右边的加法器的输出作为左边加法器的输入,形成一个高32的加法器栈。将32个加法器组成组织成一个并行树,这样,只用等待log2(32) = 5次32位长加法的时间,而不是等待32次加法的时间(《计算机组成与设计》P124 3.3.3 )
②除法:与乘法同理,但是因为除法运算每次迭代前需要知道减法结果的符号,而乘法却可以即可生成32个部分积,因此除法需要10个周期延迟(结合书本,我的思考是这样的)。还有 一些技术可以每步生成不仅一个商位,如SRT算法。(?)请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?
答:对于md指令需要做到以下三点:
① 当md类指令到达E级时,start信号置高,表示要开始处理md类指令② start信号置高后,乘法max赋值为5,除法max赋值为10,busy信号置高,cnt置1,运算结果存入temp寄存器
③ cnt每个周期自增,当达到max时,将temp寄存器的值写入HI,LO寄存器,同时cnt和busy_reg寄存器清零
④阻塞条件
1
assign stall_MD = D_MD && (E_busy || E_start);
请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)
答:①清晰性:能够清晰知道哪个字节需要被写入
②统一性:对于sw,sh,sb等不同指令写入不同位,只需考虑字节使能信号哪些位置1即可,处理多种情况具有统一性请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?
答:①实际从DM获得的数据和向DM写入数据都是一字,而不是一字节②对于半字访存或者字节访存时,按字节读或写的效率更高。如果此时还采用按字访问,则需要首先将整个字从内存中拿出来,然后再从字中寻找,效率会更低。
为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?
①将指令分类,分为cal_R,cal_I,load,store,md,mf,mt几类,用这些类来处理对应信号。
②用多位宽的信号表示相似信号,比如ALUCtrl,MDCtrl,EXTCCtrl,loadOp等
祝P6上机一切顺利!
- Post title:P6
- Post author:Coooookie
- Create time:2023-01-02 20:30:13
- Post link:https://coooookie0913.github.io/2023/01/02/P6/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.