Skip to content

target/riscv: Add support for external triggers #1243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: riscv
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/openocd.texi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
50 changes: 39 additions & 11 deletions src/target/riscv/riscv-013.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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. "
Expand All @@ -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. "
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
77 changes: 77 additions & 0 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a more fitting return code:

Suggested change
return ERROR_COMMAND_SYNTAX_ERROR;
return ERROR_COMMAND_ARGUMENT_INVALID;

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",
Expand Down Expand Up @@ -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
};

Expand Down
20 changes: 20 additions & 0 deletions src/target/riscv/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand All @@ -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)
{
Expand Down Expand Up @@ -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. */
Expand Down