1. 分析指令集

  • 经过分类,指令共包含R型指令和I型指令
  • 列出其对应的32位机器码,寻找共性差异

R型指令:

Type Op Rs Rt Rd Shamt Func 解释
add 000000 (5) (5) (5) 00000 100000 相加(rs+rt->rd)
sub 000000 (5) (5) (5) 00000 100010 相减(rs-rt->rd)
and 000000 (5) (5) (5) 00000 100100 rs & rt -> rd
or 000000 (5) (5) (5) 00000 100101 rs | rt -> rd
slt 000000 (5) (5) (5) 00000 101010 rs < rt ? 1 : 0(signed)
sltu 000000 (5) (5) (5) 00000 101011 rs < rt ? 1 : 0(unsigned)
mfhi 000000 00000 00000 (5) 00000 010000 HI ->rd
mflo 000000 00000 00000 (5) 00000 010010 LO -> rd
Type Op Rs Rt 0 Func 解释
mult 000000 (5) (5) 0000000000 011000 rs * rt -> (HI, LO)(signed)
multu 000000 (5) (5) 0000000000 011001 rs * rt -> (HI, LO)(unsigned)
div 000000 (5) (5) 0000000000 011010 rs / rt -> (HI, LO)(signed)
divu 000000 (5) (5) 0000000000 011011 rs / rt -> (HI, LO)(unsigned)
Type Op Rs 0 Func 解释
mthi 000000 (5) 000000000000000 010001 rs -> HI
mtlo 000000 (5) 000000000000000 010011 rs -> LO

I型指令:

Type Op Rs Rt Immediate 解释
ori 001101 (5) (5) (16) 或运算(rs|immediate->rt)
lui 001111 00000 (5) (16) 立即数加载至高16位({immediate||{16{1’b0}}}->rt)
addi 001000 (5) (5) (16) rs + immediate -> rt
andi 001100 (5) (5) (16) rs & immediate -> rt
Type Op Rs Rt Offset 解释
lw 100011 (5) (5) (16) 加载字(rs+offset在memory中data->rt)
sw 101011 (5) (5) (16) 保存字(rt->rs+offset在memory中的data)
beq 000100 (5) (5) (16) rs与rt相等则PC偏移offset*4
lb 100000 (5) (5) (16) mem中rs +offset对应字节符号拓展 -> rt
lh 100001 (5) (5) (16) mem中rs +offset对应半字符号拓展 -> rt
sb 101000 (5) (5) (16) rt[7:0] -> mem(offset + rs)
sh 101001 (5) (5) (16) rt[15:0] -> mem(offset + rs)
bne 000101 (5) (5) (16) 不相等则跳转

J型指令

type Op instr_index 解释
jal 000011 (26) 跳转至index并且PC+4 -> $ra
type Op rs 0 0 func 解释
jr 000000 (5) {10{1’b0}} {5{1’b0}} 001000 跳转至$rs中的地址

2. 阶段分析


F阶段PC值已经确定,进行的是根据PC访存相应的Instruction操作。此时指令已经确定好了
D阶段是将instruction转换成对应的操作数、产生控制信号。
E阶段是将操作数转换成计算结果。
M阶段是方寸数据内存。
W阶段是写入GRF。

3.命名规则

采用首字母大写命名法,每个单词首字母大写。

4. PC设计

端口设计:

方向 name 位宽
input PcOp [3:0]
input BeqPc [31:0]
input JalPc [31:0]
input JrPc [31:0]
input reset 1
input clk 1
output Pc_F [31:0]
input BnePc [31:0]

转移设计:

PcOp 解释
0000 Pc_F <= Pc_F + 4
0001 Pc_F <= BeqPc
0010 Pc_F <= JalPc
0011 Pc_F <= JrPc
0100 Pc_F <= BnePc

5. CU控制信号分析

端口设计:

type name 位宽 解释
input Op [5:0] 机器码高6位
input Func [5:0] 机器码低6位
output RegDst [1:0] GRF被写入寄存器是rt还是rd。0是rt,1是rd,10是$ra
output AluSrc [1:0] ALU第二个运算值是rt还是立即数。0是rt,1是立即数
output MemToReg [1:0] GRF写入值是ALUans还是DMans。0是ALUans,1是DMans,10是PC + 4
output RegWrite 1 是否写入GRF
output MemWrite 1 是否写入RAM
output PcSel [3:0] 当前指令是否是beq\jal\jr
output ExtOp [7:0] 扩展操作类型类型,详见Extender
output AluOp [7:0] ALU运算类型,详见ALU
output InstrType [7:0] 指令类型
output TNew_D [3:0] 从D级开始,经过多少周期该指令产生运算结果
output TUse1_D,TUse2_D [3:0] 从D级开始,经过多少周期要用到运算结果
output Multiply_D 1 是否进行乘法操作
output Divide_D 1 是否进行除法操作
  • 内部电路采用最小项表达式判断法,先判断Op,再判断Func

每种指令所需控制信号:

RegDst AluSrc MemToReg RegWrite MemWrite PcSel ExtOp AluOp TNew TUse1 TUse2
add 01 00 00 1 0 0000 ADD 2 1 1
sub 01 00 00 1 0 0000 SUB 2 1 1
ori 00 01 00 1 0 0000 0000 OR 2 1 10
lui 00 01 00 1 0 0000 0010 ADD 2 10 10
lw 00 01 01 1 0 0000 0001 ADD 3 1 10
sw 11 01 0 1 0000 0001 ADD 0 1 2
beq 11 00 0 0 0001 0011 EQUAL 0 0 0
jal 10 10 1 0 0010 0100 0 10 10
jr 11 00 0 0 0011 ADD 0 0 10

6. 流水线寄存器存储信息分析

F级:

name 位宽
Pc_F [31:0]
Instruction_F [31:0]

D级:

name 位宽
Pc_D [31:0]
RD1_D [31:0]
RD2_D [31:0]
WR_D [4:0]
ExtAns_D [31:0]
MemToReg_D [1:0]
RegWrite_D 1
AluSrc_D [1:0]
MemWrite_D 1
AluOp_D [7:0]
InstrType_D [7:0]
TNew_D [7:0]
Addr1_D [4:0]
Addr2_D [4:0]

E级:

name 位宽
Pc_E [31:0]
AluAns_E [31:0]
WR_E [4:0]
RD2_E [31:0]
MemToReg_E [1:0]
RegWrite_E 1
MemWrite_E 1
InstrType_E [7:0]
TNew_E [7:0]
Addr2_E [4:0]

M级:

name 位宽
Pc_M [31:0]
AluAns_M [31:0]
WR_M [4:0]
DmAns_M [31:0]
MemToReg_M 1
RegWrite_M 1
InstrType_M [7:0]
TNew_M [7:0]

W级:

name 位宽
Pc_W [31:0]
AluAns_W [31:0]
WR_W [4:0]
DmAns_W [31:0]
MemToReg_W [1:0]
RegWrite_W 1
InstrType_W [7:0]
TNew_W [7:0]

7. 流水线转发数据

E级:

instruction Data_E
add 0
sub 0
ori 0
lui 0
beq 0
jal Pc_E + 8
jr 0
lw 0
sw 0

M级:

instruction Data_M
add AluAns_M
sub AluAns_M
ori AluAns_M
lui AluAns_M
beq 0
jal Pc_M + 8
jr 0
lw 0
sw 0

W级:

instruction Data_W
add AluAns_W
sub AluAns_W
ori AluAns_W
lui AluAns_W
beq 0
jal Pc_W + 8
jr 0
lw DmAns_W
sw 0

8. 测试方案

针对每一条新添加的指令都进行测试,确保有符号、无符号的正确性,以及周期数的正确。

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
ori $t0 12
ori $t1 21
and $t2 $t0 $t1
add $t3 $0 $t2
ori $t0 16
ori $t1 25
or $t2 $t0 $t1
add $t3 $t2 $0
addi $t0 $t0 -1
addi $t0 $t0 -1
addi $t0 $t0 -2
andi $t2 $t0 121
andi $t2 $t2 21
slt $t3 $0 $t2
addi $t1 $0 -1
slt $t3 $t1 $0
slt $t3 $0 $0
addi $t2 $0 -2
slt $t3 $t1 $t2
slt $t3 $t2 $t1
sltu $t3 $t1 $t2
ori $t4 $0 1000
slt $t3 $t1 $t4
sltu $t3 $t1 $t4
sltu $t3 $t3 $t1
bne $0 $0 label
addi $t0 $0 1
addi $t0 $0 2
addi $t0 $0 3
bne $0 $t0 label
addi $t0 $0 4
addi $t0 $0 5
addi $t0 $0 6
label:
addi $t0 $0 3
addi $t1 $0 -2
addi $t2 $0 2
mult $t0 $t1
multu $t0 $t1
mult $t0 $t2
div $t0 $t1
divu $t0 $t1
div $t0 $t2
mfhi $t0
addi $t0 $t0 1
mflo $t0
addi $t0 $t0 3
mfhi $t0
beq $t0 $0 label
addi $t1 $0 3
addi $t1 $0 7
mthi $t1
mfhi $t2
mult $t1 $t2
mtlo $t3
mflo $t3
and $t3 $t3 $t2
addi $t0 $0 -3
addi $t1 $0 1
addi $t2 $0 2
addi $t3 $0 3
sb $t0 0($0)
sb $t0 0($t1)
sb $t0 0($t2)
sb $t0 0($t3)
sh $t0 4($0)
sh $t0 8($t2)
lb $t4 4($0)
addi $t4 $t4 2
lb $t4 4($t2)
addi $t4 $t4 13
lh $t4 4($0)
addi $t4 $t4 52
ori $t0 $0 5
ori $t1 $0 34
sub $t0 $0 $t0
sub $t1 $0 $t1
mult $t1 $t0
mfhi $t2
mflo $t3

3508000c
35290015
01095024
000a5820
35080010
35290019
01095025
01405820
2108ffff
2108ffff
2108fffe
310a0079
314a0015
000a582a
2009ffff
0120582a
0000582a
200afffe
012a582a
0149582a
012a582b
340c03e8
012c582a
012c582b
0169582b
14000007
20080001
20080002
20080003
14080003
20080004
20080005
20080006
20080003
2009fffe
200a0002
01090018
01090019
010a0018
0109001a
0109001b
010a001a
00004010
21080001
00004012
21080003
00004010
1100fff1
20090003
20090007
01200011
00005010
012a0018
01600013
00005812
016a5824
2008fffd
20090001
200a0002
200b0003
a0080000
a1280000
a1480000
a1680000
a4080004
a5480008
800c0004
218c0002
814c0004
218c000d
840c0004
218c0034
34080005
34090022
00084022
00094822
01280018
00005010
00005812

9. 思考题汇总

  1. 因为乘除法所需的时钟周期不同,分别是5和10,而一般的ALU操作的时钟周期都是1。如果将乘除功能整合进ALU,会阻塞其他指令的正常进行,使得总周期数变长,指令效率变低。而独立出来则可以与ALU分别计算,增加指令执行效率。需要额外增加HI、LO是因为乘除法的计算结果需要保留,直到reset或者被重新计算新的乘除结果。如果不加,那就会被其他的计算覆盖掉,导致mfhi、mflo指令来不及生效。
  2. 从80486时代开始,CPU有了专用的乘法器、移位器运算单元,这些单元可以高效处理乘除法。乘法器通常把乘法运算拆分成多个步骤,例如分解成部分积生成、部分积相加、最终结果输出等几个阶段。CPU也有专用的除法器用来实现除法操作。通常采用特定的算法(Radix-2算法)来实现。
  3. 可以把乘法、除法运算当作Moore型有限状态机来处理,start和busy信号是输出。当E级的start或busy信号置1同时D级是乘除法相关的指令时,需要阻塞。
  4. 好处有很多:sw、sh、sb三种store型指令都可以用这个byteen信号来调控写入的字节段,不用为了每种指令单独设计写入信号,起到了统一的作用。同时byteen的每一个1都对应了一个专属的字节,这样可以清晰的写入,不怕混淆。
  5. 并不是一字节,而是一字,即四字节。当每次只写入或读出一字节时,按字节读写的效率会高于按字读写。
  6. 我对指令采取了归类的方式,先将具体的小指令提取共性归为同一类型的指令,再根据该类型的指令的特点来驱动输出信号。例如mult和multu指令分为乘法类,用来驱动Multiply_D信号,div和divu指令分为除法类等。这种手段在译码的时候能够降低指令的复杂度,减少操作次数,更清楚的分类分层。同时在阻塞和转发的时候也根据每一级的指令类型来决定转发的数据,这有助于在处理数据冲突时降低复杂度,封装结果。
  7. 有很多冲突,例如lb和bne的冒险,结构冒险,控制冒险等。解决方法就是AT法判断阻塞+无脑转发。相关测试样例在测试方案中包含了。
  8. 我是手动构造的样例。首先测试指令的基本功能,如果该指令是有符号数,那么还要测试负数的情况。之后再考虑转发和阻塞,如果该指令很早就要用到操作数(例如bne),那么就在让上一条指令写入该指令需要用的寄存器,测试阻塞功能。同时也要测试该条指令能否正常转发,因此如果该指令写入某一寄存器,那么下一条指令就要对该寄存器进行操作,验证是否转发成功。