[PATCH 03/13] hw/riscv: add n-trace encoder with ProgTraceSync message

Konstantin Semichastnov <[email protected]>
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>
Introduce N-Trace format message encoder, based on "RISC-V n-trace
(Nexus-based Trace) Specificationand" Version 1.0.

Add generation, encoding and sending logic for ProgTraceSync message

Signed-off-by: Konstantin Semichastnov <[email protected]>
---
 hw/riscv/rv-trace-messages.c | 154 +++++++++++++++++++++++++++++++++++++++++++
 hw/riscv/rv-trace-messages.h |   1 +
 hw/riscv/trace-encoder.c     |  29 +++++++-
 hw/riscv/trace-encoder.h     |   1 +
 4 files changed, 184 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/rv-trace-messages.c b/hw/riscv/rv-trace-messages.c
index 932f3ae499..0b90d6a437 100644
--- a/hw/riscv/rv-trace-messages.c
+++ b/hw/riscv/rv-trace-messages.c
@@ -338,3 +338,157 @@ size_t rv_etrace_gen_encoded_format1(uint8_t *buf,
 
     return HEADER_SIZE + header.length;
 }
+
+#define MDO_BITS_NUM  6
+#define MSEO_BITS_NUM 2
+
+#define MSEO_VAR_FIELD_FINISHED 0b01
+#define MSEO_END_OF_MESSAGE     0b11
+
+typedef enum {
+    FIELD_END = 0,
+    FIELD_FIXED,
+    FIELD_VAR,
+} NTraceFieldType;
+
+typedef enum {
+    WIDTH_TCODE  = 6,
+    WIDTH_SYNC   = 4,
+    WIDTH_ICNT   = 22,
+    WIDTH_FADDR  = 63,
+} NTraceFieldWidth;
+
+typedef enum {
+    TCODE_PROG_TRACE_SYNC        = 0x9,
+} NTraceTcodeFieldValues;
+
+typedef enum {
+    SYNC_PERIODIC      = 2,
+    SYNC_TRACE_ENABLED = 5,
+} NTraceSyncFieldValues;
+
+typedef struct NTraceFieldPayload {
+    NTraceFieldType type;
+    NTraceFieldWidth width;
+    uint64_t value;
+} NTraceFieldPayload;
+
+#define PAYLOAD_TCODE(val)    \
+    {                         \
+        .type = FIELD_FIXED,  \
+        .width = WIDTH_TCODE, \
+        .value = (val),       \
+    }
+
+#define PAYLOAD_SYNC(val)    \
+    {                        \
+        .type = FIELD_FIXED, \
+        .width = WIDTH_SYNC, \
+        .value = (val),      \
+    }
+
+#define PAYLOAD_ICNT(val)    \
+    {                        \
+        .type = FIELD_VAR,   \
+        .width = WIDTH_ICNT, \
+        .value = (val),      \
+    }
+
+#define PAYLOAD_FADDR(val)    \
+    {                         \
+        .type = FIELD_VAR,    \
+        .width = WIDTH_FADDR, \
+        .value = (val),       \
+    }
+#define PAYLOAD_END()      \
+    {                      \
+        .type = FIELD_END, \
+        .width = 0,        \
+        .value = 0,        \
+    }
+
+static size_t rv_ntrace_gen_encoded_msg(uint8_t *buf,
+                                        const NTraceFieldPayload *payload)
+{
+    uint8_t buf_byte   = 0;
+    uint8_t packed_bit = 0;
+    uint8_t packed_cur = 0;
+    uint8_t nr_move    = 0;
+
+    /*
+     * as described in "Chapter 3. N-Trace Transmission Protocol" of N-Trace
+     * spec each byte has only bits[7:2] filled with data, called MDO bits.
+     * Lowest 2 bits[1:0] are called MSEO bits and used to signal about
+     * boundaries between message fields and messages
+     */
+
+    while (payload->type != FIELD_END) {
+        for (uint8_t offset = 0; offset < payload->width; offset += nr_move) {
+            nr_move = MIN(MDO_BITS_NUM - packed_bit, payload->width - offset);
+            uint8_t chunk = extract64(payload->value, offset, nr_move);
+
+            packed_cur = deposit32(packed_cur, packed_bit, nr_move, chunk);
+            packed_bit += nr_move;
+
+            if (packed_bit == MDO_BITS_NUM) {
+                buf[buf_byte++] = packed_cur << MSEO_BITS_NUM;
+                packed_bit = 0;
+                packed_cur = 0;
+            }
+        }
+
+        /*
+         * if field has variable length - last byte of this field should have
+         * MSEO (lowest 2) bits set to 01, and zero filled to end of byte
+         *
+         * N-Trace spec: 3.1. MSEO Sequences.
+         */
+        if (payload->type == FIELD_VAR) {
+            if (packed_bit != 0) {
+                /* last chunk was not finished, leave remaining bits cleared */
+                buf[buf_byte++] = packed_cur << MSEO_BITS_NUM;
+                packed_bit = 0;
+                packed_cur = 0;
+            }
+
+            /* correct last chunk MSEO bits */
+            buf[buf_byte - 1] = deposit32(buf[buf_byte - 1], 0,
+                                          MSEO_BITS_NUM,
+                                          MSEO_VAR_FIELD_FINISHED);
+        }
+
+        payload++;
+    }
+
+    if (packed_bit != 0) {
+        /* last chunk was not finished, so finish it now */
+        buf[buf_byte++] = packed_cur;
+    }
+
+    /*
+     * last byte of message should have MSEO (lowest 2) bits set to 11
+     *
+     * N-Trace spec: 3.1. MSEO Sequences.
+     */
+    buf[buf_byte - 1] = deposit32(buf[buf_byte - 1], 0,
+                                  MSEO_BITS_NUM,
+                                  MSEO_END_OF_MESSAGE);
+
+    return buf_byte;
+}
+
+size_t rv_ntrace_gen_encoded_prog_trace_sync_msg(uint8_t *buf, uint64_t faddr)
+{
+    const NTraceFieldPayload msg_fields[] = {
+        PAYLOAD_TCODE(TCODE_PROG_TRACE_SYNC),
+        /* SRC field is not supported for now */
+        PAYLOAD_SYNC(SYNC_TRACE_ENABLED),
+        PAYLOAD_ICNT(0),
+        PAYLOAD_FADDR(faddr),
+        /* TSTAMP field is not supported for now */
+        PAYLOAD_END(),
+    };
+
+    return rv_ntrace_gen_encoded_msg(buf, msg_fields);
+}
+
diff --git a/hw/riscv/rv-trace-messages.h b/hw/riscv/rv-trace-messages.h
index 6f700eac8e..635e498393 100644
--- a/hw/riscv/rv-trace-messages.h
+++ b/hw/riscv/rv-trace-messages.h
@@ -37,4 +37,5 @@ size_t rv_etrace_gen_encoded_format1(uint8_t *buf,
                                      uint64_t addr,
                                      bool notify, bool updiscon);
 
+size_t rv_ntrace_gen_encoded_prog_trace_sync_msg(uint8_t *buf, uint64_t faddr);
 #endif
diff --git a/hw/riscv/trace-encoder.c b/hw/riscv/trace-encoder.c
index 34c8f60ce1..10e52e207e 100644
--- a/hw/riscv/trace-encoder.c
+++ b/hw/riscv/trace-encoder.c
@@ -255,7 +255,13 @@ static void trencoder_te_ctrl_postw(RegisterInfo *reg, uint64_t val)
     if (!te->trace_running && trTeInstTracing) {
         /* Starting trace. Ask the CPU for the first trace insn */
         if (te->ntrace) {
-            /* send ProgTraceSync message */
+            /*
+             * N-Trace require to trace from very first instruction,
+             * not from next instruction, after enabling the trace,
+             * like E-Trace. So we need to send message here, instead of
+             * using te->trace_next_insn
+             */
+            trencoder_send_prog_trace_sync(OBJECT(te), env->pc);
         } else {
             te->trace_next_insn = true;
         }
@@ -508,6 +514,27 @@ void trencoder_set_first_trace_insn(Object *trencoder_obj, uint64_t pc)
     trencoder_send_message_smem(trencoder, msg, msg_size);
 }
 
+static uint64_t rv_ntrace_encode_faddr(uint64_t pc)
+{
+    return pc >> 1ul;
+}
+
+void trencoder_send_prog_trace_sync(Object *trencoder_obj, uint64_t pc)
+{
+    TraceEncoder *trencoder = TRACE_ENCODER(trencoder_obj);
+    g_autofree uint8_t *msg = g_malloc0(TRACE_MSG_MAX_SIZE);
+    uint8_t msg_size;
+
+    uint64_t faddr = rv_ntrace_encode_faddr(pc);
+
+    g_assert(trencoder->ntrace);
+
+    msg_size = rv_ntrace_gen_encoded_prog_trace_sync_msg(msg, faddr);
+
+    trencoder_send_message_smem(trencoder, msg, msg_size);
+}
+
+
 void trencoder_trace_trap_insn(Object *trencoder_obj,
                                uint64_t pc, uint32_t ecause,
                                bool is_interrupt,
diff --git a/hw/riscv/trace-encoder.h b/hw/riscv/trace-encoder.h
index fed0ef9a48..96324d2b9a 100644
--- a/hw/riscv/trace-encoder.h
+++ b/hw/riscv/trace-encoder.h
@@ -53,6 +53,7 @@ struct TraceEncoder {
 OBJECT_DECLARE_SIMPLE_TYPE(TraceEncoder, TRACE_ENCODER)
 
 void trencoder_set_first_trace_insn(Object *trencoder_obj, uint64_t pc);
+void trencoder_send_prog_trace_sync(Object *trencoder_obj, uint64_t pc);
 void trencoder_trace_trap_insn(Object *trencoder_obj,
                                uint64_t pc, uint32_t ecause,
                                bool is_interrupt,

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.