target/riscv: Add `riscv etrigger` command.
Change-Id: I7982231c5067b82e4ddb2999bca51dba06ccac7a Signed-off-by: Tim Newsome <tim@sifive.com>
This commit is contained in:
parent
0b022a349e
commit
6c027e0df4
|
@ -10723,6 +10723,21 @@ cleanly onto OpenOCD's notion of hardware breakpoints. These commands let you
|
||||||
set those triggers directly. (It's also possible to do so by writing the
|
set those triggers directly. (It's also possible to do so by writing the
|
||||||
appropriate CSRs.)
|
appropriate CSRs.)
|
||||||
|
|
||||||
|
@deffn {Command} {riscv etrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] exception_codes
|
||||||
|
Set an exception trigger (type 5) on the current target, which halts the target when it
|
||||||
|
fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
|
||||||
|
which execution modes the trigger fires in. @var{exception_codes} is a bit
|
||||||
|
field, where each bit corresponds to an exception code in mcause (defined in the
|
||||||
|
RISC-V Privileged Spec). The etrigger will fire on the exceptions whose bits are
|
||||||
|
set in @var{exception_codes}.
|
||||||
|
|
||||||
|
For details on this trigger type, see the RISC-V Debug Specification.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Command} {riscv etrigger clear}
|
||||||
|
Clear the type 5 trigger that was set using @command{riscv etrigger set}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@deffn {Command} {riscv itrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{nmi}] mie_bits
|
@deffn {Command} {riscv itrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{nmi}] mie_bits
|
||||||
Set an interrupt trigger (type 4) on the current target, which halts the target when it
|
Set an interrupt trigger (type 4) on the current target, which halts the target when it
|
||||||
fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
|
fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
|
||||||
|
|
|
@ -704,6 +704,37 @@ static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu,
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int maybe_add_trigger_t5(struct target *target, bool vs, bool vu,
|
||||||
|
bool m, bool s, bool u, riscv_reg_t exception_codes,
|
||||||
|
int unique_id)
|
||||||
|
{
|
||||||
|
int idx, ret;
|
||||||
|
riscv_reg_t tdata1, tdata2;
|
||||||
|
|
||||||
|
RISCV_INFO(r);
|
||||||
|
|
||||||
|
tdata1 = 0;
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ETRIGGER);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_DMODE(riscv_xlen(target)), 1);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_ACTION, CSR_ETRIGGER_ACTION_DEBUG_MODE);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_VS, vs);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_VU, vu);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_M, m);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_S, s);
|
||||||
|
tdata1 = set_field(tdata1, CSR_ETRIGGER_U, u);
|
||||||
|
|
||||||
|
tdata2 = exception_codes;
|
||||||
|
|
||||||
|
ret = find_trigger(target, CSR_TDATA1_TYPE_ETRIGGER, false, &idx);
|
||||||
|
if (ret != ERROR_OK)
|
||||||
|
return ret;
|
||||||
|
ret = set_trigger(target, idx, tdata1, tdata2, 0);
|
||||||
|
if (ret != ERROR_OK)
|
||||||
|
return ret;
|
||||||
|
r->trigger_unique_id[idx] = unique_id;
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int maybe_add_trigger_t6(struct target *target, struct trigger *trigger)
|
static int maybe_add_trigger_t6(struct target *target, struct trigger *trigger)
|
||||||
{
|
{
|
||||||
int idx, ret;
|
int idx, ret;
|
||||||
|
@ -1126,6 +1157,9 @@ static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id)
|
||||||
case CSR_TDATA1_TYPE_ITRIGGER:
|
case CSR_TDATA1_TYPE_ITRIGGER:
|
||||||
hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target));
|
hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target));
|
||||||
break;
|
break;
|
||||||
|
case CSR_TDATA1_TYPE_ETRIGGER:
|
||||||
|
hit_mask = CSR_ETRIGGER_HIT(riscv_xlen(target));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_DEBUG("trigger %d has unknown type %d", i, type);
|
LOG_DEBUG("trigger %d has unknown type %d", i, type);
|
||||||
continue;
|
continue;
|
||||||
|
@ -3294,6 +3328,78 @@ COMMAND_HANDLER(riscv_itrigger)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(riscv_etrigger)
|
||||||
|
{
|
||||||
|
if (CMD_ARGC < 1) {
|
||||||
|
LOG_ERROR("Command takes at least 1 parameter");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
const int ETRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ETRIGGER;
|
||||||
|
|
||||||
|
if (riscv_enumerate_triggers(target) != ERROR_OK)
|
||||||
|
return ERROR_FAIL;
|
||||||
|
|
||||||
|
if (!strcmp(CMD_ARGV[0], "set")) {
|
||||||
|
if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) >= 0) {
|
||||||
|
LOG_TARGET_ERROR(target, "An etrigger is already set, and OpenOCD "
|
||||||
|
"doesn't support setting more than one at a time.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
bool vs = false;
|
||||||
|
bool vu = false;
|
||||||
|
bool m = false;
|
||||||
|
bool s = false;
|
||||||
|
bool u = false;
|
||||||
|
riscv_reg_t exception_codes = 0;
|
||||||
|
|
||||||
|
for (unsigned int i = 1; i < CMD_ARGC; i++) {
|
||||||
|
if (!strcmp(CMD_ARGV[i], "vs"))
|
||||||
|
vs = true;
|
||||||
|
else if (!strcmp(CMD_ARGV[i], "vu"))
|
||||||
|
vu = true;
|
||||||
|
else if (!strcmp(CMD_ARGV[i], "m"))
|
||||||
|
m = true;
|
||||||
|
else if (!strcmp(CMD_ARGV[i], "s"))
|
||||||
|
s = true;
|
||||||
|
else if (!strcmp(CMD_ARGV[i], "u"))
|
||||||
|
u = true;
|
||||||
|
else
|
||||||
|
COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], exception_codes);
|
||||||
|
}
|
||||||
|
if (exception_codes == 0) {
|
||||||
|
LOG_ERROR("Doesn't make sense to set etrigger with "
|
||||||
|
"exception_codes=0.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
} else if (!vs && !vu && !m && !s && !u) {
|
||||||
|
LOG_ERROR("Doesn't make sense to set etrigger without at "
|
||||||
|
"least one of vs, vu, m, s, or u.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
int result = maybe_add_trigger_t5(target, vs, vu, m, s, u, exception_codes, ETRIGGER_UNIQUE_ID);
|
||||||
|
if (result != ERROR_OK)
|
||||||
|
LOG_TARGET_ERROR(target, "Failed to set requested etrigger.");
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} else if (!strcmp(CMD_ARGV[0], "clear")) {
|
||||||
|
if (CMD_ARGC != 1) {
|
||||||
|
LOG_ERROR("clear command takes no extra arguments.");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) < 0) {
|
||||||
|
LOG_TARGET_ERROR(target, "No etrigger is set. Nothing to clear.");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
return remove_trigger(target, ETRIGGER_UNIQUE_ID);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("First argument must be either 'set' or 'clear'.");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
COMMAND_HANDLER(handle_repeat_read)
|
COMMAND_HANDLER(handle_repeat_read)
|
||||||
{
|
{
|
||||||
struct target *target = get_current_target(CMD_CTX);
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
@ -3711,6 +3817,13 @@ static const struct command_registration riscv_exec_command_handlers[] = {
|
||||||
.help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions "
|
.help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions "
|
||||||
"don't trap to OpenOCD. Defaults to on."
|
"don't trap to OpenOCD. Defaults to on."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "etrigger",
|
||||||
|
.handler = riscv_etrigger,
|
||||||
|
.mode = COMMAND_EXEC,
|
||||||
|
.usage = "set [vs] [vu] [m] [s] [u] <exception_codes>|clear",
|
||||||
|
.help = "Set or clear a single exception trigger."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "itrigger",
|
.name = "itrigger",
|
||||||
.handler = riscv_itrigger,
|
.handler = riscv_itrigger,
|
||||||
|
@ -4265,6 +4378,10 @@ int riscv_enumerate_triggers(struct target *target)
|
||||||
if (tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)))
|
if (tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target)))
|
||||||
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||||
break;
|
break;
|
||||||
|
case CSR_TDATA1_TYPE_ETRIGGER:
|
||||||
|
if (tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target)))
|
||||||
|
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
r->trigger_tinfo[t] = 1 << type;
|
r->trigger_tinfo[t] = 1 << type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,7 @@ typedef struct {
|
||||||
/* For each physical trigger contains:
|
/* For each physical trigger contains:
|
||||||
* -1: the hwbp is available
|
* -1: the hwbp is available
|
||||||
* -4: The trigger is used by the itrigger command
|
* -4: The trigger is used by the itrigger command
|
||||||
|
* -5: The trigger is used by the etrigger command
|
||||||
* >= 0: unique_id of the breakpoint/watchpoint that is using it.
|
* >= 0: unique_id of the breakpoint/watchpoint that is using it.
|
||||||
* Note that in RTOS mode the triggers are the same across all harts the
|
* Note that in RTOS mode the triggers are the same across all harts the
|
||||||
* target controls, while otherwise only a single hart is controlled. */
|
* target controls, while otherwise only a single hart is controlled. */
|
||||||
|
|
Loading…
Reference in New Issue