From: Konstantin Semichastnov <k.semichastnov@syntacore.com>
To: Daniel Henrique Barboza <dbarboza@ventanamicro.com>,
<qemu-devel@nongnu.org>
Cc: <qemu-riscv@nongnu.org>, <alistair.francis@wdc.com>,
<liwei1518@gmail.com>, <zhiwei_liu@linux.alibaba.com>,
<palmer@dabbelt.com>
Subject: Re: [PATCH v2 01/17] hw/riscv: Trace Encoder initial impl
Date: Thu, 20 Nov 2025 18:56:55 +0300 [thread overview]
Message-ID: <1ba7cae6-7683-4958-9c20-cef8fb0a8f46@syntacore.com> (raw)
In-Reply-To: <20251111114656.2285048-2-dbarboza@ventanamicro.com>
On 11/11/25 14:46, Daniel Henrique Barboza wrote:
> The Trace Encoder is a hardware module that interacts with a CPU,
> gathering execution information (a.k.a instruction delta trace), and
> send it downstream to other storage components such as a RAM Sink (a RAM
> storage). This trace info can then be read via software (e.g. perf) to
> reproduce the behavior of a given binary that ran in the CPU.
>
> This implementation is based on the Efficient Trace for RISC-V [1] and
> RISC-V Trace Control Interface Specification [2]. It's not intended to
> implement all spec features: the idea is to provide a base where we can
> add extra features on demand.
>
> We'll get back to the instruction delta trace and how we'll instrument
> TCG to produce it later. For now we'll set the minimal components to get
> the basic framework running.
>
> This Trace Encoder impl has just the minimal bits specified in [2],
> section "Minimal Implementation". RO and RSVP bits are taken verbatim
> from [2] without considering what we're actually going to support. The
> base impl is heavily inspired by the XLNZ-ZDMA device w.r.t the usage of
> the RegisterInfo and register.h framework.
>
> Discovery of the Trace Encoder will be made via fdt, a single entry per
> CPU. We'll connect each Trace Encoder to its CPU in the 'virt' board
> later.
>
> [1] https://github.com/riscv-non-isa/riscv-trace-spec/releases/download/v2.0-20250616/riscv-trace-spec-asciidoc.pdf
> [2] https://github.com/riscv-non-isa/tg-nexus-trace/releases/download/1.0_Ratified/RISC-V-Trace-Control-Interface.pdf
>
> Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
> ---
> hw/riscv/Kconfig | 5 +
> hw/riscv/meson.build | 1 +
> hw/riscv/trace-encoder.c | 216 +++++++++++++++++++++++++++++++++++++++
> hw/riscv/trace-encoder.h | 42 ++++++++
> hw/riscv/trace-events | 4 +
> 5 files changed, 268 insertions(+)
> create mode 100644 hw/riscv/trace-encoder.c
> create mode 100644 hw/riscv/trace-encoder.h
>
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index fc9c35bd98..2de0892496 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -7,6 +7,10 @@ config RISCV_NUMA
> config IBEX
> bool
>
> +config RISCV_TRACE
> + bool
> + select REGISTER
> +
> # RISC-V machines in alphabetical order
>
> config MICROCHIP_PFSOC
> @@ -68,6 +72,7 @@ config RISCV_VIRT
> select PLATFORM_BUS
> select ACPI
> select ACPI_PCI
> + select RISCV_TRACE
>
> config SHAKTI_C
> bool
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index 2a8d5b136c..b4a9988a62 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -14,5 +14,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
> 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
> riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c'))
> riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c'))
> +riscv_ss.add(when: 'CONFIG_RISCV_TRACE', if_true: files('trace-encoder.c'))
>
> hw_arch += {'riscv': riscv_ss}
> diff --git a/hw/riscv/trace-encoder.c b/hw/riscv/trace-encoder.c
> new file mode 100644
> index 0000000000..21bf650a6a
> --- /dev/null
> +++ b/hw/riscv/trace-encoder.c
> @@ -0,0 +1,216 @@
> +/*
> + * Emulation of a RISC-V Trace Encoder
> + *
> + * Copyright (C) 2025 Ventana Micro Systems Inc.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "trace-encoder.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qapi/error.h"
> +#include "trace.h"
> +#include "system/device_tree.h"
> +#include "hw/register.h"
> +#include "cpu.h"
> +
> +/*
> + * trTeControl register fields
> + */
> +REG32(TR_TE_CONTROL, 0x0)
> + FIELD(TR_TE_CONTROL, ACTIVE, 0, 1)
> + FIELD(TR_TE_CONTROL, ENABLE, 1, 1)
> + FIELD(TR_TE_CONTROL, INST_TRACING, 2, 1)
> + FIELD(TR_TE_CONTROL, EMPTY, 3, 1)
> + FIELD(TR_TE_CONTROL, INST_MODE, 4, 3)
> + FIELD(TR_TE_CONTROL, INST_SYNC_MODE, 16, 2)
> + FIELD(TR_TE_CONTROL, FORMAT, 24, 3)
> + /* reserved bits */
> + FIELD(TR_TE_CONTROL, RSVP1, 7, 2)
> + FIELD(TR_TE_CONTROL, RSVP2, 10, 1)
> + FIELD(TR_TE_CONTROL, RSVP3, 14, 1)
> + FIELD(TR_TE_CONTROL, RSVP4, 18, 2)
> + FIELD(TR_TE_CONTROL, RSVP5, 27, 4)
RSVP5 should be 5 bits long, as it occupies bits [27:31]
> +
> +#define R_TR_TE_CONTROL_RSVP_BITS (MAKE_64BIT_MASK(32, 32) | \
> + R_TR_TE_CONTROL_RSVP1_MASK | \
> + R_TR_TE_CONTROL_RSVP2_MASK | \
> + R_TR_TE_CONTROL_RSVP3_MASK | \
> + R_TR_TE_CONTROL_RSVP4_MASK | \
> + R_TR_TE_CONTROL_RSVP5_MASK)
> +
> +/* trTeControlEmpty is the only RO field and reset value */
> +#define R_TR_TE_CONTROL_RESET R_TR_TE_CONTROL_EMPTY_MASK
> +#define R_TR_TE_CONTROL_RO_BITS R_TR_TE_CONTROL_EMPTY_MASK
> +
> +/*
> + * trTeImpl register fields
> + */
> +REG32(TR_TE_IMPL, 0x4)
> + FIELD(TR_TE_IMPL, VER_MAJOR, 0, 4)
> + FIELD(TR_TE_IMPL, VER_MINOR, 4, 4)
> + FIELD(TR_TE_IMPL, COMP_TYPE, 8, 4)
> + FIELD(TR_TE_IMPL, PROTOCOL_MAJOR, 16, 4)
> + FIELD(TR_TE_IMPL, PROTOCOL_MINOR, 20, 4)
> + /* reserved bits */
> + FIELD(TR_TE_IMPL, RSVP1, 12, 4)
> + FIELD(TR_TE_IMPL, RSVP2, 24, 8)
> +
> +#define R_TR_TE_IMPL_RSVP_BITS (MAKE_64BIT_MASK(32, 32) | \
> + R_TR_TE_IMPL_RSVP1_MASK | \
> + R_TR_TE_IMPL_RSVP2_MASK)
> +
> +#define R_TR_TE_IMPL_RO_BITS (R_TR_TE_IMPL_VER_MAJOR_MASK | \
> + R_TR_TE_IMPL_VER_MINOR_MASK | \
> + R_TR_TE_IMPL_COMP_TYPE_MASK | \
> + R_TR_TE_IMPL_PROTOCOL_MAJOR_MASK | \
> + R_TR_TE_IMPL_PROTOCOL_MINOR_MASK)
> +
> +#define R_TR_TE_IMPL_RESET (BIT(0) | BIT(8))
> +
> +static RegisterAccessInfo trencoder_regs_info[] = {
> + { .name = "TR_TE_CONTROL", .addr = A_TR_TE_CONTROL,
> + .rsvd = R_TR_TE_CONTROL_RSVP_BITS,
> + .reset = R_TR_TE_CONTROL_RESET,
> + .ro = R_TR_TE_CONTROL_RO_BITS,
> + },
> + { .name = "TR_TE_IMPL", .addr = A_TR_TE_IMPL,
> + .rsvd = R_TR_TE_IMPL_RSVP_BITS,
> + .reset = R_TR_TE_IMPL_RESET,
> + .ro = R_TR_TE_IMPL_RO_BITS,
> + },
> +};
> +
> +static uint64_t trencoder_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + TraceEncoder *te = TRACE_ENCODER(opaque);
> + RegisterInfo *r = &te->regs_info[addr / 4];
> +
> + if (!r->data) {
> + trace_trencoder_read_error(addr);
> + return 0;
> + }
> +
> + return register_read(r, ~0, NULL, false);
> +}
> +
> +static void trencoder_write(void *opaque, hwaddr addr,
> + uint64_t value, unsigned size)
> +{
> + TraceEncoder *te = TRACE_ENCODER(opaque);
> + RegisterInfo *r = &te->regs_info[addr / 4];
> +
> + if (!r->data) {
> + trace_trencoder_write_error(addr, value);
> + return;
> + }
> +
> + register_write(r, value, ~0, NULL, false);
> +}
> +
> +static const MemoryRegionOps trencoder_ops = {
> + .read = trencoder_read,
> + .write = trencoder_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + },
> +};
> +
> +static void trencoder_reset(DeviceState *dev)
> +{
> + TraceEncoder *te = TRACE_ENCODER(dev);
> +
> + for (int i = 0; i < ARRAY_SIZE(te->regs_info); i++) {
> + register_reset(&te->regs_info[i]);
> + }
> +}
> +
> +static void trencoder_realize(DeviceState *dev, Error **errp)
> +{
> + TraceEncoder *te = TRACE_ENCODER(dev);
> +
> + memory_region_init_io(&te->reg_mem, OBJECT(dev),
> + &trencoder_ops, te,
> + TYPE_TRACE_ENCODER,
> + te->reg_mem_size);
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &te->reg_mem);
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, te->baseaddr);
> +
> + /* RegisterInfo init taken from hw/dma/xlnx-zdma.c */
> + for (int i = 0; i < ARRAY_SIZE(trencoder_regs_info); i++) {
> + uint32_t reg_idx = trencoder_regs_info[i].addr / 4;
> + RegisterInfo *r = &te->regs_info[reg_idx];
> +
> + *r = (RegisterInfo) {
> + .data = (uint8_t *)&te->regs[reg_idx],
> + .data_size = sizeof(uint32_t),
> + .access = &trencoder_regs_info[i],
> + .opaque = te,
> + };
> + }
> +}
> +
> +static const Property trencoder_props[] = {
> + /*
> + * We need a link to the associated CPU to
> + * enable/disable tracing.
> + */
> + DEFINE_PROP_LINK("cpu", TraceEncoder, cpu, TYPE_RISCV_CPU, RISCVCPU *),
> + DEFINE_PROP_UINT64("baseaddr", TraceEncoder, baseaddr, 0),
> + DEFINE_PROP_UINT64("dest-baseaddr", TraceEncoder, dest_baseaddr, 0),
> + DEFINE_PROP_UINT64("ramsink-ramstart", TraceEncoder,
> + ramsink_ramstart, 0),
> + DEFINE_PROP_UINT64("ramsink-ramlimit", TraceEncoder,
> + ramsink_ramlimit, 0),
> + DEFINE_PROP_UINT32("reg-mem-size", TraceEncoder,
> + reg_mem_size, TRACE_R_MAX * 4),
> + DEFINE_PROP_INT32("cpu-id", TraceEncoder, cpu_id, 0),
> +};
> +
> +static const VMStateDescription vmstate_trencoder = {
> + .name = TYPE_TRACE_ENCODER,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (const VMStateField[]) {
> + VMSTATE_UINT32_ARRAY(regs, TraceEncoder, TRACE_R_MAX),
> + VMSTATE_UINT64(baseaddr, TraceEncoder),
> + VMSTATE_UINT64(dest_baseaddr, TraceEncoder),
> + VMSTATE_UINT64(ramsink_ramstart, TraceEncoder),
> + VMSTATE_UINT64(ramsink_ramlimit, TraceEncoder),
> + VMSTATE_INT32(cpu_id, TraceEncoder),
> + VMSTATE_END_OF_LIST(),
> + }
> +};
> +
> +static void trencoder_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + device_class_set_legacy_reset(dc, trencoder_reset);
> + device_class_set_props(dc, trencoder_props);
> + dc->realize = trencoder_realize;
> + dc->vmsd = &vmstate_trencoder;
> +}
> +
> +static const TypeInfo trencoder_info = {
> + .name = TYPE_TRACE_ENCODER,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(TraceEncoder),
> + .class_init = trencoder_class_init,
> +};
> +
> +static void trencoder_register_types(void)
> +{
> + type_register_static(&trencoder_info);
> +}
> +
> +type_init(trencoder_register_types)
> diff --git a/hw/riscv/trace-encoder.h b/hw/riscv/trace-encoder.h
> new file mode 100644
> index 0000000000..71002f58a4
> --- /dev/null
> +++ b/hw/riscv/trace-encoder.h
> @@ -0,0 +1,42 @@
> +/*
> + * Emulation of a RISC-V Trace Encoder
> + *
> + * Copyright (C) 2025 Ventana Micro Systems Inc.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef RISCV_TRACE_ENCODER_H
> +#define RISCV_TRACE_ENCODER_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/register.h"
> +#include "system/dma.h"
> +#include "qom/object.h"
> +#include "cpu.h"
> +
> +#define TRACE_R_MAX (0xFFF / 4)
> +
> +struct TraceEncoder {
> + /*< private >*/
> + SysBusDevice parent_obj;
> +
> + RISCVCPU *cpu;
> + int cpu_id;
> +
> + MemoryRegion reg_mem;
> + uint32_t reg_mem_size;
> +
> + hwaddr baseaddr;
> + hwaddr dest_baseaddr;
> + hwaddr ramsink_ramstart;
> + hwaddr ramsink_ramlimit;
> + uint32_t regs[TRACE_R_MAX];
> + RegisterInfo regs_info[TRACE_R_MAX];
> +};
> +
> +#define TYPE_TRACE_ENCODER "trace-encoder"
> +
> +OBJECT_DECLARE_SIMPLE_TYPE(TraceEncoder, TRACE_ENCODER)
> +
> +#endif
> diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events
> index b50b14a654..0cbf6ffcb6 100644
> --- a/hw/riscv/trace-events
> +++ b/hw/riscv/trace-events
> @@ -24,3 +24,7 @@ riscv_iommu_hpm_incr_ctr(uint64_t cntr_val) "cntr_val 0x%"PRIx64
> riscv_iommu_hpm_iocntinh_cy(bool prev_cy_inh) "prev_cy_inh %d"
> riscv_iommu_hpm_cycle_write(uint32_t ovf, uint64_t val) "ovf 0x%x val 0x%"PRIx64
> riscv_iommu_hpm_evt_write(uint32_t ctr_idx, uint32_t ovf, uint64_t val) "ctr_idx 0x%x ovf 0x%x val 0x%"PRIx64
> +
> +# trace-encoder.c
> +trencoder_read_error(uint64_t addr) "addr 0x%" PRIx64
> +trencoder_write_error(uint64_t addr, uint64_t value) "addr 0x%" PRIx64 " value 0x%" PRIx64
next prev parent reply other threads:[~2025-11-20 15:58 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-11 11:46 [PATCH v2 00/17] hw/riscv, target/riscv: initial e-trace support Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 01/17] hw/riscv: Trace Encoder initial impl Daniel Henrique Barboza
2025-11-20 15:56 ` Konstantin Semichastnov [this message]
2025-11-11 11:46 ` [PATCH v2 02/17] hw/riscv: Trace RAM Sink " Daniel Henrique Barboza
2025-11-20 18:03 ` Konstantin Semichastnov
2025-11-11 11:46 ` [PATCH v2 03/17] hw/riscv/trace-encoder: add trace start/stop logic Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 04/17] hw/riscv/virt.c: add trace encoders and trace ram sinks Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 05/17] hw/riscv/virt.c add trace encoder and ramsink fdt nodes Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 06/17] hw/riscv: add e-trace message helpers Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 07/17] target/riscv: add initial trace instrumentation Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 08/17] hw/riscv/trace-encoder: write e-trace packets to RAM sink Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 09/17] test/qtest: add riscv-trace-test.c Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 10/17] hw/riscv/rv-trace-messages.c: add encoded trap message Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 11/17] hw/riscv, target/riscv: send trace trap messages Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 12/17] target/riscv, hw/riscv: send trace ppccd packets Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 13/17] hw/riscv/trace: add format2 msg helper Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 14/17] hw/riscv, target/riscv: send resync updiscon trace packets Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 15/17] hw/riscv/rv-trace-messages: add format 1 msgs with branch info Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 16/17] hw/riscv/trace-encoder: send branches info Daniel Henrique Barboza
2025-11-11 11:46 ` [PATCH v2 17/17] hw/riscv/trace: update branch bit in sync messages Daniel Henrique Barboza
2025-11-20 12:14 ` [PATCH v2 00/17] hw/riscv, target/riscv: initial e-trace support Konstantin Semichastnov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1ba7cae6-7683-4958-9c20-cef8fb0a8f46@syntacore.com \
--to=k.semichastnov@syntacore.com \
--cc=alistair.francis@wdc.com \
--cc=dbarboza@ventanamicro.com \
--cc=liwei1518@gmail.com \
--cc=palmer@dabbelt.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=zhiwei_liu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).