diff --git a/README.md b/README.md index 2dec4a54..ec4783eb 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Spike supports the following RISC-V ISA features: - Svpbmt extension, v1.0 - Svinval extension, v1.0 - Debug v0.14 - - 4 triggers support type=2 (mcontrol) and type=15 (disabled) + - 4 triggers support type=2 (mcontrol), type=4 (itrigger), and type=15 (disabled) - Smepmp extension v1.0 - Smstateen extension, v1.0 - Sscofpmf v0.5.2 diff --git a/riscv/execute.cc b/riscv/execute.cc index 6583a14e..1426d2d9 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -301,7 +301,11 @@ void processor_t::step(size_t n) take_trap(t, pc); n = instret; - if (unlikely(state.single_step == state.STEP_STEPPED)) { + // Trigger action takes priority over single step + triggers::match_result_t match = TM.detect_trap_match(t); + if (match.fire) + take_trigger_action(match.action, 0, state.pc); + else if (unlikely(state.single_step == state.STEP_STEPPED)) { state.single_step = state.STEP_NONE; enter_debug_mode(DCSR_CAUSE_STEP); } diff --git a/riscv/triggers.cc b/riscv/triggers.cc index 8c8f849e..a51e7994 100644 --- a/riscv/triggers.cc +++ b/riscv/triggers.cc @@ -148,6 +148,62 @@ match_result_t mcontrol_t::memory_access_match(processor_t * const proc, operati return match_result_t(false); } +reg_t itrigger_t::tdata1_read(const processor_t * const proc) const noexcept +{ + auto xlen = proc->get_xlen(); + reg_t tdata1 = 0; + tdata1 = set_field(tdata1, CSR_ITRIGGER_TYPE(xlen), CSR_TDATA1_TYPE_ITRIGGER); + tdata1 = set_field(tdata1, CSR_ITRIGGER_DMODE(xlen), dmode); + tdata1 = set_field(tdata1, CSR_ITRIGGER_HIT(xlen), hit); + tdata1 = set_field(tdata1, CSR_ITRIGGER_VS, proc->extension_enabled('H') ? vs : 0); + tdata1 = set_field(tdata1, CSR_ITRIGGER_VU, proc->extension_enabled('H') ? vu : 0); + tdata1 = set_field(tdata1, CSR_ITRIGGER_NMI, nmi); + tdata1 = set_field(tdata1, CSR_ITRIGGER_M, m); + tdata1 = set_field(tdata1, CSR_ITRIGGER_S, s); + tdata1 = set_field(tdata1, CSR_ITRIGGER_U, u); + tdata1 = set_field(tdata1, CSR_ITRIGGER_ACTION, action); + return tdata1; +} + +void itrigger_t::tdata1_write(processor_t * const proc, const reg_t val, const bool UNUSED allow_chain) noexcept +{ + auto xlen = proc->get_xlen(); + assert(get_field(val, CSR_ITRIGGER_TYPE(xlen)) == CSR_TDATA1_TYPE_ITRIGGER); + dmode = get_field(val, CSR_ITRIGGER_DMODE(xlen)); + hit = get_field(val, CSR_ITRIGGER_HIT(xlen)); + vs = get_field(val, CSR_ITRIGGER_VS); + vu = get_field(val, CSR_ITRIGGER_VU); + nmi = get_field(val, CSR_ITRIGGER_NMI); + m = get_field(val, CSR_ITRIGGER_M); + s = proc->extension_enabled_const('S') ? get_field(val, CSR_ITRIGGER_S) : 0; + u = proc->extension_enabled_const('U') ? get_field(val, CSR_ITRIGGER_U) : 0; + action = (action_t)get_field(val, CSR_ITRIGGER_ACTION); + if (action > ACTION_MAXVAL || (action==ACTION_DEBUG_MODE && dmode==0)) + action = ACTION_DEBUG_EXCEPTION; +} + +match_result_t itrigger_t::detect_trap_match(processor_t * const proc, const trap_t& t) +{ + state_t * const state = proc->get_state(); + if ((state->prv == PRV_M && !m) || + (!state->v && state->prv == PRV_S && !s) || + (!state->v && state->prv == PRV_U && !u) || + (state->v && state->prv == PRV_S && !vs) || + (state->v && state->prv == PRV_U && !vu)) { + return match_result_t(false); + } + + auto xlen = proc->get_xlen(); + bool interrupt = (t.cause() & ((reg_t)1 << (xlen - 1))) != 0; + reg_t bit = t.cause() & ~((reg_t)1 << (xlen - 1)); + assert(bit < xlen); + if (interrupt && ((bit == 0 && nmi) || ((tdata2 >> bit) & 1))) { // Assume NMI's exception code is 0 + hit = true; + return match_result_t(true, TIMING_AFTER, action); + } + return match_result_t(false); +} + module_t::module_t(unsigned count) : triggers(count) { for (unsigned i = 0; i < count; i++) { triggers[i] = new disabled_trigger_t(); @@ -187,12 +243,14 @@ bool module_t::tdata1_write(processor_t * const proc, unsigned index, const reg_ // dmode only writable from debug mode if (!proc->get_state()->debug_mode) { assert(CSR_TDATA1_DMODE(xlen) == CSR_MCONTROL_DMODE(xlen)); + assert(CSR_TDATA1_DMODE(xlen) == CSR_ITRIGGER_DMODE(xlen)); tdata1 = set_field(tdata1, CSR_TDATA1_DMODE(xlen), 0); } delete triggers[index]; switch (type) { case CSR_TDATA1_TYPE_MCONTROL: triggers[index] = new mcontrol_t(); break; + case CSR_TDATA1_TYPE_ITRIGGER: triggers[index] = new itrigger_t(); break; default: triggers[index] = new disabled_trigger_t(); break; } @@ -246,10 +304,24 @@ match_result_t module_t::memory_access_match(operation_t operation, reg_t addres return match_result_t(false); } +match_result_t module_t::detect_trap_match(const trap_t& t) +{ + state_t * const state = proc->get_state(); + if (state->debug_mode) + return match_result_t(false); + + for (auto trigger: triggers) { + match_result_t result = trigger->detect_trap_match(proc, t); + if (result.fire) + return result; + } + return match_result_t(false); +} + reg_t module_t::tinfo_read(UNUSED const processor_t * const proc, unsigned UNUSED index) const noexcept { /* In spike, every trigger supports the same types. */ - return (1 << CSR_TDATA1_TYPE_MCONTROL) | (1 << CSR_TDATA1_TYPE_DISABLED); + return (1 << CSR_TDATA1_TYPE_MCONTROL) | (1 << CSR_TDATA1_TYPE_ITRIGGER) | (1 << CSR_TDATA1_TYPE_DISABLED); } }; diff --git a/riscv/triggers.h b/riscv/triggers.h index a8270ca4..e3dd5a63 100644 --- a/riscv/triggers.h +++ b/riscv/triggers.h @@ -69,6 +69,7 @@ public: virtual match_result_t memory_access_match(processor_t UNUSED * const proc, operation_t UNUSED operation, reg_t UNUSED address, std::optional UNUSED data) { return match_result_t(false); } + virtual match_result_t detect_trap_match(processor_t UNUSED * const proc, const trap_t UNUSED & t) { return match_result_t(false); } }; class trigger_with_tdata2_t : public trigger_t { @@ -91,6 +92,28 @@ private: bool dmode; }; +class itrigger_t : public trigger_with_tdata2_t { +public: + virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override; + virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; + + bool get_dmode() const override { return dmode; } + virtual action_t get_action() const override { return action; } + + virtual match_result_t detect_trap_match(processor_t * const proc, const trap_t& t) override; + +private: + bool dmode; + bool hit; + bool vs; + bool vu; + bool nmi; + bool m; + bool s; + bool u; + action_t action; +}; + class mcontrol_t : public trigger_with_tdata2_t { public: typedef enum @@ -148,6 +171,7 @@ public: unsigned count() const { return triggers.size(); } match_result_t memory_access_match(operation_t operation, reg_t address, std::optional data); + match_result_t detect_trap_match(const trap_t& t); processor_t *proc; private: