计组P6设计文档

P6建议:在P5基础上添加乘除槽即可

源代码:solor-wind/BUAA_CO (github.com)

设计要求

处理器应为五级流水线设计,支持如下指令集:

1
2
3
4
5
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
  1. 直接通过顶层模块 mips.v 的 output 端口传出相应信号,不允许出现 display 语句,具体要求见“在线测试相关说明”小节。

  2. 要求存储器外置,即将 IM 和 DM 放置在 CPU 之外。P6 的 IM 和 DM 两个模块被内置于官方评测的 testbench 中(官方使用的 tb 已在“存储器外置”小节公开),不再需要大家自行实现 IM 和 DM 模块。官方 tb 中实现的指令存储器(IM,instruction memory)和数据存储器(DM,data memory)的容量如下:

    • IM:容量为 16KB (32bit/word × 4096word)

    • DM:容量为 12KB (32bit/word × 3072word)

  3. 需有单独的乘除法模块数据扩展模块,我们会分别在“乘除模块”和“支持按字节访存”两个小节中中予以详细说明。

  4. 最外层的 mips 模块的文件名必须为 mips.v ,该文件中的 module 也必须命名为 mips

架构

模块规格

命名规则

流水线信号共5级,IF/ID ID/EX EX/MEM MEM/WB,两个寄存器间的信号用 级别_数据/功能(_去向) 命名。

共有信号:

变量 方向 位宽 解释 来源/去向
clk in 1 时钟信号 顶层输入
rst in 1 复位信号 顶层输入
xx_pc in [31:0] 当前级的pc 上一级(IF为out)
xx_pc_yy out [31:0] 要传给下一级的pc 下一级yy(WB无)
xx_instr in [31:0] 当前级的指令 上一级
xx_instr_yy out [31:0] 要传给下一级的指令 下一级yy(WB无)

IF

变量 方向 位宽 解释 来源/去向
stall in 1 阻塞信号,使pc保持不变 staller
IF_j in 1 是否跳转 ID_j_IF
IF_pc4 in [31:0] 跳转指令地址 ID_pc_IF
IF_instr in [31:0] 从tb获取的指令 tb
IF_pc out [31:0] IF级当前指令 tb

ID

接口
变量 方向 位宽 解释 来源/去向
stall in 1 阻塞信号,清空D级 staller
WB_pc_ID in [31:0] 写入指令的pc WB_pc_ID
ID_we in 1 寄存器写入信号 WB_we_ID
ID_addr in [4:0] 要写入的寄存器 WB_addr_ID
ID_data in [31:0] 要写入的值 WB_data_ID
ID_rs_sign in [2:0] 转发选择,0为原值
ID_rt_sign in [2:0] 转发选择,0为原值
ID_rs_data in [31:0] 转发值 EX_out,EX_data_WB,MEM_data_WB
ID_rt_data in [31:0] 转发值
ID_rs_base out [31:0] rs寄存器的值 EX_rs_base
ID_rt out [31:0] rt寄存器的值 EX_rt
ID_j_IF out 1 跳转指示信号 IF_j
ID_pc_IF out [31:0] 跳转指令地址 IF_pc4
内部变量
1
2
3
wire [31:0] ID_rs_use;//搭配转发使用
wire [31:0] ID_rt_use;//搭配转发使用
reg [31:0] grf [0:31];//32个寄存器

EX

接口
变量 方向 位宽 解释 来源/去向
EX_rs_base in [31:0] rs寄存器的值 ID_rs_base
EX_rt in [31:0] 参与运算的rt寄存器的值,可能从转发处来
EX_new out 1 表示E级产生了新的要写入寄存器的值 instr
EX_addr out [4:0] E级产生新值的寄存器地址 instr
EX_out out [31:0] 运算结果 MEM_addr
EX_rt_MEM out [31:0] rt寄存器的值 MEM_data
busy out 1 mul是否正忙 mul
内部变量
1
2
3
4
5
6
reg [31:0] out;//即时给出运算结果,上升沿时赋给EX_out
reg [31:0] EX_tmp_new;//同上,EX_new
reg [4:0] EX_tmp_addr;//EX_addr
wire [2:0] start;//从E级指令给出mul所需信号
wire [31:0] hi;//
wire [31:0] lo;
内部模块——mul
变量 方向 位宽 解释 来源/去向
clk in 1 时钟信号 EX
rst in 1 复位信号 EX
start in [2:0] 开始运算信号 EX
A in [31:0] 运算数rs EX_rs_base
B in [31:0] 运算数rt EX_rt_use
busy out 1 正忙信号 busy
hi out [31:0] 乘法高位/除法余数 out
lo out [31:0] 乘法低位/除法的商 out

MEM

接口
变量 方向 位宽 解释 来源/去向
MEM_addr in [31:0] 要读出的地址 EX_out
MEM_data in [31:0] 读出的数据 m_data_rdata
MEM_new out 1 表示M级有了新的要写入寄存器的值 instr
MEM_new_addr out [4:0] M级新值的寄存器地址 instr
MEM_data_WB out [31:0] 传向下一级的数据 WB_lw_data
EX_data_WB out [31:0] 运算结果 WB_alu_data
内部变量
1
2
reg [2:0] op;//位扩展选择信号
wire [31:0] data_out;//原始读出数据
内部模块
变量 方向 位宽 解释 来源/去向
A in A 读出地址的低位 MEM_addr
Din in Din 原始数据 MEM_data
Op in Op 扩展选择信号 op
Dout out Dout 扩展后的数据 MEM_data_WB

WB

输出均不是reg型

变量 方向 位宽 解释 来源/去向
WB_lw_data in [31:0] 从内存取出的数 MEM_data_WB
WB_alu_data in [31:0] 运算结果 EX_data_WB
WB_we_ID out 1 寄存器写入信号 ID_we
WB_addr_ID out [4:0] 要写入的寄存器地址 ID_addr
WB_data_ID out [31:0] 要写入寄存器的值 ID_data

冒险处理

转发

转发共5个接受:

1
2
3
4
5
wire [31:0] ID_rs_data;
wire [31:0] ID_rt_data;
wire [31:0] EX_rs_base;
wire [31:0] EX_rt;
wire [31:0] MEM_data;

共2处提供:

1
2
wire [31:0] EX_out;
wire [31:0] WB_data_ID;

其中,2处提供接口还给出new信号与addr,代表新值产生且可用、要写入的寄存器编号

接受者只需判断new与addr即可决定转发与否(有新值且可用就转发)

阻塞

\(T_{use}\)\(T_{new}\) 的计算见表格

采用Time模块传递计算结果,[31:0] use_new 具体每位对应结果如下表

[2][1][0]分别对应rs、rt、rd

rs rt rd [4] [2] [1] [0]
-> use或new的数值 <- 1代表正在算乘除法 -> 0为use,1为new <-

\(T_{use}<T_{new}\) ,或乘除类指令遇上busy,则执行阻塞操作:

  1. 冻结IF/ID

  2. 清除ID/EX

  3. 禁止PC

注意事项

  • 从W级转发到D级,采用D级内部转发(如beq)

  • 转发时如果地址是0寄存器应剔除

添加指令

  1. 添加到const里

  2. 填写TIME表格,根据use和new更改M级中的MEM_new与MEM_new_addr,添加Time中的use与new值

  3. 根据操作在每个模块添加行为

    • IF:指令获取与延迟槽

    • ID:给出跳转指令

    • EX:各种计算行为、存储地址计算

    • MEM:给出写入内存的地址、byte信号,对读出的数据进行加工

    • WB:给出写寄存器信号、寄存器编号、数据

  4. 每类指令对应行为(P5)

    • cal:修改EX计算过程、添加WB回写信号,加入相关阻塞指令中(带cal的),加入相关转发指令中(同上,注意供需接口都有)。

    • lw:修改EX计算过程、添加WB回写信号,……

    • sw:修改EX计算过程、MEM写入,………

    • j:修改ID跳转判断与跳转地址计算,加入阻塞、转发指令

    • jal:修改ID跳转判断与跳转地址计算、EX算pc等、WB回写信号,阻塞转发

  5. 是否有新的转发通路、阻塞可能

思考题与测试

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

    乘除法相较其他运算,复杂性更高且时间更长(延迟更大),与ALU分离有利于保持模块的清晰关系、缩短时钟周期。便于在本级存储运算结果而不是回写到寄存器,否则其他使用寄存器的指令均需要被阻塞,性能下降严重。

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

    类似于竖式乘法,乘数最低位*被乘数并累加右移直至每位乘数都算完,32位需5次加法-100周期的时间。

    类似于算式除法,每次尝试减去能减去的最大值(含比较过程),重复计算得商、余数

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

    当D级是乘除类指令且E级是乘除类指令或busy时,阻塞

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

    可读性好,当前执行的指令一目了然。字、半字、字节的处理具有统一性。

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

    不是。写入低位时,按字读和按字写效率近似,写入低位效率高于写入高位

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

    • const中定义指令编码,减少代码量、提高易读性

    • time计算指令单独成一模块,采用列举法,便于新增指令

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

    见转发

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

测试部分

1
2
3
4
5
6
7
8
9
10
11
ori $4,$0,4
sw $4,-4($4)
lw $2,0($0)
sw $2,0($2)s

ori $2,$2,4
ori $3,$3,8
ori $4,$4,12
sw $2,0($0)
sh $3,6($0)
sb $4,11($0)
part1——P4的数据测试
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
.data
.space 0x3000
.text
ori $0,$0,0
ori $2,$0,2
ori $3,$0,3
ori $4,$0,4
ori $5,$0,5
ori $6,$0,6
ori $7,$0,7
ori $8,$0,8
ori $9,$0,9
ori $10,$0,10
ori $11,$0,11
ori $12,$0,12
ori $13,$0,13
ori $14,$0,14
ori $15,$0,15
ori $16,$0,16
ori $17,$0,17
ori $18,$0,18
ori $19,$0,19
ori $20,$0,20
ori $21,$0,21
ori $22,$0,22
ori $23,$0,23
ori $24,$0,24
ori $25,$0,25
ori $26,$0,26
ori $27,$0,27
ori $28,$0,28
ori $29,$0,29
ori $30,$0,30
ori $31,$0,0x310c


ori $0,$0,1#$0应始终为0
ori $2,$0,65535
ori $3,$0,0
ori $4,$0,12
lui $5,65535
lui $6,0
lui $7,8
add $8,$5,$2#-1
lui $9,32767
add $9,$9,$2#intmax
add $10,$9,$8
sub $10,$8,$9#-intmax-1
add $10,$0,$10
sub $11,$2,$4#65523
sub $12,$4,$2#-65523
add $13,$11,$12#0
sub $14,$5,$5#0

lui $18,0
ori $18,$18,0x2ffc
sw $10,($18)
sw $11,12280($0)
sw $12,-4($4)#8
lw $15,8($0)#$12,-65533
lw $16,12284($0)#$10,-intmax-1
lw $17,12280($0)#11,65523

loop:
lui $21,0
ori $21,$21,1
add $19,$19,$21
beq $19,$20,loop#$19=21跳出

sub $22,$0,$20#$22=-20
loop2:
beq $22,$20,loop2
add $22,$0,$20
beq $22,$20,end
nop
end:
add $23,$23,$21

loop3:
jr $ra
nop
jal loop3#往前跳
nop
jal loop4#往后跳
loop4:
nop
beq $24,$21,end2
add $24,$0,$21
add $25,$0,$ra
jr $25
end2:
nop
jal end2#原地蹦跶
nop
part2——冒险测试

\(A_9^4\)

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
#add,sub,ori,lui,lw,sw,beq,jal,jr
#先把每个寄存器里的值搞成非0
add $2,$2,$2
add $2,$2,$2
sw $2,-4($2)
sub $3,$2,$2
ori $2,$3,8
lw $4,4($0)
sub $5,$4,$2

lui $6,1
ori $6,$6,1
sw $6,0($0)
lw $7,0($0)
ori $7,$7,2

lw $8,0($0)
sw $8,8($0)

loop:
add $9,$10,$9
beq $9,$19,loop
ori $11,$0,1
jal loop2
add $12,$11,$11
loop2:
sw $ra,12($0)
ori $ra,$0,0
lw $30,12($0)
jr $30
nop
part3——新增指令测试

重点测试边缘数据

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
57
58
59
60
61
62
63
64
65
and $1,$2,$2
or $3,$4,$3
sub $5,$5,$6
slt $7,$5,$6
sltu $8,$5,$6
addi $9,$9,-1
andi $10,$10,-1
sb $11,-1($4)
sh $11,0($0)
lw $12,0($0)
lui $11 0x1111
ori $11 0x1111
sh $11,2($4)
lb $13,3($4)
lh $14,2($4)

mult $11,$4
mfhi $15
multu $11,$4
mfhi $16
div $11,$4
mflo $17
divu $11,$4
mflo $18
mthi $19
mtlo $20

lui $20,0
ori $20,1
loop:
bne $19,$21,loop
sub $21,$21,$20
nop
//重点测试符号乘除
lui $2,0x1111
ori $2,$2,0x1111
add $3,$2,$0
mult $2,$3
mfhi $4
mflo $5
multu $2,$3
mfhi $6
mflo $7

ori $8,$0,13
add $2,$2,$8
div $2,$8
mfhi $9
mflo $10
div $8,$2
mfhi $11
mflo $12
div $2,$3
mfhi $13
mflo $14
divu $2,$8
mfhi $15
mflo $16
divu $8,$2
mfhi $17
mflo $18
divu $2,$3
mflo $19
mflo $20


计组P6设计文档
https://solor-wind.github.io/2024/01/23/计组P6设计文档/
作者
gpf
发布于
2024年1月23日
许可协议