aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2016-09-02 12:37:38 -0700
committerTim Newsome <tim@sifive.com>2016-09-02 12:37:38 -0700
commit2b390a9dea7435c5e83c11433c05e136ad9b163a (patch)
tree1e61e6024ebce5290b99401c4d7d94f8d8b3a091
parente464ab8efb36fc089303f11ddf78e32a90530c43 (diff)
downloadspike-2b390a9dea7435c5e83c11433c05e136ad9b163a.zip
spike-2b390a9dea7435c5e83c11433c05e136ad9b163a.tar.gz
spike-2b390a9dea7435c5e83c11433c05e136ad9b163a.tar.bz2
Support triggers on TLB misses.
-rw-r--r--riscv/gdbserver.cc10
-rw-r--r--riscv/mmu.cc42
-rw-r--r--riscv/mmu.h3
3 files changed, 54 insertions, 1 deletions
diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc
index c30b6bc..481e5b9 100644
--- a/riscv/gdbserver.cc
+++ b/riscv/gdbserver.cc
@@ -459,6 +459,7 @@ class continue_op_t : public operation_t
operation_t(gdbserver), single_step(single_step) {};
bool perform_step(unsigned int step) {
+ D(fprintf(stderr, "continue step %d\n", step));
switch (step) {
case 0:
gs.dr_write_load(0, S0, SLOT_DATA0);
@@ -1104,7 +1105,14 @@ class hardware_breakpoint_insert_op_t : public operation_t
mcontrol = set_field(mcontrol, MCONTROL_EXECUTE, bp.execute);
mcontrol = set_field(mcontrol, MCONTROL_LOAD, bp.load);
mcontrol = set_field(mcontrol, MCONTROL_STORE, bp.store);
- if (bp.load)
+ // For store triggers it's nicer to fire just before the
+ // instruction than just after. However, gdb doesn't clear the
+ // breakpoints and step before resuming from a store trigger.
+ // That means that without extra code, you'll keep hitting the
+ // same watchpoint over and over again. That's not useful at all.
+ // Instead of fixing this the right way, just set timing=1 for
+ // those triggers.
+ if (bp.load || bp.store)
mcontrol = set_field(mcontrol, MCONTROL_TIMING, 1);
gs.dr_write(SLOT_DATA1, mcontrol);
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index 0b60713..878d849 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -73,9 +73,36 @@ const uint16_t* mmu_t::fetch_slow_path(reg_t vaddr)
}
}
+reg_t reg_from_bytes(size_t len, const uint8_t* bytes)
+{
+ switch (len) {
+ case 1:
+ return bytes[0];
+ case 2:
+ return bytes[0] |
+ (((reg_t) bytes[1]) << 8);
+ case 4:
+ return bytes[0] |
+ (((reg_t) bytes[1]) << 8) |
+ (((reg_t) bytes[2]) << 16) |
+ (((reg_t) bytes[3]) << 24);
+ case 8:
+ return bytes[0] |
+ (((reg_t) bytes[1]) << 8) |
+ (((reg_t) bytes[2]) << 16) |
+ (((reg_t) bytes[3]) << 24) |
+ (((reg_t) bytes[4]) << 32) |
+ (((reg_t) bytes[5]) << 40) |
+ (((reg_t) bytes[6]) << 48) |
+ (((reg_t) bytes[7]) << 56);
+ }
+ abort();
+}
+
void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
{
reg_t paddr = translate(addr, LOAD);
+
if (sim->addr_is_mem(paddr)) {
memcpy(bytes, sim->addr_to_mem(paddr), len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD))
@@ -85,11 +112,26 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes)
} else if (!sim->mmio_load(paddr, len, bytes)) {
throw trap_load_access_fault(addr);
}
+
+ if (!matched_trigger) {
+ reg_t data = reg_from_bytes(len, bytes);
+ matched_trigger = trigger_exception(OPERATION_LOAD, addr, data);
+ if (matched_trigger)
+ throw *matched_trigger;
+ }
}
void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes)
{
reg_t paddr = translate(addr, STORE);
+
+ if (!matched_trigger) {
+ reg_t data = reg_from_bytes(len, bytes);
+ matched_trigger = trigger_exception(OPERATION_STORE, addr, data);
+ if (matched_trigger)
+ throw *matched_trigger;
+ }
+
if (sim->addr_is_mem(paddr)) {
memcpy(sim->addr_to_mem(paddr), bytes, len);
if (tracer.interested_in_range(paddr, paddr + PGSIZE, STORE))
diff --git a/riscv/mmu.h b/riscv/mmu.h
index 3da2c92..1f8d34b 100644
--- a/riscv/mmu.h
+++ b/riscv/mmu.h
@@ -219,6 +219,9 @@ private:
inline trigger_matched_t *trigger_exception(trigger_operation_t operation,
reg_t address, reg_t data)
{
+ if (!proc) {
+ return NULL;
+ }
int match = proc->trigger_match(operation, address, data);
if (match == -1)
return NULL;