forked from fnuecke/sedna
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathR5CPUTemplate.java
3306 lines (2881 loc) · 121 KB
/
R5CPUTemplate.java
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
package li.cil.sedna.riscv;
import li.cil.ceres.api.Serialized;
import li.cil.sedna.api.Sizes;
import li.cil.sedna.api.device.MemoryMappedDevice;
import li.cil.sedna.api.device.rtc.RealTimeCounter;
import li.cil.sedna.api.memory.MappedMemoryRange;
import li.cil.sedna.api.memory.MemoryAccessException;
import li.cil.sedna.api.memory.MemoryMap;
import li.cil.sedna.instruction.InstructionDefinition.Field;
import li.cil.sedna.instruction.InstructionDefinition.Instruction;
import li.cil.sedna.instruction.InstructionDefinition.InstructionSize;
import li.cil.sedna.instruction.InstructionDefinition.ProgramCounter;
import li.cil.sedna.riscv.exception.R5IllegalInstructionException;
import li.cil.sedna.riscv.exception.R5MemoryAccessException;
import li.cil.sedna.utils.BitUtils;
import li.cil.sedna.utils.SoftDouble;
import li.cil.sedna.utils.SoftFloat;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
/**
* RISC-V RV64GC implementation.
* <p>
* Based on ISA specifications found at https://github.com/riscv/riscv-isa-manual/releases
* <ul>
* <li>Volume I: User-Level ISA 20191214-draft (October 18, 2020)</li>
* <li>Volume II: Privileged Architecture v1.12-draft (October 18, 2020)</li>
* </ul>
* <p>
* Limitations:
* <ul>
* <li>The fence operations are implemented as no-ops.</li>
* </ul>
*/
@Serialized
final class R5CPUTemplate implements R5CPU {
private static final int PC_INIT = 0x1000; // Initial position of program counter.
// UBE, SBE, MBE hardcoded to zero for little endianness.
private static final long MSTATUS_MASK = ~R5.STATUS_UBE_MASK & ~R5.STATUS_SBE_MASK & ~R5.STATUS_MBE_MASK;
// No time and no high perf counters.
private static final int COUNTEREN_MASK = R5.MCOUNTERN_CY | R5.MCOUNTERN_IR;
// Supervisor status (sstatus) CSR mask over mstatus.
private static final long SSTATUS_MASK = (R5.STATUS_UIE_MASK | R5.STATUS_SIE_MASK |
R5.STATUS_UPIE_MASK | R5.STATUS_SPIE_MASK |
R5.STATUS_SPP_MASK | R5.STATUS_FS_MASK |
R5.STATUS_XS_MASK | R5.STATUS_SUM_MASK |
R5.STATUS_MXR_MASK | R5.STATUS_UXL_MASK);
// Translation look-aside buffer config.
private static final int TLB_SIZE = 256; // Must be a power of two for fast modulo via `& (TLB_SIZE - 1)`.
///////////////////////////////////////////////////////////////////
// RV32I / RV64I
private long pc; // Program counter.
private byte mxl; // Current MXLEN, stored to restore after privilege change.
private int xlen; // Current XLEN, allows switching between RV32I and RV64I.
private final long[] x = new long[32]; // Integer registers. We use sign-extended longs for 32 bit mode.
///////////////////////////////////////////////////////////////////
// RV64FD
private final long[] f = new long[32]; // Float registers.
private final SoftFloat.Flags fflags = new SoftFloat.Flags(); // flags = fcsr[4:0] := NV . DZ . OF . UF . NX
private byte frm; // fcsr[7:5]
private byte fs; // FS field of mstatus, separate for convenience.
private final transient SoftFloat fpu32 = new SoftFloat(fflags);
private final transient SoftDouble fpu64 = new SoftDouble(fflags);
///////////////////////////////////////////////////////////////////
// RV64A
private long reservation_set = -1L; // Reservation set for RV64A's LR/SC.
///////////////////////////////////////////////////////////////////
// User-level CSRs
private long mcycle;
// Machine-level CSRs
private long mstatus; // Machine Status Register
private long mtvec; // Machine Trap-Vector Base-Address Register; 0b11=Mode: 0=direct, 1=vectored
private long medeleg, mideleg; // Machine Trap Delegation Registers
private final AtomicLong mip = new AtomicLong(); // Pending Interrupts
private long mie; // Enabled Interrupts
private int mcounteren; // Machine Counter-Enable Register
private long mscratch; // Machine Scratch Register
private long mepc; // Machine Exception Program Counter
private long mcause; // Machine Cause Register
private long mtval; // Machine Trap Value Register
// Supervisor-level CSRs
private long stvec; // Supervisor Trap Vector Base Address Register; 0b11=Mode: 0=direct, 1=vectored
private int scounteren; // Supervisor Counter-Enable Register
private long sscratch; // Supervisor Scratch Register
private long sepc; // Supervisor Exception Program Counter
private long scause; // Supervisor Cause Register
private long stval; // Supervisor Trap Value Register
private long satp; // Supervisor Address Translation and Protection Register
///////////////////////////////////////////////////////////////////
// Misc. state
private int priv; // Current privilege level.
private boolean waitingForInterrupt;
///////////////////////////////////////////////////////////////////
// Memory access
// Translation look-aside buffers.
private final transient TLBEntry[] fetchTLB = new TLBEntry[TLB_SIZE];
private final transient TLBEntry[] loadTLB = new TLBEntry[TLB_SIZE];
private final transient TLBEntry[] storeTLB = new TLBEntry[TLB_SIZE];
// Access to physical memory for load/store operations.
private final transient MemoryMap physicalMemory;
///////////////////////////////////////////////////////////////////
// Stepping
private int cycleDebt; // Traces may lead to us running more cycles than given, remember to pay it back.
///////////////////////////////////////////////////////////////////
// Real time counter -- at least in RISC-V Linux 5.1 the mtime CSR is needed in add_device_randomness
// where it doesn't use the SBI. Not implementing it would cause an illegal instruction exception
// halting the system.
private final transient RealTimeCounter rtc;
private transient int cycleFrequency = 50_000_000;
public R5CPUTemplate(final MemoryMap physicalMemory, @Nullable final RealTimeCounter rtc) {
// This cast is necessary so that stack frame computation in ASM does not throw
// an exception from trying to load the realization class we're generating while
// we're generating it.
this.rtc = rtc != null ? rtc : this;
this.physicalMemory = physicalMemory;
for (int i = 0; i < TLB_SIZE; i++) {
fetchTLB[i] = new TLBEntry();
}
for (int i = 0; i < TLB_SIZE; i++) {
loadTLB[i] = new TLBEntry();
}
for (int i = 0; i < TLB_SIZE; i++) {
storeTLB[i] = new TLBEntry();
}
reset();
}
@Override
public long getISA() {
return misa();
}
public void setXLEN(final int xlen) {
mxl = (byte) R5.mxl(xlen);
this.xlen = xlen;
mstatus = mstatus
& ~(R5.STATUS_UXL_MASK | R5.STATUS_SXL_MASK)
| (R5.mxl(xlen) << R5.STATUS_UXL_SHIFT)
| (R5.mxl(xlen) << R5.STATUS_SXL_SHIFT);
if (xlen == R5.XLEN_32) {
for (int i = 0; i < x.length; i++) {
x[i] = (int) x[i];
}
}
}
@Override
public void reset() {
reset(true, PC_INIT);
}
@Override
public void reset(final boolean hard, final long pc) {
this.pc = pc;
waitingForInterrupt = false;
// Volume 2, 3.3 Reset
priv = R5.PRIVILEGE_M;
mstatus = mstatus & ~R5.STATUS_MIE_MASK;
mstatus = mstatus & ~R5.STATUS_MPRV_MASK;
mcause = 0;
// Volume 2, 3.1.1: The MXL field is always set to the widest supported ISA variant at reset.
mxl = (byte) R5.mxl(R5.XLEN_64);
xlen = R5.XLEN_64;
flushTLB();
if (hard) {
Arrays.fill(x, 0);
reservation_set = -1;
mcycle = 0;
mstatus = (R5.mxl(xlen) << R5.STATUS_UXL_SHIFT) |
(R5.mxl(xlen) << R5.STATUS_SXL_SHIFT);
mtvec = 0;
medeleg = 0;
mideleg = 0;
mip.set(0);
mie = 0;
mcounteren = 0;
mscratch = 0;
mepc = 0;
mtval = 0;
stvec = 0;
scounteren = 0;
sscratch = 0;
sepc = 0;
scause = 0;
stval = 0;
satp = 0;
}
}
@Override
public long getTime() {
return mcycle;
}
@Override
public int getFrequency() {
return cycleFrequency;
}
@Override
public void setFrequency(final int value) {
cycleFrequency = value;
}
@Override
public void raiseInterrupts(final int mask) {
mip.updateAndGet(operand -> operand | mask);
if (waitingForInterrupt && (mip.get() & mie) != 0) {
waitingForInterrupt = false;
}
}
@Override
public void lowerInterrupts(final int mask) {
mip.updateAndGet(operand -> operand & ~mask);
}
@Override
public int getRaisedInterrupts() {
return (int) mip.get();
}
public void step(int cycles) {
final int paidDebt = Math.min(cycles, cycleDebt);
cycles -= paidDebt;
cycleDebt -= paidDebt;
if (waitingForInterrupt) {
mcycle += cycles;
return;
}
final long cycleLimit = mcycle + cycles;
while (!waitingForInterrupt && mcycle < cycleLimit) {
final long pending = mip.get() & mie;
if (pending != 0) {
raiseInterrupt(pending);
}
interpret();
}
if (waitingForInterrupt && mcycle < cycleLimit) {
mcycle = cycleLimit;
}
cycleDebt += cycleLimit - mcycle;
}
///////////////////////////////////////////////////////////////////
// Interpretation
private long misa() {
// Base ISA descriptor CSR (misa) (V2p16).
return (R5.mxl(xlen) << R5.mxlShift(xlen)) | R5.isa('I', 'M', 'A', 'C', 'F', 'D', 'S', 'U');
}
private long getSupervisorStatusMask() {
return SSTATUS_MASK | R5.getStatusStateDirtyMask(xlen);
}
private void interpret() {
// The idea here is to run many sequential instructions with very little overhead.
// We only need to exit the inner loop when we either leave the page we started in,
// jump around (jumps, conditionals) or some state that influences how memory access
// happens changes (e.g. satp).
// For regular execution, we grab one cache entry and keep simply incrementing our
// position inside it. This does bring with it one important point to be wary of:
// the value of the program counter field will not match our actual current execution
// location. So whenever we need to access the "real" PC and when leaving the loop
// we have to update the field to its correct value.
// Also noteworthy is that we actually only run until the last position where a 32bit
// instruction would fully fit a page. The last 16bit in a page may be the start of
// a 32bit instruction spanning two pages, a special case we handle outside the loop.
try {
final TLBEntry cache = fetchPage(pc);
final MemoryMappedDevice device = cache.device;
final int instOffset = (int) (pc + cache.toOffset);
final int instEnd = instOffset - (int) (pc & R5.PAGE_ADDRESS_MASK) // Page start.
+ ((1 << R5.PAGE_ADDRESS_SHIFT) - 2); // Page size minus 16bit.
int inst;
try {
if (instOffset < instEnd) { // Likely case, instruction fully inside page.
inst = (int) device.load(instOffset, Sizes.SIZE_32_LOG2);
} else { // Unlikely case, instruction may leave page if it is 32bit.
inst = (short) device.load(instOffset, Sizes.SIZE_16_LOG2) & 0xFFFF;
if ((inst & 0b11) == 0b11) { // 32bit instruction.
final TLBEntry highCache = fetchPage(pc + 2);
final MemoryMappedDevice highDevice = cache.device;
inst |= highDevice.load((int) (pc + 2 + highCache.toOffset), Sizes.SIZE_16_LOG2) << 16;
}
}
} catch (final MemoryAccessException e) {
raiseException(R5.EXCEPTION_FAULT_FETCH, pc);
return;
}
if (xlen == R5.XLEN_32) {
interpretTrace32(device, inst, pc, instOffset, instEnd);
} else {
interpretTrace64(device, inst, pc, instOffset, instEnd);
}
} catch (final R5MemoryAccessException e) {
raiseException(e.getType(), e.getAddress());
}
}
// NB: Yes, having the same method more or less duplicated sucks, but it's just so
// much faster than having the actual decoding happen in one more method.
@SuppressWarnings("LocalCanBeFinal") // `pc` and `instOffset` get updated by the generated code replacing decode().
private void interpretTrace32(final MemoryMappedDevice device, int inst, long pc, int instOffset, final int instEnd) {
try { // Catch any exceptions to patch PC field.
for (; ; ) { // End of page check at the bottom since we enter with a valid inst.
mcycle++;
///////////////////////////////////////////////////////////////////
// This is the hook we replace when generating the decoder code. //
decode(); //
// See R5CPUGenerator. //
///////////////////////////////////////////////////////////////////
if (Integer.compareUnsigned(instOffset, instEnd) < 0) { // Likely case: we're still fully in the page.
inst = (int) device.load(instOffset, Sizes.SIZE_32_LOG2);
} else { // Unlikely case: we reached the end of the page. Leave to do interrupts and cycle check.
this.pc = pc;
return;
}
}
} catch (final MemoryAccessException e) {
this.pc = pc;
raiseException(R5.EXCEPTION_FAULT_FETCH, pc);
} catch (final R5IllegalInstructionException e) {
this.pc = pc;
raiseException(R5.EXCEPTION_ILLEGAL_INSTRUCTION, inst);
} catch (final R5MemoryAccessException e) {
this.pc = pc;
raiseException(e.getType(), e.getAddress());
}
}
@SuppressWarnings("LocalCanBeFinal") // `pc` and `instOffset` get updated by the generated code replacing decode().
private void interpretTrace64(final MemoryMappedDevice device, int inst, long pc, int instOffset, final int instEnd) {
try { // Catch any exceptions to patch PC field.
for (; ; ) { // End of page check at the bottom since we enter with a valid inst.
mcycle++;
///////////////////////////////////////////////////////////////////
// This is the hook we replace when generating the decoder code. //
decode(); //
// See R5CPUGenerator. //
///////////////////////////////////////////////////////////////////
if (Integer.compareUnsigned(instOffset, instEnd) < 0) { // Likely case: we're still fully in the page.
inst = (int) device.load(instOffset, Sizes.SIZE_32_LOG2);
} else { // Unlikely case: we reached the end of the page. Leave to do interrupts and cycle check.
this.pc = pc;
return;
}
}
} catch (final MemoryAccessException e) {
this.pc = pc;
raiseException(R5.EXCEPTION_FAULT_FETCH, pc);
} catch (final R5IllegalInstructionException e) {
this.pc = pc;
raiseException(R5.EXCEPTION_ILLEGAL_INSTRUCTION, inst);
} catch (final R5MemoryAccessException e) {
this.pc = pc;
raiseException(e.getType(), e.getAddress());
}
}
@SuppressWarnings("RedundantThrows")
private static void decode() throws R5IllegalInstructionException, R5MemoryAccessException {
throw new UnsupportedOperationException();
}
///////////////////////////////////////////////////////////////////
// CSR
private boolean csrrwx(final int rd, final long newValue, final int csr) throws R5IllegalInstructionException {
final boolean exitTrace;
checkCSR(csr, true);
if (rd != 0) { // Explicit check, spec says no read side-effects when rd = 0.
final long oldValue = readCSR(csr);
exitTrace = writeCSR(csr, newValue);
x[rd] = oldValue; // Write to register last, avoid lingering side-effect when write errors.
} else {
exitTrace = writeCSR(csr, newValue);
}
return exitTrace;
}
private boolean csrrscx(final int rd, final int rs1, final int csr, final long mask, final boolean isSet) throws R5IllegalInstructionException {
final boolean mayChange = rs1 != 0;
if (mayChange) {
checkCSR(csr, true);
final long value = readCSR(csr);
final long masked = isSet ? (mask | value) : (~mask & value);
final boolean exitTrace = writeCSR(csr, masked);
if (rd != 0) {
x[rd] = value;
}
return exitTrace;
} else if (rd != 0) {
checkCSR(csr, false);
x[rd] = readCSR(csr);
}
return false;
}
private void checkCSR(final int csr, final boolean throwIfReadonly) throws R5IllegalInstructionException {
if (throwIfReadonly && ((csr >= 0xC00 && csr <= 0xC1F) || (csr >= 0xC80 && csr <= 0xC9F)))
throw new R5IllegalInstructionException();
// Topmost bits, i.e. csr[11:8], encode access rights for CSR by convention. Of these, the top-most two bits,
// csr[11:10], encode read-only state, where 0b11: read-only, 0b00..0b10: read-write.
if (throwIfReadonly && ((csr & 0b1100_0000_0000) == 0b1100_0000_0000))
throw new R5IllegalInstructionException();
// The two following bits, csr[9:8], encode the lowest privilege level that can access the CSR.
if (priv < ((csr >>> 8) & 0b11))
throw new R5IllegalInstructionException();
}
@SuppressWarnings("DuplicateBranchesInSwitch")
private long readCSR(final int csr) throws R5IllegalInstructionException {
switch (csr) {
// Floating-Point Control and Status Registers
case 0x001: { // fflags, Floating-Point Accrued Exceptions.
if (fs == R5.FS_OFF) return -1;
return fpu32.flags.value;
}
case 0x002: { // frm, Floating-Point Dynamic Rounding Mode.
if (fs == R5.FS_OFF) return -1;
return frm;
}
case 0x003: { // fcsr, Floating-Point Control and Status Register (frm + fflags).
if (fs == R5.FS_OFF) return -1;
return (frm << 5) | fpu32.flags.value;
}
// User Trap Setup
// 0x000: ustatus, User status register.
// 0x004: uie, User interrupt-enabled register.
// 0x005: utvec, User trap handler base address.
// User Trap Handling
// 0x040: uscratch, Scratch register for user trap handlers.
// 0x041: uepc, User exception program counter.
// 0x042: ucause, User trap cause.
// 0x043: utval, User bad address or instruction.
// 0x044: uip, User interrupt pending.
// Supervisor Trap Setup
case 0x100: { // sstatus, Supervisor status register.
return getStatus(getSupervisorStatusMask());
}
// 0x102: sedeleg, Supervisor exception delegation register.
// 0x103: sideleg, Supervisor interrupt delegation register.
case 0x104: { // sie, Supervisor interrupt-enable register.
return mie & mideleg; // Effectively read-only because we don't implement N.
}
case 0x105: { // stvec, Supervisor trap handler base address.
return stvec;
}
case 0x106: { // scounteren, Supervisor counter enable.
return scounteren;
}
// Supervisor Trap Handling
case 0x140: { // sscratch Scratch register for supervisor trap handlers.
return sscratch;
}
case 0x141: { // sepc Supervisor exception program counter.
return sepc;
}
case 0x142: { // scause Supervisor trap cause.
return scause;
}
case 0x143: { // stval Supervisor bad address or instruction.
return stval;
}
case 0x144: { // sip Supervisor interrupt pending.
return mip.get() & mideleg; // Effectively read-only because we don't implement N.
}
// Supervisor Protection and Translation
case 0x180: { // satp Supervisor address translation and protection.
if (priv == R5.PRIVILEGE_S && (mstatus & R5.STATUS_TVM_MASK) != 0) {
throw new R5IllegalInstructionException();
}
return satp;
}
// Virtual Supervisor Registers
// 0x200: vsstatus, Virtual supervisor status register.
// 0x204: vsie, Virtual supervisor interrupt-enable register.
// 0x205: vstvec, Virtual supervisor trap handler base address.
// 0x240: vsscratch, Virtual supervisor scratch register.
// 0x241: vsepc, Virtual supervisor exception program counter.
// 0x242: vscause, Virtual supervisor trap cause.
// 0x243: vstval, Virtual supervisor bad address or instruction.
// 0x244: vsip, Virtual supervisor interrupt pending.
// 0x280: vsatp, Virtual supervisor address translation and protection
// Machine Trap Setup
case 0x300: { // mstatus Machine status register.
return getStatus(MSTATUS_MASK);
}
case 0x301: { // misa ISA and extensions
return misa();
}
case 0x302: { // medeleg Machine exception delegation register.
return medeleg;
}
case 0x303: { // mideleg Machine interrupt delegation register.
return mideleg;
}
case 0x304: { // mie Machine interrupt-enable register.
return mie;
}
case 0x305: { // mtvec Machine trap-handler base address.
return mtvec;
}
case 0x306: { // mcounteren Machine counter enable.
return mcounteren;
}
case 0x310: { // mstatush, Additional machine status register, RV32 only.
if (xlen != R5.XLEN_32) throw new R5IllegalInstructionException();
return getStatus(MSTATUS_MASK) >>> 32;
}
// Debug/Trace Registers
case 0x7A0: { // tselect
return 0;
}
case 0x7A1: { // tdata1
return 0;
}
case 0x7A2: { // tdata2
return 0;
}
case 0x7A3: { // tdata3
return 0;
}
// Machine Trap Handling
case 0x340: { // mscratch Scratch register for machine trap handlers.
return mscratch;
}
case 0x341: { // mepc Machine exception program counter.
return mepc;
}
case 0x342: { // mcause Machine trap cause.
return mcause;
}
case 0x343: { // mtval Machine bad address or instruction.
return mtval;
}
case 0x344: { // mip Machine interrupt pending.
return mip.get();
}
// 0x34A: mtinst, Machine trap instruction (transformed).
// 0x34B: mtval2, Machine bad guest physical address.
// Machine Memory Protection
// 0x3A0: pmpcfg0. Physical memory protection configuration.
// 0x3A1: pmpcfg1. Physical memory protection configuration, RV32 only.
// 0x3A2: pmpcfg2. Physical memory protection configuration.
// 0x3A3...0x3AE: pmpcfg3...pmpcfg14, Physical memory protection configuration, RV32 only.
// 0x3AF: pmpcfg15, Physical memory protection configuration, RV32 only.
// 0x3B0: pmpaddr0, Physical memory protection address register.
// 0x3B1...0x3EF: pmpaddr1...pmpaddr63, Physical memory protection address register.
// Hypervisor Trap Setup
// 0x600: hstatus, Hypervisor status register.
// 0x602: hedeleg, Hypervisor exception delegation register.
// 0x603: hideleg, Hypervisor interrupt delegation register.
// 0x604: hie, Hypervisor interrupt-enable register.
// 0x606: hcounteren, Hypervisor counter enable.
// 0x607: hgeie, Hypervisor guest external interrupt-enable register.
// Hypervisor Trap Handling
// 0x643: htval, Hypervisor bad guest physical address.
// 0x644: hip, Hypervisor interrupt pending.
// 0x645: hvip, Hypervisor virtual interrupt pending.
// 0x64A: htinst, Hypervisor trap instruction (transformed).
// 0xE12: hgeip, Hypervisor guest external interrupt pending.
// Hypervisor Protection and Translation
// 0x680: hgatp, Hypervisor guest address translation and protection.
// Hypervisor Counter/Timer Virtualization Registers
// 0x605: htimedelta, Delta for VS/VU-mode timer.
// 0x615: htimedeltah, Upper 32 bits of htimedelta, RV32 only.
//Machine Counter/Timers
case 0xB00: // mcycle, Machine cycle counter.
case 0xB02: { // minstret, Machine instructions-retired counter.
return mcycle;
}
// 0xB03: mhpmcounter3, Machine performance-monitoring counter.
// 0xB04...0xB1F: mhpmcounter4...mhpmcounter31, Machine performance-monitoring counter.
case 0xB80: // mcycleh, Upper 32 bits of mcycle, RV32 only.
case 0xB82: { // minstreth, Upper 32 bits of minstret, RV32 only.
if (xlen != R5.XLEN_32) throw new R5IllegalInstructionException();
return mcycle >>> 32;
}
// 0xB83: mhpmcounter3h, Upper 32 bits of mhpmcounter3, RV32 only.
// 0xB84...0xB9F: mhpmcounter4h...mhpmcounter31h, Upper 32 bits of mhpmcounter4, RV32 only.
// Counters and Timers
case 0xC00: // cycle
case 0xC02: { // instret
// counteren[2:0] is IR, TM, CY. As such the bit index matches the masked csr value.
checkCounterAccess(csr & 0b11);
return mcycle;
}
case 0xC01: { // time
return rtc.getTime();
}
// 0xC03 ... 0xC1F: hpmcounter3 ... hpmcounter31
case 0xC80: // cycleh
case 0xC82: { // instreth
if (xlen != R5.XLEN_32) throw new R5IllegalInstructionException();
// counteren[2:0] is IR, TM, CY. As such the bit index matches the masked csr value.
checkCounterAccess(csr & 0b11);
return mcycle >>> 32;
}
// 0xC81: timeh
// 0xC83 ... 0xC9F: hpmcounter3h ... hpmcounter31h
// Machine Information Registers
case 0xF11: { // mvendorid, Vendor ID.
return 0; // Not implemented.
}
case 0xF12: { // marchid, Architecture ID.
return 0; // Not implemented.
}
case 0xF13: { // mimpid, Implementation ID.
return 0; // Not implemented.
}
case 0xF14: { // mhartid, Hardware thread ID.
return 0; // Single, primary hart.
}
default: {
throw new R5IllegalInstructionException();
}
}
}
@SuppressWarnings("DuplicateBranchesInSwitch")
private boolean writeCSR(final int csr, final long value) throws R5IllegalInstructionException {
switch (csr) {
// Floating-Point Control and Status Registers
case 0x001: { // fflags, Floating-Point Accrued Exceptions.
fpu32.flags.value = (byte) (value & 0b11111);
fs = R5.FS_DIRTY;
break;
}
case 0x002: { // frm, Floating-Point Dynamic Rounding Mode.
frm = (byte) (value & 0b111);
fs = R5.FS_DIRTY;
break;
}
case 0x003: { // fcsr, Floating-Point Control and Status Register (frm + fflags).
frm = (byte) ((value >>> 5) & 0b111);
fpu32.flags.value = (byte) (value & 0b11111);
fs = R5.FS_DIRTY;
break;
}
// User Trap Setup
// 0x000: ustatus, User status register.
// 0x004: uie, User interrupt-enabled register.
// 0x005: utvec, User trap handler base address.
// User Trap Handling
// 0x040: uscratch, Scratch register for user trap handlers.
// 0x041: uepc, User exception program counter.
// 0x042: ucause, User trap cause.
// 0x043: utval, User bad address or instruction.
// 0x044: uip, User interrupt pending.
// Supervisor Trap Setup
case 0x100: { // sstatus, Supervisor status register.
final long supervisorStatusMask = getSupervisorStatusMask();
setStatus((mstatus & ~supervisorStatusMask) | (value & supervisorStatusMask));
break;
}
// 0x102: sedeleg, Supervisor exception delegation register.
// 0x103: sideleg, Supervisor interrupt delegation register.
case 0x104: { // sie, Supervisor interrupt-enable register.
final long mask = mideleg; // Can only set stuff that's delegated to S mode.
mie = (mie & ~mask) | (value & mask);
break;
}
case 0x105: { // stvec, Supervisor trap handler base address.
if ((value & 0b11) < 2) { // Don't allow reserved modes.
stvec = value;
}
break;
}
case 0x106: { // scounteren, Supervisor counter enable.
scounteren = (int) (value & COUNTEREN_MASK);
break;
}
// Supervisor Trap Handling
case 0x140: { // sscratch Scratch register for supervisor trap handlers.
sscratch = value;
break;
}
case 0x141: { // sepc Supervisor exception program counter.
sepc = value & ~0b1;
break;
}
case 0x142: { // scause Supervisor trap cause.
scause = value;
break;
}
case 0x143: { // stval Supervisor bad address or instruction.
stval = value;
break;
}
case 0x144: { // sip Supervisor interrupt pending.
final long mask = mideleg; // Can only set stuff that's delegated to S mode.
mip.updateAndGet(operand -> (operand & ~mask) | (value & mask));
break;
}
// Supervisor Protection and Translation
case 0x180: { // satp Supervisor address translation and protection.
// Say no to ASID (not implemented).
final long validatedValue;
if (xlen == R5.XLEN_32) {
validatedValue = value & ~R5.SATP_ASID_MASK32;
} else {
validatedValue = value & ~R5.SATP_ASID_MASK64;
}
final long change = satp ^ validatedValue;
if (change != 0) {
if (priv == R5.PRIVILEGE_S && (mstatus & R5.STATUS_TVM_MASK) != 0) {
throw new R5IllegalInstructionException();
}
if (xlen != R5.XLEN_32) {
// We only support Sv39 and Sv48. On unsupported writes spec says just don't change anything.
final long mode = validatedValue & R5.SATP_MODE_MASK64;
if (mode != R5.SATP_MODE_SV39 && mode != R5.SATP_MODE_SV48) {
break;
}
}
satp = validatedValue;
return true; // Invalidate fetch cache.
}
break;
}
// Virtual Supervisor Registers
// 0x200: vsstatus, Virtual supervisor status register.
// 0x204: vsie, Virtual supervisor interrupt-enable register.
// 0x205: vstvec, Virtual supervisor trap handler base address.
// 0x240: vsscratch, Virtual supervisor scratch register.
// 0x241: vsepc, Virtual supervisor exception program counter.
// 0x242: vscause, Virtual supervisor trap cause.
// 0x243: vstval, Virtual supervisor bad address or instruction.
// 0x244: vsip, Virtual supervisor interrupt pending.
// 0x280: vsatp, Virtual supervisor address translation and protection
// Machine Trap Setup
case 0x300: { // mstatus Machine status register.
setStatus(value & MSTATUS_MASK);
break;
}
case 0x301: { // misa ISA and extensions
break; // We do not support changing feature sets dynamically.
}
case 0x302: { // medeleg Machine exception delegation register.
// From Volume 2 p31: For exceptions that cannot occur in less privileged modes, the corresponding
// medeleg bits should be hardwired to zero. In particular, medeleg[11] is hardwired to zero.
medeleg = value & ~(1 << R5.EXCEPTION_MACHINE_ECALL);
break;
}
case 0x303: { // mideleg Machine interrupt delegation register.
final int mask = R5.SSIP_MASK | R5.STIP_MASK | R5.SEIP_MASK;
mideleg = (mideleg & ~mask) | (value & mask);
break;
}
case 0x304: { // mie Machine interrupt-enable register.
final int mask = R5.MTIP_MASK | R5.MSIP_MASK | R5.SEIP_MASK | R5.STIP_MASK | R5.SSIP_MASK;
mie = (mie & ~mask) | (value & mask);
break;
}
case 0x305: { // mtvec Machine trap-handler base address.
if ((value & 0b11) < 2) { // Don't allow reserved modes.
mtvec = value;
}
break;
}
case 0x306: { // mcounteren Machine counter enable.
mcounteren = (int) (value & COUNTEREN_MASK);
break;
}
case 0x310: { // mstatush Additional machine status register, RV32 only.
if (xlen != R5.XLEN_32) throw new R5IllegalInstructionException();
setStatus((value << 32) & MSTATUS_MASK);
break;
}
// Debug/Trace Registers
case 0x7A0: { // tselect
break;
}
case 0x7A1: { // tdata1
break;
}
case 0x7A2: { // tdata2
break;
}
case 0x7A3: { // tdata3
break;
}
// Machine Trap Handling
case 0x340: { // mscratch Scratch register for machine trap handlers.
mscratch = value;
break;
}
case 0x341: { // mepc Machine exception program counter.
mepc = value & ~0b1; // p38: Lowest bit must always be zero.
break;
}
case 0x342: { // mcause Machine trap cause.
mcause = value;
break;
}
case 0x343: { // mtval Machine bad address or instruction.
mtval = value;
break;
}
case 0x344: { // mip Machine interrupt pending.
// p32: MEIP, MTIP, MSIP are readonly in mip.
// Additionally, SEIP is controlled by a PLIC in our case, so we must not allow
// software to reset it, as this could lead to lost interrupts.
final int mask = R5.STIP_MASK | R5.SSIP_MASK;
mip.updateAndGet(operand -> (operand & ~mask) | (value & mask));
break;
}
// 0x34A: mtinst, Machine trap instruction (transformed).
// 0x34B: mtval2, Machine bad guest physical address.
// Machine Memory Protection
// 0x3A0: pmpcfg0. Physical memory protection configuration.
// 0x3A1: pmpcfg1. Physical memory protection configuration, RV32 only.
// 0x3A2: pmpcfg2. Physical memory protection configuration.
// 0x3A3...0x3AE: pmpcfg3...pmpcfg14, Physical memory protection configuration, RV32 only.
// 0x3AF: pmpcfg15, Physical memory protection configuration, RV32 only.
// 0x3B0: pmpaddr0, Physical memory protection address register.
// 0x3B1...0x3EF: pmpaddr1...pmpaddr63, Physical memory protection address register.
// Hypervisor Trap Setup
// 0x600: hstatus, Hypervisor status register.
// 0x602: hedeleg, Hypervisor exception delegation register.
// 0x603: hideleg, Hypervisor interrupt delegation register.
// 0x604: hie, Hypervisor interrupt-enable register.
// 0x606: hcounteren, Hypervisor counter enable.
// 0x607: hgeie, Hypervisor guest external interrupt-enable register.
// Hypervisor Trap Handling
// 0x643: htval, Hypervisor bad guest physical address.
// 0x644: hip, Hypervisor interrupt pending.
// 0x645: hvip, Hypervisor virtual interrupt pending.
// 0x64A: htinst, Hypervisor trap instruction (transformed).
// Hypervisor Protection and Translation
// 0x680: hgatp, Hypervisor guest address translation and protection.
// Hypervisor Counter/Timer Virtualization Registers
// 0x605: htimedelta, Delta for VS/VU-mode timer.
// 0x615: htimedeltah, Upper 32 bits of htimedelta, RV32 only.
// Sedna proprietary CSRs.
case 0xBC0: { // Switch to 32 bit XLEN.
// This CSR exists purely to allow switching the CPU to 32 bit mode from programs
// that were compiled for 32 bit. Since those cannot set the MXL bits of the misa
// CSR when the machine is currently in 64 bit mode.
setXLEN(R5.XLEN_32);
return true;
}
default: {
throw new R5IllegalInstructionException();
}
}
return false;
}
private void checkCounterAccess(final int bit) throws R5IllegalInstructionException {
// See Volume 2 p36: mcounteren/scounteren define availability to next lowest privilege level.
if (priv < R5.PRIVILEGE_M) {
final int counteren;
if (priv < R5.PRIVILEGE_S) {
counteren = scounteren;
} else {
counteren = mcounteren;
}
if ((counteren & (1 << bit)) == 0) {
throw new R5IllegalInstructionException();
}
}
}
///////////////////////////////////////////////////////////////////
// Misc
private long getStatus(final long mask) {
final long status = (mstatus | (fs << R5.STATUS_FS_SHIFT)) & mask;
final boolean dirty = ((mstatus & R5.STATUS_FS_MASK) == R5.STATUS_FS_MASK) ||
((mstatus & R5.STATUS_XS_MASK) == R5.STATUS_XS_MASK);
return status | (dirty ? R5.getStatusStateDirtyMask(xlen) : 0);
}
private void setStatus(final long value) {
final long change = mstatus ^ value;
final boolean mmuConfigChanged =
(change & (R5.STATUS_MPRV_MASK | R5.STATUS_SUM_MASK | R5.STATUS_MXR_MASK)) != 0 ||
((mstatus & R5.STATUS_MPRV_MASK) != 0 && (change & R5.STATUS_MPP_MASK) != 0);
if (mmuConfigChanged) {
flushTLB();
}
fs = (byte) ((value & R5.STATUS_FS_MASK) >> R5.STATUS_FS_SHIFT);
final long mask = MSTATUS_MASK & ~(R5.getStatusStateDirtyMask(xlen) | R5.STATUS_FS_MASK |
R5.STATUS_UXL_MASK | R5.STATUS_SXL_MASK);
mstatus = (mstatus & ~mask) | (value & mask);
}
private void setPrivilege(final int level) {
if (priv == level) {
return;
}
flushTLB();
switch (level) {
case R5.PRIVILEGE_S:
xlen = R5.xlen((mstatus & R5.STATUS_SXL_MASK) >>> R5.STATUS_SXL_SHIFT);
break;
case R5.PRIVILEGE_U:
xlen = R5.xlen((mstatus & R5.STATUS_UXL_MASK) >>> R5.STATUS_UXL_SHIFT);
break;
default: