Skip to content

Commit

Permalink
target/riscv:Perform single step before resume if necessary
Browse files Browse the repository at this point in the history
Two cases where single step is needed before resuming:
1. ebreak used in software breakpoint;
2. a trigger that is taken just before the instruction
   that triggered it is retired.

Signed-off-by: Songhe Zhu <[email protected]> Co-developed-by: Fei Gao <[email protected]> Co-developed-by: xiatianyi <[email protected]>
  • Loading branch information
zhusonghe committed Oct 9, 2024
1 parent a4020f1 commit 83a97ca
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 7 deletions.
37 changes: 30 additions & 7 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -623,12 +623,12 @@ static int find_first_trigger_by_id(struct target *target, int unique_id)

static unsigned int count_trailing_ones(riscv_reg_t reg)
{
assert(sizeof(riscv_reg_t) * 8 == 64);
for (unsigned int i = 0; i < 64; i++) {
const unsigned int riscv_reg_bits = sizeof(riscv_reg_t) * CHAR_BIT;
for (unsigned int i = 0; i < riscv_reg_bits; i++) {
if ((1 & (reg >> i)) == 0)
return i;
}
return 64;
return riscv_reg_bits;
}

static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2)
Expand Down Expand Up @@ -668,6 +668,16 @@ static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdat
const uint32_t type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
const bool is_mcontrol = type == CSR_TDATA1_TYPE_MCONTROL;

if (type == CSR_TDATA1_TYPE_MCONTROL) {
if (get_field(tdata1_rb, CSR_MCONTROL_TIMING) == CSR_MCONTROL_TIMING_BEFORE)
r->halt_before_dpc = true;
} else if (type == CSR_TDATA1_TYPE_MCONTROL6) {
int hit0 = get_field(tdata1_rb, CSR_MCONTROL6_HIT0);
int hit1 = get_field(tdata1_rb, CSR_MCONTROL6_HIT1);
if (((hit1 << 1) | hit0) == CSR_MCONTROL6_HIT0_BEFORE)
r->halt_before_dpc = true;
}

/* Determine if tdata1 supports what we need.
* For mcontrol triggers, we don't care about
* the value in the read-only "maskmax" field.
Expand Down Expand Up @@ -2547,16 +2557,29 @@ static int resume_prep(struct target *target, int current,
assert(target->state == TARGET_HALTED);
RISCV_INFO(r);

riscv_reg_t dcsr;
if (riscv_reg_get(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
return ERROR_FAIL;

if (!current && riscv_reg_set(target, GDB_REGNO_PC, address) != ERROR_OK)
return ERROR_FAIL;

if (handle_breakpoints) {
/* To be able to run off a trigger, we perform a step operation and then
* resume. If handle_breakpoints is true then step temporarily disables
* pending breakpoints so we can safely perform the step. */
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
false /* callbacks are not called */) != ERROR_OK)
return ERROR_FAIL;
* pending breakpoints so we can safely perform the step.
*
* Two cases where single step is needed before resuming:
* 1. ebreak used in software breakpoint;
* 2. a trigger that is taken just before the instruction that triggered it is retired.
*/
if (get_field(dcsr, CSR_DCSR_CAUSE) == CSR_DCSR_CAUSE_EBREAK
|| (get_field(dcsr, CSR_DCSR_CAUSE) == CSR_DCSR_CAUSE_TRIGGER
&& r->halt_before_dpc)) {
if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
false /* callbacks are not called */) != ERROR_OK)
return ERROR_FAIL;
}
}

if (r->get_hart_state) {
Expand Down
3 changes: 3 additions & 0 deletions src/target/riscv/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ struct riscv_info {
/* record the tinfo of each trigger */
unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];

/* record the dpc that triggered it is retired. */
bool halt_before_dpc;

/* For each physical trigger contains:
* -1: the hwbp is available
* -4: The trigger is used by the itrigger command
Expand Down

0 comments on commit 83a97ca

Please sign in to comment.