Add generation, encoding and sending logic for N-Trace
IndirectBranchHist message.

Signed-off-by: Konstantin Semichastnov <[email protected]>
Signed-off-by: Alexei Filippov <[email protected]>
---
 hw/riscv/rv-trace-messages.c | 42 +++++++++++++++++++++
 hw/riscv/rv-trace-messages.h |  4 ++
 hw/riscv/trace-encoder.c     | 87 ++++++++++++++++++++++++++++++++++++++++++++
 hw/riscv/trace-encoder.h     | 10 +++++
 target/riscv/trace_helper.c  |  3 +-
 5 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/rv-trace-messages.c b/hw/riscv/rv-trace-messages.c
index 0b90d6a437..2ee38c4354 100644
--- a/hw/riscv/rv-trace-messages.c
+++ b/hw/riscv/rv-trace-messages.c
@@ -354,12 +354,16 @@ typedef enum {
 typedef enum {
     WIDTH_TCODE  = 6,
     WIDTH_SYNC   = 4,
+    WIDTH_BTYPE  = 2,
     WIDTH_ICNT   = 22,
     WIDTH_FADDR  = 63,
+    WIDTH_UADDR  = 63,
+    WIDTH_HIST   = 32,
 } NTraceFieldWidth;
 
 typedef enum {
     TCODE_PROG_TRACE_SYNC        = 0x9,
+    TCODE_INDIRECT_BRANCH_HIST   = 0x1c,
 } NTraceTcodeFieldValues;
 
 typedef enum {
@@ -387,6 +391,13 @@ typedef struct NTraceFieldPayload {
         .value = (val),      \
     }
 
+#define PAYLOAD_BTYPE(val)    \
+    {                         \
+        .type = FIELD_FIXED,  \
+        .width = WIDTH_BTYPE, \
+        .value = (val),       \
+    }
+
 #define PAYLOAD_ICNT(val)    \
     {                        \
         .type = FIELD_VAR,   \
@@ -400,6 +411,20 @@ typedef struct NTraceFieldPayload {
         .width = WIDTH_FADDR, \
         .value = (val),       \
     }
+
+#define PAYLOAD_UADDR(val)    \
+    {                         \
+        .type = FIELD_VAR,    \
+        .width = WIDTH_UADDR, \
+        .value = (val),       \
+    }
+
+#define PAYLOAD_HIST(val)    \
+    {                        \
+        .type = FIELD_VAR,   \
+        .width = WIDTH_HIST, \
+        .value = (val),      \
+    }
 #define PAYLOAD_END()      \
     {                      \
         .type = FIELD_END, \
@@ -492,3 +517,20 @@ size_t rv_ntrace_gen_encoded_prog_trace_sync_msg(uint8_t 
*buf, uint64_t faddr)
     return rv_ntrace_gen_encoded_msg(buf, msg_fields);
 }
 
+size_t rv_ntrace_gen_indirect_branch_hist_msg(uint8_t *buf, uint8_t btype,
+                                              uint32_t icnt, uint64_t uaddr,
+                                              uint32_t hist)
+{
+    const NTraceFieldPayload msg_fields[] = {
+        PAYLOAD_TCODE(TCODE_INDIRECT_BRANCH_HIST),
+        /* SRC field is not supported for now */
+        PAYLOAD_BTYPE(btype),
+        PAYLOAD_ICNT(icnt),
+        PAYLOAD_UADDR(uaddr >> 1),
+        PAYLOAD_HIST(hist),
+        /* 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 635e498393..d113129d65 100644
--- a/hw/riscv/rv-trace-messages.h
+++ b/hw/riscv/rv-trace-messages.h
@@ -38,4 +38,8 @@ size_t rv_etrace_gen_encoded_format1(uint8_t *buf,
                                      bool notify, bool updiscon);
 
 size_t rv_ntrace_gen_encoded_prog_trace_sync_msg(uint8_t *buf, uint64_t faddr);
+size_t rv_ntrace_gen_indirect_branch_hist_msg(uint8_t *buf, uint8_t btype,
+                                              uint32_t icnt, uint64_t uaddr,
+                                              uint32_t hist);
+
 #endif
diff --git a/hw/riscv/trace-encoder.c b/hw/riscv/trace-encoder.c
index 46501e4f03..fafafb7b9b 100644
--- a/hw/riscv/trace-encoder.c
+++ b/hw/riscv/trace-encoder.c
@@ -31,6 +31,11 @@
 
 #define TRACE_MAX_BRANCHES 31
 
+static uint64_t rv_ntrace_encode_uaddr(TraceEncoder *trencoder, uint64_t 
target_pc);
+static uint32_t rv_ntrace_encode_and_reset_branch_map(TraceEncoder *trencoder);
+static uint32_t rv_ntrace_get_and_reset_icnt(TraceEncoder *trencoder,
+                                             uint64_t pc, uint32_t inst_len);
+
 static TracePrivLevel trencoder_get_curr_priv_level(TraceEncoder *te)
 {
     CPURISCVState *env = &te->cpu->env;
@@ -478,6 +483,28 @@ static void trencoder_send_sync_msg(Object *trencoder_obj, 
uint64_t pc)
     trencoder_send_message_smem(trencoder, msg, msg_size);
 }
 
+void trencoder_send_indirect_branch_hist(TraceEncoder *trencoder,
+                                         uint64_t curr_pc,
+                                         uint32_t curr_inst_len,
+                                         uint64_t target_pc,
+                                         TraceBranchType btype)
+{
+    g_autofree uint8_t *msg = g_malloc0(TRACE_MSG_MAX_SIZE);
+    uint8_t msg_size;
+
+    uint32_t icnt = rv_ntrace_get_and_reset_icnt(trencoder, curr_pc, 
curr_inst_len);
+    uint64_t uaddr = rv_ntrace_encode_uaddr(trencoder, target_pc);
+    uint32_t hist = rv_ntrace_encode_and_reset_branch_map(trencoder);
+
+    g_assert(trencoder->ntrace);
+
+    msg_size = rv_ntrace_gen_indirect_branch_hist_msg(msg, btype, icnt, uaddr, 
hist);
+    trencoder->last_addr_reported = target_pc;
+    trencoder->last_icnt_reset = target_pc;
+
+    trencoder_send_message_smem(trencoder, msg, msg_size);
+}
+
 static void trencoder_send_updiscon(TraceEncoder *trencoder, uint64_t pc)
 {
     g_autofree uint8_t *format2_msg = g_malloc0(TRACE_MSG_MAX_SIZE);
@@ -551,6 +578,66 @@ void trencoder_send_prog_trace_sync(Object *trencoder_obj, 
uint64_t pc)
     trencoder_send_message_smem(trencoder, msg, msg_size);
 }
 
+static uint64_t rv_ntrace_encode_uaddr(TraceEncoder *trencoder, uint64_t 
target_pc)
+{
+    return trencoder->last_addr_reported ^ target_pc;
+}
+
+static uint32_t rv_ntrace_encode_and_reset_branch_map(TraceEncoder *trencoder)
+{
+    const uint32_t stop_bit = 1;
+
+    uint32_t hist = trencoder->branch_map;
+
+    if (trencoder->branches == 0) {
+        /*
+         * if branches == 0, we will overflow on bit shift, so just return
+         * empty history value here (i.e. - only stop bit)
+         */
+        return stop_bit;
+    }
+
+    /*
+     * trencoder->branch_map stores bit 1 for NOT taken branches,
+     * but N-Trace expects to stored bit 1 for TAKEN branches,
+     * so, we should invert bits
+     */
+    hist = ~hist;
+
+    /*
+     * Also, trencoder->branch_map is implemented in such way,
+     * that oldest branch is LSB bit, but N-Trace format require
+     * to HIST values be in shift-left format: the most recent branch
+     * should be in LSB bit. So we should reverse order
+     * of valid history bits
+     */
+    hist = revbit32(hist) >> (32 - trencoder->branches);
+
+    /* And finally N-Trace expects bit 1 as stop bit at the MSB position */
+    hist = deposit32(hist, trencoder->branches, 1, stop_bit);
+
+    trencoder->branch_map = 0;
+    trencoder->branches = 0;
+
+    return hist;
+}
+
+static uint32_t rv_ntrace_get_and_reset_icnt(TraceEncoder *trencoder,
+                                             uint64_t pc, uint32_t inst_len)
+{
+    uint64_t icnt = (pc + inst_len - trencoder->last_icnt_reset) / 2 +
+                    trencoder->icnt_accumulated;
+
+    trencoder->last_icnt_reset = pc;
+    trencoder->icnt_accumulated = 0;
+
+    if (icnt > MAKE_64BIT_MASK(0, 22)) {
+        /* I-CNT overflow reporting is not supported for now */
+        error_setg(&error_fatal, "N-Trace icnt overflowed");
+    }
+
+    return icnt;
+}
 
 void trencoder_trace_trap_insn(Object *trencoder_obj,
                                uint64_t pc, uint32_t ecause,
diff --git a/hw/riscv/trace-encoder.h b/hw/riscv/trace-encoder.h
index 6551ac77f9..f8a6a254dc 100644
--- a/hw/riscv/trace-encoder.h
+++ b/hw/riscv/trace-encoder.h
@@ -57,6 +57,11 @@ typedef enum {
     BRANCH_TAKEN_UNCONDITIONAL = 2,
 } TraceBranchRes;
 
+typedef enum {
+    BTYPE_CONTROL_FLOW = 0,
+    BTYPE_EXCEPTION    = 2,
+    BTYPE_INTERRUPT    = 3,
+} TraceBranchType;
 
 #define TYPE_TRACE_ENCODER "trace-encoder"
 
@@ -64,6 +69,11 @@ 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_send_indirect_branch_hist(TraceEncoder *trencoder,
+                                         uint64_t curr_pc,
+                                         uint32_t curr_inst_len,
+                                         uint64_t target_pc,
+                                         TraceBranchType btype);
 void trencoder_trace_trap_insn(Object *trencoder_obj,
                                uint64_t pc, uint32_t ecause,
                                bool is_interrupt,
diff --git a/target/riscv/trace_helper.c b/target/riscv/trace_helper.c
index f6201e2fb9..d7763a998f 100644
--- a/target/riscv/trace_helper.c
+++ b/target/riscv/trace_helper.c
@@ -36,7 +36,8 @@ void helper_trace_updiscon(CPURISCVState *env, target_ulong 
curr_pc,
     TraceEncoder *te = TRACE_ENCODER(cpu->trencoder);
 
     if (te->ntrace) {
-        /* send IndirectBranchHist message */
+        trencoder_send_indirect_branch_hist(te, curr_pc, curr_inst_len, 
env->pc,
+                                            BTYPE_CONTROL_FLOW);
     } else {
         te->updiscon_pending = true;
         te->trace_next_insn = true;

-- 
2.43.0


Reply via email to