From 8216d33a0309dc1597fb8fb97b406ecbde85b3e9 Mon Sep 17 00:00:00 2001 From: liangzhen Date: Mon, 31 Mar 2025 15:18:52 +0800 Subject: [PATCH] target/riscv: Add support for external triggers Add support for associating a halt/resume group with an external trigger via a newly exposed configuration option "riscv smp_add_ext_triggers". 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 | 5 ++++ src/target/riscv/riscv-013.c | 49 +++++++++++++++++++++++++++++++++--- src/target/riscv/riscv.c | 47 ++++++++++++++++++++++++++++++++++ src/target/riscv/riscv.h | 14 +++++++++++ 4 files changed, 111 insertions(+), 4 deletions(-) diff --git a/doc/openocd.texi b/doc/openocd.texi index 94a8fc78a..a8376b207 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11616,6 +11616,11 @@ 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 smp_add_ext_triggers} grouptype1 num1 [grouptype2 num2] ... +Associate the supplied external trigger with the halt group for the harts. When +the external trigger fires the harts in the halt group will be halted. +@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 46c61cab8..8ddff3fef 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -68,10 +68,6 @@ 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); @@ -1784,6 +1780,37 @@ static void deinit_target(struct target *target) info->version_specific = NULL; } +static int smp_add_ext_trigger(struct target *target, unsigned int group, + riscv_ext_trigger_t external_trigger) +{ + uint32_t write_val = DM_DMCS2_HGSELECT; + assert(group <= 31); + assert(external_trigger.dmexttrigger < 16); + write_val = set_field(write_val, DM_DMCS2_GROUP, group); + write_val = set_field(write_val, DM_DMCS2_GROUPTYPE, external_trigger.grouptype); + write_val = set_field(write_val, DM_DMCS2_DMEXTTRIGGER, external_trigger.dmexttrigger); + if (dm_write(target, DM_DMCS2, write_val) != ERROR_OK) + return ERROR_FAIL; + write_val = set_field(write_val, DM_DMCS2_HGWRITE, 1); + 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 (get_field(read_val, DM_DMCS2_GROUP) == group && + get_field(read_val, DM_DMCS2_DMEXTTRIGGER) == external_trigger.dmexttrigger && + get_field(read_val, DM_DMCS2_HGSELECT) == 1) { + LOG_TARGET_INFO(target, "External trigger %d added to %s group %d", external_trigger.dmexttrigger, + external_trigger.grouptype ? "resume" : "halt", group); + } else { + LOG_TARGET_ERROR(target, "External trigger %d not supported %s group %d", external_trigger.dmexttrigger, + external_trigger.grouptype ? "resume" : "halt", group); + } + + return ERROR_OK; +} + static int set_group(struct target *target, bool *supported, unsigned int group, grouptype_t grouptype) { @@ -2153,6 +2180,20 @@ static int examine(struct target *target) else LOG_TARGET_INFO(target, "Core %d could not be made part of halt group %d.", info->index, target->smp); + + /* TODO: Resume groups with external input triggers look to be very problematic. + * Neither OpenOCD nor GDB is ready for the case when a target suddenly + * resumes (due to the trigger), without an explicit resume request and + * at arbitrary moment in time. */ + + for (unsigned int i = 0; i < RISCV_MAX_EXTTRIGGERS; i++) { + if (r->external_triggers[i].is_set) { + if (smp_add_ext_trigger(target, target->smp, r->external_triggers[i]) != ERROR_OK) + return ERROR_FAIL; + } else { + break; + } + } } /* Some regression suites rely on seeing 'Examined RISC-V core' to know diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index aae5eb35a..ac980cc1f 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -5588,6 +5588,46 @@ COMMAND_HANDLER(handle_riscv_virt2phys_mode) return ERROR_OK; } +COMMAND_HANDLER(riscv_smp_add_ext_triggers) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (CMD_ARGC % 2) { + LOG_ERROR("Command takes an even number of parameters."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (CMD_ARGC > 2 * RISCV_MAX_EXTTRIGGERS) { + LOG_ERROR("The number of parameters can not exceeds %d.", 2 * RISCV_MAX_EXTTRIGGERS); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + unsigned int index = 0; + for (unsigned int i = 0; i < CMD_ARGC - 1; i += 2) { + if (!strcmp("halt_group", CMD_ARGV[i])) { + r->external_triggers[index].grouptype = HALT_GROUP; + } else if (!strcmp("resume_group", CMD_ARGV[i])) { + r->external_triggers[index].grouptype = RESUME_GROUP; + } else { + LOG_ERROR("%s is not a valid argument for command," + "should be halt_group or resume_group.", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + int value = atoi(CMD_ARGV[i + 1]); + if (value < 0 || value > 15) { + LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[i + 1]); + return ERROR_FAIL; + } + r->external_triggers[index].dmexttrigger = value; + r->external_triggers[index].is_set = true; + index++; + } + + return ERROR_OK; +} + + static const struct command_registration riscv_exec_command_handlers[] = { { .name = "dump_sample_buf", @@ -5850,6 +5890,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 = "smp_add_ext_triggers", + .handler = riscv_smp_add_ext_triggers, + .mode = COMMAND_CONFIG, + .usage = "grouptype1 num1 [grouptype2 num2]......", + .help = "Add the given external triggers to the halt/resume group" + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 082445e40..83914d1d1 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 32 #define DEFAULT_COMMAND_TIMEOUT_SEC 5 @@ -144,6 +145,17 @@ typedef struct riscv_mem_access_args { uint32_t increment; } riscv_mem_access_args_t; +typedef enum { + HALT_GROUP, + RESUME_GROUP +} grouptype_t; + +typedef struct { + grouptype_t grouptype; + uint32_t dmexttrigger; + bool is_set; +} riscv_ext_trigger_t; + static inline bool riscv_mem_access_is_valid(const riscv_mem_access_args_t args) { @@ -365,6 +377,8 @@ struct riscv_info { bool wp_allow_ge_lt_trigger; bool autofence; + + riscv_ext_trigger_t external_triggers[RISCV_MAX_EXTTRIGGERS]; }; enum riscv_priv_mode {