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/+/9216
-- gerrit commit fd0bd8e6432a4f1d9a687d86378b480d4c52d087 Author: liangzhen <[email protected]> Date: Sat Nov 1 23:03:13 2025 +0800 target/riscv: rvtrace: Add trace encoder support Add support for the trace encoder component. Change-Id: I10e0f023a5252c1c317df69f868dbf276279d9de Signed-off-by: liangzhen <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index 2aee6f92b7..9ebec22821 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11697,6 +11697,9 @@ or @option{-target} @emph{target_name} here. @deffn {Command} {rvtrace types} Lists all supported rvtrace component types. At this writing, the supported rvtrace component types are: +@itemize +@item @code{encoder} - this a rvtrace encoder component. +@end itemize @end deffn @deffn {Command} {rvtrace names} @@ -11757,6 +11760,55 @@ Write @var{value} to the rvtrace component register with the symbolic name @var{ Print the value read from the rvtrace component register with the symbolic name @var{reg_name}. @end deffn +@subsection Encoder Component +When the rvtrace component is a @code{encoder} type, the configure configparams +specific to the @code{encoder} are: +@itemize +@item @option{-inst-mode} @code{off|resv1|resv2|btm|resv4|resv5|htm|resv7} - sets +the instruction trace generation mode +@item @option{-context} @code{on|off} - sets to allow sending trace messages/fields +and/or privilege levels using sontext/mcontext values. +@item @option{-trigger} @code{on|off} - sets to allow trTeInstTracing to be set or +cleared by Trace-on and Traceoff signals generated by the corresponding trigger module. +@item @option{-stall} @code{on|off} - sets if encoder cannot send a message, the +hart is stalled until it can. +@item @option{-inhb-src} @code{on|off} - disable inclusion of source field in trace +messages/packets. +@item @option{-sync-mode} @code{off|messages|clock|instruction} - sets the periodic +instruction trace synchronization message/packet generation mechanism. +@item @option{-sync-max} @emph{value} - The maximum interval (determined by +@option{-sync-mode}) between instruction trace synchronization messages/packets. +@item @option{-fulladdr} @code{on|off} - sets the trace messages/packets always +carry a full address. +@item @option{-no-trapaddr} @code{on|off} - sets do not include trap handler +address in trap messages/packets. +@item @option{-seq-jump} @code{on|off} - sets to treat sequentially inferrable +jumps as inferable PC discontinuities. +@item @option{-implicit-return} @code{on|off} - sets to treat returns as inferable +PC discontinuities when returning from a recent call on a stack. +@item @option{-branch-prediction} @code{on|off} - sets the Branch Predictor based +compression is enabled. +@item @option{-jump-target-cache} @code{on|off} - sets the Jump Target Cache based +compression is enabled. +@item @option{-implicit-return-mode} @code{no|simple|partial|full} - sets how the +decoder is handling stack of return addresses(if @option{-implicit-return} @code{on}). +@item @option{-repeated-history} @code{on|off} - sets enable repeated branch +history/map detection. +@item @option{-all-jumps} @code{on|off} - sets Enable emitting of trace message +or add history/map bit for direct unconditional/inferable control flow changes +(jumps or calls). +@item @option{-ext-msb} @code{on|off} - sets to allow extended handing of MSB +address bits. +@item @option{-srcid} @emph{id} - Trace source ID assigned to this trace encoder. +@item @option{-srcbits} @emph{num} - The number of bits in the trace source +field (0..12), unless disabled by @option{-inhb-src} @code{on}. +@end itemize +The commands specific to the @code{encoder} are: +@itemize +@item @command{component_name encoder start} - start instruction trace. +@item @command{component_name encoder stop} - stop instruction trace. +@end itemize + @section RISC-V Architecture @uref{http://riscv.org/, RISC-V} is a free and open ISA. OpenOCD supports JTAG diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index 8d53da48ee..541ab38386 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -17,4 +17,5 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/riscv-013.c \ %D%/riscv.c \ %D%/riscv_semihosting.c \ - %D%/riscv_trace.c + %D%/riscv_trace.c \ + %D%/trace_encoder.c diff --git a/src/target/riscv/riscv_trace.c b/src/target/riscv/riscv_trace.c index 88321e6d9b..c3fc36a00a 100644 --- a/src/target/riscv/riscv_trace.c +++ b/src/target/riscv/riscv_trace.c @@ -25,6 +25,7 @@ static OOCD_LIST_HEAD(all_rvtrace_component); static struct rvtrace_type *rvtrace_type[] = { + &rvtrace_encoder, }; int rvtrace_write_reg(const struct rvtrace_component *comp, unsigned int reg, uint32_t value) diff --git a/src/target/riscv/riscv_trace.h b/src/target/riscv/riscv_trace.h index 0aea588258..5d039f836a 100644 --- a/src/target/riscv/riscv_trace.h +++ b/src/target/riscv/riscv_trace.h @@ -102,4 +102,6 @@ int rvtrace_cleanup_all(void); int rvtrace_register_commands(struct command_context *cmd_ctx); +extern struct rvtrace_type rvtrace_encoder; + #endif /* OPENOCD_TARGET_RISCV_RISCV_TRACE_H */ diff --git a/src/target/riscv/trace_encoder.c b/src/target/riscv/trace_encoder.c new file mode 100644 index 0000000000..b752775033 --- /dev/null +++ b/src/target/riscv/trace_encoder.c @@ -0,0 +1,912 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2025 by liangzhen * + * [email protected] * + ***************************************************************************/ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <helper/log.h> +#include <helper/types.h> +#include <helper/command.h> +#include <helper/jim-nvp.h> +#include "riscv_trace.h" + +#define RVTRACE_ENCODER_CTRL 0x000 +#define RVTRACE_ENCODER_IMPL 0x004 +#define RVTRACE_ENCODER_INSTFEAT 0x008 + +/* register description */ +/* trTeControl - 0x000 */ +#define RVTRACE_ENCODER_CTRL_ACTIVE 0x1 +#define RVTRACE_ENCODER_CTRL_ENABLE 0x2 +#define RVTRACE_ENCODER_CTRL_ITRACE 0x4 +#define RVTRACE_ENCODER_CTRL_EMPTY 0x8 +#define RVTRACE_ENCODER_CTRL_INSTMODE 0x70 +#define RVTRACE_ENCODER_CTRL_CONTEXT 0x200 +#define RVTRACE_ENCODER_CTRL_INSTTRIGENABLE 0x800 +#define RVTRACE_ENCODER_CTRL_INSTSTALLOROVERFLOW 0x1000 +#define RVTRACE_ENCODER_CTRL_INSTSTALLENA 0x2000 +#define RVTRACE_ENCODER_CTRL_INHIBITSRC 0x8000 +#define RVTRACE_ENCODER_CTRL_INSTSYNCMODE 0x30000 +#define RVTRACE_ENCODER_CTRL_INSTSYNCMAX 0xf00000 +#define RVTRACE_ENCODER_CTRL_FORMAT 0x7000000 +/* trTeInstFeatures - 0x008 */ +#define RVTRACE_ENCODER_INSTFEAT_NOADDRDIFF 0x1 +#define RVTRACE_ENCODER_INSTFEAT_NOTRAPADDR 0x2 +#define RVTRACE_ENCODER_INSTFEAT_ENSEQUENTIALJUMP 0x4 +#define RVTRACE_ENCODER_INSTFEAT_ENIMPLICITRETURN 0x8 +#define RVTRACE_ENCODER_INSTFEAT_ENBRANCHPREDICTION 0x10 +#define RVTRACE_ENCODER_INSTFEAT_ENJUMPTARGETCACHE 0x20 +#define RVTRACE_ENCODER_INSTFEAT_IMPLICITRETURNMODE 0xc0 +#define RVTRACE_ENCODER_INSTFEAT_ENREPEATEDHISTORT 0x100 +#define RVTRACE_ENCODER_INSTFEAT_ENALLJUMPS 0x200 +#define RVTRACE_ENCODER_INSTFEAT_EXTENDADDRMSB 0x400 +#define RVTRACE_ENCODER_INSTFEAT_SRCID 0xfff0000 +#define RVTRACE_ENCODER_INSTFEAT_SRCBITS 0xf0000000 + +#define RVTRACE_ENCODER_CTRL_INSTSYNCMAX_MAXVAL 0xf + +#define RVTRACE_ENCODER_INSTFEAT_SRCBITS_MAXVAL 0xc + +enum rvtrace_format { + RVTRACE_FORMAT_ETRACE, + RVTRACE_FORMAT_NTRACE, + RVTRACE_FORMAT_UNKNOWN = 7 +}; + +struct rv_encoder_info { + bool was_enabled; + enum rvtrace_format format; +}; + +enum inst_mode { + INSTMODE_OFF, + INSTMODE_RESV1, + INSTMODE_RESV2, + INSTMODE_BTM, + INSTMODE_RESV4, + INSTMODE_RESV5, + INSTMODE_HTM, + INSTMODE_RESV7 +}; + +enum sync_mode { + SYNCMODE_OFF, + SYNCMODE_MESSAGES, + SYNCMODE_CLOCK, + SYNCMODE_INSTRUCTION +}; + +enum implicit_return_mode { + IMPLICITRETURNMODE_NOT_SUPPORTED, + IMPLICITRETURNMODE_SIMPLE_COUNTING, + IMPLICITRETURNMODE_PARTIAL_ADDRESS, + IMPLICITRETURNMODE_FULL_ADDRESS +}; + +struct rv_encoder_private_config { + uint32_t inst_mode; + bool context; + bool trigger; + bool stall; + bool inhb_src; + enum sync_mode sync_mode; + uint32_t sync_max; + bool fulladdr; + bool no_trapaddr; + bool seq_jump; + bool implicit_return; + bool branch_prediction; + bool jump_target_cache; + enum implicit_return_mode implicit_return_mode; + bool repeated_history; + bool all_jumps; + bool ext_msb; + uint32_t srcid; + uint32_t srcbits; +}; + +static inline struct rv_encoder_info *rv_encoder_info(struct rvtrace_component *comp) +{ + assert(comp->component_info); + return comp->component_info; +} + +static inline struct rv_encoder_private_config *rv_encoder_private_config(struct rvtrace_component *comp) +{ + assert(comp->private_config); + return comp->private_config; +} + +#define RV_ENCODER_INFO(R) struct rv_encoder_info *R = rv_encoder_info(comp); + +static struct rv_encoder_private_config *alloc_default_rv_encoder_private_config(void) +{ + struct rv_encoder_private_config *config = malloc(sizeof(struct rv_encoder_private_config)); + if (!config) { + LOG_ERROR("Out of memory!"); + return NULL; + } + + /* The trTeInstMode WARL field should be set to 6 - it may revert to different mode */ + config->inst_mode = INSTMODE_HTM; + config->context = true; + config->trigger = true; + config->sync_mode = SYNCMODE_CLOCK; + config->sync_max = RVTRACE_ENCODER_CTRL_INSTSYNCMAX_MAXVAL; + + return config; +} + +static const struct { + uint32_t offset; + const char *label; +} encoder_names[] = { + { RVTRACE_ENCODER_CTRL, "CONTROL" }, + { RVTRACE_ENCODER_IMPL, "IMPL" }, + { RVTRACE_ENCODER_INSTFEAT, "INSTFEATURES" }, +}; + +static int encoder_find_reg_offset(const char *name) +{ + for (size_t i = 0; i < ARRAY_SIZE(encoder_names); i++) { + if (!strcasecmp(name, encoder_names[i].label)) + return encoder_names[i].offset; + } + + LOG_ERROR("unknown coresight encoder register %s", name); + return -1; +} + +static int encoder_create(struct rvtrace_component *comp) +{ + LOG_DEBUG("encoder_create()"); + struct rv_encoder_private_config *config = comp->private_config; + if (!config) { + config = alloc_default_rv_encoder_private_config(); + if (!config) + return ERROR_FAIL; + comp->private_config = config; + } + comp->component_info = calloc(1, sizeof(struct rv_encoder_info)); + if (!comp->component_info) { + LOG_ERROR("Failed to allocate encoder info structure."); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static struct jim_nvp nvp_inst_mode_opts[] = { + { .name = "off", .value = INSTMODE_OFF }, + { .name = "resv1", .value = INSTMODE_RESV1 }, + { .name = "resv2", .value = INSTMODE_RESV2 }, + { .name = "btm", .value = INSTMODE_BTM }, + { .name = "resv4", .value = INSTMODE_RESV4 }, + { .name = "resv5", .value = INSTMODE_RESV5 }, + { .name = "htm", .value = INSTMODE_HTM }, + { .name = "resv7", .value = INSTMODE_RESV7 }, + { .name = NULL, .value = -1 }, +}; + +static struct jim_nvp nvp_sync_mode_opts[] = { + { .name = "off", .value = SYNCMODE_OFF }, + { .name = "messages", .value = SYNCMODE_MESSAGES }, + { .name = "clock", .value = SYNCMODE_CLOCK }, + { .name = "instruction", .value = SYNCMODE_INSTRUCTION }, + { .name = NULL, .value = -1 }, +}; + +static struct jim_nvp nvp_implicit_return_mode_opts[] = { + { .name = "no", .value = IMPLICITRETURNMODE_NOT_SUPPORTED }, + { .name = "simple", .value = IMPLICITRETURNMODE_SIMPLE_COUNTING}, + { .name = "partial", .value = IMPLICITRETURNMODE_PARTIAL_ADDRESS }, + { .name = "full", .value = IMPLICITRETURNMODE_FULL_ADDRESS }, + { .name = NULL, .value = -1 }, +}; + +static struct jim_nvp nvp_on_off_opts[] = { + { .name = "off", .value = false }, + { .name = "on", .value = true }, + { .name = NULL, .value = -1 } +}; + +enum rv_encoder_cfg_opts { + RV_ENCODER_CFG_INST_MODE, + RV_ENCODER_CFG_CONTEXT, + RV_ENCODER_CFG_TRIGGER, + RV_ENCODER_CFG_STALL, + RV_ENCODER_CFG_INHB_SRC, + RV_ENCODER_CFG_SYNC_MODE, + RV_ENCODER_CFG_SYNC_MAX, + RV_ENCODER_CFG_FULLADDR, + RV_ENCODER_CFG_NO_TRAPADDR, + RV_ENCODER_CFG_SEQ_JUMP, + RV_ENCODER_CFG_IMPLICIT_RETURN, + RV_ENCODER_CFG_BRANCH_PREDICTION, + RV_ENCODER_CFG_JUMP_TARGET_CACHE, + RV_ENCODER_CFG_IMPLICIT_RETURN_MODE, + RV_ENCODER_CFG_REPEATED_HISTORY, + RV_ENCODER_CFG_ALL_JUMPS, + RV_ENCODER_CFG_EXT_MSB, + RV_ENCODER_CFG_SRCID, + RV_ENCODER_CFG_SRCBITS +}; + +static struct jim_nvp nvp_config_opts[] = { + { .name = "-inst-mode", .value = RV_ENCODER_CFG_INST_MODE }, + { .name = "-context", .value = RV_ENCODER_CFG_CONTEXT }, + { .name = "-trigger", .value = RV_ENCODER_CFG_TRIGGER }, + { .name = "-stall", .value = RV_ENCODER_CFG_STALL }, + { .name = "-inhb-src", .value = RV_ENCODER_CFG_INHB_SRC }, + { .name = "-sync-mode", .value = RV_ENCODER_CFG_SYNC_MODE }, + { .name = "-sync-max", .value = RV_ENCODER_CFG_SYNC_MAX }, + { .name = "-fulladdr", .value = RV_ENCODER_CFG_FULLADDR }, + { .name = "-no-trapaddr", .value = RV_ENCODER_CFG_NO_TRAPADDR }, + { .name = "-seq-jump", .value = RV_ENCODER_CFG_SEQ_JUMP }, + { .name = "-implicit-return", .value = RV_ENCODER_CFG_IMPLICIT_RETURN }, + { .name = "-branch-prediction", .value = RV_ENCODER_CFG_BRANCH_PREDICTION }, + { .name = "-jump-target-cache", .value = RV_ENCODER_CFG_JUMP_TARGET_CACHE }, + { .name = "-implicit-return-mode", .value = RV_ENCODER_CFG_IMPLICIT_RETURN_MODE }, + { .name = "-repeated-history", .value = RV_ENCODER_CFG_REPEATED_HISTORY }, + { .name = "-all-jumps", .value = RV_ENCODER_CFG_ALL_JUMPS }, + { .name = "-ext-msb", .value = RV_ENCODER_CFG_EXT_MSB }, + { .name = "-srcid", .value = RV_ENCODER_CFG_SRCID }, + { .name = "-srcbits", .value = RV_ENCODER_CFG_SRCBITS }, + { .name = NULL, .value = -1 } +}; + +static int encoder_jim_configure(struct rvtrace_component *comp, struct jim_getopt_info *goi) +{ + struct rv_encoder_private_config *config = comp->private_config; + if (!config) { + config = alloc_default_rv_encoder_private_config(); + if (!config) + return JIM_ERR; + comp->private_config = config; + } + + if (!goi->argc) + return JIM_OK; + + struct jim_nvp *n; + int e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, goi->argv[0], &n); + + if (e != JIM_OK) + return JIM_CONTINUE; + + e = jim_getopt_obj(goi, NULL); + if (e != JIM_OK) + return e; + + if (!goi->is_configure && goi->argc > 0) { + /*no arguments */ + Jim_WrongNumArgs(goi->interp, 2, goi->argv - 2, ""); + return JIM_ERR; + } + + switch (n->value) { + case RV_ENCODER_CFG_INST_MODE: + if (goi->is_configure) { + struct jim_nvp *inst_mode_nvp; + e = jim_getopt_nvp(goi, nvp_inst_mode_opts, &inst_mode_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_inst_mode_opts, /*hadprefix*/ true); + return e; + } + config->inst_mode = inst_mode_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_inst_mode_opts, config->inst_mode)->name, -1); + } + break; + case RV_ENCODER_CFG_CONTEXT: + if (goi->is_configure) { + struct jim_nvp *context_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &context_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->context = context_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->context)->name, -1); + } + break; + case RV_ENCODER_CFG_TRIGGER: + if (goi->is_configure) { + struct jim_nvp *trigger_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &trigger_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->trigger = trigger_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->trigger)->name, -1); + } + break; + case RV_ENCODER_CFG_STALL: + if (goi->is_configure) { + struct jim_nvp *stall_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &stall_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->stall = stall_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->stall)->name, -1); + } + break; + case RV_ENCODER_CFG_INHB_SRC: + if (goi->is_configure) { + struct jim_nvp *inhb_src_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &inhb_src_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->inhb_src = inhb_src_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->inhb_src)->name, -1); + } + break; + case RV_ENCODER_CFG_SYNC_MODE: + if (goi->is_configure) { + struct jim_nvp *sync_mode_nvp; + e = jim_getopt_nvp(goi, nvp_sync_mode_opts, &sync_mode_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_sync_mode_opts, /*hadprefix*/ true); + return e; + } + config->sync_mode = sync_mode_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_sync_mode_opts, config->sync_mode)->name, -1); + } + break; + case RV_ENCODER_CFG_SYNC_MAX: + if (goi->is_configure) { + jim_wide w; + e = jim_getopt_wide(goi, &w); + if (e != JIM_OK) + return e; + if (w > RVTRACE_ENCODER_CTRL_INSTSYNCMAX_MAXVAL) { + Jim_SetResultString(goi->interp, "Invalid sync max value(>15)!", -1); + } + config->sync_max = (uint32_t)w; + } else { + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, config->sync_max)); + } + break; + case RV_ENCODER_CFG_FULLADDR: + if (goi->is_configure) { + struct jim_nvp *fulladdr_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &fulladdr_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->fulladdr = fulladdr_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->fulladdr)->name, -1); + } + break; + case RV_ENCODER_CFG_NO_TRAPADDR: + if (goi->is_configure) { + struct jim_nvp *no_trapaddr_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &no_trapaddr_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->no_trapaddr = no_trapaddr_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->no_trapaddr)->name, -1); + } + break; + case RV_ENCODER_CFG_SEQ_JUMP: + if (goi->is_configure) { + struct jim_nvp *seq_jump_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &seq_jump_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->seq_jump = seq_jump_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->seq_jump)->name, -1); + } + break; + case RV_ENCODER_CFG_IMPLICIT_RETURN: + if (goi->is_configure) { + struct jim_nvp *implicit_return_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &implicit_return_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->implicit_return = implicit_return_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->implicit_return)->name, -1); + } + break; + case RV_ENCODER_CFG_BRANCH_PREDICTION: + if (goi->is_configure) { + struct jim_nvp *branch_prediction_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &branch_prediction_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->branch_prediction = branch_prediction_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->branch_prediction)->name, -1); + } + break; + case RV_ENCODER_CFG_JUMP_TARGET_CACHE: + if (goi->is_configure) { + struct jim_nvp *jump_target_cache_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &jump_target_cache_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->jump_target_cache = jump_target_cache_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->jump_target_cache)->name, -1); + } + break; + case RV_ENCODER_CFG_IMPLICIT_RETURN_MODE: + if (goi->is_configure) { + struct jim_nvp *implicit_return_mode_nvp; + e = jim_getopt_nvp(goi, nvp_implicit_return_mode_opts, &implicit_return_mode_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_implicit_return_mode_opts, /*hadprefix*/ true); + return e; + } + config->implicit_return_mode = implicit_return_mode_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_sync_mode_opts, config->implicit_return_mode)->name, -1); + } + break; + case RV_ENCODER_CFG_REPEATED_HISTORY: + if (goi->is_configure) { + struct jim_nvp *repeated_history_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &repeated_history_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->repeated_history = repeated_history_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->repeated_history)->name, -1); + } + break; + case RV_ENCODER_CFG_ALL_JUMPS: + if (goi->is_configure) { + struct jim_nvp *all_jumps_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &all_jumps_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->all_jumps = all_jumps_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->all_jumps)->name, -1); + } + break; + case RV_ENCODER_CFG_EXT_MSB: + if (goi->is_configure) { + struct jim_nvp *ext_msb_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, &ext_msb_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->ext_msb = ext_msb_nvp->value; + } else { + Jim_SetResultString(goi->interp, + jim_nvp_value2name_simple(nvp_on_off_opts, config->ext_msb)->name, -1); + } + break; + case RV_ENCODER_CFG_SRCID: + if (goi->is_configure) { + jim_wide w; + e = jim_getopt_wide(goi, &w); + if (e != JIM_OK) + return e; + if (w > (1 << RVTRACE_ENCODER_INSTFEAT_SRCBITS_MAXVAL) - 1) { + Jim_SetResultString(goi->interp, "Invalid source id(>4095)!", -1); + } + config->srcid = (uint32_t)w; + } else { + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, config->srcid)); + } + break; + case RV_ENCODER_CFG_SRCBITS: + if (goi->is_configure) { + jim_wide w; + e = jim_getopt_wide(goi, &w); + if (e != JIM_OK) + return e; + if (w > RVTRACE_ENCODER_INSTFEAT_SRCBITS_MAXVAL) { + Jim_SetResultString(goi->interp, "Invalid source bits (>12)!", -1); + } + config->srcid = (uint32_t)w; + } else { + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, config->srcid)); + } + break; + } + return JIM_OK; +} + +static int encoder_set_config(struct rvtrace_component *comp) +{ + struct rv_encoder_private_config *config = rv_encoder_private_config(comp); + uint32_t ctrl, features, field_rb; + + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_INSTMODE, config->inst_mode); + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_CONTEXT, config->context); + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_INSTTRIGENABLE, config->trigger); + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_INSTSTALLENA, config->stall); + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_INHIBITSRC, config->inhb_src); + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_INSTSYNCMODE, config->sync_mode); + ctrl = set_field(ctrl, RVTRACE_ENCODER_CTRL_INSTSYNCMAX, config->sync_max); + + if (rvtrace_write_reg(comp, RVTRACE_ENCODER_CTRL, ctrl) != ERROR_OK) + return ERROR_FAIL; + + /* read back ctrl register and check WARL bits */ + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_INSTMODE); + if (field_rb != config->inst_mode) + LOG_WARNING("%s: `-inst-mode` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_inst_mode_opts, config->inst_mode)->name, + jim_nvp_value2name_simple(nvp_inst_mode_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_CONTEXT); + if (field_rb != config->context) + LOG_WARNING("%s: `-context` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->context)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_INSTTRIGENABLE); + if (field_rb != config->trigger) + LOG_WARNING("%s: `-trigger` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->trigger)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_INSTSTALLENA); + if (field_rb != config->stall) + LOG_WARNING("%s: `-stall` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->stall)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_INHIBITSRC); + if (field_rb != config->inhb_src) + LOG_WARNING("%s: `-inhb-src` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->inhb_src)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_INSTSYNCMODE); + if (field_rb != config->sync_mode) + LOG_WARNING("%s: `-sync-mode` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_sync_mode_opts, config->sync_mode)->name, + jim_nvp_value2name_simple(nvp_sync_mode_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_ENCODER_CTRL_INSTSYNCMAX); + if (field_rb != config->sync_max) + LOG_WARNING("%s: `-sync-max` 0x%x is not supported, revert to 0x%x.", comp->name, + config->sync_max, field_rb); + + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_INSTFEAT, &features) != ERROR_OK) + return ERROR_FAIL; + + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_NOADDRDIFF, config->fulladdr); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_NOTRAPADDR, config->no_trapaddr); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_ENSEQUENTIALJUMP, config->seq_jump); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_ENIMPLICITRETURN, config->implicit_return); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_ENBRANCHPREDICTION, config->branch_prediction); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_ENJUMPTARGETCACHE, config->jump_target_cache); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_IMPLICITRETURNMODE, config->implicit_return_mode); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_ENREPEATEDHISTORT, config->repeated_history); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_ENALLJUMPS, config->all_jumps); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_EXTENDADDRMSB, config->ext_msb); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_SRCID, config->srcid); + features = set_field(features, RVTRACE_ENCODER_INSTFEAT_SRCBITS, config->srcbits); + + if (rvtrace_write_reg(comp, RVTRACE_ENCODER_INSTFEAT, features) != ERROR_OK) + return ERROR_FAIL; + + /* read back features register and check WARL bits */ + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_NOADDRDIFF); + if (field_rb != config->fulladdr) + LOG_WARNING("%s: `-fulladdr` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->fulladdr)->name, + jim_nvp_value2name_simple(nvp_inst_mode_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_NOTRAPADDR); + if (field_rb != config->no_trapaddr) + LOG_WARNING("%s: `-no-trapaddr` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->no_trapaddr)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_ENSEQUENTIALJUMP); + if (field_rb != config->seq_jump) + LOG_WARNING("%s: `-seq-jump` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->seq_jump)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_ENIMPLICITRETURN); + if (field_rb != config->implicit_return) + LOG_WARNING("%s: `-implicit-return` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->implicit_return)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_ENBRANCHPREDICTION); + if (field_rb != config->branch_prediction) + LOG_WARNING("%s: `-branch-prediction` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->branch_prediction)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_ENJUMPTARGETCACHE); + if (field_rb != config->jump_target_cache) + LOG_WARNING("%s: `-jump-target-cache` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->jump_target_cache)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_IMPLICITRETURNMODE); + if (field_rb != config->implicit_return_mode) + LOG_WARNING("%s: `-implicit-return-mode` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_implicit_return_mode_opts, config->implicit_return_mode)->name, + jim_nvp_value2name_simple(nvp_implicit_return_mode_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_ENREPEATEDHISTORT); + if (field_rb != config->repeated_history) + LOG_WARNING("%s: `-repeated-history` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->repeated_history)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_ENALLJUMPS); + if (field_rb != config->all_jumps) + LOG_WARNING("%s: `-all-jumps` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->all_jumps)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_EXTENDADDRMSB); + if (field_rb != config->ext_msb) + LOG_WARNING("%s: `ext-msb` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_on_off_opts, config->ext_msb)->name, + jim_nvp_value2name_simple(nvp_on_off_opts, field_rb)->name); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_SRCID); + if (field_rb != config->srcid) + LOG_WARNING("%s: `-srcid` 0x%x is not supported, revert to 0x%x.", comp->name, + config->srcid, field_rb); + + field_rb = get_field(features, RVTRACE_ENCODER_INSTFEAT_SRCBITS); + if (field_rb != config->srcbits) + LOG_WARNING("%s: `-srcbits` 0x%x is not supported, revert to 0x%x.", comp->name, + config->srcid, field_rb); + + return ERROR_OK; +} + +static int encoder_examine(struct rvtrace_component *comp) +{ + RV_ENCODER_INFO(info); + uint32_t ctrl; + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + info->format = get_field(ctrl, RVTRACE_ENCODER_CTRL_FORMAT); + + LOG_INFO("[%s] Trace recording/protocol format: %s", comp->name, + (info->format == 0) ? "E-Trace" : (info->format == 1) ? "N-Trace" : "Unknown"); + + return ERROR_OK; +} + +static int encoder_enable(struct rvtrace_component *comp) +{ + RV_ENCODER_INFO(info); + if (info->was_enabled) + return ERROR_OK; + + if (encoder_set_config(comp) != ERROR_OK) + return ERROR_FAIL; + + if (rvtrace_enable_component(comp) != ERROR_OK) + return ERROR_FAIL; + + info->was_enabled = true; + + return ERROR_OK; +} + +static int encoder_disable(struct rvtrace_component *comp) +{ + RV_ENCODER_INFO(info); + if (!info->was_enabled) + return ERROR_OK; + + if (rvtrace_disable_component(comp) != ERROR_OK) + return ERROR_FAIL; + + if (rvtrace_poll_register(comp, RVTRACE_ENCODER_CTRL, RVTRACE_ENCODER_CTRL_EMPTY, 1) != ERROR_OK) + return ERROR_FAIL; + + info->was_enabled = false; + + return ERROR_OK; +} + +static int encoder_status(struct rvtrace_component *comp,struct command_invocation *cmd) +{ + uint32_t ctrl; + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + command_print(cmd, "Control: %s", + get_field(ctrl, RVTRACE_ENCODER_CTRL_ENABLE) ? "enabled" : "disabled"); + command_print(cmd, "InstTracing: %s", + get_field(ctrl, RVTRACE_ENCODER_CTRL_ITRACE) ? "enabled" : "disabled"); + command_print(cmd, "Empty: %d", get_field(ctrl, RVTRACE_ENCODER_CTRL_EMPTY)); + + return ERROR_OK; +} + +static int encoder_write(struct rvtrace_component *comp, const char *reg_name, uint32_t value) +{ + int offset = encoder_find_reg_offset(reg_name); + if (offset < 0) + return ERROR_FAIL; + + return rvtrace_write_reg(comp, offset, value); +} + +static int encoder_read(struct rvtrace_component *comp, const char *reg_name, uint32_t *value) +{ + int offset = encoder_find_reg_offset(reg_name); + if (offset < 0) + return ERROR_FAIL; + + return rvtrace_read_reg(comp, offset, value); +} + +static int encoder_start(struct rvtrace_component *comp) +{ + uint32_t value; + + if (encoder_enable(comp) != ERROR_OK) + return ERROR_FAIL; + + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_CTRL, &value) != ERROR_OK) + return ERROR_FAIL; + + value |= RVTRACE_ENCODER_CTRL_ITRACE; + + if (rvtrace_write_reg(comp, RVTRACE_ENCODER_CTRL, value) != ERROR_OK) + return ERROR_FAIL; + + return rvtrace_poll_register(comp, RVTRACE_ENCODER_CTRL, RVTRACE_ENCODER_CTRL_ITRACE, 1); +} + +static int encoder_stop(struct rvtrace_component *comp) +{ + RV_ENCODER_INFO(info); + if (!info->was_enabled) + return ERROR_OK; + + uint32_t value; + if (rvtrace_read_reg(comp, RVTRACE_ENCODER_CTRL, &value) != ERROR_OK) + return ERROR_FAIL; + + value &= ~RVTRACE_ENCODER_CTRL_ITRACE; + + if (rvtrace_write_reg(comp, RVTRACE_ENCODER_CTRL, value) != ERROR_OK) + return ERROR_FAIL; + + return rvtrace_poll_register(comp, RVTRACE_ENCODER_CTRL, RVTRACE_ENCODER_CTRL_ITRACE, 0); +} + +COMMAND_HANDLER(handle_encoder_start) +{ + struct rvtrace_component *comp = CMD_DATA; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (encoder_start(comp) != ERROR_OK) + return ERROR_FAIL; + + command_print(CMD, "%s: Instruction trace is being generated", comp->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_encoder_stop) +{ + struct rvtrace_component *comp = CMD_DATA; + + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (encoder_stop(comp) != ERROR_OK) + return ERROR_FAIL; + + command_print(CMD, "%s: Instruction trace is not being generated", comp->name); + + return ERROR_OK; +} + +static const struct command_registration encoder_exec_command_handlers[] = { + { + .name = "start", + .mode = COMMAND_EXEC, + .handler = handle_encoder_start, + .help = "start encoder trace", + .usage = "", + }, + { + .name = "stop", + .mode = COMMAND_EXEC, + .handler = handle_encoder_stop, + .help = "stop encoder trace", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration encoder_command_handlers[] = { + { + .name = "encoder", + .mode = COMMAND_ANY, + .help = "Encoder Command Group", + .usage = "", + .chain = encoder_exec_command_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +struct rvtrace_type rvtrace_encoder = { + .name = "encoder", + .id_type = RVTRACE_COMPONENT_TYPE_ENCODER, + .version = rvtrace_component_mkversion(1, 0), + + .rvtrace_create = encoder_create, + .rvtrace_jim_configure = encoder_jim_configure, + + .examine = encoder_examine, + + .enable = encoder_enable, + .disable = encoder_disable, + .status = encoder_status, + .write = encoder_write, + .read = encoder_read, + + .commands = encoder_command_handlers, +}; --
