Add `riscv icount` command.

Also refactor shared code for clearing itrigger/etrigger/icount.

Change-Id: Iac2e756332c89d2ed43435391e3c097abc825255
Signed-off-by: Tim Newsome <tim@sifive.com>
This commit is contained in:
Tim Newsome 2023-02-13 13:38:34 -08:00
parent 2b4826cd32
commit fb3376b7f0
2 changed files with 144 additions and 16 deletions

View File

@ -10738,6 +10738,23 @@ For details on this trigger type, see the RISC-V Debug Specification.
Clear the type 5 trigger that was set using @command{riscv etrigger set}. Clear the type 5 trigger that was set using @command{riscv etrigger set}.
@end deffn @end deffn
@deffn {Command} {riscv icount set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{pending}] count
Set an instruction count
trigger (type 3) 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. If [@option{pending}] is passed then the
pending bit is set, which is unlikely to be useful unless you're debugging the
hardware implementation of this trigger.
@var{count} sets the number of instructions to execute before the trigger is
taken.
For details on this trigger type, see the RISC-V Debug Specification.
@end deffn
@deffn {Command} {riscv icount clear}
Clear the type 3 trigger that was set using @command{riscv icount 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

View File

@ -684,6 +684,38 @@ MATCH_EQUAL:
return ERROR_OK; return ERROR_OK;
} }
static int maybe_add_trigger_t3(struct target *target, bool vs, bool vu,
bool m, bool s, bool u, bool pending, unsigned int count,
int unique_id)
{
int ret;
riscv_reg_t tdata1;
RISCV_INFO(r);
tdata1 = 0;
tdata1 = set_field(tdata1, CSR_ICOUNT_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ICOUNT);
tdata1 = set_field(tdata1, CSR_ICOUNT_DMODE(riscv_xlen(target)), 1);
tdata1 = set_field(tdata1, CSR_ICOUNT_ACTION, CSR_ICOUNT_ACTION_DEBUG_MODE);
tdata1 = set_field(tdata1, CSR_ICOUNT_VS, vs);
tdata1 = set_field(tdata1, CSR_ICOUNT_VU, vu);
tdata1 = set_field(tdata1, CSR_ICOUNT_PENDING, pending);
tdata1 = set_field(tdata1, CSR_ICOUNT_M, m);
tdata1 = set_field(tdata1, CSR_ICOUNT_S, s);
tdata1 = set_field(tdata1, CSR_ICOUNT_U, u);
tdata1 = set_field(tdata1, CSR_ICOUNT_COUNT, count);
unsigned int idx;
ret = find_trigger(target, CSR_TDATA1_TYPE_ICOUNT, false, &idx);
if (ret != ERROR_OK)
return ret;
ret = set_trigger(target, idx, tdata1, 0, CSR_MCONTROL_MASKMAX(riscv_xlen(target)));
if (ret != ERROR_OK)
return ret;
r->trigger_unique_id[idx] = unique_id;
return ERROR_OK;
}
static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu, static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu,
bool nmi, bool m, bool s, bool u, riscv_reg_t interrupts, bool nmi, bool m, bool s, bool u, riscv_reg_t interrupts,
int unique_id) int unique_id)
@ -1169,6 +1201,9 @@ static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id)
case CSR_TDATA1_TYPE_MCONTROL6: case CSR_TDATA1_TYPE_MCONTROL6:
hit_mask = CSR_MCONTROL6_HIT; hit_mask = CSR_MCONTROL6_HIT;
break; break;
case CSR_TDATA1_TYPE_ICOUNT:
hit_mask = CSR_ICOUNT_HIT;
break;
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;
@ -3239,6 +3274,20 @@ COMMAND_HANDLER(riscv_set_ebreaku)
return ERROR_OK; return ERROR_OK;
} }
COMMAND_HELPER(riscv_clear_trigger, int trigger_id, const char *name)
{
struct target *target = get_current_target(CMD_CTX);
if (CMD_ARGC != 1) {
LOG_ERROR("clear command takes no extra arguments.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (find_first_trigger_by_id(target, trigger_id) < 0) {
LOG_TARGET_ERROR(target, "No %s is set. Nothing to clear.", name);
return ERROR_FAIL;
}
return remove_trigger(target, trigger_id);
}
COMMAND_HANDLER(riscv_itrigger) COMMAND_HANDLER(riscv_itrigger)
{ {
if (CMD_ARGC < 1) { if (CMD_ARGC < 1) {
@ -3297,15 +3346,74 @@ COMMAND_HANDLER(riscv_itrigger)
return result; return result;
} else if (!strcmp(CMD_ARGV[0], "clear")) { } else if (!strcmp(CMD_ARGV[0], "clear")) {
if (CMD_ARGC != 1) { return riscv_clear_trigger(CMD, ITRIGGER_UNIQUE_ID, "itrigger");
LOG_ERROR("clear command takes no extra arguments.");
} else {
LOG_ERROR("First argument must be either 'set' or 'clear'.");
return ERROR_COMMAND_SYNTAX_ERROR; return ERROR_COMMAND_SYNTAX_ERROR;
} }
if (find_first_trigger_by_id(target, ITRIGGER_UNIQUE_ID) < 0) { return ERROR_OK;
LOG_TARGET_ERROR(target, "No itrigger is set. Nothing to clear."); }
COMMAND_HANDLER(riscv_icount)
{
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 ICOUNT_UNIQUE_ID = -CSR_TDATA1_TYPE_ICOUNT;
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
if (!strcmp(CMD_ARGV[0], "set")) {
if (find_first_trigger_by_id(target, ICOUNT_UNIQUE_ID) >= 0) {
LOG_TARGET_ERROR(target, "An icount trigger is already set, and OpenOCD "
"doesn't support setting more than one at a time.");
return ERROR_FAIL; return ERROR_FAIL;
} }
return remove_trigger(target, ITRIGGER_UNIQUE_ID); bool vs = false;
bool vu = false;
bool m = false;
bool s = false;
bool u = false;
bool pending = false;
unsigned int count = 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], "pending"))
pending = 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(uint, CMD_ARGV[i], count);
}
if (count == 0) {
LOG_ERROR("Doesn't make sense to set icount trigger with "
"count=0.");
return ERROR_FAIL;
} else if (!vs && !vu && !m && !s && !u) {
LOG_ERROR("Doesn't make sense to set itrigger without at "
"least one of vs, vu, m, s, or u.");
return ERROR_FAIL;
}
int result = maybe_add_trigger_t3(target, vs, vu, m, s, u, pending, count, ICOUNT_UNIQUE_ID);
if (result != ERROR_OK)
LOG_TARGET_ERROR(target, "Failed to set requested icount trigger.");
return result;
} else if (!strcmp(CMD_ARGV[0], "clear")) {
return riscv_clear_trigger(CMD, ICOUNT_UNIQUE_ID, "icount trigger");
} else { } else {
LOG_ERROR("First argument must be either 'set' or 'clear'."); LOG_ERROR("First argument must be either 'set' or 'clear'.");
@ -3369,15 +3477,7 @@ COMMAND_HANDLER(riscv_etrigger)
return result; return result;
} else if (!strcmp(CMD_ARGV[0], "clear")) { } else if (!strcmp(CMD_ARGV[0], "clear")) {
if (CMD_ARGC != 1) { return riscv_clear_trigger(CMD, ETRIGGER_UNIQUE_ID, "etrigger");
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 { } else {
LOG_ERROR("First argument must be either 'set' or 'clear'."); LOG_ERROR("First argument must be either 'set' or 'clear'.");
@ -3797,6 +3897,13 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.usage = "set [vs] [vu] [m] [s] [u] <exception_codes>|clear", .usage = "set [vs] [vu] [m] [s] [u] <exception_codes>|clear",
.help = "Set or clear a single exception trigger." .help = "Set or clear a single exception trigger."
}, },
{
.name = "icount",
.handler = riscv_icount,
.mode = COMMAND_EXEC,
.usage = "set [vs] [vu] [m] [s] [u] [pending] <count>|clear",
.help = "Set or clear a single instruction count trigger."
},
{ {
.name = "itrigger", .name = "itrigger",
.handler = riscv_itrigger, .handler = riscv_itrigger,
@ -4349,6 +4456,10 @@ int riscv_enumerate_triggers(struct target *target)
if (tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target))) if (tdata1 & CSR_MCONTROL6_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_ICOUNT:
if (tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target)))
riscv_set_register(target, GDB_REGNO_TDATA1, 0);
break;
case CSR_TDATA1_TYPE_ITRIGGER: case CSR_TDATA1_TYPE_ITRIGGER:
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);