diff options
Diffstat (limited to 'riscv/triggers.cc')
-rw-r--r-- | riscv/triggers.cc | 108 |
1 files changed, 75 insertions, 33 deletions
diff --git a/riscv/triggers.cc b/riscv/triggers.cc index de3da40..9c21330 100644 --- a/riscv/triggers.cc +++ b/riscv/triggers.cc @@ -52,15 +52,57 @@ void trigger_t::tdata3_write(processor_t * const proc, const reg_t val) noexcept mhselect = get_field(val, CSR_TEXTRA_MHSELECT(xlen)); sbytemask = get_field(val, CSR_TEXTRA_SBYTEMASK(xlen)); svalue = proc->extension_enabled_const('S') ? get_field(val, CSR_TEXTRA_SVALUE(xlen)) : 0; - sselect = (sselect_t)((proc->extension_enabled_const('S') && get_field(val, CSR_TEXTRA_SSELECT(xlen)) <= SSELECT_MAXVAL) ? get_field(val, CSR_TEXTRA_SSELECT(xlen)) : SSELECT_IGNORE); + sselect = (sselect_t)((proc->extension_enabled_const('S') && get_field(val, CSR_TEXTRA_SSELECT(xlen)) <= SSELECT_MAXVAL) ? get_field(val, CSR_TEXTRA_SSELECT(xlen)) : (reg_t)SSELECT_IGNORE); +} + +static reg_t tcontrol_value(const state_t * state) { + if (state->tcontrol) + return state->tcontrol->read(); + else + return 0; } bool trigger_t::common_match(processor_t * const proc, bool use_prev_prv) const noexcept { auto state = proc->get_state(); auto prv = use_prev_prv ? state->prev_prv : state->prv; auto v = use_prev_prv ? state->prev_v : state->v; - auto m_enabled = get_action() != 0 || (state->tcontrol->read() & CSR_TCONTROL_MTE); - return (prv < PRV_M || m_enabled) && mode_match(prv, v) && textra_match(proc); + + if (!mode_match(prv, v)) + return false; + + if (!textra_match(proc)) + return false; + + if (get_action() == ACTION_DEBUG_EXCEPTION) { + if (proc->extension_enabled('S')) { + // The hardware prevents triggers with action=0 from matching or firing + // while in M-mode and while MIE in mstatus is 0. If medeleg [3]=1 then it + // prevents triggers with action=0 from matching or firing while in S-mode + // and while SIE in sstatus is 0. If medeleg [3]=1 and hedeleg [3]=1 then + // it prevents triggers with action=0 from matching or firing while in + // VS-mode and while SIE in vstatus is 0. + + const bool mstatus_mie = state->mstatus->read() & MSTATUS_MIE; + if (prv == PRV_M && !mstatus_mie) + return false; + + const bool sstatus_sie = state->sstatus->read() & MSTATUS_SIE; + const bool medeleg_breakpoint = (state->medeleg->read() >> CAUSE_BREAKPOINT) & 1; + if (prv == PRV_S && !v && medeleg_breakpoint && !sstatus_sie) + return false; + + const bool vsstatus_sie = state->vsstatus->read() & MSTATUS_SIE; + const bool hedeleg_breakpoint = (state->hedeleg->read() >> CAUSE_BREAKPOINT) & 1; + if (prv == PRV_S && v && medeleg_breakpoint && hedeleg_breakpoint && !vsstatus_sie) + return false; + } else { + // mte and mpte in tcontrol is implemented. medeleg [3] is hard-wired to 0. + if (prv == PRV_M && !(tcontrol_value(state) & CSR_TCONTROL_MTE)) + return false; + } + } + + return true; } bool trigger_t::mode_match(reg_t prv, bool v) const noexcept @@ -110,21 +152,6 @@ bool trigger_t::textra_match(processor_t * const proc) const noexcept return true; } -bool trigger_t::allow_action(const state_t * const state) const -{ - if (get_action() == ACTION_DEBUG_EXCEPTION) { - const bool mstatus_mie = state->mstatus->read() & MSTATUS_MIE; - const bool sstatus_sie = state->sstatus->read() & MSTATUS_SIE; - const bool vsstatus_sie = state->vsstatus->read() & MSTATUS_SIE; - const bool medeleg_breakpoint = (state->medeleg->read() >> CAUSE_BREAKPOINT) & 1; - const bool hedeleg_breakpoint = (state->hedeleg->read() >> CAUSE_BREAKPOINT) & 1; - return (state->prv != PRV_M || mstatus_mie) && - (state->prv != PRV_S || state->v || !medeleg_breakpoint || sstatus_sie) && - (state->prv != PRV_S || !state->v || !medeleg_breakpoint || !hedeleg_breakpoint || vsstatus_sie); - } - return true; -} - reg_t disabled_trigger_t::tdata1_read(const processor_t * const proc) const noexcept { auto xlen = proc->get_xlen(); @@ -146,7 +173,7 @@ reg_t mcontrol_t::tdata1_read(const processor_t * const proc) const noexcept { auto xlen = proc->get_xlen(); v = set_field(v, MCONTROL_TYPE(xlen), CSR_TDATA1_TYPE_MCONTROL); v = set_field(v, CSR_MCONTROL_DMODE(xlen), dmode); - v = set_field(v, MCONTROL_MASKMAX(xlen), 0); + v = set_field(v, MCONTROL_MASKMAX(xlen), maskmax); v = set_field(v, CSR_MCONTROL_HIT, hit); v = set_field(v, MCONTROL_SELECT, select); v = set_field(v, MCONTROL_TIMING, timing); @@ -171,7 +198,7 @@ void mcontrol_t::tdata1_write(processor_t * const proc, const reg_t val, const b timing = legalize_timing(val, MCONTROL_TIMING, MCONTROL_SELECT, MCONTROL_EXECUTE, MCONTROL_LOAD); action = legalize_action(val, MCONTROL_ACTION, CSR_MCONTROL_DMODE(xlen)); chain = allow_chain ? get_field(val, MCONTROL_CHAIN) : 0; - match = legalize_match(get_field(val, MCONTROL_MATCH)); + match = legalize_match(get_field(val, MCONTROL_MATCH), maskmax); m = get_field(val, MCONTROL_M); s = proc->extension_enabled_const('S') ? get_field(val, CSR_MCONTROL_S) : 0; u = proc->extension_enabled_const('U') ? get_field(val, CSR_MCONTROL_U) : 0; @@ -195,13 +222,17 @@ bool mcontrol_common_t::simple_match(unsigned xlen, reg_t value) const { return value < tdata2; case MATCH_MASK_LOW: { - reg_t mask = tdata2 >> (xlen/2); - return (value & mask) == (tdata2 & mask); + reg_t tdata2_high = tdata2 >> (xlen/2); + reg_t tdata2_low = tdata2 & ((reg_t(1) << (xlen/2)) - 1); + reg_t value_low = value & ((reg_t(1) << (xlen/2)) - 1); + return (value_low & tdata2_high) == tdata2_low; } case MATCH_MASK_HIGH: { - reg_t mask = tdata2 >> (xlen/2); - return ((value >> (xlen/2)) & mask) == (tdata2 & mask); + reg_t tdata2_high = tdata2 >> (xlen/2); + reg_t tdata2_low = tdata2 & ((reg_t(1) << (xlen/2)) - 1); + reg_t value_high = value >> (xlen/2); + return (value_high & tdata2_high) == tdata2_low; } } assert(0); @@ -231,7 +262,7 @@ std::optional<match_result_t> mcontrol_common_t::detect_memory_access_match(proc value &= 0xffffffff; } - if (simple_match(xlen, value) && allow_action(proc->get_state())) { + if (simple_match(xlen, value)) { /* This is OK because this function is only called if the trigger was not * inhibited by the previous trigger in the chain. */ set_hit(timing ? HIT_IMMEDIATELY_AFTER : HIT_BEFORE); @@ -240,11 +271,14 @@ std::optional<match_result_t> mcontrol_common_t::detect_memory_access_match(proc return std::nullopt; } -mcontrol_common_t::match_t mcontrol_common_t::legalize_match(reg_t val) noexcept +mcontrol_common_t::match_t mcontrol_common_t::legalize_match(reg_t val, reg_t maskmax) noexcept { switch (val) { - case MATCH_EQUAL: case MATCH_NAPOT: + if (maskmax == 0) + return MATCH_EQUAL; + [[fallthrough]]; + case MATCH_EQUAL: case MATCH_GE: case MATCH_LT: case MATCH_MASK_LOW: @@ -261,7 +295,14 @@ bool mcontrol_common_t::legalize_timing(reg_t val, reg_t timing_mask, reg_t sele return TIMING_AFTER; if (get_field(val, execute_mask)) return TIMING_BEFORE; - return get_field(val, timing_mask); + if (timing_mask) { + // Use the requested timing. + return get_field(val, timing_mask); + } else { + // For mcontrol6 you can't request a timing. Default to before since that's + // most useful to the user. + return TIMING_BEFORE; + } } reg_t mcontrol6_t::tdata1_read(const processor_t * const proc) const noexcept { @@ -290,13 +331,14 @@ void mcontrol6_t::tdata1_write(processor_t * const proc, const reg_t val, const auto xlen = proc->get_const_xlen(); assert(get_field(val, CSR_MCONTROL6_TYPE(xlen)) == CSR_TDATA1_TYPE_MCONTROL6); dmode = get_field(val, CSR_MCONTROL6_DMODE(xlen)); + const reg_t maskmax6 = xlen - 1; vs = get_field(val, CSR_MCONTROL6_VS); vu = get_field(val, CSR_MCONTROL6_VU); hit = hit_t(2 * get_field(val, CSR_MCONTROL6_HIT1) + get_field(val, CSR_MCONTROL6_HIT0)); // 2-bit field {hit1,hit0} select = get_field(val, CSR_MCONTROL6_SELECT); action = legalize_action(val, CSR_MCONTROL6_ACTION, CSR_MCONTROL6_DMODE(xlen)); chain = allow_chain ? get_field(val, CSR_MCONTROL6_CHAIN) : 0; - match = legalize_match(get_field(val, CSR_MCONTROL6_MATCH)); + match = legalize_match(get_field(val, CSR_MCONTROL6_MATCH), maskmax6); m = get_field(val, CSR_MCONTROL6_M); s = proc->extension_enabled_const('S') ? get_field(val, CSR_MCONTROL6_S) : 0; u = proc->extension_enabled_const('U') ? get_field(val, CSR_MCONTROL6_U) : 0; @@ -312,7 +354,7 @@ void mcontrol6_t::tdata1_write(processor_t * const proc, const reg_t val, const std::optional<match_result_t> icount_t::detect_icount_fire(processor_t * const proc) noexcept { - if (!common_match(proc) || !allow_action(proc->get_state())) + if (!common_match(proc)) return std::nullopt; std::optional<match_result_t> ret = std::nullopt; @@ -327,7 +369,7 @@ std::optional<match_result_t> icount_t::detect_icount_fire(processor_t * const p void icount_t::detect_icount_decrement(processor_t * const proc) noexcept { - if (!common_match(proc) || !allow_action(proc->get_state())) + if (!common_match(proc)) return; if (count >= 1) { @@ -419,7 +461,7 @@ std::optional<match_result_t> trap_common_t::detect_trap_match(processor_t * con bool interrupt = (t.cause() & ((reg_t)1 << (xlen - 1))) != 0; reg_t bit = t.cause() & ~((reg_t)1 << (xlen - 1)); assert(bit < xlen); - if (simple_match(interrupt, bit) && allow_action(proc->get_state())) { + if (simple_match(interrupt, bit)) { hit = true; return match_result_t(TIMING_AFTER, action); } @@ -636,4 +678,4 @@ reg_t module_t::tinfo_read(unsigned UNUSED index) const noexcept (CSR_TINFO_VERSION_1 << CSR_TINFO_VERSION_OFFSET); } -}; +} |