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, 
&timestamp_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, 
&timestamp_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
+};

-- 

Reply via email to