Before making the trace encoder writing into the RAM sink we need a way
to encode the messages/packets. The encoding is LSB (least significant
bit) based. The doc "Efficient Trace for RISC-V", Chapter 7, mentions:
"The remainder of this section describes the contents of the payload
portion which should be independent of the infrastructure. In each
table, the fields are listed in transmission order: first field in
the table is transmitted first, and multi-bit fields are transmitted
LSB first."
The "RISC-V Trace Control Interface Specification" docs, Chapter 7,
states when talking about the Trace RAM Sink:
"Trace data is placed in memory in LSB order (first byte of trace
packet/data is placed on LSB)."
This means that the LSB encoding must be used to write into the RAM Sink
memory, which is our goal.
The design we're going for is to have all these encoder helpers, along
with the message formats, in a separated file. The trace encoder will
make use of these helpers to blindly write a byte array with the packet
desired, and then write it as is in the RAM Sink.
We'll start by modeling the synchronisation packet first, adding more
formats as we increment the Trace Encoder capabilities.
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
hw/riscv/meson.build | 3 +-
hw/riscv/rv-trace-messages.c | 94 ++++++++++++++++++++++++++++++++++++
hw/riscv/rv-trace-messages.h | 25 ++++++++++
3 files changed, 121 insertions(+), 1 deletion(-)
create mode 100644 hw/riscv/rv-trace-messages.c
create mode 100644 hw/riscv/rv-trace-messages.h
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 2aadbe1e50..7d3576fcdf 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -14,6 +14,7 @@ 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', 'trace-ram-sink.c'))
+riscv_ss.add(when: 'CONFIG_RISCV_TRACE', if_true: files('trace-encoder.c',
+ 'trace-ram-sink.c', 'rv-trace-messages.c'))
hw_arch += {'riscv': riscv_ss}
diff --git a/hw/riscv/rv-trace-messages.c b/hw/riscv/rv-trace-messages.c
new file mode 100644
index 0000000000..215135dd47
--- /dev/null
+++ b/hw/riscv/rv-trace-messages.c
@@ -0,0 +1,94 @@
+/*
+ * Helpers for RISC-V Trace Messages
+ *
+ * Copyright (C) 2025 Ventana Micro Systems Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "rv-trace-messages.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+typedef struct RVTraceMessageHeader {
+ uint8_t length:5;
+ uint8_t flow:2;
+ uint8_t extend:1;
+} RVTraceMessageHeader;
+#define HEADER_SIZE 1
+
+/*
+ * Format 3 subformat 0 without 'time' and 'context' fields
+ */
+typedef struct RVTraceSyncPayload {
+ uint8_t format:2;
+ uint8_t subformat:2;
+ uint8_t branch:1;
+ uint8_t privilege:3;
+ uint32_t addressLow;
+ uint32_t addressHigh;
+} RVTraceSyncPayload;
+#define SYNC_PAYLOAD_SIZE_64BITS 9
+
+static void rv_etrace_write_bits(uint8_t *bytes, uint32_t bit_pos,
+ uint32_t num_bits, uint32_t val)
+{
+ uint32_t pos, byte_index, byte_pos, byte_bits = 0;
+
+ if (!num_bits || 32 < num_bits) {
+ return;
+ }
+
+ for (pos = 0; pos < num_bits; pos += byte_bits) {
+ byte_index = (bit_pos + pos) >> 3;
+ byte_pos = (bit_pos + pos) & 0x7;
+ byte_bits = (8 - byte_pos) < (num_bits - pos) ?
+ (8 - byte_pos) : (num_bits - pos);
+ bytes[byte_index] &= ~(((1U << byte_bits) - 1) << byte_pos);
+ bytes[byte_index] |= ((val >> pos) & ((1U << byte_bits) - 1)) << byte_pos;
+ }
+}
+
+static void rv_etrace_write_header(uint8_t *buf, RVTraceMessageHeader header)
+{
+ /* flow and extend are always zero, i.e just write length */
+ rv_etrace_write_bits(buf, 0, 5, header.length);
+}
+
+size_t rv_etrace_gen_encoded_sync_msg(uint8_t *buf, uint64_t pc,
+ TracePrivLevel priv_level)
+{
+ RVTraceSyncPayload payload = {.format = 0b11,
+ .subformat = 0b00,
+ .branch = 1,
+ .privilege = priv_level};
+ RVTraceMessageHeader header = {.flow = 0, .extend = 0,
+ .length = SYNC_PAYLOAD_SIZE_64BITS};
+ uint8_t bit_pos;
+
+ payload.addressLow = extract64(pc, 0, 32);
+ payload.addressHigh = extract64(pc, 32, 32);
+
+ rv_etrace_write_header(buf, header);
+ bit_pos = 8;
+
+ rv_etrace_write_bits(buf, bit_pos, 2, payload.format);
+ bit_pos += 2;
+ rv_etrace_write_bits(buf, bit_pos, 2, payload.subformat);
+ bit_pos += 2;
+ rv_etrace_write_bits(buf, bit_pos, 1, payload.branch);
+ bit_pos += 1;
+ rv_etrace_write_bits(buf, bit_pos, 3, payload.privilege);
+ bit_pos += 3;
+
+ rv_etrace_write_bits(buf, bit_pos, 32, payload.addressLow);
+ bit_pos += 32;
+ rv_etrace_write_bits(buf, bit_pos, 32, payload.addressHigh);
+
+ return HEADER_SIZE + SYNC_PAYLOAD_SIZE_64BITS;
+}
diff --git a/hw/riscv/rv-trace-messages.h b/hw/riscv/rv-trace-messages.h
new file mode 100644
index 0000000000..aeafea8849
--- /dev/null
+++ b/hw/riscv/rv-trace-messages.h
@@ -0,0 +1,25 @@
+/*
+ * Helpers for RISC-V Trace Messages
+ *
+ * Copyright (C) 2025 Ventana Micro Systems Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RISCV_RV_TRACE_MESSAGES_H
+#define RISCV_RV_TRACE_MESSAGES_H
+
+typedef enum {
+ U = 0,
+ S_HS = 1,
+ RESERVED = 2,
+ M = 3,
+ D = 4,
+ VU = 5,
+ VS = 6,
+} TracePrivLevel;
+
+size_t rv_etrace_gen_encoded_sync_msg(uint8_t *buf, uint64_t pc,
+ TracePrivLevel priv_level);
+
+#endif
--
2.51.1