P5 设计文档
5 级流水线 CPU ## 支持指令
add, sub, ori, lw, sw, beq, lui, j, jr, jal, nop
(其中 add,sub 为无符号加减法)
Instuction | |
---|---|
add | GPR[rd] = GPR[rs] + GPR[rt] |
sub | GPR[rd] = GPR[rs] - GPR[rt] |
ori | GPR[rt] = GPR[rs] | ZeroExt(Imm) |
lui | GPR[rt] = {imm, 16’b0} |
lw | R[rt] = Mem[GPR[rs]+sign_ext(offset)] |
sw | Mem[GPR[rs]+sign_ext(offset)] = R[rt] |
beq | if (GPR[rs] == GPR[rt]) PC = PC + 4 + BranchAddr |
j | PC = JumpAddr |
jal | PC = JumpAddr; GPR[31] = PC + 4 |
jr | PC = GPR[rs] |
五个阶段
阶段 | 简称 | 功能概述 |
---|---|---|
取指阶段 (Fetch) | F | 从指令存储器中读取指令 |
译码阶段 (Decode) | D | 从寄存器文件中读取源操作数并对指令译码以便得到控制信号 |
执行阶段 (Execute) | E | 使用 ALU 执行计算 |
存储阶段 (Memory) | M | 读或写数据存储器 |
写回阶段 (Writeback) | W | 将结果写回到寄存器文件 |
关键数据通路
流程模块设计
CU 模块设计
- 相较 P4,省去 RegWrite 信号,直接译出当前指令需要写入的地址,如不需写入,默认写至 0,在写入 GRF 时直接略去
- 直接译出当前指令 rs, rt, rd, shamt, imm16, imm26 以及所有控制信号供每个阶段选取使用,还需译出 Tuse_rs/rt 以及 E_Tnew 与 M_Tnew,各级输出对应信号至 Conflict 模块
- 将指令分类,分为:cal_r, cal_i, load, save, branch, shift, jreg, jadd, jlink(ori 被归为 cal_i)
Port name | Direction | Type | Description |
---|---|---|---|
Ins | input | [31:0] | 当级指令 |
branchTrue | input | 分支控制信号 | |
控制信号 | |||
GRF_WA | output | [4:0] | 写入的地址 |
GRF_WDSrc | output | [2:0] | 写入数据选择 |
ALUSrc | output | ALU_B 的数据源选择 | |
ALUSelect | output | [2:0] | ALU 运算类型选择 |
MemWrite | output | 内存写入控制 | |
EXTSelect | output | EXT 位拓展类型选择 | |
BranchSelect | output | [2:0] | branch 判断类型选择 |
NPCSelect | output | [2:0] | NPC 类型选择 |
ByteLW | output | 字节写入读出控制 | |
指令译码 | |||
opcode | output | [5:0] | |
funct | output | ||
rs | output | [4:0] | |
rt | output | ||
rd | output | ||
shamt | output | [4:0] | |
imm16 | output | [15:0] | |
imm26 | output | [25:0] | |
T 计算 | |||
Tuse_rs | output | [1:0] | |
Tuse_rt | output | [1:0] | |
E_Tnew | output | [1:0] | |
M_Tnew | output | [1:0] |
T 计算表格
Ins | Tuse_rs | Tuse_rt | E_Tnew | M_Tnew | W_Tnew | |
---|---|---|---|---|---|---|
cal_r | add||sub | 1 | 1 | 1 | 0 | 0 |
cal_i | ori||lui | 1 | 1 | 0 | 0 | |
load | lw | 2 | 2 | 1 | 0 | |
save | sw | 1 | 2 | |||
branch | beq | 0 | 0 | |||
jreg | jr | 0 | ||||
jadd | j||jal | |||||
jlink | jal | 0 | 0 | 0 |
Conflit 模块设计:AT 控制阻塞,直接转发
阻塞
- D 级判断将要使用的寄存器数据是否能得到转发更新,即后续写入相同寄存器的 Tnew 是否有大于 Tuse 的,如果有则需要阻塞,以在后续能得到转发更新。特判 0 号寄存器不需要阻塞,能够直接获得数据 0
- 需要得到 D 级指令 rs, rt 的 Tuse,以及后续 E, M 级指令的 Tnew,在各级 CU 中计算,发送至冲突单元(W 级 Tnew 全是 0 不需要考虑,都可以内部转发解决)
- 阻塞时需要暂停更新 PC 以及 F 级读出的指令,并且清空 D 级当前指令的译码输出,以替换为 nop 空泡
转发
阻塞后,所有指令在需要读寄存器数据的时候都能够获得后续计算完毕的数据,每级转发出已算出的数据,发送给之前各级即可。
需要读寄存器:D 级 GRF,Branch 计算需要 rs, rt 数据;E 级 ALU 需要 rs,rt 数据;M 级 DM 写入数据口需要 rt 数据
需要写寄存器:E 级可转发出 D 级算的 PC+8;M 级可转发出 D 级算的 PC+8 和 E 级算的的 ALU_Y;W 级可转发出 D 级算的 PC+8,E 级算的的 ALU_Y 和 M 级读出的 DM 数据。 根据当前指令 CU 译码得到的 GRF_WDSrc 进行选择 。此外还有 W 级寄存器写入,可直接内部转发至 D 级读出
- 在主模块中,获取各级需要读的寄存器编号(D_rs,D_rt,E_rs,E_rt,M_rt),寄存器原读数(D_rs_data,D_rt_data,E_rs_data,E_rt_data,M_rt_data),写入的寄存器编号(E_GRF_WA,M_GRF_WA,W_GRF_WA)和数据(E_GRF_WD,M_GRF_WD,W_GRF_WD)
- 比较读的编号和写的编号是否有相等的,如有相等的则代表有数据已经更新需要转发,转发优先级为更新次序,最后一次更新优先转发,即优先转发距离需要数据的阶段近的数据,特判如果需要读 0 号寄存器的数据,直接转发 0
- 转发的数据(D_rs_fw,D_rt_fw,E_rs_fw,E_rt_fw,M_rt_fw)发送至各级需要的部分运算,并传递给下一级
五级模块设
每个阶段之间以寄存器隔开,寄存器设计在每个模块输出处,使用 reg 类型
每个阶段之间需要流水传递 Ins,PC,传给各级 CU 以译码出当前阶段的 rs,rt 以及需要写入的地址和写入数据的选择
部分阶段前后间需要传递需要使用的 NPC, EXTout, ALU_Y, DM_RD
1. Fetch
- 包含 F_IFU,FDReg
- Fetch
Port name | Direction | Type | Description |
---|---|---|---|
clk | input | ||
reset | input | ||
F_Flush | input | 清空延迟槽信号 | |
F_Stall | input | 阻塞更新 PC | |
NPC | input | [31:0] | D 级 NPC 计算出的 NPC 传入 |
F_PC | output | [31:0] | F 级直接传给 NPC 的 F 级 PC,以直接计算 F_PC+4 |
F_Ins | [31:0] | 指令流水 | |
FD 寄存器 | |||
D_Stall | input | 阻塞更新 FD 间寄存器 | |
D_PC | output | reg [31:0] | <=F_PC |
D_Ins | output | reg [31:0] | <=F_Ins |
- F_IFU
Port name | Direction | Type | Description |
---|---|---|---|
clk | input | ||
reset | input | ||
F_Stall | input | ||
NPC | input | [31:0] | |
PC | output | [31:0] | |
Ins | output | [31:0] |
2. Decode
- 包括 D_CU, EXT, NPC (Branch), DEReg
Port name | Direction | Type | Description |
---|---|---|---|
clk | input | ||
reset | input | ||
D_PC | input | [31:0] | PC 流水 |
D_Ins | input | [31:0] | 指令流水 |
Conflict/Forward | |||
Tuse_rs | output | [1:0] | AT 算阻塞 |
Tuse_rt | output | [1:0] | |
D_rs | output | [4:0] | D 级指令读寄存器的编号 |
D_rt | output | [4:0] | |
D_rs_data | output | [31:0] | D 级指令读寄存器原数据 |
D_rt_data | output | [31:0] | |
D_rs_fw | input | [31:0] | D 级转发后寄存器数据 |
D_rt_fw | input | [31:0] | |
EXT | |||
imm16 | [15:0] | EXT 输入 | |
EXTSelect | EXT 功能选择 | ||
D_EXT_out | [31:0] | EXT 输出 | |
NPC | |||
NPCSelect | [2:0] | 下一指令地址选择 | |
branchTrue | 是否分支信号 | ||
F_PC | input | [31:0] | 算 NPC 用 |
NPC | output | [31:0] | 传给 F 级 IFU |
DEReg | |||
E_Flush | input | 阻塞清空 DE 寄存器 | |
E_PC | output | reg [31:0] | <=D_PC |
E_Ins | output | reg [31:0] | <=D_Ins |
E_rs_data | output | reg [31:0] | <=D_rs_fw |
E_rt_data | output | reg [31:0] | <=D_rt_fw |
E_EXT_out | output | reg [31:0] | <=D_EXT_out |
3. Execute
- 包括 E_CU, ALU, EMReg
Port name | Direction | Type | Description |
---|---|---|---|
clk | input | ||
reset | input | ||
E_PC | input | [31:0] | PC 流水 |
E_Ins | input | [31:0] | 指令流水 |
Conflict/Forward | |||
E_Tnew | output | [1:0] | AT 算阻塞 |
E_rs | output | [4:0] | E 级指令读寄存器的编号 |
E_rt | output | [4:0] | |
E_rs_data | output | [31:0] | E 级指令读寄存器原数据 |
E_rt_data | output | [31:0] | |
E_GRF_WA | output | [4:0] | E 级指令写寄存器的编号 |
E_rs_fw | input | [31:0] | E 级接收转发后寄存器数据 |
E_rt_fw | input | [31:0] | |
GRF_WDSrc | [2:0] | E 级指令写寄存器的数据选择 | |
E_GRF_WD | output | [31:0] | E 级指令写寄存器的数据 |
ALU | |||
E_EXT_out | input | [31:0] | |
ALUSrc | ALU_B 数据源选择 | ||
ALUSelect | [2:0] | ALU 功能选择 | |
E_ALU_A | [31:0] | =E_rs_fw:ALU_A 口数据 | |
E_ALU_B | [31:0] | =E_rt_fw/E_EXT_out:ALU_B 口数据 | |
EMReg | |||
M_PC | output | reg [31:0] | <=E_PC |
M_Ins | output | reg [31:0] | <=E_Ins |
M_ALU_Y | output | reg [31:0] | <=E_ALU_Y |
M_rt_data | output | reg [31:0] | <=E_rt_fw |
- E_ALU
Port name | Direction | Type | Description |
---|---|---|---|
op | input | [2:0] | |
A | input | [31:0] | |
B | input | [31:0] | |
Y | output | [31:0] | |
Greater | output | ||
Equal | output | ||
Less | output |
4. Memory
包括 M_CU, DM
Port name | Direction | Type | Description |
---|---|---|---|
clk | input | ||
reset | input | ||
M_PC | input | [31:0] | |
M_Ins | input | [31:0] | |
Conflict/Forward | |||
M_Tnew | output | [1:0] | AT 算阻塞 |
M_rt | output | [4:0] | M 级指令读寄存器编号 |
M_rt_data | output | [31:0] | M 级指令读寄存器数据 |
M_rt_fw | input | [31:0] | M 级接收转发后寄存器数据 |
M_GRF_WA | output | [4:0] | M 级指令写寄存器编号 |
GRF_WDSrc | [2:0] | M 级指令写寄存器数据选择 | |
M_ALU_Y | input | [31:0] | 待转发的 E 级 ALU 计算结果 |
M_GRF_WD | output | [31:0] | M 级指令写寄存器数据 |
DM | |||
MemWrite | DM 写使能 | ||
DM_A | [31:0] | =M_ALU_Y:DM 读写地址 | |
DM_WD | [31:0] | =M_rt_fw:DM 写入数据 | |
DM_RD | [31:0] | DM 读出数据 | |
MWReg | |||
W_PC | output | reg [31:0] | <=M_PC |
W_Ins | output | reg [31:0] | <=M_Ins |
W_ALU_Y | output | reg [31:0] | <=M_ALU_Y |
W_DM_RD | output | reg [31:0] | <=DM_RD |
- M_DM
Port name | Direction | Type | Description |
---|---|---|---|
reset | input | ||
clk | input | ||
WE | input | ||
ByteLW | input | ||
A | input | [31:0] | |
WD | input | [31:0] | |
RD | output | [31:0] | |
PC | input | [31:0] |
5. Writeback
- 包括 W_CU
Port name | Direction | Type | Description |
---|---|---|---|
clk | input | ||
reset | input | ||
W_Ins | input | [31:0] | |
W_PC | input | [31:0] | |
Conflict/Forward | |||
W_GRF_WA | output | [4:0] | W 级指令写寄存器编号 |
W_ALU_Y | input | [31:0] | 待转发的 E 级 ALU 计算结果 |
W_DM_RD | input | [31:0] | 待转发的 M 级 DM 读出数据 |
GRF_WDSrc | [2:0] | W 级指令写寄存器数据选择 | |
W_GRF_WD | output | [31:0] | W 级指令写寄存器数据 |
测试
- 枚举各指令排列和之间距离
1 | .text |
- 对 0 号寄存器读写测试
注意事项
- 需要寄存器数据进行运算或是向后传递的端口需要的是接收转发后正确的数据
- GRF 不能实例化两次,数据不相通
- 读出 0 号寄存器时直接转发 5’b00000。此时 rs, rt 的 Tnew 均为 0
- 注意数据是否是直接发送,或是需要等待寄存器一个周期
思考题
- 提前分支判断也代表提前需要 rs, rt 寄存器数据,如之前为 lw 指令,则可能还需阻塞一周期。指令序列:
1 | lw $t0,0($t2) |
- 因为延迟槽在处理的时候会将跳转前的必定执行的指令放在跳转指令后一句,此时 PC+4 处为延迟槽指令,跳转指令下一条实际距离跳转指令 8 字节,返回地址需保存 PC+8
- 直接从功能部件转发会导致转发与接收转发两个阶段数据通路相连,导致指令指令最短周期必须大幅增加以保证数据可以在两阶段中完整流动,降低整体执行效率
- 如没有转发,W 级写入数据在上升沿才能开始写入,而读出则在上升沿瞬间已经执行,寄存器堆中数据还没有更新。实现方法为判断写入数据寄存器编号是否和读出寄存器编号相等,如相等则直接读出即将写入的数据。当寄存器为 0 时,直接转发 0
- 需求者:D 级 branch 判断处以及 NPC 跳转地址处,E 级 ALU 计算处,M 级 DM 写入数据处
- 供给者:DEReg 处写给 $31 的 PC+8,EMReg 处 ALU 计算结果,MW 处 DM 读出数据
- branch 指令需要拓展输入数据来源以及 branch 类型,并且可能需要把是否跳转的信号传值各级 CU 控制是否读写和 PC 来源选择
- GRF 写入地址可能是寄存器读出的数据(?),可在 Writeback 级内部转发读出结果至写入地址
- 延迟槽可能需要清空,需要增加对 FD 寄存器的清空信号
- 译码器为一个输出包含输入指令的所有控制信号,指令分解部分,写入寄存器编号,读出寄存器编号,Tuse_rs,Tuse_rt,E_Tnew 和 M_Tnew。通过输入的指令全部译码,各流水级分别实例化,获取所需的控制信号与数据