体系结构
指令集: add, sub, ori, lw, sw, beq, lui, nop, jr, jal
端口:
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
i_inst_rdata[31:0] | I | i_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] | O | M 级 PC |
w_grf_we | O | GRF 写使能信号 |
w_grf_addr [4:0] | O | GRF 中待写入寄存器编号 |
w_grf_wdata [31:0] | O | GRF 中待写入数据 |
w_inst_addr [31:0] | O | W 级 PC |
基本模块
F 级
IFU
PC & NPC
PC
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
en | I | 写使能信号, 阻塞时不可写入 NPC |
NPC[31:0] | I | 下一指令地址 |
PC[31:0] | O | 取指令地址 |
NPC
| 信号 | 方向 | 描述 |
|---|---|---|
F_PC[31:0] | I | F 级指令当地址 |
ra[31:0] | I | jr指令, 寄存器寻址, 跳转地址 |
offset[15:0] | I | 地址偏移量 |
imm[25:0] | I | J 型指令,相对 PC 寻址, 跳转地址 |
NPCSel[1:0] | I | 下一 PC 选择信号 |
Zero | I | rs 和 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] | O | i_inst_addr 对应的 32 位指令 |
FDReg
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
en | I | 写入使能信号, 阻塞时无效, 不得进入 D 级 |
clr | I | 清除信号, 阻塞时有效, 清除 F 级要进入 D 级中的信息, 插入气泡 |
F_instr[31:0] | I | F 级取出的指令 |
F_PCplus8[31:0] | I | F 级取出的指令对应的 PC + 8 |
D_instr[31:0] | O | 传入 D 级的指令 |
D_PCplus8[31:0] | O | 传入 D 级的指令对应的 PC + 8 |
D 级
RF
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
clr | I | 清空当前错误分支预测 |
WE | I | 写使能信号 |
A1[4:0] | I | 地址输入信号,读出到 RD1 |
A2[4:0] | I | 地址输入信号,读出到 RD2 |
A3[4:0] | I | 地址输入信号,写入的目标寄存器 |
WD[31:0] | I | 32 位数据输入信号 |
RD1[31:0] | O | 输出 A1 指定的寄存器数据 |
RD2[31:0] | O | 输出 A2 指定的寄存器数据 |
EXT
| 信号 | 方向 | 描述 |
|---|---|---|
imm[15:0] | I | 指令中的立即数 |
Ext_op | I | 扩展控制信号(0=无符号扩展,1=符号扩展) |
Ext_out[31:0] | O | 扩展后的结果 |
DEReg
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
clr | I | 清除信号, 阻塞时有效, 清除 D 级要进入 E 级中的信息, 插入气泡 |
D_PCplus8[31:0] | I | 本条指令对应的 PC + 8 |
D_instr[31:0] | I | D 级译码的指令 |
D_RD1[31:0] | I | 从寄存器读出的 RD1 数据 |
D_RD2[31:0] | I | 从寄存器读出的 RD2 数据 |
D_A3[4:0] | I | 本条指令目的寄存器 |
D_ext[31:0] | I | D 级立即数扩展结果 |
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 | 比较类型 |
zero | O | 比较结果 |
E 级
ALU
ALUControl编码:
| 运算 | 编码 |
|---|---|
or | 0001 |
add | 0010 |
and | 0011 |
sll16 | 0101 |
slt | 0110 |
sub | 0111 |
sltu | 1000 |
端口:
| 信号 | 方向 | 描述 |
|---|---|---|
SrcA | I | 操作数 1 |
SrcB | I | 操作数 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
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
E_PCplus8[31:0] | I | 本条指令对应的 PC + 8 |
E_instr[31:0] | I | E 级执行的的指令 |
E_ALUResult[31:0] | I | E 级 ALU 产生的结果 |
E_RD2[31:0] | I | 从寄存器读出的第二个操作数 |
E_A3[4:0] | I | 本条指令的目的寄存器 |
M_PCplus8[31:0] | O | 本条指令对应的 PC + 8 |
M_instr[31:0] | O | M 级执行的的指令 |
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] | I | load 指令 |
Dout[31:0] | O | 最终输出 |
BE
| 信号 | 方向 | 描述 |
|---|---|---|
addr[31:0] | I | 访问地址 |
data[31:0] | I | 存入内容 |
store[5:0] | I | store 指令 |
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] | I | M 级 PC |
m_data_rdata[31:0] | O | 数据存储器存储的相应数据 |
MWReg
| 信号 | 方向 | 描述 |
|---|---|---|
clk | I | 时钟信号 |
reset | I | 同步复位信号 |
M_PCplus8[31:0] | I | 本条指令对应的 PC + 8 |
M_instr[31:0] | I | M 级执行的的指令 |
M_ALUResult[31:0] | I | 传入 M 级的, 本条指令的 ALU 计算结果 |
M_RD_Mem[31:0] | I | M 级读出的 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] | I | R 指令的[5:0], funct 部分 |
MemtoReg[1:0] | O | 寄存器回写的数据来源(00=ALU 结果, 01=内存数据, 10=PCPlus8) |
NPCSel[1:0] | O | 下一 PC 选择信号 |
ALUSrc | O | ALU 第二操作数选择(0=寄存器, 1=立即数) |
RegDst[1:0] | O | 寄存器写入目标(00=rt, 01=rd, 10=0x1F) |
ExtOp | O | 扩展方式(0=无符号, 1=有符号) |
RegWrite | O | 寄存器写使能信号 |
ALUControl[3:0] | O | ALU 运算选择信号 |
t_rs[2:0] | O | 距离需要 rs 寄存器的值的周期数 |
t_rt[2:0] | O | 距离需要 rt 寄存器的值的周期数 |
t[2:0] | O | 距离产生可用数值的周期数 |
MDU_mf | O | 指令是否是 mfhi, mflo(0=否, 1=是) |
start | O | 乘除法计算开始 |
MDU_Act | O | MDU 模块激活信号, 指令为乘除法相关指令时激活 |
指令
指令集:
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] | ALUSrc | RegDst[1:0] | RegWrite | ExtOp | ALUControl | |
R(jr excluded, MDU excluded) | 00 | 00 | 0 | 01 | 1 | 0 | 根据funct |
ori,addi,and | 00 | 00 | 1 | 00 | 1 | 0 | 0001(or) |
lw, lh, lb | 01 | 00 | 1 | 00 | 1 | 1 | 0110(add) |
sw, sh, sb | 00 | 00 | 1 | 00 | 0 | 1 | 0110(add) |
beq, bne | 00 | 11 | 0 | 00 | 0 | 1 | 0111(sub) |
lui | 00 | 00 | 1 | 00 | 1 | 0 | 0101(sll16) |
jr | 00 | 10 | 0 | 00 | 0 | 0 | 0000 |
jal | 00 | 01 | 0 | 10 | 1 | 0 | 0000 |
mult, multu, div, divu | 00 | 00 | 0 | 00 | 0 | 0 | 0000 |
mfhi, mflo | 00 | 00 | 0 | 01 | 1 | 0 | 0000 |
mthi, mtlo | 00 | 00 | 0 | 00 | 0 | 0 | 0000 |
转发与阻塞:
- 考虑:
- , 如果 WA 的话, 一定要检查一下.
- 需不需要转发/被转发
- 需不需要阻塞/被阻塞
- 被阻塞怎么办
- 关于写未定寄存器的访存类
- 应该注意 E, M 级的转发与阻塞. 具体而言:
- 在 W 级已经确认寄存器, 并且和其余数据合流了.
- 而在 M 级到 W 级的时候要修正真正访问的寄存器.
- 在 E 级不要阻塞不可能访问的寄存器, 在 M 级根据确定的寄存器决定阻塞与否.
assign M_A3_F = (M_opcode == OPCODE) ? NEW_A3_VALUE : M_A3;, 所有 M 级出发的转发(除 Plus8), 都用新寄存器地址
- 应该注意 E, M 级的转发与阻塞. 具体而言:
- 关于阻塞:
- 对方写的是 0 号寄存器吗?
- 对方用的寄存器和我们写入的寄存器一样吗?
- 对方会用到这个寄存器吗? (使用时间是不是
4'hf?) - 对方使用的时间是否晚于我们这条指令可以产生数据的时间?
- 关于跳转并清空延迟槽:
- 对于分支: 更改 CMP 模块,决定分支逻辑. 清空延迟操给一个 signal.
- 对于链接: 更改 MIPS 模块,在 Plus8 转发相关判断增加这条指令的 opcode.
- 对于清空延迟槽: 在 FDReg 增加一个
FD_clr信号, 如果需要清空就激活. - 但必须注意和阻塞的配合: 当指令被阻塞在 D 级, 是不能允许 FDReg 被清零的, 否则指令会自我清理, 也就是:
signal_F = (signal & (~stall)),FD_clr = signal_F
- 关于未定写使能:
RegWrite和t均无法由指令名获得.- 因此, 我们默认打开
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实例化接口修改- 模块本身接口修改
- 模块本身内部修改
- 始末点:
- 起点: 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)).
-
转发判断
-
T 值 令 D 级、E 级、M 级、W 级分别为 0 、1 、2 、3
指令 R( jrexcluded, MDU excluded)2 1 1 ori,addi,andi2 1 X lw,lh,lb3 1 X sw,sh,sbX 1 2 beq,bneX 0 0 lui2 X X jal0 X X jrX 0 X mult,multu,div,divuX 1 1 mfhi,mflo2 X X mthi,mtloX 1 X -
转发要求:
- 即:
- 其中, 都是距离需要/可用的级别数
-
测评
关于 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:思考题
-
为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?
乘除法硬件远比加减复杂, 延迟长且难以在 ALU 中一个周期内完成, 放在 ALU 里会拖慢 CPU 的周期, 独立乘除单元可多周期运行而不影响 ALU.
并且乘法结果为 64 位, 除法有商和余数共计 64 位. 而 HI,LO 可以用于保存 64 位结果, 避免改动 ALU 和通用寄存器的互动机制. -
真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。
真实 CPU 将乘除法指令放入独立的多周期执行单元, 在后台运行, 完成后写回 HI/LO 或寄存器. 接收端通过 busy 位避免冲突, 不阻塞其他简单指令, 只阻塞乘除法相关指令.
-
请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?
我检测 D 级是否为乘除法相关指令, 并且 E 级的 MDU 是否处于 start 或 busy 状态. 如果同时满足就阻塞.
-
请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)
字节使能使所有写指令统一走一个写口, 由按字节写使能信号
byteen控制具体写哪些字节, 不必为存储类指令分别设计不同数据通路或者控制信号.逻辑更清晰且模块复用性高, 可以统一用 BE 模块处理. -
请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?
并非一字节, 都是一个字, 由模块结合指令负责指令写或者读哪些字节. 当读写指令大量为
sh,lh,sb,lb时, 按字节读写会更快. -
为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?
为了对抗复杂性, 我使用宏文件统一编写命名控制信号, 并且在整个项目中使用, 很好地提高了我项目的可读性和可维护性.
比如在 controller 中为新指令编写控制信号时, 我甚至不用关注我过去如何编码的, 只要根据我的命名规范, 使用对应的宏, 就能控制他的读写使能等信号了, 和其他的指令统一, 也不必为此修改外界数据通路. 我还为一些转发阻塞信号设置了一些起到 boolean 作用的 wire 值, 封装了复杂的逻辑判断, 防止冗长的布尔表达式影响阅读编写转发阻塞的主干内容. -
在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?
我的测试覆盖了数据的 RAW, Load-Use 冲突, 分支的控制冲突, MDU 模块的运算冲突. 通过 旁路转发(E, M, W→D, M, W→E)与 暂停(Load-Use, MDU Busy)解决. 测试中例如:
- lw → add 检验 Load-Use stall
- add, addi → beq 检验 D 级比较前的转发
- mult, div → mflo, mfhi 检验 Busy 停顿
- 多条无关指令插入,验证 MDU 运算期间流水线可继续执行
并且及时把结果
sw, 使每类冲突都有对应检查点写回内存. -
如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。
我手动构造了测试.
我首先不考虑流水线, 进行了指令的测试, 确保每一种指令本身功能正常.
随后我按照转发, 阻塞等控制, 依次测试每一种转发情景, 每一条转发线路, 每一种阻塞的可能. 最后我针对新添加的乘除法模块, 进行了额外的测试, 同样考虑到阻塞或不阻塞的情形, 依次编写.