aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2022-05-02 09:40:07 -0700
committerGitHub <noreply@github.com>2022-05-02 09:40:07 -0700
commitb6dddfacc05ea981aabcf76ed155f68b677950a1 (patch)
tree13ccf722691719c29d0507e085ca280a71f6e164
parent9d737af35126c3a857e1c6a3356ab3879e92b6eb (diff)
parent1979ad5594ba1ebf1f5e0876f780c4c1c8f187cb (diff)
downloadriscv-openocd-b6dddfacc05ea981aabcf76ed155f68b677950a1.zip
riscv-openocd-b6dddfacc05ea981aabcf76ed155f68b677950a1.tar.gz
riscv-openocd-b6dddfacc05ea981aabcf76ed155f68b677950a1.tar.bz2
Merge pull request #694 from riscv/trigger_hit
Look at trigger hit bits to see which trigger was hit.
-rw-r--r--src/target/breakpoints.h2
-rw-r--r--src/target/riscv/riscv.c87
-rw-r--r--src/target/riscv/riscv.h6
3 files changed, 87 insertions, 8 deletions
diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h
index 0a59495..fc5b50b 100644
--- a/src/target/breakpoints.h
+++ b/src/target/breakpoints.h
@@ -56,7 +56,7 @@ struct watchpoint {
bool is_set;
unsigned int number;
struct watchpoint *next;
- int unique_id;
+ uint32_t unique_id;
};
void breakpoint_clear_target(struct target *target);
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index bde85b5..f1dde7c 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -1022,6 +1022,67 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK;
}
+/**
+ * Look at the trigger hit bits to find out which trigger is the reason we're
+ * halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is
+ * ~0, no match was found.
+ */
+static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id)
+{
+ RISCV_INFO(r);
+
+ riscv_reg_t tselect;
+ if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ return ERROR_FAIL;
+
+ *unique_id = ~0;
+ for (unsigned int i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == -1)
+ continue;
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, i) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t tdata1;
+ if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+ int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+
+ uint64_t hit_mask = 0;
+ switch (type) {
+ case 1:
+ /* Doesn't support hit bit. */
+ break;
+ case 2:
+ hit_mask = CSR_MCONTROL_HIT;
+ break;
+ case 6:
+ hit_mask = CSR_MCONTROL6_HIT;
+ break;
+ default:
+ LOG_DEBUG("trigger %d has unknown type %d", i, type);
+ continue;
+ }
+
+ /* Note: If we ever use chained triggers, then this logic needs
+ * to be changed to ignore triggers that are not the last one in
+ * the chain. */
+ if (tdata1 & hit_mask) {
+ LOG_DEBUG("Trigger %d (unique_id=%d) has hit bit set.", i, r->trigger_unique_id[i]);
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
+ return ERROR_FAIL;
+
+ *unique_id = r->trigger_unique_id[i];
+ break;
+ }
+ }
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
/* Sets *hit_watchpoint to the first watchpoint identified as causing the
* current halt.
*
@@ -1030,14 +1091,19 @@ int riscv_remove_watchpoint(struct target *target,
* and new value. */
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
{
- struct watchpoint *wp = target->watchpoints;
+ RISCV_INFO(r);
LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
- /*TODO instead of disassembling the instruction that we think caused the
- * trigger, check the hit bit of each watchpoint first. The hit bit is
- * simpler and more reliable to check but as it is optional and relatively
- * new, not all hardware will implement it */
+ /* If we identified which trigger caused the halt earlier, then just use
+ * that. */
+ for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) {
+ if (wp->unique_id == r->trigger_hit) {
+ *hit_watchpoint = wp;
+ return ERROR_OK;
+ }
+ }
+
riscv_reg_t dpc;
riscv_get_register(target, &dpc, GDB_REGNO_DPC);
const uint8_t length = 4;
@@ -1086,6 +1152,7 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
return ERROR_FAIL;
}
+ struct watchpoint *wp = target->watchpoints;
while (wp) {
/*TODO support length/mask */
if (wp->address == mem_addr) {
@@ -1104,7 +1171,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
return ERROR_FAIL;
}
-
static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints)
{
@@ -1198,12 +1264,21 @@ int riscv_flush_registers(struct target *target)
/* Convert: RISC-V hart's halt reason --> OpenOCD's generic debug reason */
int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
{
+ RISCV_INFO(r);
+ r->trigger_hit = -1;
switch (halt_reason) {
case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT;
break;
case RISCV_HALT_TRIGGER:
+ if (riscv_hit_trigger_hit_bit(target, &r->trigger_hit) != ERROR_OK)
+ return ERROR_FAIL;
target->debug_reason = DBG_REASON_WATCHPOINT;
+ /* Check if we hit a hardware breakpoint. */
+ for (struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) {
+ if (bp->unique_id == r->trigger_hit)
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ }
break;
case RISCV_HALT_INTERRUPT:
case RISCV_HALT_GROUP:
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index f3b7530..59fdb38 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -124,6 +124,10 @@ typedef struct {
* target controls, while otherwise only a single hart is controlled. */
int trigger_unique_id[RISCV_MAX_HWBPS];
+ /* The unique id of the trigger that caused the most recent halt. If the
+ * most recent halt was not caused by a trigger, then this is -1. */
+ uint32_t trigger_hit;
+
/* The number of entries in the debug buffer. */
int debug_buffer_size;
@@ -216,7 +220,7 @@ typedef struct {
struct reg_data_type_union vector_union;
struct reg_data_type type_vector;
- /* Set when trigger registers are changed by the user. This indicates we eed
+ /* Set when trigger registers are changed by the user. This indicates we need
* to beware that we may hit a trigger that we didn't realize had been set. */
bool manual_hwbp_set;