P7 Design documents
Verilog流水线CPU设计文档
一、 CPU设计方案综述
(一) 总体设计概述
使用Verilog开发一个简单的流水线CPU,总体概述如下:
-
此CPU为32位CPU
-
此CPU为流水线设计
-
此CPU支持的指令集为:
mips-c指令集所有指令
-
add, sub不支持溢出
(二) 关键模块定义
主代码mips
Folding coding
1 |
|
宏的定义
Folding coding
1 |
|
CPU模块
<一>.F级流水线
1. PC
(1) 端口说明
序号 | 信号名 | 方向 | 描述 |
---|---|---|---|
1 | clk | I | 时钟信号 |
2 | reset | I | 同步复位信号,将PC值置为0x0000_3000: 0:无效 1:复位 |
3 | en | I | 使能信号,决定是否阻塞 |
4 | NPC[31:0] | I | 下一周期PC的地址 |
6 | PC[31:0] | O | 当前执行的PC |
(2) 功能定义
序号 | 功能 | 描述 |
---|---|---|
1 | 复位 | reset有效时,PC置为0x00003000 |
2 | 更新PC的值 | 将PC赋值为NPC |
<二>.D级流水线
1. GRF
(1) 端口说明
序号 | 信号名 | 方向 | 描述 |
---|---|---|---|
1 | clk | I | 时钟信号 |
2 | reset | I | 同步复位信号,将32个寄存器中全部清零 1:清零 0:无效 |
3 | WE | I | 写使能信号 1:可向GRF中写入数据 0:不能向GRF中写入数据 |
4 | A1[4:0] | I | 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD1 |
5 | A2[4:0] | I | 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD2 |
6 | A3[4:0] | I | 5位地址输入信号,指定32个寄存器中的一个,作为RD的写入地址 |
7 | WD[31:0] | I | 32位写入数据 |
8 | WPC[31:0] | I | 当前写入GRF的PC |
9 | RD1[31:0] | O | 输出A1指定的寄存器的32位数据 |
10 | RD2[31:0] | O | 输出A2指定的寄存器的32位数据 |
(2) 功能定义
序号 | 功能 | 描述 |
---|---|---|
1 | 同步复位 | reset为1时,将所有寄存器清零 |
2 | 读数据 | 将A1和A2地址对应的寄存器的值分别通过RD1和RD2读出 |
3 | 写数据 | 当WE为1且时钟上升沿来临时,将WD写入到A3对应的寄存器内部 |
4 | 内部转发 | 当A1和A2之一与A3相等且写入信号为1时,用WD代替RD1或RD2的输出 |
2. EXT
(1) 端口说明
序号 | 信号名 | 方向 | 描述 |
---|---|---|---|
1 | imm16[15:0] | I | 代扩展的16位信号 |
2 | sign[2:0] | I | 无符号或符号扩展选择信号 3’b001:无符号扩展 3’b010:符号扩展 3’b100: 寄存到高位 |
3 | imm32[31:0] | O | 扩展后的32位的信号 |
(2) 功能定义
序号 | 功能 | 描述 |
---|---|---|
1 | 无符号扩展 | 当sign为3’b001时,将imm16无符号扩展输出 |
2 | 符号扩展 | 当sign为3‘b010时,将imm16符号扩展输出 |
3 | 存储到高位 | 当sign为3’100时,将imm16存在高16位 |
3. Controller
(1) 端口说明
序号 | 信号名 | 方向 | 描述 |
---|---|---|---|
1 | op[5:0] | I | instr[31:26]6位控制信号 |
2 | func[5:0] | I | instr[5:0]6位控制信号 |
3 | rt[4:0] | I | instr[20:16]5位控制信号 |
4 | AluOp[11:0] | O | ALU的控制信号 |
5 | WeGrf | O | GRF写使能信号 0:禁止写入 1:允许写入 |
6 | WeDm | O | DM的写入信号 0:禁止写入 1:允许写入 |
7 | branch[3:0] | O | PC转移位置选择信号 4’b0001:其他情况 4’b0010:beq 4’b0100:j||jal 4’b1000:jr; |
8 | AluSrc1[3:0] | O | 参与ALU运算的第一个数 4’b0001:RD1 4’b0010:RD2 |
9 | AluSrc2[3:0] | O | 参与ALU运算的第二个数,来自GRF还是imm 4’b0001:RD2 4’b0010:imm32 4’b0100: offset |
10 | WhichtoReg[7:0] | O | 将何种数据写入GRF? 8’b0000_0001:ALU计算结果 8’b0000_0010:DM读出信号 8’b0000_0100:upperImm 8’b0000_1000: PC+4 |
11 | RegDst[3:0] | O | 写入GRF的哪个寄存器? 4’b0001:rd 4’b0010:rt 4’b0100:31号寄存器 |
12 | SignExt[2:0] | O | 拓展方式: 3’b001:0拓展 3’b010:符号拓展 3’b100:存储到高位 |
13 | B_change[3:0] | O | B转移的类型 4’b0001:beq 4’b0010:slt 4’b0100:blez |
14 | DM_type[5:0] | O | 存取指令类型: 6’b000001:lw/sw 6’b000010:lh/sh 6’b000100:lb/sb 6’b001000:lhu 6’b010000:lbu |
15 | MDop[3:0] | O | 乘除指令选择信号: 4‘b0000:无操作 4’b0001:mult 4’b0010:multu 4’b0011:div 4’b0100:divu 4’b0101:mfhi 4’b0110:mflo 4’b0111:mthi 4’b1000:mtlo |
16 | D_Tuse_rs[1:0] | O | 指令的rs寄存器的值从第一次进入D级到被使用的周期数 |
17 | D_Tuse_rt[1:0] | O | 指令的rt寄存器的值从第一次进入D级到被使用的周期数 |
18 | D_Tnew[1:0] | O | 指令从进入D开始到产生运算结果需要的周期数 |
4.NPC
NPC(下一指令计算单元)
该模块根据当前pc(包括D级和F级)和其他控制信号(NPCOp,CMP输出信息),计算出下一指令所在的地址npc,传入IFU模块。
- 端口定义
信号名 | 方向 | 位宽 | 描述 |
---|---|---|---|
F_pc | I | 32 | F级指令地址 |
D_pc | I | 32 | D级指令地址 |
offset | I | 32 | 地址偏移量,用于计算B类指令所要跳转的地址 |
imm26 | I | 26 | 当前指令数据的前26位(0~25),用于计算jal和j指令所要跳转的地址 |
ra | I | 32 | 储存在寄存器($ra或是jalr指令中存储“PC+4”的寄存器)中的地址数据,用于实现jr和jalr指令 |
ALU_change | I | 1 | B类指令判断结果 1:说明当前B类指令的判断结果为真 0:说明判断结果为假 |
branch | I | 4 | NPC功能选择 0x000:顺序执行 0x001:B类指令跳转 0x010: jal/j跳转 0x011: jr/jalr跳转 |
npc | O | 32 | 输出下一指令地址 |
PC8 | O | 32 | jr指令时存储PC+8的值 |
5.B_transfer(B类指令比较单元)
该单元根据输入的CMPOp信号对当前B指令的类型进行判断,进而对当前输入的数值进行相应比较,最后输出结果。
- 端口定义
信号名 | 方向 | 位宽 | 描述 |
---|---|---|---|
A | I | 32 | 输入B_transfer单元的第一个数据 |
B | I | 32 | 输入B_transfer单元的第二个数据 |
Type | I | 4 | Type功能选择信号 0x0001:beq判断 0x0010:slt判断 0x0100:blez判断 |
ALU_change | O | 1 | 判断结果输出 1: 判断结果为真 0:判断结果为假 |
<三>.E级流水线
1. ALU
(1) 端口说明
序号 | 信号名 | 方向 | 描述 |
---|---|---|---|
1 | A[31:0] | I | 参与运算的第一个数 |
2 | B[31:0] | I | 参与运算的第二个数 |
3 | ALUop[11:0] | I | 决定ALU做何种操作 12’b0000_0000_0001:无符号加 12’b0000_0000_0010:无符号减 12’b0000_0000_0100:与 12’b0000_0000_1000:或 12‘b0000_0001_0000:异或 12‘b0000_0010_0000:或非 12‘b0000_0100_0000:sll 12‘b0000_1000_0000:sra 12‘b0001_0000_0000:srl 12‘b0010_0000_0000:slt 12‘b0100_0000_0000:sltu |
4 | res | O | A与B做运算后的结果 |
(2) 功能定义
序号 | 功能 | 描述 |
---|---|---|
1 | 加运算 | res = A + B |
2 | 减运算 | res = A - B |
3 | 与运算 | res = A & B |
4 | 或运算 | res = A | B |
5 | 左移位运算 | Res=A<<B |
2.MD_Unit
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | mips.v中的clk |
I | reset | 1 | 同步复位信号 | mips.v中的reset |
I | en | 1 | MD_Unit使能信号 | 判断是否为乘除计算 |
I | MDop | 4 | 指令选择信号 | Controller |
I | A | 32 | 第一个计算数 | e_A |
I | B | 32 | 第二个计算数 | e_B |
O | HI | 32 | HI寄存器输出 | |
O | LO | 32 | LO寄存器输出 | |
O | out | 32 | 取出HI或LO的值 | |
O | busy | 1 | 是否在进行乘除计算 |
<四>.M级流水线
1. DM_In
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | low2 | 2 | 时钟信号 | Address低两位 |
I | WD | 31 | 处理前写入结果 | 32位写入数据 |
I | DM_type | 6 | 决定存取指令类型 | 存取指令类型: 6’b000001:lw/sw 6’b000010:lh/sh 6’b000100:lb/sb 6’b001000:lhu 6’b010000:lbu |
O | data_byteen | 4 | 字节使能信号 | |
O | DM_input | 32 | 处理后的写入结果 |
2.DM_Out
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | low2 | 2 | 时钟信号 | Address低两位 |
I | DM_output | 32 | 处理前读入信号 | 32位读入数据 |
I | DM_type | 6 | 决定存取指令类型 | 存取指令类型: 6’b000001:lw/sw 6’b000010:lh/sh 6’b000100:lb/sb 6’b001000:lhu 6’b010000:lbu |
O | RD | 32 | 处理后的读入结果 |
3.CP0
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | cpu主时钟 |
I | reset | 1 | 复位信号 | cpu同步复位信号 |
I | WE | 1 | 写入使能 | 前一级相同信号输入 |
I | Address | 5 | CP0 寄存器编号 | 前一级相同信号输入 |
I | WD | 32 | CP0 寄存器的写入数据 | 前一级相同信号输入 |
I | BD_in | 1 | 分支延迟槽指令标志 | 前一级相同信号输入 |
I | VPC | 32 | 中断/异常时的 PC | 前一级相同信号输入 |
I | ExcCode_in | 5 | 中断/异常的类型 | 前一级相同信号输入 |
I | HWInt | 6 | 6 个设备中断 | 前一级相同信号输入 |
I | EXLClr | 1 | 置 0 SR 的EXL 位 | m_eret控制信号输入 |
O | req | 1 | 异常/中断请求 | |
O | EPC | 32 | 处理后的读入结果 | |
O | D_out | 32 | CP0 寄存器的输出数据 |
Folding coding
1 |
|
<五>.各级流水线寄存器
1.IF_ID
D_Reg(IF/ID流水寄存器)
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | mips.v中的clk |
I | reset | 1 | 同步复位信号 | mips.v中的reset |
I | en | 1 | D级寄存器使能信号 | stall信号取反 |
I | clr | 1 | D级寄存器清空信号 | 是否清空延迟槽决定 |
I | req | 1 | D级寄存器异常/中断请求信号 | CP0 |
I | F_instr | 32 | F级instr输入 | IFU_instr |
I | F_pc | 32 | F级pc输入 | IFU_pc |
I | F_ExcCode | 5 | F级ExcCode输入 | 前一级相同信号 |
I | F_BD | 1 | F级BD输入 | 前一级相同信号 |
O | D_instr | 32 | D级instr输出 | |
O | D_pc | 32 | D级pc输出 | |
O | D_ExcCode | 5 | D级ExcCode输出 | |
O | D_BD | 1 | D级BD输出 |
2.ID_EX
E_Reg(ID/EX流水寄存器)
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | mips.v中的clk |
I | reset | 1 | 同步复位信号 | mips.v中的reset |
I | clr | 1 | E级寄存器清空信号 | HazardUnit中stall信号 |
I | req | 1 | ||
I | D_RD1 | 32 | D级GRF输出RD1 | 通过B_transfer_D1转发的数据 |
I | D_RD2 | 32 | D级GRF输出RD2 | 通过B_transfer_D2转发的数据 |
I | D_instr_s | 5 | D级instr的shamt | D_instr的s域数据 |
I | D_A1 | 5 | D级A1输入 | D_instr的rs域数据 |
I | D_A2 | 5 | D级A2输入 | D_instr的rt域数据 |
I | D_A3 | 5 | D级A3输入 | 通过MUX_A3选择出的数据 |
I | D_imm32 | 32 | D级imm32输入 | 通过EXT模块扩展出的数据 |
I | D_PC | 32 | D级PC输入 | 前一级相同信号 |
I | D_PC8 | 32 | D级PC8输入 | 前一级相同信号 |
I | Tnew_D | 2 | D级指令的Tnew输入 | 前一级相同信号 |
I | D_Wegrf | 1 | D级控制信号输入 | 前一级相同信号 |
I | D_WeDm | 1 | D级控制信号输入 | 前一级相同信号 |
I | D_ALUop | 7 | D级控制信号输入 | 前一级相同信号 |
I | D_AluSrc1 | 4 | D级控制信号输入 | 前一级相同信号 |
I | D_AluSrc2 | 4 | D级控制信号输入 | 前一级相同信号 |
I | D_WhichtoReg | 8 | D级控制信号输入 | 前一级相同信号 |
I | D_RegDst | 4 | D级控制信号输入 | 前一级相同信号 |
I | D_DM_type | 6 | D级控制信号输入 | 前一级相同信号 |
I | D_ALU_change | 1 | D级ALU_change输入 | 前一级相同信号 |
I | D_MDop | 4 | D级MDop输入 | 前一级相同信号 |
I | D_branch | 5 | D级branch输入 | 前一级相同信号 |
I | D_ExcCode | 5 | D级ExcCode输入 | 前一级相同信号 |
I | D_BD | 1 | D级BD输入 | 前一级相同信号 |
I | D_mfc0 | 1 | D级mfc0输入 | 前一级相同信号 |
I | D_mtc0 | 1 | D级mtc0输入 | 前一级相同信号 |
I | D_eret | 1 | D级eret输入 | 前一级相同信号 |
I | D_syscall | 1 | D级syscall输入 | 前一级相同信号 |
O | E_RD1 | 32 | E级RD1输出 | |
O | E_RD2 | 32 | E级RD2输出 | |
O | E_instr_s | 5 | 移位指令的位移数 | |
O | E_A1 | 5 | E级A1输出 | |
O | E_A2 | 5 | E级A2输出 | |
O | E_A3 | 5 | E级A3输出 | |
O | E_imm32 | 32 | E级imm32输出 | |
O | E_PC | 32 | E级PC输出 | |
O | E_PC8 | 32 | E级PC8输出 | |
O | E_Tnew | 2 | E级指令的Tnew输出 | |
O | E_Wegrf | 1 | E级控制信号输出 | |
O | E_WeDm | 1 | E级控制信号输出 | |
O | E_ALUop | 12 | E级控制信号输出 | |
O | E_AluSrc1 | 4 | E级控制信号输出 | |
O | E_AluSrc2 | 4 | E级控制信号输出 | |
O | E_WhichtoReg | 11 | E级控制信号输出 | |
O | E_RegDst | 3 | E级控制信号输出 | |
O | E_DM_type | 6 | E级控制信号输出 | |
O | E_ALU_change | 1 | E级ALU_change输出 | |
O | E_MDop | 4 | E级MDop输出 | |
O | E_branch | 5 | E级branch输出 | |
O | E_ExcCode | 5 | E级ExcCode输出 | |
O | E_BD | 1 | E级BD输出 | |
O | E_mfc0 | 1 | E级mfc0输出 | |
O | E_mtc0 | 1 | E级mtc0输出 | |
O | E_eret | 1 | E级eret输出 | |
O | E_syscall | 1 | E级syscall输出 |
-
运算功能
Tnew_E = (Tnew_D > 0) ? Tnew_D - 1: 0Tnew_E=(Tnew_D>0)?Tnew_D−1:0
3.EX_MEM
M_Reg(EX/MEM流水寄存器)
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | mips.v中的clk |
I | reset | 1 | 同步复位信号 | mips.v中的reset |
I | E_A2 | 5 | E级A2输入 | ALU_out数据 |
I | E_A3 | 5 | E级A3输入(转发值) | MUX_ALU选择出来的数据 |
I | E_RD2 | 32 | E级RD2输入 | 前一级相同信号 |
I | E_ALUout | 32 | E级res输入 | 前一级相同信号 |
I | E_PC | 32 | E级PC输入 | 前一级相同信号 |
I | E_PC8 | 32 | E级PC8输入 | 前一级相同信号 |
I | E_Tnew | 2 | E级Tnew输入 | 前一级相同信号 |
I | E_Wegrf | 1 | E级控制信号输入 | 前一级相同信号 |
I | E_WeDm | 1 | E级控制信号输入 | 前一级相同信号 |
I | E_WhichtoReg | 8 | E级控制信号输入 | 前一级相同信号 |
I | E_RegDst | 4 | E级控制信号输入 | 前一级相同信号 |
I | E_DM_type | 6 | E级控制信号输入 | 前一级相同信号 |
I | E_ALU_change | 1 | E级ALU_change输入 | 前一级相同信号 |
I | E_imm32 | 32 | E级imm32输入 | 前一级相同信号 |
I | E_branch | 5 | E级branch输入 | 前一级相同信号 |
I | E_ExcCode | 5 | E级ExcCode输入 | 前一级相同信号 |
I | E_BD | 1 | E级BD输入 | 前一级相同信号 |
I | E_mfc0 | 1 | E级mfc0输入 | 前一级相同信号 |
I | E_mtc0 | 1 | E级mtc0输入 | 前一级相同信号 |
I | E_eret | 1 | E级eret输入 | 前一级相同信号 |
I | E_syscall | 1 | E级syscall输入 | 前一级相同信号 |
O | M_A2 | 5 | M级A2输出 | |
O | M_A3 | 5 | M级A3输出 | |
O | M_RD2 | 32 | M级RD2输出 | |
O | M_ALUout | 32 | M级res输出 | |
O | M_PC | 32 | M级PC输出 | |
O | M_PC8 | 32 | M级PC8输出 | |
O | M_Tnew | 2 | M级Tnew输出 | |
O | M_Wegrf | 1 | M级Tnew输出 | |
O | M_WeDm | 1 | M级控制信号输出 | |
O | M_WhichtoReg | 8 | M级控制信号输出 | |
O | M_RegDst | 4 | M级控制信号输出 | |
O | M_DM_type | 6 | M级控制信号输出 | |
O | M_ALU_change | 1 | M级ALU_change输出 | |
O | M_imm32 | 32 | M级imm32输出 | |
O | M_branch | 5 | M级branch输出 | |
O | M_ExcCode | 5 | M级ExcCode输出 | |
O | M_BD | 1 | M级BD输出 | |
O | M_mfc0 | 1 | M级mfc0输出 | |
O | M_mtc0 | 1 | M级mtc0输出 | |
O | M_eret | 1 | M级eret输出 | |
O | M_syscall | 1 | M级syscall输出 |
-
运算功能
Tnew_M = (Tnew_E > 0) ? Tnew_E - 1: 0Tnew_M=(Tnew_E>0)?Tnew_E−1:0
4.MEM_WB
W_Reg(MEM/WB流水寄存器)
- 接口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | mips.v中的clk |
I | reset | 1 | 同步复位信号 | mips.v中的reset |
I | M_A3 | 5 | M级A3输入 | 前一级相同信号 |
I | M_RD | 32 | M级RD输入 | 前一级相同信号 |
I | M_PC | 32 | M级PC输入 | 前一级相同信号 |
I | M_PC8 | 32 | M级PC8输入 | 前一级相同信号 |
I | M_Wegrf | 1 | M级控制信号输入 | 前一级相同信号 |
I | M_WhichtoReg | 1 | M级控制信号输入 | 前一级相同信号 |
I | M_RegDst | 4 | M级控制信号输入 | 前一级相同信号 |
I | M_imm32 | 32 | M级imm32输入 | 前一级相同信号 |
I | M_ALU_change | 1 | M级ALU_change输入 | 前一级相同信号 |
I | M_Tnew | 2 | M级Tnew输入 | 前一级相同信号 |
O | W_A3 | 5 | W级A3输出 | |
O | W_ALUout | 32 | W级res输出 | |
O | W_RD | 32 | W级RD输出 | |
O | W_PC | 32 | W级PC输出 | |
O | W_PC8 | 32 | W级PC8输出 | |
O | W_Wegrf | 1 | W级控制信号输出 | |
O | W_WhichtoReg | 8 | W级控制信号输出 | |
O | W_RegDst | 4 | W级控制信号输出 | |
O | W_imm32 | 32 | W级imm32输出 | |
O | W_ALU_change | 1 | W级ALU_change输出 | |
O | W_Tnew | 2 | W级Tnew输出 |
<六>.暂停、转发处理及相关多路选择器
(一).冲突综合单元(HazardUnit)
方向 | 信号名 | 位宽 | 描述 |
---|---|---|---|
I | D_A1 | 5 | D级A1端输入 |
I | D_A2 | 5 | D级A2端输入 |
I | E_A1 | 5 | E级A1端输入 |
I | E_A2 | 5 | E级A2端输入 |
I | M_A2 | 5 | M级A2端输入 |
I | E_A3 | 5 | E级A3端输入 |
I | M_A3 | 5 | M级A3端输入 |
I | W_A3 | 5 | W级A3端输入 |
I | D_Tuse_rs | 2 | D_Tuse_rs输入 |
I | D_Tuse_rt | 2 | D_Tuse_rt输入 |
I | E_Tnew | 2 | E级Tnew输入 |
I | M_Tnew | 2 | M级Tnew输入 |
I | W_Tnew | 2 | W级Tnew输入 |
I | E_Wegrf | 1 | E级Wegrf输入 |
I | M_Wegrf | 1 | M级Wegrf输入 |
I | W_Wegrf | 1 | W级Wegrf输入 |
I | E_WhichtoReg | 8 | E级WhichtoReg输入 |
I | M_WhichtoReg | 8 | M级WhichtoReg输入 |
I | start | 1 | 乘除开始信号 |
I | busy | 1 | 乘除忙碌信号 |
I | MDU_en | 1 | D级将进行乘除运算信号 |
I | D_eret | 1 | D级eret输入 |
I | E_mtc0 | 1 | E级mtc0输入 |
I | M_mtc0 | 1 | M级mtc0输入 |
O | SelB_D1 | 2 | B_transfer的D1输入转发信号 |
O | SelB_D2 | 2 | B_transfer的D2输入转发信号 |
O | SelALU_A | 2 | ALU输入A转发信号 |
O | SelALU_B | 2 | ALU输入B转发信号 |
O | SelDM | 1 | DM写入WD转发信号 |
O | stall | 1 | 冲突信号 |
(二).控制和冒险简述
-
对于控制冒险,本实验要求大家实现比较过程前移至 D 级,并采用延迟槽。
-
对于数据冒险,两大策略及其应用:
1
假设当前我需要的数据,其实已经计算出来,只是还没有进入寄存器堆,那么我们可以用**转发**( Forwarding )来解决,即不引用寄存器堆的值,而是直接从后面的流水级的供给者把计算结果发送到前面流水级的需求者来引用。如果我们需要的数据还没有算出来。则我们就只能**暂停**( Stall ),让流水线停止工作,等到我们需要的数据计算完毕,再开始下面的工作。
(三).冒险处理
冒险处理我们均通过“A_T”法实现——
转发(forward)
当前面的指令要写寄存器但还未写入,而后面的指令需要用到没有被写入的值时,这时候会产生数据冒险,我们首先考虑进行转发。我们假设所有的数据冒险均可通过转发解决。也就是说,当某一指令前进到必须使用某一寄存器的值的流水阶段时,这个寄存器的值一定已经产生,并存储于后续某个流水线寄存器中。
在这一阶段,我们不管需要的值有没由计算出,都要进行转发,即暴力转发。为实现这一机制,我们要清楚哪些模块需要转发后的数据(需求者)和保存着写入值的流水寄存器(供应者)
- 供应者及其产生的数据
流水级 | 产生数据 | MUX名&选择信号名 | MUX输出名 |
---|---|---|---|
E | E_imm32, E_PC8 |
直接流水线传递 | 直接流水线传递 |
M | M_ALUout, M_PC8 |
直接流水线传递 | 直接流水线传递 |
W | w_res, w_RD, w_imm32, W_PC8 |
w_WhichtoReg | WD |
注:当M级指令为读hi和lo的指令时, M_AO中的结果是从上一周期在乘除槽中读取的hi或lo的值;如果是其他指令,M_AO是上一周期ALU的计算结果。
- 需求者及其产生的数据
接收端口 | 选择数据 | HMUX名&选择信号名 | MUX输出名 |
---|---|---|---|
B_transfer_D1 | D_V1, M_out, E_out |
SelB_D1 | d_b_transfer1 |
B_transfer_D2 | d_RD2, m_res, e_res |
SelB_D2 | d_b_transfer2 |
ALU_A | e_RD1, WD, m_res |
SelALU_A | e_A |
ALU_B | e_RD2, WD, m_res |
SelALU_B | e_B |
DM_WD | m_RD2, WD |
SelDM | M_WD_f |
NPC_ra | D_V1_f , E_PC8 , M_PC8 |
SelJr | ra |
从上表可以看出,W级中的数据没有转发到D级,原因是我们在GRF内实现了内部转发机制,将GRF输入端的数据(还未写入)及时反映到RD1或这RD2,判断条件为A3 == A2
或者A3 == A1
。
此时为了生成HMUX的选择信号,我们需要向HCU(冒险控制器)输入”A”数据,然后进行选择信号的计算,执行转发的条件为——
暂停(stall)
接下来,我们来处理通过转发不能处理的数据冒险。在这种情况下,新的数据还未来得及产生。我们只能暂停流水线,等待新的数据产生。为了方便处理,我们仅仅为D级的指令进行暂停处理。
我们把Tuse和Tnew作为暂停的判断依据——
- Tuse:指令进入 D 级后,其后的某个功能部件再经过多少时钟周期就必须要使用寄存器值。对于有两个操作数的指令,其每个操作数的 Tuse 值可能不等(如 store 型指令 rs、rt 的 Tuse 分别为 1 和 2 )。
- Tnew:位于 E 级及其后各级的指令,再经过多少周期就能够产生要写入寄存器的结果。在我们目前的 CPU 中,W 级的指令Tnew 恒为 0;对于同一条指令,Tnew@M = max(Tnew@E - 1, 0)、
在这一阶段,我们找到D级生成的Tuse_rs和Tuse_rt和在E,M,W级寄存器中流水的Tnew_D,Tnew_M,Tnew_W,如下表所示
- Tuse表和计算表达式
指令类型 | Tuse_rs | Tuse_rt |
---|---|---|
calc_R | 1 | 1 |
calc_I | 1 | X |
shift | X | 1 |
shiftv | 1 | 1 |
load | 1 | X |
store | 1 | 2 |
md | 1 | 1 |
mt | 1 | X |
mf | X | X |
branch | 0 | 0 |
j / jr | X | X |
jal / jalr | 0 | X |
lui | X | X |
- Tnew表和计算表达式
指令类型 | Tnew_D | Tnew_E | Tnew_M | Tnew_W |
---|---|---|---|---|
calc_R | 2 | 1 | 0 | 0 |
calc_I | 2 | 1 | 0 | 0 |
shift | 2 | 1 | 0 | 0 |
shiftv | 2 | 1 | 0 | 0 |
load | 3 | 2 | 1 | 0 |
store | X | X | X | X |
md | X | X | X | X |
mt | X | X | X | X |
mf | 2 | 1 | 0 | 0 |
branch | X | X | X | X |
jal / jalr | 0 | 0 | 0 | 0 |
j / jr | X | X | X | X |
lui | 1 | 0 | 0 | 0 |
然后我们Tnew和Tuse传入HCU(冒险控制器中),然后进行stall信号的计算。如果满足以下条件则stall有效——
-
Tnew > Tuse
-
前位点的读取寄存器地址和某转发输入来源的写入寄存器地址相等且不为 0
-
写使能信号有效
-
当E级延迟槽在进行运算(
start | busy
)时,D级为md、mt、mf指令 -
阻塞的构造(D级)
Folding coding
1 | assign stall_rs = (D_A1!= 0)&&((D_Tuse_rs<E_Tnew)&&(D_A1 == E_A3)&&E_Wegrf|| |
真值表
端口 | addu | subu | ori | lw | sw | lui | beq |
---|---|---|---|---|---|---|---|
op | 000000 | 000000 | 001101 | 100011 | 101011 | 001111 | 000100 |
func | 100001 | 100011 | |||||
AluOp | 0000001 | 0000010 | 0001000 | 0000000 | 0000000 | 0000000 | 0000000 |
WeGrf | 1 | 1 | 1 | 1 | 0 | 1 | 0 |
WeDm | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
branch | 0001 | 0001 | 0001 | 0001 | 0001 | 0001 | 0010 |
AluSrc1 | 0001 | 0001 | 0001 | 0001 | 0001 | 0001 | 0001 |
AluSrc2 | 0001 | 0001 | 0010 | 0010 | 0010 | 0001 | 0001 |
WhichtoReg | 0001 | 0001 | 0001 | 0010 | 0001 | 0100 | 0001 |
RegDst | 0001 | 0001 | 0010 | 0010 | 0010 | 0010 | 1010 |
SignExt | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
端口 | andi | jal | j | jr | sll | add | sub |
op | 001100 | 000011 | 000010 | 000000 | 000000 | 000000 | 000000 |
func | 001000 | 000000 | 100000 | 100010 | |||
AluOp | 0000100 | 0000000 | 0000000 | 0000000 | 0010000 | 0000000 | 0000001 |
WeGrf | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
WeDm | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
branch | 0001 | 0100 | 0100 | 1000 | 0001 | 0001 | 0001 |
AluSrc1 | 0001 | 0001 | 0001 | 0001 | 0010 | 0001 | 0001 |
AluSrc2 | 0010 | 0001 | 0001 | 0001 | 0100 | 0001 | 0001 |
WhichtoReg | 0001 | 1000 | 0001 | 0001 | 0001 | 0001 | 0001 |
RegDst | 0010 | 0100 | 0001 | 0001 | 0001 | 0001 | 0001 |
SignExt | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
桥和计数器
1.Bridge
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | Address_in | 32 | 写入/读取的外设单元的地址 | 前一级相同信号 |
I | WD_in | 32 | 写入外设单元的数据 | 前一级相同信号 |
I | byteen | 4 | 写入外设单元的使能 | 前一级相同信号 |
I | DM_RD | 32 | DM读取值的输入 | 前一级相同信号 |
I | T0_RD | 32 | Timer1读取值的输入 | 前一级相同信号 |
I | T1_RD | 32 | Timer0读取值的输入 | 前一级相同信号 |
O | DM_WE | 4 | DM写入使能 | |
O | T0_WE | 1 | Timer1写入使能 | |
O | T1_WE | 1 | Timer2写入使能 | |
O | Address_out | 32 | 写入/读取的外设单元的地址 | |
O | WD_out | 32 | 写入外设单元的数据 | |
O | RD_out | 32 | 外设单元的读取值输出 |
Folding coding
1 |
|
2.TC
- 端口定义
方向 | 信号名 | 位宽 | 描述 | 输入来源 |
---|---|---|---|---|
I | clk | 1 | 时钟信号 | 前一级相同信号 |
I | reset | 1 | 同步复位信号 | 前一级相同信号 |
I | Addr | 30 | Timer写入地址 | 前一级相同信号 |
I | WE | 1 | Timer写入使能 | 前一级相同信号 |
I | Din | 32 | Timer写入数据 | 前一级相同信号 |
O | D_out | 32 | Timer读取数据 | |
O | IRQ | 1 | 中断请求 |
Folding coding
1 |
|
重要机制实现方法
CP0响应机制
CP0是检测异常和中断的重要部件,异常和中断通过以下接口传入——
- 中断信息通过 HWInt[7:2] 接口进入,其中HWInt[2]连接Timer0的终端请求,HWInt[3]连接Timer1的终端请求,HWInt[4]连接外部的中断请求。
- 异常信息通过ExcCode_in和BD_in两个接口传入,ExcCode_in传入的是异常代码(ADEL,ADES,RI,OV),BD_in传入的是延迟槽指令标志(有效则表示当前指令为延迟槽指令)。
检测到中断或者异常时,CP0会进行判断并响应,决定是否将req(CP0向CPU发出的中断请求)置1。判断逻辑如下——
1 | wire inter_req = (|(HWInt & IM)) & IE & (!EXL);//中断有效判断 |
当req有效,CP0还需要完成以下任务——
-
EXL置1
-
将M级PC存入EPC(延迟槽指令中存入EPC-4)
-
如果当前响应中断,ExcCode寄存器写入0;若响应异常,ExcCode寄存器写入外部传入的异常代码ExcCode_in
-
BD寄存器写入外部传入的BD_in信号
-
此外,无论是否发出中断请求,在每一周期均需要将HWInt[6:2]写入Cause寄存器中的的IP域
系统桥设计
系统桥其实是充当一个“交换机”的角色,将CPU传来的地址写入相应的外设(DM、Timer0、Timer1),只需要组合逻辑便可实现。
异常识别
P7中我们考虑的异常情况有以下几种——
- F级异常:
- PC地址没有字对齐(AdEL)
- PC地址超过0x3000 ~ 0x6ffc(AdEL)
- D级异常:
- 未知的指令码(RI)
- E级异常:
- addi、add、sub计算溢出(Ov)
- load类指令计算地址时加法溢出(AdEL)
- store类指令计算地址时加法溢出(AdES)
- M级异常:
- lw取数地址未 4 字节对齐(AdEL)
- lh、lhu取数地址未与 2 字节对齐(AdEL)
- lh、lhu、lb、lbu取 Timer 寄存器的值(AdEL)
- load型指令取数地址超出 DM、Timer0、Timer1 的范围(AdEL)
- sw存数地址未 4 字节对齐(AdES)
- sh存数地址未 2 字节对齐(AdES)
- sh、sb存 Timer 寄存器的值(AdES)
- sw向计时器的 Count 寄存器存值(AdES)
- store型指令存数地址超出 DM、Timer0、Timer1 的范围(AdES)
针对以上列出的异常情况,我们在每一个流水级对异常进行检测。由于教程提出了以下要求——
- 发生取指异常后视为
nop
直至提交到 CP0。 - 发生
RI
异常后视为nop
直至提交到 CP0。 load
与store
类算址溢出按照AdEL
与AdES
处理。
因此不会出现一个指令在多级出现异常的情况。如果某个流水级出现了新的异常,我们将这个异常流水到下一级即可,而不是流水上一级传来的异常;如果这个流水级没有出现新的异常,则将上一级传来的异常继续流水给下一级即可。
- F级异常:
二、 测试方案
(1) 测试代码:
注:本测试方案覆盖所有异常,并未测试中断
Folding coding
1 | .text |
(2) 该CPU运行结果
Folding coding
1 | 42@00003004: $ 1 <= 0000fffc |
三、 思考题
(一)请查阅相关资料,说明鼠标和键盘的输入信号是如何被 CPU 知晓的?
鼠标和键盘等外设并不是直接与CPU相连的,中间需要通过软件来连接,这个软件也就是我们熟知的驱动。驱动和硬件之间通过操作系统进行处理。
(二)请思考为什么我们的 CPU 处理中断异常必须是已经指定好的地址?如果你的 CPU 支持用户自定义入口地址,即处理中断异常的程序由用户提供,其还能提供我们所希望的功能吗?如果可以,请说明这样可能会出现什么问题?否则举例说明。(假设用户提供的中断处理程序合法)
CPU处理中断异常使用指定好的地址,有利于操作系统对中断处理程序的设计,更有利于异常中断的处理。如果CPU支持用户自定义入口地址,只要定义与其他外设设备的DM空间不冲突,理论上也是可以的,但如果发生地址冲突,则可能因为地址内容覆盖而造成无法挽回的损失。
(三)为何与外设通信需要 Bridge?
因为实际生活中I/O设备相对于CPU是外置的,而系统桥正是模拟了各种I/O接口,通过Bridge来实现CPU和各种外设之间的数据交互。
(四) 请阅读官方提供的定时器源代码,阐述两种中断模式的异同,并针对每一种模式绘制状态移图
在模式0下:当计数器倒计数为 0 后,计数器停止计数,此时控制寄存器中的使能 Enable 自动变为 0。当使能 Enable 被设置为 1 后,初值寄存器值再次被加载至计数器, 计数器重新启动倒计数。 模式 0 通常用于产生定时中断。例如,为操作系统的时间片调度机制提供定 时。模式 0 下的中断信号将持续有效,直至控制寄存器中的中断屏蔽位被设置为 0。
在模式1下:当计数器倒计数为 0 后,初值寄存器值被自动加载至计数器,计数器继续倒 计数。 模式 1 通常用于产生周期性脉冲。例如,可以用模式 1 产生步进电机所需的 步进控制信号。不同于模式 0,模式 1 下计数器每次计数循环中只产生一周期的中断信号。
模式0计时结束后,一直保持中断,直到en或IM被修改,模式1计时结束后,中断一个周期,再重新计数。可以理解为中断保持的逻辑不同。
(五)倘若中断信号流入的时候,在检测宏观 PC 的一级如果是一条空泡(你的 CPU 该级所有信息均为空)指令,此时会发生什么问题?在此例基础上请思考:在 P7 中,清空流水线产生的空泡指令应该保留原指令的哪些信息?
在阻塞时我们会往流水线中插入 nop,这个 nop 的 pc
和 bd
信号都是 0
。此时宏观 PC 会显示错误的值。并且如果此时发生了中断,就会导致 EPC 存入错误的值。
所以在P7中,清空流水线产生的空泡指令应该保留原址令的 PC
和 bd
。
(六)为什么 jalr 指令为什么不能写成 jalr $31, $31?
如果写成jalr $31 $31的话,相当于PC<=GPR[$31],GPR[$31]<=PC+4,按照流水线的执行顺序会先将PC+4进行Grf中内部转发,所以相当于进行了一次PC<=PC+4,而没有起到跳转到GPR[$31]的效果。
选做题
(一)请详细描述你的测试方案及测试数据构造策略。
由于p7的实现方式复杂性,所以通过自动化生成数据的效果并不好,所以我只能通过手动构造异常数据和各种支持不同位置中断的tb来实现,并使用数据对拍。
数据构造策略就是分别实现不同的数据之间组合模拟各种常见的异常,以及在中断发生在跳转指令、延迟槽、阻塞指令等不同情况,从而实现测试方案。
之后后续借助于我自己的自动化测试工具和课程组的Mars,我实现了自行测试。
四.自动化测试
命令行自动测试
具体源码可见:Charles_BUAA_CO_2022/p7/statistic at main · Charles2530/Charles_BUAA_CO_2022 (github.com)
Folding coding
1 | import os |
Folding coding
1 | import random |
Folding coding
1 | # from filediff.diff import file_diff_compare |
Folding coding
1 |
|
反编译工具
1 | # -*- coding: utf-8 -*- |
- Title: P7 Design documents
- Author: Charles
- Created at : 2022-12-26 21:12:09
- Updated at : 2023-11-05 21:36:02
- Link: https://charles2530.github.io/2022/12/26/p7-design-documents/
- License: This work is licensed under CC BY-NC-SA 4.0.