diff options
author | Scott Johnson <scott.johnson@arilinc.com> | 2023-01-30 12:01:45 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-30 12:01:45 -0600 |
commit | 06ab1fe13a8e3f8a6cc9fe219646d9d004f1b680 (patch) | |
tree | bf391a0e5df6bc3a9c445f10f989009bd732483a | |
parent | f9e7b8f5c06d87498ce1837ac192293e6b7c61e6 (diff) | |
parent | c6d6a9459ffcbee096c63f7141167303e81e17de (diff) | |
download | spike-06ab1fe13a8e3f8a6cc9fe219646d9d004f1b680.zip spike-06ab1fe13a8e3f8a6cc9fe219646d9d004f1b680.tar.gz spike-06ab1fe13a8e3f8a6cc9fe219646d9d004f1b680.tar.bz2 |
Merge pull request #1220 from YenHaoChen/pr-icount
Add icount trigger
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | riscv/execute.cc | 10 | ||||
-rw-r--r-- | riscv/processor.cc | 6 | ||||
-rw-r--r-- | riscv/processor.h | 1 | ||||
-rw-r--r-- | riscv/triggers.cc | 97 | ||||
-rw-r--r-- | riscv/triggers.h | 24 |
6 files changed, 130 insertions, 10 deletions
@@ -39,7 +39,7 @@ Spike supports the following RISC-V ISA features: - Svinval extension, v1.0 - Sdext extension, v1.0-STABLE - Sdtrig extension, v1.0-STABLE - - 4 triggers support type=2 (mcontrol), type=4 (itrigger), type=5 (etrigger), type=6 (mcontrol6), and type=15 (disabled) + - 4 triggers support type={2, 3, 4, 5, 6, 15} (mcontrol, icount, itrigger, etrigger, mcontrol6, disabled) - Smepmp extension v1.0 - Smstateen extension, v1.0 - Sscofpmf v0.5.2 diff --git a/riscv/execute.cc b/riscv/execute.cc index b093e72..d244419 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -204,7 +204,7 @@ static inline reg_t execute_insn_logged(processor_t* p, reg_t pc, insn_fetch_t f bool processor_t::slow_path() { return debug || state.single_step != state.STEP_NONE || state.debug_mode || - log_commits_enabled || histogram_enabled || in_wfi; + log_commits_enabled || histogram_enabled || in_wfi || check_triggers_icount; } // fetch/decode/execute loop @@ -262,6 +262,14 @@ void processor_t::step(size_t n) state.single_step = state.STEP_STEPPED; } + if (!state.serialized) { + auto match = TM.detect_icount_match(); + if (match.has_value()) { + assert(match->timing == triggers::TIMING_BEFORE); + throw triggers::matched_t((triggers::operation_t)0, 0, match->action); + } + } + // debug mode wfis must nop if (unlikely(in_wfi && !state.debug_mode)) { throw wait_for_interrupt_t(); diff --git a/riscv/processor.cc b/riscv/processor.cc index 81ae0ce..d55d218 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -34,7 +34,7 @@ processor_t::processor_t(const isa_parser_t *isa, const cfg_t *cfg, : debug(false), halt_request(HR_NONE), isa(isa), cfg(cfg), sim(sim), id(id), xlen(0), histogram_enabled(false), log_commits_enabled(false), log_file(log_file), sout_(sout_.rdbuf()), halt_on_reset(halt_on_reset), - in_wfi(false), + in_wfi(false), check_triggers_icount(false), impl_table(256, false), last_pc(1), executions(1), TM(cfg->trigger_count) { VU.p = this; @@ -1123,6 +1123,7 @@ void processor_t::trigger_updated(const std::vector<triggers::trigger_t *> &trig mmu->check_triggers_fetch = false; mmu->check_triggers_load = false; mmu->check_triggers_store = false; + check_triggers_icount = false; for (auto trigger : triggers) { if (trigger->get_execute()) { @@ -1134,5 +1135,8 @@ void processor_t::trigger_updated(const std::vector<triggers::trigger_t *> &trig if (trigger->get_store()) { mmu->check_triggers_store = true; } + if (trigger->icount_check_needed()) { + check_triggers_icount = true; + } } } diff --git a/riscv/processor.h b/riscv/processor.h index 7c4c24f..3aa8372 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -301,6 +301,7 @@ private: std::ostream sout_; // needed for socket command interface -s, also used for -d and -l, but not for --log bool halt_on_reset; bool in_wfi; + bool check_triggers_icount; std::vector<bool> impl_table; std::vector<insn_desc_t> instructions; diff --git a/riscv/triggers.cc b/riscv/triggers.cc index 64de725..1ba7a62 100644 --- a/riscv/triggers.cc +++ b/riscv/triggers.cc @@ -248,7 +248,7 @@ bool mcontrol_common_t::legalize_timing(reg_t val, reg_t timing_mask, reg_t sele reg_t mcontrol6_t::tdata1_read(const processor_t * const proc) const noexcept { unsigned xlen = proc->get_const_xlen(); reg_t tdata1 = 0; - tdata1 = set_field(tdata1, CSR_MCONTROL6_TYPE(xlen), 6); + tdata1 = set_field(tdata1, CSR_MCONTROL6_TYPE(xlen), CSR_TDATA1_TYPE_MCONTROL6); tdata1 = set_field(tdata1, CSR_MCONTROL6_DMODE(xlen), dmode); tdata1 = set_field(tdata1, CSR_MCONTROL6_VS, proc->extension_enabled('H') ? vs : 0); tdata1 = set_field(tdata1, CSR_MCONTROL6_VU, proc->extension_enabled('H') ? vu : 0); @@ -287,6 +287,67 @@ void mcontrol6_t::tdata1_write(processor_t * const proc, const reg_t val, const load = get_field(val, CSR_MCONTROL6_LOAD); } +std::optional<match_result_t> icount_t::detect_icount_match(processor_t * const proc) noexcept +{ + if (!common_match(proc)) + return std::nullopt; + + std::optional<match_result_t> ret = std::nullopt; + if (pending) { + pending = 0; + hit = true; + ret = match_result_t(TIMING_BEFORE, action); + } + + if (count >= 1) { + if (count == 1) + pending = 1; + count = count - 1; + } + + return ret; +} + +reg_t icount_t::tdata1_read(const processor_t * const proc) const noexcept +{ + auto xlen = proc->get_xlen(); + reg_t tdata1 = 0; + tdata1 = set_field(tdata1, CSR_ICOUNT_TYPE(xlen), CSR_TDATA1_TYPE_ICOUNT); + tdata1 = set_field(tdata1, CSR_ICOUNT_DMODE(xlen), dmode); + tdata1 = set_field(tdata1, CSR_ICOUNT_VS, proc->extension_enabled('H') ? vs : 0); + tdata1 = set_field(tdata1, CSR_ICOUNT_VU, proc->extension_enabled('H') ? vu : 0); + tdata1 = set_field(tdata1, CSR_ICOUNT_HIT, hit); + tdata1 = set_field(tdata1, CSR_ICOUNT_COUNT, count_read_value); + tdata1 = set_field(tdata1, CSR_ICOUNT_M, m); + tdata1 = set_field(tdata1, CSR_ICOUNT_PENDING, pending_read_value); + tdata1 = set_field(tdata1, CSR_ICOUNT_S, s); + tdata1 = set_field(tdata1, CSR_ICOUNT_U, u); + tdata1 = set_field(tdata1, CSR_ICOUNT_ACTION, action); + return tdata1; +} + +void icount_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_ICOUNT_TYPE(xlen)) == CSR_TDATA1_TYPE_ICOUNT); + dmode = proc->get_state()->debug_mode ? get_field(val, CSR_ICOUNT_DMODE(xlen)) : 0; + vs = get_field(val, CSR_ICOUNT_VS); + vu = get_field(val, CSR_ICOUNT_VU); + hit = get_field(val, CSR_ICOUNT_HIT); + count = count_read_value = get_field(val, CSR_ICOUNT_COUNT); + m = get_field(val, CSR_ICOUNT_M); + pending = pending_read_value = get_field(val, CSR_ICOUNT_PENDING); + s = proc->extension_enabled_const('S') ? get_field(val, CSR_ICOUNT_S) : 0; + u = proc->extension_enabled_const('U') ? get_field(val, CSR_ICOUNT_U) : 0; + action = legalize_action(val, CSR_ICOUNT_ACTION, CSR_ICOUNT_DMODE(xlen)); +} + +void icount_t::stash_read_values() +{ + count_read_value = count; + pending_read_value = pending; +} + reg_t itrigger_t::tdata1_read(const processor_t * const proc) const noexcept { auto xlen = proc->get_xlen(); @@ -423,6 +484,7 @@ bool module_t::tdata1_write(unsigned index, const reg_t val) noexcept delete triggers[index]; switch (type) { case CSR_TDATA1_TYPE_MCONTROL: triggers[index] = new mcontrol_t(); break; + case CSR_TDATA1_TYPE_ICOUNT: triggers[index] = new icount_t(); break; case CSR_TDATA1_TYPE_ITRIGGER: triggers[index] = new itrigger_t(); break; case CSR_TDATA1_TYPE_ETRIGGER: triggers[index] = new etrigger_t(); break; case CSR_TDATA1_TYPE_MCONTROL6: triggers[index] = new mcontrol6_t(); break; @@ -474,6 +536,7 @@ std::optional<match_result_t> module_t::detect_memory_access_match(operation_t o bool chain_ok = true; + std::optional<match_result_t> ret = std::nullopt; for (auto trigger: triggers) { if (!chain_ok) { chain_ok = !trigger->get_chain(); @@ -487,12 +550,30 @@ std::optional<match_result_t> module_t::detect_memory_access_match(operation_t o * trigger in the chain will never get `hit` set unless the entire chain * matches. */ auto result = trigger->detect_memory_access_match(proc, operation, address, data); - if (result.has_value() && !trigger->get_chain()) - return result; + if (result.has_value() && !trigger->get_chain() && (!ret.has_value() || ret->action < result->action)) + ret = result; chain_ok = result.has_value() || !trigger->get_chain(); } - return std::nullopt; + return ret; +} + +std::optional<match_result_t> module_t::detect_icount_match() noexcept +{ + for (auto trigger: triggers) + trigger->stash_read_values(); + + state_t * const state = proc->get_state(); + if (state->debug_mode) + return std::nullopt; + + std::optional<match_result_t> ret = std::nullopt; + for (auto trigger: triggers) { + auto result = trigger->detect_icount_match(proc); + if (result.has_value() && (!ret.has_value() || ret->action < result->action)) + ret = result; + } + return ret; } std::optional<match_result_t> module_t::detect_trap_match(const trap_t& t) noexcept @@ -501,18 +582,20 @@ std::optional<match_result_t> module_t::detect_trap_match(const trap_t& t) noexc if (state->debug_mode) return std::nullopt; + std::optional<match_result_t> ret = std::nullopt; for (auto trigger: triggers) { auto result = trigger->detect_trap_match(proc, t); - if (result.has_value()) - return result; + if (result.has_value() && (!ret.has_value() || ret->action < result->action)) + ret = result; } - return std::nullopt; + return ret; } reg_t module_t::tinfo_read(unsigned UNUSED index) const noexcept { /* In spike, every trigger supports the same types. */ return (1 << CSR_TDATA1_TYPE_MCONTROL) | + (1 << CSR_TDATA1_TYPE_ICOUNT) | (1 << CSR_TDATA1_TYPE_ITRIGGER) | (1 << CSR_TDATA1_TYPE_ETRIGGER) | (1 << CSR_TDATA1_TYPE_MCONTROL6) | diff --git a/riscv/triggers.h b/riscv/triggers.h index bc64c4d..199a3c2 100644 --- a/riscv/triggers.h +++ b/riscv/triggers.h @@ -79,9 +79,12 @@ public: virtual bool get_store() const { return false; } virtual bool get_load() const { return false; } virtual action_t get_action() const { return ACTION_DEBUG_EXCEPTION; } + virtual bool icount_check_needed() const { return false; } + virtual void stash_read_values() {} virtual std::optional<match_result_t> detect_memory_access_match(processor_t UNUSED * const proc, operation_t UNUSED operation, reg_t UNUSED address, std::optional<reg_t> UNUSED data) noexcept { return std::nullopt; } + virtual std::optional<match_result_t> detect_icount_match(processor_t UNUSED * const proc) { return std::nullopt; } virtual std::optional<match_result_t> detect_trap_match(processor_t UNUSED * const proc, const trap_t UNUSED & t) noexcept { return std::nullopt; } protected: @@ -233,6 +236,26 @@ public: virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override; }; +class icount_t : public trigger_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 bool icount_check_needed() const override { return count > 0 || pending; } + virtual void stash_read_values() override; + + virtual std::optional<match_result_t> detect_icount_match(processor_t * const proc) noexcept override; + +private: + bool dmode; + bool hit; + unsigned count, count_read_value; + bool pending, pending_read_value; + action_t action; +}; + class module_t { public: module_t(unsigned count); @@ -249,6 +272,7 @@ public: unsigned count() const { return triggers.size(); } std::optional<match_result_t> detect_memory_access_match(operation_t operation, reg_t address, std::optional<reg_t> data) noexcept; + std::optional<match_result_t> detect_icount_match() noexcept; std::optional<match_result_t> detect_trap_match(const trap_t& t) noexcept; processor_t *proc; |