-
Notifications
You must be signed in to change notification settings - Fork 692
/
csr_regfile.sv
1071 lines (1011 loc) · 49.7 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
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// 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 logic [63:0] DmBaseAddress = 64'h0, // debug module base address
parameter int AsidWidth = 1,
parameter int unsigned NrCommitPorts = 2
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
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
// commit acknowledge
input scoreboard_entry_t [NrCommitPorts-1:0] commit_instr_i, // the instruction we want to commit
input logic [NrCommitPorts-1:0] commit_ack_i, // Commit acknowledged a instruction -> increase instret CSR
// Core and Cluster ID
input logic [63:0] boot_addr_i, // Address from which to start booting, mtvec is set to the same address
input logic [63:0] hart_id_i, // Hart id in a multicore environment (reflected in a CSR)
// 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 dirty_fp_state_i, // Mark the FP sate as dirty
input logic csr_write_fflags_i, // Write fflags register e.g.: we are retiring a floating point instruction
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 riscv::priv_lvl_t priv_lvl_o, // Current privilege level the CPU is in
// FPU
output riscv::xs_t fs_o, // Floating point extension status
output logic [4:0] fflags_o, // Floating-Point Accured Exceptions
output logic [2:0] frm_o, // Floating-Point Dynamic Rounding Mode
output logic [6:0] fprec_o, // Floating-Point Precision Control
// MMU
output logic en_translation_o, // enable VA translation
output logic en_ld_st_translation_o, // enable VA translation for load and stores
output riscv::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 [AsidWidth-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
input logic debug_req_i, // debug request in
output logic set_debug_pc_o,
// Virtualization Support
output logic tvm_o, // trap virtual memory
output logic tw_o, // timeout wait
output logic tsr_o, // trap sret
output logic debug_mode_o, // we are in debug mode -> that will change some decoding
output logic single_step_o, // we are in single-step mode
// Caches
output logic icache_en_o, // L1 ICache Enable
output logic dcache_en_o, // L1 DCache Enable
// Performance Counter
output logic [4:0] perf_addr_o, // read/write address to performance counter module (up to 29 aux counters possible in riscv encoding.h)
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;
riscv::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 mprv;
logic mret; // return from M-mode exception
logic sret; // return from S-mode exception
logic dret; // return from debug mode
// CSR write causes us to mark the FPU state as dirty
logic dirty_fp_state_csr;
riscv::csr_t csr_addr;
// ----------------
// Assignments
// ----------------
assign csr_addr = riscv::csr_t'(csr_addr_i);
assign fs_o = mstatus_q.fs;
// ----------------
// CSR Registers
// ----------------
// privilege level register
riscv::priv_lvl_t priv_lvl_d, priv_lvl_q;
// we are in debug
logic debug_mode_q, debug_mode_d;
riscv::status_rv64_t mstatus_q, mstatus_d;
riscv::satp_t satp_q, satp_d;
riscv::dcsr_t dcsr_q, dcsr_d;
logic mtvec_rst_load_q;// used to determine whether we came out of reset
logic [63:0] dpc_q, dpc_d;
logic [63:0] dscratch0_q, dscratch0_d;
logic [63:0] dscratch1_q, dscratch1_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] 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;
riscv::fcsr_t fcsr_q, fcsr_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;
perf_addr_o = csr_addr.address[4:0];;
if (csr_read) begin
unique case (csr_addr.address)
riscv::CSR_FFLAGS: begin
if (mstatus_q.fs == riscv::Off) begin
read_access_exception = 1'b1;
end else begin
csr_rdata = {59'b0, fcsr_q.fflags};
end
end
riscv::CSR_FRM: begin
if (mstatus_q.fs == riscv::Off) begin
read_access_exception = 1'b1;
end else begin
csr_rdata = {61'b0, fcsr_q.frm};
end
end
riscv::CSR_FCSR: begin
if (mstatus_q.fs == riscv::Off) begin
read_access_exception = 1'b1;
end else begin
csr_rdata = {56'b0, fcsr_q.frm, fcsr_q.fflags};
end
end
// non-standard extension
riscv::CSR_FTRAN: begin
if (mstatus_q.fs == riscv::Off) begin
read_access_exception = 1'b1;
end else begin
csr_rdata = {57'b0, fcsr_q.fprec};
end
end
// debug registers
riscv::CSR_DCSR: csr_rdata = {32'b0, dcsr_q};
riscv::CSR_DPC: csr_rdata = dpc_q;
riscv::CSR_DSCRATCH0: csr_rdata = dscratch0_q;
riscv::CSR_DSCRATCH1: csr_rdata = dscratch1_q;
// trigger module registers
riscv::CSR_TSELECT:; // not implemented
riscv::CSR_TDATA1:; // not implemented
riscv::CSR_TDATA2:; // not implemented
riscv::CSR_TDATA3:; // not implemented
// supervisor registers
riscv::CSR_SSTATUS: begin
csr_rdata = mstatus_q & ariane_pkg::SMODE_STATUS_READ_MASK;
end
riscv::CSR_SIE: csr_rdata = mie_q & mideleg_q;
riscv::CSR_SIP: csr_rdata = mip_q & mideleg_q;
riscv::CSR_STVEC: csr_rdata = stvec_q;
riscv::CSR_SCOUNTEREN: csr_rdata = 64'b0; // not implemented
riscv::CSR_SSCRATCH: csr_rdata = sscratch_q;
riscv::CSR_SEPC: csr_rdata = sepc_q;
riscv::CSR_SCAUSE: csr_rdata = scause_q;
riscv::CSR_STVAL: csr_rdata = stval_q;
riscv::CSR_SATP: begin
// intercept reads to SATP if in S-Mode and TVM is enabled
if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm) begin
read_access_exception = 1'b1;
end else begin
csr_rdata = satp_q;
end
end
// machine mode registers
riscv::CSR_MSTATUS: csr_rdata = mstatus_q;
riscv::CSR_MISA: csr_rdata = ISA_CODE;
riscv::CSR_MEDELEG: csr_rdata = medeleg_q;
riscv::CSR_MIDELEG: csr_rdata = mideleg_q;
riscv::CSR_MIE: csr_rdata = mie_q;
riscv::CSR_MTVEC: csr_rdata = mtvec_q;
riscv::CSR_MCOUNTEREN: csr_rdata = 64'b0; // not implemented
riscv::CSR_MSCRATCH: csr_rdata = mscratch_q;
riscv::CSR_MEPC: csr_rdata = mepc_q;
riscv::CSR_MCAUSE: csr_rdata = mcause_q;
riscv::CSR_MTVAL: csr_rdata = mtval_q;
riscv::CSR_MIP: csr_rdata = mip_q;
riscv::CSR_MVENDORID: csr_rdata = 64'b0; // not implemented
riscv::CSR_MARCHID: csr_rdata = ARIANE_MARCHID;
riscv::CSR_MIMPID: csr_rdata = 64'b0; // not implemented
riscv::CSR_MHARTID: csr_rdata = hart_id_i;
riscv::CSR_MCYCLE: csr_rdata = cycle_q;
riscv::CSR_MINSTRET: csr_rdata = instret_q;
// custom (non RISC-V) cache control
riscv::CSR_DCACHE: csr_rdata = dcache_q;
riscv::CSR_ICACHE: csr_rdata = icache_q;
// Counters and Timers
riscv::CSR_CYCLE: csr_rdata = cycle_q;
riscv::CSR_INSTRET: csr_rdata = instret_q;
riscv::CSR_L1_ICACHE_MISS,
riscv::CSR_L1_DCACHE_MISS,
riscv::CSR_ITLB_MISS,
riscv::CSR_DTLB_MISS,
riscv::CSR_LOAD,
riscv::CSR_STORE,
riscv::CSR_EXCEPTION,
riscv::CSR_EXCEPTION_RET,
riscv::CSR_BRANCH_JUMP,
riscv::CSR_CALL,
riscv::CSR_RET,
riscv::CSR_MIS_PREDICT,
riscv::CSR_SB_FULL,
riscv::CSR_IF_EMPTY: csr_rdata = perf_data_i;
default: read_access_exception = 1'b1;
endcase
end
end
// ---------------------------
// CSR Write and update logic
// ---------------------------
logic [63:0] mask;
always_comb begin : csr_update
automatic riscv::satp_t sapt;
automatic logic [63:0] instret;
sapt = satp_q;
instret = instret_q;
// --------------------
// Counters
// --------------------
cycle_d = cycle_q;
instret_d = instret_q;
if (!debug_mode_q) begin
// increase instruction retired counter
for (int i = 0; i < NrCommitPorts; i++) begin
if (commit_ack_i[i] && !ex_i.valid) instret++;
end
instret_d = instret;
// increment the cycle count
if (ENABLE_CYCLE_COUNT) cycle_d = cycle_q + 1'b1;
else cycle_d = instret;
end
eret_o = 1'b0;
flush_o = 1'b0;
update_access_exception = 1'b0;
set_debug_pc_o = 1'b0;
perf_we_o = 1'b0;
perf_data_o = 'b0;
fcsr_d = fcsr_q;
priv_lvl_d = priv_lvl_q;
debug_mode_d = debug_mode_q;
dcsr_d = dcsr_q;
dpc_d = dpc_q;
dscratch0_d = dscratch0_q;
dscratch1_d = dscratch1_q;
mstatus_d = mstatus_q;
// check whether we come out of reset
// this is a workaround. some tools have issues
// having boot_addr_i in the asynchronous
// reset assignment to mtvec_d, even though
// boot_addr_i will be assigned a constant
// on the top-level.
if (mtvec_rst_load_q) begin
mtvec_d = boot_addr_i + 'h40;
end else begin
mtvec_d = mtvec_q;
end
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;
dirty_fp_state_csr = 1'b0;
// check for correct access rights and that we are writing
if (csr_we) begin
unique case (csr_addr.address)
// Floating-Point
riscv::CSR_FFLAGS: begin
if (mstatus_q.fs == riscv::Off) begin
update_access_exception = 1'b1;
end else begin
dirty_fp_state_csr = 1'b1;
fcsr_d.fflags = csr_wdata[4:0];
// this instruction has side-effects
flush_o = 1'b1;
end
end
riscv::CSR_FRM: begin
if (mstatus_q.fs == riscv::Off) begin
update_access_exception = 1'b1;
end else begin
dirty_fp_state_csr = 1'b1;
fcsr_d.frm = csr_wdata[2:0];
// this instruction has side-effects
flush_o = 1'b1;
end
end
riscv::CSR_FCSR: begin
if (mstatus_q.fs == riscv::Off) begin
update_access_exception = 1'b1;
end else begin
dirty_fp_state_csr = 1'b1;
fcsr_d[7:0] = csr_wdata[7:0]; // ignore writes to reserved space
// this instruction has side-effects
flush_o = 1'b1;
end
end
riscv::CSR_FTRAN: begin
if (mstatus_q.fs == riscv::Off) begin
update_access_exception = 1'b1;
end else begin
dirty_fp_state_csr = 1'b1;
fcsr_d.fprec = csr_wdata[6:0]; // ignore writes to reserved space
// this instruction has side-effects
flush_o = 1'b1;
end
end
// debug CSR
riscv::CSR_DCSR: begin
dcsr_d = csr_wdata[31:0];
// debug is implemented
dcsr_d.xdebugver = 4'h4;
// privilege level
dcsr_d.prv = priv_lvl_q;
// currently not supported
dcsr_d.nmip = 1'b0;
dcsr_d.stopcount = 1'b0;
dcsr_d.stoptime = 1'b0;
end
riscv::CSR_DPC: dpc_d = csr_wdata;
riscv::CSR_DSCRATCH0: dscratch0_d = csr_wdata;
riscv::CSR_DSCRATCH1: dscratch1_d = csr_wdata;
// trigger module CSRs
riscv::CSR_TSELECT:; // not implemented
riscv::CSR_TDATA1:; // not implemented
riscv::CSR_TDATA2:; // not implemented
riscv::CSR_TDATA3:; // not implemented
// sstatus is a subset of mstatus - mask it accordingly
riscv::CSR_SSTATUS: begin
mask = ariane_pkg::SMODE_STATUS_WRITE_MASK;
mstatus_d = (mstatus_q & ~mask) | (csr_wdata & mask);
// hardwire to zero if floating point extension is not present
if (!FP_PRESENT) begin
mstatus_d.fs = riscv::Off;
end
// hardwired extension registers
mstatus_d.sd = (&mstatus_q.xs) | (&mstatus_q.fs);
// 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
riscv::CSR_SIE: begin
// the mideleg makes sure only delegate-able register (and therefore also only implemented registers) are written
mie_d = (mie_q & ~mideleg_q) | (csr_wdata & mideleg_q);
end
riscv::CSR_SIP: begin
// only the supervisor software interrupt is write-able, iff delegated
mask = riscv::MIP_SSIP & mideleg_q;
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
end
riscv::CSR_SCOUNTEREN:;
riscv::CSR_STVEC: stvec_d = {csr_wdata[63:2], 1'b0, csr_wdata[0]};
riscv::CSR_SSCRATCH: sscratch_d = csr_wdata;
riscv::CSR_SEPC: sepc_d = {csr_wdata[63:1], 1'b0};
riscv::CSR_SCAUSE: scause_d = csr_wdata;
riscv::CSR_STVAL: stval_d = csr_wdata;
// supervisor address translation and protection
riscv::CSR_SATP: begin
// intercept SATP writes if in S-Mode and TVM is enabled
if (priv_lvl_o == riscv::PRIV_LVL_S && mstatus_q.tvm)
update_access_exception = 1'b1;
else begin
sapt = riscv::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-AsidWidth){1'b0}}, {AsidWidth{1'b1}}};
// only update if we actually support this mode
if (sapt.mode == MODE_OFF || sapt.mode == MODE_SV39) 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
riscv::CSR_MSTATUS: begin
mstatus_d = csr_wdata;
// hardwired zero registers
mstatus_d.sd = (&mstatus_q.xs) | (&mstatus_q.fs);
mstatus_d.xs = riscv::Off;
if (!FP_PRESENT) begin
mstatus_d.fs = riscv::Off;
end
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)
riscv::CSR_MISA:;
// machine exception delegation register
// 0 - 15 exceptions supported
riscv::CSR_MEDELEG: begin
mask = (1 << riscv::INSTR_ADDR_MISALIGNED) |
(1 << riscv::BREAKPOINT) |
(1 << riscv::ENV_CALL_UMODE) |
(1 << riscv::INSTR_PAGE_FAULT) |
(1 << riscv::LOAD_PAGE_FAULT) |
(1 << riscv::STORE_PAGE_FAULT);
medeleg_d = (medeleg_q & ~mask) | (csr_wdata & mask);
end
// machine interrupt delegation register
// we do not support user interrupt delegation
riscv::CSR_MIDELEG: begin
mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP;
mideleg_d = (mideleg_q & ~mask) | (csr_wdata & mask);
end
// mask the register so that unsupported interrupts can never be set
riscv::CSR_MIE: begin
mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP | riscv::MIP_MSIP | riscv::MIP_MTIP;
mie_d = (mie_q & ~mask) | (csr_wdata & mask); // we only support supervisor and M-mode interrupts
end
riscv::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
riscv::CSR_MCOUNTEREN:;
riscv::CSR_MSCRATCH: mscratch_d = csr_wdata;
riscv::CSR_MEPC: mepc_d = {csr_wdata[63:1], 1'b0};
riscv::CSR_MCAUSE: mcause_d = csr_wdata;
riscv::CSR_MTVAL: mtval_d = csr_wdata;
riscv::CSR_MIP: begin
mask = riscv::MIP_SSIP | riscv::MIP_STIP | riscv::MIP_SEIP;
mip_d = (mip_q & ~mask) | (csr_wdata & mask);
end
// performance counters
riscv::CSR_MCYCLE: cycle_d = csr_wdata;
riscv::CSR_MINSTRET: instret = csr_wdata;
riscv::CSR_DCACHE: dcache_d = csr_wdata[0]; // enable bit
riscv::CSR_ICACHE: icache_d = csr_wdata[0]; // enable bit
riscv::CSR_L1_ICACHE_MISS,
riscv::CSR_L1_DCACHE_MISS,
riscv::CSR_ITLB_MISS,
riscv::CSR_DTLB_MISS,
riscv::CSR_LOAD,
riscv::CSR_STORE,
riscv::CSR_EXCEPTION,
riscv::CSR_EXCEPTION_RET,
riscv::CSR_BRANCH_JUMP,
riscv::CSR_CALL,
riscv::CSR_RET,
riscv::CSR_MIS_PREDICT: begin
perf_data_o = csr_wdata;
perf_we_o = 1'b1;
end
default: update_access_exception = 1'b1;
endcase
end
mstatus_d.sxl = riscv::XLEN_64;
mstatus_d.uxl = riscv::XLEN_64;
// mark the floating point extension register as dirty
if (FP_PRESENT && (dirty_fp_state_csr || dirty_fp_state_i)) begin
mstatus_d.fs = riscv::Dirty;
end
// write the floating point status register
if (csr_write_fflags_i) begin
fcsr_d.fflags = csr_wdata_i[4:0] | fcsr_q.fflags;
end
// ---------------------
// External Interrupts
// ---------------------
// Machine Mode External Interrupt Pending
mip_d[riscv::IRQ_M_EXT] = irq_i[0];
// Machine software interrupt
mip_d[riscv::IRQ_M_SOFT] = ipi_i;
// Timer interrupt pending, coming from platform timer
mip_d[riscv::IRQ_M_TIMER] = time_irq_i;
// -----------------------
// Manage Exception Stack
// -----------------------
// update exception CSRs
// we got an exception update cause, pc and stval register
trap_to_priv_lvl = riscv::PRIV_LVL_M;
// Exception is taken and we are not in debug mode
// exceptions in debug mode don't update any fields
if (!debug_mode_q && 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_o == riscv::PRIV_LVL_M) ? riscv::PRIV_LVL_M : riscv::PRIV_LVL_S;
end
// trap to supervisor mode
if (trap_to_priv_lvl == riscv::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 = priv_lvl_q[0];
// set cause
scause_d = ex_i.cause;
// set epc
sepc_d = pc_i;
// set mtval or stval
stval_d = (ariane_pkg::ZERO_TVAL
&& (ex_i.cause inside {
riscv::ILLEGAL_INSTR,
riscv::BREAKPOINT,
riscv::ENV_CALL_UMODE,
riscv::ENV_CALL_SMODE,
riscv::ENV_CALL_MMODE
} || ex_i.cause[63])) ? '0 : 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 = (ariane_pkg::ZERO_TVAL
&& (ex_i.cause inside {
riscv::ILLEGAL_INSTR,
riscv::BREAKPOINT,
riscv::ENV_CALL_UMODE,
riscv::ENV_CALL_SMODE,
riscv::ENV_CALL_MMODE
} || ex_i.cause[63])) ? '0 : ex_i.tval;
end
priv_lvl_d = trap_to_priv_lvl;
end
// ------------------------------
// Debug
// ------------------------------
// Explains why Debug Mode was entered.
// When there are multiple reasons to enter Debug Mode in a single cycle, hardware should set cause to the cause with the highest priority.
// 1: An ebreak instruction was executed. (priority 3)
// 2: The Trigger Module caused a breakpoint exception. (priority 4)
// 3: The debugger requested entry to Debug Mode. (priority 2)
// 4: The hart single stepped because step was set. (priority 1)
// we are currently not in debug mode and could potentially enter
if (!debug_mode_q) begin
dcsr_d.prv = priv_lvl_o;
// trigger module fired
// caused by a breakpoint
if (ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin
// check that we actually want to enter debug depending on the privilege level we are currently in
unique case (priv_lvl_o)
riscv::PRIV_LVL_M: begin
debug_mode_d = dcsr_q.ebreakm;
set_debug_pc_o = dcsr_q.ebreakm;
end
riscv::PRIV_LVL_S: begin
debug_mode_d = dcsr_q.ebreaks;
set_debug_pc_o = dcsr_q.ebreaks;
end
riscv::PRIV_LVL_U: begin
debug_mode_d = dcsr_q.ebreaku;
set_debug_pc_o = dcsr_q.ebreaku;
end
default:;
endcase
// save PC of next this instruction e.g.: the next one to be executed
dpc_d = pc_i;
dcsr_d.cause = dm::CauseBreakpoint;
end
// we've got a debug request (and we have an instruction which we can associate it to)
// don't interrupt the AMO
if (debug_req_i && commit_instr_i[0].valid) begin
// save the PC
dpc_d = pc_i;
// enter debug mode
debug_mode_d = 1'b1;
// jump to the base address
set_debug_pc_o = 1'b1;
// save the cause as external debug request
dcsr_d.cause = dm::CauseRequest;
end
// single step enable and we just retired an instruction
if (dcsr_q.step && commit_ack_i[0]) begin
// valid CTRL flow change
if (commit_instr_i[0].fu == CTRL_FLOW) begin
// we saved the correct target address during execute
dpc_d = commit_instr_i[0].bp.predict_address;
// exception valid
end else if (ex_i.valid) begin
dpc_d = trap_vector_base_o;
// return from environment
end else if (eret_o) begin
dpc_d = epc_o;
// consecutive PC
end else begin
dpc_d = commit_instr_i[0].pc + (commit_instr_i[0].is_compressed ? 'h2 : 'h4);
end
debug_mode_d = 1'b1;
set_debug_pc_o = 1'b1;
dcsr_d.cause = dm::CauseSingleStep;
end
end
// go in halt-state again when we encounter an exception
if (debug_mode_q && ex_i.valid && ex_i.cause == riscv::BREAKPOINT) begin
set_debug_pc_o = 1'b1;
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 (mprv && satp_q.mode == MODE_SV39 && (mstatus_q.mpp != riscv::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 = (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 = riscv::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_q.spie;
// restore the previous privilege level
priv_lvl_d = riscv::priv_lvl_t'({1'b0, mstatus_q.spp});
// set spp to user mode
mstatus_d.spp = 1'b0;
// set spie to 1
mstatus_d.spie = 1'b1;
end
// return from debug mode
if (dret) begin
// return from exception, IF doesn't care from where we are returning
eret_o = 1'b1;
// restore the previous privilege level
priv_lvl_d = riscv::priv_lvl_t'(dcsr_q.prv);
// actually return from debug mode
debug_mode_d = 1'b0;
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;
dret = 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
DRET: begin
// the return should not have any write or read side-effects
csr_we = 1'b0;
csr_read = 1'b0;
dret = 1'b1; // signal a return from debug 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;
dret = 1'b0;
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
// -----------------
// TODO(zarubaf): Move interrupt handling to commit stage.
// 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[riscv::S_TIMER_INTERRUPT[5:0]] && mip_q[riscv::S_TIMER_INTERRUPT[5:0]])
interrupt_cause = riscv::S_TIMER_INTERRUPT;
// Supervisor Software Interrupt
if (mie_q[riscv::S_SW_INTERRUPT[5:0]] && mip_q[riscv::S_SW_INTERRUPT[5:0]])
interrupt_cause = riscv::S_SW_INTERRUPT;
// Supervisor External Interrupt
// The logical-OR of the software-writable bit and the signal from the external interrupt controller is
// used to generate external interrupts to the supervisor
if (mie_q[riscv::S_EXT_INTERRUPT[5:0]] && (mip_q[riscv::S_EXT_INTERRUPT[5:0]] | irq_i[1]))
interrupt_cause = riscv::S_EXT_INTERRUPT;
// Machine Timer Interrupt
if (mip_q[riscv::M_TIMER_INTERRUPT[5:0]] && mie_q[riscv::M_TIMER_INTERRUPT[5:0]])
interrupt_cause = riscv::M_TIMER_INTERRUPT;
// Machine Mode Software Interrupt
if (mip_q[riscv::M_SW_INTERRUPT[5:0]] && mie_q[riscv::M_SW_INTERRUPT[5:0]])
interrupt_cause = riscv::M_SW_INTERRUPT;
// Machine Mode External Interrupt
if (mip_q[riscv::M_EXT_INTERRUPT[5:0]] && mie_q[riscv::M_EXT_INTERRUPT[5:0]])
interrupt_cause = riscv::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.
// All interrupts are masked in debug mode
interrupt_global_enable = (~debug_mode_q)
// interrupts are enabled during single step or we are not stepping
& (~dcsr_q.step | dcsr_q.stepie)
& ((mstatus_q.mie & (priv_lvl_o == riscv::PRIV_LVL_M))
| (priv_lvl_o != riscv::PRIV_LVL_M));
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_o == riscv::PRIV_LVL_S) || priv_lvl_o == riscv::PRIV_LVL_U)
csr_exception_o.valid = 1'b1;
end else begin
csr_exception_o.valid = 1'b1;
end
end
// -----------------
// Privilege Check
// -----------------
// if we are reading or writing, check for the correct privilege level this has
// precedence over interrupts
if (csr_we || csr_read) begin
if ((riscv::priv_lvl_t'(priv_lvl_o & csr_addr.csr_decode.priv_lvl) != csr_addr.csr_decode.priv_lvl)) begin
csr_exception_o.cause = riscv::ILLEGAL_INSTR;
csr_exception_o.valid = 1'b1;
end
// check access to debug mode only CSRs
if (csr_addr_i[11:4] == 8'h7b && !debug_mode_q) begin
csr_exception_o.cause = riscv::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 = riscv::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
// -------------------
// Wait for Interrupt
// -------------------
// if there is any interrupt pending un-stall the core
// also un-stall if we want to enter debug mode
if (|mip_q || debug_req_i || irq_i[1]) begin
wfi_d = 1'b0;
// or alternatively if there is no exception pending and we are not in debug mode wait here
// for the interrupt
end else if (!debug_mode_q && csr_op_i == WFI && !ex_i.valid) begin
wfi_d = 1'b1;
end
end
// 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 == riscv::PRIV_LVL_S) begin
trap_vector_base_o = {stvec_q[63:2], 2'b0};
end
// if we are in debug mode jump to a specific address
if (debug_mode_q) begin
trap_vector_base_o = DmBaseAddress + dm::ExceptionAddress;
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
// we are returning from debug mode, to take the dpc register
if (dret) begin
epc_o = dpc_q;
end
end
// -------------------
// Output Assignments
// -------------------
always_comb begin
// When the SEIP bit is read with a CSRRW, CSRRS, or CSRRC instruction, the value
// returned in the rd destination register contains the logical-OR of the software-writable
// bit and the interrupt signal from the interrupt controller.
csr_rdata_o = csr_rdata;
unique case (csr_addr.address)
riscv::CSR_MIP: csr_rdata_o = csr_rdata | (irq_i[1] << riscv::IRQ_S_EXT);
// in supervisor mode we also need to check whether we delegated this bit
riscv::CSR_SIP: begin
csr_rdata_o = csr_rdata
| ((irq_i[1] & mideleg_q[riscv::IRQ_S_EXT]) << riscv::IRQ_S_EXT);
end
default:;
endcase
end
// in debug mode we execute with privilege level M
assign priv_lvl_o = (debug_mode_q) ? riscv::PRIV_LVL_M : priv_lvl_q;
// FPU outputs
assign fflags_o = fcsr_q.fflags;
assign frm_o = fcsr_q.frm;
assign fprec_o = fcsr_q.fprec;
// MMU outputs
assign satp_ppn_o = satp_q.ppn;
assign asid_o = satp_q.asid[AsidWidth-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_o != riscv::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] & (~debug_mode_q);
assign dcache_en_o = dcache_q[0];
// determine if mprv needs to be considered if in debug mode
assign mprv = (debug_mode_q && !dcsr_q.mprven) ? 1'b0 : mstatus_q.mprv;
assign debug_mode_o = debug_mode_q;
assign single_step_o = dcsr_q.step;
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
priv_lvl_q <= riscv::PRIV_LVL_M;
// floating-point registers
fcsr_q <= 64'b0;
// debug signals
debug_mode_q <= 1'b0;
dcsr_q <= '0;
dcsr_q.prv <= riscv::PRIV_LVL_M;
dpc_q <= 64'b0;
dscratch0_q <= 64'b0;
dscratch1_q <= 64'b0;
// machine mode registers
mstatus_q <= 64'b0;
// set to boot address + direct mode + 4 byte offset which is the initial trap
mtvec_rst_load_q <= 1'b1;
mtvec_q <= '0;
medeleg_q <= 64'b0;
mideleg_q <= 64'b0;
mip_q <= 64'b0;
mie_q <= 64'b0;
mepc_q <= 64'b0;
mcause_q <= 64'b0;