virtual-machine 0.0.4 → 0.0.10
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/build/{chunk-V6I6APCK.mjs → chunk-MELUIZWO.mjs} +3 -3
- package/build/cli.js +591 -610
- package/build/index.d.ts +2 -2
- package/build/index.js +5 -5
- package/build/index.mjs +4 -4
- package/build/{riscv_vm-QIJGD3E2.mjs → riscv_vm-JTPPWIT3.mjs} +1 -1
- package/package.json +12 -5
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +0 -1
- package/Cargo.toml +0 -49
- package/build.sh +0 -25
- package/cli.ts +0 -268
- package/index.ts +0 -16
- package/src/bus.rs +0 -558
- package/src/clint.rs +0 -132
- package/src/console.rs +0 -83
- package/src/cpu.rs +0 -1913
- package/src/csr.rs +0 -67
- package/src/decoder.rs +0 -789
- package/src/dram.rs +0 -146
- package/src/emulator.rs +0 -603
- package/src/lib.rs +0 -249
- package/src/main.rs +0 -449
- package/src/mmu.rs +0 -331
- package/src/net.rs +0 -121
- package/src/net_webtransport.rs +0 -446
- package/src/plic.rs +0 -261
- package/src/uart.rs +0 -231
- package/src/virtio.rs +0 -1074
- package/tsconfig.json +0 -19
- package/tsup/index.ts +0 -79
- package/tsup/tsup.cli.ts +0 -8
- package/tsup/tsup.core.cjs.ts +0 -7
- package/tsup/tsup.core.esm.ts +0 -8
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
|
-
}
|