figure

指令集

  1. add(无符号加法,不考虑溢出)
    add
  2. sub(无符号减法,不考虑溢出)
    sub
    sub
  3. ori
    ori
  4. lw
    lw
    lw
  5. sw
    sw
  6. beq
    beq
  7. lui
    lui
  8. jal
    jal
  9. jr
    jr
  10. nop

控制信号

add sub ori lui lw sw beq jal jr
RegDst<1:0> Rd Rd Rt Rt Rt $31
ALUSrc 1 1 1 1
RegWrite 1 1 1 1 1 1
MemWrite 1
ExtOp 1 1
CMPOp 2’b01
npc_sel PC_4 PC_4 PC_4 PC_4 PC_4 PC_4 branch jump jr
ALUop<3:0> ADD SUB OR SHIFT ADD ADD
OutE_sel imm32 PC_8
OutM_sel ALU_result ALU_result ALU_result Out_E Out_E
OutW_sel out_M out_M out_M out_M MemData out_M
RegDst value
2’b00 Rt
2’b01 Rd
2’b10 $31
2’b11
npc_sel value
3’b00 PC_4
3’b01 branch
3’b10 jump
3’b11 jr
npc_sel value
4’b0000 add
4’b0001 sub
4’b0010 or
OutE_sel value
2’b00 0
2’b01 imm32
2’b10 PC_8
OutM_sel value
2’b00 out_E
2’b01 ALU_result
OutW_sel value
2’b00 out_M
2’b01 MemData

新增模块

CMP

端口 类型 位宽
inA I 32
inB I 32
CMPOp I 2
Branch O 1

流水线的阻塞与转发

$T_{use}$

add sub ori lui lw sw beq jal jr
rs_Tuse 1 1 1 1 1 1 0 0
rt_Tuse 1 1 2 0

$T_{new}$

add sub ori lui lw sw beq jal jr
E_Tnew 1 1 1 1 2 0 0 0 0
M_Tnew 0 0 0 0 1 0 0 0 0
W_Tnew 0 0 0 0 0 0 0 0 0

SF

$ET_{new}$ $MT_{new}$ $WT_{new}$
$T_{use}$ 2 1 0 1 0 0
0 S S F S F F
1 S F F F F F
2 F F F F F F

Stall

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
/*----------Stall-----------*/
//Rs
wire Stall_Rs0_E2;
wire Stall_Rs0_E1;
wire Stall_Rs0_M1;
wire Stall_Rs1_E2;
assign Stall_Rs0_E2 = (Rs_T_use == 2'b0 && T_new_E == 2'b10 && A1_D == A3_E && RegWrite_E && A1_D != 5'b0);
assign Stall_Rs0_E1 = (Rs_T_use == 2'b0 && T_new_E == 2'b01 && A1_D == A3_E && RegWrite_E && A1_D != 5'b0);
assign Stall_Rs0_M1 = (Rs_T_use == 2'b0 && T_new_M == 2'b10 && A1_D == A3_M && RegWrite_M && A1_D != 5'b0);
assign Stall_Rs1_E2 = (Rs_T_use == 2'b1 && T_new_E == 2'b10 && A1_D == A3_E && RegWrite_E && A1_D != 5'b0);
//Rt
wire Stall_Rt0_E2;
wire Stall_Rt0_E1;
wire Stall_Rt0_M1;
wire Stall_Rt1_E2;
assign Stall_Rt0_E2 = (Rt_T_use == 2'b0 && T_new_E == 2'b10 && A2_D == A3_E && RegWrite_E && A2_D != 5'b0);
assign Stall_Rt0_E1 = (Rt_T_use == 2'b0 && T_new_E == 2'b01 && A2_D == A3_E && RegWrite_E && A2_D != 5'b0);
assign Stall_Rt0_M1 = (Rt_T_use == 2'b0 && T_new_M == 2'b10 && A2_D == A3_M && RegWrite_M && A2_D != 5'b0);
assign Stall_Rt1_E2 = (Rt_T_use == 2'b1 && T_new_E == 2'b10 && A2_D == A3_E && RegWrite_E && A2_D != 5'b0);
//Stall
wire Stall_Rs;
wire Stall_Rt;
assign Stall_Rs = (Stall_Rs0_E2||Stall_Rs0_E1||Stall_Rs0_M1||Stall_Rs1_E2);
assign Stall_Rt = (Stall_Rt0_E2||Stall_Rt0_E1||Stall_Rt0_M1||Stall_Rt1_E2);
assign Stall = (Stall_Rs||Stall_Rt);
/*--------------------------*/

转发

提供者:M_Reg,W_Reg
需求者:D_CMP,ALU_src,DM_WD

1
2
3
4
5
6
7
8
9
10
11
/*---------Forward----------*/
assign RD1_D_sel = (T_new_M == 2'b0 && A1_D == A3_M && A1_D != 5'b0 && RegWrite_M) ? 2'b10 :
(T_new_W == 2'b0 && A1_D == A3_W && A1_D != 5'b0 && RegWrite_W) ? 2'b01 : 2'b0;
assign RD2_D_sel = (T_new_M == 2'b0 && A2_D == A3_M && A2_D != 5'b0 && RegWrite_M) ? 2'b10 :
(T_new_W == 2'b0 && A2_D == A3_W && A2_D != 5'b0 && RegWrite_W) ? 2'b01 : 2'b0;
assign RD1_E_sel = (T_new_M == 2'b0 && A1_E == A3_M && A1_E != 5'b0 && RegWrite_M) ? 2'b10 :
(T_new_W == 2'b0 && A1_E == A3_W && A1_E != 5'b0 && RegWrite_W) ? 2'b01 : 2'b0;
assign RD2_E_sel = (T_new_M == 2'b0 && A2_E == A3_M && A2_E != 5'b0 && RegWrite_M) ? 2'b10 :
(T_new_W == 2'b0 && A2_E == A3_W && A2_E != 5'b0 && RegWrite_W) ? 2'b01 : 2'b0;
assign DM_WD_sel = (T_new_W == 2'b0 && A2_M == A3_W && A2_M != 5'b0 && RegWrite_W);
/*--------------------------*/

测试方案

分析模拟出所有可能的冒险情况,进行测试。
当然,有一种转发情况弱侧没有测出来,这里也给出一个示例:

1
2
3
4
5
6
7
8
ori $4,$0,4
add $1,$0,$0
ori $3,$0,0x1234
sw $3,100($4)
add $1,$4,$0
ori $4,$0,4
lw $2,100($4)
sw $2,100($0)

思考题

我们使用提前分支判断的方法尽早产生结果来减少因不确定而带来的开销,但实际上这种方法并非总能提高效率,请从流水线冒险的角度思考其原因并给出一个指令序列的例子。

beq提前到D级判断,使得其Tuse降为0,这显然提高了阻塞率。当beq指令前面是Tnew = 1的指令时均阻塞。
提前分支判断,虽然在某些情况下有可能损失更大,但是大多数情况下是无损失的。

1
2
add $1,$2,$3
beq $1,$0,label

因为延迟槽的存在,对于 jal 等需要将指令地址写入寄存器的指令,要写回 PC + 8,请思考为什么这样设计?

因为延迟槽的存在,jal后面的那条指令是一定会被执行的,跳转回来后要跳过已经执行的指令。

我们要求大家所有转发数据都来源于流水寄存器而不能是功能部件(如 DM、ALU),请思考为什么?

其一,功能部件与转发是顺序关系,有可能会导致时钟周期的长度增加;流水寄存器的转发和下一周期的操作独立并行,不影响执行速度。
其二,功能部件实时计算,数据可能不稳定。

我们为什么要使用 GPR 内部转发?该如何实现?

GRF内部转发本质上是W到D的转发。

1
2
3
4
/*ctrl*/
assign DM_WD_sel = (T_new_W == 2'b0 && A2_M == A3_W && A2_M != 5'b0 && RegWrite_W);
/*Data_sel*/
assign DM_WD_M_valid = (DM_WD_sel) ? GRF_WD : DM_WD_M;

我们转发时数据的需求者和供给者可能来源于哪些位置?共有哪些转发数据通路?

需求者:D级CMP的Rs_Data和Rd_Data,E级的ALU_srcA和ALU_srcB,M级的DM_WD。
供给者:E级流水寄存器,M级流水线寄存器和W级流水线寄存器。
转发通路:MD,ME,WD,WE,WM,ED(lui,jal)

在课上测试时,我们需要你现场实现新的指令,对于这些新的指令,你可能需要在原有的数据通路上做哪些扩展或修改?提示:你可以对指令进行分类,思考每一类指令可能修改或扩展哪些位置。

计算类型:

  1. ALU模块内部:添加运算新类型
  2. 控制信号: ALUOp,RegDst,RegWrite,OutMsel
  3. 暂停和转发:HazardCtrl内部添加新信号的识别,同时Tuse和Tnew等与addsub等保持一致

跳转类型

  1. CMP模块内部:添加新的跳转条件判断
  2. 控制信号:CMPOp,RegDst,RegWrite,OutEsel,npc_sel
  3. 暂停和转发:HazardCtrl内部添加新信号的识别,同时Tuse和Tnew等与beq等保持一致
  4. 清空延时槽操作

访存类型

  1. DM模块后:添加新的运算模块,输出需要的A3和WD
  2. 控制信号:ALUSrc,RegDst,RegWrite,ExtOp,MemRead
  3. 暂停和转发:
    1. HazardCtrl内部添加新信号的识别和输出
    2. Tuse和Tnew等与lw等保持一致
    3. Hazard添加new_E,new_M
    4. Stall添加针对新信号排除不可能写入寄存器号的判断
  4. 加入选择new_WD,new_A3得到A3_M_valid,outM_valid

确定你的译码方式,简要描述你的译码器架构,并思考该架构的优势以及不足。

分布式译码器,并且采取列出控制信号每种取值所对应的指令的方式。
好处是内部撰写简洁,好对应。
坏处是需要重复实例化多个控制器,控制信号对应的指令容易有遗漏。

修正过的错误

CPUcmt

总结一下自P5过了弱测之后修正过的bug

  • 1 增加MW数据通路,解决lw后立即sw的情况
  • 2 将lui提前到D级流水线实现
  • 3 完成DM的初始化(i<32 -> i<3072)
  • 4 完成ED转发(针对jal和lui)
  • 5 增加M级转发的数据类型,包括PC+8,ALUResult,imm32(lui)
  • 6 修正jal对应的T值
  • 7 修正stall中T0M1的情况,M-Tnew == 2’b10->M-Tnew == 2’b01
  • 8 增加cmp模块,用于branch&&link指令的跳转
  • 9 | a 增加outE,outM,outW,便于转发数据的选择
  • b 将RegWrite添加到流水寄存器,使判断写入的情况更方便
  • c 增加清空延迟槽的操作
  • d 确保npcSel为3位
  • e 完善flush,之前忘记把flush实例化到RegD里了
  • f 添加j指令(方便测试)将jal的Tuse由0改为3
  • g 简化Stall方便判断,增加RegDst在EM的接口,方便后续访存指令的添加
  • h添加default,防止以后遇到没有初始化信号或者已经初始化但是后面使用时打错的情况
  • i为新指令预留好接口,方便添加指令

计算类指令

addei

1
2
3
4
5
6
7
8
9
temp ← (one_extend(offset)31 || one_extend(offset)31...0) + (GPR[rs]31 || GPR[rs]31...0)

if temp32 != temp31

GPR[rt] ← one_extend(offset)

else

GPR[rt] ← temp

ALU计算类指令的添加

  1. 涉及到Ctrl的更改:
    • 新增addei的识别
    • RegWrite,RegDst,ALUsrc,ExtOp
    • ALUOp(新增指令对应的计算),outMsel(选ALUresult)
  2. 涉及到HazardCtrl
    • 新增addei的识别
    • Tuse,Tnew保持与add和sub相同

跳转类指令

bonall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
I:

target_offset ← sign_extend(offset||02)

Condition ← GPR[rs] + GPR[rt] = 032

GPR[31] ← PC + 8

I+1:

If condition then

PC ← PC + target_offset

else

NullifyCurrentInstruction()

endif

注意verilog默认无符号数计算和比较
研究还需要添加哪些控制信号到流水寄存器中

blztal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
I:

target_offset ← sign_extend(offset||0(2+GPR[rt]1...0))

Condition ← GPR[rs] < 0

If condition then

GPR[31] ← PC + 8

endif

I+1:

If condition then

PC ← PC + target_offset

endif

==左移的优先级低于+-==
写移位的时候一定要记得带括号
确保运算的优先级与期望的一致

因为Regwrite的判断和Rs有关
建议将其加入流水寄存器,
否则实时判断更加麻烦

注意写==位宽==!!!!!

访存类指令

lhogez

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
vAddr ← sign_extend(offset) + GPR[base]

pAddr ← vAddr31...2 || 02

memword ← memory[pAddr]

bit ← vAddr1

def:

countOne(x): x 中 1 的个数

countZero(x): x 中 0 的个数

memHalf ← memword15+16*bit ..16*bit

Condition ← countZero(memHalf) ≤ countOne(memHalf)

if Condition:

GPR[rt] ← sign_extend(memHalf)

else:

GPR[31] ← PC + 4

end if

cmtRecord:
f2从Instr里取rt取错了,取的Rs
f3忘记把lhogez实例化到hazardCtrlE和M里去了
f4为了找出错误把Hazard寄存器号判断限制删掉了,有一个点tle
f5发现StallRs/t0M1限制条件取的是TE,更正为M后通过

lbget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vAddr ← sign_extend(offset) + GPR[base]

pAddr ← vAddr31...2 || 02

memword ← memory[pAddr]

byte ← vAddr1...0

Condition ← memword7+8*byte = 0

if Condition:

GPR[rt] ← sign_extend(memword7+8*byte..8*byte)

else:

GPR[base] ← sign_extend(memword7+8*byte..8*byte)

end if

cmtRecord:
f2添加Stall的特别判断
f3修正判断负数的写法,判断第一位是不是1,verilog默认无符号数比较
f4换了另一种写法,加了$signed<0,依然没过
f5简化寄存器号的限制条件,依然没有变化
f6将更新后的A3Mvalid传到RegW里去而不是更新前的A3M
f7修正==编译错误==,有一个端口少加了wire
f8修正Stall限制条件,把M错打成了E,通过

lwer

1
2
3
4
5
addr ← GPR[base]+sign_ext(offset)

memword ← Memory[addr[31:2]||02]

GPR[(memword+GPR[rt]) & 0x1e] = memword

cmtRecord:
f1第一次提交 没有输出
f2 修正A3M-valid的接线错误(拼写错误)添加阻塞RsTuse,Tnew,
RtStall添加判断,如果lwer,且寄存器号为偶数,不为0,才发生阻塞
f3 gprRt应在E级纳入,使用最新的RD2Evalid
f4 修正Rt阻塞判断,不需要满足两个寄存器号相等,M级才知道要写入的寄存器
f5 添加阻塞RtTuse(gprRt在M级会用到)
f6 为Rs添加和Rt一样的阻塞判断!!!是lwer转发给别人用的值!!!