diff options
author | Tim Newsome <tim@sifive.com> | 2016-08-22 09:49:20 -0700 |
---|---|---|
committer | Tim Newsome <tim@sifive.com> | 2016-08-22 09:49:20 -0700 |
commit | d6dae451821656d7676fec3ec515691d4c66e065 (patch) | |
tree | ab8d1a8129710b380147cefc44e97c4dad28425b /riscv/execute.cc | |
parent | 5e1d0059353a4756740df709f85c35ee86138ad6 (diff) | |
download | spike-d6dae451821656d7676fec3ec515691d4c66e065.zip spike-d6dae451821656d7676fec3ec515691d4c66e065.tar.gz spike-d6dae451821656d7676fec3ec515691d4c66e065.tar.bz2 |
Implement address and data triggers.
So far I only have testcases for instruction and data address.
Not implemented is the mechanism that lets the debugger prevent a user
program from using triggers at all. I'll be adding that soonish.
The critical path is unchanged, but my experimenting shows the
simulation is slowed down about 8% by this code. Reducing the size of
trigger_match() (which is never called during my benchmark) fixes that,
but making it not be inlined has no effect. I suspect the slowdown comes
from cache alignment or something similar, and on a different CPU or
after more code changes the speed will come back.
Diffstat (limited to 'riscv/execute.cc')
-rw-r--r-- | riscv/execute.cc | 34 |
1 files changed, 32 insertions, 2 deletions
diff --git a/riscv/execute.cc b/riscv/execute.cc index 20567af..3d5625f 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -51,6 +51,13 @@ static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch) return npc; } +void processor_t::update_slow_path() +{ + slow_path = debug || state.single_step != state.STEP_NONE || state.dcsr.cause; + if (slow_path) + return; +} + // fetch/decode/execute loop void processor_t::step(size_t n) { @@ -90,8 +97,7 @@ void processor_t::step(size_t n) { take_interrupt(); - // When we might single step, use the slow loop instead of the fast one. - if (unlikely(debug || state.single_step != state.STEP_NONE || state.dcsr.cause)) + if (unlikely(slow_path)) { while (instret < n) { @@ -150,6 +156,30 @@ miss: take_trap(t, pc); n = instret; } + catch (trigger_matched_t& t) + { + if (mmu->matched_trigger) { + // This exception came from the MMU. That means the instruction hasn't + // fully executed yet. We start it again, but this time it won't throw + // an exception because matched_trigger is already set. (All memory + // instructions are idempotent so restarting is safe.) + + insn_fetch_t fetch = mmu->load_insn(pc); + pc = execute_insn(this, pc, fetch); + advance_pc(); + + delete mmu->matched_trigger; + mmu->matched_trigger = NULL; + } + assert(state.mcontrol[t.index].action != ACTION_NONE); + switch (state.mcontrol[t.index].action) { + case ACTION_DEBUG_MODE: + enter_debug_mode(DCSR_CAUSE_HWBP); + break; + default: + assert(0); + } + } state.minstret += instret; n -= instret; |