体系结构

指令集: add, sub, ori, lw, sw, beq, lui, nop, jr, jal 端口:

信号方向描述
clkI时钟信号
resetI同步复位信号
i_inst_rdata[31:0]Ii_inst_addr 对应的 32 位指令
m_data_rdata[31:0]I数据存储器存储的相应数据
i_inst_addr[31:0]O需要进行取指操作的流水级 PC
m_data_addr[31:0]O待写入/读出的数据存储器相应地址
m_data_wdata[31:0]O待写入数据存储器相应数据
m_data_byteen[3:0]O四位字节使能
m_inst_addr[31:0]OM 级 PC
w_grf_weOGRF 写使能信号
w_grf_addr [4:0]OGRF 中待写入寄存器编号
w_grf_wdata [31:0]OGRF 中待写入数据
w_inst_addr [31:0]OW 级 PC

基本模块

F 级

IFU

PC & NPC

PC
信号方向描述
clkI时钟信号
resetI同步复位信号
enI写使能信号, 阻塞时不可写入 NPC
NPC[31:0]I下一指令地址
PC[31:0]O取指令地址
NPC
信号方向描述
F_PC[31:0]IF 级指令当地址
ra[31:0]Ijr指令, 寄存器寻址, 跳转地址
offset[15:0]I地址偏移量
imm[25:0]IJ 型指令,相对 PC 寻址, 跳转地址
NPCSel[1:0]I下一 PC 选择信号
ZeroIrs 和 rt 相等标志(1=相等, 0=不等)
PCPlus4[31:0]O当前指令地址+4
NPC[31:0]O下一指令地址

由于延迟槽, 跳转指令在 D 级时, 决定跳转与否时, 后一条指令已经取出, 也就是 PC 已经加 4, 因此偏移量无需 PC + 4 + offset, 而是 F_PC + offset
同理, J 型指令, 需要的是 PC + 4 的高四位, 只需要 F_PC 的高四位即可
jr 指令要存储的是 PC + 8

  • NPCSel编码:

    NPCSel含义
    00顺序执行
    01J 型跳转
    10JR 寄存器跳转
    11分支跳转

IM(外置)

信号方向描述
i_inst_addr[31:0]I需要进行取指操作的流水级 PC
i_inst_rdata[31:0]Oi_inst_addr 对应的 32 位指令

FDReg

信号方向描述
clkI时钟信号
resetI同步复位信号
enI写入使能信号, 阻塞时无效, 不得进入 D 级
clrI清除信号, 阻塞时有效, 清除 F 级要进入 D 级中的信息, 插入气泡
F_instr[31:0]IF 级取出的指令
F_PCplus8[31:0]IF 级取出的指令对应的 PC + 8
D_instr[31:0]O传入 D 级的指令
D_PCplus8[31:0]O传入 D 级的指令对应的 PC + 8

D 级

RF

信号方向描述
clkI时钟信号
resetI同步复位信号
clrI清空当前错误分支预测
WEI写使能信号
A1[4:0]I地址输入信号,读出到 RD1
A2[4:0]I地址输入信号,读出到 RD2
A3[4:0]I地址输入信号,写入的目标寄存器
WD[31:0]I32 位数据输入信号
RD1[31:0]O输出 A1 指定的寄存器数据
RD2[31:0]O输出 A2 指定的寄存器数据

EXT

信号方向描述
imm[15:0]I指令中的立即数
Ext_opI扩展控制信号(0=无符号扩展,1=符号扩展)
Ext_out[31:0]O扩展后的结果

DEReg

信号方向描述
clkI时钟信号
resetI同步复位信号
clrI清除信号, 阻塞时有效, 清除 D 级要进入 E 级中的信息, 插入气泡
D_PCplus8[31:0]I本条指令对应的 PC + 8
D_instr[31:0]ID 级译码的指令
D_RD1[31:0]I从寄存器读出的 RD1 数据
D_RD2[31:0]I从寄存器读出的 RD2 数据
D_A3[4:0]I本条指令目的寄存器
D_ext[31:0]ID 级立即数扩展结果
E_PCplus8[31:0]O进入 E 级的 PC + 8
E_instr[31:0]O进入 E 级的指令编码
E_RD1[31:0]O进入 E 级的 RD1 数据
E_RD2[31:0]O进入 E 级的 RD2 数据
E_A3[4:0]O进入 E 级的目的寄存器
E_ext[31:0]O进入 E 级的扩展立即数结果

CMP

信号方向描述
inA[31:0]I比较数 A
inB[31:0]I比较数 B
cond[5:0]I比较类型
zeroO比较结果

E 级

ALU

ALUControl编码:

运算编码
or0001
add0010
and0011
sll160101
slt0110
sub0111
sltu1000

端口:

信号方向描述
SrcAI操作数 1
SrcBI操作数 2
ALUControl[3:0]I运算种类控制信号
Result[31:0]O运算结果

MDU

  • 内部结构:

    • 乘除法计算组合逻辑
    • HI, LO 寄存器, counter 寄存器(记录运算周期数)
  • 端口:

    信号方向描述
    clkI时钟信号
    resetI同步复位信号
    startI乘除法运算起始信号
    SrcA[31:0]I操作数 1
    SrcB[31:0]I操作数 2
    funct[5:0]I指令类型, 与 MDU_Act 配合判断是否为某个乘除法相关指令
    MDU_ActI是否启动 MDU 运算(0=非乘除法指令, 否; 1=乘除法指令,是)
    MDUResult[31:0]O输出结果
    busyO运算处理中信号

EMReg

信号方向描述
clkI时钟信号
resetI同步复位信号
E_PCplus8[31:0]I本条指令对应的 PC + 8
E_instr[31:0]IE 级执行的的指令
E_ALUResult[31:0]IE 级 ALU 产生的结果
E_RD2[31:0]I从寄存器读出的第二个操作数
E_A3[4:0]I本条指令的目的寄存器
M_PCplus8[31:0]O本条指令对应的 PC + 8
M_instr[31:0]OM 级执行的的指令
M_ALUResult[31:0]O传入 M 级的 ALU 计算结果
M_RD2[31:0]O传入 M 级的第二个操作数
M_A3[4:0]O传入 M 级的目的寄存器序号

M 级

ByteDance

信号方向描述
addr[31:0]I访问地址
data[31:0]I取出内容
load[5:0]Iload 指令
Dout[31:0]O最终输出

BE

信号方向描述
addr[31:0]I访问地址
data[31:0]I存入内容
store[5:0]Istore 指令
byteen[3:0]O字节写使能
Din[31:0]O最终输入

DM(外置)

信号方向描述
m_data_addr[31:0]I待写入/读出的数据存储器相应地址
m_data_wdata[31:0]I待写入数据存储器相应数据
m_data_byteen[3:0]I四位字节使能
m_inst_addr[31:0]IM 级 PC
m_data_rdata[31:0]O数据存储器存储的相应数据

MWReg

信号方向描述
clkI时钟信号
resetI同步复位信号
M_PCplus8[31:0]I本条指令对应的 PC + 8
M_instr[31:0]IM 级执行的的指令
M_ALUResult[31:0]I传入 M 级的, 本条指令的 ALU 计算结果
M_RD_Mem[31:0]IM 级读出的 DM 数据
M_A3[4:0]I本条指令的目的寄存器序号
W_instr[31:0]O传入 W 级的指令
W_PCplus8[31:0]O本条指令对应的 PC + 8
W_ALUResult[31:0]I传入 W 级的, 本条指令的 ALU 计算结果
W_RD_Mem[31:0]I传入 W 级的, 本条指令读出的 DM 数据
W_A3[4:0]I传入 W 级的,本条指令的目的寄存器序号

Controller

端口:

信号方向描述
Opcode[5:0]I指令的[31:26], op 部分
Funct[5:0]IR 指令[5:0], funct 部分
MemtoReg[1:0]O寄存器回写的数据来源(00=ALU 结果, 01=内存数据, 10=PCPlus8)
NPCSel[1:0]O下一 PC 选择信号
ALUSrcOALU 第二操作数选择(0=寄存器, 1=立即数)
RegDst[1:0]O寄存器写入目标(00=rt, 01=rd, 10=0x1F)
ExtOpO扩展方式(0=无符号, 1=有符号)
RegWriteO寄存器写使能信号
ALUControl[3:0]OALU 运算选择信号
t_rs[2:0]O距离需要 rs 寄存器的值的周期数
t_rt[2:0]O距离需要 rt 寄存器的值的周期数
t[2:0]O距离产生可用数值的周期数
MDU_mfO指令是否是 mfhi, mflo(0=否, 1=是)
startO乘除法计算开始
MDU_ActOMDU 模块激活信号, 指令为乘除法相关指令时激活

指令

指令集:

add, sub, and, or, slt, sltu, lui
addi, andi, ori
lb, lh, lw, sb, sh, sw
mult, multu, div, divu, mfhi, mflo, mthi, mtlo
beq, bne, jal, jr
  • 控制信号
指令信号
MemtoReg[1:0]NPCSel[1:0]ALUSrcRegDst[1:0]RegWriteExtOpALUControl
R(jr excluded, MDU excluded)000000110根据funct
ori,addi,and0000100100001(or)
lw, lh, lb0100100110110(add)
sw, sh, sb0000100010110(add)
beq, bne0011000010111(sub)
lui0000100100101(sll16)
jr0010000000000
jal0001010100000
mult, multu, div, divu0000000000000
mfhi, mflo0000001100000
mthi, mtlo0000000000000

转发与阻塞:

  1. 考虑:
  • , 如果 WA 的话, 一定要检查一下.
  • 需不需要转发/被转发
  • 需不需要阻塞/被阻塞
  • 被阻塞怎么办
  • 关于写未定寄存器的访存类
    • 应该注意 E, M 级的转发与阻塞. 具体而言:
      • 在 W 级已经确认寄存器, 并且和其余数据合流了.
      • 而在 M 级到 W 级的时候要修正真正访问的寄存器.
      • 在 E 级不要阻塞不可能访问的寄存器, 在 M 级根据确定的寄存器决定阻塞与否.
    • assign M_A3_F = (M_opcode == OPCODE) ? NEW_A3_VALUE : M_A3;, 所有 M 级出发的转发(除 Plus8), 都用新寄存器地址
  • 关于阻塞:
    • 对方写的是 0 号寄存器吗?
    • 对方用的寄存器和我们写入的寄存器一样吗?
    • 对方会用到这个寄存器吗? (使用时间是不是 4'hf?)
    • 对方使用的时间是否晚于我们这条指令可以产生数据的时间?
  • 关于跳转并清空延迟槽:
    • 对于分支: 更改 CMP 模块,决定分支逻辑. 清空延迟操给一个 signal.
    • 对于链接: 更改 MIPS 模块,在 Plus8 转发相关判断增加这条指令的 opcode.
    • 对于清空延迟槽: 在 FDReg 增加一个FD_clr信号, 如果需要清空就激活.
    • 但必须注意和阻塞的配合: 当指令被阻塞在 D 级, 是不能允许 FDReg 被清零的, 否则指令会自我清理, 也就是: signal_F = (signal & (~stall)), FD_clr = signal_F
  • 关于未定写使能:
    • RegWritet 均无法由指令名获得.
    • 因此, 我们默认打开 RegWrite, 根据他正常写入的阶段编写t.
    • 为了应对不写入需要从 ALU 放出一个signal信号, 不满足写入置为 1. 这个信号随着指令流水到 W 级.
    • 在 W 级, signal 为 1 的时候, 将写使能关闭; 在 E,M,W 级, signal为 1 的时候, t设置为4'hf: assign W_WriteReg_F = (W_RegWrite & (~W_signal));, assign E_t = (E_signal) ? 4'hf : ...(M_t, W_t同理)
    • 或者: 在 E 级检测到signal为 1, 就把写入寄存器改为 0 号寄存器: assign E_A3_F = signal ? 5'b00000 : E_A3;
  • 一定注意加接口后, 在以下地方修改:
    • mips.v实例化接口修改
    • 模块本身接口修改
    • 模块本身内部修改
  1. 始末点:
  • 起点: DE 寄存器发出的 PC@D+8; EM 寄存器出来的 ALUResult, PC@D+8; W 级 GRF 写入前的 WD.
  • 终点: D 级 GRF 出来的 RD1, RD2; DE 寄存器出来, 进入 Mux 和 ALU 前的 SrcA, SrcB; M 级写入 DM 的 WD. 共 种路线(DE 同级转发不可能(-2); DE, EM 转发给 M 级不可能(-3)).
  1. 转发判断

    • T 值 令 D 级、E 级、M 级、W 级分别为 0 、1 、2 、3

      指令
      R(jr excluded, MDU excluded)211
      ori, addi, andi21X
      lw, lh, lb31X
      sw, sh, sbX12
      beq, bneX00
      lui2XX
      jal0XX
      jrX0X
      mult, multu, div, divuX11
      mfhi, mflo2XX
      mthi, mtloX1X
    • 转发要求:

      • 即:
      • 其中, 都是距离需要/可用的级别数

测评

关于 Editor: E:\\Microsoft\ VS\ Code\\Code.exe -r . -r -g $1:$2

    # 0. 初始化
    ori  $s0, $0, 0         # $s0 作为写入内存的地址指针 (Base Address)
    nop
 
    # 1. 基础 ALU 指令测试 (无冲突)
    # Test LUI & ORI
    lui  $t0, 0x1234        # $t0 = 0x12340000
    ori  $t0, $t0, 0x5678   # $t0 = 0x12345678
    sw   $t0, 0($s0)        # [ADDR 0x00] Expect: 0x12345678
    addi $s0, $s0, 4
 
    # Test ADD & ADDI
    addi $t1, $0, 10        # $t1 = 10
    add  $t2, $t1, $t1      # $t2 = 20
    sw   $t2, 0($s0)        # [ADDR 0x04] Expect: 0x00000014
    addi $s0, $s0, 4
 
    # Test SUB
    sub  $t3, $t2, $t1      # $t3 = 20 - 10 = 10
    sw   $t3, 0($s0)        # [ADDR 0x08] Expect: 0x0000000A
    addi $s0, $s0, 4
 
    # Test SLT
    addi $t4, $0, -1        # $t4 = 0xFFFFFFFF (-1)
    addi $t5, $0, 10        # $t5 = 10
    slt  $t6, $t4, $t5      # -1 < 10 ? True (1)
    sw   $t6, 0($s0)        # [ADDR 0x0C] Expect: 0x00000001
    addi $s0, $s0, 4
 
    # Test SLTU
    sltu $t6, $t4, $t5      # 0xFFFFFFFF < 10 ? False (0)
    sw   $t6, 0($s0)        # [ADDR 0x10] Expect: 0x00000000
    addi $s0, $s0, 4
 
 
    # 2. 数据冲突与转发测试 (Forwarding)
 
    # 2.1 ALU-ALU Forwarding (M->E, W->E)
    addi $t1, $0, 0x11
    add  $t2, $t1, $t1      # $t2 = 0x22
    # M->E & W->E Forwarding
    add  $t3, $t2, $t1      # $t3 = 0x22 + 0x11 = 0x33
    sw   $t3, 0($s0)        # [ADDR 0x14] Expect: 0x00000033
    addi $s0, $s0, 4
 
    # 2.2 Mem-ALU Forwarding (W->E)
    lw   $t1, -4($s0)       # Load 0x33 back into $t1
    nop
    add  $t2, $t1, $t1      # $t2 = 0x66
    sw   $t2, 0($s0)        # [ADDR 0x18] Expect: 0x00000066
    addi $s0, $s0, 4
 
 
    # 3. 暂停测试 (Stalling)
 
    # 3.1 Load-Use Hazard
    lw   $t1, -4($s0)       # Load 0x66
    add  $t2, $t1, $0       # 立即使用 $t1。
                            # 若暂停成功,$t2 = 0x66
    sw   $t2, 0($s0)        # [ADDR 0x1C] Expect: 0x00000066
    addi $s0, $s0, 4
 
 
    # 4. 分支与控制冲突测试
 
    # 4.1 Branch Data Hazard (需要转发到 D 级 CMP)
    addi $t1, $0, 5
    addi $t2, $0, 5
    # $t2 在 E 级,$t1 在 M 级,CMP 在 D 级进行比较
    # 如果没有转发到 D 级,比较将失败
    beq  $t1, $t2, branch_ok
    addi $t3, $0, 0xBAD     # 如果没跳转,写入错误码
    sw   $t3, 0($s0)
    jal  branch_end
 
branch_ok:
    addi $t3, $0, 0xC00D    # 跳转成功
    sw   $t3, 0($s0)        # [ADDR 0x20] Expect: 0x0000C00D
    addi $s0, $s0, 4
 
branch_end:
 
    # 4.2 JAL & JR 测试
    jal  my_function        # $ra = PC + 8
    nop                     # Delay slot (Standard MIPS) or just buffer
 
    # 返回后继续执行
    sw   $v0, 0($s0)        # [ADDR 0x24] Expect: 0x00000077 (Func result)
    addi $s0, $s0, 4
    jal  skip_func
 
my_function:
    addi $v0, $0, 0x77      # Set return value
    jr   $ra                # Return
    nop
 
skip_func:
 
 
    # 5. 访存位宽测试 (SB, SH, LB, LH)
 
    # 将 0x12345678 写入内存,然后测试部分加载
    lui  $t0, 0x1234
    ori  $t0, $t0, 0x5678   # 0x12345678
    sw   $t0, 0($s0)
 
    # LB Test (Byte 0, Little Endian -> 0x78)
    lb   $t1, 0($s0)        # Expect 0x78
 
    # SB Test (Store 0xAA to Byte 1)
    # Memory: 12 34 56 78 -> 12 34 AA 78
    addi $t2, $0, 0xAA
    sb   $t2, 1($s0)        # [ADDR 0x28] Expect: 0x1234AA78
 
    # LH Test (Load halfword lower)
    # Expect 0xAA78 -> 0xFFFFAA78
    lh   $t3, 0($s0)
 
    # 保存结果进行检查
    sw   $t3, 4($s0)        # [ADDR 0x2C] Expect: 0xFFFFAA78
    addi $s0, $s0, 8        # Skip the scratch space
 
 
    # 6. MDU 测试
 
    # 6.1 MT/MF 数据移动测试
    # 测试能否手动修改 HI/LO 寄存器
    lui  $t0, 0xAABB
    ori  $t0, $t0, 0xCCDD   # $t0 = 0xAABBCCDD
    lui  $t1, 0x1122
    ori  $t1, $t1, 0x3344   # $t1 = 0x11223344
 
    mthi $t0                # HI = 0xAABBCCDD
    mtlo $t1                # LO = 0x11223344
 
    mfhi $t2                # 读回 HI
    mflo $t3                # 读回 LO
 
    sw   $t2, 0($s0)        # [ADDR 0x30] Expect: 0xAABBCCDD (MTHI check)
    addi $s0, $s0, 4
    sw   $t3, 0($s0)        # [ADDR 0x34] Expect: 0x11223344 (MTLO check)
    addi $s0, $s0, 4
 
    # 6.2 MULT + 立即读取
    # 10 * -2 = -20 (0xFFFFFFEC)
    addi $t1, $0, 10
    addi $t2, $0, -2
 
    mult $t1, $t2
 
    # 立即读取,流水线应在此处暂停,直到 Busy 信号拉低
    # 如果没有暂停机制,这里会读到旧数据 (0x11223344)
    mflo $t3
 
    sw   $t3, 0($s0)        # [ADDR 0x38] Expect: 0xFFFFFFEC (-20)
    addi $s0, $s0, 4
 
    # 6.3 MULTU + 流水线并行性测试
    # 0x80000000 * 2
    # 2147483648 * 2 = 4294967296 (0x1_00000000) -> HI=1, LO=0
 
    lui  $t1, 0x8000        # $t1 = 0x80000000
    addi $t2, $0, 2
 
    multu $t1, $t2
 
    # 在乘法进行期间,执行无关指令,测试流水线是否继续流动
    # 理论上 CPU 不应暂停,因为没有访问 HI/LO
    addi $t4, $0, 1
    add  $t5, $t4, $t4
    nop
    nop
    nop
 
    # 此时大约过去了3-4周期,乘法还没完或者刚完
    # 此时读取,暂停时间应该比 6.2 短,或者如果不需暂停则直接通过
    mfhi $t6
    mflo $t7
 
    sw   $t6, 0($s0)        # [ADDR 0x3C] Expect: 0x00000001 (HI)
    addi $s0, $s0, 4
    sw   $t7, 0($s0)        # [ADDR 0x40] Expect: 0x00000000 (LO)
    addi $s0, $s0, 4
 
    # 检查中间的无关指令是否执行正确
    sw   $t5, 0($s0)        # [ADDR 0x44] Expect: 0x00000002
    addi $s0, $s0, 4
 
    # 6.4 DIV (有符号除法) + 余数符号测试
    # -13 / 5 = -2 ... -3
    addi $t1, $0, -13
    addi $t2, $0, 5
 
    div  $t1, $t2
 
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
 
    mflo $t3                # Quotient
    mfhi $t4                # Remainder
 
    sw   $t3, 0($s0)        # [ADDR 0x48] Expect: 0xFFFFFFFE (-2)
    addi $s0, $s0, 4
    sw   $t4, 0($s0)        # [ADDR 0x4C] Expect: 0xFFFFFFFD (-3)
    addi $s0, $s0, 4
 
    # 6.5 DIVU + 立即读取
    # 0xFFFFFFFF / 2 = 0x7FFFFFFF ... 1
    addi $t1, $0, -1
    addi $t2, $0, 2
 
    divu $t1, $t2
 
    # 流水线暂停 10 个周期
    mflo $t3
    mfhi $t4
 
    sw   $t3, 0($s0)        # [ADDR 0x50] Expect: 0x7FFFFFFF
    addi $s0, $s0, 4
    sw   $t4, 0($s0)        # [ADDR 0x54] Expect: 0x00000001
    addi $s0, $s0, 4
 
    # 结束
end:

思考题

  1. 为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?

    乘除法硬件远比加减复杂, 延迟长且难以在 ALU 中一个周期内完成, 放在 ALU 里会拖慢 CPU 的周期, 独立乘除单元可多周期运行而不影响 ALU.
    并且乘法结果为 64 位, 除法有商和余数共计 64 位. 而 HI,LO 可以用于保存 64 位结果, 避免改动 ALU 和通用寄存器的互动机制.

  2. 真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。

    真实 CPU 将乘除法指令放入独立的多周期执行单元, 在后台运行, 完成后写回 HI/LO 或寄存器. 接收端通过 busy 位避免冲突, 不阻塞其他简单指令, 只阻塞乘除法相关指令.

  3. 请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?

    我检测 D 级是否为乘除法相关指令, 并且 E 级的 MDU 是否处于 start 或 busy 状态. 如果同时满足就阻塞.

  4. 请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)

    字节使能使所有写指令统一走一个写口, 由按字节写使能信号 byteen 控制具体写哪些字节, 不必为存储类指令分别设计不同数据通路或者控制信号.逻辑更清晰且模块复用性高, 可以统一用 BE 模块处理.

  5. 请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?

    并非一字节, 都是一个字, 由模块结合指令负责指令写或者读哪些字节. 当读写指令大量为 sh, lh, sb, lb 时, 按字节读写会更快.

  6. 为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?

    为了对抗复杂性, 我使用宏文件统一编写命名控制信号, 并且在整个项目中使用, 很好地提高了我项目的可读性和可维护性.
    比如在 controller 中为新指令编写控制信号时, 我甚至不用关注我过去如何编码的, 只要根据我的命名规范, 使用对应的宏, 就能控制他的读写使能等信号了, 和其他的指令统一, 也不必为此修改外界数据通路. 我还为一些转发阻塞信号设置了一些起到 boolean 作用的 wire 值, 封装了复杂的逻辑判断, 防止冗长的布尔表达式影响阅读编写转发阻塞的主干内容.

  7. 在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?

    我的测试覆盖了数据的 RAW, Load-Use 冲突, 分支的控制冲突, MDU 模块的运算冲突. 通过 旁路转发(E, M, WD, M, WE)与 暂停(Load-Use, MDU Busy)解决. 测试中例如:

    • lw add 检验 Load-Use stall
    • add, addi beq 检验 D 级比较前的转发
    • mult, div mflo, mfhi 检验 Busy 停顿
    • 多条无关指令插入,验证 MDU 运算期间流水线可继续执行

    并且及时把结果 sw , 使每类冲突都有对应检查点写回内存.

  8. 如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。

    我手动构造了测试.
    我首先不考虑流水线, 进行了指令的测试, 确保每一种指令本身功能正常.
    随后我按照转发, 阻塞等控制, 依次测试每一种转发情景, 每一条转发线路, 每一种阻塞的可能. 最后我针对新添加的乘除法模块, 进行了额外的测试, 同样考虑到阻塞或不阻塞的情形, 依次编写.