softcore_rv64/
lib.rs

1//! Softcore RISC-V 64
2//!
3//! This library is a wrapper around a Rust translation of the official [RISC-V executable
4//! specification][1]. The software core can be used to test the behavior of the hardware, for
5//! instance to check if a memory access is allowed, or the register state after taking a trap.
6//! This is especially helpful to test or verify low-level software, such as kernels, hypervisors,
7//! or firmware.
8//!
9//! The raw translation is exposed in the [raw] module. A more polished (and slightly more stable)
10//! interface is exposed through the [Core] methods.
11//!
12//! [1]: https://github.com/riscv/sail-riscv
13
14mod arch_prelude;
15pub mod config;
16pub mod registers;
17
18/// The raw translation of the official RISC-V executable specification.
19///
20/// The [RISC-V executable specification][1] is written in [Sail][2], a domain specific language to
21/// specify Instruction Set Architectures (ISA). The translation is automated using a custom
22/// Sail-to-Rust back-end for the Sail compiler.
23///
24/// [1]: https://github.com/riscv/sail-riscv
25/// [2]: https://github.com/rems-project/sail
26#[rustfmt::skip]
27pub mod raw;
28
29pub use raw::{Core, ExceptionType, ExecutionResult, Privilege, ast};
30use raw::{cregidx, regidx};
31use registers::GeneralRegister;
32use registers::*;
33pub use softcore_prelude as prelude;
34use softcore_prelude::{BitVector, bv};
35
36// ———————————————————————— Initialization Constants ———————————————————————— //
37
38const DEFAULT_PMP_CFG: raw::Pmpcfg_ent = raw::Pmpcfg_ent { bits: bv(0) };
39const DEFAULT_HPM_EVENT: raw::HpmEvent = raw::HpmEvent { bits: bv(0) };
40const DEFAULT_TLB_ENTRY: Option<raw::TLB_Entry> = None;
41const ZEROES: BitVector<64> = bv(0);
42
43// ————————————————————————————— Trap Handling —————————————————————————————— //
44
45/// Wether or not a trap occured after executing an instruction.
46#[derive(Debug, Clone, Copy)]
47pub enum Trap {
48    /// The instruction trapped, contains the address of the trap handler.
49    Some(u64),
50    /// The instruction did not trap.
51    None,
52}
53
54impl Trap {
55    pub fn trapped(self) -> bool {
56        match self {
57            Trap::Some(_) => true,
58            Trap::None => false,
59        }
60    }
61}
62
63// —————————————————————————— Core implementation ——————————————————————————— //
64
65impl Core {
66    /// Reset the core, initializing registers with specified reset values.
67    ///
68    /// This does not reset all registers and CSRs of the core, it only performs the minimal reset
69    /// required by the specification.
70    ///
71    /// This function should be called on a fresh core to ensure the core starts in a sensible
72    /// state.
73    pub fn reset(&mut self) {
74        raw::_reset_all_registers(self);
75        raw::reset_sys(self, ());
76    }
77
78    /// Execute one instruction
79    pub fn execute(&mut self, instr: ast) -> Trap {
80        let exec_res = raw::execute(self, instr);
81        self.process_execution_result(exec_res, instr)
82    }
83
84    /// Handle the necessary steps post instruction execution, such as jumping to the trap handler
85    /// if necessary.
86    fn process_execution_result(&mut self, exec_res: ExecutionResult, instr: ast) -> Trap {
87        match exec_res {
88            ExecutionResult::Retire_Success(_) => Trap::None,
89            ExecutionResult::Wait_For_Interrupt(_) => Trap::None,
90            ExecutionResult::Illegal_Instruction(_) => {
91                let instr_bits = raw::encdec_forwards(self, instr);
92                raw::handle_illegal(self, instr_bits);
93                Trap::Some(self.nextPC.bits())
94            }
95            ExecutionResult::Trap((privilege, ctl, pc)) => {
96                let pc = raw::exception_handler(self, privilege, ctl, pc);
97                raw::set_next_pc(self, pc);
98                Trap::Some(self.nextPC.bits())
99            }
100            ExecutionResult::Memory_Exception(_) => todo!("handle Memory_Exception"),
101            ExecutionResult::Ext_CSR_Check_Failure(_) => todo!("handle Ext_CSR_Check_Failure"),
102            ExecutionResult::Ext_ControlAddr_Check_Failure(_) => {
103                todo!("handle Ext_ControlAddr_Check_Failure")
104            }
105            ExecutionResult::Ext_DataAddr_Check_Failure(_) => {
106                todo!("handle Ext_DataAddr_Check_Failure")
107            }
108            ExecutionResult::Ext_XRET_Priv_Failure(_) => todo!("handle Ext_XRET_Priv_Failure"),
109        }
110    }
111
112    /// Get the value of a general purpose register.
113    pub fn get(&mut self, reg: GeneralRegister) -> u64 {
114        let reg = match reg {
115            raw::regidx::Regidx(reg) => reg.bits() as i128,
116        };
117        raw::rX(self, raw::regno::Regno(reg)).bits()
118    }
119
120    /// Set the value of a general purpose register.
121    pub fn set(&mut self, reg: GeneralRegister, value: u64) {
122        let reg = match reg {
123            raw::regidx::Regidx(reg) => reg.bits() as i128,
124        };
125        raw::wX(self, raw::regno::Regno(reg), bv(value));
126    }
127
128    /// Get the value of a CSR identified by its CSR index.
129    ///
130    /// This function returns [None] if the CSR can not be read by the current privilege level or
131    /// is not implemented given the core configuration.
132    pub fn get_csr(&mut self, csr: u64) -> Option<u64> {
133        let csr = bv(csr);
134        if raw::check_CSR(self, csr, self.cur_privilege, false) {
135            Some(raw::read_CSR(self, csr).bits())
136        } else {
137            None
138        }
139    }
140
141    /// Set the value of a CSR identified by its CSR index.
142    ///
143    /// This function returns [None] if the CSR can not be written by the current privilege level
144    /// or is not implemented given the core configuration. Otherwise the new CSR value is
145    /// returned.
146    pub fn set_csr(&mut self, csr: u64, value: u64) -> Option<u64> {
147        let csr = bv(csr);
148        if raw::check_CSR(self, csr, self.cur_privilege, true) {
149            Some(raw::write_CSR(self, csr, bv(value)).bits())
150        } else {
151            None
152        }
153    }
154
155    /// Atomic Read and Write CSR
156    ///
157    /// This function has the same effect as executing the `CSRRW` instruction, except for moving
158    /// the PC on success and trapping on failure.
159    pub fn csrrw(
160        &mut self,
161        rd: GeneralRegister,
162        csr: u64,
163        rs1: GeneralRegister,
164    ) -> Result<(), raw::ExecutionResult> {
165        let val = self.get(rs1);
166        self.do_csr(val, csr, rd, raw::csrop::CSRRW, true)
167    }
168
169    /// Atomic Read and Set bits CSR
170    ///
171    /// This function has the same effect as executing the `CSRRS` instruction, except for moving
172    /// the PC on success and trapping on failure.
173    pub fn csrrs(
174        &mut self,
175        rd: GeneralRegister,
176        csr: u64,
177        rs1: GeneralRegister,
178    ) -> Result<(), raw::ExecutionResult> {
179        let val = self.get(rs1);
180        self.do_csr(val, csr, rd, raw::csrop::CSRRS, rs1 != X0)
181    }
182
183    /// Atomic Read and Clear bits CSR
184    ///
185    /// This function has the same effect as executing the `CSRRc` instruction, except for moving
186    /// the PC on success and trapping on failure.
187    pub fn csrrc(
188        &mut self,
189        rd: GeneralRegister,
190        csr: u64,
191        rs1: GeneralRegister,
192    ) -> Result<(), raw::ExecutionResult> {
193        let val = self.get(rs1);
194        self.do_csr(val, csr, rd, raw::csrop::CSRRC, rs1 != X0)
195    }
196
197    /// Atomic Read and Write immediate CSR
198    ///
199    /// This function has the same effect as executing the `CSRRWI` instruction, except for moving
200    /// the PC on success and trapping on failure.
201    pub fn csrrwi(
202        &mut self,
203        rd: GeneralRegister,
204        csr: u64,
205        uimm: u64,
206    ) -> Result<(), raw::ExecutionResult> {
207        let uimm = uimm & 0b11111; // The immediate is only 5 bits wide
208        self.do_csr(uimm, csr, rd, raw::csrop::CSRRW, true)
209    }
210
211    /// Atomic Read and Set bits immediate CSR
212    ///
213    /// This function has the same effect as executing the `CSRRSI` instruction, except for moving
214    /// the PC on success and trapping on failure.
215    pub fn csrrsi(
216        &mut self,
217        rd: GeneralRegister,
218        csr: u64,
219        uimm: u64,
220    ) -> Result<(), raw::ExecutionResult> {
221        let uimm = uimm & 0b11111; // The immediate is only 5 bits wide
222        self.do_csr(uimm, csr, rd, raw::csrop::CSRRS, uimm != 0)
223    }
224
225    /// Atomic Read and Clear bits immediate CSR
226    ///
227    /// This function has the same effect as executing the `CSRRCI` instruction, except for moving
228    /// the PC on success and trapping on failure.
229    pub fn csrrci(
230        &mut self,
231        rd: GeneralRegister,
232        csr: u64,
233        uimm: u64,
234    ) -> Result<(), raw::ExecutionResult> {
235        let uimm = uimm & 0b11111; // The immediate is only 5 bits wide
236        self.do_csr(uimm, csr, rd, raw::csrop::CSRRC, uimm != 0)
237    }
238
239    /// Private helper functions to call the raw doCSR.
240    ///
241    /// Refer to the sail definition of `execute CSRReg` for how to use this function.
242    fn do_csr(
243        &mut self,
244        val: u64,
245        csr: u64,
246        rd: GeneralRegister,
247        op: raw::csrop,
248        is_write: bool,
249    ) -> Result<(), raw::ExecutionResult> {
250        let csr = bv(csr);
251        let val = bv(val);
252        let res = raw::doCSR(self, csr, val, rd, op, is_write);
253        match res {
254            raw::ExecutionResult::Retire_Success(()) => Ok(()),
255            _ => Err(res),
256        }
257    }
258
259    /// Return the current privilege mode.
260    pub fn mode(&self) -> Privilege {
261        self.cur_privilege
262    }
263
264    /// Set the privilege mode
265    pub fn set_mode(&mut self, mode: Privilege) {
266        self.cur_privilege = mode
267    }
268
269    /// Decode an instruction
270    pub fn decode_instr(&mut self, instr: u32) -> ast {
271        raw::encdec_backwards(self, bv(instr as u64))
272    }
273
274    /// Encode and instruction
275    pub fn encode_instr(&mut self, instr: ast) -> u32 {
276        raw::encdec_forwards(self, instr).bits() as u32
277    }
278
279    /// Return true if the CSR is defined (and enabled) on the core
280    pub fn is_csr_defined(&mut self, csr_id: usize) -> bool {
281        raw::is_CSR_defined(self, bv(csr_id as u64))
282    }
283
284    /// Dispatch pending interrupt
285    ///
286    /// This function looks for pending and enabled interrupts and perform the dispatch for the
287    /// interrupt with highest priority.
288    pub fn dispatch_interrupt(&mut self) {
289        if let Some((int, target_priv)) = raw::dispatchInterrupt(self, self.cur_privilege) {
290            raw::handle_interrupt(self, int, target_priv);
291        }
292    }
293
294    /// Inject an exception, triggerring the appropriate trap handler
295    ///
296    /// The target privilege mode depends on the current execution mode and the *deleg CSR
297    /// registers.
298    /// The `tval` is the trap value, which depends on the exception type. Memory access fault will
299    /// usually provide the faulting address.
300    pub fn inject_exception(&mut self, exception: ExceptionType, tval: u64) {
301        let current_level = self.cur_privilege;
302        let target_level = raw::exception_delegatee(self, exception, current_level);
303        raw::trap_handler(
304            self,
305            target_level,
306            false,
307            raw::exceptionType_to_bits(exception),
308            self.PC,
309            Some(bv(tval)),
310            None,
311        );
312    }
313
314    /// Return the `pmpaddr<index>` register.
315    pub fn get_pmpaddr(&self, index: usize) -> u64 {
316        self.pmpaddr_n[index].bits()
317    }
318
319    /// Set the `pmpaddr<index>` register to the given value.
320    pub fn set_pmpaddr(&mut self, index: usize, val: u64) {
321        raw::pmpWriteAddrReg(self, index as i128, bv(val));
322    }
323
324    /// Set the `pmpcfg<index>` register to the given value.
325    pub fn set_pmpcfg(&mut self, index: usize, val: u64) {
326        raw::pmpWriteCfgReg(self, index as i128, bv(val));
327    }
328
329    /// Check if an 8 byte access is allowed with the current mode and PMP configuration.
330    ///
331    /// Return None is the check succeed, or an error otherwise.
332    pub fn pmp_check(
333        &mut self,
334        addr: u64,
335        access_kind: raw::AccessType<()>,
336    ) -> Option<raw::ExceptionType> {
337        let addr = raw::physaddr::Physaddr(bv(addr));
338        let width = 8;
339        raw::pmpCheck(self, addr, width, access_kind, self.cur_privilege)
340    }
341}
342
343/// Returns a fresh core instance with the provided configuration.
344///
345/// IMPORTANT: The freshtly created core is not guaranteed to be in a valid state. Call
346/// [Core::reset] or update CSRs manually to ensure the core enters a valid starting state.
347pub const fn new_core(config: raw::Config) -> Core {
348    Core {
349        PC: bv(0),
350        nextPC: bv(0),
351        x1: bv(0),
352        x2: bv(0),
353        x3: bv(0),
354        x4: bv(0),
355        x5: bv(0),
356        x6: bv(0),
357        x7: bv(0),
358        x8: bv(0),
359        x9: bv(0),
360        x10: bv(0),
361        x11: bv(0),
362        x12: bv(0),
363        x13: bv(0),
364        x14: bv(0),
365        x15: bv(0),
366        x16: bv(0),
367        x17: bv(0),
368        x18: bv(0),
369        x19: bv(0),
370        x20: bv(0),
371        x21: bv(0),
372        x22: bv(0),
373        x23: bv(0),
374        x24: bv(0),
375        x25: bv(0),
376        x26: bv(0),
377        x27: bv(0),
378        x28: bv(0),
379        x29: bv(0),
380        x30: bv(0),
381        x31: bv(0),
382        cur_privilege: raw::Privilege::Machine,
383        cur_inst: bv(0),
384        misa: raw::Misa { bits: bv(0) },
385        mstatus: raw::Mstatus { bits: bv(0) },
386        menvcfg: raw::MEnvcfg { bits: bv(0) },
387        senvcfg: raw::SEnvcfg { bits: bv(0) },
388        mie: raw::Minterrupts { bits: bv(0) },
389        mip: raw::Minterrupts { bits: bv(0) },
390        medeleg: raw::Medeleg { bits: bv(0) },
391        mideleg: raw::Minterrupts { bits: bv(0) },
392        mtvec: raw::Mtvec { bits: bv(0) },
393        mcause: raw::Mcause { bits: bv(0) },
394        mepc: bv(0),
395        mtval: bv(0),
396        mscratch: bv(0),
397        scounteren: raw::Counteren { bits: bv(0) },
398        mcounteren: raw::Counteren { bits: bv(0) },
399        mcountinhibit: raw::Counterin { bits: bv(0) },
400        mcycle: bv(0),
401        mtime: bv(0),
402        minstret: bv(0),
403        minstret_increment: false,
404        mvendorid: bv(0),
405        mimpid: bv(0),
406        marchid: bv(0),
407        mhartid: bv(0),
408        mconfigptr: bv(0),
409        stvec: raw::Mtvec { bits: bv(0) },
410        sscratch: bv(0),
411        sepc: bv(0),
412        scause: raw::Mcause { bits: bv(0) },
413        stval: bv(0),
414        tselect: bv(0),
415        vstart: bv(0),
416        vl: bv(0),
417        vtype: raw::Vtype { bits: bv(0) },
418        pmpcfg_n: [DEFAULT_PMP_CFG; 64],
419        pmpaddr_n: [ZEROES; 64],
420        vr0: bv(0),
421        vr1: bv(0),
422        vr2: bv(0),
423        vr3: bv(0),
424        vr4: bv(0),
425        vr5: bv(0),
426        vr6: bv(0),
427        vr7: bv(0),
428        vr8: bv(0),
429        vr9: bv(0),
430        vr10: bv(0),
431        vr11: bv(0),
432        vr12: bv(0),
433        vr13: bv(0),
434        vr14: bv(0),
435        vr15: bv(0),
436        vr16: bv(0),
437        vr17: bv(0),
438        vr18: bv(0),
439        vr19: bv(0),
440        vr20: bv(0),
441        vr21: bv(0),
442        vr22: bv(0),
443        vr23: bv(0),
444        vr24: bv(0),
445        vr25: bv(0),
446        vr26: bv(0),
447        vr27: bv(0),
448        vr28: bv(0),
449        vr29: bv(0),
450        vr30: bv(0),
451        vr31: bv(0),
452        vcsr: raw::Vcsr { bits: bv(0) },
453        mhpmevent: [DEFAULT_HPM_EVENT; 32],
454        mhpmcounter: [ZEROES; 32],
455        float_result: bv(0),
456        float_fflags: bv(0),
457        f0: bv(0),
458        f1: bv(0),
459        f2: bv(0),
460        f3: bv(0),
461        f4: bv(0),
462        f5: bv(0),
463        f6: bv(0),
464        f7: bv(0),
465        f8: bv(0),
466        f9: bv(0),
467        f10: bv(0),
468        f11: bv(0),
469        f12: bv(0),
470        f13: bv(0),
471        f14: bv(0),
472        f15: bv(0),
473        f16: bv(0),
474        f17: bv(0),
475        f18: bv(0),
476        f19: bv(0),
477        f20: bv(0),
478        f21: bv(0),
479        f22: bv(0),
480        f23: bv(0),
481        f24: bv(0),
482        f25: bv(0),
483        f26: bv(0),
484        f27: bv(0),
485        f28: bv(0),
486        f29: bv(0),
487        f30: bv(0),
488        f31: bv(0),
489        fcsr: raw::Fcsr { bits: bv(0) },
490        mcyclecfg: raw::CountSmcntrpmf { bits: bv(0) },
491        minstretcfg: raw::CountSmcntrpmf { bits: bv(0) },
492        mtimecmp: bv(0),
493        stimecmp: bv(0),
494        htif_tohost: bv(0),
495        htif_done: false,
496        htif_exit_code: bv(0),
497        htif_cmd_write: false,
498        htif_payload_writes: bv(0),
499        tlb: [DEFAULT_TLB_ENTRY; raw::num_tlb_entries as usize],
500        satp: bv(0),
501        hart_state: raw::HartState::HART_ACTIVE(()),
502        config,
503    }
504}
505
506// ———————————————————————————————— Helpers ————————————————————————————————— //
507
508impl regidx {
509    /// Creates a new regidx from a register index
510    pub fn new(reg: u8) -> regidx {
511        regidx::Regidx(bv(reg as u64))
512    }
513
514    /// Return the register index as bits.
515    pub fn bits(self) -> u8 {
516        let regidx::Regidx(bits) = self;
517        bits.bits() as u8
518    }
519}
520
521impl cregidx {
522    /// Return the compressed register index as bits.
523    ///
524    /// Warning: this is not the same as the uncompressed register index.
525    pub fn bits(self) -> u8 {
526        let cregidx::Cregidx(bits) = self;
527        bits.bits() as u8
528    }
529
530    /// Convert a compressed register index into an uncompressed register index.
531    pub fn to_regidx(self) -> regidx {
532        raw::creg2reg_idx(self)
533    }
534}
535
536// ————————————————————————————————— Tests —————————————————————————————————— //
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use crate::raw::*;
542
543    #[test]
544    fn pmp_check() {
545        let mut core = new_core(config::U74);
546        let addr = 0x8000_0000;
547        let access = AccessType::Read(());
548
549        // Check the default access rights
550        assert!(
551            core.pmp_check(addr, access).is_none(),
552            "M-mode can access all memory by default"
553        );
554
555        core.set_mode(Privilege::User);
556        assert_eq!(
557            core.pmp_check(addr, access),
558            Some(ExceptionType::E_Load_Access_Fault(())),
559            "U-mode has no access by default"
560        );
561
562        // Now let's add a PMP entry to allow reads from U-mode
563        let pmp_addr = addr >> 2; // There is a shift of 2 in the pmpaddr registers
564        core.set_pmpaddr(0, pmp_addr);
565        core.set_pmpaddr(1, 2 * pmp_addr);
566        core.set_pmpcfg(0, 0b0000_1001 << 8); // Entry 1, Read-only access, ToR matching mode
567        assert!(
568            core.pmp_check(addr, access).is_none(),
569            "PMP allow read access"
570        );
571    }
572
573    #[test]
574    fn decoder() {
575        let mut ctx = new_core(config::U74);
576        let uimm0 = bv(0);
577
578        // Load/Store
579
580        assert_eq!(
581            ctx.decode_instr(0xff87b703),
582            ast::LOAD((
583                bv(0xFFF - 7), // immediate is -8
584                X15,
585                X14,
586                false,
587                word_width::DOUBLE,
588                false,
589                false
590            ))
591        );
592
593        // CSR instructions
594
595        // csrrw x0, mstatus, x0
596        assert_eq!(
597            ctx.decode_instr(0x30001073),
598            ast::CSRReg((bv(0x300), X0, X0, csrop::CSRRW))
599        );
600        // csrrs x0, mstatus, x0
601        assert_eq!(
602            ctx.decode_instr(0x30002073),
603            ast::CSRReg((bv(0x300), X0, X0, csrop::CSRRS))
604        );
605        // csrrc x0, mstatus, x0
606        assert_eq!(
607            ctx.decode_instr(0x30003073),
608            ast::CSRReg((bv(0x300), X0, X0, csrop::CSRRC))
609        );
610        // csrrwi x0, mstatus, 0
611        assert_eq!(
612            ctx.decode_instr(0x30005073),
613            ast::CSRImm((bv(0x300), uimm0, X0, csrop::CSRRW))
614        );
615        // csrrsi x0, mstatus, 0
616        assert_eq!(
617            ctx.decode_instr(0x30006073),
618            ast::CSRImm((bv(0x300), uimm0, X0, csrop::CSRRS))
619        );
620        // csrrci x0, mstatus, 0
621        assert_eq!(
622            ctx.decode_instr(0x30007073),
623            ast::CSRImm((bv(0x300), uimm0, X0, csrop::CSRRC))
624        );
625
626        // Illegal
627        assert_eq!(ctx.decode_instr(0x30001072), ast::ILLEGAL(bv(0x30001072)));
628    }
629
630    #[test]
631    fn general_purpose_registers() {
632        let mut ctx = new_core(config::U74);
633
634        // Test X0 (ZERO) - should always be hardwired to 0
635        assert_eq!(ctx.get(X0), 0, "X0 should be hardwired to 0");
636        assert_eq!(ctx.get(ZERO), 0, "ZERO should be hardwired to 0");
637
638        // Try to write to X0 - should remain 0
639        ctx.set(X0, 0xDEADBEEF);
640        assert_eq!(ctx.get(X0), 0, "X0 should remain 0 after write attempt");
641
642        // Test some other registers using ABI names
643        ctx.set(RA, 0x12345678);
644        assert_eq!(ctx.get(RA), 0x12345678, "RA register should store value");
645        assert_eq!(ctx.get(X1), 0x12345678, "X1 and RA should be the same");
646
647        ctx.set(SP, 0x87654321);
648        assert_eq!(ctx.get(SP), 0x87654321, "SP register should store value");
649        assert_eq!(ctx.get(X2), 0x87654321, "X2 and SP should be the same");
650
651        // Test function argument registers
652        ctx.set(A0, 0xAAAAAAAA);
653        ctx.set(A1, 0xBBBBBBBB);
654        assert_eq!(ctx.get(A0), 0xAAAAAAAA, "A0 register should store value");
655        assert_eq!(ctx.get(A1), 0xBBBBBBBB, "A1 register should store value");
656        assert_eq!(ctx.get(X10), 0xAAAAAAAA, "X10 and A0 should be the same");
657        assert_eq!(ctx.get(X11), 0xBBBBBBBB, "X11 and A1 should be the same");
658
659        // Test saved registers
660        ctx.set(S0, 0xCCCCCCCC);
661        ctx.set(S1, 0xDDDDDDDD);
662        assert_eq!(ctx.get(S0), 0xCCCCCCCC, "S0 register should store value");
663        assert_eq!(ctx.get(S1), 0xDDDDDDDD, "S1 register should store value");
664        assert_eq!(ctx.get(FP), 0xCCCCCCCC, "FP and S0 should be the same");
665        assert_eq!(ctx.get(X8), 0xCCCCCCCC, "X8 and S0 should be the same");
666        assert_eq!(ctx.get(X9), 0xDDDDDDDD, "X9 and S1 should be the same");
667
668        // Test temporary registers
669        ctx.set(T0, 0xEEEEEEEE);
670        ctx.set(T6, 0xFFFFFFFF);
671        assert_eq!(ctx.get(T0), 0xEEEEEEEE, "T0 register should store value");
672        assert_eq!(ctx.get(T6), 0xFFFFFFFF, "T6 register should store value");
673        assert_eq!(ctx.get(X5), 0xEEEEEEEE, "X5 and T0 should be the same");
674        assert_eq!(ctx.get(X31), 0xFFFFFFFF, "X31 and T6 should be the same");
675
676        // Verify X0 is still 0 after all the other operations
677        assert_eq!(
678            ctx.get(X0),
679            0,
680            "X0 should still be 0 after other register operations"
681        );
682    }
683
684    #[test]
685    fn csr_defined() {
686        let mut core = new_core(config::U74);
687
688        // Test standard machine-level CSRs that should exist
689        assert!(core.is_csr_defined(0x300), "mstatus should be defined");
690        assert!(core.is_csr_defined(0x301), "misa should be defined");
691        assert!(core.is_csr_defined(0x304), "mie should be defined");
692        assert!(core.is_csr_defined(0x305), "mtvec should be defined");
693        assert!(core.is_csr_defined(0x341), "mepc should be defined");
694        assert!(core.is_csr_defined(0x342), "mcause should be defined");
695        assert!(core.is_csr_defined(0x343), "mtval should be defined");
696        assert!(core.is_csr_defined(0x344), "mip should be defined");
697
698        // Test PMP configuration registers
699        // U74 core has 16 PMP entries, so pmpcfg0, pmpcfg2 should exist, but not pmpcfg4 or
700        assert!(core.is_csr_defined(0x3A0), "pmpcfg0 should be defined");
701        assert!(core.is_csr_defined(0x3A2), "pmpcfg2 should be defined");
702        assert!(!core.is_csr_defined(0x3A4), "pmpcfg4 should not be defined");
703        assert!(!core.is_csr_defined(0x3A6), "pmpcfg6 should not be defined");
704
705        // Test that odd pmpcfg registers don't exist (RV64 uses even pmpcfg registers only)
706        assert!(
707            !core.is_csr_defined(0x3A1),
708            "pmpcfg1 should not be defined on RV64"
709        );
710        assert!(
711            !core.is_csr_defined(0x3A3),
712            "pmpcfg3 should not be defined on RV64"
713        );
714        assert!(
715            !core.is_csr_defined(0x3A5),
716            "pmpcfg5 should not be defined on RV64"
717        );
718
719        // Test PMP address registers
720        // U74 core has 16 PMP entries, so pmpaddr0-pmpaddr15 should exist
721        assert!(core.is_csr_defined(0x3B0), "pmpaddr0 should be defined");
722        assert!(core.is_csr_defined(0x3B5), "pmpaddr5 should be defined");
723        assert!(core.is_csr_defined(0x3BF), "pmpaddr15 should be defined");
724
725        // Test that PMP address registers beyond 16 don't exist on U74
726        assert!(
727            !core.is_csr_defined(0x3C0),
728            "pmpaddr16 should not be defined on U74"
729        );
730        assert!(
731            !core.is_csr_defined(0x3C8),
732            "pmpaddr24 should not be defined on U74"
733        );
734        assert!(
735            !core.is_csr_defined(0x3CF),
736            "pmpaddr31 should not be defined on U74"
737        );
738
739        // Test some CSRs that definitely shouldn't exist
740        assert!(
741            !core.is_csr_defined(0x000),
742            "CSR 0x000 should not be defined"
743        );
744        assert!(
745            !core.is_csr_defined(0xFFF),
746            "CSR 0xFFF should not be defined"
747        );
748        assert!(
749            !core.is_csr_defined(0x200),
750            "CSR 0x200 should not be defined"
751        );
752    }
753
754    #[test]
755    fn inject_exception() {
756        let mut core = new_core(config::U74);
757
758        // Set initial state
759        core.set_mode(Privilege::User);
760        core.PC = bv(0x1000);
761        let initial_pc = core.PC.bits();
762
763        assert_eq!(core.mode(), Privilege::User, "Initial mode should be User");
764
765        // Inject a load access fault exception
766        let fault_addr = 0x8000_0000;
767        core.inject_exception(ExceptionType::E_Load_Access_Fault(()), fault_addr);
768
769        // After exception, should be in Machine mode
770        assert_eq!(
771            core.mode(),
772            Privilege::Machine,
773            "Mode should be Machine after exception"
774        );
775
776        // Check that mepc was set to the PC at the time of the exception
777        assert_eq!(
778            core.mepc.bits(),
779            initial_pc,
780            "mepc should contain the PC when exception occurred"
781        );
782
783        // Check that mtval contains the fault address
784        assert_eq!(
785            core.mtval.bits(),
786            fault_addr,
787            "mtval should contain the fault address"
788        );
789    }
790
791    #[test]
792    fn csr_read_operations() {
793        let mut core = new_core(config::U74);
794
795        // Test reading standard CSRs - these should not panic
796        let _mstatus = core.get_csr(0x300);
797        let _misa = core.get_csr(0x301);
798        let _mie = core.get_csr(0x304);
799        let _mtvec = core.get_csr(0x305);
800        let _mepc = core.get_csr(0x341);
801        let _mcause = core.get_csr(0x342);
802        let _mtval = core.get_csr(0x343);
803        let _mip = core.get_csr(0x344);
804
805        // Test that we can read and write mscratch
806        let initial_value = core.get_csr(0x340);
807        assert_eq!(initial_value, Some(0), "mscratch should be 0 initially");
808    }
809
810    #[test]
811    fn csr_write_operations() {
812        let mut core = new_core(config::U74);
813
814        // Test CSRRW (read-write) operation
815        let initial_value = 0x12345678;
816        core.set(X1, initial_value);
817
818        // Write to mscratch (a read-write register)
819        let result = core.csrrw(X2, 0x340, X1);
820        assert!(result.is_ok(), "csrrw should succeed for mscratch");
821
822        // Read back the value
823        let read_value = core.get_csr(0x340);
824        assert_eq!(
825            read_value,
826            Some(initial_value),
827            "mscratch should contain written value"
828        );
829
830        // X2 should contain the old value (0 for fresh core)
831        assert_eq!(core.get(X2), 0, "rd should contain old CSR value");
832
833        // Test writing to X0 (should not update rd)
834        let new_value = 0x87654321;
835        core.set(X3, new_value);
836        let result = core.csrrw(X0, 0x340, X3);
837        assert!(result.is_ok(), "csrrw with X0 as rd should succeed");
838
839        // X0 should remain 0
840        assert_eq!(core.get(X0), 0, "X0 should remain 0");
841
842        // mscratch should have new value
843        let read_value = core.get_csr(0x340);
844        assert_eq!(
845            read_value,
846            Some(new_value),
847            "mscratch should contain new value"
848        );
849    }
850
851    #[test]
852    fn csr_set_operations() {
853        let mut core = new_core(config::U74);
854
855        // Initialize mscratch with a known value
856        core.set(X1, 0xFF00FF00);
857        let _ = core.csrrw(X0, 0x340, X1);
858
859        // Test CSRRS (read-set) operation
860        let set_bits = 0x00FF00FF;
861        core.set(X2, set_bits);
862
863        let result = core.csrrs(X3, 0x340, X2);
864        assert!(result.is_ok(), "csrrs should succeed for mscratch");
865
866        // X3 should contain the old value
867        assert_eq!(core.get(X3), 0xFF00FF00, "rd should contain old CSR value");
868
869        // mscratch should have bits set
870        let read_value = core.get_csr(0x340);
871        assert_eq!(
872            read_value,
873            Some(0xFFFFFFFF),
874            "mscratch should have bits set"
875        );
876
877        // Test CSRRS with X0 as rs1 (should only read, not modify)
878        let result = core.csrrs(X4, 0x340, X0);
879        assert!(result.is_ok(), "csrrs with X0 as rs1 should succeed");
880
881        // X4 should contain current value
882        assert_eq!(
883            core.get(X4),
884            0xFFFFFFFF,
885            "rd should contain current CSR value"
886        );
887
888        // mscratch should be unchanged
889        let read_value = core.get_csr(0x340);
890        assert_eq!(read_value, Some(0xFFFFFFFF), "mscratch should be unchanged");
891    }
892
893    #[test]
894    fn csr_clear_operations() {
895        let mut core = new_core(config::U74);
896
897        // Initialize mscratch with all bits set
898        core.set(X1, 0xFFFFFFFF);
899        let _ = core.csrrw(X0, 0x340, X1);
900
901        // Test CSRRC (read-clear) operation
902        let clear_bits = 0x0F0F0F0F;
903        core.set(X2, clear_bits);
904
905        let result = core.csrrc(X3, 0x340, X2);
906        assert!(result.is_ok(), "csrrc should succeed for mscratch");
907
908        // X3 should contain the old value
909        assert_eq!(core.get(X3), 0xFFFFFFFF, "rd should contain old CSR value");
910
911        // mscratch should have bits cleared
912        let read_value = core.get_csr(0x340);
913        assert_eq!(
914            read_value,
915            Some(0xF0F0F0F0),
916            "mscratch should have bits cleared"
917        );
918
919        // Test CSRRC with X0 as rs1 (should only read, not modify)
920        let result = core.csrrc(X4, 0x340, X0);
921        assert!(result.is_ok(), "csrrc with X0 as rs1 should succeed");
922
923        // X4 should contain current value
924        assert_eq!(
925            core.get(X4),
926            0xF0F0F0F0,
927            "rd should contain current CSR value"
928        );
929
930        // mscratch should be unchanged
931        let read_value = core.get_csr(0x340);
932        assert_eq!(read_value, Some(0xF0F0F0F0), "mscratch should be unchanged");
933    }
934
935    #[test]
936    fn csr_immediate_operations() {
937        let mut core = new_core(config::U74);
938
939        // Test CSRRWI (read-write immediate)
940        let result = core.csrrwi(X1, 0x340, 0x15);
941        assert!(result.is_ok(), "csrrwi should succeed for mscratch");
942
943        // X1 should contain old value (0)
944        assert_eq!(core.get(X1), 0, "rd should contain old CSR value");
945
946        // mscratch should have immediate value
947        let read_value = core.get_csr(0x340);
948        assert_eq!(
949            read_value,
950            Some(0x15),
951            "mscratch should contain immediate value"
952        );
953
954        // Test CSRRSI (read-set immediate)
955        let result = core.csrrsi(X2, 0x340, 0x0A);
956        assert!(result.is_ok(), "csrrsi should succeed for mscratch");
957
958        // X2 should contain old value
959        assert_eq!(core.get(X2), 0x15, "rd should contain old CSR value");
960
961        // mscratch should have bits set
962        let read_value = core.get_csr(0x340);
963        assert_eq!(read_value, Some(0x1F), "mscratch should have bits set");
964
965        // Test CSRRCI (read-clear immediate)
966        let result = core.csrrci(X3, 0x340, 0x05);
967        assert!(result.is_ok(), "csrrci should succeed for mscratch");
968
969        // X3 should contain old value
970        assert_eq!(core.get(X3), 0x1F, "rd should contain old CSR value");
971
972        // mscratch should have bits cleared
973        let read_value = core.get_csr(0x340);
974        assert_eq!(read_value, Some(0x1A), "mscratch should have bits cleared");
975
976        // Test that immediate values are masked to 5 bits
977        let result = core.csrrwi(X4, 0x340, 0xFF);
978        assert!(result.is_ok(), "csrrwi with large immediate should succeed");
979
980        // mscratch should only have lower 5 bits of immediate
981        let read_value = core.get_csr(0x340);
982        assert_eq!(
983            read_value,
984            Some(0x1F),
985            "immediate should be masked to 5 bits"
986        );
987
988        // Test immediate operations with zero immediate (should not modify for set/clear)
989        core.set(X5, 0x12345678);
990        let _ = core.csrrw(X0, 0x340, X5);
991
992        let result = core.csrrsi(X6, 0x340, 0);
993        assert!(result.is_ok(), "csrrsi with zero immediate should succeed");
994        assert_eq!(core.get(X6), 0x12345678, "rd should contain current value");
995
996        let read_value = core.get_csr(0x340);
997        assert_eq!(
998            read_value,
999            Some(0x12345678),
1000            "CSR should be unchanged with zero immediate"
1001        );
1002
1003        let result = core.csrrci(X7, 0x340, 0);
1004        assert!(result.is_ok(), "csrrci with zero immediate should succeed");
1005        assert_eq!(core.get(X7), 0x12345678, "rd should contain current value");
1006
1007        let read_value = core.get_csr(0x340);
1008        assert_eq!(
1009            read_value,
1010            Some(0x12345678),
1011            "CSR should be unchanged with zero immediate"
1012        );
1013    }
1014
1015    #[test]
1016    fn execute_addi() {
1017        let mut core = new_core(config::U74);
1018
1019        // Test basic ADDI: addi a1, x0, 0x42
1020        assert_eq!(core.get(A1), 0, "A1 should start at 0");
1021        let result = core.execute(ast::ITYPE((bv(0x42), X0, A1, iop::ADDI)));
1022        assert!(matches!(result, Trap::None));
1023        assert_eq!(core.get(A1), 0x42, "A1 should contain immediate value");
1024
1025        // Test ADDI with register source: addi a1, a1, 0x20
1026        let result = core.execute(ast::ITYPE((bv(0x20), A1, A1, iop::ADDI)));
1027        assert!(matches!(result, Trap::None));
1028        assert_eq!(
1029            core.get(A1),
1030            0x62,
1031            "A1 should contain sum of previous value and immediate"
1032        );
1033
1034        // Test ADDI with negative immediate (sign extension)
1035        core.set(T0, 100);
1036        let result = core.execute(ast::ITYPE((bv((-10i64) as u64), T0, T1, iop::ADDI)));
1037        assert!(matches!(result, Trap::None));
1038        assert_eq!(
1039            core.get(T1),
1040            90,
1041            "T1 should contain result of adding negative immediate"
1042        );
1043
1044        // Test ADDI with X0 as destination (should be ignored)
1045        let result = core.execute(ast::ITYPE((bv(0xFF), T0, X0, iop::ADDI)));
1046        assert!(matches!(result, Trap::None));
1047        assert_eq!(core.get(X0), 0, "X0 should remain hardwired to 0");
1048
1049        // Test overflow behavior
1050        core.set(T2, u64::MAX);
1051        let result = core.execute(ast::ITYPE((bv(1), T2, T3, iop::ADDI)));
1052        assert!(matches!(result, Trap::None));
1053        assert_eq!(core.get(T3), 0, "Addition should wrap around on overflow");
1054    }
1055
1056    #[test]
1057    fn execute_jalr() {
1058        let mut core = new_core(config::U74);
1059
1060        // Set up initial PC and register state
1061        core.PC = bv(0x1000);
1062        core.nextPC = bv(0x1004);
1063        core.set(T0, 0x3000); // Target address base
1064
1065        let initial_pc = core.PC.bits();
1066
1067        // Test JALR: jalr ra, t0, 8
1068        // This should jump to (t0 + 8) & ~1 and store PC+4 in ra
1069        let result = core.execute(ast::JALR((bv(8), T0, RA)));
1070
1071        assert!(matches!(result, Trap::None));
1072
1073        // Check that return address was stored (old PC + 4)
1074        assert_eq!(
1075            core.get(RA),
1076            initial_pc + 4,
1077            "Return address should be old PC + 4"
1078        );
1079
1080        // Check that nextPC was updated to target address (t0 + offset) with LSB cleared
1081        let expected_target = (0x3000 + 8) & !1; // JALR clears the LSB
1082        assert_eq!(
1083            core.nextPC.bits(),
1084            expected_target,
1085            "nextPC should be updated to target address"
1086        );
1087    }
1088
1089    #[test]
1090    fn execute_mul() {
1091        let mut core = new_core(config::U74);
1092
1093        // Test MUL instruction: mul t0, t1, t2
1094        core.set(T1, 123);
1095        core.set(T2, 456);
1096
1097        let mul_op = raw::mul_op {
1098            high: false,
1099            signed_rs1: true,
1100            signed_rs2: true,
1101        };
1102        let result = core.execute(ast::MUL((T2, T1, T0, mul_op)));
1103
1104        assert!(matches!(result, Trap::None));
1105
1106        // Check that the multiplication result is correct
1107        let expected = 123u64.wrapping_mul(456u64);
1108        assert_eq!(core.get(T0), expected, "MUL result should be correct");
1109
1110        // Test with negative numbers (signed multiplication)
1111        core.set(T1, (-5i64) as u64); // -5 in two's complement
1112        core.set(T2, 3);
1113
1114        let result = core.execute(ast::MUL((T2, T1, T0, mul_op)));
1115        assert!(matches!(result, Trap::None));
1116
1117        let expected = ((-5i64).wrapping_mul(3i64)) as u64;
1118        assert_eq!(
1119            core.get(T0),
1120            expected,
1121            "MUL with negative numbers should work"
1122        );
1123    }
1124
1125    #[test]
1126    fn execute_div() {
1127        let mut core = new_core(config::U74);
1128
1129        // Test DIV instruction: div t0, t1, t2 (signed division)
1130        core.set(T1, 100);
1131        core.set(T2, 7);
1132
1133        let result = core.execute(ast::DIV((T2, T1, T0, true))); // true = signed division
1134
1135        assert!(matches!(result, Trap::None));
1136
1137        // Check signed division result
1138        let expected = (100i64 / 7i64) as u64;
1139        assert_eq!(core.get(T0), expected, "DIV result should be correct");
1140
1141        // Test division by zero (should return -1 according to RISC-V spec)
1142        core.set(T1, 42);
1143        core.set(T2, 0);
1144
1145        let result = core.execute(ast::DIV((T2, T1, T0, true)));
1146        assert!(matches!(result, Trap::None));
1147
1148        assert_eq!(
1149            core.get(T0),
1150            (-1i64) as u64,
1151            "Division by zero should return -1"
1152        );
1153
1154        // Test signed division with negative numbers
1155        core.set(T1, (-20i64) as u64);
1156        core.set(T2, 3);
1157
1158        let result = core.execute(ast::DIV((T2, T1, T0, true)));
1159        assert!(matches!(result, Trap::None));
1160
1161        let expected = ((-20i64) / 3i64) as u64;
1162        assert_eq!(
1163            core.get(T0),
1164            expected,
1165            "Signed division should work correctly"
1166        );
1167
1168        // Test unsigned division
1169        core.set(T1, 100);
1170        core.set(T2, 7);
1171
1172        let result = core.execute(ast::DIV((T2, T1, T0, false))); // false = unsigned division
1173        assert!(matches!(result, Trap::None));
1174
1175        let expected = 100u64 / 7u64;
1176        assert_eq!(
1177            core.get(T0),
1178            expected,
1179            "Unsigned division should work correctly"
1180        );
1181    }
1182}