diff --git a/difftest/spike_interfaces/spike_interfaces.cc b/difftest/spike_interfaces/spike_interfaces.cc index 479ebe11b..4ab4b62a5 100644 --- a/difftest/spike_interfaces/spike_interfaces.cc +++ b/difftest/spike_interfaces/spike_interfaces.cc @@ -56,6 +56,13 @@ const char *proc_disassemble(spike_processor_t *proc) { return strdup(disasm->disassemble(fetch.insn).c_str()); } +const char *proc_disassemble_with_pc(spike_processor_t *proc, reg_t pc) { + auto mmu = proc->p->get_mmu(); + auto disasm = proc->p->get_disassembler(); + auto fetch = mmu->load_insn(pc); + return strdup(disasm->disassemble(fetch.insn).c_str()); +} + spike_processor_t *spike_get_proc(spike_t *spike) { return new spike_processor_t{spike->s->get_proc()}; } @@ -67,20 +74,79 @@ spike_state_t *proc_get_state(spike_processor_t *proc) { } reg_t proc_func(spike_processor_t *proc) { - auto pc = proc->p->get_state()->pc; - auto mmu = proc->p->get_mmu(); - auto fetch = mmu->load_insn(pc); - try { - return fetch.func(proc->p, fetch.insn, pc); + reg_t pc = proc->p->get_state()->pc; + mmu_t* mmu = proc->p->get_mmu(); + insn_fetch_t fetch; + reg_t res; + // todo: consider interrupt + try { + fetch = mmu->load_insn(pc); + res = fetch.func(proc->p, fetch.insn, pc); } catch (trap_t &trap) { - std::cerr << "Error: spike trapped with " << trap.name() - << " (tval=" << std::uppercase << std::setfill('0') - << std::setw(8) << std::hex << trap.get_tval() - << ", tval2=" << std::setw(8) << std::hex << trap.get_tval2() - << ", tinst=" << std::setw(8) << std::hex << trap.get_tinst() - << ")" << std::endl; - throw trap; - } + //printf("catch exception\n"); + unsigned max_xlen = proc->p->get_const_xlen(); + state_t* state = proc->p->get_state(); + reg_t hsdeleg = (state->prv <= PRV_S) ? state->medeleg->read() : 0; + bool curr_virt = state->v; + reg_t bit = trap.cause(); + + if (state->prv <= PRV_S && bit < max_xlen && ((hsdeleg >> bit) & 1)) { + reg_t vector = (state->nonvirtual_stvec->read() & 1) ? 4 * bit : 0; + state->pc = (state->nonvirtual_stvec->read() & ~(reg_t)1) + vector; + state->nonvirtual_scause->write(trap.cause()); + state->nonvirtual_sepc->write(pc); + state->nonvirtual_stval->write(trap.get_tval()); + state->htval->write(trap.get_tval2()); + state->htinst->write(trap.get_tinst()); + + reg_t s = state->nonvirtual_sstatus->read(); + s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); + s = set_field(s, MSTATUS_SPP, state->prv); + s = set_field(s, MSTATUS_SIE, 0); + s = set_field(s, MSTATUS_SPELP, state->elp); + state->elp = elp_t::NO_LP_EXPECTED; + state->nonvirtual_sstatus->write(s); + proc->p->set_privilege(PRV_S, false); + } else { + const reg_t vector = (state->mtvec->read() & 1) ? 4 * bit : 0; + const reg_t trap_handler_address = (state->mtvec->read() & ~(reg_t)1) + vector; + + // todo: consider nmi + //const reg_t rnmi_trap_handler_address = 0; + //const bool nmie = !(state->mnstatus && !get_field(state->mnstatus->read(), MNSTATUS_NMIE)); + //state->pc = !nmie ? rnmi_trap_handler_address : trap_handler_address; + state->pc = trap_handler_address; + state->mepc->write(pc); + state->mcause->write(trap.cause()); + state->mtval->write(trap.get_tval()); + state->mtval2->write(trap.get_tval2()); + state->mtinst->write(trap.get_tinst()); + + reg_t s = state->mstatus->read(); + s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE)); + s = set_field(s, MSTATUS_MPP, state->prv); + s = set_field(s, MSTATUS_MIE, 0); + s = set_field(s, MSTATUS_MPV, curr_virt); + s = set_field(s, MSTATUS_GVA, trap.has_gva()); + s = set_field(s, MSTATUS_MPELP, state->elp); + state->elp = elp_t::NO_LP_EXPECTED; + state->mstatus->write(s); + if (state->mstatush) state->mstatush->write(s >> 32); + //state->tcontrol->write((state->tcontrol->read() & CSR_TCONTROL_MTE) ? CSR_TCONTROL_MPTE : 0); + proc->p->set_privilege(PRV_M, false); + } + //std::cerr << "Error: spike trapped with " << trap.name() + // << " (tval=" << std::uppercase << std::setfill('0') + // << std::setw(8) << std::hex << trap.get_tval() + // << ", tval2=" << std::setw(8) << std::hex << trap.get_tval2() + // << ", tinst=" << std::setw(8) << std::hex << trap.get_tinst() + // << ")" << std::endl; + //throw trap; + proc->is_exception = true; + res = state->pc; + } + + return res; } reg_t proc_get_insn(spike_processor_t *proc) { @@ -90,6 +156,16 @@ reg_t proc_get_insn(spike_processor_t *proc) { return fetch.insn.bits(); } +reg_t proc_get_insn_with_pc(spike_processor_t *proc, reg_t pc) { + try { + auto mmu = proc->p->get_mmu(); + auto fetch = mmu->load_insn(pc); + return fetch.insn.bits(); + } catch(...) { + return 0; + } +} + uint8_t proc_get_vreg_data(spike_processor_t *proc, uint32_t vreg_idx, uint32_t vreg_offset) { return proc->p->VU.elt(vreg_idx, vreg_offset); @@ -109,18 +185,45 @@ uint32_t proc_get_rs1(spike_processor_t *proc) { return (uint32_t)fetch.insn.rs1(); } +uint32_t proc_get_rs1_with_pc(spike_processor_t *proc, reg_t pc) { + try{ + auto fetch = proc->p->get_mmu()->load_insn(pc); + return (uint32_t)fetch.insn.rs1(); + } catch(...) { + return 0; + } +} + uint32_t proc_get_rs2(spike_processor_t *proc) { auto pc = proc->p->get_state()->pc; auto fetch = proc->p->get_mmu()->load_insn(pc); return (uint32_t)fetch.insn.rs2(); } +uint32_t proc_get_rs2_with_pc(spike_processor_t *proc, reg_t pc) { + try{ + auto fetch = proc->p->get_mmu()->load_insn(pc); + return (uint32_t)fetch.insn.rs2(); + } catch(...) { + return 0; + } +} + uint32_t proc_get_rd(spike_processor_t *proc) { auto pc = proc->p->get_state()->pc; auto fetch = proc->p->get_mmu()->load_insn(pc); return fetch.insn.rd(); } +uint32_t proc_get_rd_with_pc(spike_processor_t *proc, reg_t pc) { + try { + auto fetch = proc->p->get_mmu()->load_insn(pc); + return fetch.insn.rd(); + } catch(...) { + return 0; + } +} + uint64_t proc_vu_get_vtype(spike_processor_t *proc) { return proc->p->VU.vtype->read(); } diff --git a/difftest/spike_interfaces/spike_interfaces.h b/difftest/spike_interfaces/spike_interfaces.h index ceffa8f7f..bf9276dc0 100644 --- a/difftest/spike_interfaces/spike_interfaces.h +++ b/difftest/spike_interfaces/spike_interfaces.h @@ -61,6 +61,7 @@ struct spike_t { }; struct spike_processor_t { processor_t *p; + bool is_exception; }; struct spike_state_t { state_t *s; diff --git a/difftest/spike_interfaces/spike_interfaces_c.h b/difftest/spike_interfaces/spike_interfaces_c.h index 6c43acaf0..bbbed864a 100644 --- a/difftest/spike_interfaces/spike_interfaces_c.h +++ b/difftest/spike_interfaces/spike_interfaces_c.h @@ -17,17 +17,22 @@ void spike_register_callback(void *ffi_target, ffi_callback callback); spike_t *spike_new(const char *set, const char *lvl, size_t lane_number); const char *proc_disassemble(spike_processor_t *proc); +const char *proc_disassemble_with_pc(spike_processor_t *proc, reg_t pc); void proc_reset(spike_processor_t *proc); spike_processor_t *spike_get_proc(spike_t *spike); spike_state_t *proc_get_state(spike_processor_t *proc); uint64_t proc_func(spike_processor_t *proc); uint64_t proc_get_insn(spike_processor_t *proc); +uint64_t proc_get_insn_with_pc(spike_processor_t *proc, reg_t pc); uint8_t proc_get_vreg_data(spike_processor_t *proc, uint32_t vreg_idx, uint32_t vreg_offset); uint32_t proc_get_rs1(spike_processor_t *proc); +uint32_t proc_get_rs1_with_pc(spike_processor_t *proc, reg_t pc); uint32_t proc_get_rs2(spike_processor_t *proc); +uint32_t proc_get_rs2_with_pc(spike_processor_t *proc, reg_t pc); uint32_t proc_get_rd(spike_processor_t *proc); +uint32_t proc_get_rd_with_pc(spike_processor_t *proc, reg_t pc); uint64_t proc_vu_get_vtype(spike_processor_t *proc); uint32_t proc_vu_get_vxrm(spike_processor_t *proc); diff --git a/rocketemu/driver/src/sim.rs b/rocketemu/driver/src/sim.rs index 0420a7f8c..0c32249d2 100644 --- a/rocketemu/driver/src/sim.rs +++ b/rocketemu/driver/src/sim.rs @@ -43,7 +43,7 @@ pub struct SimulationArgs { pub log_level: String, /// The timeout value - #[arg(long, default_value_t = 1_0000)] + #[arg(long, default_value_t = 3_00000)] pub timeout: u64, #[cfg(feature = "trace")] diff --git a/rocketemu/nix/verilated-csrc.nix b/rocketemu/nix/verilated-csrc.nix index f32ade7af..265a8d37e 100644 --- a/rocketemu/nix/verilated-csrc.nix +++ b/rocketemu/nix/verilated-csrc.nix @@ -43,6 +43,7 @@ stdenv.mkDerivation { -O1 \ -Wno-WIDTHEXPAND \ -Wno-LATCH \ + -Wno-UNOPTTHREADS \ --cc TestBench echo "[nix] building verilated C lib" diff --git a/rocketemu/offline/src/difftest.rs b/rocketemu/offline/src/difftest.rs index eaf755497..b16dadb94 100644 --- a/rocketemu/offline/src/difftest.rs +++ b/rocketemu/offline/src/difftest.rs @@ -35,7 +35,7 @@ impl Difftest { } if se.is_rd_written() && se.rd_idx != 0 { let event = self.dut.step()?; - + match event { JsonEvents::RegWrite { addr, data, cycle } => { self.runner.cycle = *cycle; diff --git a/rocketemu/spike_rs/src/lib.rs b/rocketemu/spike_rs/src/lib.rs index 0d64e6d24..6452b7279 100644 --- a/rocketemu/spike_rs/src/lib.rs +++ b/rocketemu/spike_rs/src/lib.rs @@ -55,7 +55,7 @@ impl Spike { pub fn get_proc(&self) -> Processor { let processor = unsafe { spike_get_proc(self.spike) }; - Processor { processor } + Processor { processor: processor, is_exception: false } } pub fn load_bytes_to_mem( @@ -88,6 +88,7 @@ impl Drop for Spike { pub struct Processor { processor: *mut (), + is_exception: bool, } impl Processor { @@ -97,6 +98,12 @@ impl Processor { format!("{}", c_str.to_string_lossy()) } + pub fn disassemble_with_pc(&self, pc: u64) -> String { + let bytes = unsafe { proc_disassemble_with_pc(self.processor, pc) }; + let c_str = unsafe { CStr::from_ptr(bytes as *mut c_char) }; + format!("{}", c_str.to_string_lossy()) + } + pub fn reset(&self) { unsafe { proc_reset(self.processor) } } @@ -114,6 +121,10 @@ impl Processor { unsafe { proc_get_insn(self.processor) as u32 } } + pub fn get_insn_with_pc(&self, pc: u64) -> u32 { + unsafe { proc_get_insn_with_pc(self.processor, pc) as u32 } + } + pub fn get_vreg_data(&self, idx: u32, offset: u32) -> u8 { unsafe { proc_get_vreg_data(self.processor, idx, offset) } } @@ -122,14 +133,26 @@ impl Processor { unsafe { proc_get_rs1(self.processor) } } + pub fn get_rs1_with_pc(&self, pc: u64) -> u32 { + unsafe { proc_get_rs1_with_pc(self.processor, pc) } + } + pub fn get_rs2(&self) -> u32 { unsafe { proc_get_rs2(self.processor) } } + pub fn get_rs2_with_pc(&self, pc: u64) -> u32 { + unsafe { proc_get_rs2_with_pc(self.processor, pc) } + } + pub fn get_rd(&self) -> u32 { unsafe { proc_get_rd(self.processor) } } + pub fn get_rd_with_pc(&self, pc: u64) -> u32 { + unsafe { proc_get_rd_with_pc(self.processor, pc) } + } + // vu pub fn vu_get_vtype(&self) -> u32 { unsafe { proc_vu_get_vtype(self.processor) as u32 } @@ -158,6 +181,14 @@ impl Processor { pub fn vu_get_vstart(&self) -> u16 { unsafe { proc_vu_get_vstart(self.processor) } } + + pub fn is_exception(&self) -> bool { + self.is_exception + } + + pub fn reset_exception(&mut self) { + self.is_exception = false + } } impl Drop for Processor { @@ -249,14 +280,19 @@ extern "C" { fn spike_get_proc(spike: *mut ()) -> *mut (); fn spike_destruct(spike: *mut ()); fn proc_disassemble(proc: *mut ()) -> *mut c_char; + fn proc_disassemble_with_pc(proc: *mut(), pc: u64) -> *mut c_char; fn proc_reset(proc: *mut ()); fn proc_get_state(proc: *mut ()) -> *mut (); fn proc_func(proc: *mut ()) -> u64; fn proc_get_insn(proc: *mut ()) -> u64; + fn proc_get_insn_with_pc(proc: *mut(), pc: u64) -> u64; fn proc_get_vreg_data(proc: *mut (), vreg_idx: u32, vreg_offset: u32) -> u8; fn proc_get_rs1(proc: *mut ()) -> u32; + fn proc_get_rs1_with_pc(proc: *mut(), pc: u64) -> u32; fn proc_get_rs2(proc: *mut ()) -> u32; + fn proc_get_rs2_with_pc(proc: *mut(), pc: u64) -> u32; fn proc_get_rd(proc: *mut ()) -> u32; + fn proc_get_rd_with_pc(proc: *mut(), pc: u64) -> u32; fn proc_vu_get_vtype(proc: *mut ()) -> u64; fn proc_vu_get_vxrm(proc: *mut ()) -> u32; diff --git a/rocketemu/spike_rs/src/spike_event.rs b/rocketemu/spike_rs/src/spike_event.rs index 7f6a2f030..cdbdc0a62 100644 --- a/rocketemu/spike_rs/src/spike_event.rs +++ b/rocketemu/spike_rs/src/spike_event.rs @@ -5,6 +5,8 @@ use Default; use crate::clip; use crate::Spike; +use tracing::debug; + #[derive(Debug, Clone)] pub struct SingleMemWrite { pub val: u8, @@ -100,6 +102,43 @@ pub struct SpikeEvent { } impl SpikeEvent { + pub fn new_with_pc(pc: u64, do_log_vrf: bool) -> Self { + SpikeEvent { + do_log_vrf, + + lsu_idx: LSU_IDX_DEFAULT, + issue_idx: ISSUE_IDX_DEFAULT, + + disasm: "".to_string(), + pc: pc, + inst_bits: 0, + + rs1: 0, + rs2: 0, + rs1_bits: 0, + rs2_bits: 0, + rd_idx: 0, + + vtype: 0, + vxrm: 0, + vnf: 0, + + vill: false, + vxsat: false, + vl: 0, + vstart: 0, + + rd_bits: 0, + + is_rd_written: false, + vd_write_record: Default::default(), + mem_access_record: Default::default(), + vrf_access_record: Default::default(), + + exit: false, + } + } + pub fn new(spike: &Spike, do_log_vrf: bool) -> Self { let proc = spike.get_proc(); let state = proc.get_state(); @@ -150,6 +189,32 @@ impl SpikeEvent { } } + pub fn fill_event(&mut self, spike: &Spike) { + let pc = self.pc; + let proc = spike.get_proc(); + let state = proc.get_state(); + + let insn_bits = proc.get_insn_with_pc(pc); + if insn_bits == 0 { + return ; + } + let opcode = clip(insn_bits, 0, 6); + let width = clip(insn_bits, 12, 14); + + let is_rs_fp = opcode == 0b1010111 && width == 0b101/* OPFVF */; + + let rs1 = proc.get_rs1_with_pc(pc); + let rs2 = proc.get_rs2_with_pc(pc); + + self.disasm = proc.disassemble_with_pc(pc); + self.inst_bits = insn_bits; + self.rs1 = rs1; + self.rs2 = rs2; + self.rs1_bits = state.get_reg(rs1, is_rs_fp); + self.rs2_bits = state.get_reg(rs2, is_rs_fp); + self.rd_idx = proc.get_rd_with_pc(pc); + } + pub fn opcode(&self) -> u32 { clip(self.inst_bits, 0, 6) } diff --git a/rocketemu/test_common/src/spike_runner.rs b/rocketemu/test_common/src/spike_runner.rs index 1da059b77..1952fabda 100644 --- a/rocketemu/test_common/src/spike_runner.rs +++ b/rocketemu/test_common/src/spike_runner.rs @@ -1,5 +1,5 @@ use std::path::Path; -use tracing::debug; +use tracing::{debug, info}; use spike_rs::spike_event::SpikeEvent; use spike_rs::util::load_elf; @@ -58,7 +58,7 @@ impl SpikeRunner { let state = proc.get_state(); let new_pc = proc.func(); - + state.handle_pc(new_pc).unwrap(); let ret = state.exit(); @@ -74,19 +74,29 @@ impl SpikeRunner { // the spike event for difftest pub fn spike_step(&mut self) -> SpikeEvent { let spike = &self.spike; - let proc = self.spike.get_proc(); + let mut proc = self.spike.get_proc(); let state = proc.get_state(); - state.set_mcycle((self.cycle + self.spike_cycle) as usize); + proc.reset_exception(); - let mut event = SpikeEvent::new(spike, self.do_log_vrf); + state.set_mcycle((self.cycle + self.spike_cycle) as usize); + + let mut event = SpikeEvent::new_with_pc(state.get_pc(), self.do_log_vrf); state.clear(); - // inst is scalar - debug!("SpikeStep: spike run scalar insn ({})", event.describe_insn()); + debug!("{:x}", state.get_pc()); let new_pc = proc.func(); - event.log_mem_write(spike).unwrap(); - event.log_reg_write(spike).unwrap(); + + // fill the SpikeEvent + if(!proc.is_exception()) { + event.fill_event(spike); + + // inst is scalar + debug!("SpikeStep: spike run scalar insn ({})", event.describe_insn()); + + event.log_mem_write(spike).unwrap(); + event.log_reg_write(spike).unwrap(); + } state.handle_pc(new_pc).unwrap(); diff --git a/rocketv/src/FetchQueue.scala b/rocketv/src/FetchQueue.scala index c1ad35fc1..c973df398 100644 --- a/rocketv/src/FetchQueue.scala +++ b/rocketv/src/FetchQueue.scala @@ -53,9 +53,16 @@ class FetchQueue(val parameter: FetchQueueParameter) override protected def implicitClock: Clock = io.clock override protected def implicitReset: Reset = io.reset + /* Queue Structure: + * | (v_0, entry_0) | (v_1, entry_1) | ... | (v_n, entry_n) | + * ^ ^ + * | | + * Deq Enq + */ private val valid = RegInit(VecInit(Seq.fill(parameter.entries) { false.B })) private val elts = Reg(Vec(parameter.entries, parameter.gen)) + /* Move entry_(n+1) -> entry_n, and enqueue the last entry. (The valid entry will be contingous.)*/ for (i <- 0 until parameter.entries) { def paddedValid(i: Int) = if (i == -1) true.B else if (i == parameter.entries) false.B else valid(i) @@ -80,7 +87,8 @@ class FetchQueue(val parameter: FetchQueueParameter) io.enq.ready := !valid(parameter.entries - 1) io.deq.valid := valid(0) io.deq.bits := elts.head - + + /* Dequeue the instruction that just enqueue */ when(io.enq.valid) { io.deq.valid := true.B } when(!valid(0)) { io.deq.bits := io.enq.bits }