Merge pull request #694 from riscv/trigger_hit

Look at trigger hit bits to see which trigger was hit.
This commit is contained in:
Tim Newsome 2022-05-02 09:40:07 -07:00 committed by GitHub
commit b6dddfacc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 8 deletions

View File

@ -56,7 +56,7 @@ struct watchpoint {
bool is_set; bool is_set;
unsigned int number; unsigned int number;
struct watchpoint *next; struct watchpoint *next;
int unique_id; uint32_t unique_id;
}; };
void breakpoint_clear_target(struct target *target); void breakpoint_clear_target(struct target *target);

View File

@ -1022,6 +1022,67 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK; 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 /* Sets *hit_watchpoint to the first watchpoint identified as causing the
* current halt. * current halt.
* *
@ -1030,14 +1091,19 @@ int riscv_remove_watchpoint(struct target *target,
* and new value. */ * and new value. */
int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint) 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)); LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
/*TODO instead of disassembling the instruction that we think caused the /* If we identified which trigger caused the halt earlier, then just use
* trigger, check the hit bit of each watchpoint first. The hit bit is * that. */
* simpler and more reliable to check but as it is optional and relatively for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) {
* new, not all hardware will implement it */ if (wp->unique_id == r->trigger_hit) {
*hit_watchpoint = wp;
return ERROR_OK;
}
}
riscv_reg_t dpc; riscv_reg_t dpc;
riscv_get_register(target, &dpc, GDB_REGNO_DPC); riscv_get_register(target, &dpc, GDB_REGNO_DPC);
const uint8_t length = 4; const uint8_t length = 4;
@ -1086,6 +1152,7 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
return ERROR_FAIL; return ERROR_FAIL;
} }
struct watchpoint *wp = target->watchpoints;
while (wp) { while (wp) {
/*TODO support length/mask */ /*TODO support length/mask */
if (wp->address == mem_addr) { if (wp->address == mem_addr) {
@ -1104,7 +1171,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
return ERROR_FAIL; return ERROR_FAIL;
} }
static int oldriscv_step(struct target *target, int current, uint32_t address, static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints) 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 */ /* 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) int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
{ {
RISCV_INFO(r);
r->trigger_hit = -1;
switch (halt_reason) { switch (halt_reason) {
case RISCV_HALT_BREAKPOINT: case RISCV_HALT_BREAKPOINT:
target->debug_reason = DBG_REASON_BREAKPOINT; target->debug_reason = DBG_REASON_BREAKPOINT;
break; break;
case RISCV_HALT_TRIGGER: 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; 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; break;
case RISCV_HALT_INTERRUPT: case RISCV_HALT_INTERRUPT:
case RISCV_HALT_GROUP: case RISCV_HALT_GROUP:

View File

@ -124,6 +124,10 @@ typedef struct {
* target controls, while otherwise only a single hart is controlled. */ * target controls, while otherwise only a single hart is controlled. */
int trigger_unique_id[RISCV_MAX_HWBPS]; 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. */ /* The number of entries in the debug buffer. */
int debug_buffer_size; int debug_buffer_size;
@ -216,7 +220,7 @@ typedef struct {
struct reg_data_type_union vector_union; struct reg_data_type_union vector_union;
struct reg_data_type type_vector; 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. */ * to beware that we may hit a trigger that we didn't realize had been set. */
bool manual_hwbp_set; bool manual_hwbp_set;