-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhw5
More file actions
743 lines (644 loc) · 24.2 KB
/
hw5
File metadata and controls
743 lines (644 loc) · 24.2 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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
`timescale 1ns / 1ns
// registers are 32 bits in RV32
`define REG_SIZE 31:0
// insns are 32 bits in RV32IM
`define INSN_SIZE 31:0
// RV opcodes are 7 bits
`define OPCODE_SIZE 6:0
`ifndef RISCV_FORMAL
`include "../hw2b/cla.sv"
`include "../hw3-singlecycle/RvDisassembler.sv"
`include "../hw4-multicycle/divider_unsigned_pipelined.sv"
`endif
module Disasm #(
byte PREFIX = "D"
) (
input wire [31:0] insn,
output wire [(8*32)-1:0] disasm
);
// synthesis translate_off
// this code is only for simulation, not synthesis
string disasm_string;
always_comb begin
disasm_string = rv_disasm(insn);
end
// HACK: get disasm_string to appear in GtkWave, which can apparently show only wire/logic. Also,
// string needs to be reversed to render correctly.
genvar i;
for (i = 3; i < 32; i = i + 1) begin : gen_disasm
assign disasm[((i+1-3)*8)-1-:8] = disasm_string[31-i];
end
assign disasm[255-:8] = PREFIX;
assign disasm[247-:8] = ":";
assign disasm[239-:8] = " ";
// synthesis translate_on
endmodule
module RegFile (
input logic [4:0] rd,
input logic [`REG_SIZE] rd_data,
input logic [4:0] rs1,
output logic [`REG_SIZE] rs1_data,
input logic [4:0] rs2,
output logic [`REG_SIZE] rs2_data,
input logic clk,
input logic we,
input logic rst
);
localparam int NumRegs = 32;
genvar i;
logic [`REG_SIZE] regs[NumRegs];
// TODO: your code here
assign regs[0] = 32'd0; // x0 is always zero
assign rs1_data = regs[rs1]; // 1st read port
assign rs2_data = regs[rs2]; // 2nd read port
for (i = 1; i < 32; i += 1) begin
always_ff @(posedge clk) begin
if (rst) begin
regs[i] <= 32'd0;
end else begin
if (we && rd == i) begin
regs[rd] <= rd_data;
end
end
end
end
endmodule
/**
* This enum is used to classify each cycle as it comes through the Writeback stage, identifying
* if a valid insn is present or, if it is a stall cycle instead, the reason for the stall. The
* enum values are mutually exclusive: only one should be set for any given cycle. These values
* are compared against the trace-*.json files to ensure that the datapath is running with the
* correct timing.
*
* You will need to set these values at various places within your pipeline, and propagate them
* through the stages until they reach Writeback where they can be checked.
*/
typedef enum {
/** invalid value, this should never appear after the initial reset sequence completes */
CYCLE_INVALID = 0,
/** a stall cycle that arose from the initial reset signal */
CYCLE_RESET = 1,
/** not a stall cycle, a valid insn is in Writeback */
CYCLE_NO_STALL = 2,
/** a stall cycle that arose from a taken branch/jump */
CYCLE_TAKEN_BRANCH = 4,
// the values below are only needed in HW5B
/** a stall cycle that arose from a load-to-use stall */
CYCLE_LOAD2USE = 8,
/** a stall cycle that arose from a div/rem-to-use stall */
CYCLE_DIV2USE = 16,
/** a stall cycle that arose from a fence.i insn */
CYCLE_FENCEI = 32
} cycle_status_e;
/** state at the start of Decode stage */
typedef struct packed {
logic [`REG_SIZE] pc;
logic [`INSN_SIZE] insn;
cycle_status_e cycle_status;
} stage_decode_t;
module DatapathPipelined (
input wire clk,
input wire rst,
output logic [`REG_SIZE] pc_to_imem,
input wire [`INSN_SIZE] insn_from_imem,
// dmem is read/write
output logic [`REG_SIZE] addr_to_dmem,
input wire [`REG_SIZE] load_data_from_dmem,
output logic [`REG_SIZE] store_data_to_dmem,
output logic [3:0] store_we_to_dmem,
output logic halt,
// The PC of the insn currently in Writeback. 0 if not a valid insn.
output logic [`REG_SIZE] trace_writeback_pc,
// The bits of the insn currently in Writeback. 0 if not a valid insn.
output logic [`INSN_SIZE] trace_writeback_insn,
// The status of the insn (or stall) currently in Writeback. See cycle_status_e enum for valid values.
output cycle_status_e trace_writeback_cycle_status
);
// opcodes - see section 19 of RiscV spec
localparam bit [`OPCODE_SIZE] OpLoad = 7'b00_000_11;
localparam bit [`OPCODE_SIZE] OpStore = 7'b01_000_11;
localparam bit [`OPCODE_SIZE] OpBranch = 7'b11_000_11;
localparam bit [`OPCODE_SIZE] OpJalr = 7'b11_001_11;
localparam bit [`OPCODE_SIZE] OpMiscMem = 7'b00_011_11;
localparam bit [`OPCODE_SIZE] OpJal = 7'b11_011_11;
localparam bit [`OPCODE_SIZE] OpRegImm = 7'b00_100_11;
localparam bit [`OPCODE_SIZE] OpRegReg = 7'b01_100_11;
localparam bit [`OPCODE_SIZE] OpEnviron = 7'b11_100_11;
localparam bit [`OPCODE_SIZE] OpAuipc = 7'b00_101_11;
localparam bit [`OPCODE_SIZE] OpLui = 7'b01_101_11;
// cycle counter, not really part of any stage but useful for orienting within GtkWave
// do not rename this as the testbench uses this value
logic [`REG_SIZE] cycles_current;
always_ff @(posedge clk) begin
if (rst) begin
cycles_current <= 0;
end else begin
cycles_current <= cycles_current + 1;
end
end
/***************/
/* FETCH STAGE */
/***************/
// fetch stage propagation components
logic [`REG_SIZE] f_pc_current, pcNext;
wire [`REG_SIZE] f_insn;
cycle_status_e f_cycle_status, e_cycle_status;
// program counter
always_ff @(posedge clk) begin
if (rst) begin
f_pc_current <= 32'd0;
// NB: use CYCLE_NO_STALL since this is the value that will persist after the last reset cycle
f_cycle_status <= CYCLE_NO_STALL;
end else
begin
f_cycle_status <= CYCLE_NO_STALL;
if (execute_state.cycle_status == CYCLE_TAKEN_BRANCH) begin
f_pc_current <= pcNext;
end
else begin
f_pc_current <= f_pc_current + 4;
end
end
end
// send PC to imem
assign pc_to_imem = f_pc_current;
assign f_insn = insn_from_imem;
// Here's how to disassemble an insn into a string you can view in GtkWave.
// Use PREFIX to provide a 1-character tag to identify which stage the insn comes from.
wire [255:0] f_disasm; // 8*32
Disasm #(
.PREFIX("F")
) disasm_0fetch (
.insn (f_insn),
.disasm(f_disasm)
);
/****************/
/* DECODE STAGE */
/****************/
// fetch stage propagates to decode stage components (F -> D)
logic [`REG_SIZE] d_pc_current;
wire [`REG_SIZE] d_insn;
cycle_status_e d_cycle_status;
// this shows how to package up state in a `struct packed`, and how to pass it between stages
stage_decode_t decode_state;
always_ff @(posedge clk) begin
if (rst) begin
decode_state <= '{
pc: 0,
insn: 0,
cycle_status: CYCLE_RESET
};
end else begin
begin
decode_state <= '{
pc: f_pc_current,
insn: f_insn,
cycle_status: f_cycle_status
};
end
end
end
assign d_pc_current = decode_state.pc;
assign d_insn = decode_state.insn;
assign d_cycle_status = decode_state.cycle_status;
// initialize some components of instructions
wire [6:0] insn_funct7;
wire [4:0] insn_rs2;
wire [4:0] insn_rs1;
wire [2:0] insn_funct3;
wire [4:0] insn_rd;
wire [`OPCODE_SIZE] insn_opcode;
// split R-type instruction - see section 2.2 of RiscV spec
assign {insn_funct7, insn_rs2, insn_rs1, insn_funct3, insn_rd, insn_opcode} = d_insn; //insn_from_imem
// setup for I, S, B & J type instructions
// I - short immediates and loads
wire [11:0] imm_i;
assign imm_i = d_insn[31:20];
wire [ 4:0] imm_shamt = d_insn[24:20];
// S - stores
wire [11:0] imm_s;
assign imm_s[11:5] = insn_funct7, imm_s[4:0] = insn_rd;
// B - conditionals
wire [12:0] imm_b;
assign {imm_b[12], imm_b[10:5]} = insn_funct7, {imm_b[4:1], imm_b[11]} = insn_rd, imm_b[0] = 1'b0;
// J - unconditional jumps
wire [20:0] imm_j;
assign {imm_j[20], imm_j[10:1], imm_j[11], imm_j[19:12], imm_j[0]} = {d_insn[31:12], 1'b0};
wire [`REG_SIZE] imm_i_sext = {{20{imm_i[11]}}, imm_i[11:0]};
wire [`REG_SIZE] imm_s_sext = {{20{imm_s[11]}}, imm_s[11:0]};
wire [`REG_SIZE] imm_b_sext = {{19{imm_b[12]}}, imm_b[12:0]};
wire [`REG_SIZE] imm_j_sext = {{11{imm_j[20]}}, imm_j[20:0]};
wire insn_lui = insn_opcode == OpLui;
wire insn_auipc = insn_opcode == OpAuipc;
wire insn_jal = insn_opcode == OpJal;
wire insn_jalr = insn_opcode == OpJalr;
wire insn_beq = insn_opcode == OpBranch && decode_state.insn[14:12] == 3'b000;
wire insn_bne = insn_opcode == OpBranch && decode_state.insn[14:12] == 3'b001;
wire insn_blt = insn_opcode == OpBranch && decode_state.insn[14:12] == 3'b100;
wire insn_bge = insn_opcode == OpBranch && decode_state.insn[14:12] == 3'b101;
wire insn_bltu = insn_opcode == OpBranch && decode_state.insn[14:12] == 3'b110;
wire insn_bgeu = insn_opcode == OpBranch && decode_state.insn[14:12] == 3'b111;
wire insn_lb = insn_opcode == OpLoad && decode_state.insn[14:12] == 3'b000;
wire insn_lh = insn_opcode == OpLoad && decode_state.insn[14:12] == 3'b001;
wire insn_lw = insn_opcode == OpLoad && decode_state.insn[14:12] == 3'b010;
wire insn_lbu = insn_opcode == OpLoad && decode_state.insn[14:12] == 3'b100;
wire insn_lhu = insn_opcode == OpLoad && decode_state.insn[14:12] == 3'b101;
wire insn_sb = insn_opcode == OpStore && decode_state.insn[14:12] == 3'b000;
wire insn_sh = insn_opcode == OpStore && decode_state.insn[14:12] == 3'b001;
wire insn_sw = insn_opcode == OpStore && decode_state.insn[14:12] == 3'b010;
wire insn_addi = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b000;
wire insn_slti = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b010;
wire insn_sltiu = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b011;
wire insn_xori = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b100;
wire insn_ori = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b110;
wire insn_andi = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b111;
wire insn_slli = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b001 && decode_state.insn[31:25] == 7'd0;
wire insn_srli = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b101 && decode_state.insn[31:25] == 7'd0;
wire insn_srai = insn_opcode == OpRegImm && decode_state.insn[14:12] == 3'b101 && decode_state.insn[31:25] == 7'b0100000;
wire insn_add = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b000 && decode_state.insn[31:25] == 7'd0;
wire insn_sub = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b000 && decode_state.insn[31:25] == 7'b0100000;
wire insn_sll = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b001 && decode_state.insn[31:25] == 7'd0;
wire insn_slt = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b010 && decode_state.insn[31:25] == 7'd0;
wire insn_sltu = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b011 && decode_state.insn[31:25] == 7'd0;
wire insn_xor = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b100 && decode_state.insn[31:25] == 7'd0;
wire insn_srl = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b101 && decode_state.insn[31:25] == 7'd0;
wire insn_sra = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b101 && decode_state.insn[31:25] == 7'b0100000;
wire insn_or = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b110 && decode_state.insn[31:25] == 7'd0;
wire insn_and = insn_opcode == OpRegReg && decode_state.insn[14:12] == 3'b111 && decode_state.insn[31:25] == 7'd0;
wire insn_mul = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b000;
wire insn_mulh = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b001;
wire insn_mulhsu = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b010;
wire insn_mulhu = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b011;
wire insn_div = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b100;
wire insn_divu = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b101;
wire insn_rem = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b110;
wire insn_remu = insn_opcode == OpRegReg && decode_state.insn[31:25] == 7'd1 && decode_state.insn[14:12] == 3'b111;
wire insn_ecall = insn_opcode == OpEnviron && insn_from_imem[31:7] == 25'd0;
wire insn_fence = insn_opcode == OpMiscMem;
// instantiate the logic variable for rf and cla
logic [`REG_SIZE] rd_data, rd_data_addi, rd_data_add, rd_data_sub, cla_a, cla_b, cla_sum;
logic [`REG_SIZE] rs1_data;
logic [`REG_SIZE] rs2_data;
logic illegal_insn, regfile_we, cla_cin;
// Instance of the RegFile module
RegFile rf (
.rd (w_rd),
.rd_data (w_rd_data),
.rs1 (decode_state.insn[19:15]),
.rs1_data (rs1_data),
.rs2 (decode_state.insn[24:20]),
.rs2_data (rs2_data),
.clk (clk),
.we (w_we),
.rst (rst)
);
wire [255:0] d_disasm; // 8*32
Disasm #(
.PREFIX("D")
) disasm_1decode (
.insn (decode_state.insn),
.disasm(d_disasm)
);
// TODO: your code here, though you will also need to modify some of the code above
// TODO: the testbench requires that your register file instance is named `rf`
/*****************/
/* EXECUTE STAGE */
/*****************/
// decode stage propagates to execute stage components (D -> E)
logic [`REG_SIZE] e_pc_current, e_rs1_data, e_rs2_data, e_rd_data_out;
wire [`REG_SIZE] e_insn;
logic e_we;
logic stage_check;
logic stage_status;
//assign addr_to_dmem = {addr_load[31:2], 2'b00}; // 4B boundary, the LSB 2bits record the position of byte
logic [`REG_SIZE]reg_1;
stage_decode_t execute_state;
always_ff @(posedge clk) begin
if (rst) begin
execute_state <= '{
pc: 0,
insn: 0,
cycle_status: CYCLE_RESET
};
end else begin
execute_state <= '{
pc: decode_state.pc,
insn: decode_state.insn,
cycle_status: e_cycle_status
};
end
end
assign e_pc_current = execute_state.pc;
assign e_insn = execute_state.insn;
// Instance of cla
cla cla_unit(
.a(cla_a),
.b(cla_b),
.cin(cla_cin),
.sum(cla_sum)
);
always_latch begin
// initialize the values
illegal_insn = 1'b0;
halt = 1'b0;
cla_a=32'b0;
cla_b=32'b0;
cla_cin=1'b0;
stage_check = 1'b0;
case (insn_opcode)
OpLui: begin
if (insn_lui) begin
// TODO: start here by implementing lui
e_we = 1'b1;
e_rd_data_out = {execute_state.insn[31:12], 12'b0};
end
end
//OpAuipc : begin
// e_we = 1'b1;
// rd_data = e_pc_current + {e_insn[31:12], 12'b0};
//end
OpBranch: begin
if (insn_beq) begin
if (e_rs1_data == e_rs2_data) begin
pcNext = e_pc_current + imm_b_sext;
e_cycle_status = CYCLE_TAKEN_BRANCH;
end
end
else if (insn_bne) begin
if (e_rs1_data != e_rs2_data) begin
pcNext = e_pc_current + imm_b_sext;
e_cycle_status = CYCLE_TAKEN_BRANCH;
end
end
else if (insn_blt) begin
if ($signed(e_rs1_data) < $signed(e_rs2_data)) begin
pcNext = e_pc_current + imm_b_sext;
e_cycle_status = CYCLE_TAKEN_BRANCH;
end
end
else if (insn_bge) begin
if ($signed(e_rs1_data) >= $signed(e_rs2_data)) begin
pcNext = e_pc_current + imm_b_sext;
e_cycle_status = CYCLE_TAKEN_BRANCH;
end
end
else if (insn_bltu) begin
if ($unsigned(e_rs1_data) < $unsigned(e_rs2_data)) begin
pcNext = e_pc_current + imm_b_sext;
e_cycle_status = CYCLE_TAKEN_BRANCH;
end
end
else if (insn_bgeu) begin
if ($unsigned(e_rs1_data) >= $unsigned(e_rs2_data)) begin
pcNext = e_pc_current + imm_b_sext;
e_cycle_status = CYCLE_TAKEN_BRANCH;
end
end
end
OpRegImm: begin
e_we = 1'b1;
if (insn_addi) begin
cla_a = rs1_data;
cla_b = imm_i_sext;
e_rd_data_out = cla_sum;
end
else if (insn_slti) begin
e_rd_data_out = ($signed(e_rs1_data) < $signed(imm_i_sext)) ? 32'b1 : 32'b0;
end
else if (insn_sltiu) begin
e_rd_data_out = ($unsigned(e_rs1_data) < $unsigned(imm_i_sext)) ? 32'b1 : 32'b0;
end
else if (insn_xori) begin
e_rd_data_out = e_rs1_data ^ imm_i_sext;
end
else if (insn_ori) begin
e_rd_data_out = e_rs1_data | imm_i_sext;
end
else if (insn_andi) begin
e_rd_data_out = e_rs1_data & imm_i_sext;
end
else if (insn_slli) begin
e_rd_data_out = e_rs1_data << imm_shamt; // imm_shamt = imm_i[4:0]
end
else if (insn_srli) begin
e_rd_data_out = e_rs1_data >> imm_shamt;
end
else if (insn_srai) begin
e_rd_data_out = $signed(e_rs1_data) >>> imm_shamt;
end
end
OpRegReg: begin
e_we = 1'b1;
if (insn_add) begin
cla_a = e_rs1_data;
cla_b = e_rs2_data;
e_rd_data_out = cla_sum;
end
else if (insn_sub) begin
cla_a = e_rs1_data;
cla_b = ~e_rs2_data;
cla_cin = 1'b1;
e_rd_data_out = cla_sum;
end
else if (insn_sll) begin
e_rd_data_out = rs1_data << rs2_data[4:0];
end
else if (insn_slt) begin
e_rd_data_out = ($signed(e_rs1_data) < $signed(e_rs2_data)) ? 32'b1 : 32'b0;
end
else if (insn_sltu) begin
e_rd_data_out = ($unsigned(e_rs1_data) < $unsigned(e_rs2_data)) ? 32'b1 : 32'b0;
end
else if (insn_xor) begin
e_rd_data_out = e_rs1_data ^ e_rs2_data;
end
else if (insn_srl) begin
e_rd_data_out = e_rs1_data >> e_rs2_data[4:0];
end
else if (insn_sra) begin
e_rd_data_out = $signed(e_rs1_data) >>> e_rs2_data[4:0];
end
else if (insn_or) begin
e_rd_data_out = e_rs1_data | e_rs2_data;
end
else if (insn_and) begin
e_rd_data_out = e_rs1_data & e_rs2_data;
end
end
OpEnviron: begin
if (insn_ecall) begin
halt=1'b1;
end
end
OpMiscMem: begin
if (insn_fence) begin
end
end
default: begin
illegal_insn = 1'b1;
end
endcase
end
/****************/
/* MEMORY STAGE */
/****************/
logic [`REG_SIZE] m_pc_current, m_rd_data;
logic [4:0] m_rd, w_rd;
logic m_we;
wire [`REG_SIZE] m_insn;
cycle_status_e m_cycle_status;
stage_decode_t memory_state;
always_ff @(posedge clk) begin
if (rst) begin
memory_state <= '{
pc: 0,
insn: 0,
cycle_status: CYCLE_RESET
};
m_we <= 0;
end else begin
begin
memory_state <= '{
pc: e_pc_current,
insn: e_insn,
cycle_status: e_cycle_status
};
m_we <= e_we;
m_rd_data <= e_rd_data_out;
m_rd <= insn_rd;
end
end
end
assign m_pc_current = memory_state.pc;
assign m_insn = memory_state.insn;
/********************/
/* WRITE BACK STAGE */
/********************/
logic [`REG_SIZE] w_pc_current, w_rd_data;
logic w_we;
wire [`REG_SIZE] w_insn;
stage_decode_t writeback_state;
always_ff @(posedge clk) begin
if (rst) begin
writeback_state <= '{
pc: 0,
insn: 0,
cycle_status: CYCLE_RESET
};
end else begin
begin
writeback_state <= '{
pc: m_pc_current,
insn: m_insn,
cycle_status: m_cycle_status
};
w_we <= m_we;
w_rd <= m_rd;
w_rd_data <= m_rd_data;
end
end
end
assign trace_writeback_pc = writeback_state.pc;
assign trace_writeback_insn = writeback_state.insn;
assign trace_writeback_cycle_status = writeback_state.cycle_status;
endmodule
module MemorySingleCycle #(
parameter int NUM_WORDS = 512
) (
// rst for both imem and dmem
input wire rst,
// clock for both imem and dmem. The memory reads/writes on @(negedge clk)
input wire clk,
// must always be aligned to a 4B boundary
input wire [`REG_SIZE] pc_to_imem,
// the value at memory location pc_to_imem
output logic [`REG_SIZE] insn_from_imem,
// must always be aligned to a 4B boundary
input wire [`REG_SIZE] addr_to_dmem,
// the value at memory location addr_to_dmem
output logic [`REG_SIZE] load_data_from_dmem,
// the value to be written to addr_to_dmem, controlled by store_we_to_dmem
input wire [`REG_SIZE] store_data_to_dmem,
// Each bit determines whether to write the corresponding byte of store_data_to_dmem to memory location addr_to_dmem.
// E.g., 4'b1111 will write 4 bytes. 4'b0001 will write only the least-significant byte.
input wire [3:0] store_we_to_dmem
);
// memory is arranged as an array of 4B words
logic [`REG_SIZE] mem[NUM_WORDS];
initial begin
$readmemh("mem_initial_contents.hex", mem, 0);
end
always_comb begin
// memory addresses should always be 4B-aligned
assert (pc_to_imem[1:0] == 2'b00);
assert (addr_to_dmem[1:0] == 2'b00);
end
localparam int AddrMsb = $clog2(NUM_WORDS) + 1;
localparam int AddrLsb = 2;
always @(negedge clk) begin
if (rst) begin
end else begin
insn_from_imem <= mem[{pc_to_imem[AddrMsb:AddrLsb]}];
end
end
always @(negedge clk) begin
if (rst) begin
end else begin
if (store_we_to_dmem[0]) begin
mem[addr_to_dmem[AddrMsb:AddrLsb]][7:0] <= store_data_to_dmem[7:0];
end
if (store_we_to_dmem[1]) begin
mem[addr_to_dmem[AddrMsb:AddrLsb]][15:8] <= store_data_to_dmem[15:8];
end
if (store_we_to_dmem[2]) begin
mem[addr_to_dmem[AddrMsb:AddrLsb]][23:16] <= store_data_to_dmem[23:16];
end
if (store_we_to_dmem[3]) begin
mem[addr_to_dmem[AddrMsb:AddrLsb]][31:24] <= store_data_to_dmem[31:24];
end
// dmem is "read-first": read returns value before the write
load_data_from_dmem <= mem[{addr_to_dmem[AddrMsb:AddrLsb]}];
end
end
endmodule
/* This design has just one clock for both processor and memory. */
module RiscvProcessor (
input wire clk,
input wire rst,
output logic halt,
output wire [`REG_SIZE] trace_writeback_pc,
output wire [`INSN_SIZE] trace_writeback_insn,
output cycle_status_e trace_writeback_cycle_status
);
wire [`INSN_SIZE] insn_from_imem;
wire [`REG_SIZE] pc_to_imem, mem_data_addr, mem_data_loaded_value, mem_data_to_write;
wire [3:0] mem_data_we;
MemorySingleCycle #(
.NUM_WORDS(8192)
) mem_pipeline (
.rst (rst),
.clk (clk),
// imem is read-only
.pc_to_imem (pc_to_imem),
.insn_from_imem (insn_from_imem),
// dmem is read-write
.addr_to_dmem (mem_data_addr),
.load_data_from_dmem(mem_data_loaded_value),
.store_data_to_dmem (mem_data_to_write),
.store_we_to_dmem (mem_data_we)
);
DatapathPipelined datapath (
.clk(clk),
.rst(rst),
.pc_to_imem(pc_to_imem),
.insn_from_imem(insn_from_imem),
.addr_to_dmem(mem_data_addr),
.store_data_to_dmem(mem_data_to_write),
.store_we_to_dmem(mem_data_we),
.load_data_from_dmem(mem_data_loaded_value),
.halt(halt),
.trace_writeback_pc(trace_writeback_pc),
.trace_writeback_insn(trace_writeback_insn),
.trace_writeback_cycle_status(trace_writeback_cycle_status)
);
endmodule