-
Notifications
You must be signed in to change notification settings - Fork 648
/
csr_regfile.sv
764 lines (713 loc) · 34.8 KB
/
csr_regfile.sv
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
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Author: Florian Zaruba, ETH Zurich
// Date: 05.05.2017
// Description: CSR Register File as specified by RISC-V
import ariane_pkg::*;
module csr_regfile #(
parameter int ASID_WIDTH = 1
)(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic [63:0] time_i, // Platform Timer
input logic time_irq_i, // Timer threw a interrupt
// send a flush request out if a CSR with a side effect has changed (e.g. written)
output logic flush_o,
output logic halt_csr_o, // halt requested
// Debug CSR Port
input logic debug_csr_req_i, // Request from debug to read the CSR regfile
input logic [11:0] debug_csr_addr_i, // Address of CSR
input logic debug_csr_we_i, // Is it a read or write?
input logic [63:0] debug_csr_wdata_i, // Data to write
output logic [63:0] debug_csr_rdata_o, // Read data
// commit acknowledge
input logic commit_ack_i, // Commit acknowledged a instruction -> increase instret CSR
// Core and Cluster ID
input logic [3:0] core_id_i, // Core ID is considered static
input logic [5:0] cluster_id_i, // Cluster ID is considered static
input logic [63:0] boot_addr_i, // Address from which to start booting, mtvec is set to the same address
// we are taking an exception
input exception_t ex_i, // We've got an exception from the commit stage, take its
input fu_op csr_op_i, // Operation to perform on the CSR file
input logic [11:0] csr_addr_i, // Address of the register to read/write
input logic [63:0] csr_wdata_i, // Write data in
output logic [63:0] csr_rdata_o, // Read data out
input logic [63:0] pc_i, // PC of instruction accessing the CSR
output exception_t csr_exception_o, // attempts to access a CSR without appropriate privilege
// level or to write a read-only register also
// raises illegal instruction exceptions.
// Interrupts/Exceptions
output logic [63:0] epc_o, // Output the exception PC to PC Gen, the correct CSR (mepc, sepc) is set accordingly
output logic eret_o, // Return from exception, set the PC of epc_o
output logic [63:0] trap_vector_base_o, // Output base of exception vector, correct CSR is output (mtvec, stvec)
output priv_lvl_t priv_lvl_o, // Current privilege level the CPU is in
// MMU
output logic en_translation_o, // enable VA translation
output logic en_ld_st_translation_o, // enable VA translation for load and stores
output priv_lvl_t ld_st_priv_lvl_o, // Privilege level at which load and stores should happen
output logic sum_o,
output logic mxr_o,
output logic [43:0] satp_ppn_o,
output logic [ASID_WIDTH-1:0] asid_o,
// external interrupts
input logic [1:0] irq_i, // external interrupt in
input logic ipi_i, // inter processor interrupt -> connected to machine mode sw
// Visualization Support
output logic tvm_o, // trap virtual memory
output logic tw_o, // timeout wait
output logic tsr_o, // trap sret
// Caches
output logic icache_en_o, // L1 ICache Enable
output logic dcache_en_o, // L1 DCache Enable
// Performance Counter
output logic [11:0] perf_addr_o, // address to performance counter module
output logic [63:0] perf_data_o, // write data to performance counter module
input logic [63:0] perf_data_i, // read data from performance counter module
output logic perf_we_o
);
// internal signal to keep track of access exceptions
logic read_access_exception, update_access_exception;
logic csr_we, csr_read;
logic [63:0] csr_wdata, csr_rdata;
priv_lvl_t trap_to_priv_lvl;
// register for enabling load store address translation, this is critical, hence the register
logic en_ld_st_translation_d, en_ld_st_translation_q;
logic mret; // return from M-mode exception
logic sret; // return from S-mode exception
csr_t csr_addr;
// ----------------
// Assignments
// ----------------
// Debug MUX
assign csr_addr = csr_t'(((debug_csr_req_i) ? debug_csr_addr_i : csr_addr_i));
// Output the read data directly
assign debug_csr_rdata_o = csr_rdata;
// ----------------
// CSR Registers
// ----------------
// privilege level register
priv_lvl_t priv_lvl_d, priv_lvl_q;
typedef struct packed {
logic sd; // signal dirty - read-only - hardwired zero
logic [62:36] wpri4; // writes preserved reads ignored
logic [1:0] sxl; // variable supervisor mode xlen - hardwired to zero
logic [1:0] uxl; // variable user mode xlen - hardwired to zero
logic [8:0] wpri3; // writes preserved reads ignored
logic tsr; // trap sret
logic tw; // time wait
logic tvm; // trap virtual memory
logic mxr; // make executable readable
logic sum; // permit supervisor user memory access
logic mprv; // modify privilege - privilege level for ld/st
logic [1:0] xs; // extension register - hardwired to zero
logic [1:0] fs; // extension register - hardwired to zero
priv_lvl_t mpp; // holds the previous privilege mode up to machine
logic [1:0] wpri2; // writes preserved reads ignored
logic spp; // holds the previous privilege mode up to supervisor
logic mpie; // machine interrupts enable bit active prior to trap
logic wpri1; // writes preserved reads ignored
logic spie; // supervisor interrupts enable bit active prior to trap
logic upie; // user interrupts enable bit active prior to trap - hardwired to zero
logic mie; // machine interrupts enable
logic wpri0; // writes preserved reads ignored
logic sie; // supervisor interrupts enable
logic uie; // user interrupts enable - hardwired to zero
} status_t;
status_t mstatus_q, mstatus_d;
logic [63:0] mtvec_q, mtvec_d;
logic [63:0] medeleg_q, medeleg_d;
logic [63:0] mideleg_q, mideleg_d;
logic [63:0] mip_q, mip_d;
logic [63:0] pmpcfg0_q, pmpcfg0_d;
logic [63:0] pmpaddr0_q, pmpaddr0_d;
logic [63:0] mie_q, mie_d;
logic [63:0] mscratch_q, mscratch_d;
logic [63:0] mepc_q, mepc_d;
logic [63:0] mcause_q, mcause_d;
logic [63:0] mtval_q, mtval_d;
logic [63:0] stvec_q, stvec_d;
logic [63:0] sscratch_q, sscratch_d;
logic [63:0] sepc_q, sepc_d;
logic [63:0] scause_q, scause_d;
logic [63:0] stval_q, stval_d;
logic [63:0] dcache_q, dcache_d;
logic [63:0] icache_q, icache_d;
logic wfi_d, wfi_q;
logic [63:0] cycle_q, cycle_d;
logic [63:0] instret_q, instret_d;
typedef struct packed {
logic [3:0] mode;
logic [15:0] asid;
logic [43:0] ppn;
} satp_t;
satp_t satp_q, satp_d;
// ----------------
// CSR Read logic
// ----------------
always_comb begin : csr_read_process
// a read access exception can only occur if we attempt to read a CSR which does not exist
read_access_exception = 1'b0;
csr_rdata = 64'b0;
// feed through address of performance counter
perf_addr_o = csr_addr.address;
if (csr_read) begin
case (csr_addr.address)
CSR_SSTATUS: csr_rdata = mstatus_q & 64'h3fffe1fee;
CSR_SIE: csr_rdata = mie_q & mideleg_q;
CSR_SIP: csr_rdata = mip_q & mideleg_q;
CSR_STVEC: csr_rdata = stvec_q;
CSR_SCOUNTEREN: csr_rdata = 64'b0; // not implemented
CSR_SSCRATCH: csr_rdata = sscratch_q;
CSR_SEPC: csr_rdata = sepc_q;
CSR_SCAUSE: csr_rdata = scause_q;
CSR_STVAL: csr_rdata = stval_q;
CSR_SATP: begin
// intercept reads to SATP if in S-Mode and TVM is enabled
if (priv_lvl_q == PRIV_LVL_S && mstatus_q.tvm)
read_access_exception = 1'b1;
else
csr_rdata = satp_q;
end
CSR_MSTATUS: csr_rdata = mstatus_q;
CSR_MISA: csr_rdata = ISA_CODE;
CSR_MEDELEG: csr_rdata = medeleg_q;
CSR_MIDELEG: csr_rdata = mideleg_q;
CSR_MIE: csr_rdata = mie_q;
CSR_MTVEC: csr_rdata = mtvec_q;
CSR_MCOUNTEREN: csr_rdata = 64'b0; // not implemented
CSR_MSCRATCH: csr_rdata = mscratch_q;
CSR_MEPC: csr_rdata = mepc_q;
CSR_MCAUSE: csr_rdata = mcause_q;
CSR_MTVAL: csr_rdata = mtval_q;
CSR_MIP: csr_rdata = mip_q;
// Placeholders for M-mode protection
CSR_PMPCFG0: csr_rdata = pmpcfg0_q;
CSR_PMPADDR0: csr_rdata = pmpaddr0_q;
CSR_MVENDORID: csr_rdata = 64'b0; // not implemented
CSR_MARCHID: csr_rdata = 64'b0; // PULP, anonymous source (no allocated ID yet)
CSR_MIMPID: csr_rdata = 64'b0; // not implemented
CSR_MHARTID: csr_rdata = {53'b0, cluster_id_i[5:0], 1'b0, core_id_i[3:0]};
CSR_MCYCLE: csr_rdata = cycle_q;
CSR_MINSTRET: csr_rdata = instret_q;
CSR_DCACHE: csr_rdata = dcache_q;
CSR_ICACHE: csr_rdata = icache_q;
// Counters and Timers
CSR_CYCLE: csr_rdata = cycle_q;
CSR_TIME: csr_rdata = time_i;
CSR_INSTRET: csr_rdata = instret_q;
CSR_L1_ICACHE_MISS,
CSR_L1_DCACHE_MISS,
CSR_ITLB_MISS,
CSR_DTLB_MISS,
CSR_LOAD,
CSR_STORE,
CSR_EXCEPTION,
CSR_EXCEPTION_RET,
CSR_BRANCH_JUMP,
CSR_CALL,
CSR_RET,
CSR_MIS_PREDICT: csr_rdata = perf_data_i;
default: read_access_exception = 1'b1;
endcase
end
end
// ---------------------------
// CSR Write and update logic
// ---------------------------
always_comb begin : csr_update
automatic satp_t sapt;
automatic logic [63:0] mip;
sapt = satp_q;
mip = csr_wdata & 64'h33;
// only USIP, SSIP, UTIP, STIP are write-able
eret_o = 1'b0;
flush_o = 1'b0;
update_access_exception = 1'b0;
perf_we_o = 1'b0;
perf_data_o = 'b0;
priv_lvl_d = priv_lvl_q;
mstatus_d = mstatus_q;
mtvec_d = mtvec_q;
medeleg_d = medeleg_q;
mideleg_d = mideleg_q;
mip_d = mip_q;
mie_d = mie_q;
mepc_d = mepc_q;
mcause_d = mcause_q;
mscratch_d = mscratch_q;
mtval_d = mtval_q;
dcache_d = dcache_q;
icache_d = icache_q;
sepc_d = sepc_q;
scause_d = scause_q;
stvec_d = stvec_q;
sscratch_d = sscratch_q;
stval_d = stval_q;
satp_d = satp_q;
en_ld_st_translation_d = en_ld_st_translation_q;
// check for correct access rights and that we are writing
if (csr_we) begin
case (csr_addr.address)
// sstatus is a subset of mstatus - mask it accordingly
CSR_SSTATUS: begin
mstatus_d = csr_wdata & 64'h3fffe1fee;
// this instruction has side-effects
flush_o = 1'b1;
end
// even machine mode interrupts can be visible and set-able to supervisor
// if the corresponding bit in mideleg is set
CSR_SIE: begin
// the mideleg makes sure only delegate-able register (and therefore also only implemented registers)
// are written
for (int unsigned i = 0; i < 64; i++)
if (mideleg_q[i])
mie_d[i] = csr_wdata[i];
end
CSR_SIP: begin
for (int unsigned i = 0; i < 64; i++)
if (mideleg_q[i])
mip_d[i] = mip[i];
end
CSR_SCOUNTEREN:;
CSR_STVEC: stvec_d = {csr_wdata[63:2], 1'b0, csr_wdata[0]};
CSR_SSCRATCH: sscratch_d = csr_wdata;
CSR_SEPC: sepc_d = {csr_wdata[63:1], 1'b0};
CSR_SCAUSE: scause_d = csr_wdata;
CSR_STVAL: stval_d = csr_wdata;
// supervisor address translation and protection
CSR_SATP: begin
// intercept SATP writes if in S-Mode and TVM is enabled
if (priv_lvl_q == PRIV_LVL_S && mstatus_q.tvm)
update_access_exception = 1'b1;
else begin
sapt = satp_t'(csr_wdata);
// only make ASID_LEN - 1 bit stick, that way software can figure out how many ASID bits are supported
sapt.asid = sapt.asid & {{(16-ASID_WIDTH){1'b0}}, {ASID_WIDTH{1'b1}}};
satp_d = sapt;
end
// changing the mode can have side-effects on address translation (e.g.: other instructions), re-fetch
// the next instruction by executing a flush
flush_o = 1'b1;
end
CSR_MSTATUS: begin
mstatus_d = csr_wdata;
mstatus_d.sxl = 2'b10;
mstatus_d.uxl = 2'b10;
// hardwired zero registers
mstatus_d.sd = 1'b0;
mstatus_d.xs = 2'b0;
mstatus_d.fs = 2'b0;
mstatus_d.upie = 1'b0;
mstatus_d.uie = 1'b0;
// this register has side-effects on other registers, flush the pipeline
flush_o = 1'b1;
end
// MISA is WARL (Write Any Value, Reads Legal Value)
CSR_MISA:;
// machine exception delegation register
// 0 - 15 exceptions supported
CSR_MEDELEG: medeleg_d = csr_wdata & 64'hF7FF;
// machine interrupt delegation register
// we do not support user interrupt delegation
CSR_MIDELEG: mideleg_d = csr_wdata & 64'hBBB;
// mask the register so that unsupported interrupts can never be set
CSR_MIE: mie_d = csr_wdata & 64'hBBB; // we only support supervisor and m-mode interrupts
CSR_MTVEC: begin
mtvec_d = {csr_wdata[63:2], 1'b0, csr_wdata[0]};
// we are in vector mode, this implementation requires the additional
// alignment constraint of 64 * 4 bytes
if (csr_wdata[0])
mtvec_d = {csr_wdata[63:8], 7'b0, csr_wdata[0]};
end
CSR_MCOUNTEREN:;
CSR_MSCRATCH: mscratch_d = csr_wdata;
CSR_MEPC: mepc_d = {csr_wdata[63:1], 1'b0};
CSR_MCAUSE: mcause_d = csr_wdata;
CSR_MTVAL: mtval_d = csr_wdata;
CSR_MIP: mip_d = mip;
// Placeholders for M-mode protection
CSR_PMPCFG0: pmpcfg0_d = csr_wdata;
CSR_PMPADDR0: pmpaddr0_d = csr_wdata;
CSR_MCYCLE: cycle_d = csr_wdata;
CSR_MINSTRET: instret_d = csr_wdata;
CSR_DCACHE: dcache_d = csr_wdata[0]; // enable bit
CSR_ICACHE: icache_d = csr_wdata[0]; // enable bit
CSR_L1_ICACHE_MISS,
CSR_L1_DCACHE_MISS,
CSR_ITLB_MISS,
CSR_DTLB_MISS,
CSR_LOAD,
CSR_STORE,
CSR_EXCEPTION,
CSR_EXCEPTION_RET,
CSR_BRANCH_JUMP,
CSR_CALL,
CSR_RET,
CSR_MIS_PREDICT: begin
perf_data_o = csr_wdata;
perf_we_o = 1'b1;
end
default: update_access_exception = 1'b1;
endcase
end
// ---------------------
// External Interrupts
// ---------------------
// Machine Mode External Interrupt Pending
mip_d[11] = mie_q[11] & irq_i[1];
mip_d[9] = mie_q[9] & irq_i[0];
// Machine software interrupt
mip_d[3] = mie_q[3] & ipi_i;
// Timer interrupt pending, coming from platform timer
mip_d[7] = time_irq_i;
// -----------------------
// Manage Exception Stack
// -----------------------
// update exception CSRs
// we got an exception update cause, pc and stval register
trap_to_priv_lvl = PRIV_LVL_M;
// Exception is taken
if (ex_i.valid) begin
// do not flush, flush is reserved for CSR writes with side effects
flush_o = 1'b0;
// figure out where to trap to
// a m-mode trap might be delegated if we are taking it in S mode
// first figure out if this was an exception or an interrupt e.g.: look at bit 63
// the cause register can only be 6 bits long (as we only support 64 exceptions)
if ((ex_i.cause[63] && mideleg_q[ex_i.cause[5:0]]) ||
(~ex_i.cause[63] && medeleg_q[ex_i.cause[5:0]])) begin
// traps never transition from a more-privileged mode to a less privileged mode
// so if we are already in M mode, stay there
trap_to_priv_lvl = (priv_lvl_q == PRIV_LVL_M) ? PRIV_LVL_M : PRIV_LVL_S;
end
// trap to supervisor mode
if (trap_to_priv_lvl == PRIV_LVL_S) begin
// update sstatus
mstatus_d.sie = 1'b0;
mstatus_d.spie = mstatus_q.sie;
// this can either be user or supervisor mode
mstatus_d.spp = logic'(priv_lvl_q);
// set cause
scause_d = ex_i.cause;
// set epc
sepc_d = pc_i;
// set mtval or stval
stval_d = ex_i.tval;
// trap to machine mode
end else begin
// update mstatus
mstatus_d.mie = 1'b0;
mstatus_d.mpie = mstatus_q.mie;
// save the previous privilege mode
mstatus_d.mpp = priv_lvl_q;
mcause_d = ex_i.cause;
// set epc
mepc_d = pc_i;
// set mtval or stval
mtval_d = ex_i.tval;
end
priv_lvl_d = trap_to_priv_lvl;
end
// ------------------------------
// MPRV - Modify Privilege Level
// ------------------------------
// Set the address translation at which the load and stores should occur
// we can use the previous values since changing the address translation will always involve a pipeline flush
if (mstatus_q.mprv && satp_q.mode == 4'h8 && (mstatus_q.mpp != PRIV_LVL_M))
en_ld_st_translation_d = 1'b1;
else // otherwise we go with the regular settings
en_ld_st_translation_d = en_translation_o;
ld_st_priv_lvl_o = (mstatus_q.mprv) ? mstatus_q.mpp : priv_lvl_o;
en_ld_st_translation_o = en_ld_st_translation_q;
// ------------------------------
// Return from Environment
// ------------------------------
// When executing an xRET instruction, supposing xPP holds the value y, xIE is set to xPIE; the privilege
// mode is changed to y; xPIE is set to 1; and xPP is set to U
if (mret) begin
// return from exception, IF doesn't care from where we are returning
eret_o = 1'b1;
// return to the previous privilege level and restore all enable flags
// get the previous machine interrupt enable flag
mstatus_d.mie = mstatus_q.mpie;
// restore the previous privilege level
priv_lvl_d = mstatus_q.mpp;
// set mpp to user mode
mstatus_d.mpp = PRIV_LVL_U;
// set mpie to 1
mstatus_d.mpie = 1'b1;
end
if (sret) begin
// return from exception, IF doesn't care from where we are returning
eret_o = 1'b1;
// return the previous supervisor interrupt enable flag
mstatus_d.sie = mstatus_d.spie;
// restore the previous privilege level
priv_lvl_d = priv_lvl_t'({1'b0, mstatus_d.spp});
// set spp to user mode
mstatus_d.spp = logic'(PRIV_LVL_U);
// set spie to 1
mstatus_d.spie = 1'b1;
end
// --------------------
// Counters
// --------------------
instret_d = instret_q;
// just increment the cycle count
cycle_d = cycle_q + 1'b1;
// increase instruction retired counter
if (commit_ack_i) begin
instret_d = instret_q + 1'b1;
end
end
// ---------------------------
// CSR OP Select Logic
// ---------------------------
always_comb begin : csr_op_logic
csr_wdata = csr_wdata_i;
csr_we = 1'b1;
csr_read = 1'b1;
mret = 1'b0;
sret = 1'b0;
unique case (csr_op_i)
CSR_WRITE: csr_wdata = csr_wdata_i;
CSR_SET: csr_wdata = csr_wdata_i | csr_rdata;
CSR_CLEAR: csr_wdata = (~csr_wdata_i) & csr_rdata;
CSR_READ: csr_we = 1'b0;
SRET: begin
// the return should not have any write or read side-effects
csr_we = 1'b0;
csr_read = 1'b0;
sret = 1'b1; // signal a return from supervisor mode
end
MRET: begin
// the return should not have any write or read side-effects
csr_we = 1'b0;
csr_read = 1'b0;
mret = 1'b1; // signal a return from machine mode
end
default: begin
csr_we = 1'b0;
csr_read = 1'b0;
end
endcase
// if we are retiring an exception do not return from exception
if (ex_i.valid) begin
mret = 1'b0;
sret = 1'b0;
end
// ------------------------------
// Debug Multiplexer (Priority)
// ------------------------------
if (debug_csr_req_i) begin
// Use the data supplied by the debug unit
csr_wdata = debug_csr_wdata_i;
csr_we = debug_csr_we_i;
csr_read = ~debug_csr_we_i;
end
end
logic interrupt_global_enable;
// --------------------------------------
// Exception Control & Interrupt Control
// --------------------------------------
always_comb begin : exception_ctrl
automatic logic [63:0] interrupt_cause;
interrupt_cause = '0;
// wait for interrupt register
wfi_d = wfi_q;
csr_exception_o = {
64'b0, 64'b0, 1'b0
};
// -----------------
// Interrupt Control
// -----------------
// we decode an interrupt the same as an exception, hence it will be taken if the instruction did not
// throw any previous exception.
// we have three interrupt sources: external interrupts, software interrupts, timer interrupts (order of precedence)
// for two privilege levels: Supervisor and Machine Mode
// Supervisor Timer Interrupt
if (mie_q[S_TIMER_INTERRUPT[5:0]] && mip_q[S_TIMER_INTERRUPT[5:0]])
interrupt_cause = S_TIMER_INTERRUPT;
// Supervisor Software Interrupt
if (mie_q[S_SW_INTERRUPT[5:0]] && mip_q[S_SW_INTERRUPT[5:0]])
interrupt_cause = S_SW_INTERRUPT;
// Supervisor External Interrupt
if (mie_q[S_EXT_INTERRUPT[5:0]] && mip_q[S_EXT_INTERRUPT[5:0]])
interrupt_cause = S_EXT_INTERRUPT;
// Machine Timer Interrupt
if (mip_q[M_TIMER_INTERRUPT[5:0]] && mie_q[M_TIMER_INTERRUPT[5:0]])
interrupt_cause = M_TIMER_INTERRUPT;
// Machine Mode Software Interrupt
if (mip_q[M_SW_INTERRUPT[5:0]] && mie_q[M_SW_INTERRUPT[5:0]])
interrupt_cause = M_SW_INTERRUPT;
// Machine Mode External Interrupt
if (mip_q[M_EXT_INTERRUPT[5:0]] && mie_q[M_EXT_INTERRUPT[5:0]])
interrupt_cause = M_EXT_INTERRUPT;
// An interrupt i will be taken if bit i is set in both mip and mie, and if interrupts are globally enabled.
// By default, M-mode interrupts are globally enabled if the hart’s current privilege mode is less
// than M, or if the current privilege mode is M and the MIE bit in the mstatus register is set.
interrupt_global_enable = (mstatus_q.mie && (priv_lvl_q == PRIV_LVL_M)) || (priv_lvl_q inside {PRIV_LVL_S, PRIV_LVL_U});
if (interrupt_cause[63] && interrupt_global_enable) begin
// we can set the cause here
csr_exception_o.cause = interrupt_cause;
// However, if bit i in mideleg is set, interrupts are considered to be globally enabled if the hart’s current privilege
// mode equals the delegated privilege mode (S or U) and that mode’s interrupt enable bit
// (SIE or UIE in mstatus) is set, or if the current privilege mode is less than the delegated privilege mode.
if (mideleg_q[interrupt_cause[5:0]]) begin
if ((mstatus_q.sie && priv_lvl_q == PRIV_LVL_S) || priv_lvl_q == PRIV_LVL_U)
csr_exception_o.valid = 1'b1;
end else begin
csr_exception_o.valid = 1'b1;
end
end
// -----------------
// Privilege Check
// -----------------
// only if this is not a CSR request from debug (debug has M privilege status)
if (!debug_csr_req_i) begin
// if we are reading or writing, check for the correct privilege level
if (csr_we || csr_read) begin
if ((priv_lvl_t'(priv_lvl_q & csr_addr.csr_decode.priv_lvl) != csr_addr.csr_decode.priv_lvl)) begin
csr_exception_o.cause = ILLEGAL_INSTR;
csr_exception_o.valid = 1'b1;
end
end
// we got an exception in one of the processes above
// throw an illegal instruction exception
if (update_access_exception || read_access_exception) begin
csr_exception_o.cause = ILLEGAL_INSTR;
// we don't set the tval field as this will be set by the commit stage
// this spares the extra wiring from commit to CSR and back to commit
csr_exception_o.valid = 1'b1;
end
end
// -------------------
// Wait for Interrupt
// -------------------
// if there is any interrupt pending un-stall the core
if (|mip_q) begin
wfi_d = 1'b0;
// or alternatively if there is no exception pending, wait here for the interrupt
end else if (csr_op_i == WFI && !ex_i.valid) begin
wfi_d = 1'b1;
end
end
// -------------------
// Output Assignments
// -------------------
assign csr_rdata_o = csr_rdata;
assign priv_lvl_o = priv_lvl_q;
// MMU outputs
assign satp_ppn_o = satp_q.ppn;
assign asid_o = satp_q.asid[ASID_WIDTH-1:0];
assign sum_o = mstatus_q.sum;
// we support bare memory addressing and SV39
assign en_translation_o = (satp_q.mode == 4'h8 && priv_lvl_q != PRIV_LVL_M) ? 1'b1 : 1'b0;
assign mxr_o = mstatus_q.mxr;
assign tvm_o = mstatus_q.tvm;
assign tw_o = mstatus_q.tw;
assign tsr_o = mstatus_q.tsr;
assign halt_csr_o = wfi_q;
assign icache_en_o = icache_q[0];
assign dcache_en_o = dcache_q[0];
// output assignments dependent on privilege mode
always_comb begin : priv_output
trap_vector_base_o = {mtvec_q[63:2], 2'b0};
// output user mode stvec
if (trap_to_priv_lvl == PRIV_LVL_S) begin
trap_vector_base_o = {stvec_q[63:2], 2'b0};
end
// check if we are in vectored mode, if yes then do BASE + 4 * cause
// we are imposing an additional alignment-constraint of 64 * 4 bytes since
// we want to spare the costly addition
if ((mtvec_q[0] || stvec_q[0]) && csr_exception_o.cause[63]) begin
trap_vector_base_o[7:2] = csr_exception_o.cause[5:0];
end
epc_o = mepc_q;
// we are returning from supervisor mode, so take the sepc register
if (sret) begin
epc_o = sepc_q;
end
end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
priv_lvl_q <= PRIV_LVL_M;
// machine mode registers
mstatus_q <= 64'b0;
mtvec_q <= {boot_addr_i[63:2], 2'b0}; // set to boot address + direct mode
medeleg_q <= 64'b0;
mideleg_q <= 64'b0;
mip_q <= 64'b0;
mie_q <= 64'b0;
mepc_q <= 64'b0;
mcause_q <= 64'b0;
mscratch_q <= 64'b0;
mtval_q <= 64'b0;
dcache_q <= 64'b1;
icache_q <= 64'b1;
// supervisor mode registers
sepc_q <= 64'b0;
scause_q <= 64'b0;
stvec_q <= 64'b0;
sscratch_q <= 64'b0;
stval_q <= 64'b0;
satp_q <= 64'b0;
// timer and counters
cycle_q <= 64'b0;
instret_q <= 64'b0;
// aux registers
en_ld_st_translation_q <= 1'b0;
// wait for interrupt
wfi_q <= 1'b0;
end else begin
priv_lvl_q <= priv_lvl_d;
// machine mode registers
mstatus_q <= mstatus_d;
mtvec_q <= mtvec_d;
medeleg_q <= medeleg_d;
mideleg_q <= mideleg_d;
mip_q <= mip_d;
mie_q <= mie_d;
mepc_q <= mepc_d;
mcause_q <= mcause_d;
mscratch_q <= mscratch_d;
mtval_q <= mtval_d;
dcache_q <= dcache_d;
icache_q <= icache_d;
// supervisor mode registers
sepc_q <= sepc_d;
scause_q <= scause_d;
stvec_q <= stvec_d;
sscratch_q <= sscratch_d;
stval_q <= stval_d;
satp_q <= satp_d;
// timer and counters
cycle_q <= cycle_d;
instret_q <= instret_d;
// aux registers
en_ld_st_translation_q <= en_ld_st_translation_d;
// wait for interrupt
wfi_q <= wfi_d;
end
end
//-------------
// Assertions
//-------------
`ifndef SYNTHESIS
`ifndef VERILATOR
// check that eret and ex are never valid together
assert property (
@(posedge clk_i) !(eret_o && ex_i.valid))
else begin $error("eret and exception should never be valid at the same time"); $stop(); end
`endif
`endif
endmodule