diff --git a/README.md b/README.md index bdb81bb44..de1ac5f25 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Spike supports the following RISC-V ISA features: - Sdtrig extension, v1.0-STABLE - Smepmp extension v1.0 - Smstateen extension, v1.0 + - Smdbltrp extension, v1.0 - Sscofpmf v0.5.2 - Ssdbltrp extension, v1.0 - Ssqosid extension, v1.0 diff --git a/disasm/isa_parser.cc b/disasm/isa_parser.cc index 54dbf65a5..95ef8fb0b 100644 --- a/disasm/isa_parser.cc +++ b/disasm/isa_parser.cc @@ -355,6 +355,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) elen = std::max(elen, new_elen); } else if (ext_str == "ssdbltrp") { extension_table[EXT_SSDBLTRP] = true; + } else if (ext_str == "smdbltrp") { + extension_table[EXT_SMDBLTRP] = true; } else if (ext_str[0] == 'x') { extension_table['X'] = true; if (ext_str.size() == 1) { diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 6fdd6a38c..7ea1e4fa1 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -548,13 +548,17 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { | (has_page ? MSTATUS_TVM : 0) | (has_gva ? MSTATUS_GVA : 0) | (has_mpv ? MSTATUS_MPV : 0) + | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0) | (proc->extension_enabled(EXT_ZICFILP) ? (MSTATUS_SPELP | MSTATUS_MPELP) : 0) | (proc->extension_enabled(EXT_SSDBLTRP) ? SSTATUS_SDT : 0) ; const reg_t requested_mpp = proc->legalize_privilege(get_field(val, MSTATUS_MPP)); const reg_t adjusted_val = set_field(val, MSTATUS_MPP, requested_mpp); - const reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + if (new_mstatus & MSTATUS_MDT) { + new_mstatus = new_mstatus & ~MSTATUS_MIE; + } maybe_flush_tlb(new_mstatus); this->val = adjust_sd(new_mstatus); return true; @@ -569,6 +573,7 @@ reg_t mstatus_csr_t::compute_mstatus_initial_value() const noexcept { | (proc->extension_enabled_const('U') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_UXL, xlen_to_uxl(proc->get_const_xlen())) : 0) | (proc->extension_enabled_const('S') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_SXL, xlen_to_uxl(proc->get_const_xlen())) : 0) | (proc->get_mmu()->is_target_big_endian() ? big_endian_bits : 0) + | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0) | 0; // initial value for mstatus } @@ -1343,6 +1348,8 @@ dcsr_csr_t::dcsr_csr_t(processor_t* const proc, const reg_t addr): halt(false), v(false), cause(0), + ext_cause(0), + cetrig(0), pelp(elp_t::NO_LP_EXPECTED) { } @@ -1363,6 +1370,9 @@ reg_t dcsr_csr_t::read() const noexcept { result = set_field(result, DCSR_STOPCOUNT, 0); result = set_field(result, DCSR_STOPTIME, 0); result = set_field(result, DCSR_CAUSE, cause); + result = set_field(result, DCSR_EXTCAUSE, ext_cause); + if (proc->extension_enabled(EXT_SMDBLTRP)) + result = set_field(result, DCSR_CETRIG, cetrig); result = set_field(result, DCSR_STEP, step); result = set_field(result, DCSR_PRV, prv); result = set_field(result, CSR_DCSR_V, v); @@ -1382,12 +1392,14 @@ bool dcsr_csr_t::unlogged_write(const reg_t val) noexcept { v = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_V) : false; pelp = proc->extension_enabled(EXT_ZICFILP) ? static_cast(get_field(val, DCSR_PELP)) : elp_t::NO_LP_EXPECTED; + cetrig = proc->extension_enabled(EXT_SMDBLTRP) ? get_field(val, DCSR_CETRIG) : false; return true; } -void dcsr_csr_t::update_fields(const uint8_t cause, const reg_t prv, +void dcsr_csr_t::update_fields(const uint8_t cause, uint8_t ext_cause, const reg_t prv, const bool v, const elp_t pelp) noexcept { this->cause = cause; + this->ext_cause = ext_cause; this->prv = prv; this->v = v; this->pelp = pelp; diff --git a/riscv/csrs.h b/riscv/csrs.h index db61fba98..4055d8627 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -684,7 +684,7 @@ class dcsr_csr_t: public csr_t { dcsr_csr_t(processor_t* const proc, const reg_t addr); virtual void verify_permissions(insn_t insn, bool write) const override; virtual reg_t read() const noexcept override; - void update_fields(const uint8_t cause, const reg_t prv, + void update_fields(const uint8_t cause, const uint8_t ext_cause, const reg_t prv, const bool v, const elp_t pelp) noexcept; protected: virtual bool unlogged_write(const reg_t val) noexcept override; @@ -699,6 +699,8 @@ class dcsr_csr_t: public csr_t { bool halt; bool v; uint8_t cause; + uint8_t ext_cause; + bool cetrig; elp_t pelp; }; diff --git a/riscv/encoding.h b/riscv/encoding.h index 675b4f643..5f8eb7a69 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -4,7 +4,7 @@ /* * This file is auto-generated by running 'make' in - * https://github.com/riscv/riscv-opcodes (c55d30f) + * https://github.com/riscv/riscv-opcodes (048218e) */ #ifndef RISCV_CSR_ENCODING_H @@ -110,6 +110,9 @@ #define DCSR_CAUSE_STEP 4 #define DCSR_CAUSE_HALT 5 #define DCSR_CAUSE_GROUP 6 +#define DCSR_CAUSE_EXTCAUSE 7 + +#define DCSR_EXTCAUSE_CRITERR 0 #define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) #define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) @@ -117,7 +120,7 @@ #define MCONTROL_SELECT (1<<19) #define MCONTROL_TIMING (1<<18) -#define MCONTROL_ACTION (0x3f<<12) +#define MCONTROL_ACTION (0xf<<12) #define MCONTROL_CHAIN (1<<11) #define MCONTROL_MATCH (0xf<<7) #define MCONTROL_M (1<<6) diff --git a/riscv/execute.cc b/riscv/execute.cc index cc77d88ee..f4c88cafa 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -213,12 +213,11 @@ void processor_t::step(size_t n) { if (!state.debug_mode) { if (halt_request == HR_REGULAR) { - enter_debug_mode(DCSR_CAUSE_DEBUGINT); + enter_debug_mode(DCSR_CAUSE_DEBUGINT, 0); } else if (halt_request == HR_GROUP) { - enter_debug_mode(DCSR_CAUSE_GROUP); - } - else if (state.dcsr->halt) { - enter_debug_mode(DCSR_CAUSE_HALT); + enter_debug_mode(DCSR_CAUSE_GROUP, 0); + } else if (state.dcsr->halt) { + enter_debug_mode(DCSR_CAUSE_HALT, 0); } } @@ -257,7 +256,7 @@ void processor_t::step(size_t n) if (unlikely(!state.serialized && state.single_step == state.STEP_STEPPED)) { state.single_step = state.STEP_NONE; if (!state.debug_mode) { - enter_debug_mode(DCSR_CAUSE_STEP); + enter_debug_mode(DCSR_CAUSE_STEP, 0); // enter_debug_mode changed state.pc, so we can't just continue. break; } @@ -286,6 +285,17 @@ void processor_t::step(size_t n) disasm(fetch.insn); pc = execute_insn_logged(this, pc, fetch); advance_pc(); + + // Resume from debug mode in critical error + if (state.critical_error && !state.debug_mode) { + if (state.dcsr->read() & DCSR_CETRIG) { + enter_debug_mode(DCSR_CAUSE_EXTCAUSE, DCSR_EXTCAUSE_CRITERR); + } else { + // Handling of critical error is implementation defined + // For now just enter debug mode + enter_debug_mode(DCSR_CAUSE_HALT, 0); + } + } } } else while (instret < n) @@ -311,13 +321,23 @@ void processor_t::step(size_t n) take_trap(t, pc); n = instret; + // If critical error then enter debug mode critical error trigger enabled + if (state.critical_error) { + if (state.dcsr->read() & DCSR_CETRIG) { + enter_debug_mode(DCSR_CAUSE_EXTCAUSE, DCSR_EXTCAUSE_CRITERR); + } else { + // Handling of critical error is implementation defined + // For now just enter debug mode + enter_debug_mode(DCSR_CAUSE_HALT, 0); + } + } // Trigger action takes priority over single step auto match = TM.detect_trap_match(t); if (match.has_value()) take_trigger_action(match->action, 0, state.pc, 0); else if (unlikely(state.single_step == state.STEP_STEPPED)) { state.single_step = state.STEP_NONE; - enter_debug_mode(DCSR_CAUSE_STEP); + enter_debug_mode(DCSR_CAUSE_STEP, 0); } } catch (triggers::matched_t& t) @@ -330,7 +350,7 @@ void processor_t::step(size_t n) } catch(trap_debug_mode&) { - enter_debug_mode(DCSR_CAUSE_SWBP); + enter_debug_mode(DCSR_CAUSE_SWBP, 0); } catch (wait_for_interrupt_t &t) { diff --git a/riscv/insns/dret.h b/riscv/insns/dret.h index 60aaf217b..0f94f882a 100644 --- a/riscv/insns/dret.h +++ b/riscv/insns/dret.h @@ -4,8 +4,16 @@ if (ZICFILP_xLPE(STATE.dcsr->v, STATE.dcsr->prv)) { STATE.elp = STATE.dcsr->pelp; } p->set_privilege(STATE.dcsr->prv, STATE.dcsr->v); -if (STATE.prv < PRV_M) +if (STATE.prv < PRV_M) { STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MPRV); + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MDT); +} + +if (STATE.dcsr->prv == PRV_U || STATE.dcsr->v) + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_SDT); + +if (STATE.dcsr->v && STATE.dcsr->prv == PRV_U) + STATE.vsstatus->write(STATE.vsstatus->read() & ~SSTATUS_SDT); if (STATE.dcsr->prv == PRV_U || STATE.dcsr->v) STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_SDT); diff --git a/riscv/insns/mret.h b/riscv/insns/mret.h index 140ebde77..4172b75fb 100644 --- a/riscv/insns/mret.h +++ b/riscv/insns/mret.h @@ -13,6 +13,7 @@ if (ZICFILP_xLPE(prev_virt, prev_prv)) { STATE.elp = static_cast(get_field(s, MSTATUS_MPELP)); } s = set_field(s, MSTATUS_MPELP, elp_t::NO_LP_EXPECTED); +s = set_field(s, MSTATUS_MDT, 0); if (prev_prv == PRV_U || prev_virt) s = set_field(s, MSTATUS_SDT, 0); if (prev_virt && prev_prv == PRV_U) diff --git a/riscv/insns/sret.h b/riscv/insns/sret.h index fe007d328..efb4fa6d7 100644 --- a/riscv/insns/sret.h +++ b/riscv/insns/sret.h @@ -26,6 +26,15 @@ if (!STATE.v) { if (ZICFILP_xLPE(prev_virt, prev_prv)) { STATE.elp = static_cast(get_field(s, SSTATUS_SPELP)); } + +if (STATE.prv == PRV_M) { + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MDT); + if (prev_prv == PRV_U || prev_virt) + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_SDT); + if (prev_virt && prev_prv == PRV_U) + STATE.vsstatus->write(STATE.vsstatus->read() & ~SSTATUS_SDT); +} + s = set_field(s, SSTATUS_SPELP, elp_t::NO_LP_EXPECTED); if (STATE.prv == PRV_S) { diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index 45f637c64..12cc52ec1 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -82,6 +82,7 @@ typedef enum { EXT_ZICFILP, EXT_ZICFISS, EXT_SSDBLTRP, + EXT_SMDBLTRP, EXT_SMMPM, EXT_SMNPM, EXT_SSNPM, diff --git a/riscv/processor.cc b/riscv/processor.cc index d480f8f33..0b318f5e1 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -159,6 +159,8 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) elp = elp_t::NO_LP_EXPECTED; + critical_error = false; + csr_init(proc, max_isa); } @@ -379,11 +381,11 @@ const char* processor_t::get_privilege_string() abort(); } -void processor_t::enter_debug_mode(uint8_t cause) +void processor_t::enter_debug_mode(uint8_t cause, uint8_t extcause) { const bool has_zicfilp = extension_enabled(EXT_ZICFILP); state.debug_mode = true; - state.dcsr->update_fields(cause, state.prv, state.v, state.elp); + state.dcsr->update_fields(cause, extcause, state.prv, state.v, state.elp); state.elp = elp_t::NO_LP_EXPECTED; set_privilege(PRV_M, false); state.dpc->write(state.pc); @@ -500,10 +502,23 @@ void processor_t::take_trap(trap_t& t, reg_t epc) // Handle the trap in M-mode const reg_t vector = (state.mtvec->read() & 1) && interrupt ? 4 * bit : 0; const reg_t trap_handler_address = (state.mtvec->read() & ~(reg_t)1) + vector; - // RNMI exception vector is implementation-defined. Since we don't model // RNMI sources, the feature isn't very useful, so pick an invalid address. + // RNMI exception vector is implementation-defined. Since we don't model const reg_t rnmi_trap_handler_address = 0; const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE)); + + reg_t s = state.mstatus->read(); + if ( extension_enabled(EXT_SMDBLTRP)) { + if (get_field(s, MSTATUS_MDT) || !nmie) { + // Critical error - Double trap in M-mode or trap when nmie is 0 + // RNMI is not modeled else double trap in M-mode would trap to + // RNMI handler instead of leading to a critical error + state.critical_error = 1; + return; + } + s = set_field(s, MSTATUS_MDT, 1); + } + state.pc = !nmie ? rnmi_trap_handler_address : trap_handler_address; state.mepc->write(epc); state.mcause->write(supv_double_trap ? CAUSE_DOUBLE_TRAP : t.cause()); @@ -511,7 +526,6 @@ void processor_t::take_trap(trap_t& t, reg_t epc) state.mtval2->write(supv_double_trap ? t.cause() : t.get_tval2()); state.mtinst->write(t.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); @@ -537,7 +551,7 @@ void processor_t::take_trigger_action(triggers::action_t action, reg_t breakpoin switch (action) { case triggers::ACTION_DEBUG_MODE: - enter_debug_mode(DCSR_CAUSE_HWBP); + enter_debug_mode(DCSR_CAUSE_HWBP, 0); break; case triggers::ACTION_DEBUG_EXCEPTION: { trap_breakpoint trap(virt, breakpoint_tval); diff --git a/riscv/processor.h b/riscv/processor.h index df77fc6ac..7744e861a 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -190,6 +190,8 @@ struct state_t elp_t elp; + bool critical_error; + private: void csr_init(processor_t* const proc, reg_t max_isa); }; @@ -405,7 +407,7 @@ class processor_t : public abstract_device_t void register_insn(insn_desc_t, bool); int paddr_bits(); - void enter_debug_mode(uint8_t cause); + void enter_debug_mode(uint8_t cause, uint8_t ext_cause); void debug_output_log(std::stringstream *s); // either output to interactive user or write to log file