要求
- 全力转发!
- 支持指令集:{add,sub,ori,lui,lw,sw,beq,jr,jal,nop}
需要重构的地方
译码方式
选择集中式译码 & 控制信号驱动型译码
e.g.
1 | //宏定义 |
Splitter
要取代的:
- ControlSplitter
- InstrSplitter
- 核心思想:用宏定义代替splitter
- 各种控制信号在D级生成后打包成controlbus向后传
- instruction和controlbus都直接完整向后传,如果要用再利用宏定义在对应阶段取出即可
宏
专门建立一个define.v,在里面进行宏定义
1 | // constants.v |
这样可以保证不重复宏定义
命名方式
E_xx:来自E Register的 xx 值/在E stage产生的 xx 值
添加BranchControl模块
把原alu输出equal等跳转判断信号和原npc使用equal等信号判断是否跳转的功能集成到BranchControl模块中。
- 实现分支判断提前到ID
拆分P4中的grfSel模块
grfSel = WASel + WDSel
- WASel:放在ID中实现,然后一路传递
- WDSel:WB中实现
GRF内部转发
-
当GRF同时在WB被写入,在ID被读出,且存在读写寄存器冲突,读出的寄存器值应为新写入的值
1
2assign RD1 = (RA1 != 0 && RA1 == WA && RegWrite) ? WD : register[RA1];
assign RD2 = (RA2 != 0 && RA2 == WA && RegWrite) ? WD : register[RA2];
前置知识
延迟槽
时序相关

- 规定:
CCn的上升沿指的是CCn开始的上升沿 - 初始状态:第0个周期,实际上已经预加载了第一条指令的IF阶段。也就是说,
CC1上升沿第一条指令实际上是进入ID阶段。
冒险
数据冒险
-
转发/旁路:
- 概念:将尚
未写入RF但已经暂存在流水线寄存器中的计算结果传递给相关功能部件(或流水线寄存器)的技术 - 基本要点:
- 前递:将M(后级)保存的ALUresult向EX stage(前级)的ALU传递
- 选择:
- 有依赖才转发
- 不依赖不转发,就直接用grf读出的数据参与计算
- 转发的数据一定是已经锁存过的数据,而不能从正在计算的组合逻辑里拉线!!!
- 概念:将尚
-
完备性
转发到/数据来源 M锁存的ALUresult W_WD M_PC8 E_PC8 ID stage RtData/RsData RtData/RsData RtData/RsData RtData/RsData EX stage RtData/RsData RtData/RsData RtData/RsData 不存在 MEM stage 不存在 RtData 不存在 不存在 例如:
- lw - R:
- lw处于ME时插入空拍,R指令仍处于ID stage,PC冻结;
- lw处于WB时,R型指令进入EX stage,将W_reg暂存的计算结果转发到ALU输入端(
代替operand1/operand2)
- lw - xxx - R:
- R型指令处于ID stage时,将W_reg暂存的计算结果转发进E_reg(
代替RsData/RtData)
- R型指令处于ID stage时,将W_reg暂存的计算结果转发进E_reg(
- lw - R:
-
阻塞的实现
- 冻结PC
- 冻结D_reg
- 清零E_reg (本质上是向EX stage插入nop指令)
控制冒险
- 提前分支判断:在D级就完成分支判断(已经用D级 BranchControl模块实现)
- 延迟槽:即使判断跳转,也至少让紧接着的那条指令执行(不用舍弃IF已经取到的指令)
加指令思路
分析指令RTL
严格按照指令说明来!!!
- branch类
- 修改
BranchControl对ifLink、D_flush和BranchEn的输出逻辑 - 清空延迟槽(
NullifyCurrentInstruction()) =D_flush(清空延迟槽)
- 是否为条件写寄存器?
改造类似
条件link:ControlBus添加isConditionalWrite,在能确定是否写入的阶段设置ifConditionalWrite并随流水线向后传递,参与TrueRegWrite的选择实现。- 涉及访存的条件写:在MEM阶段才能确定是否要写入(M_TrueRegWrite\W_TrueRegWrite)
- 不涉及访存的条件写:在ID阶段就可以确定是否要写入
- 是否为条件选择写入寄存器数据?
- 不涉及到访存:增加ALU逻辑即可,在MEM阶段确定写入的数据
- 涉及到访存:增加ALU逻辑 & 增加WDsel逻辑,在WB阶段确定写入的数据(本质:lw)
易错点
- STALL时不能清空延迟槽(因为此时D级还没有取到正确的data,分支判断无法进行,输出的D_flush不对)
- STALL > D_flush
计算用组合逻辑积累
-
rtData是否为rsData相反数?
wire isNegPair = (rsData + rtData == 0) && (rsData != 32'h8000_0000);
-
取部分信号
data[i*4 +:4]等价于data[i*4+3:i*4] -
对比有无符号
- 默认无符号
$signed()
-
判断奇偶 wire isOdd = rsData[0]; wire isEven = ~rsData[0];
-
判断是否是2^n
只有一个 bit 是 1 -
是否为 rs 的绝对值比 rt 大
1 | wire absgt = |
模块设计
顶层设计

数据通路
-
五个转发去处(D_NewRsData/D_NewRtData/E_NewRsData/E_NewRtData/M_NewRtData):
-
本质上是两种转发来源
- 来自M级:ALUresult、PC+4
- 来自W级:WD
-
以D_NewRsData为例:

IF
IFU
- 模块规格
信号名 方向 位宽 描述 reset I 1 同步复位信号 clk I 1 时钟信号 NPC I 32 pc更新值 stall I 32 冻住 instruction O 32 指令机器码 PC O 32 当前PC
D
Reg_D
- 模块规格
信号名 方向 位宽 描述 clk I 1 clock reset I 1 同步复位信号 F_instruction I 32 F_instruction F_PC I 32 F_PC stall I 1 冻住 D_flush I 1 是否清空延迟槽 E_instruction O 32 E_instruction E_PC O 32 E_PC
ID
Control
-
控制信号表;
控制信号\指令 add sub ori lw sw beq lui jal jr RegWrite 1 1 1 1 0 0 1 1 0 aluControlSel 000 000 001 001 001 000 001 000 000 ALUop 0000 0001 0011 0000 0000 0000 0100 0000 0000 MemWrite 0 0 0 0 1 0 0 0 0 MemRead 0 0 0 1 0 0 0 0 0 EXTop 0 0 0 1 1 1 0 0 0 RegDst 000 000 001 001 000 000 001 011 000 WDsel 00 00 00 01 00 00 00 10 00 NPCop 000 000 000 000 000 001 000 010 011 MemWriteDataOp 00 00 00 00 00 00 00 00 00 ifB 0 0 0 0 0 1 0 0 0 ifJ 0 0 0 0 0 0 0 1 1 BranchOp 000 000 000 000 000 000 000 000 000 useRt 1 1 0 0 1 1 0 0 0 islink 0 0 0 0 0 0 0 1 0 isConditionalLink 0 0 0 0 0 0 0 0 0 isLikely 0 0 0 0 0 0 0 0 0 -
T模型时间表
-
:这条指令位于 D 级的时候,再经过多少个时钟周期就必须要使用相应的数据 -
:位于某个流水级的某个指令,它经过 个时钟周期,该指令的结果(即最终要写回 GRF 的值)才会被计算出来,并存入对应的流水级寄存器中,从而可以被后续指令安全使用或转发。指令\T RsTuse RtTuse D_Tnew 说明 R_cal 1 1 2 Rs、Rt在E级送入ALU,结果在E级产生 I_cal 1 \ 2 只用Rs,结果在E级产生 lw(及所有涉及到 读内存的指令)1 \ 3 Rs(base)在E级用于算地址,结果在M级(访存后)产生 sw 1 2 0 Rs(地址)在E级,Rt(要写入内存的数据)在M级,无需写回GRF j \ \ 0 不读寄存器、不写寄存器 jal \ \ 1 不读寄存器,要写入寄存器的值在D级生成(PC + 4) jr 0 \ 0 Rs在D级作为跳转目标被使用,不写寄存器 beq 0 0 0 Rs\Rt在D级参与跳转判断;不写入寄存器 -
-
-
模块规格
信号名 方向 位宽 描述 opcode I 6 指令[31:26]位 funct I 6 指令[5:0]位 RegWrite O 1 GRF写使能信号 aluControlSel O 3 alu操作数选择信号
000:rs/rt
001:rs/immALUop O 4 ALU运算模式选择信号 MemWrite O 1 DM写使能信号 MemRead O 1 DM读使能信号 EXTop O 1 EXT扩展模式选择信号 RegDst O 3 GRF写回地址选择信号:
001:rt
000:rd
010:rs
011:GPR[31]WDsel O 2 GRF写入数据选择信号:
00:ALU运算结果
01:DM读出的数据(lw)
10:PC + 8(延迟槽)NPCop O 3 PC更新模式选择信号:
000:顺序执行
001:beq分支跳转
010:j/jal跳转
011:jr跳转MemWriteDataOp O 2 写入DM数据选择信号
00:RtDataifB O 1 是否为B类指令 ifJ O 1 是否为J类指令 BranchOp O 3 判断是否跳转的规则的选择信号
3’b000:if(rsData == rtData) 跳转useRt O 1 判断该指令是否涉及到读Rt islink O 1 判断该指令是否要link(将PC + 8写入 $31)isConditionalLink O 1 判断该指令是否是条件link isLikely O 1 判断该指令是否涉及分支清空延迟槽
HCU(冒险控制单元)
-
核心任务:(按优先级实现)
- 顶层是否空拍一周期?
- 是否需要转发?转发路径?
- 维持原数据
-
模块规格
信号名 方向 位宽 描述 D_Rs I 5 ID中指令对应的Rs地址 D_Rt I 5 ID中指令对应的Rt地址 E_Rs I 5 EX中指令对应的Rs地址 E_Rt I 5 EX中指令对应的Rt地址 M_Rt I 5 MEM中指令对应的Rt地址 E_WA I 5 “上一条”指令(EX中指令)最终要写入的寄存器地址 M_WA I 5 “上上条”指令(MEM中指令)最终要写入的寄存器地址 W_WA I 5 (WB中指令)最终要写入的寄存器地址 E_RegWrite I 1 EX中指令是否涉及写寄存器 M_RegWrite I 1 MEM中指令是否涉及写寄存器 W_RegWrite I 1 WB中指令是否涉及写寄存器 D_useRt I 1 ID中指令是否涉及到读Rt M_ifLink I 1 M中指令是否要link stall O 1 是否空拍一次 D_RsData_Sel O 3 实际写入E_reg\NPC的数据选择信号
000:原RsData
001:M_ALUresult
010:W_WD
011:M_PC8
100:EPC8
tip:必须先判断是否涉及写入$0,再判断001&011,最后判断011(即是否涉及从最近的一条在MEM的指令跳转)D_RtData_Sel O 3 实际写入E_reg的数据选择信号
000:原RtData
001:M_ALUresult
010:W_WD
011:M_PC8
100:EPC8E_RsData_Sel O 3 实际写入ALUControl的数据选择信号
000:原RsData
001:M_ALUresult
010: W_WD
011: M_PC8
100:EPC8E_RtData_Sel O 3 实际写入ALUControl的数据选择信号
000:原RtData
001:M_ALUresult
010: W_WD
011: M_PC8
100:EPC8M_RtData_Sel O 3 实际写入MemWriteDataSel的数据选择信号
000: 原RtData
001: W_WD
BranchControl
- 核心任务: 判断是否需要跳转
- 整体设计:
- 模块规格
信号名 方向 位宽 描述 rsData I 32 rs data rtData I 32 rt data ifB I 1 是否是B类指令 ifJ I 1 是否为J类指令 islink I 1 判断该指令是否要link(将PC + 8写入 $31)isConditionalLink I 1 判断该指令是否是条件link isLikely I 1 判断该指令是否涉及分支清空延迟槽 BranchOp I 3 跳转规则的选择信号
3’b000:beqBranchEn O 1 是否跳转 ifLink O 1 是否真正要link D_flush O 1 是否清空延迟槽
NPC
-
核心任务 计算下一周期PC应更新成的值
-
整体设计 所有分支/跳转目标地址必须基于 D_PC
- 顺序执行:
NPC = PC + 4 - 分支跳转:
NPC = (PC + 4) + (SignExtend(offset)) - j跳转:
NPC = (PC + 4)31...28||instr_index||00 - jr跳转:
NPC = GPR[rs]
- 顺序执行:
-
模块规格
信号名 方向 位宽 描述 immEXT I 32 拓展后的立即数(offset) instr_index I 26 instr_index NPCop I 3 000:NPC = PC + 4
001:分支跳转
010:j
011:jrrd1OrRs I 32 rs储存的值 BranchEn I 1 B型指令是否跳转F_PC I 32 刚进入流水线(IF阶段取出)的指令地址 D_PC I 32 D_PC NPC O 32 IFU中PC更新值
GRF
- 整体设计
- 读数据:指令最多需要同时读取
两个不同地址的数据 -两个读端口 - 写数据
-
0 号寄存器的值始终保持为 0。其他寄存器初始值(复位后)均为 0,无需专门设置
- 读数据:指令最多需要同时读取
- 模块规格:
信号名 方向 位宽 描述 reset I 1 异步复位信号 clk I 1 时钟信号 RA1 I 5 读地址1 RA2 I 5 读地址2 WA I 5 写地址 WD I 32 写数据 RegWrite I 1 写使能信号 RD1 O 32 读数据1 RD2 O 32 读数据2
WASel
- 整体设计 选择存入GRF的地址
- 模块规格:
信号名 方向 位宽 描述 RegDst I 3 GRF写回地址选择信号:
001:rt
000:rd
010:rs
011:GPR[31]rs I 5 rs rt I 5 rt rd I 5 rd
EXT
- 核心任务 把16位立即数扩展到32位
- 整体设计
能够对立即数进行
零扩展/符号扩展 - 模块规格
信号名 方向 位宽 描述 immOrOffset I 16 待扩展的立即数 EXTop I 1 扩展方式选择信号:
1 - 符号扩展
0 - 零扩展immEXT O 32 扩展后的立即数
E
Reg_E
- 模块规格
信号名 方向 位宽 描述 clk I 1 clock reset I 1 同步复位信号 stall I 1 清零信号 D_ControlBus I CTL_BUS_WIDTHD_ControlBus D_instruction I 32 D_instruction D_RD1 I 32 D_RD1 D_RD2 I 32 D_RD2 D_immEXT I 32 D_immEXT D_PC I 32 D_PC D_WA I 5 D_WA D_ifLink I 1 D_ifLink E_ControlBus O CTL_BUS_WIDTHE_ControlBus E_instruction O 32 E_instruction E_RD1 O 32 E_RD1 E_RD2 O 32 E_RD2 E_immEXT O 32 E_immEXT E_PC O 32 E_PC E_WA O 5 E_WA E_ifLink O 1 E_ifLInk
EX
ALUControl
- 整体设计 为alu选择operand1,operand2
- 模块规格
信号名 方向 位宽 描述 RsData I 32 RsData RtData I 32 RtData immEXT I 32 扩展后的立即数 shamt I 5 shamt aluControlSel I 3 选择信号
000:rs/rt
001:rs/immoperand1 O 32 operand1 operand2 O 32 operand2
ALU
- 整体设计
- 能够实现多种运算功能,并根据ALUop选择对应功能
ALUop 运算 0000 无符号 op1 + op20001 无符号 op1 - op20010 op1 & op20011 op1 | op20100 op2低16位加载至高位,低位补0 0101 $signed(operand1)<$signed(operand2) ? 1:0
- 能够实现多种运算功能,并根据ALUop选择对应功能
- 模块规格
信号名 方向 位宽 描述 ALUop I 4 选择运算功能 operand1 I 32 operand1 operand2 I 32 operand2 ALUresult O 32 运算结果
M
Reg_M
- 模块规格
信号名 方向 位宽 描述 clk I 1 clock reset I 1 同步复位信号 E_ControlBus I CTL_BUS_WIDTH+1来自 E 级的控制信号 E_instruction I 32 E_instruction E_ALUresult I 32 E_ALUresult E_RD1 I 32 E_RD1 E_RD2 I 32 E_RD2 E_immEXT I 32 E_immEXT E_MemWriteData I 32 E_MemWriteData E_PC I 32 E_PC E_WA I 5 E_WA E_ifLink I 1 E_ifLInk M_ControlBus O CTL_BUS_WIDTH+1M_ControlBus M_instruction O 32 M_instruction M_ALUresult O 32 M_ALUresult M_RD1 O 32 M_RD1 M_RD2 O 32 M_RD2 M_immEXT O 32 M_immEXT M_MemWriteData O 32 M_MemWriteData M_PC O 32 M_PC M_WA O 5 M_WA M_ifLink O 1 M_ifLInk
MEM
MemWriteDataSel
- 整体设计 选择写入内存的数据
- 模块规格
信号名 方向 位宽 描述 MemWriteDataOp I 2 写入DM数据选择信号
00:RtDataRtData I 32 rt data MemWriteData O 32 写入DM的数据
DM
- 模块规格
信号名 方向 位宽 描述 Address I 32 读/写地址,默认是ALUresult WriteData I 32 要写入的数据 MemWrite I 1 写使能信号 MemRead I 1 读使能信号 clk I 1 时钟信号 reset I 1 异步复位信号 PC I 32 当前指令的地址 LoadType I 2 load模式选择信号
2’b00:lw
2’b01:lhSaveType I 2 Save模式选择信号
2’b00:sw
2’b01:shMemReadData O 32 读出的数据
W
Reg_W
- 模块规格
信号名 方向 位宽 描述 clk I 1 clock reset I 1 同步复位信号 M_ControlBus I CTL_BUS_WIDTH+1来自 M 级的控制信号 M_instruction I 32 M_instruction M_ALUresult I 32 M_ALUresult M_RD1 I 32 M_RD1 M_RD2 I 32 M_RD2 M_immEXT I 32 M_immEXT M_MemReadData I 32 M_MemReadData M_PC I 32 M_PC M_WA I 5 M_WA M_ifLink I 1 M_ifLInk W_ControlBus O CTL_BUS_WIDTH+1W_ControlBus W_instruction O 32 W_instruction W_ALUresult O 32 W_ALUresult W_RD1 O 32 W_RD1 W_RD2 O 32 W_RD2 W_immEXT O 32 W_immEXT W_MemReadData O 32 W_MemReadData W_PC O 32 W_PC W_WA O 5 W_WA W_ifLink O 1 W_ifLInk
WB
WDSel
- 整体思路 选择写回GRF的数据
- 模块规格
信号名 方向 位宽 描述 WDsel I 2 GRF写入数据选择信号:
00:ALU运算结果
01:DM读出的数据(lw)
10:PC + 8(延迟槽)ALUresult I 32 ALU运算结果 MemData I 32 DM读出的数据 PC I 32 PC WD O 32 WD
测试
测试覆盖
- 基本指令功能
- 转发测试
- 暂停测试
基本指令功能测试
略
转发测试
按照完备性表格分类测试
| 转发到/数据来源 | M锁存的ALUresult | W_WD | M_PC8 |
|---|---|---|---|
| ID stage | RtData/RsData | RtData/RsData | RtData/RsData |
| EX stage | RtData/RsData | RtData/RsData | RtData/RsData |
| MEM stage | 不存在 | RtData | 不存在 |
-
(1,1):ID ← M_ALUresult
1
2
3
4
5ori $3, $0, 1
ori $4, $0, 2
beq $5, $3, label
label:
nop -
(1,2):ID ← W_WD
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57ori $3, $0, 1
ori $4, $0, 2
ori $6, $0, 2
beq $5, $3, label
label:
nop
```
- (1,3):ID ← M_PC + 8
```mips
jal return
ori $4, $0, 2
beq $5, $31, label
return:
jr $31
label:
nop
```
- (2,1):EX ← M_ALUresult
```mips
ori $3, $0, 1
ori $4, $3, 2
```
- (2,2):EX ← W_WD
```mips
ori $3, $0, 1
add $5, $3, $3
ori $4, $3, 2
```
- (2,3):EX ← M_PC + 8
```mips
jal return
ori $4, $31, 2
return:
jr $31
label:
nop
```
- (3,2):MEM ← W_WD
```mips
ori $3, $0, 5
sw $3, 0($0)
```
## 暂停测试
- E_Conflict:
```mips
ori $1,$0,3
beq $1,$0,label
label:
nop -
M_Conflict:
1
2
3
4
5lw $1, 0($0)
nop
beq $1, $0, label
label:
nop
测试出来的问题
link类
- 问题
- 我默认传递的PC值不会被覆盖,所以在WB阶段直接写入W_PC + 4。但实际上由于stall、D_flush的存在,PC值在中间是可能被覆盖的。
- 所以为了保证link的正确性,我们应该额外设置数据通路,将实际要写回的
D_PC + 4往后传。
- 所以为了保证link的正确性,我们应该额外设置数据通路,将实际要写回的
- 我默认传递的PC值不会被覆盖,所以在WB阶段直接写入W_PC + 4。但实际上由于stall、D_flush的存在,PC值在中间是可能被覆盖的。
What is FLUSH ?
- D_flush(likely导致的):在D级注入一条nop(本质:REPLACEMENT)
- 清空instruction
- PC保持原值
- E_flush(stall导致的):在E级注入一条nop(本质:INSERTION)
- 清空instruction
- PC保持原值(该nop是凭空加在两条指令之间的,没有对应的PC)
思考题
-
我们使用提前分支判断的方法尽早产生结果来减少因不确定而带来的开销,但实际上这种方法并非总能提高效率,请从流水线冒险的角度思考其原因并给出一个指令序列的例子。
- 原因:提前判断需要RsData和RtData,而这两个数据可能需要转发。如果HCU判断需要stall,那么提前判断节省的一个T和stall延迟的一个T就抵消了,并没有提高效率。
- 例如:
1
2lw $t0, 0($t1)
beq $t0, $zero, L
-
因为延迟槽的存在,对于 jal 等需要将指令地址写入寄存器的指令,要写回 PC + 8,请思考为什么这样设计?
- jal把下一条“应该执行”的指令地址写入
$ra($31),但由于延迟槽的存在,下一条要执行的指令实际上是PC + 8处的(下下条)指令。
- jal把下一条“应该执行”的指令地址写入
-
我们要求大家所有转发数据都来源于流水寄存器而不能是功能部件(如 DM 、 ALU ),请思考为什么?
- 功能部件在同一周期内输出还不稳定,但来自流水寄存器的数据已经进行过一次锁存,是稳定的数据
- 运用其它阶段正在计算的中间值本质上破坏了流水线的时序结构,相当于又类似单周期CPU一样形成了横跨各阶段的巨大组合逻辑
-
我们为什么要使用 GPR 内部转发?该如何实现?
- 内部转发本质:当GRF同时在WB被写入,在ID被读出,且存在读写寄存器冲突,读出的寄存器值应为新写入的值
- 如果不实现内部转发,就会引发
同周期的WD-ID数据冒险 - 实现:
1
2assign RD1 = (RA1 != 0 && RA1 == WA && RegWrite) ? WD : register[RA1];
assign RD2 = (RA2 != 0 && RA2 == WA && RegWrite) ? WD : register[RA2];
-
我们转发时数据的需求者和供给者可能来源于哪些位置?共有哪些转发数据通路? 需求者\供给者|M锁存的ALUresult|W_WD|M_PC8|E_PC8 —|—|—|— ID stage:|RtData/RsData|RtData/RsData|RtData/RsData|RtData/RsData EX stage:|RtData/RsData|RtData/RsData|RtData/RsData|不存在 MEM stage:|不存在|RtData|不存在|不存在
-
在课上测试时,我们需要你现场实现新的指令,对于这些新的指令,你可能需要在原有的数据通路上做哪些扩展或修改?提示:你可以对指令进行分类,思考每一类指令可能修改或扩展哪些位置。
- 对指令进行分类
- R_cal
- I_cal
- B
- J
- B&J可能有以下属性
- Link
- Likely
- ConditionalLink
- B&J可能有以下属性
- Load:所有涉及到读dm的指令
- Save
- 具体见上“加指令思路”
- 对指令进行分类
-
确定你的译码方式,简要描述你的译码器架构,并思考该架构的优势以及不足。
- 选择集中式译码 & 控制信号驱动型译码
- 优势:
- 集中式译码:只用设置一个Control单元,译码之后把ControlBus一直往后传即可
- 控制信号驱动型译码:代码简洁,拓展性好
- 不足:
- 集中式译码:ControlBus位宽大(即数据通路过宽)