From 63f9a1f53c284718df1ef9f798bad5b3877e60d3 Mon Sep 17 00:00:00 2001 From: liangzhen Date: Mon, 31 Mar 2025 15:18:52 +0800 Subject: [PATCH] target/riscv: Introduce `riscv set_group` command Add support for associating a halt group with current target or an external trigger via a newly exposed configuration option "riscv set_group". Original merge request: https://github.com/riscv-collab/riscv-openocd/pull/1179 Original author: Rob Bradford rbradford@rivosinc.com https://github.com/rbradford --- doc/openocd.texi | 14 +++++++ src/target/riscv/riscv-013.c | 50 +++++++++++++++++------ src/target/riscv/riscv.c | 77 ++++++++++++++++++++++++++++++++++++ src/target/riscv/riscv.h | 20 ++++++++++ 4 files changed, 150 insertions(+), 11 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 1dcb7f3f5a..7ece6b58c9 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11673,6 +11673,20 @@ The second argument configures how OpenOCD should use the selected trigger featu With no parameters, prints current trigger features configuration. @end deffn +@deffn {Command} {riscv set_group} grouptype group ['trigger' triggernum] +OpenOCD assigns a hart or a given external trigger into a RISC-V halt group. +This command associates current target or supplied external trigger(s) with this halt group. + +@option{trigger} and @var{triggernum} Specify the index of the external trigger. +When an external input trigger fires, the harts in the halt group will get halted. +Similarly, all external output triggers will be notified when a hart in the halt group halts. + +The only supported value of @var{grouptype} is @option{halt_group}. RISC-V resume groups +are not allowed because the concept of a target resuming due to a different reason than +user's resume request is not supported by OpenOCD and GDB. +@end deffn + + @subsection RISC-V Authentication Commands The following commands can be used to authenticate to a RISC-V system. Eg. a diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 46c61cab8d..85ec0b6734 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -68,12 +68,8 @@ static int riscv013_access_memory(struct target *target, const riscv_mem_access_ static bool riscv013_get_impebreak(const struct target *target); static unsigned int riscv013_get_progbufsize(const struct target *target); -typedef enum { - HALT_GROUP, - RESUME_GROUP -} grouptype_t; static int set_group(struct target *target, bool *supported, unsigned int group, - grouptype_t grouptype); + enum grouptype grouptype, bool is_trigger, unsigned int trigger_num); /** * Since almost everything can be accomplish by scanning the dbus register, all @@ -138,6 +134,8 @@ typedef struct { * abstractcs.busy may have remained set. In that case we may need to * re-check the busy state before executing these operations. */ bool abstract_cmd_maybe_busy; + + struct riscv_ext_trigger external_triggers[RISCV_MAX_EXTTRIGGERS]; } dm013_info_t; typedef struct { @@ -1732,7 +1730,7 @@ static int halt_set_dcsr_ebreak(struct target *target) if (info->haltgroup_supported) { bool supported; - if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK) + if (set_group(target, &supported, 0, HALT_GROUP, false, 0) != ERROR_OK) return ERROR_FAIL; if (!supported) LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. " @@ -1754,7 +1752,7 @@ static int halt_set_dcsr_ebreak(struct target *target) /* Add it back to the halt group. */ if (info->haltgroup_supported) { bool supported; - if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK) + if (set_group(target, &supported, target->smp, HALT_GROUP, false, 0) != ERROR_OK) return ERROR_FAIL; if (!supported) LOG_TARGET_ERROR(target, "Couldn't place hart back in halt group %d. " @@ -1785,19 +1783,46 @@ static void deinit_target(struct target *target) } static int set_group(struct target *target, bool *supported, unsigned int group, - grouptype_t grouptype) + enum grouptype grouptype, bool is_trigger, unsigned int trigger_num) { - uint32_t write_val = DM_DMCS2_HGWRITE; assert(group <= 31); + assert(trigger_num < 16); + + if (!is_trigger && dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + if (is_trigger && dm->external_triggers[trigger_num].haltgroup_was_set && + dm->external_triggers[trigger_num].haltgroup_num == group) { + LOG_TARGET_WARNING(target, "External trigger %d (at address dbgbase=0x%" PRIx32 ") " + "for halt group %d has been set.", trigger_num, dm->base, group); + return ERROR_OK; + } + + uint32_t write_val = DM_DMCS2_HGWRITE; write_val = set_field(write_val, DM_DMCS2_GROUP, group); write_val = set_field(write_val, DM_DMCS2_GROUPTYPE, (grouptype == HALT_GROUP) ? 0 : 1); + write_val = set_field(write_val, DM_DMCS2_DMEXTTRIGGER, trigger_num); + write_val = set_field(write_val, DM_DMCS2_HGSELECT, + is_trigger ? DM_DMCS2_HGSELECT_TRIGGERS : DM_DMCS2_HGSELECT_HARTS); if (dm_write(target, DM_DMCS2, write_val) != ERROR_OK) return ERROR_FAIL; uint32_t read_val; if (dm_read(target, &read_val, DM_DMCS2) != ERROR_OK) return ERROR_FAIL; if (supported) - *supported = (get_field(read_val, DM_DMCS2_GROUP) == group); + *supported = (get_field(read_val, DM_DMCS2_GROUP) == group && + get_field(read_val, DM_DMCS2_GROUPTYPE) == ((grouptype == HALT_GROUP) ? 0 : 1) && + get_field(read_val, DM_DMCS2_HGSELECT) == + (is_trigger ? DM_DMCS2_HGSELECT_TRIGGERS : DM_DMCS2_HGSELECT_HARTS) && + get_field(read_val, DM_DMCS2_DMEXTTRIGGER) == trigger_num); + if (is_trigger && *supported) { + dm->external_triggers[trigger_num].haltgroup_was_set = true; + dm->external_triggers[trigger_num].haltgroup_num = group; + } + return ERROR_OK; } @@ -2145,8 +2170,9 @@ static int examine(struct target *target) } if (target->smp) { - if (set_group(target, &info->haltgroup_supported, target->smp, HALT_GROUP) != ERROR_OK) + if (set_group(target, &info->haltgroup_supported, target->smp, HALT_GROUP, false, 0) != ERROR_OK) return ERROR_FAIL; + if (info->haltgroup_supported) LOG_TARGET_INFO(target, "Core %d made part of halt group %d.", info->index, target->smp); @@ -2881,6 +2907,8 @@ static int init_target(struct command_context *cmd_ctx, generic_info->handle_became_unavailable = &handle_became_unavailable; generic_info->tick = &tick; + generic_info->set_group = &set_group; + if (!generic_info->version_specific) { generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); if (!generic_info->version_specific) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index aae5eb35a9..708775220b 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -5588,6 +5588,76 @@ COMMAND_HANDLER(handle_riscv_virt2phys_mode) return ERROR_OK; } +COMMAND_HANDLER(riscv_set_group) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + enum grouptype grouptype; + if (!strcmp("halt_group", CMD_ARGV[0])) { + grouptype = HALT_GROUP; + } else { + /* Ext. triggers for resume groups are not supported: neither OpenOCD nor GDB + * have a concept of hart resuming on its own due to an external reason, without + * an explicit resume request. */ + LOG_ERROR("%s is not a valid argument. " + "The only supported group type is 'halt_group'.", CMD_ARGV[1]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + unsigned int group; + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], group); + if (group > 31) { + LOG_ERROR("%d is not a valid group number - expecting a number in range 0..31.", + group); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + if (!r->set_group) { + LOG_TARGET_ERROR(target, "set_group is not implemented for this target."); + return ERROR_FAIL; + } + + bool is_trigger = false; + unsigned int trigger_num = 0; + switch (CMD_ARGC) { + case 2: + break; + case 4: + if (strcmp("trigger", CMD_ARGV[2])) + return ERROR_COMMAND_SYNTAX_ERROR; + is_trigger = true; + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[3], trigger_num); + if (trigger_num > 15) { + LOG_ERROR("%d is not a valid external trigger number - expecting a number in range 0..15.", + trigger_num); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + break; + default: + return ERROR_COMMAND_SYNTAX_ERROR; + } + + bool supported; + if (r->set_group(target, &supported, group, grouptype, is_trigger, trigger_num) != ERROR_OK) + return ERROR_FAIL; + + if (supported) + LOG_TARGET_INFO(target, "%s %d made part of halt group %d.", + (is_trigger ? "External trigger" : "Core"), + (is_trigger ? trigger_num : (unsigned int)target->coreid), group); + else + LOG_TARGET_WARNING(target, "%s %d could not be made part of halt group %d.", + (is_trigger ? "External trigger" : "Core"), + (is_trigger ? trigger_num : (unsigned int)target->coreid), group); + + return ERROR_OK; +} + + static const struct command_registration riscv_exec_command_handlers[] = { { .name = "dump_sample_buf", @@ -5850,6 +5920,13 @@ static const struct command_registration riscv_exec_command_handlers[] = { "When off, users need to take care of memory coherency themselves, for example by using " "`riscv exec_progbuf` to execute fence or CMO instructions." }, + { + .name = "set_group", + .handler = riscv_set_group, + .mode = COMMAND_ANY, + .usage = "grouptype group ['trigger' triggernum]", + .help = "Set a hart or a given external trigger to the halt group." + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 082445e40a..013e3add5c 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -21,6 +21,7 @@ struct riscv_program; #define RISCV_MAX_TRIGGERS 32 #define RISCV_MAX_HWBPS 16 #define RISCV_MAX_DMS 100 +#define RISCV_MAX_EXTTRIGGERS 16 #define DEFAULT_COMMAND_TIMEOUT_SEC 5 @@ -122,6 +123,11 @@ typedef struct { char *name; } range_list_t; +enum grouptype { + HALT_GROUP, + RESUME_GROUP +}; + #define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1) #define RISCV_TINFO_VERSION_UNKNOWN (-1) @@ -144,6 +150,17 @@ typedef struct riscv_mem_access_args { uint32_t increment; } riscv_mem_access_args_t; +struct riscv_ext_trigger { + bool haltgroup_was_set; + unsigned int haltgroup_num; + /* + In future, this can be added: + + bool resumegroup_was_set; + unsigned int resumegroup_num; + */ +}; + static inline bool riscv_mem_access_is_valid(const riscv_mem_access_args_t args) { @@ -307,6 +324,9 @@ struct riscv_info { unsigned int (*data_bits)(struct target *target); + int (*set_group)(struct target *target, bool *supported, unsigned int group, + enum grouptype grouptype, bool is_trigger, unsigned int trigger_num); + COMMAND_HELPER((*print_info), struct target *target); /* Storage for arch_info of non-custom registers. */