P5流水线CPU设计文档
设计整体概述
预计实现指令集 :add、sub、ori、Lui、lw、sw、beq、nop、j、jr、jal
opcode
rs
rt
rd
shamt
funct
6
5
5
5
5
6
opcode
rs
rt
offset
6
5
5
16
其中,nop指令为空指令,不执行任何操作,add、sub不考虑溢出,故实际执行addu和subu
实现功能 :实现五级流水线CPU,通过F、D、E、M、W五个阶段实现指令的执行,其中F阶段为取指,D阶段为译码,E阶段为执行,M阶段为访存,W阶段为写回,各阶段均通过流水线寄存器实现流水线操作,通过转发和阻塞机制解决冒险问题。
顶层设计图 :
整体架构参考了PPT上的设计
命名格式 :模块命名采用所在级_模块名
方式,模块内部信号名采取来源_功能
方式命名,顶层信号采用所在级_模块名_信号名
方式命名。
数据通路模块设计
F级:取指Fetch
F_IFU 指令单元
定义 :模块内部包含PC
程序计数器、IM
指令存储器实现,负责根据PC的值从指令存储器中读取指令输出。
端口定义
信号名
方向
位宽
说明
D_Next_PC
I
32
NPC
模块输出的下一条指令地址
clk
I
1
时钟信号
reset
I
1
复位信号
stop
I
1
停机信号
F_Instr
O
32
F级的32位指令
F_PC
O
32
输出F级PC值,流入D级寄存器
F_PC8
O
32
PC
加8的值,用于分支延迟槽指令
1 2 3 4 reg [31 :0 ] IM_Reg [0 :4095 ];initial begin $readmemh ("code.txt" , IM_Reg); end
实现从文件中读取指令的功能
2. 实现功能
序号
功能
描述
1
同步复位
当reset
信号为1且处于时钟上升沿时,PC
置0x00003000
2
停机
当stop信号为1时,PC
和指令保持不变
3
取指
当stop信号为0时,在clk上升沿得到的下一条指令地址PC,读取指令
PC
初始化为0x00003000
IM
取PC[13:2]作为地址,减去0x00003000后读取指令
D级:译码decode
D_REG D级流水线寄存器
定义 :用于在时钟上升沿将F级的指令和PC值传递到D级,并在复位重置寄存器值,并在停机信号为1时保持不变
端口定义
信号名
方向
位宽
说明
clk
I
1
时钟信号
reset
I
1
复位信号
stall
I
1
停机信号
F_Instr
I
32
F级的32位指令
F_PC
I
32
F级的PC值
F_PC8
I
32
F级的PC+8值
D_Instr
O
32
D级的32位指令
D_PC
O
32
D级的PC值
D_PC8
O
32
D级的PC+8值
实现功能
序号
功能
描述
1
同步复位
当reset
信号为1且处于时钟上升沿时,D_Instr
置0,D_PC
置PC_Reset
,D_PC8
置PC_Reset + 4
2
停机
当stall
信号为1时,保持D_Instr
、D_PC
和D_PC8
的值不变
3
正常传递
当stall
信号为0时,在时钟上升沿将F_Instr
、F_PC
和F_PC8
的值传递到D_Instr
、D_PC
和D_PC8
D_NPC 指令计算单元
定义 :根据当前PC值、指令类型和条件分支结果,计算下一条指令的PC值。
端口定义
信号名
方向
位宽
说明
F_PC
I
32
当前指令的PC值
Instr_index
I
26
跳转指令的目标地址
offset
I
16
分支指令的偏移量
rs
I
32
寄存器rs的值
NPCSel
I
3
下一条指令PC选择信号
CMPOut
I
1
比较结果信号
D_Next_PC
O
32
计算得到的下一条指令的PC值
实现功能
序号
功能
描述
1
计算分支地址
根据当前PC值和偏移量计算分支指令的目标地址
2
计算跳转地址
根据当前PC值和指令索引计算跳转指令的目标地址
3
选择下一条指令PC
根据NPCSel信号选择下一条指令的PC值,支持顺序执行、分支、跳转和寄存器跳转
选择信号
位宽
数值
描述
BranchType
3
3’b011
B型跳转,与CMPOut共同决定跳转
JrType
3
3’b010
JR型跳转
JumpType
3
3’b001
J型跳转
D_EXT 指令扩展
定义 :根据控制信号,对输入的16位立即数进行零扩展或符号扩展。
端口定义
信号名
方向
位宽
说明
offset
I
16
16位立即数
ExtOp
I
1
扩展选择信号,0为零扩展,1为符号扩展
Extend
O
32
扩展后的32位立即数
实现功能
序号
功能
描述
1
符号扩展
当ExtOp
信号为1时,Extend
输出符号扩展后的32位立即数
2
零扩展
当ExtOp
信号为0时,Extend
输出零扩展后的32位立即数
D_CMP 比较器
定义 :根据控制信号,对输入的两个数进行比较,输出比较结果。
端口定义
信号名
方向
位宽
说明
RD1
I
32
读寄存器1数据
RD2
I
32
读寄存器2数据
CMPOp
I
3
比较操作选择信号
CMPOut
O
1
比较结果输出
实现功能
序号
功能
描述
1
相等比较
当CMPOp
信号为CMP_BEQ
时,若RD1
等于RD2
,CMPOut
输出1,否则输出0
2
不等比较
当CMPOp
信号为CMP_BNE
时,若RD1
不等于RD2
,CMPOut
输出1,否则输出0
3
小于等于零
当CMPOp
信号为CMP_BLEZ
时,若RD1
小于等于0,CMPOut
输出1,否则输出0
4
大于零
当CMPOp
信号为CMP_BGTZ
时,若RD1
大于0,CMPOut
输出1,否则输出0
GRF 寄存器堆
定义 :根据读写信号,对寄存器堆进行读写操作。
端口定义
信号名
方向
位宽
说明
A1
I
5
读寄存器1地址
A2
I
5
读寄存器2地址
RegAddr
I
5
写寄存器地址
RegData
I
32
写数据
clk
I
1
时钟信号
reset
I
1
复位信号
RegWrite
I
1
写使能信号
PC
I
32
当前PC地址,用于输出
RD1
O
32
读寄存器1数据
RD2
O
32
读寄存器2数据
1 2 3 4 5 6 if (RegWrite) begin if (RegAddr != 0 ) begin GRF_reg[RegAddr] <= RegData; $display ("@%h: $%d <= %h" , PC, RegAddr, RegData); end end
输出寄存器堆中寄存器改变的信息
3. 实现功能
序号
功能
描述
1
复位
当reset为1时,将所有寄存器置为0
2
读
当RegWrite为0时,将A1、A2对应的寄存器数据输出到RD1、RD2
3
写
当RegWrite为1时,当RegAddr不为0时,在时钟上升沿将RegAddr对应的寄存器写入RegData
A1、A2分别读取Rs、Rt寄存器,RegAddress根据RegDst选择Rt或R),RegData根据MemRead选择ALU的运算结果或I型指令的立即数
内部转发
当RegWrite
为1时,若D级读取的寄存器和W级写入的寄存器相同且不为0,则将RegAddr
对应的寄存器的值输出到RD1
或RD2
。
1 2 3 4 assign RD1 = (A1 == RegAddr && RegWrite && RegAddr != 0 ) ? RegData : GRF_reg[A1]; assign RD2 = (A2 == RegAddr && RegWrite && RegAddr != 0 ) ? RegData : GRF_reg[A2];
E级:执行Execute
E_REG E级流水线寄存器
定义 :存储E级流水线寄存器的数据。
端口定义
信号名
方向
位宽
说明
clk
I
1
时钟信号
reset
I
1
复位信号
clr
I
1
清除信号
D_Instr
I
32
D级指令
D_PC
I
32
D级PC值
D_PC8
I
32
D级PC+8值
D_A1
I
5
D级寄存器地址1
D_A2
I
5
D级寄存器地址2
D_RD1
I
32
D级寄存器数据1
D_RD2
I
32
D级寄存器数据2
D_Rt
I
5
D级Rt寄存器地址
D_Rd
I
5
D级Rd寄存器地址
ALUOp_D
I
4
D级ALU操作码
D_Tnew
I
2
D级新指令周期
ALUBSrcSel_D
I
1
D级ALU B源选择信号
RegWrite_D
I
1
D级寄存器写使能信号
MemWrite_D
I
1
D级内存写使能信号
RegDataSel_D
I
3
D级寄存器数据选择信号
RegDstSel_D
I
3
D级寄存器目标选择信号
D_Extend
I
32
D级扩展数据
E_Instr
O
32
E级指令
E_PC
O
32
E级PC值
E_PC8
O
32
E级PC+8值
E_A1
O
5
E级寄存器地址1
E_A2
O
5
E级寄存器地址2
E_RD1
O
32
E级寄存器数据1
E_RD2
O
32
E级寄存器数据2
E_Rt
O
5
E级Rt寄存器地址
E_Rd
O
5
E级Rd寄存器地址
ALUOp_E
O
4
E级ALU操作码
E_Tnew
O
2
E级新指令周期
ALUBSrcSel_E
O
1
E级ALU B源选择信号
RegWrite_E
O
1
E级寄存器写使能信号
MemWrite_E
O
1
E级内存写使能信号
RegDataSel_E
O
3
E级寄存器数据选择信号
RegDstSel_E
O
3
E级寄存器目标选择信号
E_Extend
O
32
E级扩展数据
实现功能
序号
功能
描述
1
同步复位
当reset
信号为1或clr
信号为1时,所有输出信号清空,对于暂停操作而言相当于插入nop指令
2
正常传递
在时钟上升沿,将D级输入信号传递到E级输出信号
ALU 算术逻辑单元
定义 :根据控制信号,对输入的两个数进行算术或逻辑运算。
端口定义
信号名
方向
位宽
说明
A
I
32
输入操作数A
B
I
32
输入操作数B
ALU_Op
I
4
ALU操作码
ALU_Res
O
32
运算结果
实现功能
根据ALU_Op的值,对输入的A和B进行相应的算术或逻辑运算,并将结果输出到ALU_Res。
序号
功能
描述
1
ADD
A + B
2
SUB
A - B
3
AND
A & B
4
OR
A | B
5
XOR
A ^ B
6
LUI
加载到高位
M级:存储Memory
M_REG M级流水线寄存器
定义 :存储M级流水线寄存器的数据。
端口定义
信号名
方向
位宽
说明
clk
I
1
时钟信号
reset
I
1
复位信号
E_Instr
I
32
E级指令
E_PC
I
32
E级PC值
E_PC8
I
32
E级PC+8值
E_A1
I
5
E级寄存器地址1
E_A2
I
5
E级寄存器地址2
E_RegAddr
I
5
E级寄存器写地址
E_RD1
I
32
E级寄存器数据1
E_RD2
I
32
E级寄存器数据2
MemWrite_E
I
1
E级内存写使能信号
RegWrite_E
I
1
E级寄存器写使能信号
RegDataSel_E
I
3
E级寄存器数据选择信号
E_ALU_Res
I
32
E级ALU运算结果
E_Tnew
I
2
E级新指令周期
E_ALU_Fwd_MUXOut
I
32
E级ALU转发MUX输出
M_Instr
O
32
M级指令
M_PC
O
32
M级PC值
M_PC8
O
32
M级PC+8值
M_A1
O
5
M级寄存器地址1
M_A2
O
5
M级寄存器地址2
M_RegAddr
O
5
M级寄存器写地址
M_RD1
O
32
M级寄存器数据1
M_RD2
O
32
M级寄存器数据2
MemWrite_M
O
1
M级内存写使能信号
RegWrite_M
O
1
M级寄存器写使能信号
RegDataSel_M
O
3
M级寄存器数据选择信号
M_ALU_Res
O
32
M级ALU运算结果
M_Tnew
O
2
M级新指令周期
M_ALU_Fwd_MUXOut
O
32
M级ALU转发MUX输出
实现功能
序号
功能
描述
1
同步复位
当reset
信号为1时,所有输出信号清空,对于暂停操作而言相当于插入nop指令
2
正常传递
在时钟上升沿,将E级输入信号传递到M级输出信号
M_DM 数据存储器
定义 :使用RAM实现,根据控制信号,对输入的地址和数据进行读写操作。
端口定义
信号名
方向
位宽
说明
MemAddr
I
32
32位地址
WD
I
32
32位数据
clk
I
1
时钟信号
reset
I
1
同步复位信号
WE
I
1
读/写控制信号,0为读,1为写
PC
I
32
PC地址
DM_RD
O
32
32位数据
实现功能
序号
功能
描述
1
复位
当reset为1时,将所有数据存储单元置零
2
读
当WE为0时,输出地址对应的32位数据DM_RD
3
写
当WE为1时,将MemAddr对应地址写入32位数据WD
W级:写回WriteBack
W_REG W级流水线寄存器
定义 :存储W级流水线寄存器的数据。
端口定义
信号名
方向
位宽
说明
clk
I
1
时钟信号
reset
I
1
复位信号
M_Instr
I
32
M级指令
M_PC
I
32
M级PC值
M_PC8
I
32
M级PC+8值
M_A1
I
5
M级寄存器地址1
M_A2
I
5
M级寄存器地址2
M_RegAddr
I
5
M级寄存器写地址
M_RD1
I
32
M级寄存器数据1
M_RD2
I
32
M级寄存器数据2
M_ALU_Res
I
32
M级ALU运算结果
M_DM_RD
I
32
M级数据存储器读数据
RegWrite_M
I
1
M级寄存器写使能信号
RegDataSel_M
I
3
M级寄存器数据选择信号
M_Tnew
I
2
M级新指令周期
W_Instr
O
32
W级指令
W_PC
O
32
W级PC值
W_PC8
O
32
W级PC+8值
W_A1
O
5
W级寄存器地址1
W_A2
O
5
W级寄存器地址2
W_RegAddr
O
5
W级寄存器写地址
W_RD1
O
32
W级寄存器数据1
W_RD2
O
32
W级寄存器数据2
W_ALU_Res
O
32
W级ALU运算结果
W_DM_RD
O
32
W级数据存储器读数据
RegWrite_W
O
1
W级寄存器写使能信号
RegDataSel_W
O
3
W级寄存器数据选择信号
W_Tnew
O
2
W级新指令周期
实现功能
序号
功能
描述
1
同步复位
当reset
信号为1时,所有输出信号清空,对于暂停操作而言相当于插入nop指令
2
正常传递
在时钟上升沿,将M级输入信号传递到W级输出信号
MCU 控制单元
定义
MCU 控制单元用于生成控制信号,控制其他模块的行为。
端口定义
信号名
方向
位宽
说明
Op
I
6
操作码
Func
I
6
功能码
RegDstSel
O
3
寄存器目标选择信号
ALUBSrcSel
O
1
ALU_B源选择信号
RegDataSel
O
3
寄存器数据选择信号
RegWrite_D
O
1
寄存器写使能信号
MemWrite_D
O
1
内存写使能信号
NPCSel
O
3
下一条指令PC选择信号
ExtOp_D
O
1
扩展操作选择信号
CMPOp
O
3
比较操作选择信号
ALU_Op
O
4
ALU操作码
Tuse_rs
O
2
rs使用需要周期数
Tuse_rt
O
2
rt使用需要周期数
Tnew_D
O
2
产生数据所用周期数
实现功能
序号
功能
描述
1
指令译码
根据操作码和功能码生成对应的控制信号
2
控制信号生成
生成寄存器目标选择信号、ALU B源选择信号、寄存器数据选择信号等
3
冒险时间生成
生成rs和rt的使用时间以及新指令周期
多路选择器说明
RegDstSel :寄存器目标选择信号,用于选择写入寄存器的目标寄存器。
类型
方向
位宽
说明
RaType
I
32
写回ra寄存器
RdType
I
32
写回rd寄存器
RtType
I
32
写回rt寄存器
ALUBSel :ALU B源选择信号,用于选择ALU B输入端的源。
类型
方向
位宽
说明
E_ALU_Fwd_MUXOut
I
32
寄存器值
EXT
I
32
扩展后的立即数
RegDataSel :寄存器数据选择信号,用于选择写入寄存器的数据源。
类型
方向
位宽
说明
FromPC
I
32
来自PC8
FromALU
I
32
来自ALU
FromDM
I
32
来自DM
HCU 冒险控制单元
定义 :冒险控制单元用于检测流水线中的数据冒险,并生成相应的转发和停顿信号。
端口定义
信号名
方向
位宽
说明
Tuse_rs
I
2
rs使用需要周期数
Tuse_rt
I
2
rt使用需要周期数
Tnew_E
I
2
E级产生数据需要周期
Tnew_M
I
2
M级产生数据需要周期
Tnew_W
I
2
W级产生数据需要周期
D_A1
I
5
D级寄存器地址1
D_A2
I
5
D级寄存器地址2
E_A1
I
5
E级寄存器地址1
E_A2
I
5
E级寄存器地址2
E_A3
I
5
E级寄存器写地址
M_A2
I
5
M级寄存器地址2
M_A3
I
5
M级寄存器写地址
W_A3
I
5
W级寄存器写地址
RegWrite_E
I
1
E级寄存器写使能信号
RegWrite_M
I
1
M级寄存器写使能信号
RegWrite_W
I
1
W级寄存器写使能信号
CMP_RD1_Fwd_D
O
3
比较器RD1转发信号
CMP_RD2_Fwd_D
O
3
比较器RD2转发信号
ALU_A_Fwd_E
O
3
ALU A转发信号
ALU_B_Fwd_E
O
3
ALU B转发信号
DM_WD_Fwd_M
O
3
数据存储器写数据转发信号
stall
O
1
停顿信号
实现功能
序号
功能
描述
1
停顿检测
根据寄存器地址和使用周期数,检测是否需要停顿
2
转发信号生成
生成比较器、ALU和数据存储器的转发信号
3
停顿信号生成
生成流水线停顿信号
暂停Stall
使用A-T法判断,当address相同时,若D级的Tuse_rs或Tuse_rt
比E、M、W级的Tnew小,则说明在D级需要使用寄存器中数据时,E、M、W级相应的寄存器数据还未准备好,需要暂停流水线。
1 2 3 4 5 6 7 8 E_Stall_Rs = (E_A3 == D_A1) && (D_A1 != 5'b0 ) && (Tnew_E > Tuse_rs) && RegWrite_E; E_Stall_Rt = (E_A3 == D_A2) && (D_A2 != 5'b0 ) && (Tnew_E > Tuse_rt) && RegWrite_E; M_Stall_Rs = (M_A3 == D_A1) && (D_A1 != 5'b0 ) && (Tnew_M > Tuse_rs) && RegWrite_M; M_Stall_Rt = (M_A3 == D_A2) && (D_A2 != 5'b0 ) && (Tnew_M > Tuse_rt) && RegWrite_M; stall = E_Stall_Rs || E_Stall_Rt || M_Stall_Rs || M_Stall_Rt;
转发Foward
执行暴力转发,使用A-T法判断,当address相同且D级的Tuse_rs或Tuse_rt大于E、M级相应的Tnew时,说明D级需要使用的数据在E、M级中,需要转发。
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 assign CMP_RD1_Fwd_D = (D_A1 == 5'b0 ) ? `FWD_GRF : (E_A3 == D_A1 && RegWrite_E) ? `FWD_EXE : (M_A3 == D_A1 && RegWrite_M) ? `FWD_MEM : (W_A3 == D_A1 && RegWrite_W) ? `FWD_WB : `FWD_GRF; assign CMP_RD2_Fwd_D = (D_A2 == 5'b0 ) ? `FWD_GRF : (E_A3 == D_A2 && RegWrite_E) ? `FWD_EXE : (M_A3 == D_A2 && RegWrite_M) ? `FWD_MEM : (W_A3 == D_A2 && RegWrite_W) ? `FWD_WB : `FWD_GRF; assign ALU_A_Fwd_E = (E_A1 == 5'b0 ) ? `FWD_GRF : (M_A3 == E_A1 && RegWrite_M) ? `FWD_MEM : (W_A3 == E_A1 && RegWrite_W) ? `FWD_WB : `FWD_GRF; assign ALU_B_Fwd_E = (E_A2 == 5'b0 ) ? `FWD_GRF : (M_A3 == E_A2 && RegWrite_M) ? `FWD_MEM : (W_A3 == E_A2 && RegWrite_W) ? `FWD_WB : `FWD_GRF; assign DM_WD_Fwd_M = (M_A3 == 5'b0 ) ? `FWD_GRF : (W_A3 == M_A3 && RegWrite_W) ? `FWD_WB : `FWD_GRF;
顶层模块
定义 :顶层模块,将所有模块连接起来,实现整个CPU的功能。
端口定义
信号名
方向
位宽
说明
clk
I
1
时钟信号
reset
I
1
复位信号
功能实现 :实例化所有模块,定义各模块间的连接,在顶层模块中用?:
实现多路选择器。
测试方案
第五次思考题
我们使用提前分支判断的方法尽早产生结果来减少因不确定而带来的开销,但实际上这种方法并非总能提高效率,请从流水线冒险的角度思考其原因并给出一个指令序列的例子。
由于分支跳转指令的Tuse为0,因此若对应寄存器在E、M、W级的Tnew大于0,则会导致冒险,必须阻塞流水线,效率反而降低。
1 2 lui $1 , 100 beq $1 , $0 , label1
因为延迟槽的存在,对于 jal 等需要将指令地址写入寄存器的指令,要写回 PC + 8,请思考为什么这样设计?
因为延迟槽的存在,jal等指令在D级才能译码,此时F级的指令地址为PC+4,当写回地址时,若仍按照PC+4,则会跳转到延迟槽中的指令,这显然是不对的,因此只要写回D_PC+8,即跳转指令地址+8,或者是F_PC+4,也是正确的。(由于延迟槽存在,实际上能够保证此时F_PC=D_PC+4)
我们要求所有转发数据都来源于流水寄存器而不能是功能部件(如 DM、ALU),请思考为什么?
因为流水线寄存器中的数据是在上一周期已经计算好的,而在这一周期的时钟上升沿写入,可以保证在下一时钟周期之前稳定输出,而功能部件使用组合逻辑,其输入取决于输入端口的值,而如果在转发供给者功能部件输出端口转发,由于功能部件输出有延迟,可能导致接收者的输入改变的时间点不确定,从而无法保证数据正确性与稳定性。
我们为什么要使用 GPR 内部转发?该如何实现?
设计文档里有说明 当W级与D级使用同一个寄存器时,为了避免数据冒险,使用内部转发既可以避免D级取到错误的寄存器值,也可以避免阻塞流水线。实现如下:
1 2 3 4 5 assign RD1 = (A1 == RegAddr && RegWrite && RegAddr != 0 ) ? RegData : GRF_reg[A1]; assign RD2 = (A2 == RegAddr && RegWrite && RegAddr != 0 ) ? RegData : GRF_reg[A2];
我们转发时数据的需求者和供给者可能来源于哪些位置?共有哪些转发数据通路?
需求者:D级的CMP(RD1和RD2),E级的ALU(ALU_A和ALU_B),M级的DM(DM_WD)
供给者:W级的GRF(RegData),E级的ALU(ALU_Res),M级的DM(DM_RD)
转发数据通路:GRF内部转发,M级到D级,M级到E级,W级到D级(GRF内部转发),W级到E级,W级到M级
在课上测试时,我们需要你现场实现新的指令,对于这些新的指令,你可能需要在原有的数据通路上做哪些扩展或修改?提示:你可以对指令进行分类,思考每一类指令可能修改或扩展哪些位置。
计算指令:首先处理ALU的运算逻辑,然后在MCU中添加对应的信号处理语句,我一般会用宏定义来处理指令的Op和Func,再选择对应的信号,处理Tnew和Tuse,基本不需要改变数据通路。
跳转指令:可能是条件跳转+无条件链接
,条件跳转+条件链接
,对于条件跳转,在CMP中添加对应判断;对于链接,无条件链接直接修改为RaType
,条件链接则需要对CMP比较结果进行流水,在对RegDst选择与RegWrite选择时根据CMP结果判断,在MCU中添加对应逻辑。
访存指令:如果是条件存储
,需要到M级才能得到结果,对于转发和暂停需要设置D级的Tnew为3,需要在数据通路中增加一个多路选择器用于选择该信号数据或地址,在MCU中添加以上控制信号逻辑。
简要描述你的译码器架构,并思考该架构的优势以及不足。
采用分布式译码,优点在于增加指令时更为便利,无需流水到每一级,直接增加端口就可以控制;缺点是当指令数量较多时,译码器会变得复杂,且浪费资源。