RooKie_Z P4 Verilog单周期CPU设计文档

RooKie_Z P4 Verilog单周期CPU设计文档

写在前面

这是RooKie_Z的P4单周期CPU设计文档,在课上测试中本CPU取得了 满分💯的成绩。

总体设计概述

本次要求实现的指令集为 add, sub, ori, lw, sw, beq, lui, jal, jr, nop,要求与P3相似,考虑到我P3已经实现了这些指令,并且经过课上测试,感觉CPU总体架构模块化较为清晰完备,故本次暂不重构直接翻译Logisim电路

同样的,与P3类似本次依然做好了实现 各类branch,jump,link指令和诸如lb,lbu,lh,lhu,sb,sh等指令的工作,以备不时之需,但相较Logisim,显然Verilog里面的指令可以更加花哨,这时就比较考验写组合逻辑电路的手法了


整体架构仍与与肖利民老师课件中的图类似


ISE完成后整体文件树形图如下

仿真模板如下


关键模块介绍

命名规则

  • 在顶层模块 mips.v,内,所有实例化元件的命名均为大写字母开头的模块名,如 Ifu, Npc, Alu 等。
  • wire类电线命名仍采用小驼峰法与下划线结合的方式,力求可读性强,便于扩展,分为几类讨论,如在顶层模块mips.v内采用小驼峰命名法,在不产生重复误会的前提下,力求wire名称与实例化后的模块端口名称一致,而在对于易混信号,则采用类似 grf_rd1,RD1_rs,这样的命名法加以区别,在 CTRL 内,对于判断具体指令,则采用指令相同的命名,如add,sub……
  • 控制信号如果是控制元件的操作类型以Op结尾,如ALUOpDMOp等;如果是多路选择器的控制信号,则以Sel结尾,如A3SelWDSel等;
  • 端口命名与P3相同
  • 控制信号的宏定义命名为元件名+下划线+功能,如ALU_addNPC_jalDM_wDM_h
  • opcodefunct信号的宏命名为OP/FUNC+下划线+小写指令名,如 OP_beqFUNC_add

宏定义介绍

“工欲善其事必先利其器”,好的宏定义是规范CPU搭建的前提,否则指令一多,程序可读性便急剧降低。

因此,本次搭建CPU时特意建立了一个 signal_def.v 的宏定义文件,来统筹管理各个信号定义,具体分为一下几部分

  • OP_Code and Func_Code,管理各指令的指令编码,便于识别;
  • GRF signals,管理有关GRF的控制信号,包括 WDSelA3Sel 两块;
  • DM signals,管理DM读写方式的信号,主要供 DMOp 使用;
  • EXTOp, 管理扩展方式;
  • NPCOp,管理NPC生成方式;
  • CMPOp, 管理CMP的比较方式;
  • ALUSrcB,管理B选择数据来源;
  • ALUOp,管理ALU的计算方式;

至此,宏定义部分结束,课上添加指令时一定要看好并在signal_def.v的文件里进行增添!

Warningdefine后不要加分号!!!define生成的宏使用时要加 “`”!!!


综合考量了课上进行功能拓展的方便程度和适宜Verilog语法的需求,我对于P3的CPU中的模块进行了重新排列组合,并且参考当时CPU中BranchDefine子电路的思想,新增了CMP模块,来控制分支信号。

GRF

直接翻译P3电路

端口说明

端口名称 方向 描述
A1[4:0] I 5位地址信号,将其对应寄存器中的值输出到RD1
A2[4:0] I 5位地址信号,将其对应寄存器中的值输出到RD2
A3[4:0] I 5位地址信号,将WD输入的值写入其对应的寄存器中
WD[31:0] I 32位输入信号,将值写入A3对应的寄存器中
WE I 1位写使能信号,高电平写入,低电平无事发生
RD1[31:0] O 32位输出值,输出A1对应寄存器中储存的值
RD2[31:0] O 32位输出值,输出A2对应寄存器中储存的值
clk I 时钟信号
reset I 同步复位信号,高电平将所有寄存器复位到0,低电平无事发生

相关控制信号说明

1、A3Sel
控制信号值 功能
A3Sel_rd 选择待写入寄存器地址来自Instr[15:11] (R Type)
A3Sel_rt 选择待写入寄存器地址来自Instr[20:16] (I Type)
A3Sel_ra 选择写入寄存器的地址为31($ra) (含link的指令)
2、WDSel
控制信号值 功能
WDSel_DMout 选择写入寄存器的数据来自DM (lw sw等指令)
WDSel_ALUout 选择写入寄存器的数据来自ALU运算结果
WDSel_PC4 选择写入寄存器的数据为PC+4 (含link的指令存$ra的值)

EXT

16位二进制数进行零扩展符号扩展到32位

控制信号说明

控制信号值 功能
EXT_unsigned 零扩展
EXT_signed 符号扩展

IFU(指令读取单元)

与P3不同,考虑到NPC需要便于维护,改为将IMPC合在一起组成IFU

端口说明

端口名称 方向 描述
NPC[31:0] I 待写入PC的指令地址
clk I 时钟信号
reset I 同步复位信号,高电平复位PC到32’h0000_3000
PC O 当前指令地址
Instr[31:0] O 32位的指令值
rs[4:0] O $rs地址
rt[4:0] O $rt地址
rd[4:0] O $rd地址
imm16[15:0] O 16位立即数
imm[31:0] O 32位立即数,实际上取26位
func[5:0] O 6位func
opcode[5:0] O 6位opcode
shamt[4:0] O 5位位移量,用于sll,srl等位移指令

ALU

端口说明

端口名称 方向 功能描述
A[31:0] I 32位输入运算数A
B[31:0] I 32位输入运算数B
shamt[4:0] I 移位指令所移位数
ALUOp[4:0] O 控制信号
ALUout[31:0] O 32位输出运算结果

控制信号说明

1. ALUOp
控制信号值 功能
ALU_add 执行加法运算
ALU_sub 执行减法运算
ALU_or 执行逻辑或运算
ALU_lui 执行lui指令
2. ALUBSel
控制信号值 功能
SrcB_rt 选择寄存器中的值进行运算
SrcB_imm 选择立即数进行运算

CMP(分支比较)

参考P3中CPU内BranchDefine子电路生成的CMP模块,来生成jump信号,判断分支是否跳转,link是否写入

目前实现了beq, bne, bgtz, bltz等指令

代码实现:

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
`timescale 1ns / 1ps
`include "signal_def.v"

module CMP(
input [31:0] rs,
input [31:0] rt,
input [2:0] CMPOp,
output reg jump
);

wire eq = (rs == rt);
wire ne = !eq;
wire ltz = ($signed(rs) < 0);
wire gtz = ($signed(rs) > 0);
wire eqz = (rs == 0);

always @(*) begin
case (CMPOp)
`CMP_beq: begin
jump = eq ? 1 : 0;
end
`CMP_bne: begin
jump = ne ? 1 : 0;
end
`CMP_bltz: begin
jump = ltz ? 1 : 0;
end
`CMP_bgtz: begin
jump = gtz ? 1 : 0;
end
default: jump = 1'b0;
endcase
end
endmodule

端口说明

端口名称 方向 功能描述
rs[31:0] I $rs寄存器的值
rt[31:0] I $rt寄存器的值
CMPOp[2:0] I 控制信号
jump O 指示分支是否跳转

NPC(次地址计算)

有了CMP之后,NPC的功能也更加简洁,只需根据NPCOpjump信号输出NPC信号的值就行

端口说明

端口名称 方向 功能描述
PC[31:0] I 32位输入当前地址
imm[31:0] I 32位立即数
jump I 指示b类型指令是否跳转
NPCOp[2:0] I 控制信号
RD1_rs[31:0] I $ra寄存器保存的32位地址
NPC[31:0] O 32位输出次地址
PC4[31:0] O 输出$PC+4$的值

控制信号说明

控制信号值 功能
NPC_PC4 $NPC = PC + 4$
NPC_branch 执行branch指令
NPC_jal 执行jjal指令
NPC_jalr 执行jalrjr指令

DM(数据储存器)

端口说明

信号名称 方向 功能描述
Addr[31:0] I 待操作的内存地址
WD[31:0] I 待写入内存的值
clk I 时钟信号
reset I 同步复位信号
DMWr I 写使能信号;1:写入有效;0:写入无效
DMOp[2:0] I 控制信号
DMout[31:0] O 输入地址指向的内存中储存的值

控制信号说明

控制信号值 功能
DM_w 对应lwsw指令,写入或读取整个字
DM_h 对应lhsh指令,写入或读取半字
DM_b 对应lbsb指令,写入或读取整个字
DM_hu 对应lhu指令
DM_bu 对应lbu指令

数据通路分析

指令 opcode funct NPCOp A3Sel WDSel EXTOp GRFWE ALUSRCB ALUOp DMWr DMOp
add 000000 100000 NPC_PC4 A3Sel_rd WDSel_ALUout X 1 SrcB_rt ALU_add 0 X
sub 000000 100010 NPC_PC4 A3Sel_rd WDSel_ALUout X 1 SrcB_rt ALU_sub 0 X
ori 001101 X NPC_PC4 A3Sel_rt WDSel_ALUout EXT_unsigned 1 SrcB_imm ALU_or 0 X
lw 100011 X NPC_PC4 A3Sel_rt WDSel_DMout EXT_signed 1 SrcB_imm ALU_add 0 DM_w
sw 101011 X NPC_PC4 X WDSel_DMout EXT_signed 0 SrcB_imm ALU_add 1 DM_w
beq 000100 X NPC_branch X X X 0 X X 0 X
lui 001111 X NPC_PC4 A3Sel_rt WDSel_ALUout X 1 SrcB_imm ALU_lui 0 X
jal 000011 X NPC_jal A3Sel_ra WDSel_PC4 X 1 X X 0 X
jr 000000 001000 NPC_jalr X X X 0 X X X X

调试过程记录

Warnings

  • 首先是要看清楚RTL语言,这是最重要的。比如说最经典的就是beq指令后面的offset这个偏移量实际上要左移再加上$PC + 4$;
  • 再就是要区分RTL语言中的符号,比如说注意区分 rt$ rt ,前者代表 rt 的地址,后者代表 GPR($rt)(即$rt对应寄存器的值)

测试方案

通过弱测后,采用C++程序随机生成较强数据的方法进行测试

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
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <set>
#include <unordered_map>
#include <vector>
#include <ctime>
typedef long long ll;
using namespace std;
unsigned int grf[32];
int reg[] = {0, 1, 2, 3, 31};
int dm[1024];
#define R reg[rand() % 5]
#define I (rand() + rand())
#define B (rand() % 650)
void add(int rs, int rt, int rd)
{
printf("add $%d,$%d,$%d\n", rd, rt, rs);
if (rd)
grf[rd] = grf[rs] + grf[rt];
}
void sub(int rs, int rt, int rd)
{
printf("sub $%d,$%d,$%d\n", rd, rt, rs);
if (rd)
grf[rd] = grf[rs] - grf[rt];
}
void ori(int rs, int rt, int imm)
{
printf("ori $%d,$%d,%d\n", rt, rs, imm);
if (rt)
grf[rt] = grf[rs] | imm;
}
void lui(int rs, int rt, int imm)
{
printf("lui $%d,%d\n", rs, imm);
if (rs)
grf[rs] = 1u * imm << 16;
}
void lw(int rs, int rt)
{
int imm = rand() % 1024 * 4;
printf("lw $%d,%d($0)\n", rt, imm);
grf[rt] = dm[imm / 4];
}
void sw(int rs, int rt)
{
int imm = rand() % 1024 * 4;
printf("sw $%d,%d($0)\n", rt, imm);
dm[imm / 4] = grf[rt];
}
int jump[1010];
void beq(int rs, int rt)
{
int jaddr = B;
while (jump[jaddr])
jaddr = B;
printf("beq $%d,$%d,label%d\n", rs, rt, jaddr);
}
void j()
{
int jaddr = B;
while (jump[jaddr])
jaddr = B;
printf("j label%d\n", jaddr);
}
void jal()
{
int jaddr = B;
while (jump[jaddr])
jaddr = B;
printf("jal label%d\n", jaddr);
}
int jr(int rs, int rt)
{
int i;
vector<int> can;
can.clear();
for (i = 0; i < 5; i++)
if (reg[i] > 0x3000 && reg[i] <= 0x3700)
can.push_back(reg[i]);
if (can.size() == 0)
{
beq(rs, rt);
return 0;
}
rs = can[rand() % can.size()];
printf("jr $%d\n", rs);
return 1;
}
void nop()
{
printf("nop\n");
}
int main()
{
int i;
srand(time(NULL));
freopen("mips_code.txt", "w", stdout);
printf("sub $31,$31,$31\n"); //���$sp
int last = -1;
for (i = 0; i < 1000; i++)
{
printf("label%d: ", i);
int instr = rand() % 11;
while ((i < 300 || last == 1) && instr >= 6 && instr <= 9)
{ //j+j
instr = rand() % 11;
}
int rs = R, rt = R, rd = R, imm = I;
if (instr == 0)
add(rs, rt, rd);
else if (instr == 1)
sub(rs, rt, rd);
else if (instr == 2)
ori(rs, rt, imm);
else if (instr == 3)
lui(rs, 0, imm);
else if (instr == 4)
lw(rs, rt);
else if (instr == 5)
sw(rs, rt);
else if (instr == 6)
beq(rs, rt);
else if (instr == 7)
j();
else if (instr == 8)
jal();
else if (instr == 9)
{
int yes = jr(rs, rt);
if (!yes)
instr = 6; //beq
}
else
nop();
jump[i] = last = (instr >= 6 && instr <= 9);
}
printf("label:\n beq $0,$0,label\nnop");
return 0;
}

其中一组较强数据:

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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
ori $29,$0,0x22fc
ori $28,$0,0x1800
ori $31,$0,0x3000
label0:nop
label1:sub $6,$7,$18
label2:add $8,$15,$23
label3:add $26,$3,$15
label4:ori $19,$13,0xaa14
label5:nop
label6:ori $11,$0,0x13c
lw $21,28($11)
label7:nop
label8:ori $7,$0,0xd0
lw $2,36($7)
label9:beq $12,$20,label11
label10:beq $18,$3,label187
label11:ori $20,$0,0x14c
sw $27,36($20)
label12:beq $17,$0,label26
label13:add $12,$15,$3
label14:ori $20,$0,0x1c0
sw $4,12($20)
label15:lui $2,0x8226
label16:add $6,$17,$15
label17:nop
label18:nop
label19:add $20,$14,$16
label20:ori $23,$0,0x1b4
sw $4,8($23)
label21:nop
label22:beq $16,$24,label80
label23:nop
label24:ori $14,$0,0x1c4
sw $25,40($14)
label25:add $0,$7,$3
label26:nop
label27:sub $28,$12,$27
label28:ori $3,$0,0x0
sw $27,32($3)
label29:ori $18,$0,0x1b8
sw $26,24($18)
label30:ori $26,$0,0x11c
sw $18,24($26)
label31:ori $23,$0,0x94
sw $19,16($23)
label32:ori $16,$0,0xc4
lw $4,16($16)
label33:ori $26,$0,0x120
lw $20,28($26)
label34:ori $18,$0,0x178
sw $1,16($18)
label35:nop
label36:nop
label37:ori $19,$0,0x140
sw $16,12($19)
label38:nop
label39:lui $11,0xa5e6
label40:lui $22,0xce2e
label41:add $17,$30,$8
label42:ori $17,$0,0x118
lw $25,36($17)
label43:ori $0,$0,0x1dc
sw $15,8($0)
label44:sub $8,$22,$2
label45:ori $14,$0,0x1b8
lw $6,40($14)
label46:nop
label47:nop
label48:add $5,$14,$22
label49:ori $17,$29,0x5cc8
label50:ori $24,$0,0x100
sw $22,16($24)
label51:sub $21,$24,$28
label52:ori $29,$0,0xf8
lw $22,24($29)
label53:sub $19,$2,$23
label54:ori $5,$22,0x4cba
label55:add $7,$22,$9
label56:lui $15,0xa746
label57:add $30,$6,$25
label58:ori $23,$0,0x90
sw $4,8($23)
label59:ori $22,$0,0xfc
sw $6,36($22)
label60:jal label129
label61:beq $1,$21,label184
label62:sub $4,$28,$0
label63:add $7,$17,$12
label64:add $13,$2,$26
label65:lui $24,0x1093
label66:sub $24,$2,$17
label67:ori $16,$0,0x1a4
lw $29,8($16)
label68:ori $24,$0,0x1d0
lw $21,0($24)
label69:sub $17,$27,$17
label70:nop
label71:nop
label72:jr $31
label73:ori $26,$0,0x6c
lw $20,40($26)
label74:nop
label75:ori $17,$0,0x184
lw $4,24($17)
label76:ori $26,$0,0xb8
sw $16,28($26)
label77:ori $3,$0,0xe0
sw $19,24($3)
label78:ori $16,$10,0xdd62
label79:add $21,$23,$15
label80:ori $1,$0,0x98
lw $18,24($1)
label81:beq $25,$19,label127
label82:nop
label83:ori $5,$0,0x1e8
sw $14,36($5)
label84:add $7,$11,$17
label85:ori $21,$0,0x6d65
label86:beq $26,$28,label119
label87:lui $8,0x12ca
label88:ori $8,$0,0x1e0
sw $18,36($8)
label89:add $9,$3,$21
label90:sub $25,$29,$15
label91:lui $3,0x2f2f
label92:beq $26,$14,label196
label93:nop
label94:ori $24,$0,0x8
lw $12,24($24)
label95:jal label161
label96:ori $10,$0,0x2c
sw $13,0($10)
label97:ori $22,$0,0x34
lw $20,36($22)
label98:ori $17,$0,0xec
sw $24,16($17)
label99:sub $1,$21,$7
label100:ori $24,$0,0xc8
sw $30,24($24)
label101:ori $5,$0,0x124
lw $24,4($5)
label102:ori $30,$0,0x16c
lw $20,36($30)
label103:jr $31
label104:sub $29,$1,$7
label105:jal label108
label106:ori $22,$22,0xc2c4
label107:jr $31
label108:ori $1,$0,0x1c0
lw $18,0($1)
label109:nop
label110:nop
label111:ori $18,$0,0xfc
sw $1,24($18)
label112:nop
label113:beq $25,$23,label163
label114:jal label161
label115:sub $27,$1,$19
label116:jr $31
label117:beq $19,$2,label161
label118:ori $1,$6,0xe0c4
label119:sub $27,$24,$5
label120:sub $17,$22,$8
label121:lui $20,0xad45
label122:sub $7,$16,$9
label123:ori $6,$0,0x168
sw $6,36($6)
label124:ori $21,$0,0x1d0
lw $4,24($21)
label125:sub $4,$30,$10
label126:ori $20,$7,0xae65
label127:ori $1,$0,0x1c
sw $17,12($1)
label128:jal label163
label129:ori $13,$0,0x17c
sw $25,36($13)
label130:ori $5,$0,0x1a4
lw $24,0($5)
label131:nop
label132:ori $22,$0,0x198
lw $27,32($22)
label133:ori $16,$0,0xf8
sw $7,28($16)
label134:ori $26,$29,0xc317
label135:beq $2,$14,label172
label136:add $7,$16,$0
label137:beq $6,$14,label190
label138:lui $23,0xe5d7
label139:beq $23,$2,label164
label140:ori $17,$0,0x134
lw $11,32($17)
label141:nop
label142:add $2,$28,$26
label143:beq $7,$22,label197
label144:nop
label145:ori $5,$30,0xbbf8
label146:sub $15,$13,$15
label147:jal label191
label148:jr $31
label149:beq $20,$1,label176
label150:ori $9,$0,0x198
sw $6,40($9)
label151:nop
label152:ori $19,$0,0xf8
sw $13,40($19)
label153:lui $12,0x5e6d
label154:nop
label155:ori $20,$0,0xf4
sw $12,8($20)
label156:ori $15,$2,0xa110
label157:ori $29,$0,0x24
sw $24,0($29)
label158:sub $29,$23,$27
label159:ori $5,$12,0x4d32
label160:ori $2,$0,0xec
sw $25,36($2)
label161:beq $26,$10,label192
label162:lui $17,0xa5b3
label163:nop
label164:jal label195
label165:ori $27,$0,0x14
lw $9,12($27)
label166:jal label178
label167:lui $6,0x64df
label168:jal label195
label169:jal label193
label170:ori $1,$0,0x94
lw $17,24($1)
label171:jr $31
label172:ori $25,$19,0xf861
label173:sub $0,$15,$4
label174:beq $30,$20,label183
label175:nop
label176:beq $22,$21,label187
label177:nop
label178:beq $26,$8,label186
label179:ori $10,$0,0x1e0
lw $12,12($10)
label180:beq $16,$29,label198
label181:nop
label182:add $22,$2,$26
label183:nop
label184:ori $9,$0,0x30
sw $23,4($9)
label185:lui $24,0xe9ab
label186:add $1,$16,$7
label187:ori $26,$0,0x80
sw $19,40($26)
label188:beq $0,$29,label194
label189:sub $4,$19,$19
label190:jal label196
label191:add $27,$19,$7
label192:nop
label193:add $28,$3,$14
label194:jal label199
label195:jal label198
label196:beq $15,$16,label197
label197:sub $1,$20,$5
label198:nop
label199:nop

魔改MARS输出:

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
@00003000: $29 <= 000022fc
@00003004: $28 <= 00001800
@00003008: $31 <= 00003000
@00003010: $ 6 <= 00000000
@00003014: $ 8 <= 00000000
@00003018: $26 <= 00000000
@0000301c: $19 <= 0000aa14
@00003024: $11 <= 0000013c
@00003028: $21 <= 00000000
@00003030: $ 7 <= 000000d0
@00003034: $ 2 <= 00000000
@00003040: $20 <= 0000014c
@00003044: *00000170 <= 00000000
@00003090: $28 <= 00000000
@00003094: $ 3 <= 00000000
@00003098: *00000020 <= 00000000
@0000309c: $18 <= 000001b8
@000030a0: *000001d0 <= 00000000
@000030a4: $26 <= 0000011c
@000030a8: *00000134 <= 000001b8
@000030ac: $23 <= 00000094
@000030b0: *000000a4 <= 0000aa14
@000030b4: $16 <= 000000c4
@000030b8: $ 4 <= 00000000
@000030bc: $26 <= 00000120
@000030c0: $20 <= 00000000
@000030c4: $18 <= 00000178
@000030c8: *00000188 <= 00000000
@000030d4: $19 <= 00000140
@000030d8: *0000014c <= 000000c4
@000030e0: $11 <= a5e60000
@000030e4: $22 <= ce2e0000
@000030e8: $17 <= 00000000
@000030ec: $17 <= 00000118
@000030f0: $25 <= 00000000
@000030f8: *00000008 <= 00000000
@000030fc: $ 8 <= ce2e0000
@00003100: $14 <= 000001b8
@00003104: $ 6 <= 00000000
@00003110: $ 5 <= ce2e01b8
@00003114: $17 <= 00007efc
@00003118: $24 <= 00000100
@0000311c: *00000110 <= ce2e0000
@00003120: $21 <= 00000100
@00003124: $29 <= 000000f8
@00003128: $22 <= ce2e0000
@0000312c: $19 <= ffffff6c
@00003130: $ 5 <= ce2e4cba
@00003134: $ 7 <= ce2e0000
@00003138: $15 <= a7460000
@0000313c: $30 <= 00000000
@00003140: $23 <= 00000090
@00003144: *00000098 <= 00000000
@00003148: $22 <= 000000fc
@0000314c: *00000120 <= 00000000
@00003150: $31 <= 00003154
@000032b8: $13 <= 0000017c
@000032bc: *000001a0 <= 00000000
@000032c0: $ 5 <= 000001a4
@000032c4: $24 <= 00000000
@000032cc: $22 <= 00000198
@000032d0: $27 <= 00000000
@000032d4: $16 <= 000000f8
@000032d8: *00000114 <= ce2e0000
@000032dc: $26 <= 0000c3ff
@000032e4: $ 7 <= 000000f8
@000032ec: $23 <= e5d70000
@000032f4: $17 <= 00000134
@000032f8: $11 <= 00000000
@00003300: $ 2 <= 0000c3ff
@0000330c: $ 5 <= 0000bbf8
@00003310: $15 <= 58ba017c
@00003314: $31 <= 00003318
@000033ec: $27 <= 00000064
@000033f4: $28 <= 000001b8
@000033f8: $31 <= 000033fc

Isim仿真输出

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
@00003000: $29 <= 000022fc
@00003004: $28 <= 00001800
@00003008: $31 <= 00003000
@00003010: $ 6 <= 00000000
@00003014: $ 8 <= 00000000
@00003018: $26 <= 00000000
@0000301c: $19 <= 0000aa14
@00003024: $11 <= 0000013c
@00003028: $21 <= 00000000
@00003030: $ 7 <= 000000d0
@00003034: $ 2 <= 00000000
@00003040: $20 <= 0000014c
@00003044: *00000170 <= 00000000
@00003090: $28 <= 00000000
@00003094: $ 3 <= 00000000
@00003098: *00000020 <= 00000000
@0000309c: $18 <= 000001b8
@000030a0: *000001d0 <= 00000000
@000030a4: $26 <= 0000011c
@000030a8: *00000134 <= 000001b8
@000030ac: $23 <= 00000094
@000030b0: *000000a4 <= 0000aa14
@000030b4: $16 <= 000000c4
@000030b8: $ 4 <= 00000000
@000030bc: $26 <= 00000120
@000030c0: $20 <= 00000000
@000030c4: $18 <= 00000178
@000030c8: *00000188 <= 00000000
@000030d4: $19 <= 00000140
@000030d8: *0000014c <= 000000c4
@000030e0: $11 <= a5e60000
@000030e4: $22 <= ce2e0000
@000030e8: $17 <= 00000000
@000030ec: $17 <= 00000118
@000030f0: $25 <= 00000000
@000030f8: *00000008 <= 00000000
@000030fc: $ 8 <= ce2e0000
@00003100: $14 <= 000001b8
@00003104: $ 6 <= 00000000
@00003110: $ 5 <= ce2e01b8
@00003114: $17 <= 00007efc
@00003118: $24 <= 00000100
@0000311c: *00000110 <= ce2e0000
@00003120: $21 <= 00000100
@00003124: $29 <= 000000f8
@00003128: $22 <= ce2e0000
@0000312c: $19 <= ffffff6c
@00003130: $ 5 <= ce2e4cba
@00003134: $ 7 <= ce2e0000
@00003138: $15 <= a7460000
@0000313c: $30 <= 00000000
@00003140: $23 <= 00000090
@00003144: *00000098 <= 00000000
@00003148: $22 <= 000000fc
@0000314c: *00000120 <= 00000000
@00003150: $31 <= 00003154
@000032b8: $13 <= 0000017c
@000032bc: *000001a0 <= 00000000
@000032c0: $ 5 <= 000001a4
@000032c4: $24 <= 00000000
@000032cc: $22 <= 00000198
@000032d0: $27 <= 00000000
@000032d4: $16 <= 000000f8
@000032d8: *00000114 <= ce2e0000
@000032dc: $26 <= 0000c3ff
@000032e4: $ 7 <= 000000f8
@000032ec: $23 <= e5d70000
@000032f4: $17 <= 00000134
@000032f8: $11 <= 00000000
@00003300: $ 2 <= 0000c3ff
@0000330c: $ 5 <= 0000bbf8
@00003310: $15 <= 58ba017c
@00003314: $31 <= 00003318
@000033ec: $27 <= 00000064
@000033f4: $28 <= 000001b8
@000033f8: $31 <= 000033fc

完全一致,测试通过!


Warnings

易错点:

1.非阻塞赋值与display的内容不相符

评测机是通过看我们display的东西和它需要的一样不一样来评判我们是否正确的。如果采用非阻塞赋值,紧接着来一句display的话,由于非阻塞赋值是在过程块结束时才统一赋值的,所以输出的东西是未修改的。

2.位宽

对于0x0000_3000,它的位宽是32位,所以二进制写法是32’b00000000000000000011000000000000,省事写16进制的话,是32’h0000_3000,这里的32指的是位宽,而不是这个数在某种进制下有几位!

3.jr跳转

jr不只是可以跳31号寄存器存的地址,任何寄存器存储的地址它都可以跳!这是室友P4课上遭遇的车祸现场,课下弱测并没有测试出来! 请务必检查jr是否写对了!

4.还是display

需要输出32位宽的内存地址,不是【13:2】(12位)地址,请大家看好自己的输出!和教程中提供的输出比对一下,应该就会发现。

5.Reset常见误区

首先是同步复位,这个老生常谈了。之后就是reset的地方够不够,对不对,我们需要reset的地方是程序计数器PC,寄存器堆GRF,数据存储器DM,除此之外,如果发现自己在其他地方也使用了reset,就得好好斟酌一下,这里到底需不需要reset?比如,我发现自己在IM中加了一个reset,本地测试过了,提交没输出,为何?经过了一天的思考和从各方获取经验,我隐约意识到可能是复位出了问题,IM里面为什么需要reset?是防止PC值没有变成0x0000_3000吗?我最开始的确是这么想的,所以在里面加了reset,但是,后来我发现,我reset的并不是输入进来的PC,而是,指令存储空间!!也就是说,考虑到评测机最开始上来就reset一下,我读入的code直接没了,当然之后没有输出。为何本地没有测出来问题?因为我没有检查reset,只是保持reset=0跑的程序。这个点卡了我一天多,也卡了评论区不少同学好长时间。还有类似的问题如输出比正常的慢一周期的,也请看看reset。这样,关于reset的坑差不多就介绍完了。

新增指令要点:

  1. 注意条件分支带link的指令需改动的点,signal_def中添加OPCMP_OP,在CMP中分析跳转模式,增加相应case分支。重点CTRL内的信号添加。首先是添加相应导线,再在NPCOp内添加相应的| 导线名WDsel的部分添加相应的| 导线名A3sel的部分也添加相应的| 导线名,最后在于关键的WE信号,因为我采用了取反的方式,所以直接在括号内添加相应的指令模块,如对于bgezal指令WE可写为

    assign GRFWE = !(sw | beq | j | sh | sb | jr | (bgezal & !jump));,重点就在添加(bgezal & !jump)部分!!! 谨记!!!

  2. 对于复杂计算,则观察计算方法,由于ALU内部采用了case语句的模式,故可以考虑另写functional来保证计算方式合理添加,具体寄存器选择信号则观察指令相似处,与相同模式指令同处加| 导线名即可!!!


思考题

  1. 阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024字),根据你的理解回答,这个 addr 信号又是从哪里来的?地址信号 addr 位数为什么是 [11:2] 而不是 [9:0] ?

    MIPS中以字节为单位,而在我们设计的DM中,每一个reg[31:0]为一个单位。

    Addr来自ALU的输出端口,代表要读取的DM存储器的地址,在DM设计中应当取[11:0],又因为按字节寻址,因此除以4[11:2]

  2. 思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣。

    指令对应控制信号取值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    `define R 6'b000000 
    `define lw 6'b100011
    case(Instr[31:26])
    `R: begin
    case(Instr[5:0])
    6'b100000:add RegDst=2'b01; Regwrite=1'b1; ALUsrc=2'b00; ALUctrl=3‘b000; Memwrite=0; MemtoReg=2'b00;
    6'b100010:sub RegDst=2'b01; Regwrite=1'b1; ALUsrc=2'b00; ALUctrl=3‘b001; Memwrite=0; MemtoReg=2'b00;
    ……
    endcase
    end `
    `lw: ……
    endcase

    这种译码方式,对于信号的控制不容易遗漏,对于每一个信号都需要给一个值,清晰易读,比较直观,但是添加指令比较复杂,需要给出完整的控制信号,对于不需要的信号也需要给定默认值,这样也会导致控制部分的代码比较长。

    控制信号每种取值所对应的指令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    wire add=(Instr[31:26]==6'b000000 && Instr[5:0] == 6'b100000); 
    wire sub=(Instr[31:26]==6'b000000 && Instr[5:0] == 6'b100010); ……
    assign RegDst[1]=1'b0|jal; assign RegDst[0]=1'b0|add|sub;
    assign Regwrite=1'b0|add|sub|ori|lw|lui|jal;
    assign EXTop=1'b0|lw|sw; assign ALUsrc[1]=1'b0;
    assign ALUsrc[0]=1'b0|ori|lw|sw|lui; assign ALUctrl[2]=1'b0;
    assign ALUctrl[1]=1'b0|ori;
    assign ALUctrl[0]=1'b0|sub;
    assign Memwrite=1'b0|sw;
    assign MemtoReg[1]=1'b0|lui|jal;
    assign MemtoReg[0]=1'b0|lw|lui;
    assign NPCop[1]=1'b0|beq|jr;
    assign NPCop[0]=1'b0|jal|jr|j;
    assign Branchop[2]=1'b0;
    assign Branchop[1]=1'b0;
    assign Branchop[0]=1'b0;
    assign DMop[1]=1'b0;
    assign DMop[0]=1'b0;

    这种译码方式,是对控制信号用了或指令的方式,如果满足这条指令,就会使的控制信号有效,这种方式的优点在于可以很容易的添加指令,对于指令只需要在相应控制信号之后或上一个即可,但是缺点是不够直观,可能会造成漏加信号的错误

    经过P3的拷打,我认为还是第二种比较好。

  3. 在相应的部件中,复位信号的设计都是同步复位,这与 P3 中的设计要求不同。请对比同步复位异步复位这两种方式的 reset 信号与 clk 信号优先级的关系。

    在同步复位中,clk的优先级是高于reset的,只有在时钟上升沿到来时reset信号有效才进行复位,单有reset信号而时钟上升沿信号没有到来不能进行复位。

    在异步复位中,reset优先级高于clk,不论是否是时钟上升沿来临,只要reset信号高电平就清零。

  4. C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,addi 与 addiu 是等价的,add 与 addu 是等价的。提示:阅读《MIPS32® Architecture For Programmers Volume II: The MIPS32® Instruction Set》中相关指令的 Operation 部分。

    根据RTL语言描述:addiaddiu的区别在于当出现溢出时,addiu忽略溢出,并将溢出的最高位舍弃;addi会报告SignalException(IntegerOverflow)

    故忽略溢出,二者等价。

RooKie_Z P4 Verilog单周期CPU设计文档

https://rookie-zgy1513.github.io/2025/08/10/P4-CPU/

作者

RooKie_Z

发布于

2025-08-10

更新于

2025-08-16

许可协议

评论