From: Zane Leung <liangzhen@linux.spacemit.com>
To: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
palmer@dabbelt.com, pjw@kernel.org, gregkh@linuxfoundation.org,
alexander.shishkin@linux.intel.com, irogers@google.com
Cc: coresight@lists.linaro.org, peterz@infradead.org,
mingo@redhat.com, namhyung@kernel.org, mark.rutland@arm.com,
jolsa@kernel.org, adrian.hunter@intel.com,
kan.liang@linux.intel.com, mchitale@gmail.com,
anup@brainfault.org, atish.patra@linux.dev,
andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com,
linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org,
anup.patel@oss.qualcomm.com, mayuresh.chitale@oss.qualcomm.com,
zhuangqiubin@linux.spacemit.com
Subject: [RFC PATCH 10/12] perf tools: Add Nexus RISC-V Trace decoder
Date: Tue, 14 Apr 2026 11:41:51 +0800 [thread overview]
Message-ID: <20260414034153.3272485-11-liangzhen@linux.spacemit.com> (raw)
In-Reply-To: <20260414034153.3272485-1-liangzhen@linux.spacemit.com>
From: liangzhen <zhen.liang@spacemit.com>
Add support for RISC-V Nexus Trace decoder based on the reference
implementation from the RISC-V Nexus Trace specification. This includes
a CoreSight frame deformatter to remove the trace formatter overhead.
Signed-off-by: liangzhen <zhen.liang@spacemit.com>
---
tools/arch/riscv/include/asm/insn.h | 645 ++++++++
tools/perf/arch/riscv/util/auxtrace.c | 1 +
tools/perf/util/Build | 2 +
tools/perf/util/nexus-rv-decoder/Build | 1 +
.../util/nexus-rv-decoder/nexus-rv-decoder.c | 1364 +++++++++++++++++
.../util/nexus-rv-decoder/nexus-rv-decoder.h | 139 ++
.../perf/util/nexus-rv-decoder/nexus-rv-msg.h | 190 +++
7 files changed, 2342 insertions(+)
create mode 100644 tools/arch/riscv/include/asm/insn.h
create mode 100644 tools/perf/util/nexus-rv-decoder/Build
create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
create mode 100644 tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
diff --git a/tools/arch/riscv/include/asm/insn.h b/tools/arch/riscv/include/asm/insn.h
new file mode 100644
index 000000000000..6c020afe926b
--- /dev/null
+++ b/tools/arch/riscv/include/asm/insn.h
@@ -0,0 +1,645 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 SiFive
+ */
+
+#ifndef _ASM_RISCV_INSN_H
+#define _ASM_RISCV_INSN_H
+
+#include <linux/bits.h>
+
+#define RV_INSN_FUNCT3_MASK GENMASK(14, 12)
+#define RV_INSN_FUNCT3_OPOFF 12
+#define RV_INSN_OPCODE_MASK GENMASK(6, 0)
+#define RV_INSN_OPCODE_OPOFF 0
+#define RV_INSN_FUNCT12_OPOFF 20
+
+#define RV_ENCODE_FUNCT3(f_) (RVG_FUNCT3_##f_ << RV_INSN_FUNCT3_OPOFF)
+#define RV_ENCODE_FUNCT12(f_) (RVG_FUNCT12_##f_ << RV_INSN_FUNCT12_OPOFF)
+
+/* The bit field of immediate value in I-type instruction */
+#define RV_I_IMM_SIGN_OPOFF 31
+#define RV_I_IMM_11_0_OPOFF 20
+#define RV_I_IMM_SIGN_OFF 12
+#define RV_I_IMM_11_0_OFF 0
+#define RV_I_IMM_11_0_MASK GENMASK(11, 0)
+
+/* The bit field of immediate value in J-type instruction */
+#define RV_J_IMM_SIGN_OPOFF 31
+#define RV_J_IMM_10_1_OPOFF 21
+#define RV_J_IMM_11_OPOFF 20
+#define RV_J_IMM_19_12_OPOFF 12
+#define RV_J_IMM_SIGN_OFF 20
+#define RV_J_IMM_10_1_OFF 1
+#define RV_J_IMM_11_OFF 11
+#define RV_J_IMM_19_12_OFF 12
+#define RV_J_IMM_10_1_MASK GENMASK(9, 0)
+#define RV_J_IMM_11_MASK GENMASK(0, 0)
+#define RV_J_IMM_19_12_MASK GENMASK(7, 0)
+
+/*
+ * U-type IMMs contain the upper 20bits [31:20] of an immediate with
+ * the rest filled in by zeros, so no shifting required. Similarly,
+ * bit31 contains the signed state, so no sign extension necessary.
+ */
+#define RV_U_IMM_SIGN_OPOFF 31
+#define RV_U_IMM_31_12_OPOFF 0
+#define RV_U_IMM_31_12_MASK GENMASK(31, 12)
+
+/* The bit field of immediate value in B-type instruction */
+#define RV_B_IMM_SIGN_OPOFF 31
+#define RV_B_IMM_10_5_OPOFF 25
+#define RV_B_IMM_4_1_OPOFF 8
+#define RV_B_IMM_11_OPOFF 7
+#define RV_B_IMM_SIGN_OFF 12
+#define RV_B_IMM_10_5_OFF 5
+#define RV_B_IMM_4_1_OFF 1
+#define RV_B_IMM_11_OFF 11
+#define RV_B_IMM_10_5_MASK GENMASK(5, 0)
+#define RV_B_IMM_4_1_MASK GENMASK(3, 0)
+#define RV_B_IMM_11_MASK GENMASK(0, 0)
+
+/* The register offset in RVG instruction */
+#define RVG_RS1_OPOFF 15
+#define RVG_RS2_OPOFF 20
+#define RVG_RD_OPOFF 7
+#define RVG_RS1_MASK GENMASK(4, 0)
+#define RVG_RS2_MASK GENMASK(4, 0)
+#define RVG_RD_MASK GENMASK(4, 0)
+
+/* The bit field of immediate value in RVC J instruction */
+#define RVC_J_IMM_SIGN_OPOFF 12
+#define RVC_J_IMM_4_OPOFF 11
+#define RVC_J_IMM_9_8_OPOFF 9
+#define RVC_J_IMM_10_OPOFF 8
+#define RVC_J_IMM_6_OPOFF 7
+#define RVC_J_IMM_7_OPOFF 6
+#define RVC_J_IMM_3_1_OPOFF 3
+#define RVC_J_IMM_5_OPOFF 2
+#define RVC_J_IMM_SIGN_OFF 11
+#define RVC_J_IMM_4_OFF 4
+#define RVC_J_IMM_9_8_OFF 8
+#define RVC_J_IMM_10_OFF 10
+#define RVC_J_IMM_6_OFF 6
+#define RVC_J_IMM_7_OFF 7
+#define RVC_J_IMM_3_1_OFF 1
+#define RVC_J_IMM_5_OFF 5
+#define RVC_J_IMM_4_MASK GENMASK(0, 0)
+#define RVC_J_IMM_9_8_MASK GENMASK(1, 0)
+#define RVC_J_IMM_10_MASK GENMASK(0, 0)
+#define RVC_J_IMM_6_MASK GENMASK(0, 0)
+#define RVC_J_IMM_7_MASK GENMASK(0, 0)
+#define RVC_J_IMM_3_1_MASK GENMASK(2, 0)
+#define RVC_J_IMM_5_MASK GENMASK(0, 0)
+
+/* The bit field of immediate value in RVC B instruction */
+#define RVC_B_IMM_SIGN_OPOFF 12
+#define RVC_B_IMM_4_3_OPOFF 10
+#define RVC_B_IMM_7_6_OPOFF 5
+#define RVC_B_IMM_2_1_OPOFF 3
+#define RVC_B_IMM_5_OPOFF 2
+#define RVC_B_IMM_SIGN_OFF 8
+#define RVC_B_IMM_4_3_OFF 3
+#define RVC_B_IMM_7_6_OFF 6
+#define RVC_B_IMM_2_1_OFF 1
+#define RVC_B_IMM_5_OFF 5
+#define RVC_B_IMM_4_3_MASK GENMASK(1, 0)
+#define RVC_B_IMM_7_6_MASK GENMASK(1, 0)
+#define RVC_B_IMM_2_1_MASK GENMASK(1, 0)
+#define RVC_B_IMM_5_MASK GENMASK(0, 0)
+
+#define RVC_INSN_FUNCT4_MASK GENMASK(15, 12)
+#define RVC_INSN_FUNCT4_OPOFF 12
+#define RVC_INSN_FUNCT3_MASK GENMASK(15, 13)
+#define RVC_INSN_FUNCT3_OPOFF 13
+#define RVC_INSN_J_RS1_MASK GENMASK(11, 7)
+#define RVC_INSN_J_RS2_MASK GENMASK(6, 2)
+#define RVC_INSN_OPCODE_MASK GENMASK(1, 0)
+#define RVC_ENCODE_FUNCT3(f_) (RVC_FUNCT3_##f_ << RVC_INSN_FUNCT3_OPOFF)
+#define RVC_ENCODE_FUNCT4(f_) (RVC_FUNCT4_##f_ << RVC_INSN_FUNCT4_OPOFF)
+
+/* The register offset in RVC op=C0 instruction */
+#define RVC_C0_RS1_OPOFF 7
+#define RVC_C0_RS2_OPOFF 2
+#define RVC_C0_RD_OPOFF 2
+
+/* The register offset in RVC op=C1 instruction */
+#define RVC_C1_RS1_OPOFF 7
+#define RVC_C1_RS2_OPOFF 2
+#define RVC_C1_RD_OPOFF 7
+
+/* The register offset in RVC op=C2 instruction */
+#define RVC_C2_RS1_OPOFF 7
+#define RVC_C2_RS2_OPOFF 2
+#define RVC_C2_RD_OPOFF 7
+#define RVC_C2_RS1_MASK GENMASK(4, 0)
+
+/* parts of opcode for RVG*/
+#define RVG_OPCODE_FENCE 0x0f
+#define RVG_OPCODE_AUIPC 0x17
+#define RVG_OPCODE_BRANCH 0x63
+#define RVG_OPCODE_JALR 0x67
+#define RVG_OPCODE_JAL 0x6f
+#define RVG_OPCODE_SYSTEM 0x73
+#define RVG_SYSTEM_CSR_OFF 20
+#define RVG_SYSTEM_CSR_MASK GENMASK(12, 0)
+
+/* parts of opcode for RVF, RVD and RVQ */
+#define RVFDQ_FL_FS_WIDTH_OFF 12
+#define RVFDQ_FL_FS_WIDTH_MASK GENMASK(2, 0)
+#define RVFDQ_FL_FS_WIDTH_W 2
+#define RVFDQ_FL_FS_WIDTH_D 3
+#define RVFDQ_LS_FS_WIDTH_Q 4
+#define RVFDQ_OPCODE_FL 0x07
+#define RVFDQ_OPCODE_FS 0x27
+
+/* parts of opcode for RVV */
+#define RVV_OPCODE_VECTOR 0x57
+#define RVV_VL_VS_WIDTH_8 0
+#define RVV_VL_VS_WIDTH_16 5
+#define RVV_VL_VS_WIDTH_32 6
+#define RVV_VL_VS_WIDTH_64 7
+#define RVV_OPCODE_VL RVFDQ_OPCODE_FL
+#define RVV_OPCODE_VS RVFDQ_OPCODE_FS
+
+/* parts of opcode for RVC*/
+#define RVC_OPCODE_C0 0x0
+#define RVC_OPCODE_C1 0x1
+#define RVC_OPCODE_C2 0x2
+
+/* parts of funct3 code for I, M, A extension*/
+#define RVG_FUNCT3_JALR 0x0
+#define RVG_FUNCT3_BEQ 0x0
+#define RVG_FUNCT3_BNE 0x1
+#define RVG_FUNCT3_BLT 0x4
+#define RVG_FUNCT3_BGE 0x5
+#define RVG_FUNCT3_BLTU 0x6
+#define RVG_FUNCT3_BGEU 0x7
+
+/* parts of funct3 code for C extension*/
+#define RVC_FUNCT3_C_BEQZ 0x6
+#define RVC_FUNCT3_C_BNEZ 0x7
+#define RVC_FUNCT3_C_J 0x5
+#define RVC_FUNCT3_C_JAL 0x1
+#define RVC_FUNCT4_C_JR 0x8
+#define RVC_FUNCT4_C_JALR 0x9
+#define RVC_FUNCT4_C_EBREAK 0x9
+
+#define RVG_FUNCT12_ECALL 0x0
+#define RVG_FUNCT12_EBREAK 0x1
+#define RVG_FUNCT12_SRET 0x102
+#define RVG_FUNCT12_MRET 0x303
+
+#define RVG_MATCH_AUIPC (RVG_OPCODE_AUIPC)
+#define RVG_MATCH_JALR (RV_ENCODE_FUNCT3(JALR) | RVG_OPCODE_JALR)
+#define RVG_MATCH_JAL (RVG_OPCODE_JAL)
+#define RVG_MATCH_FENCE (RVG_OPCODE_FENCE)
+#define RVG_MATCH_BEQ (RV_ENCODE_FUNCT3(BEQ) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BNE (RV_ENCODE_FUNCT3(BNE) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BLT (RV_ENCODE_FUNCT3(BLT) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BGE (RV_ENCODE_FUNCT3(BGE) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BLTU (RV_ENCODE_FUNCT3(BLTU) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_BGEU (RV_ENCODE_FUNCT3(BGEU) | RVG_OPCODE_BRANCH)
+#define RVG_MATCH_ECALL (RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM)
+#define RVG_MATCH_EBREAK (RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM)
+#define RVG_MATCH_SRET (RV_ENCODE_FUNCT12(SRET) | RVG_OPCODE_SYSTEM)
+#define RVG_MATCH_MRET (RV_ENCODE_FUNCT12(MRET) | RVG_OPCODE_SYSTEM)
+#define RVC_MATCH_C_BEQZ (RVC_ENCODE_FUNCT3(C_BEQZ) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_BNEZ (RVC_ENCODE_FUNCT3(C_BNEZ) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_J (RVC_ENCODE_FUNCT3(C_J) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_JAL (RVC_ENCODE_FUNCT3(C_JAL) | RVC_OPCODE_C1)
+#define RVC_MATCH_C_JR (RVC_ENCODE_FUNCT4(C_JR) | RVC_OPCODE_C2)
+#define RVC_MATCH_C_JALR (RVC_ENCODE_FUNCT4(C_JALR) | RVC_OPCODE_C2)
+#define RVC_MATCH_C_EBREAK (RVC_ENCODE_FUNCT4(C_EBREAK) | RVC_OPCODE_C2)
+
+#define RVG_MASK_AUIPC (RV_INSN_OPCODE_MASK)
+#define RVG_MASK_JALR (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_JAL (RV_INSN_OPCODE_MASK)
+#define RVG_MASK_FENCE (RV_INSN_OPCODE_MASK)
+#define RVC_MASK_C_JALR (RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_JR (RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_JAL (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_J (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVG_MASK_BEQ (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BNE (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BLT (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BGE (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BLTU (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVG_MASK_BGEU (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK)
+#define RVC_MASK_C_BEQZ (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_BNEZ (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK)
+#define RVC_MASK_C_EBREAK 0xffff
+#define RVG_MASK_EBREAK 0xffffffff
+#define RVG_MASK_ECALL 0xffffffff
+#define RVG_MASK_SRET 0xffffffff
+#define RVG_MASK_MRET 0xffffffff
+
+#define __INSN_LENGTH_MASK _UL(0x3)
+#define __INSN_LENGTH_GE_32 _UL(0x3)
+#define __INSN_OPCODE_MASK _UL(0x7F)
+#define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH)
+
+#define __RISCV_INSN_FUNCS(name, mask, val) \
+static __always_inline bool riscv_insn_is_##name(u32 code) \
+{ \
+ BUILD_BUG_ON(~(mask) & (val)); \
+ return (code & (mask)) == (val); \
+} \
+
+#if __riscv_xlen == 32
+/* C.JAL is an RV32C-only instruction */
+__RISCV_INSN_FUNCS(c_jal, RVC_MASK_C_JAL, RVC_MATCH_C_JAL)
+#else
+#define riscv_insn_is_c_jal(opcode) 0
+#endif
+__RISCV_INSN_FUNCS(auipc, RVG_MASK_AUIPC, RVG_MATCH_AUIPC)
+__RISCV_INSN_FUNCS(jalr, RVG_MASK_JALR, RVG_MATCH_JALR)
+__RISCV_INSN_FUNCS(jal, RVG_MASK_JAL, RVG_MATCH_JAL)
+__RISCV_INSN_FUNCS(c_j, RVC_MASK_C_J, RVC_MATCH_C_J)
+__RISCV_INSN_FUNCS(beq, RVG_MASK_BEQ, RVG_MATCH_BEQ)
+__RISCV_INSN_FUNCS(bne, RVG_MASK_BNE, RVG_MATCH_BNE)
+__RISCV_INSN_FUNCS(blt, RVG_MASK_BLT, RVG_MATCH_BLT)
+__RISCV_INSN_FUNCS(bge, RVG_MASK_BGE, RVG_MATCH_BGE)
+__RISCV_INSN_FUNCS(bltu, RVG_MASK_BLTU, RVG_MATCH_BLTU)
+__RISCV_INSN_FUNCS(bgeu, RVG_MASK_BGEU, RVG_MATCH_BGEU)
+__RISCV_INSN_FUNCS(c_beqz, RVC_MASK_C_BEQZ, RVC_MATCH_C_BEQZ)
+__RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ)
+__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK)
+__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK)
+__RISCV_INSN_FUNCS(ecall, RVG_MASK_EBREAK, RVG_MATCH_EBREAK)
+__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET)
+__RISCV_INSN_FUNCS(mret, RVG_MASK_MRET, RVG_MATCH_MRET)
+__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE);
+
+/* special case to catch _any_ system instruction */
+static __always_inline bool riscv_insn_is_system(u32 code)
+{
+ return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_SYSTEM;
+}
+
+/* special case to catch _any_ branch instruction */
+static __always_inline bool riscv_insn_is_branch(u32 code)
+{
+ return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_BRANCH;
+}
+
+static __always_inline bool riscv_insn_is_c_jr(u32 code)
+{
+ return (code & RVC_MASK_C_JR) == RVC_MATCH_C_JR &&
+ (code & RVC_INSN_J_RS1_MASK) != 0;
+}
+
+static __always_inline bool riscv_insn_is_c_jalr(u32 code)
+{
+ return (code & RVC_MASK_C_JALR) == RVC_MATCH_C_JALR &&
+ (code & RVC_INSN_J_RS1_MASK) != 0;
+}
+
+#define INSN_MATCH_LB 0x3
+#define INSN_MASK_LB 0x707f
+#define INSN_MATCH_LH 0x1003
+#define INSN_MASK_LH 0x707f
+#define INSN_MATCH_LW 0x2003
+#define INSN_MASK_LW 0x707f
+#define INSN_MATCH_LD 0x3003
+#define INSN_MASK_LD 0x707f
+#define INSN_MATCH_LBU 0x4003
+#define INSN_MASK_LBU 0x707f
+#define INSN_MATCH_LHU 0x5003
+#define INSN_MASK_LHU 0x707f
+#define INSN_MATCH_LWU 0x6003
+#define INSN_MASK_LWU 0x707f
+#define INSN_MATCH_SB 0x23
+#define INSN_MASK_SB 0x707f
+#define INSN_MATCH_SH 0x1023
+#define INSN_MASK_SH 0x707f
+#define INSN_MATCH_SW 0x2023
+#define INSN_MASK_SW 0x707f
+#define INSN_MATCH_SD 0x3023
+#define INSN_MASK_SD 0x707f
+
+#define INSN_MATCH_C_LD 0x6000
+#define INSN_MASK_C_LD 0xe003
+#define INSN_MATCH_C_SD 0xe000
+#define INSN_MASK_C_SD 0xe003
+#define INSN_MATCH_C_LW 0x4000
+#define INSN_MASK_C_LW 0xe003
+#define INSN_MATCH_C_SW 0xc000
+#define INSN_MASK_C_SW 0xe003
+#define INSN_MATCH_C_LDSP 0x6002
+#define INSN_MASK_C_LDSP 0xe003
+#define INSN_MATCH_C_SDSP 0xe002
+#define INSN_MASK_C_SDSP 0xe003
+#define INSN_MATCH_C_LWSP 0x4002
+#define INSN_MASK_C_LWSP 0xe003
+#define INSN_MATCH_C_SWSP 0xc002
+#define INSN_MASK_C_SWSP 0xe003
+
+#define INSN_OPCODE_MASK 0x007c
+#define INSN_OPCODE_SHIFT 2
+#define INSN_OPCODE_SYSTEM 28
+
+#define INSN_MASK_WFI 0xffffffff
+#define INSN_MATCH_WFI 0x10500073
+
+#define INSN_MASK_WRS 0xffffffff
+#define INSN_MATCH_WRS 0x00d00073
+
+#define INSN_MATCH_CSRRW 0x1073
+#define INSN_MASK_CSRRW 0x707f
+#define INSN_MATCH_CSRRS 0x2073
+#define INSN_MASK_CSRRS 0x707f
+#define INSN_MATCH_CSRRC 0x3073
+#define INSN_MASK_CSRRC 0x707f
+#define INSN_MATCH_CSRRWI 0x5073
+#define INSN_MASK_CSRRWI 0x707f
+#define INSN_MATCH_CSRRSI 0x6073
+#define INSN_MASK_CSRRSI 0x707f
+#define INSN_MATCH_CSRRCI 0x7073
+#define INSN_MASK_CSRRCI 0x707f
+
+#define INSN_MATCH_FLW 0x2007
+#define INSN_MASK_FLW 0x707f
+#define INSN_MATCH_FLD 0x3007
+#define INSN_MASK_FLD 0x707f
+#define INSN_MATCH_FLQ 0x4007
+#define INSN_MASK_FLQ 0x707f
+#define INSN_MATCH_FSW 0x2027
+#define INSN_MASK_FSW 0x707f
+#define INSN_MATCH_FSD 0x3027
+#define INSN_MASK_FSD 0x707f
+#define INSN_MATCH_FSQ 0x4027
+#define INSN_MASK_FSQ 0x707f
+
+#define INSN_MATCH_C_FLD 0x2000
+#define INSN_MASK_C_FLD 0xe003
+#define INSN_MATCH_C_FLW 0x6000
+#define INSN_MASK_C_FLW 0xe003
+#define INSN_MATCH_C_FSD 0xa000
+#define INSN_MASK_C_FSD 0xe003
+#define INSN_MATCH_C_FSW 0xe000
+#define INSN_MASK_C_FSW 0xe003
+#define INSN_MATCH_C_FLDSP 0x2002
+#define INSN_MASK_C_FLDSP 0xe003
+#define INSN_MATCH_C_FSDSP 0xa002
+#define INSN_MASK_C_FSDSP 0xe003
+#define INSN_MATCH_C_FLWSP 0x6002
+#define INSN_MASK_C_FLWSP 0xe003
+#define INSN_MATCH_C_FSWSP 0xe002
+#define INSN_MASK_C_FSWSP 0xe003
+
+#define INSN_MATCH_C_LHU 0x8400
+#define INSN_MASK_C_LHU 0xfc43
+#define INSN_MATCH_C_LH 0x8440
+#define INSN_MASK_C_LH 0xfc43
+#define INSN_MATCH_C_SH 0x8c00
+#define INSN_MASK_C_SH 0xfc43
+
+#define INSN_16BIT_MASK 0x3
+#define INSN_IS_16BIT(insn) (((insn) & INSN_16BIT_MASK) != INSN_16BIT_MASK)
+#define INSN_LEN(insn) (INSN_IS_16BIT(insn) ? 2 : 4)
+
+#define SHIFT_RIGHT(x, y) \
+ ((y) < 0 ? ((x) << -(y)) : ((x) >> (y)))
+
+#define REG_MASK \
+ ((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES))
+
+#define REG_OFFSET(insn, pos) \
+ (SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK)
+
+#define REG_PTR(insn, pos, regs) \
+ ((ulong *)((ulong)(regs) + REG_OFFSET(insn, pos)))
+
+#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs))
+#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs))
+#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs))
+#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs))
+#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs))
+#define GET_SP(regs) (*REG_PTR(2, 0, regs))
+#define SET_RD(insn, regs, val) (*REG_PTR(insn, SH_RD, regs) = (val))
+#define IMM_I(insn) ((s32)(insn) >> 20)
+#define IMM_S(insn) (((s32)(insn) >> 25 << 5) | \
+ (s32)(((insn) >> 7) & 0x1f))
+
+#define SH_RD 7
+#define SH_RS1 15
+#define SH_RS2 20
+#define SH_RS2C 2
+#define MASK_RX 0x1f
+
+#if defined(CONFIG_64BIT)
+#define LOG_REGBYTES 3
+#else
+#define LOG_REGBYTES 2
+#endif
+
+#define MASK_FUNCT3 0x7000
+
+#define GET_FUNCT3(insn) (((insn) >> 12) & 7)
+
+#define RV_IMM_SIGN(x) (-(((x) >> 31) & 1))
+#define RVC_IMM_SIGN(x) (-(((x) >> 12) & 1))
+#define RV_X_MASK(X, s, mask) (((X) >> (s)) & (mask))
+#define RV_X(X, s, n) RV_X_MASK(X, s, ((1 << (n)) - 1))
+#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \
+ (RV_X(x, 10, 3) << 3) | \
+ (RV_X(x, 5, 1) << 6))
+#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \
+ (RV_X(x, 5, 2) << 6))
+#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \
+ (RV_X(x, 12, 1) << 5) | \
+ (RV_X(x, 2, 2) << 6))
+#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \
+ (RV_X(x, 12, 1) << 5) | \
+ (RV_X(x, 2, 3) << 6))
+#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \
+ (RV_X(x, 7, 2) << 6))
+#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \
+ (RV_X(x, 7, 3) << 6))
+#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3))
+#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3))
+#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5)
+#define RVC_X(X, s, mask) RV_X_MASK(X, s, mask)
+
+#define RV_EXTRACT_FUNCT3(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RV_INSN_FUNCT3_OPOFF, \
+ RV_INSN_FUNCT3_MASK >> RV_INSN_FUNCT3_OPOFF)); })
+
+#define RV_EXTRACT_RS1_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RVG_RS1_OPOFF, RVG_RS1_MASK)); })
+
+#define RV_EXTRACT_RS2_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RVG_RS2_OPOFF, RVG_RS2_MASK)); })
+
+#define RV_EXTRACT_RD_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RVG_RD_OPOFF, RVG_RD_MASK)); })
+
+#define RV_EXTRACT_UTYPE_IMM(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RV_U_IMM_31_12_OPOFF, RV_U_IMM_31_12_MASK)); })
+
+#define RV_EXTRACT_JTYPE_IMM(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RV_J_IMM_10_1_OPOFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_OFF) | \
+ (RV_X_MASK(x_, RV_J_IMM_11_OPOFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OFF) | \
+ (RV_X_MASK(x_, RV_J_IMM_19_12_OPOFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_12_OFF) | \
+ (RV_IMM_SIGN(x_) << RV_J_IMM_SIGN_OFF); })
+
+#define RV_EXTRACT_ITYPE_IMM(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RV_I_IMM_11_0_OPOFF, RV_I_IMM_11_0_MASK)) | \
+ (RV_IMM_SIGN(x_) << RV_I_IMM_SIGN_OFF); })
+
+#define RV_EXTRACT_BTYPE_IMM(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RV_B_IMM_4_1_OPOFF, RV_B_IMM_4_1_MASK) << RV_B_IMM_4_1_OFF) | \
+ (RV_X_MASK(x_, RV_B_IMM_10_5_OPOFF, RV_B_IMM_10_5_MASK) << RV_B_IMM_10_5_OFF) | \
+ (RV_X_MASK(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) | \
+ (RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); })
+
+#define RVC_EXTRACT_C2_RS1_REG(x) \
+ ({typeof(x) x_ = (x); \
+ (RV_X_MASK(x_, RVC_C2_RS1_OPOFF, RVC_C2_RS1_MASK)); })
+
+#define RVC_EXTRACT_JTYPE_IMM(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_J_IMM_3_1_OPOFF, RVC_J_IMM_3_1_MASK) << RVC_J_IMM_3_1_OFF) | \
+ (RVC_X(x_, RVC_J_IMM_4_OPOFF, RVC_J_IMM_4_MASK) << RVC_J_IMM_4_OFF) | \
+ (RVC_X(x_, RVC_J_IMM_5_OPOFF, RVC_J_IMM_5_MASK) << RVC_J_IMM_5_OFF) | \
+ (RVC_X(x_, RVC_J_IMM_6_OPOFF, RVC_J_IMM_6_MASK) << RVC_J_IMM_6_OFF) | \
+ (RVC_X(x_, RVC_J_IMM_7_OPOFF, RVC_J_IMM_7_MASK) << RVC_J_IMM_7_OFF) | \
+ (RVC_X(x_, RVC_J_IMM_9_8_OPOFF, RVC_J_IMM_9_8_MASK) << RVC_J_IMM_9_8_OFF) | \
+ (RVC_X(x_, RVC_J_IMM_10_OPOFF, RVC_J_IMM_10_MASK) << RVC_J_IMM_10_OFF) | \
+ (RVC_IMM_SIGN(x_) << RVC_J_IMM_SIGN_OFF); })
+
+#define RVC_EXTRACT_BTYPE_IMM(x) \
+ ({typeof(x) x_ = (x); \
+ (RVC_X(x_, RVC_B_IMM_2_1_OPOFF, RVC_B_IMM_2_1_MASK) << RVC_B_IMM_2_1_OFF) | \
+ (RVC_X(x_, RVC_B_IMM_4_3_OPOFF, RVC_B_IMM_4_3_MASK) << RVC_B_IMM_4_3_OFF) | \
+ (RVC_X(x_, RVC_B_IMM_5_OPOFF, RVC_B_IMM_5_MASK) << RVC_B_IMM_5_OFF) | \
+ (RVC_X(x_, RVC_B_IMM_7_6_OPOFF, RVC_B_IMM_7_6_MASK) << RVC_B_IMM_7_6_OFF) | \
+ (RVC_IMM_SIGN(x_) << RVC_B_IMM_SIGN_OFF); })
+
+#define RVG_EXTRACT_SYSTEM_CSR(x) \
+ ({typeof(x) x_ = (x); RV_X_MASK(x_, RVG_SYSTEM_CSR_OFF, RVG_SYSTEM_CSR_MASK); })
+
+#define RVFDQ_EXTRACT_FL_FS_WIDTH(x) \
+ ({typeof(x) x_ = (x); RV_X_MASK(x_, RVFDQ_FL_FS_WIDTH_OFF, \
+ RVFDQ_FL_FS_WIDTH_MASK); })
+
+#define RVV_EXTRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x)
+
+/*
+ * Get the immediate from a J-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_jtype_imm(u32 insn)
+{
+ return RV_EXTRACT_JTYPE_IMM(insn);
+}
+
+/*
+ * Update a J-type instruction with an immediate value.
+ *
+ * @insn: pointer to the jtype instruction
+ * @imm: the immediate to insert into the instruction
+ */
+static inline void riscv_insn_insert_jtype_imm(u32 *insn, s32 imm)
+{
+ /* drop the old IMMs, all jal IMM bits sit at 31:12 */
+ *insn &= ~GENMASK(31, 12);
+ *insn |= (RV_X_MASK(imm, RV_J_IMM_10_1_OFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_OPOFF) |
+ (RV_X_MASK(imm, RV_J_IMM_11_OFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OPOFF) |
+ (RV_X_MASK(imm, RV_J_IMM_19_12_OFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_12_OPOFF) |
+ (RV_X_MASK(imm, RV_J_IMM_SIGN_OFF, 1) << RV_J_IMM_SIGN_OPOFF);
+}
+
+/*
+ * Put together one immediate from a U-type and I-type instruction pair.
+ *
+ * The U-type contains an upper immediate, meaning bits[31:12] with [11:0]
+ * being zero, while the I-type contains a 12bit immediate.
+ * Combined these can encode larger 32bit values and are used for example
+ * in auipc + jalr pairs to allow larger jumps.
+ *
+ * @utype_insn: instruction containing the upper immediate
+ * @itype_insn: instruction
+ * Return: combined immediate
+ */
+static inline s32 riscv_insn_extract_utype_itype_imm(u32 utype_insn, u32 itype_insn)
+{
+ s32 imm;
+
+ imm = RV_EXTRACT_UTYPE_IMM(utype_insn);
+ imm += RV_EXTRACT_ITYPE_IMM(itype_insn);
+
+ return imm;
+}
+
+/*
+ * Update a set of two instructions (U-type + I-type) with an immediate value.
+ *
+ * Used for example in auipc+jalrs pairs the U-type instructions contains
+ * a 20bit upper immediate representing bits[31:12], while the I-type
+ * instruction contains a 12bit immediate representing bits[11:0].
+ *
+ * This also takes into account that both separate immediates are
+ * considered as signed values, so if the I-type immediate becomes
+ * negative (BIT(11) set) the U-type part gets adjusted.
+ *
+ * @utype_insn: pointer to the utype instruction of the pair
+ * @itype_insn: pointer to the itype instruction of the pair
+ * @imm: the immediate to insert into the two instructions
+ */
+static inline void riscv_insn_insert_utype_itype_imm(u32 *utype_insn, u32 *itype_insn, s32 imm)
+{
+ /* drop possible old IMM values */
+ *utype_insn &= ~(RV_U_IMM_31_12_MASK);
+ *itype_insn &= ~(RV_I_IMM_11_0_MASK << RV_I_IMM_11_0_OPOFF);
+
+ /* add the adapted IMMs */
+ *utype_insn |= (imm & RV_U_IMM_31_12_MASK) + ((imm & BIT(11)) << 1);
+ *itype_insn |= ((imm & RV_I_IMM_11_0_MASK) << RV_I_IMM_11_0_OPOFF);
+}
+
+/*
+ * Get the immediate from a B-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_btype_imm(u32 insn)
+{
+ return RV_EXTRACT_BTYPE_IMM(insn);
+}
+
+/*
+ * Get the immediate from a RVC B-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_cbtype_imm(u32 insn)
+{
+ return RVC_EXTRACT_BTYPE_IMM(insn);
+}
+
+/*
+ * Get the immediate from a RVC J-type instruction.
+ *
+ * @insn: instruction to process
+ * Return: immediate
+ */
+static inline s32 riscv_insn_extract_cjtype_imm(u16 insn)
+{
+ return RVC_EXTRACT_JTYPE_IMM(insn);
+}
+
+#endif /* _ASM_RISCV_INSN_H */
diff --git a/tools/perf/arch/riscv/util/auxtrace.c b/tools/perf/arch/riscv/util/auxtrace.c
index 5feee198ef97..a5450bef1a8a 100644
--- a/tools/perf/arch/riscv/util/auxtrace.c
+++ b/tools/perf/arch/riscv/util/auxtrace.c
@@ -10,6 +10,7 @@
#include <linux/zalloc.h>
#include <linux/string.h>
#include <time.h>
+#include <errno.h>
#include <internal/lib.h> // page_size
#include "../../../util/auxtrace.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index bcccad7487a9..648a8552df9e 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -148,6 +148,8 @@ perf-util-y += cs-etm-decoder/
endif
perf-util-y += cs-etm-base.o
+perf-util-y += nexus-rv-decoder/
+
perf-util-y += parse-branch-options.o
perf-util-y += dump-insn.o
perf-util-y += parse-regs-options.o
diff --git a/tools/perf/util/nexus-rv-decoder/Build b/tools/perf/util/nexus-rv-decoder/Build
new file mode 100644
index 000000000000..61cc960b1d04
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/Build
@@ -0,0 +1 @@
+perf-util-y += nexus-rv-decoder.o
diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
new file mode 100644
index 000000000000..2f41d818ca32
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.c
@@ -0,0 +1,1364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/zalloc.h>
+#include <linux/types.h>
+#include <errno.h>
+#include <api/fs/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../debug.h"
+#include "../color.h"
+#include "../intlist.h"
+#include "nexus-rv-decoder.h"
+#include "nexus-rv-msg.h"
+#include "../../../arch/riscv/include/asm/insn.h"
+
+#define BUFFER_SIZE 1024
+
+#define RV_REGNO_ZERO 0
+#define RV_REGNO_RA 1
+
+static struct nexus_rv_defmt_buf *nexus_rv_get_buf(struct nexus_rv_defmt_buf defmt_bufs[],
+ unsigned char id)
+{
+ if (defmt_bufs[id].buf != NULL)
+ return &defmt_bufs[id];
+
+ defmt_bufs[id].buf = (unsigned char *)malloc(BUFFER_SIZE);
+ if (!defmt_bufs[id].buf)
+ return NULL;
+
+ defmt_bufs[id].size = 0;
+ defmt_bufs[id].capacity = BUFFER_SIZE;
+
+ return &defmt_bufs[id];
+}
+
+static int nexus_rv_defmt_buf_append(struct nexus_rv_defmt_buf *defmt_buf, FILE *nexus,
+ unsigned char data)
+{
+ if (defmt_buf->size == defmt_buf->capacity) {
+ defmt_buf->capacity *= 2;
+ defmt_buf->buf = realloc(defmt_buf->buf, defmt_buf->capacity);
+ if (!defmt_buf->buf)
+ return -ENOMEM;
+ }
+ defmt_buf->buf[defmt_buf->size++] = data;
+
+ if ((data & 0x3) == 0x3) {
+ size_t n = fwrite(defmt_buf->buf, defmt_buf->size, 1, nexus);
+
+ if (n != 1) {
+ pr_err("Encoder: failed to write nexus data\n");
+ return -EINVAL;
+ }
+ defmt_buf->size = 0;
+ }
+
+ return 0;
+}
+
+static size_t skip_frame_syncs(const unsigned char *buf, size_t len, size_t *fsync_count)
+{
+ const uint32_t FSYNC_PATTERN = 0x7FFFFFFF; /* LE host pattern for Frame SYNC */
+ size_t skipped_bytes = 0;
+
+ while (skipped_bytes + 4 <= len) {
+ if (*((uint32_t *)(buf + skipped_bytes)) == FSYNC_PATTERN) {
+ skipped_bytes += 4;
+ (*fsync_count)++;
+ } else {
+ break;
+ }
+ }
+
+ return skipped_bytes;
+}
+
+/* remove coresight formatter frame */
+static int nexus_rv_pkt_defmt(struct nexus_rv_defmt_buf defmt_bufs[], FILE *nexus,
+ const unsigned char *buf, size_t len)
+{
+ int err;
+ uint8_t flag_bit;
+ unsigned char data = 0;
+ int cur_id = -1, old_id = -1, new_id = -1;
+
+ /* 16 bytes is output by coresight trace formatter */
+ while (len >= 16) {
+ /* some linux drivers (e.g. for perf) will insert FSYNCS to pad or differentiate
+ * between blocks of aligned data, always in frame aligned complete 16 byte frames.
+ * we need to skip past these frames, resetting as we go.
+ */
+ size_t fsync_count = 0;
+ size_t fsync_bytes = skip_frame_syncs(buf, len, &fsync_count);
+
+ if (fsync_bytes > 0) {
+ if (fsync_count % 4 == 0) {
+ cur_id = -1;
+ old_id = -1;
+ new_id = -1;
+ } else {
+ pr_err("Incorrect FSYNC reset pattern\n");
+ return -EINVAL;
+ }
+ buf += fsync_bytes;
+ len -= fsync_bytes;
+ continue;
+ }
+
+ flag_bit = 0x1;
+ for (int i = 0; i < 15; i += 2) {
+ if (buf[i] & 0x1) {
+ /* it's id */
+ old_id = new_id;
+ new_id = buf[i] >> 1; /* get new_id */
+ cur_id = (flag_bit & buf[15]) ? old_id : new_id;
+ } else {
+ /* it's data */
+ data = buf[i] | ((flag_bit & buf[15]) ? 0x1 : 0x0);
+ }
+
+ if (IS_VALID_ID(cur_id)) {
+ struct nexus_rv_defmt_buf *defmt_buf =
+ nexus_rv_get_buf(defmt_bufs, cur_id);
+
+ if (!defmt_buf)
+ return -ENOMEM;
+
+ /* it's data */
+ if ((buf[i] & 0x1) == 0) {
+ err = nexus_rv_defmt_buf_append(defmt_buf, nexus, data);
+ if (err)
+ return err;
+ }
+
+ /* buf[i+1] is alway data when i != 14 */
+ if (i != 14) {
+ err = nexus_rv_defmt_buf_append(defmt_buf, nexus,
+ buf[i + 1]);
+ if (err)
+ return err;
+ }
+ }
+
+ if (cur_id != new_id)
+ cur_id = new_id;
+
+ flag_bit <<= 1;
+ }
+ buf += 16;
+ len -= 16;
+ }
+ return 0;
+}
+
+/* dump all nexus messages (from nexus file) */
+static int nexus_rv_pkt_dump(struct nexus_rv_pkt_decoder *decoder, FILE *nexus)
+{
+ const char *color = PERF_COLOR_BLUE;
+ int fld_def = -1;
+ int fld_bits = 0;
+ u64 fld_val = 0;
+
+ int msg_cnt = 0;
+ int msg_bytes = 0;
+ int msg_errors = 0;
+ int idle_cnt = 0;
+
+ bool find_next_package = false;
+ int error_msgs = 0;
+
+ unsigned int tcode = 0;
+ unsigned int correlation_hist = 0;
+ unsigned int resourcefull_hrepeat = 0;
+
+ unsigned char msg_byte = 0;
+ unsigned char prev_byte = 0;
+ unsigned int mdo = 0;
+ unsigned int mseo = 0;
+
+ for (;;) {
+ prev_byte = msg_byte;
+ if (fread(&msg_byte, 1, 1, nexus) != 1)
+ break; /* EOF */
+
+ if (find_next_package) {
+ if ((prev_byte & 0x3) == 0x3) { /* last byte */
+ error_msgs++;
+ find_next_package = false;
+
+ /* reinit some val */
+ fld_def = -1;
+ fld_bits = 0;
+ fld_val = 0;
+ } else {
+ continue;
+ }
+ }
+
+ /* This will skip long sequnece of idles (visible in true captures ...) */
+ if (msg_byte == 0xFF && prev_byte == 0xFF)
+ continue;
+
+ mdo = msg_byte >> 2;
+ mseo = msg_byte & 0x3;
+
+ if (mseo == 0x2) {
+ pr_err(" ERROR: At offset %d: MSEO='10' is not allowed\n",
+ msg_bytes + idle_cnt);
+ find_next_package = true;
+ continue;
+ }
+
+ if (fld_def < 0) {
+ if (mseo == 0x3) {
+ color_fprintf(stdout, color, "MSG #%d +%d - IDLE\n",
+ msg_cnt, msg_bytes);
+ msg_cnt++;
+ msg_bytes++;
+ idle_cnt++;
+ continue;
+ }
+
+ if (mseo != 0x0) {
+ pr_err(" ERROR: At offset %d: Message must start from MSEO='00'\n",
+ msg_bytes + idle_cnt);
+ find_next_package = true;
+ continue;
+ }
+
+ for (int d = 0; NEXUS_MSG_DEF[d].def != 0; d++) {
+ if ((NEXUS_MSG_DEF[d].def & 0x100) == 0)
+ continue;
+ if ((NEXUS_MSG_DEF[d].def & 0xFF) == mdo) {
+ fld_def = d; /* Found TCODE */
+ tcode = mdo;
+ break;
+ }
+ }
+
+ if (fld_def < 0) {
+ pr_err(" ERROR: At offset %d: Message with TCODE=%d is not defined for N-Trace\n",
+ msg_bytes + idle_cnt, mdo);
+ find_next_package = true;
+ continue;
+ }
+
+ color_fprintf(stdout, color, "MSG #%d +%d - %s TCODE[6]=%d",
+ msg_cnt, msg_bytes, NEXUS_MSG_DEF[fld_def].name, mdo);
+ msg_cnt++;
+ msg_bytes++;
+
+ if (mdo == NEXUS_TCODE_Error)
+ msg_errors++;
+
+ fld_def++;
+ fld_bits = 0;
+ fld_val = 0;
+ continue;
+ }
+
+ /* Accumulate 'mdo' to field value */
+ fld_val |= (((u64)mdo) << fld_bits);
+ fld_bits += 6;
+
+ msg_bytes++;
+
+ /* Process fixed size fields (there may be more than one in one MDO record) */
+ while (NEXUS_MSG_DEF[fld_def].def & 0x200) {
+ int fld_size = NEXUS_MSG_DEF[fld_def].def & 0xFF;
+
+ if (fld_size & 0x80) {
+ /* Size of this field is defined by parameter ... */
+ fld_size = decoder->src_bits;
+ }
+ if (fld_bits < fld_size)
+ break; /* Not enough bits for this field */
+ color_fprintf(stdout, color, " %s[%d]=0x%lx",
+ NEXUS_MSG_DEF[fld_def].name, fld_size,
+ fld_val & ((((u64)1) << fld_size) - 1));
+ fld_def++;
+ fld_val >>= fld_size;
+ fld_bits -= fld_size;
+ }
+
+ if (mseo == 0x0)
+ continue;
+
+ if (NEXUS_MSG_DEF[fld_def].def & 0x400) {
+ /* Process ResourceFull cfg HREPEAT field */
+ if (tcode == NEXUS_TCODE_ResourceFull) {
+ if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "RCODE")
+ && fld_val == 0x2)
+ resourcefull_hrepeat = 1;
+
+ if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "HREPEAT")
+ && resourcefull_hrepeat != 1) {
+ /* no HREPEAT field */
+ fld_def++;
+ resourcefull_hrepeat = 0;
+ }
+ }
+
+ /* Process ProgTraceCorrelation cfg HIST field */
+ if (tcode == NEXUS_TCODE_ProgTraceCorrelation) {
+ if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "CDF") && fld_val == 0x2)
+ correlation_hist = 1;
+
+ if (!strcmp(NEXUS_MSG_DEF[fld_def].name, "HIST")
+ && correlation_hist != 1) {
+ /* no HIST field */
+ fld_def++;
+ correlation_hist = 0;
+ }
+ }
+
+ /* Variable size field */
+ color_fprintf(stdout, color, " %s[%d]=0x%lx",
+ NEXUS_MSG_DEF[fld_def].name, fld_bits, fld_val);
+
+ if (mseo == 3) {
+ printf("\n");
+ fld_def = -1;
+ } else {
+ fld_def++;
+ }
+ fld_bits = 0;
+ fld_val = 0;
+ continue;
+ }
+
+ if (fld_bits > 0) {
+ pr_err(" ERROR: At offset %d: Not enough bits for non-variable field\n",
+ msg_bytes + idle_cnt);
+ find_next_package = true;
+ continue;
+ }
+ }
+
+ color_fprintf(stdout, color,
+ "\nStat: %d bytes, %d idles, %d messages, %d error messages, %d invalid messages",
+ msg_bytes, idle_cnt, msg_cnt, msg_errors, error_msgs);
+ if (msg_cnt > 0)
+ color_fprintf(stdout, color, "%.2lf bytes/message", ((double)msg_bytes) / msg_cnt);
+
+ printf("\n");
+
+ return 0;
+}
+
+struct nexus_rv_pkt_decoder *nexus_rv_pkt_decoder_new(struct nexus_rv_pkt_decoder_params *params)
+{
+ struct nexus_rv_pkt_decoder *decoder;
+
+ if (!params)
+ return NULL;
+
+ decoder = zalloc(sizeof(struct nexus_rv_pkt_decoder));
+ if (!decoder)
+ return NULL;
+
+ decoder->formatted = params->formatted;
+ decoder->src_bits = params->src_bits;
+
+ return decoder;
+}
+
+void nexus_rv_pkt_decoder_free(struct nexus_rv_pkt_decoder *decoder)
+{
+ for (int i = 0; i < MAX_ID; ++i)
+ free(decoder->defmt_bufs[i].buf);
+
+ free(decoder);
+}
+
+int nexus_rv_pkt_desc(struct nexus_rv_pkt_decoder *decoder, const unsigned char *buf, size_t len)
+{
+ int err;
+ char filename[PATH_MAX];
+ FILE *nexus;
+ char *dir = getenv("PERF_BUILDID_DIR");
+
+ /* TODO: The filename here is always trace.bin, which results in
+ * only the last Nexus trace data being retained. A corresponding
+ * Nexus filename should be generated for each AUX data.
+ */
+ snprintf(filename, sizeof(filename), "%s/trace.bin", dir);
+ nexus = fopen(filename, "w+");
+
+ if (decoder->formatted) {
+ err = nexus_rv_pkt_defmt(decoder->defmt_bufs, nexus, buf, len);
+ if (err) {
+ pr_err("Encoder: failed to remove coresight trace formatter\n");
+ return err;
+ }
+ } else {
+ size_t n = fwrite(buf, len, 1, nexus);
+
+ if (n != 1) {
+ pr_err("Encoder: failed to write nexus data\n");
+ fclose(nexus);
+ return -EINVAL;
+ }
+ }
+
+ fseek(nexus, 0, SEEK_SET);
+ err = nexus_rv_pkt_dump(decoder, nexus);
+
+ fclose(nexus);
+
+ return err;
+}
+
+static int nexus_rv_init_packet_buffer(struct nexus_rv_packet_buffer *packet_buffer)
+{
+ if (!packet_buffer->packets) {
+ packet_buffer->packets = calloc(BUFFER_SIZE, sizeof(struct nexus_rv_packet));
+ if (!packet_buffer->packets)
+ return -ENOMEM;
+ }
+
+ packet_buffer->size = 0;
+ packet_buffer->capacity = BUFFER_SIZE;
+
+ return 0;
+}
+
+static int nexus_rv_packet_buffer_append(struct nexus_rv_packet_buffer *packet_buffer,
+ struct nexus_rv_packet packet)
+{
+ if (packet_buffer->size == packet_buffer->capacity) {
+ packet_buffer->capacity *= 2;
+ packet_buffer->packets = realloc(packet_buffer->packets,
+ sizeof(struct nexus_rv_packet) * packet_buffer->capacity);
+ if (!packet_buffer->packets)
+ return -ENOMEM;
+ }
+ packet_buffer->packets[packet_buffer->size++] = packet;
+
+ return 0;
+}
+
+static void nexus_rv_free_packet_buffer(struct nexus_rv_packet_buffer *packet_buffer)
+{
+ free(packet_buffer->packets);
+}
+
+static int nexus_rv_init_stack(struct nexus_rv_stack *stack)
+{
+ if (!stack->data) {
+ stack->data = (u64 *)malloc(sizeof(u64) * BUFFER_SIZE);
+ if (!stack->data)
+ return -ENOMEM;
+ }
+
+ stack->top = -1;
+ stack->capacity = BUFFER_SIZE;
+
+ return 0;
+}
+
+static int nexus_rv_stack_push(struct nexus_rv_stack *stack, u64 value)
+{
+ if (stack->top == stack->capacity - 1) {
+ stack->capacity *= 2;
+ stack->data = (u64 *)realloc(stack->data, sizeof(u64) * stack->capacity);
+ if (!stack->data)
+ return -ENOMEM;
+ }
+ stack->data[++stack->top] = value;
+
+ return 0;
+}
+
+static int nexus_rv_stack_pop(struct nexus_rv_stack *stack)
+{
+ u64 value;
+
+ if (stack->top == -1)
+ return 1; /* Empty */
+
+ value = stack->data[stack->top--];
+ return value;
+}
+
+static void nexus_rv_free_stack(struct nexus_rv_stack *stack)
+{
+ free(stack->data);
+}
+
+static struct nexus_rv_src_context *nexus_rv_get_src_context(struct nexus_rv_insn_decoder *decoder,
+ int src_id)
+{
+ struct int_node *node;
+ struct nexus_rv_src_context *ctx;
+
+ node = intlist__find(decoder->src_contexts, src_id);
+ if (node)
+ return (struct nexus_rv_src_context *)node->priv;
+
+ ctx = zalloc(sizeof(struct nexus_rv_src_context));
+ if (!ctx)
+ return NULL;
+
+ ctx->src_id = src_id;
+ ctx->nexdeco_pc = 1;
+ ctx->nexdeco_lastaddr = 1;
+ ctx->prv = 0;
+ ctx->v = 0;
+ ctx->timestamp = 0;
+ ctx->context = -1;
+ ctx->resourcefull_icnt = 0;
+
+ if (nexus_rv_init_stack(&ctx->stack)) {
+ free(ctx);
+ return NULL;
+ }
+
+ /* Add to RB Tree */
+ node = intlist__findnew(decoder->src_contexts, src_id);
+ if (!node) {
+ nexus_rv_free_stack(&ctx->stack);
+ free(ctx);
+ return NULL;
+ }
+
+ node->priv = ctx;
+
+ return ctx;
+}
+
+static void nexus_rv_free_src_contexts(struct intlist *src_contexts)
+{
+ struct int_node *node;
+
+ intlist__for_each_entry(node, src_contexts) {
+ struct nexus_rv_src_context *ctx = (struct nexus_rv_src_context *)node->priv;
+
+ nexus_rv_free_stack(&ctx->stack);
+ free(ctx);
+ }
+ intlist__delete(src_contexts);
+}
+
+static int handle_error_msg(struct nexus_rv_src_context *ctx, const char *err)
+{
+ pr_err("\nERROR: %s\n", err);
+ ctx->nexdeco_pc = 1; /* 1 means, that last address is unknown */
+ nexus_rv_init_stack(&ctx->stack);
+ return -EINVAL;
+}
+
+static int nexus_rv_insn_info_get(struct nexus_rv_insn_decoder *decoder, u8 *info, u64 *dest)
+{
+ u32 insn;
+ struct nexus_rv_src_context *ctx = decoder->current_src_ctx;
+ u64 addr = ctx->nexdeco_pc;
+
+ if (!decoder->mem_access(decoder->data, addr, ctx->prv, ctx->context,
+ sizeof(insn), (u8 *)&insn))
+ return -EINVAL;
+
+ *info = INFO_LINEAR;
+ if ((insn & (__INSN_LENGTH_MASK)) != (__INSN_LENGTH_GE_32)) {
+ if (riscv_insn_is_c_beqz(insn) || riscv_insn_is_c_bnez(insn)) {
+ *info = INFO_BRANCH;
+ *dest = addr + riscv_insn_extract_cbtype_imm(insn);
+ } else if (riscv_insn_is_c_j(insn)) {
+ /* c.j offset */
+ *info = INFO_JUMP;
+ *dest = addr + riscv_insn_extract_cjtype_imm(insn);
+ } else if (riscv_insn_is_c_jr(insn)) {
+ *info = INFO_JUMP | INFO_INDIRECT;
+ /* ret => c.jr x1 */
+ if (RVC_EXTRACT_C2_RS1_REG(insn) == RV_REGNO_RA)
+ *info |= INFO_RET;
+ } else if (riscv_insn_is_c_jalr(insn)) {
+ *info = INFO_JUMP | INFO_CALL | INFO_INDIRECT;
+ } else {
+ *info = INFO_LINEAR;
+ }
+ } else {
+ if (riscv_insn_is_branch(insn)) {
+ *info = INFO_BRANCH;
+ *dest = addr + riscv_insn_extract_btype_imm(insn);
+ } else if (riscv_insn_is_jalr(insn)) {
+ *info = INFO_JUMP | INFO_INDIRECT;
+ if (RV_EXTRACT_RD_REG(insn) == RV_REGNO_ZERO
+ && riscv_insn_extract_jtype_imm(insn) == 0) {
+ /* ret => jalr x0,x1,0 */
+ if (RV_EXTRACT_RS1_REG(insn) == RV_REGNO_RA)
+ *info |= INFO_RET;
+ else
+ /* jr rs1 => jalr x0,rs1,0 */
+ *info |= INFO_CALL;
+ }
+ } else if (riscv_insn_is_jal(insn)) {
+ *info = INFO_JUMP;
+ /* j offset => jal x0,offset */
+ if (RV_EXTRACT_RD_REG(insn) != RV_REGNO_ZERO)
+ *info |= INFO_CALL;
+ *dest = addr + riscv_insn_extract_jtype_imm(insn);
+ } else if (riscv_insn_is_sret(insn) || riscv_insn_is_mret(insn)) {
+ *info = INFO_JUMP | INFO_INDIRECT | INFO_RET;
+ } else if (riscv_insn_is_ecall(insn)) {
+ *info = INFO_JUMP | INFO_INDIRECT | INFO_CALL;
+ } else {
+ *info = INFO_LINEAR;
+ }
+ *info |= INFO_4;
+ }
+
+ pr_debug2(".nexdeco_pc=0x%lx .insn=0x%0*x\n", addr,
+ (*info & INFO_4) ? 8 : 4,
+ (*info & INFO_4) ? insn : (u16)insn);
+ return 0;
+}
+
+static int nexus_rv_emit_icnt(struct nexus_rv_insn_decoder *decoder, int n, u32 hist)
+{
+ u64 a = 0;
+ u32 hist_mask;
+ u8 info;
+ int done_icnt = 0; /* Number of done instruction count */
+ struct nexus_rv_src_context *ctx = decoder->current_src_ctx;
+
+ if (ctx->nexdeco_pc & 1)
+ return 0; /* Not synchronized ... */
+
+ pr_debug2(".PC=0x%lX, EmitICNT(n=%d,hist=0x%x)\n", ctx->nexdeco_pc, n, hist);
+
+ /*
+ * Adjust ICNT by what was handled by ResourceFull message[s]
+ * before this message (message with normal ICNT field).
+ * NOTE: ctx->resourcefull_icnt maybe positive or negative!
+ */
+ if (n >= 0 && ctx->resourcefull_icnt != 0) {
+ n += ctx->resourcefull_icnt;
+ if (n < 0)
+ return handle_error_msg(ctx, "ICNT adjustment ERROR");
+
+ ctx->resourcefull_icnt = 0; /* Make adjustment 'consumed' */
+ }
+
+ hist_mask = 0; /* MSB is first in history, so we need sliding mask */
+ if (hist != 0) {
+ if (hist & (1 << (sizeof(u32) * 8 - 1))) {
+ hist_mask = (1 << (sizeof(u32) * 8 - 2));
+ } else {
+ hist_mask = 0x1;
+ while (hist_mask <= hist)
+ hist_mask <<= 1;
+ hist_mask >>= 2;
+ }
+ }
+
+ while (n != 0) {
+ ctx->current_pc = ctx->nexdeco_pc;
+ if (nexus_rv_insn_info_get(decoder, &info, &a))
+ return handle_error_msg(ctx, "failed to get insn info");
+
+ if (n > 0) {
+ n -= (info & INFO_4) ? 2 : 1;
+ if (n < 0)
+ return handle_error_msg(ctx, "ICNT too small");
+ done_icnt += 1;
+ }
+
+ if (info & INFO_CALL) {
+ u64 ret = ctx->nexdeco_pc + ((info & INFO_4) ? 4 : 2);
+
+ if (nexus_rv_stack_push(&ctx->stack, ret))
+ return handle_error_msg(ctx, "failed to push value to stack");
+ }
+
+ if (info & INFO_INDIRECT) { /* Cannot continue over indirect... */
+ if (info & INFO_RET) {
+ u64 ret = nexus_rv_stack_pop(&ctx->stack);
+
+ ctx->nexdeco_pc = ret;
+ if (n != 0) {
+ if (ret == 1)
+ return handle_error_msg(ctx,
+ "Not enough entries on callstack");
+ continue;
+ }
+ }
+
+ if (n > 0)
+ return handle_error_msg(ctx,
+ "indirect address encountered in ICNT");
+
+ break;
+ }
+
+ if (info & INFO_BRANCH) {
+ if (hist == 0) {
+ /* This is calling as DirectBranch */
+ if (n == 0)
+ info |= INFO_JUMP; /* Force PC change below */
+ } else {
+ if (hist_mask & hist)
+ info |= INFO_JUMP; /* Force PC change below */
+ hist_mask >>= 1;
+ if (hist_mask == 0 && n < 0)
+ n = 0;
+ }
+ }
+
+ if (info & INFO_JUMP)
+ ctx->nexdeco_pc = a; /* Direct jump/call/branch */
+ else if (info & INFO_4)
+ ctx->nexdeco_pc += 4;
+ else
+ ctx->nexdeco_pc += 2;
+
+ }
+
+ return done_icnt;
+}
+
+static u64 nexus_rv_field_get(struct nexus_rv_insn_decoder *decoder, const char *name)
+{
+ for (int d = decoder->msg_field_pos; NEXUS_MSG_DEF[d].def != 1; d++) {
+ if (strcmp(NEXUS_MSG_DEF[d].name, name) == 0) {
+ int fi = d - decoder->msg_field_pos;
+
+ if (fi <= decoder->msg_field_cnt)
+ return decoder->msg_fields[fi];
+ return 0;
+ }
+ }
+ return 0;
+}
+
+#define NEX_FLDGET(n) nexus_rv_field_get(decoder, #n)
+
+static u64 nexus_rv_calculate_addr(u64 fu_addr, int full, u64 prev_addr)
+{
+ fu_addr <<= 1; /* LSB bit is never sent */
+ if ((fu_addr >> 32) & 0x10000) /* Perform MSB extension */
+ fu_addr |= 0xFFFF000000000000UL;
+ /* Update (NEW or XOR) */
+ if (!full) {
+ if (prev_addr & 1) /* Not Sync */
+ fu_addr = prev_addr;
+ else
+ fu_addr ^= prev_addr;
+ }
+ return fu_addr;
+}
+
+static int nexus_rv_msg_handle(struct nexus_rv_insn_decoder *decoder)
+{
+ int ret = 0;
+ u64 addr;
+ struct nexus_rv_packet packet;
+ int n = 0;
+ int new_src = 0;
+ u64 start_addr = 0;
+ int TCODE = decoder->msg_fields[0];
+
+ switch (TCODE) {
+ case NEXUS_TCODE_Ownership:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ decoder->current_src_ctx->prv = NEX_FLDGET(PRV);
+ decoder->current_src_ctx->v = NEX_FLDGET(V);
+ if (NEX_FLDGET(FORMAT) && NEX_FLDGET(CONTEXT))
+ decoder->current_src_ctx->context = NEX_FLDGET(CONTEXT);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_DirectBranch:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_IndirectBranch:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+ addr = NEX_FLDGET(UADDR);
+ decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 0,
+ decoder->current_src_ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+ decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_ProgTraceSync:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+ addr = NEX_FLDGET(FADDR);
+ decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+ decoder->current_src_ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+ decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_DirectBranchSync:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+ addr = NEX_FLDGET(FADDR);
+ decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+ decoder->current_src_ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+ decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_IndirectBranchSync:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, 0x0);
+
+ addr = NEX_FLDGET(FADDR);
+ decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+ decoder->current_src_ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+ decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_IndirectBranchHist:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, NEX_FLDGET(HIST));
+
+ addr = NEX_FLDGET(UADDR);
+ decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 0,
+ decoder->current_src_ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+ decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_IndirectBranchHistSync:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, NEX_FLDGET(HIST));
+
+ addr = NEX_FLDGET(FADDR);
+ decoder->current_src_ctx->nexdeco_lastaddr = nexus_rv_calculate_addr(addr, 1,
+ decoder->current_src_ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", decoder->current_src_ctx->nexdeco_lastaddr);
+ decoder->current_src_ctx->nexdeco_pc = decoder->current_src_ctx->nexdeco_lastaddr;
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_ResourceFull:
+ /* Determine repeat count (for RCODE=2) */
+ int hrepeat = 0;
+ int rcode = NEX_FLDGET(RCODE);
+
+ if (rcode == 2)
+ hrepeat = NEX_FLDGET(HREPEAT);
+
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ if (rcode == 1 || rcode == 2) {
+ int rdata = NEX_FLDGET(RDATA);
+
+ if (rdata > 1) {
+ /* Special calling to emit HIST only ... */
+ if (decoder->disp_hist_repeat) {
+ pr_debug2("RepeatHIST,0x%x,%d\n",
+ rdata, decoder->disp_hist_repeat);
+ decoder->disp_hist_repeat = 0;
+ }
+ do {
+ /* ICNT is unknown (-1), what will process only HIST bits */
+ ret = nexus_rv_emit_icnt(decoder, -1, rdata);
+ if (ret < 0)
+ break;
+
+ /* Consume, so next time ICNT will be adjusted */
+ decoder->current_src_ctx->resourcefull_icnt -= ret;
+ hrepeat--;
+ } while (hrepeat > 0);
+ }
+ } else if (rcode == 0) {
+ decoder->current_src_ctx->resourcefull_icnt += NEX_FLDGET(RDATA);
+ }
+ break;
+
+ case NEXUS_TCODE_ProgTraceCorrelation:
+ int hist = 0;
+ int cdf = NEX_FLDGET(CDF);
+
+ if (cdf == 1)
+ hist = NEX_FLDGET(HIST);
+
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+
+ start_addr = decoder->current_src_ctx->nexdeco_pc;
+ n = NEX_FLDGET(ICNT);
+ ret = nexus_rv_emit_icnt(decoder, n, hist);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_Error:
+ new_src = NEX_FLDGET(SRC);
+ if (new_src != decoder->current_src) {
+ struct nexus_rv_src_context *ctx = nexus_rv_get_src_context(decoder,
+ new_src);
+ if (!ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ decoder->current_src_ctx = ctx;
+ }
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ break;
+
+ case NEXUS_TCODE_RepeatBranch: /* Handled differently! */
+ default:
+ return -EINVAL;
+ }
+
+ if (ret >= 0) {
+ if (NEXUS_TCODE_Error == TCODE)
+ packet.sample_type = RVTRACE_LOSS;
+ else
+ packet.sample_type = ret ? RVTRACE_RANGE : RVTRACE_EMPTY;
+ packet.start_addr = start_addr;
+ packet.end_addr = decoder->current_src_ctx->current_pc;
+ packet.insn_cnt = ret;
+ packet.cpu = new_src;
+ packet.prv = decoder->current_src_ctx->prv;
+ packet.v = decoder->current_src_ctx->v;
+ packet.context = decoder->current_src_ctx->context;
+ packet.timestamp = decoder->current_src_ctx->timestamp;
+ } else {
+ packet.sample_type = RVTRACE_ERROR;
+ }
+
+ ret = nexus_rv_packet_buffer_append(&decoder->packet_buffer, packet);
+
+ return ret;
+}
+
+static int nexus_rv_insn_decode(struct nexus_rv_insn_decoder *decoder, FILE *nexus)
+{
+ int fld_def = -1;
+ int fld_bits = 0;
+ u64 fld_val = 0;
+
+ int msg_cnt = 0;
+ int msg_bytes = 0;
+ int msg_errors = 0;
+
+ bool find_next_package = false;
+
+ unsigned char msg_byte = 0;
+ unsigned char prev_byte = 0;
+
+ unsigned int mdo = 0;
+ unsigned int mseo = 0;
+
+ decoder->msg_field_cnt = 0; /* No fields */
+
+ for (;;) {
+ prev_byte = msg_byte;
+ if (fread(&msg_byte, 1, 1, nexus) != 1)
+ break; /* EOF */
+
+ if (find_next_package) {
+ if ((prev_byte & 0x3) == 0x3) { /* last byte */
+ find_next_package = false;
+
+ /* reinit some val */
+ fld_def = -1;
+ fld_bits = 0;
+ fld_val = 0;
+ } else {
+ continue;
+ }
+ }
+
+ /* This will skip long sequnece of idles (visible in true captures ...) */
+ if (msg_byte == 0xFF && prev_byte == 0xFF)
+ continue;
+
+ mdo = msg_byte >> 2;
+ mseo = msg_byte & 0x3;
+
+ if (mseo == 0x2) {
+ pr_err("ERROR: MSEO='10' is not allowed\n");
+ find_next_package = true;
+ continue;
+ }
+
+ if (fld_def < 0) {
+ if (mseo == 0x3)
+ continue; /* skip idle */
+
+ if (mseo != 0x0) {
+ pr_err("ERROR: Message must start from MSEO='00'\n");
+ find_next_package = true;
+ continue;
+ }
+
+ for (int d = 0; NEXUS_MSG_DEF[d].def != 0; d++) {
+ if ((NEXUS_MSG_DEF[d].def & 0x100) == 0)
+ continue;
+ if ((NEXUS_MSG_DEF[d].def & 0xFF) == mdo) {
+ fld_def = d; /* Found TCODE */
+ break;
+ }
+ }
+
+ if (fld_def < 0) {
+ pr_err("ERROR: Message with TCODE=%d is not defined for RISC-V\n",
+ mdo);
+ find_next_package = true;
+ continue;
+ }
+
+ /*
+ * Special handling for RepeatBranch message.
+ * We want to preserve previous packet, so we can
+ * repeat it at end of RepeatBranch handling.
+ */
+ if (mdo == NEXUS_TCODE_RepeatBranch) {
+ /* Save previous message fields */
+ decoder->saved_fields[0] = decoder->msg_field_pos;
+ decoder->saved_fields[1] = decoder->msg_field_cnt;
+ decoder->saved_fields[2] = decoder->msg_fields[0];
+ decoder->saved_fields[3] = decoder->msg_fields[1];
+ decoder->saved_fields[4] = decoder->msg_fields[2];
+ decoder->saved_fields[5] = decoder->msg_fields[3];
+ decoder->saved_fields[6] = decoder->msg_fields[4];
+ }
+
+ /* Save to allow later decoding */
+ decoder->msg_field_pos = fld_def;
+ decoder->msg_field_cnt = 0;
+ decoder->msg_fields[decoder->msg_field_cnt++] = mdo;
+
+ msg_cnt++;
+ msg_bytes++;
+
+ if (mdo == NEXUS_TCODE_Error)
+ msg_errors++;
+
+ fld_def++;
+ fld_bits = 0;
+ fld_val = 0;
+ continue;
+ }
+
+ /* Accumulate 'mdo' to field value */
+ fld_val |= (((u64)mdo) << fld_bits);
+ fld_bits += 6;
+
+ msg_bytes++;
+
+ /* Process fixed size fields (there may be more than one in one MDO record) */
+ while (NEXUS_MSG_DEF[fld_def].def & 0x200) {
+ int fld_size = NEXUS_MSG_DEF[fld_def].def & 0xFF;
+
+ if (fld_size & 0x80)
+ fld_size = decoder->src_bits;
+ if (fld_bits < fld_size)
+ break; /* Not enough bits for this field */
+
+ /* Save field */
+ decoder->msg_fields[decoder->msg_field_cnt++] =
+ fld_val & ((((u64)1) << fld_size) - 1);
+
+ fld_def++;
+ fld_val >>= fld_size;
+ fld_bits -= fld_size;
+ }
+
+ if (mseo == 0x0)
+ continue;
+
+ if (NEXUS_MSG_DEF[fld_def].def & 0x400) {
+ /* Variable size field */
+ decoder->msg_fields[decoder->msg_field_cnt++] = fld_val; /* Save fieliid */
+
+ if (mseo == 3) {
+ int cnt = 1;
+
+ decoder->disp_hist_repeat = 0;
+
+ if (decoder->msg_fields[0] == NEXUS_TCODE_RepeatBranch) {
+ /* Special handling for repeat branch (which only
+ * has 1 field!)
+ */
+ /* Counter set in RepeatBranch message */
+ cnt = decoder->msg_fields[1];
+ decoder->msg_field_pos = decoder->saved_fields[0];
+ decoder->msg_field_cnt = decoder->saved_fields[1];
+ decoder->msg_fields[0] = decoder->saved_fields[2];
+ decoder->msg_fields[1] = decoder->saved_fields[3];
+ decoder->msg_fields[2] = decoder->saved_fields[4];
+ decoder->msg_fields[3] = decoder->saved_fields[5];
+ decoder->msg_fields[4] = decoder->saved_fields[6];
+ decoder->disp_hist_repeat = cnt;
+ }
+
+ while (cnt > 0) { /* Handle (1 or many times ...) */
+ int err = nexus_rv_msg_handle(decoder);
+
+ if (err < 0)
+ return err;
+ cnt--;
+ }
+
+ fld_def = -1;
+ } else {
+ fld_def++;
+ }
+ fld_bits = 0;
+ fld_val = 0;
+ continue;
+ }
+
+ if (fld_bits > 0) {
+ pr_err("Decode: Not enough bits for non-variable field\n");
+ find_next_package = true;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+struct nexus_rv_insn_decoder *nexus_rv_insn_decoder_new(struct nexus_rv_insn_decoder_params *params)
+{
+ int err;
+ struct nexus_rv_insn_decoder *decoder;
+
+ if (!params)
+ return NULL;
+
+ decoder = zalloc(sizeof(struct nexus_rv_insn_decoder));
+ if (!decoder)
+ return NULL;
+
+ decoder->mem_access = params->mem_access;
+ decoder->data = params->data;
+ decoder->formatted = params->formatted;
+ decoder->src_bits = params->src_bits;
+ decoder->current_src = -1;
+
+ decoder->src_contexts = intlist__new(NULL);
+ if (!decoder->src_contexts)
+ goto err_out;
+
+ err = nexus_rv_init_packet_buffer(&decoder->packet_buffer);
+ if (err)
+ goto err_out;
+
+ return decoder;
+
+err_out:
+ nexus_rv_free_src_contexts(decoder->src_contexts);
+ nexus_rv_free_packet_buffer(&decoder->packet_buffer);
+ free(decoder);
+ return NULL;
+}
+
+void nexus_rv_insn_decoder_free(struct nexus_rv_insn_decoder *decoder)
+{
+ for (int i = 0; i < MAX_ID; ++i)
+ free(decoder->defmt_bufs[i].buf);
+
+ nexus_rv_free_src_contexts(decoder->src_contexts);
+ nexus_rv_free_packet_buffer(&decoder->packet_buffer);
+ free(decoder);
+}
+
+int nexus_rv_insn_decoder_reset(struct nexus_rv_insn_decoder *decoder)
+{
+ int err;
+ struct int_node *node;
+ struct nexus_rv_src_context *ctx;
+
+ if (decoder->src_contexts) {
+ intlist__for_each_entry(node, decoder->src_contexts) {
+ ctx = (struct nexus_rv_src_context *)node->priv;
+ ctx->nexdeco_pc = 1;
+ ctx->nexdeco_lastaddr = 1;
+ ctx->prv = 0;
+ ctx->v = 0;
+ ctx->timestamp = 0;
+ ctx->context = -1;
+ ctx->resourcefull_icnt = 0;
+ nexus_rv_init_stack(&ctx->stack);
+ }
+ }
+ decoder->current_src_ctx = NULL;
+ decoder->current_src = -1;
+
+ err = nexus_rv_init_packet_buffer(&decoder->packet_buffer);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int nexus_rv_insn_decode_data(struct nexus_rv_insn_decoder *decoder,
+ const unsigned char *buf, size_t size)
+{
+ int err;
+ char filename[PATH_MAX];
+ FILE *nexus;
+ char *dir = getenv("PERF_BUILDID_DIR");
+
+ /* TODO: The filename here is always trace.bin, which results in
+ * only the last Nexus trace data being retained. A corresponding
+ * Nexus filename should be generated for each AUX data.
+ */
+ snprintf(filename, sizeof(filename), "%s/trace.bin", dir);
+ nexus = fopen(filename, "w+");
+
+ if (decoder->formatted) {
+ err = nexus_rv_pkt_defmt(decoder->defmt_bufs, nexus, buf, size);
+ if (err) {
+ pr_err("Encoder: failed to remove coresight trace formatter\n");
+ fclose(nexus);
+ return err;
+ }
+ } else {
+ size_t n = fwrite(buf, size, 1, nexus);
+
+ if (n != 1) {
+ pr_err("Encoder: failed to write nexus data\n");
+ fclose(nexus);
+ return -EINVAL;
+ }
+ }
+
+ fseek(nexus, 0, SEEK_SET);
+ err = nexus_rv_insn_decode(decoder, nexus);
+
+ fclose(nexus);
+
+ return err;
+}
diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
new file mode 100644
index 000000000000..1595b59f3b3c
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-decoder.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(C) 2026 Spacemit Limited. All rights reserved.
+ * Author: liangzhen <zhen.liang@spacemit.com>
+ */
+
+#ifndef INCLUDE__NEXUS_RV_DECODER_H__
+#define INCLUDE__NEXUS_RV_DECODER_H__
+
+#define INFO_LINEAR 0x1 // Linear (plain instruction or not taken BRANCH)
+#define INFO_4 0x2 // If not 4, it must be 2 on RISC-V
+// 0x4 // Reserved (for exception or so ...)
+#define INFO_INDIRECT 0x8 // Possible for most types above
+#define INFO_BRANCH 0x10 // Always direct on RISC-V (may have LINEAR too)
+#define INFO_JUMP 0x20 // Direct or indirect
+#define INFO_CALL 0x40 // Direct or indirect (and always a jump)
+#define INFO_RET 0x80 // Return (always indirect and always a jump)
+
+#define MAX_ID 112 // Values of 0x00 and 0x70-0x7F are reserved by the ATB specification
+#define MSGFIELDS_MAX 10
+#define INSN_SZ 16
+
+/* check an ID is in the valid range */
+#define IS_VALID_ID(id) \
+ ((id > 0) && (id < MAX_ID))
+
+struct rvtrace_queue;
+
+struct nexus_rv_defmt_buf {
+ unsigned char *buf;
+ size_t size;
+ size_t capacity;
+};
+
+struct nexus_rv_pkt_decoder {
+ bool formatted;
+ struct nexus_rv_defmt_buf defmt_bufs[MAX_ID];
+ u32 src_bits;
+};
+
+struct nexus_rv_pkt_decoder_params {
+ bool formatted;
+ u32 src_bits;
+};
+
+struct nexus_rv_stack {
+ u64 *data;
+ int top;
+ int capacity;
+};
+
+enum rvtrace_sample_type {
+ RVTRACE_EMPTY,
+ RVTRACE_RANGE,
+ RVTRACE_LOSS,
+ RVTRACE_ERROR,
+};
+
+enum riscv_privilege_mode {
+ RISCV_PRIV_USER_MODE,
+ RISCV_PRIV_SUPERVISOR_MODE,
+ RISCV_PRIV_MACHINE_MODE = 3,
+};
+
+typedef u32 (*rvtrace_mem_access_type)(void *, u64, enum riscv_privilege_mode,
+ int, size_t, u8 *);
+
+struct nexus_rv_packet {
+ enum rvtrace_sample_type sample_type;
+ u64 start_addr;
+ u64 end_addr;
+ u32 insn_cnt;
+ int cpu;
+ enum riscv_privilege_mode prv;
+ bool v;
+ int context;
+ u64 timestamp;
+};
+
+struct nexus_rv_packet_buffer {
+ struct nexus_rv_packet *packets;
+ int size;
+ int capacity;
+};
+
+struct nexus_rv_src_context {
+ int src_id;
+ u64 nexdeco_pc;
+ u64 nexdeco_lastaddr;
+ u64 current_pc;
+ u64 timestamp;
+ enum riscv_privilege_mode prv;
+ bool v;
+ int context;
+ int resourcefull_icnt;
+ struct nexus_rv_stack stack;
+};
+
+struct nexus_rv_insn_decoder {
+ rvtrace_mem_access_type mem_access;
+ void *data;
+ bool formatted;
+ struct nexus_rv_defmt_buf defmt_bufs[MAX_ID];
+ u32 src_bits;
+ int msg_field_pos;
+ u64 msg_fields[MSGFIELDS_MAX];
+ u64 saved_fields[MSGFIELDS_MAX];
+ int msg_field_cnt;
+ int disp_hist_repeat;
+ int current_src;
+ struct intlist *src_contexts;
+ struct nexus_rv_src_context *current_src_ctx;
+ struct nexus_rv_packet_buffer packet_buffer;
+};
+
+struct nexus_rv_insn_decoder_params {
+ rvtrace_mem_access_type mem_access;
+ void *data;
+ bool formatted;
+ u32 src_bits;
+};
+
+struct nexus_rv_pkt_decoder *nexus_rv_pkt_decoder_new(struct nexus_rv_pkt_decoder_params *params);
+
+void nexus_rv_pkt_decoder_free(struct nexus_rv_pkt_decoder *decoder);
+
+int nexus_rv_pkt_desc(struct nexus_rv_pkt_decoder *decoder, const unsigned char *buf, size_t len);
+
+struct nexus_rv_insn_decoder *nexus_rv_insn_decoder_new(
+ struct nexus_rv_insn_decoder_params *params);
+
+void nexus_rv_insn_decoder_free(struct nexus_rv_insn_decoder *decoder);
+
+int nexus_rv_insn_decoder_reset(struct nexus_rv_insn_decoder *decoder);
+
+int nexus_rv_insn_decode_data(struct nexus_rv_insn_decoder *decoder,
+ const unsigned char *buf, size_t size);
+
+#endif
diff --git a/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h b/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
new file mode 100644
index 000000000000..90d95f2c5f5f
--- /dev/null
+++ b/tools/perf/util/nexus-rv-decoder/nexus-rv-msg.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 IAR Systems AB.
+ */
+
+#ifndef INCLUDE__NEXUS_RV_MSG_H__
+#define INCLUDE__NEXUS_RV_MSG_H__
+
+/****************************************************************************/
+/* Nexus RISC-V Trace header. */
+
+// Header with common Nexus Trace definitions
+
+#include <stdint.h> // For uint32_t and uint64_t
+
+/****************************************************************************/
+/* Nexus specific values (based on Nexus Standard PDF) */
+
+#define NEXUS_FLDSIZE_TCODE 6 // This is standard
+
+// Nexus TCODE values applicable to RISC-V
+#define NEXUS_TCODE_Ownership 2
+#define NEXUS_TCODE_DirectBranch 3
+#define NEXUS_TCODE_IndirectBranch 4
+#define NEXUS_TCODE_Error 8
+#define NEXUS_TCODE_ProgTraceSync 9
+#define NEXUS_TCODE_DirectBranchSync 11
+#define NEXUS_TCODE_IndirectBranchSync 12
+#define NEXUS_TCODE_ResourceFull 27
+#define NEXUS_TCODE_IndirectBranchHist 28
+#define NEXUS_TCODE_IndirectBranchHistSync 29
+#define NEXUS_TCODE_RepeatBranch 30
+#define NEXUS_TCODE_ProgTraceCorrelation 33
+
+// End of standard values
+/****************************************************************************/
+
+/****************************************************************************/
+/* RISC-V Nexus Trace related values (most 'recommended' by Nexus) */
+
+// Sizes of fields:
+#define NEXUS_FLDSIZE_BTYPE 2 // Branch type
+#define NEXUS_FLDSIZE_SYNC 4 // Synchronization code
+#define NEXUS_FLDSIZE_ETYPE 4 // Error type
+#define NEXUS_FLDSIZE_EVCODE 4 // Event code (correlation)
+#define NEXUS_FLDSIZE_CDF 2
+#define NEXUS_FLDSIZE_RCODE 4 // Resource full code size
+// from PROCESS fields:
+#define NEXUS_FLDSIZE_FORMAT 2
+#define NEXUS_FLDSIZE_PRV 2
+#define NEXUS_FLDSIZE_V 1
+
+#define NEXUS_PAR_SIZE_SRC 0 // SRC field size is defined by parameter #0
+
+#define NEXUS_HIST_BITS 31 // Number of valid HIST bits
+
+// Address skipping (on RISC-V LSB of PC is always 0, so it is not encoded)
+#define NEXUS_PARAM_AddrSkip 1
+#define NEXUS_PARAM_AddrUnit 1
+
+// End of RISC-V related values
+/****************************************************************************/
+
+// Nexus RISC-V Trace message definitions (dump & decode)
+
+// Macros to define Nexus Messages (NEXM_...)
+// NOTE: These macros refer to 'NEXUS_TCODE_...' and 'NEXUS_FLDSIZE_...'
+// It is also possible to NOT do it, but it provides less flexibility.
+//
+// name def (marker | value)
+//#define NEXM_BEG(n, t) {#n, 0x100 | (t) }
+#define NEXM_BEG(n) {#n, 0x100 | (NEXUS_TCODE_##n) } \
+ // , NEXM_FLD_PAR(SRC) // SRC is always following TCODE
+//#define NEXM_FLD(n, s) {#n, 0x200 | (s) }
+#define NEXM_FLD(n) {#n, 0x200 | (NEXUS_FLDSIZE_##n) }
+// 0x80 means size is a parameter
+#define NEXM_FLD_PAR(n) {#n, 0x200 | 0x80 | (NEXUS_PAR_SIZE_##n) }
+#define NEXM_VAR(n) {#n, 0x400 }
+#define NEXM_ADR(n) {#n, 0xC00 }
+#define NEXM_END() {NULL, 1 }
+
+// Definition of Nexus Messages (subset applicable to RISC-V PC trace)
+static struct NEXM_MSGDEF_STRU {
+ const char *name; // Name of message/field
+ int def; // Definition of field (see NEXM_... above)
+} NEXUS_MSG_DEF[] = {
+
+ NEXM_BEG(Ownership),
+ NEXM_FLD_PAR(SRC),
+ //NEXM_VAR(PROCESS),
+ NEXM_FLD(FORMAT),
+ NEXM_FLD(PRV),
+ NEXM_FLD(V),
+ NEXM_VAR(CONTEXT),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(DirectBranch),
+ NEXM_FLD_PAR(SRC),
+ NEXM_VAR(ICNT),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(IndirectBranch),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(BTYPE),
+ NEXM_VAR(ICNT),
+ NEXM_ADR(UADDR),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(Error),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(ETYPE),
+ NEXM_VAR(PAD),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(ProgTraceSync),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(SYNC),
+ NEXM_VAR(ICNT),
+ NEXM_ADR(FADDR),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(DirectBranchSync),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(SYNC),
+ NEXM_VAR(ICNT),
+ NEXM_ADR(FADDR),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(IndirectBranchSync),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(SYNC),
+ NEXM_FLD(BTYPE),
+ NEXM_VAR(ICNT),
+ NEXM_ADR(FADDR),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(ResourceFull),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(RCODE),
+ NEXM_VAR(RDATA),
+ NEXM_VAR(HREPEAT),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(IndirectBranchHist),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(BTYPE),
+ NEXM_VAR(ICNT),
+ NEXM_ADR(UADDR),
+ NEXM_VAR(HIST),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(IndirectBranchHistSync),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(SYNC),
+ NEXM_FLD(BTYPE),
+ // NEXM_FLD(CANCEL),
+ NEXM_VAR(ICNT),
+ NEXM_ADR(FADDR),
+ NEXM_VAR(HIST),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(RepeatBranch),
+ NEXM_FLD_PAR(SRC),
+ NEXM_VAR(BCNT),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ NEXM_BEG(ProgTraceCorrelation),
+ NEXM_FLD_PAR(SRC),
+ NEXM_FLD(EVCODE),
+ NEXM_FLD(CDF),
+ NEXM_VAR(ICNT),
+ NEXM_VAR(HIST), // Only if CDF=1!
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ { NULL, 0 } // End-marker ('def == 0' is not otherwise used - see NEXM_...)
+};
+
+#endif
--
2.34.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
next prev parent reply other threads:[~2026-04-14 3:44 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-14 3:41 [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Zane Leung
2026-04-14 3:41 ` [RFC PATCH 01/12] coresight: Add RISC-V support to CoreSight tracing Zane Leung
2026-04-14 3:41 ` [RFC PATCH 02/12] coresight: Initial implementation of RISC-V trace driver Zane Leung
2026-04-14 3:41 ` [RFC PATCH 03/12] coresight: Add RISC-V Trace Encoder driver Zane Leung
2026-04-14 3:41 ` [RFC PATCH 04/12] coresight: Add RISC-V Trace Funnel driver Zane Leung
2026-04-14 3:41 ` [RFC PATCH 05/12] coresight: Add RISC-V Trace ATB Bridge driver Zane Leung
2026-04-14 3:41 ` [RFC PATCH 06/12] coresight rvtrace: Add timestamp component support for encoder and funnel Zane Leung
2026-04-14 3:41 ` [RFC PATCH 07/12] coresight: Add RISC-V PMU name support Zane Leung
2026-04-14 3:41 ` [RFC PATCH 08/12] perf tools: riscv: making rvtrace PMU listable Zane Leung
2026-04-14 3:41 ` [RFC PATCH 09/12] perf tools: Add RISC-V trace PMU record capabilities Zane Leung
2026-04-14 23:31 ` Bo Gan
2026-04-14 3:41 ` Zane Leung [this message]
2026-04-14 3:41 ` [RFC PATCH 11/12] perf symbols: Add RISC-V PLT entry sizes Zane Leung
2026-04-14 3:41 ` [RFC PATCH 12/12] perf tools: Integrate RISC-V trace decoder into auxtrace Zane Leung
2026-04-14 4:15 ` [RFC PATCH 00/12] Add Linux RISC-V trace support via CoreSight Jie Gan
2026-04-14 8:08 ` Zane Leung
2026-04-14 7:23 ` Anup Patel
2026-04-14 9:04 ` Zane Leung
2026-04-15 0:10 ` Bo Gan
2026-04-15 1:23 ` Zane Leung
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=20260414034153.3272485-11-liangzhen@linux.spacemit.com \
--to=liangzhen@linux.spacemit.com \
--cc=adrian.hunter@intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=andrew.jones@oss.qualcomm.com \
--cc=anup.patel@oss.qualcomm.com \
--cc=anup@brainfault.org \
--cc=atish.patra@linux.dev \
--cc=conor+dt@kernel.org \
--cc=coresight@lists.linaro.org \
--cc=gregkh@linuxfoundation.org \
--cc=irogers@google.com \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=mark.rutland@arm.com \
--cc=mayuresh.chitale@oss.qualcomm.com \
--cc=mchitale@gmail.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=palmer@dabbelt.com \
--cc=peterz@infradead.org \
--cc=pjw@kernel.org \
--cc=robh@kernel.org \
--cc=sunilvl@oss.qualcomm.com \
--cc=zhuangqiubin@linux.spacemit.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