This is an automated email from Gerrit. "liangzhen <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9215
-- gerrit commit 72e793ff054c6cf561df063ab2d62c28375b9c3c Author: liangzhen <[email protected]> Date: Wed Oct 29 19:38:57 2025 +0800 target: Add RISC-V trace support Implement a basic framework for riscv trace where each type of riscv trace components only need to implement corresponding interfaces. Change-Id: I8eb19a73a86891d6816092d94fe3a7b8914ceedf Signed-off-by: liangzhen <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index 91949d4089..2aee6f92b7 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11676,6 +11676,87 @@ Displays all DSP registers' contents or get/set value by register name. Will dis an error if current CPU does not support DSP. @end deffn +@section RISC-V Trace Drivers + +OpenOCD has limited support for RISC-V trace drivers using the @emph{rvtrace} +group of commands. + +@deffn {Command} {rvtrace create} component_name type configparams... +This command creates a rvtrace component instance @var{component_name}. +@itemize +@item @emph{component_name} ... is the name of the rvtrace component instance. +This name is also used to create the rvtrace component object command, referred +to here as $component_name, and in other places the component needs to be identified. +@item @emph{type} ... specifies the rvtrace component type. +@item @emph{configparams} ... all parameters accepted by $component_name configure +are permitted. You must set the @option{-dap} @emph{dap_name} @option{-ap-num} @emph{apn} +or @option{-target} @emph{target_name} here. +@end itemize +@end deffn + +@deffn {Command} {rvtrace types} +Lists all supported rvtrace component types. At this writing, the supported +rvtrace component types are: +@end deffn + +@deffn {Command} {rvtrace names} +Lists the names of all current rvtrace components in the list. +@end deffn + +@deffn {Command} {$component_name configure configparams...} +The options accepted by this command may also be specified as parameters +to @command{rvtrace create}. These values can later be queried one at a +time by using the @command{$component_name cget} command. +@itemize +@item @option{-dap} @emph{dap_name} - names the DAP used to access this +rvtrace component. +@item @option{-ap-num} @emph{apn} - set DAP access port for the rvtrace +component. +On ADIv5 DAP ap number is the numeric index of the DAP AP the component +is connected to. On ADIv6 DAP ap number is the base address of the DAP +AP the component is connected to. +@item @option{-target} @emph{target_name} - names the target used to +access this rvtrace component. +@item @option{-baseaddr} @emph{base_address} - base address of the +component on the respective MEM-AP or target. +@end itemize +@end deffn + +@deffn {Command} {$component_name cget queryparm} +Each configuration parameter accepted by @command{$component_name configure} can +be individu ally queried, to return its current value. The queryparm is a parameter +name accepted by that command, such as -baseaddr. There is a special cases: +@itemize +@item @option{-type} – returns the rvtrace component type. This is a special case +because this is set using @command{rvtrace create} and can’t be changed using +@command{$component_name configure}. +@end itemize +@end deffn + +@deffn {Command} {$component_name reset} +Reset the rvtrace component. +@end deffn + +@deffn {Command} {$component_name enable} +Enable the rvtrace component. +@end deffn + +@deffn {Command} {$component_name disable} +Disable the rvtrace component. +@end deffn + +@deffn {Command} {$component_name status} +Show the rvtrace component status. +@end deffn + +@deffn {Command} {$component_name write} @var{reg_name} @var{value} +Write @var{value} to the rvtrace component register with the symbolic name @var{reg_name}. +@end deffn + +@deffn {Command} {$component_name read} @var{reg_name} +Print the value read from the rvtrace component register with the symbolic name @var{reg_name}. +@end deffn + @section RISC-V Architecture @uref{http://riscv.org/, RISC-V} is a free and open ISA. OpenOCD supports JTAG diff --git a/src/openocd.c b/src/openocd.c index 6e7018ce02..4dc800f6fc 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -28,6 +28,7 @@ #include <target/arm_adi_v5.h> #include <target/arm_tpiu_swo.h> #include <target/coresight_trace.h> +#include <target/riscv/riscv_trace.h> #include <rtt/rtt.h> #include <server/server.h> @@ -168,6 +169,9 @@ COMMAND_HANDLER(handle_init_command) if (command_run_line(CMD_CTX, "tpiu init") != ERROR_OK) return ERROR_FAIL; + if (command_run_line(CMD_CTX, "rvtrace init") != ERROR_OK) + return ERROR_FAIL; + jtag_poll_unmask(save_poll_mask); /* initialize telnet subsystem */ @@ -250,6 +254,7 @@ static int (* const command_registrants[])(struct command_context *cmd_ctx_value dap_register_commands, arm_tpiu_swo_register_commands, cstrace_register_commands, + rvtrace_register_commands, }; static struct command_context *setup_command_handler(Jim_Interp *interp) @@ -361,6 +366,8 @@ int openocd_main(int argc, char *argv[]) unregister_all_commands(cmd_ctx, NULL); help_del_all_commands(cmd_ctx); + rvtrace_cleanup_all(); + /* free all DAP and CTI objects */ cstrace_cleanup_all(); arm_cti_cleanup_all(); diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index 4b6a74f0b8..8d53da48ee 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -10,9 +10,11 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/opcodes.h \ %D%/program.h \ %D%/riscv.h \ + %D%/riscv_trace.h \ %D%/batch.c \ %D%/program.c \ %D%/riscv-011.c \ %D%/riscv-013.c \ %D%/riscv.c \ - %D%/riscv_semihosting.c + %D%/riscv_semihosting.c \ + %D%/riscv_trace.c diff --git a/src/target/riscv/riscv_trace.c b/src/target/riscv/riscv_trace.c new file mode 100644 index 0000000000..88321e6d9b --- /dev/null +++ b/src/target/riscv/riscv_trace.c @@ -0,0 +1,694 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 by liangzhen * + * [email protected] * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <helper/log.h> +#include <helper/types.h> +#include <helper/list.h> +#include <helper/command.h> +#include <helper/nvp.h> +#include <helper/jim-nvp.h> +#include <target/target.h> +#include "riscv.h" +#include "riscv_trace.h" + +static OOCD_LIST_HEAD(all_rvtrace_component); + +static struct rvtrace_type *rvtrace_type[] = { +}; + +int rvtrace_write_reg(const struct rvtrace_component *comp, unsigned int reg, uint32_t value) +{ + if (comp->rvtrace_ap->type == RVTRACE_AP_TYPE_TARGET) + return target_write_u32(comp->rvtrace_ap->ap, comp->spot.base + reg, value); + + return mem_ap_write_atomic_u32(comp->rvtrace_ap->ap, comp->spot.base + reg, value); +} + +int rvtrace_read_reg(const struct rvtrace_component *comp, unsigned int reg, uint32_t *value) +{ + if (!value) + return ERROR_COMMAND_ARGUMENT_INVALID; + + if (comp->rvtrace_ap->type == RVTRACE_AP_TYPE_TARGET) + return target_read_u32(comp->rvtrace_ap->ap, comp->spot.base + reg, value); + + return mem_ap_read_atomic_u32(comp->rvtrace_ap->ap, comp->spot.base + reg, value); +} + +int rvtrace_read_buffer(const struct rvtrace_component *comp, target_addr_t address, + uint32_t size, uint8_t *buffer) +{ + if (comp->rvtrace_ap->type == RVTRACE_AP_TYPE_TARGET) + return target_read_buffer(comp->rvtrace_ap->ap, address, size, buffer); + + return mem_ap_read_buf(comp->rvtrace_ap->ap, buffer, 4, size / 4, address); +} + +int riscv_trace_timeout_sec = DEFAULT_RVTRACE_TIMEOUT_SEC; + +int rvtrace_poll_register(struct rvtrace_component *comp, unsigned int reg, uint32_t mask, uint32_t value) +{ + uint32_t regval; + time_t start = time(NULL); + + while (1) { + if (rvtrace_read_reg(comp, reg, ®val) != ERROR_OK) + return ERROR_FAIL; + + if (get_field(regval, mask) == value) + break; + + if (time(NULL) - start > riscv_trace_timeout_sec) { + LOG_ERROR("%s poll %x timeout", comp->name, reg); + return ERROR_WAIT; + } + } + + return ERROR_OK; +} + +int rvtrace_enable_component(struct rvtrace_component *comp) +{ + uint32_t value; + if (rvtrace_read_reg(comp, RVTRACE_COMPONENT_CTRL, &value) != ERROR_OK) + return ERROR_FAIL; + + value |= RVTRACE_COMPONENT_CTRL_ENABLE; + if (rvtrace_write_reg(comp, RVTRACE_COMPONENT_CTRL, value) != ERROR_OK) + return ERROR_FAIL; + + return rvtrace_poll_register(comp, RVTRACE_COMPONENT_CTRL, RVTRACE_COMPONENT_CTRL_ENABLE, 0x1); +} + +int rvtrace_disable_component(struct rvtrace_component *comp) +{ + uint32_t value; + if (rvtrace_read_reg(comp, RVTRACE_COMPONENT_CTRL, &value) != ERROR_OK) + return ERROR_FAIL; + + value &= ~RVTRACE_COMPONENT_CTRL_ENABLE; + if (rvtrace_write_reg(comp, RVTRACE_COMPONENT_CTRL, value) != ERROR_OK) + return ERROR_FAIL; + + return rvtrace_poll_register(comp, RVTRACE_COMPONENT_CTRL, RVTRACE_COMPONENT_CTRL_ENABLE, 0x0); +} + +static int rvtrace_component_reset(struct rvtrace_component *comp) +{ + int ret; + + if (rvtrace_write_reg(comp, RVTRACE_COMPONENT_CTRL, 0x0) != ERROR_OK) + return ERROR_FAIL; + + ret = rvtrace_poll_register(comp, RVTRACE_COMPONENT_CTRL, RVTRACE_COMPONENT_CTRL_ACTIVE, 0x0); + if (ret != ERROR_OK) + return ret; + + if (rvtrace_write_reg(comp, RVTRACE_COMPONENT_CTRL, 0x1) != ERROR_OK) + return ERROR_FAIL; + + return rvtrace_poll_register(comp, RVTRACE_COMPONENT_CTRL, RVTRACE_COMPONENT_CTRL_ACTIVE, 0x1); +} + +static int rvtrace_component_examine(struct rvtrace_component *comp) +{ + int ret; + uint32_t impl, type, major, minor, version; + + ret = rvtrace_component_reset(comp); + if (ret != ERROR_OK) + return ret; + + ret = rvtrace_read_reg(comp, RVTRACE_COMPONENT_IMPL, &impl); + if (ret != ERROR_OK) + return ret; + + type = get_field(impl, RVTRACE_COMPONENT_IMPL_TYPE); + if (type != comp->type->id_type) { + LOG_ERROR("%s: 0x%x does not match expected %s type value(0x%x).", comp->name, type, comp->type->name, comp->type->id_type); + return ERROR_FAIL; + } + + major = get_field(impl, RVTRACE_COMPONENT_IMPL_VERMAJOR); + minor = get_field(impl, RVTRACE_COMPONENT_IMPL_VERMINOR); + version = rvtrace_component_mkversion(major, minor); + if (version != comp->type->version) { + LOG_ERROR("%s: 0x%x does not match expected %s version value(0x%x).", comp->name, version, comp->type->name, comp->type->version); + return ERROR_FAIL; + } + + LOG_INFO("[%s] type=0x%x, version=0x%x", comp->name, type, version); + + if (comp->type->examine) { + ret = (*comp->type->examine)(comp); + if (ret != ERROR_OK) + return ret; + } + + return ERROR_OK; +} + +int rvtrace_cleanup_all(void) +{ + struct rvtrace_component *obj, *tmp; + list_for_each_entry_safe(obj, tmp, &all_rvtrace_component, lh) { + rvtrace_disable_component(obj); + free(obj->component_info); + free(obj->private_config); + free(obj->rvtrace_ap); + free(obj->name); + free(obj->type); + free(obj); + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rvtrace_read) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + uint32_t value; + int retval = comp->type->read(comp, CMD_ARGV[0], &value); + if (retval != ERROR_OK) { + command_print(CMD, "failed to read register '%s'", CMD_ARGV[0]); + return retval; + } + + command_print(CMD, "0x%08"PRIx32, value); + + return retval; +} + +COMMAND_HANDLER(handle_rvtrace_write) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + uint32_t value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + int retval = comp->type->write(comp, CMD_ARGV[0], value); + if (retval != ERROR_OK) + command_print(CMD, "failed to write '0x%x' to register '%s'", value, CMD_ARGV[0]); + + return retval; +} + +COMMAND_HANDLER(handle_rvtrace_status) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + int retval = comp->type->status(comp, CMD); + if (retval != ERROR_OK) + command_print(CMD, "failed to get '%s' status", comp->name); + + return retval; +} + +COMMAND_HANDLER(handle_rvtrace_disable) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + int retval = comp->type->disable(comp); + + if (retval != ERROR_OK) + command_print(CMD, "failed to disable '%s'", comp->name); + + return retval; +} + +COMMAND_HANDLER(handle_rvtrace_enable) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + int retval = comp->type->enable(comp); + if (retval != ERROR_OK) + command_print(CMD, "failed to enable '%s'", comp->name); + + return retval; +} + +COMMAND_HANDLER(handle_rvtrace_reset) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + int retval = rvtrace_component_reset(comp); + if (retval != ERROR_OK) + command_print(CMD, "failed to reset '%s'", comp->name); + + return retval; +} + +enum rvtrace_cfg_opts { + RVTRACE_CFG_TYPE, + RVTRACE_CFG_TARGET, + RVTRACE_CFG_INVALID = -1 +}; + +static struct nvp nvp_config_opts[] = { + { .name = "-type", .value = RVTRACE_CFG_TYPE }, + { .name = "-target", .value = RVTRACE_CFG_TARGET }, + { .name = NULL, .value = RVTRACE_CFG_INVALID }, +}; + +static COMMAND_HELPER(rvtrace_component_configure, struct rvtrace_component* comp, unsigned int index, bool is_configure) +{ + const struct nvp *n; + struct jim_getopt_info goi; + const char *result; + int reslen; + int retval; + while (index < CMD_ARGC) { + if (comp->type->rvtrace_jim_configure) { + jim_getopt_setup(&goi, CMD_CTX->interp, CMD_ARGC - index, CMD_JIMTCL_ARGV + index); + goi.is_configure = is_configure; + retval = (*comp->type->rvtrace_jim_configure)(comp, &goi); + index = CMD_ARGC - goi.argc; + + result = Jim_GetString(Jim_GetResult(CMD_CTX->interp), &reslen); + if (reslen > 0) + command_print(CMD, "%s", result); + + if (retval == JIM_OK) { + /* more? */ + continue; + } + if (retval == JIM_ERR) { + /* An error */ + return ERROR_FAIL; + } + /* otherwise we 'continue' below */ + } + + jim_getopt_setup(&goi, CMD_CTX->interp, CMD_ARGC - index, CMD_JIMTCL_ARGV + index); + goi.is_configure = is_configure; + retval = adiv5_jim_mem_ap_spot_configure(&comp->spot, &goi); + index = CMD_ARGC - goi.argc; + + result = Jim_GetString(Jim_GetResult(CMD_CTX->interp), &reslen); + if (reslen > 0) + command_print(CMD, "%s", result); + + if (retval == JIM_OK) + continue; + if (retval == JIM_ERR) + return retval; + + n = nvp_name2value(nvp_config_opts, CMD_ARGV[index]); + if (!n->name) { + nvp_unknown_command_print(CMD, nvp_config_opts, NULL, CMD_ARGV[index]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + index++; + switch (n->value) { + case RVTRACE_CFG_TYPE: + if (is_configure) { + command_print(CMD, "not settable: %s", n->name); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + if (index != CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + command_print(CMD, "%s", comp->type->name); + break; + case RVTRACE_CFG_TARGET: + if (is_configure) { + if (index == CMD_ARGC) { + command_print(CMD, "missing argument to %s", CMD_ARGV[index - 1]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + struct target *target = get_target(CMD_ARGV[index]); + if (!target) { + command_print(CMD, "Target '%s' could not be found", CMD_ARGV[index]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + comp->rvtrace_ap->type = RVTRACE_AP_TYPE_TARGET; + comp->rvtrace_ap->ap = target; + index++; + } else { + if (index != CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + if (comp->rvtrace_ap->type == RVTRACE_AP_TYPE_TARGET) { + struct target *target = comp->rvtrace_ap->ap; + command_print(CMD, "%s", target_name(target)); + } else { + command_print(CMD, "not target"); + } + } + break; + } + } + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rvtrace_configure) +{ + if (!CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + + bool is_configure = !strcmp(CMD_NAME, "configure"); + + struct rvtrace_component *comp = CMD_DATA; + + return CALL_COMMAND_HANDLER(rvtrace_component_configure, comp, 0, is_configure); +} + +static const struct command_registration rvtrace_instance_command_handlers[] = { + { + .name = "configure", + .mode = COMMAND_ANY, + .handler = handle_rvtrace_configure, + .help = "configure a new riscv trace component for use", + .usage = "[rvtrace_attribute ...]", + }, + { + .name = "cget", + .mode = COMMAND_ANY, + .handler = handle_rvtrace_configure, + .help = "returns the specified riscv trace component attribute", + .usage = "rvtrace_attribute", + }, + { + .name = "reset", + .handler = handle_rvtrace_reset, + .mode = COMMAND_EXEC, + .help = "reset the riscv trace component", + .usage = "", + }, + { + .name = "enable", + .handler = handle_rvtrace_enable, + .mode = COMMAND_EXEC, + .help = "enable the riscv trace component", + .usage = "", + }, + { + .name = "disable", + .handler = handle_rvtrace_disable, + .mode = COMMAND_EXEC, + .help = "disable the riscv trace component", + .usage = "", + }, + { + .name = "status", + .mode = COMMAND_EXEC, + .handler = handle_rvtrace_status, + .help = "show riscv trace component status", + .usage = "", + }, + { + .name = "write", + .mode = COMMAND_EXEC, + .handler = handle_rvtrace_write, + .help = "write to a riscv trace component register", + .usage = "<register> <value>", + }, + { + .name = "read", + .mode = COMMAND_EXEC, + .handler = handle_rvtrace_read, + .help = "read a riscv trace component register", + .usage = "<register>", + }, + COMMAND_REGISTRATION_DONE +}; + +COMMAND_HANDLER(handle_rvtrace_create) +{ + int retval = ERROR_OK; + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + /* check if the riscv trace component name clashes with an existing command name */ + Jim_Cmd *jimcmd = Jim_GetCommand(CMD_CTX->interp, CMD_JIMTCL_ARGV[0], JIM_NONE); + if (jimcmd) { + command_print(CMD, "Command: %s Exists", CMD_ARGV[0]); + return ERROR_FAIL; + } + + /* does riscv trace component type exist */ + const char *cp = CMD_ARGV[1]; + size_t x; + for (x = 0; x < ARRAY_SIZE(rvtrace_type); x++) { + if (strcmp(cp, rvtrace_type[x]->name) == 0) { + /* found */ + break; + } + } + if (x == ARRAY_SIZE(rvtrace_type)) { + char *all = NULL; + for (x = 0; x < ARRAY_SIZE(rvtrace_type); x++) { + char *prev = all; + if (all) + all = alloc_printf("%s, %s", all, rvtrace_type[x]->name); + else + all = alloc_printf("%s", rvtrace_type[x]->name); + free(prev); + if (!all) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + } + command_print(CMD, "Unknown coresight trace component type %s, try one of %s", cp, all); + free(all); + return ERROR_FAIL; + } + + /* Create it */ + struct rvtrace_component *comp = calloc(1, sizeof(struct rvtrace_component)); + if (!comp) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + /* allocate memory for each unique rvtrace type */ + comp->type = malloc(sizeof(struct rvtrace_type)); + if (!comp->type) { + LOG_ERROR("Out of memory"); + free(comp); + return ERROR_FAIL; + } + + memcpy(comp->type, rvtrace_type[x], sizeof(struct rvtrace_type)); + + comp->name = strdup(CMD_ARGV[0]); + if (!comp->name) { + LOG_ERROR("Out of memory"); + free(comp->type); + free(comp); + return ERROR_FAIL; + } + + adiv5_mem_ap_spot_init(&comp->spot); + + comp->rvtrace_ap = calloc(1, sizeof(struct rvtrace_ap)); + if (!comp->rvtrace_ap) { + LOG_ERROR("Out of memory"); + free(comp->name); + free(comp->type); + free(comp); + return ERROR_FAIL; + } + + /* Do the rest as "configure" options */ + bool is_configure = true; + retval = CALL_COMMAND_HANDLER(rvtrace_component_configure, comp, 2, is_configure); + if (retval == ERROR_OK) { + if (!comp->rvtrace_ap->ap && !comp->spot.dap) { + command_print(CMD, "-target or -dap required when creating riscv trace component"); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + if (retval != ERROR_OK) { + free(comp->rvtrace_ap); + free(comp->name); + free(comp->type); + free(comp); + return retval; + } + + if (comp->type->rvtrace_create) { + retval = (*comp->type->rvtrace_create)(comp); + if (retval != ERROR_OK) { + LOG_DEBUG("rvtrace_create failed"); + free(comp->private_config); + free(comp->component_info); + free(comp->rvtrace_ap); + free(comp->name); + free(comp->type); + free(comp); + return retval; + } + } + + /* now - create the new rvtrace component name command */ + const struct command_registration rvtrace_subcommands[] = { + { + .chain = rvtrace_instance_command_handlers, + }, + { + .chain = comp->type->commands, + }, + COMMAND_REGISTRATION_DONE + }; + const struct command_registration rvtrace_commands[] = { + { + .name = CMD_ARGV[0], + .mode = COMMAND_ANY, + .help = "coresight trace command group", + .usage = "", + .chain = rvtrace_subcommands, + }, + COMMAND_REGISTRATION_DONE + }; + retval = register_commands_with_data(CMD_CTX, NULL, rvtrace_commands, comp); + if (retval != ERROR_OK) { + free(comp->private_config); + free(comp->component_info); + free(comp->rvtrace_ap); + free(comp->name); + free(comp->type); + free(comp); + return retval; + } + + if (!comp->rvtrace_ap->ap) { + comp->rvtrace_ap->ap = dap_get_ap(comp->spot.dap, comp->spot.ap_num); + comp->rvtrace_ap->type = RVTRACE_AP_TYPE_MEM_AP; + } + + if (!comp->rvtrace_ap->ap) { + LOG_ERROR("riscv trace cannot get AP"); + free(comp->private_config); + free(comp->component_info); + free(comp->rvtrace_ap); + free(comp->name); + free(comp->type); + free(comp); + return ERROR_FAIL; + } + + list_add_tail(&comp->lh, &all_rvtrace_component); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rvtrace_types) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + for (size_t x = 0; x < ARRAY_SIZE(rvtrace_type); x++) + command_print(CMD, "%s", rvtrace_type[x]->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rvtrace_names) +{ + struct rvtrace_component *entry; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(entry, &all_rvtrace_component, lh) + command_print(CMD, "%s", entry->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_rvtrace_init) +{ + struct rvtrace_component *entry; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + list_for_each_entry(entry, &all_rvtrace_component, lh) { + if (rvtrace_component_examine(entry) != ERROR_OK) + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static const struct command_registration rvtrace_subcommand_handlers[] = { + { + .name = "create", + .mode = COMMAND_ANY, + .handler = handle_rvtrace_create, + .usage = "name type [options ...]", + .help = "Creates a new riscv trace component", + }, + { + .name = "types", + .mode = COMMAND_ANY, + .handler = handle_rvtrace_types, + .help = "Returns the available riscv trace component types as " + "a list of strings", + .usage = "", + }, + { + .name = "names", + .mode = COMMAND_ANY, + .handler = handle_rvtrace_names, + .help = "Returns the names of all riscv trace components as a " + "list of strings", + .usage = "", + }, + { + .name = "init", + .mode = COMMAND_ANY, + .handler = handle_rvtrace_init, + .help = "Initialize rvtrace component", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration rvtrace_command_handlers[] = { + { + .name = "rvtrace", + .mode = COMMAND_CONFIG, + .help = "configure riscv trace", + .chain = rvtrace_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +int rvtrace_register_commands(struct command_context *cmd_ctx) +{ + return register_commands(cmd_ctx, NULL, rvtrace_command_handlers); +} diff --git a/src/target/riscv/riscv_trace.h b/src/target/riscv/riscv_trace.h new file mode 100644 index 0000000000..0aea588258 --- /dev/null +++ b/src/target/riscv/riscv_trace.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 by liangzhen * + * [email protected] * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_RISCV_RISCV_TRACE_H +#define OPENOCD_TARGET_RISCV_RISCV_TRACE_H + +#include <target/arm_adi_v5.h> + +/* Control register common across all RISC-V trace components */ +#define RVTRACE_COMPONENT_CTRL 0x000 +#define RVTRACE_COMPONENT_CTRL_ACTIVE 0x1 +#define RVTRACE_COMPONENT_CTRL_ENABLE 0x2 + +/* Implementation register common across all RISC-V trace components */ +#define RVTRACE_COMPONENT_IMPL 0x004 +#define RVTRACE_COMPONENT_IMPL_VERMAJOR 0xf +#define RVTRACE_COMPONENT_IMPL_VERMINOR 0xf0 +#define RVTRACE_COMPONENT_IMPL_TYPE 0xf00 + +#define DEFAULT_RVTRACE_TIMEOUT_SEC 10 + +/* Possible component types defined by the RISC-V Trace Control Interface */ +enum rvtrace_component_type { + RVTRACE_COMPONENT_TYPE_RESV0, + RVTRACE_COMPONENT_TYPE_ENCODER, /* 0x1 */ + RVTRACE_COMPONENT_TYPE_RESV2, + RVTRACE_COMPONENT_TYPE_RESV3, + RVTRACE_COMPONENT_TYPE_RESV4, + RVTRACE_COMPONENT_TYPE_RESV5, + RVTRACE_COMPONENT_TYPE_RESV6, + RVTRACE_COMPONENT_TYPE_RESV7, + RVTRACE_COMPONENT_TYPE_FUNNEL, /* 0x8 */ + RVTRACE_COMPONENT_TYPE_RAMSINK, /* 0x9 */ + RVTRACE_COMPONENT_TYPE_PIBSINK, /* 0xA */ + RVTRACE_COMPONENT_TYPE_RESV11, + RVTRACE_COMPONENT_TYPE_RESV12, + RVTRACE_COMPONENT_TYPE_RESV13, + RVTRACE_COMPONENT_TYPE_ATBBRIDGE, /* 0xE */ + RISCV_TRACE_COMPONENT_TYPE_RESV15, + RISCV_TRACE_COMPONENT_TYPE_MAX +}; + +enum rvtrace_ap_type { + RVTRACE_AP_TYPE_MEM_AP, + RVTRACE_AP_TYPE_TARGET, +}; + +struct rvtrace_ap { + enum rvtrace_ap_type type; + void *ap; +}; + +struct rvtrace_component { + struct list_head lh; + target_addr_t base; + char *name; + struct rvtrace_type *type; + struct adiv5_mem_ap_spot spot; + struct rvtrace_ap *rvtrace_ap; + void *component_info; + void *private_config; +}; + +struct rvtrace_type { + const char *name; + enum rvtrace_component_type id_type; + uint32_t version; + int (*rvtrace_create)(struct rvtrace_component *comp); + int (*rvtrace_jim_configure)(struct rvtrace_component *comp, struct jim_getopt_info *goi); + int (*rvtrace_jim_commands)(struct rvtrace_component *comp, struct jim_getopt_info *goi); + + int (*examine)(struct rvtrace_component *comp); + + int (*enable)(struct rvtrace_component *comp); + int (*disable)(struct rvtrace_component *comp); + int (*status)(struct rvtrace_component *comp, struct command_invocation *cmd); + int (*write)(struct rvtrace_component *comp, const char *reg_name, uint32_t value); + int (*read)(struct rvtrace_component *comp, const char *reg_name, uint32_t *value); + + const struct command_registration *commands; +}; + +#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) + +#define rvtrace_component_mkversion(major, minor) ((((major) & 0xf) << 4) | ((minor) & 0xf)) + +int rvtrace_write_reg(const struct rvtrace_component *comp, unsigned int reg, uint32_t value); +int rvtrace_read_reg(const struct rvtrace_component *comp, unsigned int reg, uint32_t *value); +int rvtrace_read_buffer(const struct rvtrace_component *comp, target_addr_t address, uint32_t size, uint8_t *buffer); + +int rvtrace_poll_register(struct rvtrace_component *comp, unsigned int reg, uint32_t mask, uint32_t value); + +int rvtrace_enable_component(struct rvtrace_component *comp); +int rvtrace_disable_component(struct rvtrace_component *comp); + +int rvtrace_cleanup_all(void); + +int rvtrace_register_commands(struct command_context *cmd_ctx); + +#endif /* OPENOCD_TARGET_RISCV_RISCV_TRACE_H */ --
