virtual-machine 0.0.4 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/cpu.rs DELETED
@@ -1,1913 +0,0 @@
1
- use crate::bus::Bus;
2
- use crate::clint::{CLINT_BASE, MTIME_OFFSET};
3
- use crate::csr::{
4
- Mode, CSR_MCAUSE, CSR_MEPC, CSR_MISA, CSR_MSTATUS, CSR_MTVEC, CSR_MTVAL, CSR_MIDELEG,
5
- CSR_MIE, CSR_MIP, CSR_SATP, CSR_MEDELEG, CSR_STVEC, CSR_SEPC, CSR_SCAUSE, CSR_STVAL, CSR_SIE,
6
- CSR_SSTATUS, CSR_SIP, CSR_TIME, CSR_MENVCFG, CSR_STIMECMP,
7
- };
8
- use crate::decoder::{self, Op, Register};
9
- use crate::mmu::{self, AccessType as MmuAccessType, Tlb};
10
- use crate::Trap;
11
- use std::collections::HashMap;
12
-
13
- pub struct Cpu {
14
- pub regs: [u64; 32],
15
- pub pc: u64,
16
- /// Reservation set address for LR/SC (granule-aligned), or None if no reservation.
17
- reservation: Option<u64>,
18
- /// Simple CSR storage for Zicsr (12-bit CSR address space).
19
- csrs: [u64; 4096],
20
- /// Current privilege mode (Machine/Supervisor/User).
21
- pub mode: Mode,
22
- /// Per-hart TLB for Sv39/Sv48 translation.
23
- pub tlb: Tlb,
24
- }
25
-
26
- impl Cpu {
27
- pub fn new(pc: u64) -> Self {
28
- let mut csrs = [0u64; 4096];
29
- // misa: rv64imac_zicsr_zifencei (value from phase-0.md)
30
- const MISA_RV64IMAC_ZICSR_ZIFENCEI: u64 = 0x4000_0000_0018_1125;
31
- csrs[CSR_MISA as usize] = MISA_RV64IMAC_ZICSR_ZIFENCEI;
32
-
33
- // mstatus initial value: all zeros except UXL/SXL can be left as 0 (WARL).
34
- csrs[CSR_MSTATUS as usize] = 0;
35
-
36
- Self {
37
- regs: [0; 32],
38
- pc,
39
- reservation: None,
40
- csrs,
41
- mode: Mode::Machine,
42
- tlb: Tlb::new(),
43
- }
44
- }
45
-
46
- /// Export the current CSR image into a compact map suitable for
47
- /// serialization in snapshots.
48
- pub fn export_csrs(&self) -> HashMap<u16, u64> {
49
- let mut map = HashMap::new();
50
- for (idx, &val) in self.csrs.iter().enumerate() {
51
- if val != 0 {
52
- map.insert(idx as u16, val);
53
- }
54
- }
55
- map
56
- }
57
-
58
- /// Restore CSRs from a previously exported map.
59
- ///
60
- /// Any CSR not present in the map is reset to 0. This is intentionally
61
- /// low-level and bypasses architectural WARL checks; it is only used for
62
- /// snapshot/restore.
63
- pub fn import_csrs(&mut self, map: &HashMap<u16, u64>) {
64
- self.csrs = [0u64; 4096];
65
- for (&addr, &val) in map.iter() {
66
- let idx = addr as usize;
67
- if idx < self.csrs.len() {
68
- self.csrs[idx] = val;
69
- }
70
- }
71
- }
72
-
73
- pub fn read_reg(&self, reg: Register) -> u64 {
74
- if reg == Register::X0 {
75
- 0
76
- } else {
77
- self.regs[reg.to_usize()]
78
- }
79
- }
80
-
81
- pub fn write_reg(&mut self, reg: Register, val: u64) {
82
- if reg != Register::X0 {
83
- self.regs[reg.to_usize()] = val;
84
- }
85
- }
86
-
87
- fn reservation_granule(addr: u64) -> u64 {
88
- const GRANULE: u64 = 64;
89
- addr & !(GRANULE - 1)
90
- }
91
-
92
- fn clear_reservation_if_conflict(&mut self, addr: u64) {
93
- if let Some(res) = self.reservation {
94
- if Self::reservation_granule(res) == Self::reservation_granule(addr) {
95
- self.reservation = None;
96
- }
97
- }
98
- }
99
-
100
- pub fn read_csr(&self, addr: u16) -> Result<u64, Trap> {
101
- // Privilege checks per RISC-V privileged spec:
102
- // CSR address bits [9:8] encode the lowest privilege level that can access:
103
- // 00 = User, 01 = Supervisor, 10 = Hypervisor (reserved), 11 = Machine
104
- let required_priv = (addr >> 8) & 0x3;
105
- let current_priv = match self.mode {
106
- Mode::User => 0,
107
- Mode::Supervisor => 1,
108
- Mode::Machine => 3,
109
- };
110
- if current_priv < required_priv {
111
- return Err(Trap::IllegalInstruction(addr as u64));
112
- }
113
-
114
- match addr {
115
- CSR_SSTATUS => {
116
- let mstatus = self.csrs[CSR_MSTATUS as usize];
117
- // Mask for sstatus view: SIE(1), SPIE(5), SPP(8), FS(13:14), XS(15:16), SUM(18), MXR(19), UXL(32:33), SD(63)
118
- // Simplified mask for this emulator:
119
- let mask = (1 << 1) | (1 << 5) | (1 << 8) | (3 << 13) | (1 << 18) | (1 << 19);
120
- Ok(mstatus & mask)
121
- }
122
- CSR_SIE => {
123
- let mie = self.csrs[CSR_MIE as usize];
124
- // Mask delegated interrupts: SSIP(1), STIP(5), SEIP(9)
125
- let mask = (1 << 1) | (1 << 5) | (1 << 9);
126
- Ok(mie & mask)
127
- }
128
- CSR_SIP => {
129
- let mip = self.csrs[CSR_MIP as usize];
130
- let mask = (1 << 1) | (1 << 5) | (1 << 9);
131
- Ok(mip & mask)
132
- }
133
- _ => Ok(self.csrs[addr as usize]),
134
- }
135
- }
136
-
137
- pub fn write_csr(&mut self, addr: u16, val: u64) -> Result<(), Trap> {
138
- // Read-only CSRs have bits [11:10] == 0b11
139
- let read_only = (addr >> 10) & 0x3 == 0x3;
140
- if read_only {
141
- // Writes to read-only CSRs are ignored (WARL behavior for some, illegal for others)
142
- // For simplicity, we just ignore the write
143
- return Ok(());
144
- }
145
-
146
- // Privilege checks per RISC-V privileged spec:
147
- // CSR address bits [9:8] encode the lowest privilege level that can access
148
- let required_priv = (addr >> 8) & 0x3;
149
- let current_priv = match self.mode {
150
- Mode::User => 0,
151
- Mode::Supervisor => 1,
152
- Mode::Machine => 3,
153
- };
154
- if current_priv < required_priv {
155
- return Err(Trap::IllegalInstruction(addr as u64));
156
- }
157
-
158
- match addr {
159
- CSR_SSTATUS => {
160
- let mut mstatus = self.csrs[CSR_MSTATUS as usize];
161
- let mask = (1 << 1) | (1 << 5) | (1 << 8) | (3 << 13) | (1 << 18) | (1 << 19);
162
- mstatus = (mstatus & !mask) | (val & mask);
163
- self.csrs[CSR_MSTATUS as usize] = mstatus;
164
- Ok(())
165
- }
166
- CSR_SIE => {
167
- let mut mie = self.csrs[CSR_MIE as usize];
168
- let mask = (1 << 1) | (1 << 5) | (1 << 9);
169
- mie = (mie & !mask) | (val & mask);
170
- self.csrs[CSR_MIE as usize] = mie;
171
- Ok(())
172
- }
173
- CSR_SIP => {
174
- let mut mip = self.csrs[CSR_MIP as usize];
175
- // Only SSIP is writable in SIP
176
- let mask = 1 << 1;
177
- mip = (mip & !mask) | (val & mask);
178
- self.csrs[CSR_MIP as usize] = mip;
179
- Ok(())
180
- }
181
- _ => {
182
- self.csrs[addr as usize] = val;
183
- Ok(())
184
- }
185
- }
186
- }
187
-
188
- /// Map a `Trap` into (is_interrupt, cause, tval) per privileged spec, or `None` if it's a host-only error.
189
- fn trap_to_cause_tval(trap: &Trap) -> Option<(bool, u64, u64)> {
190
- match *trap {
191
- Trap::InstructionAddressMisaligned(addr) => Some((false, 0, addr)),
192
- Trap::InstructionAccessFault(addr) => Some((false, 1, addr)),
193
- Trap::IllegalInstruction(bits) => Some((false, 2, bits)),
194
- Trap::Breakpoint => Some((false, 3, 0)),
195
- Trap::LoadAddressMisaligned(addr) => Some((false, 4, addr)),
196
- Trap::LoadAccessFault(addr) => Some((false, 5, addr)),
197
- Trap::StoreAddressMisaligned(addr) => Some((false, 6, addr)),
198
- Trap::StoreAccessFault(addr) => Some((false, 7, addr)),
199
- Trap::EnvironmentCallFromU => Some((false, 8, 0)),
200
- Trap::EnvironmentCallFromS => Some((false, 9, 0)),
201
- Trap::EnvironmentCallFromM => Some((false, 11, 0)),
202
- Trap::InstructionPageFault(addr) => Some((false, 12, addr)),
203
- Trap::LoadPageFault(addr) => Some((false, 13, addr)),
204
- Trap::StorePageFault(addr) => Some((false, 15, addr)),
205
-
206
- Trap::SupervisorSoftwareInterrupt => Some((true, 1, 0)),
207
- Trap::MachineSoftwareInterrupt => Some((true, 3, 0)),
208
- Trap::SupervisorTimerInterrupt => Some((true, 5, 0)),
209
- Trap::MachineTimerInterrupt => Some((true, 7, 0)),
210
- Trap::SupervisorExternalInterrupt => Some((true, 9, 0)),
211
- Trap::MachineExternalInterrupt => Some((true, 11, 0)),
212
-
213
- Trap::RequestedTrap(_) | Trap::Fatal(_) => None,
214
- }
215
- }
216
-
217
- fn handle_trap<T>(&mut self, trap: Trap, pc: u64, _insn_raw: Option<u32>) -> Result<T, Trap> {
218
- // Fatal/host-only traps bypass architectural trap entry.
219
- if let Some((is_interrupt, cause, tval)) = Self::trap_to_cause_tval(&trap) {
220
- // Determine delegation target per medeleg/mideleg
221
- let medeleg = self.csrs[CSR_MEDELEG as usize];
222
- let mideleg = self.csrs[CSR_MIDELEG as usize];
223
- let deleg_bit = 1u64 << (cause as u64);
224
-
225
- let deleg_to_s = match self.mode {
226
- // Delegation to a lower privilege is only meaningful when not in Machine mode
227
- Mode::Machine => false,
228
- _ => {
229
- if is_interrupt {
230
- (mideleg & deleg_bit) != 0
231
- } else {
232
- (medeleg & deleg_bit) != 0
233
- }
234
- }
235
- };
236
-
237
- if deleg_to_s {
238
- // Supervisor trap entry (do not modify M-mode CSRs)
239
- // Save faulting PC and tval to supervisor CSRs
240
- self.csrs[CSR_SEPC as usize] = pc;
241
- self.csrs[CSR_STVAL as usize] = tval;
242
- let scause_val = ((is_interrupt as u64) << 63) | (cause & 0x7FFF_FFFF_FFFF_FFFF);
243
- self.csrs[CSR_SCAUSE as usize] = scause_val;
244
-
245
- // Update mstatus: SPP, SPIE, clear SIE
246
- let mut mstatus = self.csrs[CSR_MSTATUS as usize];
247
- if log::log_enabled!(log::Level::Trace) {
248
- log::trace!("Trap to S-mode: mstatus_before={:x}", mstatus);
249
- }
250
-
251
- let sie = (mstatus >> 1) & 1;
252
- // SPIE <= SIE
253
- mstatus = (mstatus & !(1 << 5)) | (sie << 5);
254
- // SIE <= 0
255
- mstatus &= !(1 << 1);
256
- // SPP <= current privilege (1 if S, 0 if U)
257
- let spp = match self.mode {
258
- Mode::Supervisor => 1,
259
- _ => 0,
260
- };
261
- mstatus = (mstatus & !(1 << 8)) | (spp << 8);
262
- self.csrs[CSR_MSTATUS as usize] = mstatus;
263
-
264
- if log::log_enabled!(log::Level::Trace) {
265
- log::trace!("Trap to S-mode: mstatus_after={:x}", mstatus);
266
- }
267
-
268
- self.mode = Mode::Supervisor;
269
-
270
- // Set PC to stvec (vectored if interrupt and mode==1)
271
- let stvec = self.csrs[CSR_STVEC as usize];
272
- let base = stvec & !0b11;
273
- let mode = stvec & 0b11;
274
- let vectored = mode == 1;
275
- let target_pc = if is_interrupt && vectored {
276
- base.wrapping_add(4 * cause)
277
- } else {
278
- base
279
- };
280
- self.pc = target_pc;
281
- } else {
282
- // Machine trap entry (default)
283
- // Save faulting PC and tval.
284
- self.csrs[CSR_MEPC as usize] = pc;
285
- self.csrs[CSR_MTVAL as usize] = tval;
286
-
287
- let mcause_val = ((is_interrupt as u64) << 63) | (cause & 0x7FFF_FFFF_FFFF_FFFF);
288
- self.csrs[CSR_MCAUSE as usize] = mcause_val;
289
-
290
- // Update mstatus: MPP, MPIE, clear MIE
291
- let mut mstatus = self.csrs[CSR_MSTATUS as usize];
292
- let mie = (mstatus >> 3) & 1;
293
- // MPIE <= MIE, MIE <= 0
294
- mstatus = (mstatus & !(1 << 7)) | (mie << 7);
295
- mstatus &= !(1 << 3);
296
- // MPP <= current mode.
297
- let mpp = self.mode.to_mpp();
298
- mstatus = (mstatus & !(0b11 << 11)) | (mpp << 11);
299
- self.csrs[CSR_MSTATUS as usize] = mstatus;
300
- self.mode = Mode::Machine;
301
-
302
- // Set PC to mtvec (vectored if interrupt and mode==1)
303
- let mtvec = self.csrs[CSR_MTVEC as usize];
304
- let base = mtvec & !0b11;
305
- let mode = mtvec & 0b11;
306
- let vectored = mode == 1;
307
- let target_pc = if is_interrupt && vectored {
308
- base.wrapping_add(4 * cause)
309
- } else {
310
- base
311
- };
312
- self.pc = target_pc;
313
- }
314
- }
315
-
316
- Err(trap)
317
- }
318
-
319
- /// Translate a virtual address to a physical address using the MMU.
320
- ///
321
- /// On translation failure, this enters the trap handler and returns the
322
- /// trap via `Err`.
323
- fn translate_addr(
324
- &mut self,
325
- bus: &mut dyn Bus,
326
- vaddr: u64,
327
- access: MmuAccessType,
328
- pc: u64,
329
- insn_raw: Option<u32>,
330
- ) -> Result<u64, Trap> {
331
- let satp = self.csrs[CSR_SATP as usize];
332
- let mstatus = self.csrs[CSR_MSTATUS as usize];
333
- match mmu::translate(bus, &mut self.tlb, self.mode, satp, mstatus, vaddr, access) {
334
- Ok(pa) => Ok(pa),
335
- Err(trap) => self.handle_trap(trap, pc, insn_raw),
336
- }
337
- }
338
-
339
- fn fetch_and_expand(&mut self, bus: &mut dyn Bus) -> Result<(u32, u8), Trap> {
340
- let pc = self.pc;
341
- if pc % 2 != 0 {
342
- return self.handle_trap(Trap::InstructionAddressMisaligned(pc), pc, None);
343
- }
344
-
345
- // Fetch first halfword via MMU (instruction access).
346
- let pa_low = self.translate_addr(bus, pc, MmuAccessType::Instruction, pc, None)?;
347
- let half = match bus.read16(pa_low) {
348
- Ok(v) => v,
349
- Err(e) => {
350
- // Map load faults from the bus into instruction faults.
351
- let mapped = match e {
352
- Trap::LoadAccessFault(_) => Trap::InstructionAccessFault(pc),
353
- Trap::LoadAddressMisaligned(_) => Trap::InstructionAddressMisaligned(pc),
354
- other => other,
355
- };
356
- return self.handle_trap(mapped, pc, None);
357
- }
358
- };
359
-
360
- if half & 0x3 != 0x3 {
361
- // Compressed 16-bit instruction
362
- let insn32 = match decoder::expand_compressed(half) {
363
- Ok(v) => v,
364
- Err(trap) => return self.handle_trap(trap, pc, None),
365
- };
366
- Ok((insn32, 2))
367
- } else {
368
- // 32-bit instruction; fetch high half via MMU as well.
369
- let pc_hi = pc.wrapping_add(2);
370
- let pa_hi = self.translate_addr(bus, pc_hi, MmuAccessType::Instruction, pc, None)?;
371
- let hi = match bus.read16(pa_hi) {
372
- Ok(v) => v,
373
- Err(e) => {
374
- let mapped = match e {
375
- Trap::LoadAccessFault(_) => Trap::InstructionAccessFault(pc),
376
- Trap::LoadAddressMisaligned(_) => Trap::InstructionAddressMisaligned(pc),
377
- other => other,
378
- };
379
- return self.handle_trap(mapped, pc, None);
380
- }
381
- };
382
- let insn32 = (half as u32) | ((hi as u32) << 16);
383
- Ok((insn32, 4))
384
- }
385
- }
386
-
387
- fn check_pending_interrupt(&self) -> Option<Trap> {
388
- let mstatus = self.csrs[CSR_MSTATUS as usize];
389
- let mip = self.csrs[CSR_MIP as usize];
390
- let mie = self.csrs[CSR_MIE as usize];
391
- let mideleg = self.csrs[CSR_MIDELEG as usize];
392
-
393
- // SIE is a shadow of MIE for supervisor interrupt bits (SSIP=1, STIP=5, SEIP=9)
394
- let sie_mask: u64 = (1 << 1) | (1 << 5) | (1 << 9);
395
- let sie = mie & sie_mask;
396
-
397
- // Mask delegated interrupts out of machine set, and into supervisor set.
398
- let m_pending = (mip & mie) & !mideleg;
399
- let s_pending = (mip & sie) & mideleg;
400
-
401
- // Machine mode global enable:
402
- // Enabled if (currently in Machine and MIE==1) OR (currently below Machine).
403
- let m_enabled = match self.mode {
404
- Mode::Machine => ((mstatus >> 3) & 1) == 1, // MIE
405
- _ => true,
406
- };
407
- if m_enabled {
408
- if (m_pending & (1 << 11)) != 0 { return Some(Trap::MachineExternalInterrupt); } // MEIP
409
- if (m_pending & (1 << 3)) != 0 { return Some(Trap::MachineSoftwareInterrupt); } // MSIP
410
- if (m_pending & (1 << 7)) != 0 { return Some(Trap::MachineTimerInterrupt); } // MTIP
411
- }
412
-
413
- // Supervisor mode global enable:
414
- // Enabled if (currently in Supervisor and SIE==1) OR (currently in User).
415
- let s_enabled = match self.mode {
416
- Mode::Machine => false,
417
- Mode::Supervisor => ((mstatus >> 1) & 1) == 1, // SIE
418
- Mode::User => true,
419
- };
420
-
421
- // DEBUG: Check if we are about to trap for interrupt when we shouldn't
422
- if s_enabled && s_pending != 0 {
423
- if log::log_enabled!(log::Level::Trace) {
424
- log::trace!("Interrupt pending: s_pending={:x} mstatus={:x} mode={:?}", s_pending, mstatus, self.mode);
425
- }
426
- }
427
-
428
- if s_enabled {
429
- if (s_pending & (1 << 9)) != 0 { return Some(Trap::SupervisorExternalInterrupt); } // SEIP
430
- if (s_pending & (1 << 1)) != 0 { return Some(Trap::SupervisorSoftwareInterrupt); } // SSIP
431
- if (s_pending & (1 << 5)) != 0 { return Some(Trap::SupervisorTimerInterrupt); } // STIP
432
- }
433
-
434
- None
435
- }
436
-
437
- pub fn step(&mut self, bus: &mut dyn Bus) -> Result<(), Trap> {
438
- // Poll device-driven interrupts into MIP mask.
439
- let mut hw_mip = bus.poll_interrupts();
440
-
441
- // Sstc support: raise STIP (bit 5) when time >= stimecmp and Sstc enabled.
442
- // menvcfg[63] gate is optional; xv6 enables it.
443
- let menvcfg = self.csrs[CSR_MENVCFG as usize];
444
- let sstc_enabled = ((menvcfg >> 63) & 1) == 1;
445
- let stimecmp = self.csrs[CSR_STIMECMP as usize];
446
- if sstc_enabled && stimecmp != 0 {
447
- // Read CLINT MTIME directly (physical address).
448
- if let Ok(now) = bus.read64(CLINT_BASE + MTIME_OFFSET) {
449
- if now >= stimecmp {
450
- hw_mip |= 1 << 5; // STIP
451
- }
452
- }
453
- }
454
-
455
- // Update MIP: preserve software-writable bits (SSIP=bit1, STIP=bit5 if not Sstc),
456
- // but always update hardware-driven bits (MSIP=3, MTIP=7, SEIP=9, MEIP=11).
457
- // SSIP (bit 1) is software-writable and should be preserved.
458
- // STIP (bit 5) is normally read-only but Sstc makes it hardware-driven.
459
- let hw_bits: u64 = (1 << 3) | (1 << 7) | (1 << 9) | (1 << 11); // MSIP, MTIP, SEIP, MEIP
460
- let hw_bits_with_stip: u64 = hw_bits | (1 << 5); // Include STIP when Sstc enabled
461
-
462
- let mask = if sstc_enabled { hw_bits_with_stip } else { hw_bits };
463
- let old_mip = self.csrs[CSR_MIP as usize];
464
- self.csrs[CSR_MIP as usize] = (old_mip & !mask) | (hw_mip & mask);
465
-
466
- if let Some(trap) = self.check_pending_interrupt() {
467
- return self.handle_trap(trap, self.pc, None);
468
- }
469
-
470
- let pc = self.pc;
471
- // Fetch (supports compressed 16-bit and regular 32-bit instructions)
472
- let (insn_raw, insn_len) = self.fetch_and_expand(bus)?;
473
- // Decode
474
- let op = match decoder::decode(insn_raw) {
475
- Ok(v) => v,
476
- Err(trap) => return self.handle_trap(trap, pc, Some(insn_raw)),
477
- };
478
-
479
- let mut next_pc = pc.wrapping_add(insn_len as u64);
480
-
481
- match op {
482
- Op::Lui { rd, imm } => {
483
- self.write_reg(rd, imm as u64);
484
- }
485
- Op::Auipc { rd, imm } => {
486
- self.write_reg(rd, pc.wrapping_add(imm as u64));
487
- }
488
- Op::Jal { rd, imm } => {
489
- self.write_reg(rd, pc.wrapping_add(insn_len as u64));
490
- next_pc = pc.wrapping_add(imm as u64);
491
- if next_pc % 2 != 0 {
492
- return self.handle_trap(
493
- Trap::InstructionAddressMisaligned(next_pc),
494
- pc,
495
- Some(insn_raw),
496
- );
497
- }
498
- }
499
- Op::Jalr { rd, rs1, imm } => {
500
- let target = self.read_reg(rs1).wrapping_add(imm as u64) & !1;
501
- self.write_reg(rd, pc.wrapping_add(insn_len as u64));
502
- next_pc = target;
503
- if next_pc % 2 != 0 {
504
- return self.handle_trap(
505
- Trap::InstructionAddressMisaligned(next_pc),
506
- pc,
507
- Some(insn_raw),
508
- );
509
- }
510
- }
511
- Op::Branch {
512
- rs1,
513
- rs2,
514
- imm,
515
- funct3,
516
- } => {
517
- let val1 = self.read_reg(rs1);
518
- let val2 = self.read_reg(rs2);
519
- let taken = match funct3 {
520
- 0 => val1 == val2, // BEQ
521
- 1 => val1 != val2, // BNE
522
- 4 => (val1 as i64) < (val2 as i64), // BLT
523
- 5 => (val1 as i64) >= (val2 as i64), // BGE
524
- 6 => val1 < val2, // BLTU
525
- 7 => val1 >= val2, // BGEU
526
- _ => {
527
- return self.handle_trap(
528
- Trap::IllegalInstruction(insn_raw as u64),
529
- pc,
530
- Some(insn_raw),
531
- )
532
- }
533
- };
534
- if taken {
535
- next_pc = pc.wrapping_add(imm as u64);
536
- if next_pc % 2 != 0 {
537
- return self.handle_trap(
538
- Trap::InstructionAddressMisaligned(next_pc),
539
- pc,
540
- Some(insn_raw),
541
- );
542
- }
543
- }
544
- }
545
- Op::Load {
546
- rd,
547
- rs1,
548
- imm,
549
- funct3,
550
- } => {
551
- let addr = self.read_reg(rs1).wrapping_add(imm as u64);
552
- let val = match funct3 {
553
- 0 => {
554
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
555
- match bus.read8(pa) {
556
- Ok(v) => (v as i8) as i64 as u64, // LB
557
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
558
- }}
559
- 1 => {
560
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
561
- match bus.read16(pa) {
562
- Ok(v) => (v as i16) as i64 as u64, // LH
563
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
564
- }}
565
- 2 => {
566
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
567
- match bus.read32(pa) {
568
- Ok(v) => (v as i32) as i64 as u64, // LW
569
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
570
- }}
571
- 3 => {
572
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
573
- match bus.read64(pa) {
574
- Ok(v) => v, // LD
575
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
576
- }}
577
- 4 => {
578
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
579
- match bus.read8(pa) {
580
- Ok(v) => v as u64, // LBU
581
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
582
- }}
583
- 5 => {
584
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
585
- match bus.read16(pa) {
586
- Ok(v) => v as u64, // LHU
587
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
588
- }}
589
- 6 => {
590
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
591
- match bus.read32(pa) {
592
- Ok(v) => v as u64, // LWU
593
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
594
- }}
595
- _ => {
596
- return self.handle_trap(
597
- Trap::IllegalInstruction(insn_raw as u64),
598
- pc,
599
- Some(insn_raw),
600
- )
601
- }
602
- };
603
- self.write_reg(rd, val);
604
- }
605
- Op::Store {
606
- rs1,
607
- rs2,
608
- imm,
609
- funct3,
610
- } => {
611
- let addr = self.read_reg(rs1).wrapping_add(imm as u64);
612
- let pa = self.translate_addr(bus, addr, MmuAccessType::Store, pc, Some(insn_raw))?;
613
- // Any store to the reservation granule clears LR/SC reservation.
614
- self.clear_reservation_if_conflict(addr);
615
- let val = self.read_reg(rs2);
616
- let res = match funct3 {
617
- 0 => bus.write8(pa, val as u8), // SB
618
- 1 => bus.write16(pa, val as u16), // SH
619
- 2 => bus.write32(pa, val as u32), // SW
620
- 3 => bus.write64(pa, val), // SD
621
- _ => {
622
- return self.handle_trap(
623
- Trap::IllegalInstruction(insn_raw as u64),
624
- pc,
625
- Some(insn_raw),
626
- )
627
- }
628
- };
629
- if let Err(e) = res {
630
- return self.handle_trap(e, pc, Some(insn_raw));
631
- }
632
- }
633
- Op::OpImm {
634
- rd,
635
- rs1,
636
- imm,
637
- funct3,
638
- funct7,
639
- } => {
640
- let val1 = self.read_reg(rs1);
641
- let res = match funct3 {
642
- 0 => val1.wrapping_add(imm as u64), // ADDI
643
- 2 => {
644
- if (val1 as i64) < imm {
645
- 1
646
- } else {
647
- 0
648
- }
649
- } // SLTI
650
- 3 => {
651
- if val1 < (imm as u64) {
652
- 1
653
- } else {
654
- 0
655
- }
656
- } // SLTIU
657
- 4 => val1 ^ (imm as u64), // XORI
658
- 6 => val1 | (imm as u64), // ORI
659
- 7 => val1 & (imm as u64), // ANDI
660
- 1 => {
661
- // SLLI
662
- let shamt = imm & 0x3F;
663
- val1 << shamt
664
- }
665
- 5 => {
666
- // SRLI / SRAI
667
- let shamt = imm & 0x3F;
668
- if funct7 & 0x20 != 0 {
669
- // SRAI
670
- ((val1 as i64) >> shamt) as u64
671
- } else {
672
- // SRLI
673
- val1 >> shamt
674
- }
675
- }
676
- _ => {
677
- return self.handle_trap(
678
- Trap::IllegalInstruction(insn_raw as u64),
679
- pc,
680
- Some(insn_raw),
681
- )
682
- }
683
- };
684
- self.write_reg(rd, res);
685
- }
686
- Op::Op {
687
- rd,
688
- rs1,
689
- rs2,
690
- funct3,
691
- funct7,
692
- } => {
693
- let val1 = self.read_reg(rs1);
694
- let val2 = self.read_reg(rs2);
695
- let res = match (funct3, funct7) {
696
- (0, 0x00) => val1.wrapping_add(val2), // ADD
697
- (0, 0x20) => val1.wrapping_sub(val2), // SUB
698
- // M-extension (RV64M) - MUL/DIV/REM on XLEN=64
699
- (0, 0x01) => {
700
- // MUL: low 64 bits of signed(rs1) * signed(rs2)
701
- let a = val1 as i64 as i128;
702
- let b = val2 as i64 as i128;
703
- (a.wrapping_mul(b) as i64) as u64
704
- }
705
- (1, 0x00) => val1 << (val2 & 0x3F), // SLL
706
- (1, 0x01) => {
707
- // MULH: high 64 bits of signed * signed
708
- let a = val1 as i64 as i128;
709
- let b = val2 as i64 as i128;
710
- ((a.wrapping_mul(b) >> 64) as i64) as u64
711
- }
712
- (2, 0x00) => {
713
- if (val1 as i64) < (val2 as i64) {
714
- 1
715
- } else {
716
- 0
717
- }
718
- } // SLT
719
- (2, 0x01) => {
720
- // MULHSU: high 64 bits of signed * unsigned
721
- let a = val1 as i64 as i128;
722
- let b = val2 as u64 as i128;
723
- ((a.wrapping_mul(b) >> 64) as i64) as u64
724
- }
725
- (3, 0x00) => {
726
- if val1 < val2 {
727
- 1
728
- } else {
729
- 0
730
- }
731
- } // SLTU
732
- (3, 0x01) => {
733
- // MULHU: high 64 bits of unsigned * unsigned
734
- let a = val1 as u128;
735
- let b = val2 as u128;
736
- ((a.wrapping_mul(b) >> 64) as u64) as u64
737
- }
738
- (4, 0x00) => val1 ^ val2, // XOR
739
- (4, 0x01) => {
740
- // DIV (signed)
741
- let a = val1 as i64;
742
- let b = val2 as i64;
743
- let q = if b == 0 {
744
- -1i64
745
- } else if a == i64::MIN && b == -1 {
746
- i64::MIN
747
- } else {
748
- a / b
749
- };
750
- q as u64
751
- }
752
- (5, 0x00) => val1 >> (val2 & 0x3F), // SRL
753
- (5, 0x01) => {
754
- // DIVU (unsigned)
755
- let a = val1;
756
- let b = val2;
757
- let q = if b == 0 { u64::MAX } else { a / b };
758
- q
759
- }
760
- (5, 0x20) => ((val1 as i64) >> (val2 & 0x3F)) as u64, // SRA
761
- (6, 0x00) => val1 | val2, // OR
762
- (6, 0x01) => {
763
- // REM (signed)
764
- let a = val1 as i64;
765
- let b = val2 as i64;
766
- let r = if b == 0 {
767
- a
768
- } else if a == i64::MIN && b == -1 {
769
- 0
770
- } else {
771
- a % b
772
- };
773
- r as u64
774
- }
775
- (7, 0x00) => val1 & val2, // AND
776
- (7, 0x01) => {
777
- // REMU (unsigned)
778
- let a = val1;
779
- let b = val2;
780
- let r = if b == 0 { a } else { a % b };
781
- r
782
- }
783
- _ => {
784
- return self.handle_trap(
785
- Trap::IllegalInstruction(insn_raw as u64),
786
- pc,
787
- Some(insn_raw),
788
- )
789
- }
790
- };
791
- self.write_reg(rd, res);
792
- }
793
- Op::OpImm32 {
794
- rd,
795
- rs1,
796
- imm,
797
- funct3,
798
- funct7,
799
- } => {
800
- let val1 = self.read_reg(rs1);
801
- let res = match funct3 {
802
- 0 => (val1.wrapping_add(imm as u64) as i32) as i64 as u64, // ADDIW
803
- 1 => ((val1 as u32) << (imm & 0x1F)) as i32 as i64 as u64, // SLLIW
804
- 5 => {
805
- let shamt = imm & 0x1F;
806
- if funct7 & 0x20 != 0 {
807
- // SRAIW
808
- ((val1 as i32) >> shamt) as i64 as u64
809
- } else {
810
- // SRLIW
811
- ((val1 as u32) >> shamt) as i32 as i64 as u64
812
- }
813
- }
814
- _ => {
815
- return self.handle_trap(
816
- Trap::IllegalInstruction(insn_raw as u64),
817
- pc,
818
- Some(insn_raw),
819
- )
820
- }
821
- };
822
- self.write_reg(rd, res);
823
- }
824
- Op::Op32 {
825
- rd,
826
- rs1,
827
- rs2,
828
- funct3,
829
- funct7,
830
- } => {
831
- let val1 = self.read_reg(rs1);
832
- let val2 = self.read_reg(rs2);
833
- let res = match (funct3, funct7) {
834
- (0, 0x00) => (val1.wrapping_add(val2) as i32) as i64 as u64, // ADDW
835
- (0, 0x20) => (val1.wrapping_sub(val2) as i32) as i64 as u64, // SUBW
836
- (0, 0x01) => {
837
- // MULW: low 32 bits of signed* signed, sign-extended to 64
838
- let a = val1 as i32 as i64;
839
- let b = val2 as i32 as i64;
840
- let prod = (a as i128).wrapping_mul(b as i128);
841
- (prod as i32) as i64 as u64
842
- }
843
- (1, 0x00) => ((val1 as u32) << (val2 & 0x1F)) as i32 as i64 as u64, // SLLW
844
- (5, 0x00) => ((val1 as u32) >> (val2 & 0x1F)) as i32 as i64 as u64, // SRLW
845
- (4, 0x01) => {
846
- // DIVW (signed 32-bit)
847
- let a = val1 as i32 as i64;
848
- let b = val2 as i32 as i64;
849
- let q = if b == 0 {
850
- -1i64
851
- } else if a == i64::from(i32::MIN) && b == -1 {
852
- i64::from(i32::MIN)
853
- } else {
854
- a / b
855
- };
856
- (q as i32) as i64 as u64
857
- }
858
- (5, 0x20) => ((val1 as i32) >> (val2 & 0x1F)) as i64 as u64, // SRAW
859
- (5, 0x01) => {
860
- // DIVUW (unsigned 32-bit)
861
- let a = val1 as u32 as u64;
862
- let b = val2 as u32 as u64;
863
- let q = if b == 0 { u64::MAX } else { a / b };
864
- (q as u32) as i32 as i64 as u64
865
- }
866
- (6, 0x01) => {
867
- // REMW (signed 32-bit)
868
- let a = val1 as i32 as i64;
869
- let b = val2 as i32 as i64;
870
- let r = if b == 0 {
871
- a
872
- } else if a == i64::from(i32::MIN) && b == -1 {
873
- 0
874
- } else {
875
- a % b
876
- };
877
- (r as i32) as i64 as u64
878
- }
879
- (7, 0x01) => {
880
- // REMUW (unsigned 32-bit)
881
- let a = val1 as u32 as u64;
882
- let b = val2 as u32 as u64;
883
- let r = if b == 0 { a } else { a % b };
884
- (r as u32) as i32 as i64 as u64
885
- }
886
- _ => {
887
- return self.handle_trap(
888
- Trap::IllegalInstruction(insn_raw as u64),
889
- pc,
890
- Some(insn_raw),
891
- )
892
- }
893
- };
894
- self.write_reg(rd, res);
895
- }
896
- Op::Amo {
897
- rd,
898
- rs1,
899
- rs2,
900
- funct3,
901
- funct5,
902
- ..
903
- } => {
904
- let addr = self.read_reg(rs1);
905
-
906
- // Translate once per AMO/LD/ST sequence.
907
- let pa = self.translate_addr(bus, addr, MmuAccessType::Load, pc, Some(insn_raw))?;
908
-
909
- // Only word (funct3=2) and doubleword (funct3=3) widths are valid.
910
- let is_word = match funct3 {
911
- 2 => true,
912
- 3 => false,
913
- _ => {
914
- return self.handle_trap(
915
- Trap::IllegalInstruction(insn_raw as u64),
916
- pc,
917
- Some(insn_raw),
918
- )
919
- }
920
- };
921
-
922
- // LR/SC vs AMO op distinguished by funct5
923
- match funct5 {
924
- 0b00010 => {
925
- // LR.W / LR.D
926
- let loaded = if is_word {
927
- match bus.read32(pa) {
928
- Ok(v) => v as i32 as i64 as u64,
929
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
930
- }
931
- } else {
932
- match bus.read64(pa) {
933
- Ok(v) => v,
934
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
935
- }
936
- };
937
- self.write_reg(rd, loaded);
938
- self.reservation = Some(Self::reservation_granule(addr));
939
- }
940
- 0b00011 => {
941
- // SC.W / SC.D
942
- // Alignment checks (must be naturally aligned) on the virtual address.
943
- if is_word && addr % 4 != 0 {
944
- return self.handle_trap(
945
- Trap::StoreAddressMisaligned(addr),
946
- pc,
947
- Some(insn_raw),
948
- );
949
- }
950
- if !is_word && addr % 8 != 0 {
951
- return self.handle_trap(
952
- Trap::StoreAddressMisaligned(addr),
953
- pc,
954
- Some(insn_raw),
955
- );
956
- }
957
- let granule = Self::reservation_granule(addr);
958
- if self.reservation == Some(granule) {
959
- // Successful store
960
- let val = self.read_reg(rs2);
961
- let res = if is_word {
962
- bus.write32(pa, val as u32)
963
- } else {
964
- bus.write64(pa, val)
965
- };
966
- if let Err(e) = res {
967
- return self.handle_trap(e, pc, Some(insn_raw));
968
- }
969
- self.write_reg(rd, 0);
970
- self.reservation = None;
971
- } else {
972
- // Failed store, no memory access
973
- self.write_reg(rd, 1);
974
- }
975
- }
976
- // AMO* operations
977
- 0b00001 | // AMOSWAP
978
- 0b00000 | // AMOADD
979
- 0b00100 | // AMOXOR
980
- 0b01000 | // AMOOR
981
- 0b01100 | // AMOAND
982
- 0b10000 | // AMOMIN
983
- 0b10100 | // AMOMAX
984
- 0b11000 | // AMOMINU
985
- 0b11100 // AMOMAXU
986
- => {
987
- // Any AMO acts like a store to the address, so clear reservation.
988
- self.clear_reservation_if_conflict(addr);
989
-
990
- let old = if is_word {
991
- match bus.read32(pa) {
992
- Ok(v) => v as i32 as i64 as u64,
993
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
994
- }
995
- } else {
996
- match bus.read64(pa) {
997
- Ok(v) => v,
998
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
999
- }
1000
- };
1001
- let rs2_val = self.read_reg(rs2);
1002
-
1003
- let new_val = match funct5 {
1004
- 0b00001 => rs2_val, // AMOSWAP
1005
- 0b00000 => old.wrapping_add(rs2_val), // AMOADD
1006
- 0b00100 => old ^ rs2_val, // AMOXOR
1007
- 0b01000 => old | rs2_val, // AMOOR
1008
- 0b01100 => old & rs2_val, // AMOAND
1009
- 0b10000 => {
1010
- // AMOMIN (signed)
1011
- let a = old as i64;
1012
- let b = rs2_val as i64;
1013
- if a < b { old } else { rs2_val }
1014
- }
1015
- 0b10100 => {
1016
- // AMOMAX (signed)
1017
- let a = old as i64;
1018
- let b = rs2_val as i64;
1019
- if a > b { old } else { rs2_val }
1020
- }
1021
- 0b11000 => {
1022
- // AMOMINU (unsigned)
1023
- if old < rs2_val { old } else { rs2_val }
1024
- }
1025
- 0b11100 => {
1026
- // AMOMAXU (unsigned)
1027
- if old > rs2_val { old } else { rs2_val }
1028
- }
1029
- _ => unreachable!(),
1030
- };
1031
-
1032
- let res = if is_word {
1033
- bus.write32(pa, new_val as u32)
1034
- } else {
1035
- bus.write64(pa, new_val)
1036
- };
1037
- if let Err(e) = res {
1038
- return self.handle_trap(e, pc, Some(insn_raw));
1039
- }
1040
-
1041
- // rd receives the original loaded value (sign-extended to XLEN)
1042
- self.write_reg(rd, old);
1043
- }
1044
- _ => {
1045
- return self.handle_trap(
1046
- Trap::IllegalInstruction(insn_raw as u64),
1047
- pc,
1048
- Some(insn_raw),
1049
- );
1050
- }
1051
- }
1052
- }
1053
- Op::System {
1054
- rd,
1055
- rs1,
1056
- funct3,
1057
- imm,
1058
- ..
1059
- } => {
1060
- match funct3 {
1061
- 0 => {
1062
- // SYSTEM (ECALL/EBREAK, MRET/SRET, SFENCE.VMA)
1063
-
1064
- // Detect SFENCE.VMA via mask/match (funct7=0001001, opcode=0x73, rd=0).
1065
- const SFENCE_VMA_MASK: u32 = 0b1111111_00000_00000_111_00000_1111111;
1066
- const SFENCE_VMA_MATCH: u32 = 0b0001001_00000_00000_000_00000_1110011; // 0x12000073
1067
-
1068
- if (insn_raw & SFENCE_VMA_MASK) == SFENCE_VMA_MATCH {
1069
- // Only legal from S or M mode.
1070
- if matches!(self.mode, Mode::User) {
1071
- return self.handle_trap(
1072
- Trap::IllegalInstruction(insn_raw as u64),
1073
- pc,
1074
- Some(insn_raw),
1075
- );
1076
- }
1077
- // Simplest implementation: flush entire TLB.
1078
- self.tlb.flush();
1079
- } else {
1080
- match insn_raw {
1081
- 0x0010_0073 => {
1082
- // EBREAK
1083
- return self.handle_trap(Trap::Breakpoint, pc, Some(insn_raw));
1084
- }
1085
- 0x1050_0073 => {
1086
- // WFI - treat as a hint NOP
1087
- }
1088
- 0x0000_0073 => {
1089
- // ECALL - route based on current privilege mode
1090
- let trap = match self.mode {
1091
- Mode::User => Trap::EnvironmentCallFromU,
1092
- Mode::Supervisor => Trap::EnvironmentCallFromS,
1093
- Mode::Machine => Trap::EnvironmentCallFromM,
1094
- };
1095
- return self.handle_trap(trap, pc, Some(insn_raw));
1096
- }
1097
- 0x3020_0073 => {
1098
- // MRET
1099
- if self.mode != Mode::Machine {
1100
- return self.handle_trap(
1101
- Trap::IllegalInstruction(insn_raw as u64),
1102
- pc,
1103
- Some(insn_raw),
1104
- );
1105
- }
1106
-
1107
- let mut mstatus = self.csrs[CSR_MSTATUS as usize];
1108
- let mepc = self.csrs[CSR_MEPC as usize];
1109
-
1110
- // Extract MPP and MPIE
1111
- let mpp_bits = (mstatus >> 11) & 0b11;
1112
- let mpie = (mstatus >> 7) & 1;
1113
-
1114
- // Set new privilege mode from MPP
1115
- self.mode = Mode::from_mpp(mpp_bits);
1116
-
1117
- // MIE <= MPIE, MPIE <= 1, MPP <= U (00)
1118
- mstatus = (mstatus & !(1 << 3)) | (mpie << 3);
1119
- mstatus |= 1 << 7; // MPIE = 1
1120
- mstatus &= !(0b11 << 11); // MPP = U (00)
1121
-
1122
- self.csrs[CSR_MSTATUS as usize] = mstatus;
1123
- next_pc = mepc;
1124
- }
1125
- 0x1020_0073 => {
1126
- // SRET (only valid from S-mode)
1127
- if self.mode != Mode::Supervisor {
1128
- return self.handle_trap(
1129
- Trap::IllegalInstruction(insn_raw as u64),
1130
- pc,
1131
- Some(insn_raw),
1132
- );
1133
- }
1134
-
1135
- // We model only the SPP/SIE/SPIE subset of mstatus.
1136
- let mut mstatus = self.csrs[CSR_MSTATUS as usize];
1137
- let sepc = self.csrs[CSR_SEPC as usize];
1138
-
1139
- // SPP is bit 8, SPIE is bit 5, SIE is bit 1.
1140
- let spp = (mstatus >> 8) & 1;
1141
- let spie = (mstatus >> 5) & 1;
1142
-
1143
- self.mode = if spp == 0 {
1144
- Mode::User
1145
- } else {
1146
- Mode::Supervisor
1147
- };
1148
-
1149
- // SIE <= SPIE, SPIE <= 1, SPP <= U (0)
1150
- mstatus = (mstatus & !(1 << 1)) | (spie << 1);
1151
- mstatus |= 1 << 5; // SPIE = 1
1152
- mstatus &= !(1 << 8); // SPP = U
1153
-
1154
- self.csrs[CSR_MSTATUS as usize] = mstatus;
1155
- next_pc = sepc;
1156
- }
1157
- _ => {
1158
- return self.handle_trap(
1159
- Trap::IllegalInstruction(insn_raw as u64),
1160
- pc,
1161
- Some(insn_raw),
1162
- );
1163
- }
1164
- }
1165
- }
1166
- }
1167
- // Zicsr: CSRRW/CSRRS/CSRRC
1168
- 1 | 2 | 3 | 5 | 6 | 7 => {
1169
- let csr_addr = (imm & 0xFFF) as u16;
1170
- // Dynamic read for time CSR to reflect CLINT MTIME.
1171
- let old = if csr_addr == CSR_TIME {
1172
- bus.read64(CLINT_BASE + MTIME_OFFSET).unwrap_or(0)
1173
- } else {
1174
- match self.read_csr(csr_addr) {
1175
- Ok(v) => v,
1176
- Err(e) => return self.handle_trap(e, pc, Some(insn_raw)),
1177
- }
1178
- };
1179
-
1180
- let mut write_new = None::<u64>;
1181
- match funct3 {
1182
- // CSRRW: write rs1, rd = old
1183
- 1 => {
1184
- let rs1_val = self.read_reg(rs1);
1185
- write_new = Some(rs1_val);
1186
- }
1187
- // CSRRS: set bits in CSR with rs1
1188
- 2 => {
1189
- let rs1_val = self.read_reg(rs1);
1190
- if rs1 != Register::X0 {
1191
- write_new = Some(old | rs1_val);
1192
- }
1193
- }
1194
- // CSRRC: clear bits in CSR with rs1
1195
- 3 => {
1196
- let rs1_val = self.read_reg(rs1);
1197
- if rs1 != Register::X0 {
1198
- write_new = Some(old & !rs1_val);
1199
- }
1200
- }
1201
- // CSRRWI: write zero-extended zimm, rd = old
1202
- 5 => {
1203
- let zimm = rs1.to_usize() as u64;
1204
- write_new = Some(zimm);
1205
- }
1206
- // CSRRSI: set bits using zimm (if non-zero)
1207
- 6 => {
1208
- let zimm = rs1.to_usize() as u64;
1209
- if zimm != 0 {
1210
- write_new = Some(old | zimm);
1211
- }
1212
- }
1213
- // CSRRCI: clear bits using zimm (if non-zero)
1214
- 7 => {
1215
- let zimm = rs1.to_usize() as u64;
1216
- if zimm != 0 {
1217
- write_new = Some(old & !zimm);
1218
- }
1219
- }
1220
- _ => {}
1221
- }
1222
-
1223
- if let Some(new_val) = write_new {
1224
- if let Err(e) = self.write_csr(csr_addr, new_val) {
1225
- return self.handle_trap(e, pc, Some(insn_raw));
1226
- }
1227
- }
1228
-
1229
- if rd != Register::X0 {
1230
- self.write_reg(rd, old);
1231
- }
1232
- }
1233
- _ => {
1234
- return self.handle_trap(
1235
- Trap::IllegalInstruction(insn_raw as u64),
1236
- pc,
1237
- Some(insn_raw),
1238
- );
1239
- }
1240
- }
1241
- }
1242
- Op::Fence => {
1243
- // NOP
1244
- }
1245
- }
1246
-
1247
- self.pc = next_pc;
1248
- Ok(())
1249
- }
1250
- }
1251
-
1252
- #[cfg(test)]
1253
- mod tests {
1254
- use super::*;
1255
- use crate::bus::SystemBus;
1256
-
1257
- // --- Test helpers ----------------------------------------------------
1258
-
1259
- fn encode_i(imm: i32, rs1: u32, funct3: u32, rd: u32, opcode: u32) -> u32 {
1260
- (((imm as u32) & 0xFFF) << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode
1261
- }
1262
-
1263
- fn encode_r(funct7: u32, rs2: u32, rs1: u32, funct3: u32, rd: u32, opcode: u32) -> u32 {
1264
- (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | opcode
1265
- }
1266
-
1267
- fn encode_s(imm: i32, rs2: u32, rs1: u32, funct3: u32, opcode: u32) -> u32 {
1268
- let imm = imm as u32;
1269
- let imm11_5 = (imm >> 5) & 0x7F;
1270
- let imm4_0 = imm & 0x1F;
1271
- (imm11_5 << 25)
1272
- | (rs2 << 20)
1273
- | (rs1 << 15)
1274
- | (funct3 << 12)
1275
- | (imm4_0 << 7)
1276
- | opcode
1277
- }
1278
-
1279
- fn encode_b(imm: i32, rs2: u32, rs1: u32, funct3: u32, opcode: u32) -> u32 {
1280
- // imm is a signed byte offset, must be multiple of 2
1281
- let imm = imm as u32;
1282
- let imm12 = (imm >> 12) & 0x1;
1283
- let imm10_5 = (imm >> 5) & 0x3F;
1284
- let imm4_1 = (imm >> 1) & 0xF;
1285
- let imm11 = (imm >> 11) & 0x1;
1286
-
1287
- (imm12 << 31)
1288
- | (imm10_5 << 25)
1289
- | (rs2 << 20)
1290
- | (rs1 << 15)
1291
- | (funct3 << 12)
1292
- | (imm4_1 << 8)
1293
- | (imm11 << 7)
1294
- | opcode
1295
- }
1296
-
1297
- fn make_bus() -> SystemBus {
1298
- SystemBus::new(0x8000_0000, 1024 * 1024) // 1MB
1299
- }
1300
-
1301
- fn encode_amo(
1302
- funct5: u32,
1303
- aq: bool,
1304
- rl: bool,
1305
- rs2: u32,
1306
- rs1: u32,
1307
- funct3: u32,
1308
- rd: u32,
1309
- ) -> u32 {
1310
- let funct7 = (funct5 << 2) | ((aq as u32) << 1) | (rl as u32);
1311
- encode_r(funct7, rs2, rs1, funct3, rd, 0x2F)
1312
- }
1313
-
1314
- #[test]
1315
- fn test_addi() {
1316
- let mut bus = make_bus();
1317
- let mut cpu = Cpu::new(0x8000_0000);
1318
-
1319
- // ADDI x1, x0, -1
1320
- let insn = encode_i(-1, 0, 0, 1, 0x13);
1321
- bus.write32(0x8000_0000, insn).unwrap();
1322
-
1323
- cpu.step(&mut bus).unwrap();
1324
- assert_eq!(cpu.read_reg(Register::X1), 0xFFFF_FFFF_FFFF_FFFF);
1325
- assert_eq!(cpu.pc, 0x8000_0004);
1326
- }
1327
-
1328
- #[test]
1329
- fn test_lui() {
1330
- let mut bus = make_bus();
1331
- let mut cpu = Cpu::new(0x8000_0000);
1332
-
1333
- // LUI x2, 0x12345
1334
- // imm field is already << 12 in the encoding helper
1335
- let imm = 0x12345 << 12;
1336
- let insn = ((imm as u32) & 0xFFFFF000) | (2 << 7) | 0x37;
1337
- bus.write32(0x8000_0000, insn).unwrap();
1338
-
1339
- cpu.step(&mut bus).unwrap();
1340
- assert_eq!(cpu.read_reg(Register::X2), 0x0000_0000_1234_5000);
1341
- }
1342
-
1343
- #[test]
1344
- fn test_load_store() {
1345
- let mut bus = make_bus();
1346
- let mut cpu = Cpu::new(0x8000_0000);
1347
-
1348
- // SD x1, 0(x2) -> Store x1 at x2+0
1349
- // x1 = 0xDEADBEEF, x2 = 0x8000_0100
1350
- cpu.write_reg(Register::X1, 0xDEADBEEF);
1351
- cpu.write_reg(Register::X2, 0x8000_0100);
1352
-
1353
- // SD: Op=0x23, funct3=3, rs1=2, rs2=1, imm=0
1354
- // Using manual encoding here: imm=0 so only rs2/rs1/funct3/opcode matter.
1355
- let sd_insn = (1 << 20) | (2 << 15) | (3 << 12) | 0x23;
1356
- bus.write32(0x8000_0000, sd_insn).unwrap();
1357
-
1358
- cpu.step(&mut bus).unwrap();
1359
- assert_eq!(bus.read64(0x8000_0100).unwrap(), 0xDEADBEEF);
1360
-
1361
- // LD x3, 0(x2) -> Load x3 from x2+0
1362
- // LD: Op=0x03, funct3=3, rd=3, rs1=2, imm=0
1363
- let ld_insn = (2 << 15) | (3 << 12) | (3 << 7) | 0x03;
1364
- bus.write32(0x8000_0004, ld_insn).unwrap();
1365
-
1366
- cpu.step(&mut bus).unwrap(); // Execute SD (pc was incremented in previous step? No wait)
1367
- // Previous step PC went 0->4. Now at 4.
1368
-
1369
- assert_eq!(cpu.read_reg(Register::X3), 0xDEADBEEF);
1370
- }
1371
-
1372
- #[test]
1373
- fn test_x0_invariant() {
1374
- let mut bus = make_bus();
1375
- let mut cpu = Cpu::new(0x8000_0000);
1376
-
1377
- // Place a value in memory
1378
- let addr = 0x8000_0100;
1379
- bus.write64(addr, 0xDEAD_BEEF_DEAD_BEEF).unwrap();
1380
-
1381
- // Set x2 = addr
1382
- cpu.write_reg(Register::X2, addr);
1383
-
1384
- // 1) ADDI x0, x0, 5
1385
- let addi_x0 = encode_i(5, 0, 0, 0, 0x13);
1386
- // 2) LD x0, 0(x2)
1387
- let ld_x0 = encode_i(0, 2, 3, 0, 0x03);
1388
-
1389
- bus.write32(0x8000_0000, addi_x0).unwrap();
1390
- bus.write32(0x8000_0004, ld_x0).unwrap();
1391
-
1392
- cpu.step(&mut bus).unwrap();
1393
- cpu.step(&mut bus).unwrap();
1394
-
1395
- // x0 must remain hard-wired to zero
1396
- assert_eq!(cpu.read_reg(Register::X0), 0);
1397
- }
1398
-
1399
- #[test]
1400
- fn test_branch_taken_and_not_taken() {
1401
- let mut bus = make_bus();
1402
- let mut cpu = Cpu::new(0x8000_0000);
1403
-
1404
- // BEQ x1, x2, +8 (pc + 8 when taken)
1405
- let beq_insn = encode_b(8, 2, 1, 0x0, 0x63);
1406
- bus.write32(0x8000_0000, beq_insn).unwrap();
1407
-
1408
- // Taken: x1 == x2
1409
- cpu.write_reg(Register::X1, 5);
1410
- cpu.write_reg(Register::X2, 5);
1411
- cpu.pc = 0x8000_0000;
1412
- cpu.step(&mut bus).unwrap();
1413
- assert_eq!(cpu.pc, 0x8000_0008);
1414
-
1415
- // Not taken: x1 != x2
1416
- cpu.write_reg(Register::X1, 1);
1417
- cpu.write_reg(Register::X2, 2);
1418
- cpu.pc = 0x8000_0000;
1419
- cpu.step(&mut bus).unwrap();
1420
- assert_eq!(cpu.pc, 0x8000_0004);
1421
- }
1422
-
1423
- #[test]
1424
- fn test_w_ops_sign_extension() {
1425
- let mut bus = make_bus();
1426
- let mut cpu = Cpu::new(0x8000_0000);
1427
-
1428
- // Set x1 = 0x0000_0000_8000_0000 (low 32 bits have sign bit set)
1429
- cpu.write_reg(Register::X1, 0x0000_0000_8000_0000);
1430
- cpu.write_reg(Register::X2, 0); // x2 = 0
1431
-
1432
- // ADDW x3, x1, x2 (opcode=0x3B, funct3=0, funct7=0)
1433
- let addw = encode_r(0x00, 2, 1, 0x0, 3, 0x3B);
1434
- bus.write32(0x8000_0000, addw).unwrap();
1435
-
1436
- cpu.step(&mut bus).unwrap();
1437
-
1438
- // Expect sign-extended 32-bit result: 0xFFFF_FFFF_8000_0000
1439
- assert_eq!(cpu.read_reg(Register::X3), 0xFFFF_FFFF_8000_0000);
1440
- }
1441
-
1442
- #[test]
1443
- fn test_m_extension_mul_div_rem() {
1444
- let mut bus = make_bus();
1445
- let mut cpu = Cpu::new(0x8000_0000);
1446
-
1447
- // MUL: 3 * 4 = 12
1448
- cpu.write_reg(Register::X1, 3);
1449
- cpu.write_reg(Register::X2, 4);
1450
- let mul = encode_r(0x01, 2, 1, 0x0, 3, 0x33); // MUL x3, x1, x2
1451
- bus.write32(0x8000_0000, mul).unwrap();
1452
- cpu.step(&mut bus).unwrap();
1453
- assert_eq!(cpu.read_reg(Register::X3), 12);
1454
-
1455
- // MULH / MULHSU / MULHU basic sanity using large values
1456
- cpu.pc = 0x8000_0004;
1457
- cpu.write_reg(Register::X1, 0x8000_0000_0000_0000);
1458
- cpu.write_reg(Register::X2, 2);
1459
- let mulh = encode_r(0x01, 2, 1, 0x1, 4, 0x33); // MULH x4, x1, x2
1460
- let mulhsu = encode_r(0x01, 2, 1, 0x2, 5, 0x33); // MULHSU x5, x1, x2
1461
- let mulhu = encode_r(0x01, 2, 1, 0x3, 6, 0x33); // MULHU x6, x1, x2
1462
- bus.write32(0x8000_0004, mulh).unwrap();
1463
- bus.write32(0x8000_0008, mulhsu).unwrap();
1464
- bus.write32(0x8000_000C, mulhu).unwrap();
1465
- cpu.step(&mut bus).unwrap();
1466
- cpu.step(&mut bus).unwrap();
1467
- cpu.step(&mut bus).unwrap();
1468
-
1469
- // From spec example: low product is 0, high signed part negative
1470
- assert_eq!(cpu.read_reg(Register::X3), 12);
1471
- assert_ne!(cpu.read_reg(Register::X4), 0);
1472
- assert_ne!(cpu.read_reg(Register::X5), 0);
1473
- assert_ne!(cpu.read_reg(Register::X6), 0);
1474
-
1475
- // DIV / DIVU / REM / REMU corner cases
1476
- cpu.pc = 0x8000_0010;
1477
- cpu.write_reg(Register::X1, u64::MAX); // -1 as signed
1478
- cpu.write_reg(Register::X2, 0);
1479
- let div = encode_r(0x01, 2, 1, 0x4, 7, 0x33); // DIV x7, x1, x2
1480
- let divu = encode_r(0x01, 2, 1, 0x5, 8, 0x33); // DIVU x8, x1, x2
1481
- let rem = encode_r(0x01, 2, 1, 0x6, 9, 0x33); // REM x9, x1, x2
1482
- let remu = encode_r(0x01, 2, 1, 0x7, 10, 0x33); // REMU x10, x1, x2
1483
- bus.write32(0x8000_0010, div).unwrap();
1484
- bus.write32(0x8000_0014, divu).unwrap();
1485
- bus.write32(0x8000_0018, rem).unwrap();
1486
- bus.write32(0x8000_001C, remu).unwrap();
1487
-
1488
- for _ in 0..4 {
1489
- cpu.step(&mut bus).unwrap();
1490
- }
1491
-
1492
- assert_eq!(cpu.read_reg(Register::X7), u64::MAX); // DIV by 0 -> -1
1493
- assert_eq!(cpu.read_reg(Register::X8), u64::MAX); // DIVU by 0 -> all ones
1494
- assert_eq!(cpu.read_reg(Register::X9), u64::MAX); // REM by 0 -> rs1
1495
- assert_eq!(cpu.read_reg(Register::X10), u64::MAX); // REMU by 0 -> rs1
1496
-
1497
- // Overflow case: -(2^63) / -1 -> -(2^63), rem = 0
1498
- cpu.pc = 0x8000_0020;
1499
- cpu.write_reg(Register::X1, i64::MIN as u64);
1500
- cpu.write_reg(Register::X2, (!0u64) as u64); // -1
1501
- let div_over = encode_r(0x01, 2, 1, 0x4, 11, 0x33); // DIV x11, x1, x2
1502
- let rem_over = encode_r(0x01, 2, 1, 0x6, 12, 0x33); // REM x12, x1, x2
1503
- bus.write32(0x8000_0020, div_over).unwrap();
1504
- bus.write32(0x8000_0024, rem_over).unwrap();
1505
- cpu.step(&mut bus).unwrap();
1506
- cpu.step(&mut bus).unwrap();
1507
-
1508
- assert_eq!(cpu.read_reg(Register::X11), i64::MIN as u64);
1509
- assert_eq!(cpu.read_reg(Register::X12), 0);
1510
- }
1511
-
1512
- #[test]
1513
- fn test_compressed_addi_and_lwsp_paths() {
1514
- let mut bus = make_bus();
1515
- let mut cpu = Cpu::new(0x8000_0000);
1516
-
1517
- // Encodings from assembler with rv64imac (see rvc_test.S in dev notes):
1518
- let c_addi_x11_1: u16 = 0x0585; // addi x11,x11,1 (C.ADDI)
1519
- let c_addi16sp_16: u16 = 0x0141; // addi sp,sp,16 (C.ADDI16SP)
1520
- let c_lwsp_a5_12: u16 = 0x47B2; // lw a5,12(sp) (C.LWSP)
1521
-
1522
- // Initialize registers / memory
1523
- cpu.write_reg(Register::X11, 10);
1524
- let sp_base = 0x8000_0100;
1525
- cpu.write_reg(Register::X2, sp_base); // sp
1526
- // After C.ADDI16SP 16, sp = sp_base + 16. C.LWSP uses offset 12 from new sp.
1527
- bus.write32(sp_base + 16 + 12, 0xDEAD_BEEF).unwrap();
1528
-
1529
- // Place compressed instructions at 0,2,4
1530
- bus.write16(0x8000_0000, c_addi_x11_1).unwrap();
1531
- bus.write16(0x8000_0002, c_addi16sp_16).unwrap();
1532
- bus.write16(0x8000_0004, c_lwsp_a5_12).unwrap();
1533
-
1534
- // Execute three steps; PC should advance by 2 for each compressed inst.
1535
- cpu.step(&mut bus).unwrap();
1536
- assert_eq!(cpu.pc, 0x8000_0002);
1537
- assert_eq!(cpu.read_reg(Register::X11), 11);
1538
-
1539
- cpu.step(&mut bus).unwrap();
1540
- assert_eq!(cpu.pc, 0x8000_0004);
1541
- assert_eq!(cpu.read_reg(Register::X2), 0x8000_0110); // sp + 16
1542
-
1543
- cpu.step(&mut bus).unwrap();
1544
- assert_eq!(cpu.pc, 0x8000_0006);
1545
- assert_eq!(cpu.read_reg(Register::X15), 0xFFFF_FFFF_DEAD_BEEF); // a5 (sign-extended lw)
1546
- }
1547
-
1548
- #[test]
1549
- fn test_zicsr_basic_csrs() {
1550
- let mut bus = make_bus();
1551
- let mut cpu = Cpu::new(0x8000_0000);
1552
- let csr_addr: u32 = 0x300; // mstatus
1553
-
1554
- // CSRRWI x1, mstatus, 5 (mstatus = 5, x1 = old = 0)
1555
- let csrrwi = {
1556
- let zimm = 5u32;
1557
- (csr_addr << 20) | (zimm << 15) | (0x5 << 12) | (1 << 7) | 0x73
1558
- };
1559
- bus.write32(0x8000_0000, csrrwi).unwrap();
1560
- cpu.step(&mut bus).unwrap();
1561
- assert_eq!(cpu.read_reg(Register::X1), 0);
1562
-
1563
- // CSRRSI x2, mstatus, 0xA (mstatus = 5 | 0xA = 0xF, x2 = old = 5)
1564
- let csrrsi = {
1565
- let zimm = 0xAu32;
1566
- (csr_addr << 20) | (zimm << 15) | (0x6 << 12) | (2 << 7) | 0x73
1567
- };
1568
- bus.write32(0x8000_0004, csrrsi).unwrap();
1569
- cpu.step(&mut bus).unwrap();
1570
- assert_eq!(cpu.read_reg(Register::X2), 5);
1571
-
1572
- // CSRRCI x3, mstatus, 0x3 (mstatus = 0xF & !0x3 = 0xC, x3 = old = 0xF)
1573
- let csrrci = {
1574
- let zimm = 0x3u32;
1575
- (csr_addr << 20) | (zimm << 15) | (0x7 << 12) | (3 << 7) | 0x73
1576
- };
1577
- bus.write32(0x8000_0008, csrrci).unwrap();
1578
- cpu.step(&mut bus).unwrap();
1579
- assert_eq!(cpu.read_reg(Register::X3), 0xF);
1580
- }
1581
-
1582
- #[test]
1583
- fn test_a_extension_lr_sc_basic() {
1584
- let mut bus = make_bus();
1585
- let mut cpu = Cpu::new(0x8000_0000);
1586
-
1587
- let addr = 0x8000_0200;
1588
- bus.write64(addr, 0xDEAD_BEEF_DEAD_BEEF).unwrap();
1589
-
1590
- cpu.write_reg(Register::X1, addr); // base
1591
- cpu.write_reg(Register::X2, 0x0123_4567_89AB_CDEF); // value to store with SC
1592
-
1593
- // LR.D x3, 0(x1)
1594
- let lr_d = encode_amo(0b00010, false, false, 0, 1, 0x3, 3);
1595
- // SC.D x4, x2, 0(x1)
1596
- let sc_d = encode_amo(0b00011, false, false, 2, 1, 0x3, 4);
1597
-
1598
- bus.write32(0x8000_0000, lr_d).unwrap();
1599
- bus.write32(0x8000_0004, sc_d).unwrap();
1600
-
1601
- cpu.step(&mut bus).unwrap();
1602
- assert_eq!(cpu.read_reg(Register::X3), 0xDEAD_BEEF_DEAD_BEEF);
1603
-
1604
- cpu.step(&mut bus).unwrap();
1605
- assert_eq!(cpu.read_reg(Register::X4), 0); // SC success
1606
- assert_eq!(bus.read64(addr).unwrap(), 0x0123_4567_89AB_CDEF);
1607
- }
1608
-
1609
- #[test]
1610
- fn test_a_extension_reservation_and_misaligned_sc() {
1611
- let mut bus = make_bus();
1612
- let mut cpu = Cpu::new(0x8000_0000);
1613
-
1614
- let addr = 0x8000_0300;
1615
- bus.write64(addr, 0xCAFEBABE_F00D_F00D).unwrap();
1616
-
1617
- cpu.write_reg(Register::X1, addr); // base
1618
- cpu.write_reg(Register::X2, 1); // increment
1619
-
1620
- // LR.D to establish reservation
1621
- let lr_d = encode_amo(0b00010, false, false, 0, 1, 0x3, 3);
1622
- // AMOADD.D x4, x2, 0(x1) -> increments and clears reservation
1623
- let amoadd_d = encode_amo(0b00000, false, false, 2, 1, 0x3, 4);
1624
- // SC.D x5, x2, 0(x1) -> should fail (x5=1) because reservation cleared
1625
- let sc_d = encode_amo(0b00011, false, false, 2, 1, 0x3, 5);
1626
-
1627
- bus.write32(0x8000_0000, lr_d).unwrap();
1628
- bus.write32(0x8000_0004, amoadd_d).unwrap();
1629
- bus.write32(0x8000_0008, sc_d).unwrap();
1630
-
1631
- cpu.step(&mut bus).unwrap(); // LR
1632
- cpu.step(&mut bus).unwrap(); // AMOADD
1633
- cpu.step(&mut bus).unwrap(); // SC (should fail)
1634
-
1635
- assert_eq!(cpu.read_reg(Register::X5), 1);
1636
-
1637
- // Misaligned SC.D must trap with StoreAddressMisaligned
1638
- cpu.pc = 0x8000_0010;
1639
- cpu.write_reg(Register::X1, addr + 1); // misaligned
1640
- let sc_misaligned = encode_amo(0b00011, false, false, 2, 1, 0x3, 6);
1641
- bus.write32(0x8000_0010, sc_misaligned).unwrap();
1642
-
1643
- let res = cpu.step(&mut bus);
1644
- match res {
1645
- Err(Trap::StoreAddressMisaligned(a)) => assert_eq!(a, addr + 1),
1646
- _ => panic!("Expected StoreAddressMisaligned trap"),
1647
- }
1648
- }
1649
-
1650
- #[test]
1651
- fn test_load_sign_and_zero_extension() {
1652
- let mut bus = make_bus();
1653
- let mut cpu = Cpu::new(0x8000_0000);
1654
-
1655
- let addr = 0x8000_0100;
1656
- // 0xFFEE_DDCC_BBAA_9988 laid out little-endian in memory
1657
- bus.write64(addr, 0xFFEE_DDCC_BBAA_9988).unwrap();
1658
-
1659
- cpu.write_reg(Register::X1, addr); // base pointer
1660
-
1661
- // LB x2, 0(x1)
1662
- let lb = encode_i(0, 1, 0, 2, 0x03);
1663
- // LBU x3, 0(x1)
1664
- let lbu = encode_i(0, 1, 4, 3, 0x03);
1665
- // LH x4, 0(x1)
1666
- let lh = encode_i(0, 1, 1, 4, 0x03);
1667
- // LHU x5, 0(x1)
1668
- let lhu = encode_i(0, 1, 5, 5, 0x03);
1669
- // LW x6, 0(x1)
1670
- let lw = encode_i(0, 1, 2, 6, 0x03);
1671
- // LWU x7, 0(x1)
1672
- let lwu = encode_i(0, 1, 6, 7, 0x03);
1673
- // LD x8, 0(x1)
1674
- let ld = encode_i(0, 1, 3, 8, 0x03);
1675
-
1676
- let base_pc = 0x8000_0000;
1677
- for (i, insn) in [lb, lbu, lh, lhu, lw, lwu, ld].into_iter().enumerate() {
1678
- bus.write32(base_pc + (i as u64) * 4, insn).unwrap();
1679
- }
1680
-
1681
- // Execute all loads
1682
- for _ in 0..7 {
1683
- cpu.step(&mut bus).unwrap();
1684
- }
1685
-
1686
- assert_eq!(cpu.read_reg(Register::X2), 0xFFFF_FFFF_FFFF_FF88); // LB (sign-extended 0x88)
1687
- assert_eq!(cpu.read_reg(Register::X3), 0x88); // LBU
1688
- assert_eq!(cpu.read_reg(Register::X4), 0xFFFF_FFFF_FFFF_9988); // LH
1689
- assert_eq!(cpu.read_reg(Register::X5), 0x9988); // LHU
1690
- assert_eq!(cpu.read_reg(Register::X6), 0xFFFF_FFFF_BBAA_9988); // LW
1691
- assert_eq!(cpu.read_reg(Register::X7), 0xBBAA_9988); // LWU
1692
- assert_eq!(cpu.read_reg(Register::X8), 0xFFEE_DDCC_BBAA_9988); // LD
1693
- }
1694
-
1695
- #[test]
1696
- fn test_misaligned_load_and_store_traps() {
1697
- let mut bus = make_bus();
1698
- let mut cpu = Cpu::new(0x8000_0000);
1699
-
1700
- // x2 = misaligned address
1701
- cpu.write_reg(Register::X2, 0x8000_0001);
1702
-
1703
- // LW x1, 0(x2) -> should trap with LoadAddressMisaligned
1704
- let lw = encode_i(0, 2, 2, 1, 0x03);
1705
- bus.write32(0x8000_0000, lw).unwrap();
1706
-
1707
- let res = cpu.step(&mut bus);
1708
- match res {
1709
- Err(Trap::LoadAddressMisaligned(a)) => assert_eq!(a, 0x8000_0001),
1710
- _ => panic!("Expected LoadAddressMisaligned trap"),
1711
- }
1712
-
1713
- // SW x1, 0(x2) -> should trap with StoreAddressMisaligned
1714
- cpu.pc = 0x8000_0000;
1715
- let sw = encode_s(0, 1, 2, 2, 0x23);
1716
- bus.write32(0x8000_0000, sw).unwrap();
1717
-
1718
- let res = cpu.step(&mut bus);
1719
- match res {
1720
- Err(Trap::StoreAddressMisaligned(a)) => assert_eq!(a, 0x8000_0001),
1721
- _ => panic!("Expected StoreAddressMisaligned trap"),
1722
- }
1723
- }
1724
-
1725
- #[test]
1726
- fn test_access_fault_outside_dram() {
1727
- let mut bus = make_bus();
1728
- let mut cpu = Cpu::new(0x8000_0000);
1729
-
1730
- // LW x1, 0(x0) -> effective address 0x0 (outside DRAM, but aligned)
1731
- let lw = encode_i(0, 0, 2, 1, 0x03);
1732
- bus.write32(0x8000_0000, lw).unwrap();
1733
-
1734
- let res = cpu.step(&mut bus);
1735
- match res {
1736
- Err(Trap::LoadAccessFault(a)) => assert_eq!(a, 0),
1737
- _ => panic!("Expected LoadAccessFault trap"),
1738
- }
1739
- }
1740
-
1741
- #[test]
1742
- fn test_jal() {
1743
- let mut bus = make_bus();
1744
- let mut cpu = Cpu::new(0x8000_0000);
1745
-
1746
- // JAL x1, 8
1747
- // Op=0x6F, rd=1, imm=8.
1748
- // J-type: imm[20|10:1|11|19:12]
1749
- // imm=8 (0x8). bit3=1.
1750
- // imm[10:1] = 0100... no wait.
1751
- // 8 = 1000 binary.
1752
- // bit1..10 -> bits 1..4 are 0010 ? No.
1753
- // 8 >> 1 = 4.
1754
- // imm[10:1] = 4.
1755
- // insn: imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode
1756
- // 0 | 4 | 0 | 0 | 1 | 0x6F
1757
- // (4 << 21) | (1 << 7) | 0x6F
1758
- let jal_insn = (4 << 21) | (1 << 7) | 0x6F;
1759
- bus.write32(0x8000_0000, jal_insn).unwrap();
1760
-
1761
- cpu.step(&mut bus).unwrap();
1762
- assert_eq!(cpu.read_reg(Register::X1), 0x8000_0004); // Link address
1763
- assert_eq!(cpu.pc, 0x8000_0008); // Target
1764
- }
1765
-
1766
- #[test]
1767
- fn test_misaligned_fetch() {
1768
- let mut bus = make_bus();
1769
- let mut cpu = Cpu::new(0x8000_0001); // Odd PC
1770
-
1771
- let res = cpu.step(&mut bus);
1772
- match res {
1773
- Err(Trap::InstructionAddressMisaligned(addr)) => assert_eq!(addr, 0x8000_0001),
1774
- _ => panic!("Expected misaligned trap"),
1775
- }
1776
- }
1777
-
1778
- #[test]
1779
- fn test_smoke_sum() {
1780
- let mut bus = make_bus();
1781
- let mut cpu = Cpu::new(0x8000_0000);
1782
-
1783
- // Data at 0x8000_0100
1784
- let data: [u32; 5] = [1, 2, 3, 4, 5];
1785
- for (i, val) in data.iter().enumerate() {
1786
- bus.write32(0x8000_0100 + (i * 4) as u64, *val).unwrap();
1787
- }
1788
-
1789
- // Program
1790
- // Need to construct 0x80000100 without sign extension issues.
1791
- // 1. ADDI x1, x0, 1
1792
- // 2. SLLI x1, x1, 31 -> 0x80000000
1793
- // 3. ADDI x1, x1, 0x100 -> 0x80000100
1794
- let prog = [
1795
- 0x00100093, // addi x1, x0, 1
1796
- 0x01F09093, // slli x1, x1, 31
1797
- 0x10008093, // addi x1, x1, 0x100 -> Base
1798
- 0x00500113, // addi x2, x0, 5 -> Count
1799
- 0x00000193, // addi x3, x0, 0 -> Sum
1800
- // loop:
1801
- 0x0000A203, // lw x4, 0(x1)
1802
- 0x004181B3, // add x3, x3, x4
1803
- 0x00408093, // addi x1, x1, 4
1804
- 0xFFF10113, // addi x2, x2, -1
1805
- 0xFE0118E3, // bne x2, x0, loop (-16)
1806
- 0x00100073, // ebreak
1807
- ];
1808
-
1809
- for (i, val) in prog.iter().enumerate() {
1810
- bus.write32(0x8000_0000 + (i * 4) as u64, *val).unwrap();
1811
- }
1812
-
1813
- // Run until ebreak
1814
- let mut steps = 0;
1815
- loop {
1816
- steps += 1;
1817
- if steps > 1000 {
1818
- panic!("Infinite loop");
1819
- }
1820
- match cpu.step(&mut bus) {
1821
- Ok(_) => {}
1822
- Err(Trap::Breakpoint) => break,
1823
- Err(e) => panic!("Unexpected trap at pc 0x{:x}: {:?}", cpu.pc, e),
1824
- }
1825
- }
1826
-
1827
- // Check sum
1828
- assert_eq!(cpu.read_reg(Register::X3), 15);
1829
- }
1830
-
1831
- #[test]
1832
- fn test_interrupts_clint_plic() {
1833
- let mut bus = make_bus();
1834
- let mut cpu = Cpu::new(0x8000_0000);
1835
-
1836
- // 1. Setup MTVEC to 0x8000_1000 (Direct)
1837
- let mtvec_val = 0x8000_1000;
1838
- cpu.write_csr(CSR_MTVEC, mtvec_val).unwrap();
1839
-
1840
- // 2. Enable MIE in mstatus (Global Interrupt Enable)
1841
- // mstatus bit 3 is MIE.
1842
- let mstatus_val = 1 << 3;
1843
- cpu.write_csr(CSR_MSTATUS, mstatus_val).unwrap();
1844
-
1845
- // 3. Enable MTIE (Timer) and MEIE (External) and MSIE (Software) in mie
1846
- // MTIE=7, MEIE=11, MSIE=3
1847
- let mie_val = (1 << 7) | (1 << 11) | (1 << 3);
1848
- cpu.write_csr(CSR_MIE, mie_val).unwrap();
1849
-
1850
- // --- Test CLINT Timer Interrupt ---
1851
- // Set mtimecmp[0] to 100
1852
- bus.clint.mtimecmp[0] = 100;
1853
- // Set mtime to 101 (trigger condition)
1854
- bus.clint.set_mtime(101);
1855
-
1856
- // We need a valid instruction at PC to attempt fetch, although interrupt checks before fetch.
1857
- bus.write32(0x8000_0000, 0x00000013).unwrap(); // NOP (addi x0, x0, 0)
1858
-
1859
- let res = cpu.step(&mut bus);
1860
- match res {
1861
- Err(Trap::MachineTimerInterrupt) => {
1862
- // Success
1863
- assert_eq!(cpu.pc, 0x8000_1000); // jumped to mtvec
1864
- // Check mcause: Interrupt=1, Cause=7 -> 0x8000...0007
1865
- let mcause = cpu.read_csr(CSR_MCAUSE).unwrap();
1866
- assert_eq!(mcause, 0x8000_0000_0000_0007);
1867
- }
1868
- _ => panic!("Expected MachineTimerInterrupt, got {:?}", res),
1869
- }
1870
-
1871
- // Clear the interrupt condition
1872
- bus.clint.mtimecmp[0] = 200;
1873
- // CPU is now at handler. We need to "return" (mret) or just reset state for next test.
1874
- // Reset PC back to start
1875
- cpu.pc = 0x8000_0000;
1876
- // Re-enable MIE (trap disabled it)
1877
- let mut mstatus = cpu.read_csr(CSR_MSTATUS).unwrap();
1878
- mstatus |= 1 << 3;
1879
- cpu.write_csr(CSR_MSTATUS, mstatus).unwrap();
1880
-
1881
- // --- Test PLIC UART Interrupt ---
1882
- // Configure PLIC
1883
- // 1. Set Priority for Source 10 (UART) to 1
1884
- bus.plic.store(0x000000 + 4 * 10, 4, 1).unwrap();
1885
- // 2. Enable Source 10 for Context 0 (M-mode)
1886
- // Enable addr for ctx 0: 0x002000. Bit 10.
1887
- bus.plic.store(0x002000, 4, 1 << 10).unwrap();
1888
- // 3. Set Threshold for Context 0 to 0
1889
- bus.plic.store(0x200000, 4, 0).unwrap();
1890
-
1891
- // Trigger UART Interrupt
1892
- // Writing to IER (Enable RDIE=1) and pushing a char to Input
1893
- // UART RBR is at offset 0. IER is at offset 1.
1894
- bus.uart.store(1, 1, 1).unwrap(); // IER = 1 (RX Data Available Interrupt)
1895
- bus.uart.push_input(b'A');
1896
- // This should set uart.lsr[0]=1, and because IER[0]=1, uart.interrupting=true.
1897
-
1898
- // Update bus interrupts so PLIC sees UART line high
1899
- bus.check_interrupts();
1900
-
1901
- // Step CPU
1902
- let res = cpu.step(&mut bus);
1903
- match res {
1904
- Err(Trap::MachineExternalInterrupt) => {
1905
- // Success
1906
- assert_eq!(cpu.pc, 0x8000_1000);
1907
- let mcause = cpu.read_csr(CSR_MCAUSE).unwrap();
1908
- assert_eq!(mcause, 0x8000_0000_0000_000B); // Cause 11
1909
- }
1910
- _ => panic!("Expected MachineExternalInterrupt, got {:?}", res),
1911
- }
1912
- }
1913
- }