北航计算机组成P5

前言

  • P5工作量确实比P4大了不少,画设计图写文档一天半,搭CPU加测试一天,不过如果课下做好准备过了强测那课上100%能过甚至直接ak走人了

课下

  • 写完P6已经1700行了,主要是我采取了针对指令的译码方式,所以整体代码量会稍微大一点(),主要这样我比较方便debug,所以就没改了

    译码方式

  • 集中式译码方式

    • 最大的优点就是方便从P4改过来,其实我选择集中式译码的理由是方便添加一些指令,而且译码行为在D级就可以全完成,所以如果加入一些额外的指令信号不会出现高阻态或者其他的比较抽象的错误(分布式译码课上如果出锅了可能就是这个原因)
    • 最大的缺点就是需要流水的信号实在是太多了,因为所有要用的信号必须在D级就全部生成完毕,所以向E级就会流水各种信号,不过课下认真设计,仔细连线保证没有bug那在课上也没啥,反正课上最多增加两三个新增的端口,反正不会一次写两个寄存器()
  • 分布式译码

    • 最大的优点就是流水的东西少,几乎可以只流水指令,然后每次在接收到指令之后再译码,从里面接出需要的信号,然后提供给这个级
    • 缺点是,缺点是啥来着,我刚开始不想用分布式译码主要是觉得每个级单独译出$T_{new}$信号什么的特别复杂,还不如刚开始就全部翻译处出来了,但是往届学长还是有很多优秀的分布式译码框架的,只是我没有去参考
  • 译码语句没有选assign,那和我搭logisim有什么区别(怒),我verilog就要用不一样的东西,事实是不如assign,代码量还是太大了,不过课上加指令的适合比较方便,毕竟每个指令之间不会互相影响

    always@(*) begin
    NPCSel = `NPC_normal;
    ExtOp = `EXT_zero;
    GRFA3Sel = `GRFA3_zero;
    ...
    case(instr) begin
    `Op_add: begin
    GRFA3Sel = `GRFA3_rd;

    calr = 1'b1;
    end
    ...
    endcase
  • 事实上也可以像logisim一样用与或门阵列的方式,但是我不使用的原因是P3课上加指令的时候加的太慢而且老是搭错线。。。,优点是代码量压缩程度很优秀,尤其适合P6支持更多指令的时候
    wire add, sub.....;
    wire[1: 0] GRFA3Sel...;

    assign add = (Opcode == 6'b0) & (Funct == `Func_add);
    assign GRFA3Sel[0] = (add | ....)
    assign GRFA3Sel[1] = ....
    //或者
    assign GRFA3Sel = (add | ....) ? 2'b00 :
    (ori | .....) ? 2'b01

数据通路设计

F

PC
  • 同理我们先建模最简单(最重要)的PC寄存器,理由同单周期CPU,我们需要PC寄存器指向当前的指令地址
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | we | 1 | input | PC寄存器使能信号 |
    | NPC[31:0] | 32 | input | 传入更新的值 |
    | PC[31:0] | 32 | output | 传出目前的PC值 |
IM
  • 指令寄存器,同单周期CPU,没有任何改变
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | PC[31:0] | 32 | input | 传入PC值供取指 |
    | Instr[31:0] | 32 | input | 获得指令 |

D

D_REG
  • F/D流水寄存器,将F级的信息传递给D级
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | we | 1 | input | D流水寄存器使能信号 |
    | F_PC[31:0] | 32 | input | F级传入PC值 |
    | F_Instr[31:0] | 32 | input | F级传入指令 |
    | D_PC[31:0] | 32 | output | 传入D级PC值 |
    | D_Instr[31:0] | 32 | output | 传入D级别PC值 |
IM_SPL
  • D级分线器,将指令分为若干部分
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | Instr[31:0] | 32 | input | 传入解析指令 |
    | Opcode[5:0] | 6 | output | 指令操作码 |
    | Funct[5:0] | 6 | output | R型指令函数操作码 |
    | Rs[4:0] | 5 | output | 源寄存器 |
    | Rt[4:0] | 5 | output | 目标寄存器 |
    | Rd[4:0] | 5 | output | 目的寄存器 |
    | Shamt[4:0] | 5 | output | 移位值 |
    | Imm_i[15:0] | 16 | output | I型指令立即数 |
    | Imm_j[25:0] | 26 | output | J型指令立即数 |
GRF
  • D级寄存器堆,取出对应寄存器值
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | A1[4:0] | 5 | input | 第一个读寄存器 |
    | A2[4:0] | 5 | input | 第二个读寄存器 |
    | A3[4:0] (W) | 5 | input | 写寄存器 |
    | WD[31:0] (W) | 32 | input | 寄存器写入值 |
    | RD1[31:0] | 32 | output | 读寄存器值(已内部转发) |
    | RD2[31:0] | 32 | output | 读寄存器值(已内部转发) |
EXT
  • D级拓展单元

    • 端口定义

      | 端口 | 位宽 | 方向 | 描述 |
      | :-: | :—: | :—: | :—: |
      | Imm[15:0] | 16 | input | 拓展立即数 |
      | ExtOp[1:0] | 2 | input | 控制立即数拓展 |
      | Ext_imm[31:0] | 32 | input | 拓展后立即数 |

    • 控制信号定义

      | 信号名 | 取值 | 描述 |
      | :-: | :—: | :—: |
      | EXT_zero | 2’b00 | 控制ExtOp信号零拓展 |
      | EXT_sign | 2’b01 | 控制ExtOp信号符号位拓展 |
      | EXT_lui | 2’b10 | 控制ExtOp信号高位加载 |

NPC
  • D级次地址计算单元

    • 端口定义
      | 端口 | 位宽 | 方向 | 描述 |
      | :-: | :—: | :—: | :—: |
      | F_PC[31:0] | 32 | input | F级PC值 |
      | D_PC[31:0] | 32 | input | D级PC值 |
      | Rs[31:0] | 32 | input | 跳转寄存器值 |
      | Imm[25:0] | 26 | input | 立即数 |
      | NpcSel[1:0] | 2 | input | 控制NPC |
      | NPC[31:0] | 32 | output | 次地址 |

    • 控制信号定义
      | 信号名 | 取值 | 描述 |
      | :-: | :—: | :—: |
      | NPC_normal | 2’b00 | NpcSel正常跳转 |
      | NPC_branch | 2’b01 | NpcSel条件跳转 |
      | NPC_jadder | 2’b10 | NpcSel跳转地址 |
      | NPC_jrs | 2’b11 | NpcSel跳转寄存器 |

CMP
  • D级branch指令前移比较器
    • 端口定义
      | 端口 | 位宽 | 方向 | 描述 |
      | :-: | :—: | :—: | :—: |
      | Rs_val[31:0] | 32 | input | rs寄存器读出值 |
      | Rt_val[31:0] | 32 | input | rt寄存器读出值 |
      | CmpOp | 1 | input | 比较类型 |
      | Flag | 1 | output | 比较结果 |
    • 控制信号定义
      | 信号名 | 取值 | 描述 |
      | :-: | :—: | :—: |
      | CMP_eq | 1’b0 | 控制CmpOp信号相等比较 |
      | CMP_ne | 1’b1 | 控制CmpOp信号不等比较 |
Control
  • D级集中译码器
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | Opcode[5:0] | 6 | input | 指令操作码 |
    | Funct[5:0] | 6 | input | 函数码 |
    | Tuse_rs[1:0] | 2 | output | rs寄存器的Tuse |
    | Tuse_rt[1:0] | 2 | output | rt寄存器的Tuse |
    | Tnew[1:0] | 2 | output | 指令Tnew |
    | NpcSel[1:0] | 2 | output | 控制NPC |
    | CmpOp | 1 | output | 控制CMP |
    | ExtOp[1:0] | 2 | output | 控制EXT |
    | GRFA3Sel[1:0] | 2 | output | 控制GRFA3Mux |
    | ALUASel | 1 | output | 控制ALUAMux |
    | ALUBSel[1:0] | 2 | output | 控制ALUBMux |
    | ALUOp[1:0] | 2 | output | 控制ALU |
    | DMWe | 1 | output | DM使能 |
    | DMOp[2:0] | 3 | output | DM写入方式 |
    | GRFWDSel[1:0] | 2 | output | 控制GRFWDMux |
MUX
  • GRFA3Mux
    • 选择GRF写入地址的多路选择器
      | 信号名 | 取值 | 描述 |
      | :-: | :—: | :—: |
      | GRFA3_rt | 2’b00 | 选择rt寄存器值 |
      | GRFA3_rd | 2’b01 | 选择rd寄存器值 |
      | GRFA3_ra | 2’b10 | 选择$ra寄存器 |
      | GRFA3_zero | 2’b11 | 默认不写寄存器 |
  • RSFDMux
    • 对于读出的rs寄存器进行转发
  • RTFDMux
    • 对于读出的rt寄存器进行转发

E

E_REG
  • D/E流水寄存器

    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | we | 1 | input | 写使能信号 |
    | D_rs[4:0] | 5 | input | D级rs编号 |
    | D_rt[4:0] | 5 | input | D级rt编号 |
    | D_a3[4:0] | 5 | input | D级A3 |
    | D_rs_val[31:0] | 32 | input | D级rs寄存器值 |
    | D_rt_val[31:0] | 32 | input | D级rt寄存器值 |
    | D_ext_imm[31:0] | 32 | input | D级ext_imm值 |
    | D_shamt[4:0] | 5 | input | D级shamt值 |
    | D_pcwith8[31:0] | 32 | input | D级PC+8 |
    | E_rs[4:0] | 5 | output | E级rs编号 |
    | E_rt[4:0] | 5 | output | E级rt编号 |
    | E_a3[4:0] | 5 | output | E级A3 |
    | E_rs_val[31:0] | 32 | output | E级rs寄存器值 |
    | E_rt_val[31:0] | 32 | output | E级rt寄存器值 |
    | E_ext_imm[31:0] | 32 | output | E级ext_imm值 |
    | E_shamt[4:0] | 5 | output | E级shamt值 |
    | E_pcwith8[31:0] | 32 | output | E级PC+8 |
    | D_ALUASel | 1 | input | 控制ALUAMux |
    | D_ALUBSel[1:0] | 2 | input | 控制ALUBMux |
    | D_ALUOp[1:0] | 2 | input | 控制ALU |
    | D_DMWe | 1 | input | DM使能 |
    | D_DMOp[2:0] | 3 | input | DM写入方式 |
    | D_GRFWDSel[1:0] | 2 | input | 控制GRFWDMux |
    | E_ALUASel | 1 | output | 控制ALUAMux |
    | E_ALUBSel[1:0] | 2 | output | 控制ALUBMux |
    | E_ALUOp[1:0] | 2 | output | 控制ALU |
    | E_DMWe | 1 | output | DM使能 |
    | E_DMOp[2:0] | 3 | output | DM写入方式 |
    | E_GRFWDSel[1:0] | 2 | output | 控制GRFWDMux |
    | D_Tnew[1:0] | 2 | input | Tnew |
    | E_Tnew[1:0] | 2 | output | Tnew |

ALU
  • E级计算单元

    • 端口定义

      | 端口 | 位宽 | 方向 | 描述 |
      | :-: | :—: | :—: | :—: |
      | A[31:0] | 32 | input | ALU第一个操作数 |
      | B[31:0] | 32 | input | ALU第二个操作数 |
      | ALUOp[1:0] | 2 | input | ALU进行的操作 |
      | ALU_out[31:0] | 32 | output | ALU结果数 |

    • 控制信号定义

      | 信号名 | 取值 | 描述 |
      | :-: | :—: | :—: |
      | ALU_add | 2’b00 | 加法操作 |
      | ALU_sub | 2’b01 | 减法操作 |
      | ALU_or | 2’b10 | 或运算操作 |

MUX
  • ALUAMux

    | 信号名 | 取值 | 描述 |
    | :-: | :—: | :—: |
    | ALUA_rs | 2’b0 | 选择rs寄存器(一般而言) |
    | ALUA_rt | 2’b1 | 选择rt寄存器(移位操作) |

  • ALUBMux

    | 信号名 | 取值 | 描述 |
    | :-: | :—: | :—: |
    | ALUB_rt | 2’b00 | 选择rt寄存器(一般R型) |
    | ALUB_imm | 2’b01 | 选择ext_imm |
    | ALUB_shamt | 2’b10 | 选择shamt |
    | ALUB_rs | 2’b11 | 选择rs寄存器(R型移位) |

  • RSFEMux

  • RTFEMux

M

M_REG
  • E/M流水寄存器
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | we | 1 | input | 写使能信号 |
    | E_rt[4:0] | 5 | input | rt寄存器的编号 |
    | E_rt_val[31:0] | 32 | input | rt寄存器的值 |
    | E_alu_out[31:0] | 32 | input | ALU运算结果 |
    | E_a3[4:0] | 5 | input | A3结果 |
    | E_imm[31:0] | 32 | input | lui结果 |
    | E_pcwith8[31:0] | 32 | input | PC+8结果 |
    | M_rt[4:0] | 5 | output | rt寄存器的编号 |
    | M_rt_val[31:0] | 32 | output | rt寄存器的值 |
    | M_alu_out[31:0] | 32 | output | ALU运算结果 |
    | M_a3[4:0] | 5 | output | A3结果 |
    | M_imm[31:0] | 32 | output | lui结果 |
    | M_pcwith8[31:0] | 32 | output | PC+8结果 |
    | E_DMWe | 1 | input | DM写使能信号 |
    | E_DMOp[2:0] | 3 | input | DM选择 |
    | E_GRFWDSel[1:0] | 2 | input | 选择GRFWD |
    | M_DMWe | 1 | input | DM写使能信号 |
    | M_DMOp[2:0] | 3 | input | DM选择 |
    | M_GRFWDSel[1:0] | 2 | input | 选择GRFWD |
    | E_Tnew[1:0] | 2 | input | Tnew |
    | M_Tnew[1:0] | 2 | input | Tnew |
DM
  • M级数据存储器

    • 端口定义

      | 端口 | 位宽 | 方向 | 描述 |
      | :-: | :—: | :—: | :—: |
      | clk | 1 | input | 时钟信号 |
      | reset | 1 | input | 同步复位信号 |
      | we | 1 | input | 写使能 |
      | Adder[31:0] | 32 | input | 地址 |
      | WD[31:0] | 32 | input | 写入值 |
      | RD[31:0] | 32 | output | 读出值 |
      | DMOp[2:0] | 3 | input | 选择DM读写方式 |

    • 控制信号定义

      | 信号名 | 取值 | 描述 |
      | :-: | :—: | :—: |
      | DM_w | 3’b000 | 字单位写入读出 |
      | DM_h | 3’b001 | 半字单位写入,符号拓展读出 |
      | DM_hu | 3’b010 | 半字单位写入,无符号拓展读出 |
      | DM_b | 3’b011 | 字节单位写入,符号拓展读出 |
      | DM_bu | 3’b100 | 字节单位写入,无符号拓展读出 |

MUX

- RTFMMux

W

W_REG
  • M/W级流水寄存器
    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | we | 1 | input | 写使能信号 |
    | M_alu_out[31:0] | 32 | input | ALU运算结果 |
    | M_dm_rd[31:0] | 32 | input | 内存读出结果 |
    | M_a3[4:0] | 5 | input | A3结果 |
    | M_imm[31:0] | 32 | input | lui结果 |
    | M_pcwith8[31:0] | 32 | input | PC+8结果 |
    | W_alu_out[31:0] | 32 | output | ALU运算结果 |
    | W_dm_rd[31:0] | 32 | output | 内存读出结果 |
    | W_a3[4:0] | 5 | output | A3结果 |
    | W_imm[31:0] | 32 | output | lui结果 |
    | W_pcwith8[31:0] | 32 | output | PC+8结果 |
    | M_GRFWDSel[1:0] | 2 | input | 选择GRFWD |
    | W_GRFWDSel[1:0] | 2 | output | 选择GRFWD |
GRF
  • W级寄存器堆

    | 端口 | 位宽 | 方向 | 描述 |
    | :-: | :—: | :—: | :—: |
    | clk | 1 | input | 时钟信号 |
    | reset | 1 | input | 同步复位信号 |
    | A1[4:0] (D) | 5 | input | 第一个读寄存器 |
    | A2[4:0] (D) | 5 | input | 第二个读寄存器 |
    | A3[4:0] | 5 | input | 写寄存器 |
    | WD[31:0] | 32 | input | 寄存器写入值 |
    | RD1[31:0] (D) | 32 | output | 读寄存器值(已内部转发) |
    | RD2[31:0] (D) | 32 | output | 读寄存器值(已内部转发) |

MUX
  • GRFWDMux

    | 信号名 | 取值 | 描述 |
    | :-: | :—: | :—: |
    | GRFWD_alu | 2’b00 | 选择alu_out写入 |
    | GRFWD_dm | 2’b01 | 选择dm_rd写入 |
    | GRFWD_pcwith8 | 2’b10 | 选择PC+8写入 |
    | GRFWD_imm | 2’b11 | 选择ext_imm写入 |

  • 说了一大堆,主要注意D级的若干部件还是最大的D_reg,E_reg两个流水寄存器,最好写的时候像我一样将数据、控制信号什么的分开来,而且一定要先写设计文档,详细设计!!!,然后定义端口写模块照着你的设计文档就好了,数据通路的连接最好依照你的设计图,不管是手画还是用其他软件画的,


关于阻塞与转发

  • 为了方便加入指令,我们将所有指令分为若干类别
    • ALU计算R型指令cal_r: add, sub
    • ALU计算I型指令cal_i: ori
    • 特殊指令lui: lui
    • 存储指令store: sw
    • 访存指令load: lw
    • 分支指令branch: beq
    • 跳转链接指令j_l: jal
    • 跳回指令j_r: jr

      转发输出口

  • 接下来分析转发电路,明白哪些地方会产生寄存器的新值

    • 首先ALU_outDM_rd这两个端口必然会产生寄存器的新值(这里实际上考虑无脑转发可以将DM_rd换为GRF_wd
    • 对于lui指令,在D阶段后的ext_imm端口已经产生了值,可以转发
    • 对于j_l指令,在D阶段后的PC+8端口已经产生了值,可以转发

    | 转发输出 | 指令类型 |
    | :——-: | :———: |
    | ALU_out@M | cal_r、cal_i |
    | GRF_wd@W(过度转发) | (cal_r)、(cal_i)、load、(lui)、(j_l) |
    | ext_imm@{E、M} | lui |
    | PC+8@{E、M} | j_l |
    | 寄存器内部转发(M -> D) | 略 |

    • (指令)代表该指令已经转发过正确的值了
    • 由于寄存器的内部转发使得$W$不需要向$D$转发,而$W$向$E$转发的内容除了load指令外都是ALU_out的值,这里在$M$就已经向$D$转发过了,所以本身就是正确的,不过无脑转发有什么错呢,(大家只是拿一个正确的值替换另一个正确的值罢了,硬件电路不需要感情)
    • 根据需要往grf写入什么来判断需要转发什么,这里只是输出转发的内容,还需要配套转发内容的接收端的选择信号
      assign FWD_E = (E_GRFWDSel == `GRFWD_imm) ? E_imm :
      (E_GRFWDSel == `GRFWD_pcwith8) ? E_pcwith8 : 32'b0;

转发接入口

  • 分析转发的接入端口

    • D级可以接收来自E、M、W级别的转发,但是W级转发我们已经在内部实现了
      assign val1 = (A1 == 0) ? 32'b0 : grf[A1];
      assign RD1 = (A1 != 0) & (A1 == A3) ? WD : val1;
    • E级ALU计算前也可以接收转发
    • 最后单独对于store类型指令,rt寄存器在M阶段才会使用,所以DM的输入端口也可以接收转发

    | 转发输入 | 指令类型 |
    | :——-: | :———: |
    | GRF_rs@D | cal_r、cal_i、store、j_r、branch |
    | GRF_rt@D | cal_r、branch |
    | GRF_rs@E | 同上 |
    | GRF_rt@E | 同上(store指令可能也转了) |
    | DM_wd@M | store |
    | 寄存器内部转发(M -> D) | 略 |

    • 最后同上,正确的值可能被多次转发,例如store类指令的rt寄存器,在D、E阶段可能都被转发了一次,但是没关系,最后还是对的(),况且真的要考虑只转发一次极容易忽略转发路径,而且在考试的时候容易漏情况,所以我使用无脑转发了
      assign D_rs_val = (D_rs == 5'b0) ? 32'b0 :
      (D_rs == E_a3) ? FWD_E :
      (D_rs == M_a3) ? FWD_M :
      (D_rs == W_a3) ? FWD_W : D_grf_rd1;
    • 这里有一个疑惑,万一正确的数据没生成或者目前是错的但是我们已经无脑转发回去了怎么办?首先我们目前实现的指令集不需要考虑第一种情况,因为它对应着阻塞,Tuse < Tnew时会直接阻塞。因为这种情况来不及转发回去,也就是数据没生成就转发了;第二种情况因为Tuse >= Tnew,那么Tuse等于0时,Tnew一定也是0,说明正确的数据已经生成了并且已经转回去了,覆盖率原来错误的数据。
    • 注意:其实可以使用恰当转发,如加入Tnew==0条件,生成数据才转发回去,不过实际上我们目前不会使用,课上倒是又可能,不过也有其他办法规避,至少目前可以认为我们不选恰当转发(我后面会更新的

      阻塞

  • 根据指令类别判断$T_{use}$,$T_{new}$
    | 指令类别 | $ T_{use}:rs $(@D) | $ T_{use}:rt $(@D) | $T_{new} $@E | $ T_{new} $@M | $ T_{new} $@W |
    | :——-: | :————-: | :—————: | :——————: | :———-: | :——————————-: |
    | cal_r | $1$ | $1$ | $1$ | $0$ | $0$ |
    | cal_i | $1$ | ($3$) | $1$ | $0$ | $0$ |
    | lui | ($3$) | ($3$) | $0$ | $0$ | $0$ |
    | store | $1$ | $2$ | ($0$) | ($0$) | ($0$) |
    | load | $1$ | ($3$) | $2$ | $1$ | $0$ |
    | branch | $0$ | $0$ | ($0$) | ($0$) | ($0$) |
    | j_l | ($3$) | ($3$) | $0$ | $0$ | $0$ |
    | j_r | $0$ | ($3$) | ($0$) | ($0$) | ($0$) |
    • ($3$)代表不会需要读这个寄存器,所以不需要关注它的$T_{use}$,直接认为在W级使用即可,反正没有指令会在W级才用寄存器
    • ($0$)代表不会写寄存器,也即不会影响后续的读取,即A3@{E、M、W}都为0,所以转发会被我们排除,同时也不会干扰阻塞
    • 由于$T_{use}$只会在译码阶段使用所以不需要考虑后续流水,但是$T_{new}$信号将参与流水,表达式为$T_{new}$@{N+1} $= \max\{$ $T_{new}$@{N} $ -1,0\}$
  • 当D级需要使用rs或者rt且满足(以rs举例):$rs \ne 0$ && $rs == rd$@{N} && $T_{use} \lt T_{new}$@{N}则阻塞
  • 由于我们使用的是暴力转发,能转则转,应转尽转,所以不需要考虑先前是否转发过了,后续就算转发的也一定是正确内容,且转发$E \gt M \gt W \gt GRF$
  • ok,言尽于此,少年加油吧,搭完课下再来看课上的内容。跟我念P5一遍过前来还愿

课上

T1 xe

  • R型指令
    xe rd, rs, rt
    temp1 <- GPR[rs]
    temp2 <- GPR[rt]
    for i in range(0, 16):
    temp <- temp1[2*i+1] ^ temp2[2*i+1]
    GPR[rd] <- temp

T2 jtl

  • I型指令
    jtl rs, rt, label
    temp1 <- GPR[rs] + GPR[rt]
    temp2 <- PC + 4 + sign_ext(offset || 00)
    if temp1 > temp2:
    PC <- temp2
    else:
    PC <- temp1
    Nullifycation()

    T3 lwoc

  • I型指令
    lwoc rt, offset(base)
    adder <- GPR[base] + sign_ext(offset || 00)
    vadder <- adder[31: 2]
    temp1 <- Mem[vadder]
    temp2 <- temp1[3: 0]
    if temp1 < 0x80000000:
    GPR[temp2] <- temp1
    else:
    GPR[rt] <- temp1
  • D级译码一个check信号然后流水,就可以在流水线中识别有没有这条指令了
  • 这题说一嘴,我刚开始给他设置了lw的阻塞信号搭配暴力阻塞低16个寄存器,但是WA了两个sw的点,其实这里就是无脑转发的问题
    lwoc $3, 124($0)
    sw $3, 124($0)
    • 假设最后没有向$3写入,但是我们在W级才将$3修改为实际写入的寄存器,这个时候lwoc在M级别就向sw转发了一个完全错误的内容,但是没有像一般的lw一样在W级向sw再次转发,导致sw接收到错误的转发值
    • 解决方案一:刚开始就设置GRFA3Sel == `GRFA3_zero这样就不会向前转发,然后把rt和低16个寄存器堵住,等到W级知道要写什么的情况在继续即可
      assign stall_rs_E = (E_check ? (D_rs == E_rt || D_rs < 5'd16) : D_rs == E_a3) & (D_rs != 5'd0) & (Tuse_rs < E_Tnew)
      //assign stall_rt_E
      //assign stall_rs_M
      //assign stall_rt_M
    • 解决方案二;改为恰当转发,当一个指令得到最后正确的结果才会向前转发,详情可见前面讲暴力转发的时候提了一嘴
      assign stall_rs_E = (E_check ? D_rs < 5'd16 : 1'b1) & (D_rs == E_a3) & (D_rs != 0) & (Tuse_rs < E_Tnew)
      //assign stall_rt_E
      //assign stall_rs_M
      //assign stall_rt_M

      assign D_rs_val = (D_rs == 5'b0) ? 32'b0 :
      ((D_rs == E_a3) && (E_Tnew == 3'b0)) ? FWD_E :
      ((D_rs == M_a3) && (M_Tnew == 3'b0)) ? FWD_M :
      (D_rs == W_a3) ? FWD_W : D_grf_rs;
      //W级一定已经产生数据了
  • 最后怎么AC的呢,当然我考场上并没有发现可能出现的错误,但是不能完全暴力阻塞遇到这条指令则阻塞(课程组卡了一下t),所以我把$31寄存器放出来了(纯粹是因为眼缘,毕竟卡着31号寄存器大概率没啥用),所以就过了,总体而言还是暴力阻塞。