-
Notifications
You must be signed in to change notification settings - Fork 190
Expand file tree
/
Copy pathcpu.v
More file actions
414 lines (358 loc) · 11 KB
/
cpu.v
File metadata and controls
414 lines (358 loc) · 11 KB
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/*
* cpu. - five stage MIPS CPU.
*
* Many variables (wires) pass through several stages.
* The naming convention used for each stage is
* accomplished by appending the stage number (_s<num>).
* For example the variable named "data" which is
* in stage 2 and stage 3 would be named as follows.
*
* wire data_s2;
* wire data_s3;
*
* If the stage number is omitted it is assumed to
* be at the stage at which the variable is first
* established.
*/
`include "regr.v"
`include "im.v"
`include "regm.v"
`include "control.v"
`include "alu.v"
`include "alu_control.v"
`include "dm.v"
`ifndef DEBUG_CPU_STAGES
`define DEBUG_CPU_STAGES 0
`endif
module cpu(
input wire clk);
parameter NMEM = 20; // number in instruction memory
parameter IM_DATA = "im_data.txt";
wire regwrite_s5;
wire [4:0] wrreg_s5;
wire [31:0] wrdata_s5;
reg stall_s1_s2;
// {{{ diagnostic outputs
initial begin
if (`DEBUG_CPU_STAGES) begin
$display("if_pc, if_instr, id_regrs, id_regrt, ex_alua, ex_alub, ex_aluctl, mem_memdata, mem_memread, mem_memwrite, wb_regdata, wb_regwrite");
$monitor("%x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x",
pc, /* if_pc */
inst, /* if_instr */
data1, /* id_regrs */
data2, /* id_regrt */
data1_s3, /* data1_s3 */
alusrc_data2, /* alusrc_data2 */
aluctl, /* ex_aluctl */
data2_s4, /* mem_memdata */
memread_s4, /* mem_memread */
memwrite_s4, /* mem_memwrite */
wrdata_s5, /* wb_regdata */
regwrite_s5 /* wb_regwrite */
);
end
end
// }}}
// {{{ flush control
reg flush_s1, flush_s2, flush_s3;
always @(*) begin
flush_s1 <= 1'b0;
flush_s2 <= 1'b0;
flush_s3 <= 1'b0;
if (pcsrc | jump_s4) begin
flush_s1 <= 1'b1;
flush_s2 <= 1'b1;
flush_s3 <= 1'b1;
end
end
// }}}
// {{{ stage 1, IF (fetch)
reg [31:0] pc;
initial begin
pc <= 32'd0;
end
wire [31:0] pc4; // PC + 4
assign pc4 = pc + 4;
always @(posedge clk) begin
if (stall_s1_s2)
pc <= pc;
else if (pcsrc == 1'b1)
pc <= baddr_s4;
else if (jump_s4 == 1'b1)
pc <= jaddr_s4;
else
pc <= pc4;
end
// pass PC + 4 to stage 2
wire [31:0] pc4_s2;
regr #(.N(32)) regr_pc4_s2(.clk(clk),
.hold(stall_s1_s2), .clear(flush_s1),
.in(pc4), .out(pc4_s2));
// instruction memory
wire [31:0] inst;
wire [31:0] inst_s2;
im #(.NMEM(NMEM), .IM_DATA(IM_DATA))
im1(.clk(clk), .addr(pc), .data(inst));
regr #(.N(32)) regr_im_s2(.clk(clk),
.hold(stall_s1_s2), .clear(flush_s1),
.in(inst), .out(inst_s2));
// }}}
// {{{ stage 2, ID (decode)
// decode instruction
wire [5:0] opcode;
wire [4:0] rs;
wire [4:0] rt;
wire [4:0] rd;
wire [15:0] imm;
wire [4:0] shamt;
wire [31:0] jaddr_s2;
wire [31:0] seimm; // sign extended immediate
//
assign opcode = inst_s2[31:26];
assign rs = inst_s2[25:21];
assign rt = inst_s2[20:16];
assign rd = inst_s2[15:11];
assign imm = inst_s2[15:0];
assign shamt = inst_s2[10:6];
assign jaddr_s2 = {pc[31:28], inst_s2[25:0], {2{1'b0}}};
assign seimm = {{16{inst_s2[15]}}, inst_s2[15:0]};
// register memory
wire [31:0] data1, data2;
regm regm1(.clk(clk), .read1(rs), .read2(rt),
.data1(data1), .data2(data2),
.regwrite(regwrite_s5), .wrreg(wrreg_s5),
.wrdata(wrdata_s5));
// pass rs to stage 3 (for forwarding)
wire [4:0] rs_s3;
regr #(.N(5)) regr_s2_rs(.clk(clk), .clear(1'b0), .hold(stall_s1_s2),
.in(rs), .out(rs_s3));
// transfer register data to stage 3
wire [31:0] data1_s3, data2_s3;
regr #(.N(64)) reg_s2_mem(.clk(clk), .clear(flush_s2), .hold(stall_s1_s2),
.in({data1, data2}),
.out({data1_s3, data2_s3}));
// transfer seimm, rt, and rd to stage 3
wire [31:0] seimm_s3;
wire [4:0] rt_s3;
wire [4:0] rd_s3;
regr #(.N(32)) reg_s2_seimm(.clk(clk), .clear(flush_s2), .hold(stall_s1_s2),
.in(seimm), .out(seimm_s3));
regr #(.N(10)) reg_s2_rt_rd(.clk(clk), .clear(flush_s2), .hold(stall_s1_s2),
.in({rt, rd}), .out({rt_s3, rd_s3}));
// transfer PC + 4 to stage 3
wire [31:0] pc4_s3;
regr #(.N(32)) reg_pc4_s2(.clk(clk), .clear(1'b0), .hold(stall_s1_s2),
.in(pc4_s2), .out(pc4_s3));
// control (opcode -> ...)
wire regdst;
wire branch_eq_s2;
wire branch_ne_s2;
wire memread;
wire memwrite;
wire memtoreg;
wire [1:0] aluop;
wire regwrite;
wire alusrc;
wire jump_s2;
//
control ctl1(.opcode(opcode), .regdst(regdst),
.branch_eq(branch_eq_s2), .branch_ne(branch_ne_s2),
.memread(memread),
.memtoreg(memtoreg), .aluop(aluop),
.memwrite(memwrite), .alusrc(alusrc),
.regwrite(regwrite), .jump(jump_s2));
// shift left, seimm
wire [31:0] seimm_sl2;
assign seimm_sl2 = {seimm[29:0], 2'b0}; // shift left 2 bits
// branch address
wire [31:0] baddr_s2;
assign baddr_s2 = pc4_s2 + seimm_sl2;
// transfer the control signals to stage 3
wire regdst_s3;
wire memread_s3;
wire memwrite_s3;
wire memtoreg_s3;
wire [1:0] aluop_s3;
wire regwrite_s3;
wire alusrc_s3;
// A bubble is inserted by setting all the control signals
// to zero (stall_s1_s2).
regr #(.N(8)) reg_s2_control(.clk(clk), .clear(stall_s1_s2), .hold(1'b0),
.in({regdst, memread, memwrite,
memtoreg, aluop, regwrite, alusrc}),
.out({regdst_s3, memread_s3, memwrite_s3,
memtoreg_s3, aluop_s3, regwrite_s3, alusrc_s3}));
wire branch_eq_s3, branch_ne_s3;
regr #(.N(2)) branch_s2_s3(.clk(clk), .clear(flush_s2), .hold(1'b0),
.in({branch_eq_s2, branch_ne_s2}),
.out({branch_eq_s3, branch_ne_s3}));
wire [31:0] baddr_s3;
regr #(.N(32)) baddr_s2_s3(.clk(clk), .clear(flush_s2), .hold(1'b0),
.in(baddr_s2), .out(baddr_s3));
wire jump_s3;
regr #(.N(1)) reg_jump_s3(.clk(clk), .clear(flush_s2), .hold(1'b0),
.in(jump_s2),
.out(jump_s3));
wire [31:0] jaddr_s3;
regr #(.N(32)) reg_jaddr_s3(.clk(clk), .clear(flush_s2), .hold(1'b0),
.in(jaddr_s2), .out(jaddr_s3));
// }}}
// {{{ stage 3, EX (execute)
// pass through some control signals to stage 4
wire regwrite_s4;
wire memtoreg_s4;
wire memread_s4;
wire memwrite_s4;
regr #(.N(4)) reg_s3(.clk(clk), .clear(flush_s2), .hold(1'b0),
.in({regwrite_s3, memtoreg_s3, memread_s3,
memwrite_s3}),
.out({regwrite_s4, memtoreg_s4, memread_s4,
memwrite_s4}));
// ALU
// second ALU input can come from an immediate value or data
wire [31:0] alusrc_data2;
assign alusrc_data2 = (alusrc_s3) ? seimm_s3 : fw_data2_s3;
// ALU control
wire [3:0] aluctl;
wire [5:0] funct;
assign funct = seimm_s3[5:0];
alu_control alu_ctl1(.funct(funct), .aluop(aluop_s3), .aluctl(aluctl));
// ALU
wire [31:0] alurslt;
reg [31:0] fw_data1_s3;
always @(*)
case (forward_a)
2'd1: fw_data1_s3 = alurslt_s4;
2'd2: fw_data1_s3 = wrdata_s5;
default: fw_data1_s3 = data1_s3;
endcase
wire zero_s3;
alu alu1(.ctl(aluctl), .a(fw_data1_s3), .b(alusrc_data2), .out(alurslt),
.zero(zero_s3));
wire zero_s4;
regr #(.N(1)) reg_zero_s3_s4(.clk(clk), .clear(1'b0), .hold(1'b0),
.in(zero_s3), .out(zero_s4));
// pass ALU result and zero to stage 4
wire [31:0] alurslt_s4;
regr #(.N(32)) reg_alurslt(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in({alurslt}),
.out({alurslt_s4}));
// pass data2 to stage 4
wire [31:0] data2_s4;
reg [31:0] fw_data2_s3;
always @(*)
case (forward_b)
2'd1: fw_data2_s3 = alurslt_s4;
2'd2: fw_data2_s3 = wrdata_s5;
default: fw_data2_s3 = data2_s3;
endcase
regr #(.N(32)) reg_data2_s3(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in(fw_data2_s3), .out(data2_s4));
// write register
wire [4:0] wrreg;
wire [4:0] wrreg_s4;
assign wrreg = (regdst_s3) ? rd_s3 : rt_s3;
// pass to stage 4
regr #(.N(5)) reg_wrreg(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in(wrreg), .out(wrreg_s4));
wire branch_eq_s4, branch_ne_s4;
regr #(.N(2)) branch_s3_s4(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in({branch_eq_s3, branch_ne_s3}),
.out({branch_eq_s4, branch_ne_s4}));
wire [31:0] baddr_s4;
regr #(.N(32)) baddr_s3_s4(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in(baddr_s3), .out(baddr_s4));
wire jump_s4;
regr #(.N(1)) reg_jump_s4(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in(jump_s3),
.out(jump_s4));
wire [31:0] jaddr_s4;
regr #(.N(32)) reg_jaddr_s4(.clk(clk), .clear(flush_s3), .hold(1'b0),
.in(jaddr_s3), .out(jaddr_s4));
// }}}
// {{{ stage 4, MEM (memory)
// pass regwrite and memtoreg to stage 5
wire memtoreg_s5;
regr #(.N(2)) reg_regwrite_s4(.clk(clk), .clear(1'b0), .hold(1'b0),
.in({regwrite_s4, memtoreg_s4}),
.out({regwrite_s5, memtoreg_s5}));
// data memory
wire [31:0] rdata;
dm dm1(.clk(clk), .addr(alurslt_s4[8:2]), .rd(memread_s4), .wr(memwrite_s4),
.wdata(data2_s4), .rdata(rdata));
// pass read data to stage 5
wire [31:0] rdata_s5;
regr #(.N(32)) reg_rdata_s4(.clk(clk), .clear(1'b0), .hold(1'b0),
.in(rdata),
.out(rdata_s5));
// pass alurslt to stage 5
wire [31:0] alurslt_s5;
regr #(.N(32)) reg_alurslt_s4(.clk(clk), .clear(1'b0), .hold(1'b0),
.in(alurslt_s4),
.out(alurslt_s5));
// pass wrreg to stage 5
regr #(.N(5)) reg_wrreg_s4(.clk(clk), .clear(1'b0), .hold(1'b0),
.in(wrreg_s4),
.out(wrreg_s5));
// branch
reg pcsrc;
always @(*) begin
case (1'b1)
branch_eq_s4: pcsrc <= zero_s4;
branch_ne_s4: pcsrc <= ~(zero_s4);
default: pcsrc <= 1'b0;
endcase
end
// }}}
// {{{ stage 5, WB (write back)
assign wrdata_s5 = (memtoreg_s5 == 1'b1) ? rdata_s5 : alurslt_s5;
// }}}
// {{{ forwarding
// stage 3 (MEM) -> stage 2 (EX)
// stage 4 (WB) -> stage 2 (EX)
reg [1:0] forward_a;
reg [1:0] forward_b;
always @(*) begin
// If the previous instruction (stage 4) would write,
// and it is a value we want to read (stage 3), forward it.
// data1 input to ALU
if ((regwrite_s4 == 1'b1) && (wrreg_s4 == rs_s3)) begin
forward_a <= 2'd1; // stage 4
end else if ((regwrite_s5 == 1'b1) && (wrreg_s5 == rs_s3)) begin
forward_a <= 2'd2; // stage 5
end else
forward_a <= 2'd0; // no forwarding
// data2 input to ALU
if ((regwrite_s4 == 1'b1) & (wrreg_s4 == rt_s3)) begin
forward_b <= 2'd1; // stage 5
end else if ((regwrite_s5 == 1'b1) && (wrreg_s5 == rt_s3)) begin
forward_b <= 2'd2; // stage 5
end else
forward_b <= 2'd0; // no forwarding
end
// }}}
// {{{ load use data hazard detection, signal stall
/* If an operation in stage 4 (MEM) loads from memory (e.g. lw)
* and the operation in stage 3 (EX) depends on this value,
* a stall must be performed. The memory read cannot
* be forwarded because memory access is too slow. It can
* be forwarded from stage 5 (WB) after a stall.
*
* lw $1, 16($10) ; I-type, rt_s3 = $1, memread_s3 = 1
* sw $1, 32($12) ; I-type, rt_s2 = $1, memread_s2 = 0
*
* lw $1, 16($3) ; I-type, rt_s3 = $1, memread_s3 = 1
* sw $2, 32($1) ; I-type, rt_s2 = $2, rs_s2 = $1, memread_s2 = 0
*
* lw $1, 16($3) ; I-type, rt_s3 = $1, memread_s3 = 1
* add $2, $1, $1 ; R-type, rs_s2 = $1, rt_s2 = $1, memread_s2 = 0
*/
always @(*) begin
if (memread_s3 == 1'b1 && ((rt == rt_s3) || (rs == rt_s3)) ) begin
stall_s1_s2 <= 1'b1; // perform a stall
end else
stall_s1_s2 <= 1'b0; // no stall
end
// }}}
endmodule
// vim:foldmethod=marker