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/+/9218
-- gerrit commit cb329fb3ca555c9857b0dc27fca52f1cd5e9f4c0 Author: liangzhen <[email protected]> Date: Mon Nov 3 09:16:58 2025 +0800 target/riscv: rvtrace: Add trace timestamp support Add support for the trace timestamp subcomponent. Change-Id: Ic3db0965abf23ad67a57c432d001e6586dd0dce3 Signed-off-by: liangzhen <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index 5ca8baffbb..28c7102c2c 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11803,11 +11803,23 @@ 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}. +@item @option{-timestamp} @code{on|off} - enable timestamp if encoder has a +timestamp unit. +@item @option{-ts-run-debug} @code{on|off} - sets timestamp run or halt on debug. +@item @option{-ts-mode} @code{none|external|system|core|shared|resv5|resv6|resv7} - +sets the timestamp mode. +@item @option{-ts-prescale} @emph{value} - sets the prescale timestamp input clock. @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. +@item @command{component_name timestamp reset} - reset the timestamp unit. +@item @command{component_name timestamp status} - show the timestamp status. +@item @command{component_name timestamp write} @var{reg_name} @var{value} - Write +@var{value} to the encoder timestamp register with the symbolic name @var{reg_name}. +@item @command{component_name timestamp read} @var{reg_name} - Print the value +read from the encoder timestamp register with the symbolic name @var{reg_name}. @end itemize @subsection Funnel Component @@ -11816,6 +11828,21 @@ specific to the @code{funnel} are: @itemize @item @option{-ports} @emph{ports_mask} - funnel ports opened or closed by @command{component_name enable} and @command{component_name disable}. +@item @option{-timestamp} @code{on|off} - enable timestamp if funnel has a +timestamp unit. +@item @option{-ts-run-debug} @code{on|off} - sets timestamp run or halt on debug. +@item @option{-ts-mode} @code{none|external|system|core|shared|resv5|resv6|resv7} - +sets the timestamp mode. +@item @option{-ts-prescale} @emph{value} - sets the prescale timestamp input clock. +@end itemize +The commands specific to the @code{funnel} are: +@itemize +@item @command{component_name timestamp reset} - reset the timestamp unit. +@item @command{component_name timestamp status} - show the timestamp status. +@item @command{component_name timestamp write} @var{reg_name} @var{value} - Write +@var{value} to the funnel timestamp register with the symbolic name @var{reg_name}. +@item @command{component_name timestamp read} @var{reg_name} - Print the value +read from the funnel timestamp register with the symbolic name @var{reg_name}. @end itemize @section RISC-V Architecture diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index 8a102b423a..5460506135 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -19,4 +19,5 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/riscv_semihosting.c \ %D%/riscv_trace.c \ %D%/trace_encoder.c \ - %D%/trace_funnel.c + %D%/trace_funnel.c \ + %D%/trace_timestamp.c diff --git a/src/target/riscv/riscv_trace.h b/src/target/riscv/riscv_trace.h index b182a81fc1..fe881cbc4c 100644 --- a/src/target/riscv/riscv_trace.h +++ b/src/target/riscv/riscv_trace.h @@ -84,6 +84,23 @@ struct rvtrace_type { const struct command_registration *commands; }; +enum rv_timestamp_mode { + RV_TIMESTAMP_MODE_NONE, + RV_TIMESTAMP_MODE_EXTERNAL, + RV_TIMESTAMP_MODE_SYSTEM, + RV_TIMESTAMP_MODE_CORE, + RV_TIMESTAMP_MODE_SHARED, + RV_TIMESTAMP_MODE_RESV5, + RV_TIMESTAMP_MODE_RESV6, + RV_TIMESTAMP_MODE_RESV7, +}; + +struct rv_timestamp_config { + bool run_debug; + enum rv_timestamp_mode mode; + uint32_t prescale; +}; + #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) @@ -102,6 +119,12 @@ int rvtrace_cleanup_all(void); int rvtrace_register_commands(struct command_context *cmd_ctx); +int rv_timestamp_jim_configure(struct rv_timestamp_config *config, struct jim_getopt_info *goi); +int rvtrace_examine_timestamp(struct rvtrace_component *comp); +int rvtrace_enable_timestamp(struct rvtrace_component *comp, struct rv_timestamp_config *config); +int rvtrace_disable_timestamp(struct rvtrace_component *comp); +extern const struct command_registration rv_timestamp_command_handlers[]; + extern struct rvtrace_type rvtrace_encoder; extern struct rvtrace_type rvtrace_funnel; diff --git a/src/target/riscv/trace_encoder.c b/src/target/riscv/trace_encoder.c index b752775033..07baa3d038 100644 --- a/src/target/riscv/trace_encoder.c +++ b/src/target/riscv/trace_encoder.c @@ -60,6 +60,7 @@ enum rvtrace_format { struct rv_encoder_info { bool was_enabled; enum rvtrace_format format; + bool ts_examined; }; enum inst_mode { @@ -107,6 +108,8 @@ struct rv_encoder_private_config { bool ext_msb; uint32_t srcid; uint32_t srcbits; + bool ts_ctrl; + struct rv_timestamp_config timestamp_config; }; static inline struct rv_encoder_info *rv_encoder_info(struct rvtrace_component *comp) @@ -138,6 +141,8 @@ static struct rv_encoder_private_config *alloc_default_rv_encoder_private_config config->sync_mode = SYNCMODE_CLOCK; config->sync_max = RVTRACE_ENCODER_CTRL_INSTSYNCMAX_MAXVAL; + memset(&config->timestamp_config, 0, sizeof(struct rv_timestamp_config)); + return config; } @@ -232,7 +237,8 @@ enum rv_encoder_cfg_opts { RV_ENCODER_CFG_ALL_JUMPS, RV_ENCODER_CFG_EXT_MSB, RV_ENCODER_CFG_SRCID, - RV_ENCODER_CFG_SRCBITS + RV_ENCODER_CFG_SRCBITS, + RV_ENCODER_CFG_TIMESTAMP }; static struct jim_nvp nvp_config_opts[] = { @@ -255,11 +261,13 @@ static struct jim_nvp nvp_config_opts[] = { { .name = "-ext-msb", .value = RV_ENCODER_CFG_EXT_MSB }, { .name = "-srcid", .value = RV_ENCODER_CFG_SRCID }, { .name = "-srcbits", .value = RV_ENCODER_CFG_SRCBITS }, + { .name = "-timestamp", .value = RV_ENCODER_CFG_TIMESTAMP }, { .name = NULL, .value = -1 } }; static int encoder_jim_configure(struct rvtrace_component *comp, struct jim_getopt_info *goi) { + int e; struct rv_encoder_private_config *config = comp->private_config; if (!config) { config = alloc_default_rv_encoder_private_config(); @@ -267,12 +275,25 @@ static int encoder_jim_configure(struct rvtrace_component *comp, struct jim_geto return JIM_ERR; comp->private_config = config; } + struct rv_timestamp_config *timestamp_config = &config->timestamp_config; if (!goi->argc) return JIM_OK; + if (config->ts_ctrl) { + e = rv_timestamp_jim_configure(timestamp_config, goi); + if (e == JIM_OK) { + /* more? */ + return JIM_OK; + } + if (e == JIM_ERR) { + /* An error */ + return JIM_ERR; + } + } + struct jim_nvp *n; - int e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, goi->argv[0], &n); + e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, goi->argv[0], &n); if (e != JIM_OK) return JIM_CONTINUE; @@ -554,6 +575,19 @@ static int encoder_jim_configure(struct rvtrace_component *comp, struct jim_geto Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, config->srcid)); } break; + case RV_ENCODER_CFG_TIMESTAMP: + if (goi->is_configure) { + struct jim_nvp *timestamp_nvp; + e = jim_getopt_nvp(goi, nvp_on_off_opts, ×tamp_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_on_off_opts, /*hadprefix*/ true); + return e; + } + config->ts_ctrl = timestamp_nvp->value; + } else { + Jim_SetResultString(goi->interp, jim_nvp_value2name_simple(nvp_on_off_opts, config->ts_ctrl)->name, -1); + } + break; } return JIM_OK; } @@ -744,6 +778,14 @@ static int encoder_enable(struct rvtrace_component *comp) info->was_enabled = true; + struct rv_encoder_private_config *config = rv_encoder_private_config(comp); + if (config->ts_ctrl) { + if (!info->ts_examined && rvtrace_examine_timestamp(comp) == ERROR_OK) + info->ts_examined = true; + if (info->ts_examined && rvtrace_enable_timestamp(comp, &config->timestamp_config) != ERROR_OK) + LOG_ERROR("%s: fail to enable timestamp", comp->name); + } + return ERROR_OK; } @@ -761,6 +803,12 @@ static int encoder_disable(struct rvtrace_component *comp) info->was_enabled = false; + struct rv_encoder_private_config *config = rv_encoder_private_config(comp); + if (config->ts_ctrl) { + if (info->ts_examined && rvtrace_disable_timestamp(comp) != ERROR_OK) + LOG_ERROR("%s: fail to disable timestamp", comp->name); + } + return ERROR_OK; } @@ -889,6 +937,9 @@ static const struct command_registration encoder_command_handlers[] = { .usage = "", .chain = encoder_exec_command_handlers }, + { + .chain = rv_timestamp_command_handlers, + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/riscv/trace_funnel.c b/src/target/riscv/trace_funnel.c index afce53330b..decefa7a36 100644 --- a/src/target/riscv/trace_funnel.c +++ b/src/target/riscv/trace_funnel.c @@ -31,10 +31,13 @@ struct rv_funnel_info { bool was_enabled; uint32_t disinput_lenb; + bool ts_examined; }; struct rv_funnel_private_config { uint32_t ports; + bool ts_ctrl; + struct rv_timestamp_config timestamp_config; }; static inline struct rv_funnel_info *rv_funnel_info(struct rvtrace_component *comp) @@ -60,6 +63,7 @@ static struct rv_funnel_private_config *alloc_default_rv_funnel_private_config(v } config->ports = RVTRACE_FUNNEL_DISINPUT_DISINPUT; + memset(&config->timestamp_config, 0, sizeof(struct rv_timestamp_config)); return config; } @@ -99,20 +103,30 @@ static int funnel_create(struct rvtrace_component *comp) LOG_ERROR("Failed to allocate funnel info structure."); return ERROR_FAIL; } + return ERROR_OK; } +static struct jim_nvp nvp_timestamp_opts[] = { + { .name = "off", .value = false }, + { .name = "on", .value = true }, + { .name = NULL, .value = -1 } +}; + enum rv_funnel_cfg_opts { CS_FUNNEL_CFG_PORTS, + CS_FUNNEL_CFG_TIMESTAMP }; static struct jim_nvp nvp_config_opts[] = { { .name = "-ports", .value = CS_FUNNEL_CFG_PORTS }, + { .name = "-timestamp", .value = CS_FUNNEL_CFG_TIMESTAMP }, { .name = NULL, .value = -1 } }; static int funnel_jim_configure(struct rvtrace_component *comp, struct jim_getopt_info *goi) { + int e; struct rv_funnel_private_config *config = comp->private_config; if (!config) { config = alloc_default_rv_funnel_private_config(); @@ -120,12 +134,26 @@ static int funnel_jim_configure(struct rvtrace_component *comp, struct jim_getop return JIM_ERR; comp->private_config = config; } + struct rv_timestamp_config *timestamp_config = &config->timestamp_config; if (!goi->argc) return JIM_OK; + if (config->ts_ctrl) { + e = rv_timestamp_jim_configure(timestamp_config, goi); + if (e == JIM_OK) { + /* more? */ + return JIM_OK; + } + if (e == JIM_ERR) { + /* An error */ + return JIM_ERR; + } + + } + struct jim_nvp *n; - int e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, goi->argv[0], &n); + e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, goi->argv[0], &n); if (e != JIM_OK) return JIM_CONTINUE; @@ -156,6 +184,19 @@ static int funnel_jim_configure(struct rvtrace_component *comp, struct jim_getop Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, config->ports)); } break; + case CS_FUNNEL_CFG_TIMESTAMP: + if (goi->is_configure) { + struct jim_nvp *timestamp_nvp; + e = jim_getopt_nvp(goi, nvp_timestamp_opts, ×tamp_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_timestamp_opts, /*hadprefix*/ true); + return e; + } + config->ts_ctrl = timestamp_nvp->value; + } else { + Jim_SetResultString(goi->interp, jim_nvp_value2name_simple(nvp_timestamp_opts, config->ts_ctrl)->name, -1); + } + break; } return JIM_OK; } @@ -205,6 +246,13 @@ static int funnel_enable(struct rvtrace_component *comp) info->was_enabled = true; + if (config->ts_ctrl) { + if (!info->ts_examined && rvtrace_examine_timestamp(comp) == ERROR_OK) + info->ts_examined = true; + if (info->ts_examined && rvtrace_enable_timestamp(comp, &config->timestamp_config) != ERROR_OK) + LOG_ERROR("%s: fail to enable timestamp", comp->name); + } + return ERROR_OK; } @@ -223,6 +271,12 @@ static int funnel_disable(struct rvtrace_component *comp) info->was_enabled = false; + struct rv_funnel_private_config *config = rv_funnel_private_config(comp); + if (config->ts_ctrl) { + if (info->ts_examined && rvtrace_disable_timestamp(comp) != ERROR_OK) + LOG_ERROR("%s: fail to disable timestamp", comp->name); + } + return ERROR_OK; } @@ -262,6 +316,13 @@ static int funnel_read(struct rvtrace_component *comp, const char *reg_name, uin return rvtrace_read_reg(comp, offset, value); } +static const struct command_registration funnel_command_handlers[] = { + { + .chain = rv_timestamp_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + struct rvtrace_type rvtrace_funnel = { .name = "funnel", .id_type = RVTRACE_COMPONENT_TYPE_FUNNEL, @@ -277,4 +338,6 @@ struct rvtrace_type rvtrace_funnel = { .status = funnel_status, .write = funnel_write, .read = funnel_read, + + .commands = funnel_command_handlers, }; diff --git a/src/target/riscv/trace_timestamp.c b/src/target/riscv/trace_timestamp.c new file mode 100644 index 0000000000..665aadfb8e --- /dev/null +++ b/src/target/riscv/trace_timestamp.c @@ -0,0 +1,363 @@ +/* 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" + +/* Control register */ +#define RVTRACE_TIMESTAMP_CTRL 0x040 +#define RVTRACE_TIMESTAMP_CTRL_ACTIVE 0x1 +#define RVTRACE_TIMESTAMP_CTRL_COUNT 0x2 +#define RVTRACE_TIMESTAMP_CTRL_RESET 0x4 +#define RVTRACE_TIMESTAMP_CTRL_RUNINDEBUG 0x8 +#define RVTRACE_TIMESTAMP_CTRL_MODE 0x70 +#define RVTRACE_TIMESTAMP_CTRL_PRESCALE 0x300 +#define RVTRACE_TIMESTAMP_CTRL_ENABLE 0x8000 +#define RVTRACE_TIMESTAMP_CTRL_WIDTH 0x3f000000 + +#define RVTRACE_TIMESTAMP_CTRL_PRESCALE_MAX 3 + +/* Counter register */ +#define RVTRACE_TIMESTAMP_COUNTER_LOW 0x048 +#define RVTRACE_TIMESTAMP_COUNTER_HIGH 0x04c + +static const struct { + uint32_t offset; + const char *label; +} timestamp_names[] = { + { RVTRACE_TIMESTAMP_CTRL, "CONTROL" }, + { RVTRACE_TIMESTAMP_COUNTER_LOW, "COUNTERLOW" }, + { RVTRACE_TIMESTAMP_COUNTER_HIGH, "COUNTERHIGH" }, +}; + +static int timestamp_find_reg_offset(const char *name) +{ + for (size_t i = 0; i < ARRAY_SIZE(timestamp_names); i++) { + if (!strcasecmp(name, timestamp_names[i].label)) + return timestamp_names[i].offset; + } + + LOG_ERROR("unknown rvtrace timestamp register %s", name); + return -1; +} + +static struct jim_nvp nvp_run_debug_opts[] = { + { .name = "off", .value = false }, + { .name = "on", .value = true }, + { .name = NULL, .value = -1 } +}; + +static struct jim_nvp nvp_mode_opts[] = { + { .name = "none", .value = RV_TIMESTAMP_MODE_NONE }, + { .name = "external", .value = RV_TIMESTAMP_MODE_EXTERNAL }, + { .name = "system", .value = RV_TIMESTAMP_MODE_SYSTEM }, + { .name = "core", .value = RV_TIMESTAMP_MODE_SYSTEM }, + { .name = "shared", .value = RV_TIMESTAMP_MODE_SHARED }, + { .name = "resv5", .value = RV_TIMESTAMP_MODE_RESV5 }, + { .name = "resv6", .value = RV_TIMESTAMP_MODE_RESV6 }, + { .name = "resv7", .value = RV_TIMESTAMP_MODE_RESV7 }, + { .name = NULL, .value = -1 } +}; + +enum rv_timestamp_cfg_opts { + RV_TIMESTAMP_CFG_RUN_DEBUG, + RV_TIMESTAMP_CFG_MODE, + RV_TIMESTAMP_CFG_PRESCALE +}; + +static struct jim_nvp nvp_config_opts[] = { + { .name = "-ts-run-debug", .value = RV_TIMESTAMP_CFG_RUN_DEBUG }, + { .name = "-ts-mode", .value = RV_TIMESTAMP_CFG_MODE }, + { .name = "-ts-prescale", .value = RV_TIMESTAMP_CFG_PRESCALE }, + { .name = NULL, .value = -1 } +}; + +int rv_timestamp_jim_configure(struct rv_timestamp_config *config, struct jim_getopt_info *goi) +{ + 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_TIMESTAMP_CFG_RUN_DEBUG: + if (goi->is_configure) { + struct jim_nvp *run_debug_nvp; + e = jim_getopt_nvp(goi, nvp_run_debug_opts, &run_debug_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_run_debug_opts, /*hadprefix*/ true); + return e; + } + config->run_debug = run_debug_nvp->value; + } else { + Jim_SetResultString(goi->interp, jim_nvp_value2name_simple(nvp_run_debug_opts, config->run_debug)->name, -1); + } + break; + case RV_TIMESTAMP_CFG_MODE: + if (goi->is_configure) { + struct jim_nvp *mode_nvp; + e = jim_getopt_nvp(goi, nvp_mode_opts, &mode_nvp); + if (e != JIM_OK) { + jim_getopt_nvp_unknown(goi, nvp_mode_opts, /*hadprefix*/ true); + return e; + } + config->mode = mode_nvp->value; + } else { + Jim_SetResultString(goi->interp, jim_nvp_value2name_simple(nvp_mode_opts, config->mode)->name, -1); + } + break; + case RV_TIMESTAMP_CFG_PRESCALE: + if (goi->is_configure) { + jim_wide w; + e = jim_getopt_wide(goi, &w); + if (e != JIM_OK) + return e; + if (w < 0 && w > RVTRACE_TIMESTAMP_CTRL_PRESCALE_MAX) { + Jim_SetResultString(goi->interp, "Invalid timestamp prescale(>3)!", -1); + return JIM_ERR; + } + config->prescale = (uint64_t)w; + } else { + Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, config->prescale)); + } + break; + } + + return JIM_OK; +} + +static int rvtrace_reset_timestamp(struct rvtrace_component *comp) +{ + if (rvtrace_write_reg(comp, RVTRACE_TIMESTAMP_CTRL, 0x0) != ERROR_OK) + return ERROR_FAIL; + + if (rvtrace_poll_register(comp, RVTRACE_TIMESTAMP_CTRL, RVTRACE_TIMESTAMP_CTRL_ACTIVE, 0x0) != ERROR_OK) + return ERROR_FAIL; + + if (rvtrace_write_reg(comp, RVTRACE_TIMESTAMP_CTRL, 0x1) != ERROR_OK) + return ERROR_FAIL; + + return rvtrace_poll_register(comp, RVTRACE_TIMESTAMP_CTRL, RVTRACE_TIMESTAMP_CTRL_ACTIVE, 0x1); +} + +int rvtrace_examine_timestamp(struct rvtrace_component *comp) +{ + int ret = ERROR_OK; + + uint32_t ctrl; + if (rvtrace_read_reg(comp, RVTRACE_TIMESTAMP_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + /* if separated reset for timestamp component is not implemented, + * a read-only copy of the corresponding trTeActive or trFunnelActive + * bit. + */ + if (!get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_ACTIVE)) + ret = rvtrace_reset_timestamp(comp); + + if (ret != ERROR_OK) + LOG_ERROR("%s: does not have a timestamp subcomponent", comp->name); + + return ret; +} + +int rvtrace_enable_timestamp(struct rvtrace_component *comp, struct rv_timestamp_config *config) +{ + uint32_t ctrl, field_rb; + ctrl = RVTRACE_TIMESTAMP_CTRL_ACTIVE | RVTRACE_TIMESTAMP_CTRL_COUNT | + RVTRACE_TIMESTAMP_CTRL_ENABLE; + ctrl = set_field(ctrl, RVTRACE_TIMESTAMP_CTRL_RUNINDEBUG, config->run_debug); + ctrl = set_field(ctrl, RVTRACE_TIMESTAMP_CTRL_MODE, config->mode); + ctrl = set_field(ctrl, RVTRACE_TIMESTAMP_CTRL_PRESCALE, config->prescale); + + if (rvtrace_write_reg(comp, RVTRACE_TIMESTAMP_CTRL, ctrl) != ERROR_OK) + return ERROR_FAIL; + + /* read back ctrl register and check WARL bits */ + if (rvtrace_read_reg(comp, RVTRACE_TIMESTAMP_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + field_rb = get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_RUNINDEBUG); + if (field_rb != config->run_debug) + LOG_WARNING("%s: `-ts-run-debug` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_run_debug_opts, config->run_debug)->name, + jim_nvp_value2name_simple(nvp_run_debug_opts,field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_MODE); + if (field_rb != config->mode) + LOG_WARNING("%s: `-ts-mode` %s is not supported, revert to %s.", comp->name, + jim_nvp_value2name_simple(nvp_mode_opts, config->mode)->name, + jim_nvp_value2name_simple(nvp_mode_opts, field_rb)->name); + + field_rb = get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_PRESCALE); + if (field_rb != config->prescale) + LOG_WARNING("%s: `-ts-prescale` %d is not supported, revert to %d.", comp->name, + config->prescale, field_rb); + + return ERROR_OK; +} + +int rvtrace_disable_timestamp(struct rvtrace_component *comp) +{ + uint32_t ctrl; + if (rvtrace_read_reg(comp, RVTRACE_TIMESTAMP_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + ctrl = set_field(ctrl, RVTRACE_TIMESTAMP_CTRL_ENABLE, 0x0); + ctrl = set_field(ctrl, RVTRACE_TIMESTAMP_CTRL_COUNT, 0x0); + + return rvtrace_write_reg(comp, RVTRACE_TIMESTAMP_CTRL, ctrl); +} + +static int timestamp_write(struct rvtrace_component *comp, const char *reg_name, uint32_t value) +{ + int offset = timestamp_find_reg_offset(reg_name); + if (offset < 0) + return ERROR_FAIL; + + return rvtrace_write_reg(comp, offset, value); +} + +static int timestamp_read(struct rvtrace_component *comp, const char *reg_name, uint32_t *value) +{ + int offset = timestamp_find_reg_offset(reg_name); + if (offset < 0) + return ERROR_FAIL; + + return rvtrace_read_reg(comp, offset, value); +} + +COMMAND_HANDLER(handle_timestamp_reset) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + return rvtrace_reset_timestamp(comp); +} + +COMMAND_HANDLER(handle_timestamp_status) +{ + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + uint32_t ctrl; + if (rvtrace_read_reg(comp, RVTRACE_TIMESTAMP_CTRL, &ctrl) != ERROR_OK) + return ERROR_FAIL; + + command_print(cmd, "Control: %s", + get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_ENABLE) ? "enabled" : "disabled"); + command_print(cmd, "Mode: %s", jim_nvp_value2name_simple(nvp_mode_opts, + get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_MODE))->name); + command_print(cmd, "Width: 0x%x", get_field(ctrl, RVTRACE_TIMESTAMP_CTRL_WIDTH)); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_timestamp_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 = timestamp_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; + } + + return retval; +} + +COMMAND_HANDLER(handle_timestamp_read) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct rvtrace_component *comp = CMD_DATA; + + uint32_t value; + int retval = timestamp_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; +} + +static const struct command_registration timestamp_exec_command_handlers[] = { + { + .name = "reset", + .mode = COMMAND_EXEC, + .handler = handle_timestamp_reset, + .help = "reset timestamp", + .usage = "", + }, + { + .name = "status", + .mode = COMMAND_EXEC, + .handler = handle_timestamp_status, + .help = "show riscv timestamp subcomponent status", + .usage = "", + }, + { + .name = "write", + .mode = COMMAND_EXEC, + .handler = handle_timestamp_write, + .help = "write to a riscv timestamp subcomponent register", + .usage = "<register> <value>", + }, + { + .name = "read", + .mode = COMMAND_EXEC, + .handler = handle_timestamp_read, + .help = "read a riscv timestamp subcomponent register", + .usage = "<register>", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct command_registration rv_timestamp_command_handlers[] = { + { + .name = "timestamp", + .mode = COMMAND_ANY, + .help = "Timestamp command group", + .usage = "", + .chain = timestamp_exec_command_handlers + }, + COMMAND_REGISTRATION_DONE +}; --
