From: Zane Leung <liangzhen@linux.spacemit.com>
To: gregkh@linuxfoundation.org, palmer@dabbelt.com, pjw@kernel.org,
alexander.shishkin@linux.intel.com, suzuki.poulose@arm.com,
mchitale@gmail.com, anup@brainfault.org, irogers@google.com
Cc: mike.leach@arm.com, james.clark@linaro.org, leo.yan@arm.com,
peterz@infradead.org, mingo@redhat.com, namhyung@kernel.org,
mark.rutland@arm.com, jolsa@kernel.org, adrian.hunter@intel.com,
alex@ghiti.fr, atish.patra@linux.dev,
andrew.jones@oss.qualcomm.com, sunilvl@oss.qualcomm.com,
liangzhen@linux.spacemit.com, zhuangqiubin@linux.spacemit.com,
coresight@lists.linaro.org, linux-riscv@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 22/24] perf tools: Add Nexus RISC-V Trace decoder
Date: Fri, 23 Oct 2026 11:15:03 +0800 [thread overview]
Message-ID: <20261023031505.2020925-23-liangzhen@linux.spacemit.com> (raw)
In-Reply-To: <20261023031505.2020925-1-liangzhen@linux.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: Zane Leung <liangzhen@linux.spacemit.com>
---
tools/arch/riscv/include/asm/insn.h | 645 +++++++++++++
tools/perf/util/Build | 1 +
tools/perf/util/rvtrace-decoder/Build | 2 +
.../util/rvtrace-decoder/rvtrace-decoder.c | 423 ++++++++
.../util/rvtrace-decoder/rvtrace-decoder.h | 122 +++
.../rvtrace-decoder/rvtrace-ntrace-decoder.c | 910 ++++++++++++++++++
.../rvtrace-decoder/rvtrace-ntrace-decoder.h | 124 +++
7 files changed, 2227 insertions(+)
create mode 100644 tools/arch/riscv/include/asm/insn.h
create mode 100644 tools/perf/util/rvtrace-decoder/Build
create mode 100644 tools/perf/util/rvtrace-decoder/rvtrace-decoder.c
create mode 100644 tools/perf/util/rvtrace-decoder/rvtrace-decoder.h
create mode 100644 tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.c
create mode 100644 tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.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/util/Build b/tools/perf/util/Build
index d25140a0d64f..342b4f75457b 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -150,6 +150,7 @@ perf-util-y += cs-etm-decoder/
endif
perf-util-y += cs-etm-base.o
perf-util-y += gtrace-decoder.o
+perf-util-y += rvtrace-decoder/
perf-util-y += parse-branch-options.o
perf-util-y += parse-regs-options.o
diff --git a/tools/perf/util/rvtrace-decoder/Build b/tools/perf/util/rvtrace-decoder/Build
new file mode 100644
index 000000000000..dc447eec372a
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder/Build
@@ -0,0 +1,2 @@
+perf-util-y += rvtrace-decoder.o
+perf-util-y += rvtrace-ntrace-decoder.o
diff --git a/tools/perf/util/rvtrace-decoder/rvtrace-decoder.c b/tools/perf/util/rvtrace-decoder/rvtrace-decoder.c
new file mode 100644
index 000000000000..e4b54fe8f51a
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder/rvtrace-decoder.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 IAR Systems AB.
+ * Copyright (c) 2026 Spacemit Technologies, Inc.
+ */
+
+#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 "../intlist.h"
+#include "rvtrace-decoder.h"
+
+#define BUFFER_SIZE 1024
+
+/*
+ * Shared by the per-source staging buffer used during CoreSight frame
+ * de-formatting and by the contiguous de-formatted output.
+ */
+static int rvtrace_buf_append(struct rvtrace_defmt_buf *b, const void *src, size_t len)
+{
+ if (b->size + len > b->capacity) {
+ size_t new_cap = b->capacity ? b->capacity : BUFFER_SIZE;
+ unsigned char *p;
+
+ while (new_cap < b->size + len)
+ new_cap *= 2;
+
+ p = realloc(b->buf, new_cap);
+ if (!p)
+ return -ENOMEM;
+ b->buf = p;
+ b->capacity = new_cap;
+ }
+ memcpy(b->buf + b->size, src, len);
+ b->size += len;
+ return 0;
+}
+
+static struct rvtrace_defmt_buf *rvtrace_get_buf(struct rvtrace_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 rvtrace_defmt_buf_append(struct rvtrace_defmt_buf *defmt_buf,
+ struct rvtrace_defmt_buf *out, unsigned char data)
+{
+ int err;
+
+ err = rvtrace_buf_append(defmt_buf, &data, 1);
+ if (err)
+ return err;
+
+ /* A Nexus message ends when the MSEO bits of the last byte are '11'. */
+ if ((data & 0x3) == 0x3) {
+ err = rvtrace_buf_append(out, defmt_buf->buf, defmt_buf->size);
+ if (err) {
+ pr_err("Encoder: failed to write nexus data\n");
+ return err;
+ }
+ 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 rvtrace_data_defmt(struct rvtrace_defmt_buf defmt_bufs[],
+ struct rvtrace_defmt_buf *out,
+ 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 rvtrace_defmt_buf *defmt_buf =
+ rvtrace_get_buf(defmt_bufs, cur_id);
+
+ if (!defmt_buf)
+ return -ENOMEM;
+
+ /* it's data */
+ if ((buf[i] & 0x1) == 0) {
+ err = rvtrace_defmt_buf_append(defmt_buf, out, data);
+ if (err)
+ return err;
+ }
+
+ /* buf[i+1] is alway data when i != 14 */
+ if (i != 14) {
+ err = rvtrace_defmt_buf_append(defmt_buf, out,
+ 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;
+}
+
+/*
+ * Common processing for both dump and decode: de-format the raw AUX buffer
+ * into a contiguous Nexus byte stream (in memory), then dispatch to the
+ * format specific parser based on 'type'.
+ */
+static int rvtrace_decode(struct rvtrace_decoder *decoder,
+ enum rvtrace_decode_type type,
+ const unsigned char *buf, size_t len)
+{
+ int err;
+ const unsigned char *data = buf;
+ size_t size = len;
+ struct rvtrace_defmt_buf out = { 0 };
+
+ if (decoder->coresight_formatted) {
+ err = rvtrace_data_defmt(decoder->defmt_bufs, &out, buf, len);
+ if (err) {
+ pr_err("Encoder: failed to remove coresight formatter frame\n");
+ free(out.buf);
+ return err;
+ }
+ data = out.buf;
+ size = out.size;
+ }
+
+ switch (decoder->format) {
+ case GTRACE_FORMAT_NTRACE:
+ err = rvtrace_ntrace_decode(decoder, type, data, size);
+ break;
+ case GTRACE_FORMAT_ETRACE:
+ default:
+ err = -EINVAL;
+ pr_err("unsupported trace format %d\n", decoder->format);
+ break;
+ }
+
+ free(out.buf);
+ return err;
+}
+
+static int rvtrace_init_packet_buffer(struct rvtrace_packet_buffer *packet_buffer)
+{
+ if (!packet_buffer->packets) {
+ packet_buffer->packets = calloc(BUFFER_SIZE, sizeof(struct rvtrace_packet));
+ if (!packet_buffer->packets)
+ return -ENOMEM;
+ }
+
+ packet_buffer->size = 0;
+ packet_buffer->capacity = BUFFER_SIZE;
+
+ return 0;
+}
+
+static void rvtrace_free_packet_buffer(struct rvtrace_packet_buffer *packet_buffer)
+{
+ free(packet_buffer->packets);
+}
+
+int rvtrace_packet_buffer_append(struct rvtrace_packet_buffer *packet_buffer,
+ struct rvtrace_packet packet)
+{
+ if (packet_buffer->size == packet_buffer->capacity) {
+ packet_buffer->capacity *= 2;
+ packet_buffer->packets = realloc(packet_buffer->packets,
+ sizeof(struct rvtrace_packet) * packet_buffer->capacity);
+ if (!packet_buffer->packets)
+ return -ENOMEM;
+ }
+ packet_buffer->packets[packet_buffer->size++] = packet;
+
+ return 0;
+}
+
+int rvtrace_init_stack(struct rvtrace_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;
+}
+
+int rvtrace_stack_push(struct rvtrace_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;
+}
+
+int rvtrace_stack_pop(struct rvtrace_stack *stack)
+{
+ u64 value;
+
+ if (stack->top == -1)
+ return 1; /* Empty */
+
+ value = stack->data[stack->top--];
+ return value;
+}
+
+static void rvtrace_free_stack(struct rvtrace_stack *stack)
+{
+ free(stack->data);
+}
+
+struct rvtrace_src_context *rvtrace_get_src_context(struct rvtrace_decoder *decoder, int srcid)
+{
+ struct int_node *node;
+ struct rvtrace_src_context *ctx;
+
+ node = intlist__find(decoder->src_contexts, srcid);
+ if (node)
+ return (struct rvtrace_src_context *)node->priv;
+
+ ctx = zalloc(sizeof(struct rvtrace_src_context));
+ if (!ctx)
+ return NULL;
+
+ ctx->srcid = srcid;
+ 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 (rvtrace_init_stack(&ctx->stack)) {
+ free(ctx);
+ return NULL;
+ }
+
+ /* Add to RB Tree */
+ node = intlist__findnew(decoder->src_contexts, srcid);
+ if (!node) {
+ rvtrace_free_stack(&ctx->stack);
+ free(ctx);
+ return NULL;
+ }
+
+ node->priv = ctx;
+
+ return ctx;
+}
+
+static void rvtrace_free_src_contexts(struct intlist *src_contexts)
+{
+ struct int_node *node;
+
+ intlist__for_each_entry(node, src_contexts) {
+ struct rvtrace_src_context *ctx = (struct rvtrace_src_context *)node->priv;
+
+ rvtrace_free_stack(&ctx->stack);
+ free(ctx);
+ }
+ intlist__delete(src_contexts);
+}
+
+struct rvtrace_decoder *rvtrace_decoder_new(struct rvtrace_decoder_params *params)
+{
+ int err;
+ struct rvtrace_decoder *decoder;
+
+ if (!params)
+ return NULL;
+
+ decoder = zalloc(sizeof(struct rvtrace_decoder));
+ if (!decoder)
+ return NULL;
+
+ decoder->data = params->data;
+ decoder->coresight_formatted = params->coresight_formatted;
+ decoder->format = params->format;
+ decoder->srcbits = params->srcbits;
+ decoder->mem_access = params->mem_access;
+ decoder->decode = rvtrace_decode;
+ decoder->current_src = -1;
+ decoder->src_contexts = intlist__new(NULL);
+ if (!decoder->src_contexts)
+ goto err_out_free_decoder;
+
+ err = rvtrace_init_packet_buffer(&decoder->packet_buffer);
+ if (err)
+ goto err_out_free_context;
+
+ return decoder;
+
+err_out_free_context:
+ rvtrace_free_src_contexts(decoder->src_contexts);
+err_out_free_decoder:
+ zfree(&decoder);
+ return NULL;
+}
+
+void rvtrace_decoder_free(struct rvtrace_decoder *decoder)
+{
+ for (int i = 0; i < MAX_ID; ++i)
+ free(decoder->defmt_bufs[i].buf);
+
+ rvtrace_free_packet_buffer(&decoder->packet_buffer);
+ rvtrace_free_src_contexts(decoder->src_contexts);
+ zfree(&decoder);
+}
+
+int rvtrace_decoder_reset(struct rvtrace_decoder *decoder)
+{
+ int err;
+ struct int_node *node;
+ struct rvtrace_src_context *ctx;
+
+ if (decoder->src_contexts) {
+ intlist__for_each_entry(node, decoder->src_contexts) {
+ ctx = (struct rvtrace_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;
+ rvtrace_init_stack(&ctx->stack);
+ }
+ }
+ decoder->current_src_ctx = NULL;
+ decoder->current_src = -1;
+
+ err = rvtrace_init_packet_buffer(&decoder->packet_buffer);
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/tools/perf/util/rvtrace-decoder/rvtrace-decoder.h b/tools/perf/util/rvtrace-decoder/rvtrace-decoder.h
new file mode 100644
index 000000000000..27c27d5fc3b8
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder/rvtrace-decoder.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026 Spacemit Technologies, Inc.
+ */
+
+#ifndef INCLUDE__RVTRACE_DECODER_H__
+#define INCLUDE__RVTRACE_DECODER_H__
+
+#include "../gtrace.h"
+#include "rvtrace-ntrace-decoder.h"
+
+#define MAX_ID 112
+
+/* check an ID is in the valid range */
+#define IS_VALID_ID(id) \
+ ((id > 0) && (id < MAX_ID))
+
+struct rvtrace_defmt_buf {
+ unsigned char *buf;
+ size_t size;
+ size_t capacity;
+};
+
+struct rvtrace_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 rvtrace_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 rvtrace_packet_buffer {
+ struct rvtrace_packet *packets;
+ int size;
+ int capacity;
+};
+
+struct rvtrace_src_context {
+ int srcid;
+ u64 nexdeco_pc;
+ u64 nexdeco_lastaddr;
+ u64 current_pc;
+ u64 timestamp;
+ enum riscv_privilege_mode prv;
+ bool v;
+ int context;
+ int resourcefull_icnt;
+ struct rvtrace_stack stack;
+};
+
+struct rvtrace_decoder_params {
+ void *data;
+ bool coresight_formatted;
+ enum gtrace_format format;
+ u32 srcbits;
+ rvtrace_mem_access_type mem_access;
+};
+
+/* What the decoder should do with the de-formatted trace stream. */
+enum rvtrace_decode_type {
+ RVTRACE_DECODE_MSG, /* print the Nexus messages */
+ RVTRACE_DECODE_INSN, /* reconstruct instruction trace packets */
+};
+
+struct rvtrace_decoder {
+ void *data;
+ bool coresight_formatted;
+ enum gtrace_format format;
+ u32 srcbits;
+ rvtrace_mem_access_type mem_access;
+ int (*decode)(struct rvtrace_decoder *decoder, enum rvtrace_decode_type type,
+ const unsigned char *buf, size_t size);
+ struct rvtrace_defmt_buf defmt_bufs[MAX_ID];
+ int current_src;
+ struct intlist *src_contexts;
+ struct rvtrace_src_context *current_src_ctx;
+ struct rvtrace_packet_buffer packet_buffer;
+ struct rvtrace_ntrace_msg msg; /* most recently parsed message */
+ struct rvtrace_ntrace_msg saved_msg; /* previous message, for RepeatBranch replay */
+};
+
+int rvtrace_packet_buffer_append(struct rvtrace_packet_buffer *packet_buffer,
+ struct rvtrace_packet packet);
+int rvtrace_init_stack(struct rvtrace_stack *stack);
+int rvtrace_stack_push(struct rvtrace_stack *stack, u64 value);
+int rvtrace_stack_pop(struct rvtrace_stack *stack);
+struct rvtrace_src_context *
+rvtrace_get_src_context(struct rvtrace_decoder *decoder, int srcid);
+struct rvtrace_decoder *rvtrace_decoder_new(struct rvtrace_decoder_params *params);
+void rvtrace_decoder_free(struct rvtrace_decoder *decoder);
+int rvtrace_decoder_reset(struct rvtrace_decoder *decoder);
+
+int rvtrace_ntrace_decode(struct rvtrace_decoder *decoder, enum rvtrace_decode_type type,
+ const unsigned char *buf, size_t size);
+
+#endif
diff --git a/tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.c b/tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.c
new file mode 100644
index 000000000000..f72e5baa323e
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.c
@@ -0,0 +1,910 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 IAR Systems AB.
+ * Copyright (c) 2026 Spacemit Technologies, Inc.
+ */
+
+#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 "rvtrace-decoder.h"
+#include "rvtrace-ntrace-decoder.h"
+#include "../../../arch/riscv/include/asm/insn.h"
+
+#define RV_REGNO_ZERO 0
+#define RV_REGNO_RA 1
+
+/* Definition of Nexus Messages (subset applicable to RISC-V PC trace). */
+const struct rvtrace_ntrace_msg_field_def rvtrace_ntrace_msg_defs[] = {
+ NEXM_BEG(Ownership),
+ NEXM_FLD_PAR(SRC),
+ 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_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),
+ NEXM_VAR(TSTAMP),
+ NEXM_END(),
+
+ { NULL, 0 } /* end marker */
+};
+
+/*
+ * Look up the rvtrace_ntrace_msg_defs entry index of a message header whose low 8 bits
+ * match 'tcode'. Returns the index, or -1 if the TCODE is not defined.
+ */
+static int rvtrace_ntrace_find_tcode(unsigned int tcode)
+{
+ for (int d = 0; rvtrace_ntrace_msg_defs[d].def != 0; d++) {
+ if ((rvtrace_ntrace_msg_defs[d].def & NEXM_DEF_MSG) == 0)
+ continue;
+ if ((rvtrace_ntrace_msg_defs[d].def & 0xff) == tcode)
+ return d;
+ }
+ return -1;
+}
+
+/*
+ * Return the bit width of the fixed-size field at rvtrace_ntrace_msg_defs[def]. Fields
+ * marked NEXM_DEF_PARAM take their width from the decoder's srcbits.
+ */
+static int rvtrace_ntrace_field_size(struct rvtrace_decoder *decoder, int def)
+{
+ int size = rvtrace_ntrace_msg_defs[def].def & 0xff;
+
+ if (rvtrace_ntrace_msg_defs[def].def & NEXM_DEF_PARAM)
+ size = decoder->srcbits;
+ return size;
+}
+
+/*
+ * Feed the byte stream one byte at a time. The parser accumulates
+ * MDO/MSEO bits into 'msg' and returns:
+ *
+ * 1 a complete message has been parsed into *msg
+ * 0 more bytes are needed (or an idle/partial byte was consumed)
+ */
+static int rvtrace_ntrace_msg_parse(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_msg_parser *p,
+ struct rvtrace_ntrace_msg *msg,
+ unsigned char byte, unsigned char prev_byte)
+{
+ unsigned int mdo = byte >> 2;
+ unsigned int mseo = byte & 0x3;
+
+ pr_debug3("0x%02x ", byte);
+ for (int b = 0x80; b != 0; b >>= 1) {
+ if (b == 0x2)
+ pr_debug3("_");
+ pr_debug3("%d", !!(byte & b));
+ }
+ pr_debug3("\n");
+
+ if (p->resync) {
+ if ((prev_byte & 0x3) == 0x3) { /* MSEO==3: message end */
+ p->resync = false;
+ p->field_def = -1;
+ p->field_bits = 0;
+ p->field_val = 0;
+ }
+ return 0;
+ }
+
+ /* Skip long runs of idle bytes (0xFF). */
+ if (byte == 0xFF && prev_byte == 0xFF)
+ return 0;
+
+ if (mseo == 0x2) {
+ pr_err("ERROR: MSEO='10' is not allowed\n");
+ p->resync = true;
+ return 0;
+ }
+
+ /* Message header: the first byte's MDO is the TCODE. */
+ if (p->field_def < 0) {
+ if (mseo == 0x3)
+ return 0; /* skip idle */
+
+ if (mseo != 0x0) {
+ pr_err("ERROR: Message must start from MSEO='00'\n");
+ p->resync = true;
+ return 0;
+ }
+
+ p->field_def = rvtrace_ntrace_find_tcode(mdo);
+ if (p->field_def < 0) {
+ pr_err("ERROR: Message with TCODE=%d is not defined for RISC-V\n", mdo);
+ p->resync = true;
+ return 0;
+ }
+
+ msg->field_pos = p->field_def;
+ msg->tcode = mdo;
+ msg->nfields = 0;
+ msg->fields[msg->nfields++] = mdo;
+
+ p->field_def++;
+ p->field_bits = 0;
+ p->field_val = 0;
+ return 0;
+ }
+
+ /* Accumulate this byte's MDO into the current field value. */
+ p->field_val |= (u64)mdo << p->field_bits;
+ p->field_bits += 6;
+
+ /* Emit as many complete fixed-size fields as we now have bits for. */
+ while ((rvtrace_ntrace_msg_defs[p->field_def].def & NEXM_DEF_FLD) &&
+ p->field_bits >= rvtrace_ntrace_field_size(decoder, p->field_def)) {
+ int width = rvtrace_ntrace_field_size(decoder, p->field_def);
+
+ msg->fields[msg->nfields++] = p->field_val & ((1ULL << width) - 1);
+ p->field_def++;
+ p->field_val >>= width;
+ p->field_bits -= width;
+ }
+
+ if (mseo == 0x0)
+ return 0;
+
+ if (rvtrace_ntrace_msg_defs[p->field_def].def & NEXM_DEF_VAR) {
+ /* Variable-size field: the whole remaining value. */
+ msg->fields[msg->nfields++] = p->field_val;
+
+ if (rvtrace_ntrace_msg_defs[p->field_def].def & NEXM_DEF_ADR)
+ msg->addr_bits = p->field_bits;
+
+ if (mseo == 3) {
+ p->field_def = -1;
+ p->field_bits = 0;
+ p->field_val = 0;
+ return 1; /* complete message */
+ }
+ p->field_def++;
+ p->field_bits = 0;
+ p->field_val = 0;
+ return 0;
+ }
+
+ if (p->field_bits > 0) {
+ pr_err("Decode: Not enough bits for non-variable field\n");
+ p->resync = true;
+ }
+ return 0;
+}
+
+static int rvtrace_ntrace_msg_sprint(char *buf, size_t size,
+ struct rvtrace_ntrace_msg *msg, int msgs, int bytes)
+{
+ int len;
+
+ len = snprintf(buf, size, "MSG #%d +%d - %s TCODE[6]=%d",
+ msgs, bytes,
+ rvtrace_ntrace_msg_defs[msg->field_pos].name, msg->tcode);
+
+ for (int i = 1; i < (int)msg->nfields && len < (int)size; i++)
+ len += snprintf(buf + len, size - len, " %s=0x%llx",
+ rvtrace_ntrace_msg_defs[msg->field_pos + i].name,
+ (unsigned long long)msg->fields[i]);
+
+ return len;
+}
+
+static int handle_error_msg(struct rvtrace_src_context *ctx, const char *err)
+{
+ pr_err("\nERROR: %s\n", err);
+ ctx->nexdeco_pc = 1; /* 1 means, that last address is unknown */
+ rvtrace_init_stack(&ctx->stack);
+ return -EINVAL;
+}
+
+static int rvtrace_ntrace_insn_info_get(struct rvtrace_decoder *decoder, u8 *info, u64 *dest)
+{
+ u32 insn;
+ struct rvtrace_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 rvtrace_ntrace_emit_icnt(struct rvtrace_decoder *decoder, int n, u32 hist)
+{
+ u64 a = 0;
+ u32 hist_mask;
+ u8 info;
+ int done_icnt = 0; /* Number of done instruction count */
+ struct rvtrace_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 (rvtrace_ntrace_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 (rvtrace_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 = rvtrace_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 rvtrace_ntrace_field_get(struct rvtrace_decoder *decoder, const char *name)
+{
+ struct rvtrace_ntrace_msg *msg = &decoder->msg;
+
+ for (int d = msg->field_pos; rvtrace_ntrace_msg_defs[d].def != 1; d++) {
+ if (strcmp(rvtrace_ntrace_msg_defs[d].name, name) == 0) {
+ int fi = d - msg->field_pos;
+
+ if (fi < (int)msg->nfields)
+ return msg->fields[fi];
+ return 0;
+ }
+ }
+ return 0;
+}
+
+#define NEX_FLDGET(n) rvtrace_ntrace_field_get(decoder, #n)
+
+/*
+ * Switch the active source context to 'new_src', allocating it on first use.
+ * Returns 0 on success, negative on error.
+ */
+static int rvtrace_ntrace_switch_src(struct rvtrace_decoder *decoder, int new_src)
+{
+ if (new_src == decoder->current_src)
+ return 0;
+
+ decoder->current_src_ctx = rvtrace_get_src_context(decoder, new_src);
+ if (!decoder->current_src_ctx)
+ return -ENOMEM;
+
+ decoder->current_src = new_src;
+ return 0;
+}
+
+/*
+ * Fill 'packet' from the active source context and append it to the decoder's
+ * packet buffer. 'ret' is the instruction count produced by emit_icnt.
+ */
+static int rvtrace_ntrace_emit_packet(struct rvtrace_decoder *decoder, int tcode,
+ u64 start_addr, int ret, int src)
+{
+ struct rvtrace_packet packet;
+
+ if (ret >= 0) {
+ packet.sample_type = (tcode == NEXUS_TCODE_Error) ?
+ RVTRACE_LOSS :
+ (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 = 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;
+ }
+
+ return rvtrace_packet_buffer_append(&decoder->packet_buffer, packet);
+}
+
+/* Reconstruct an address from the transmitted F-ADDR/U-ADDR field */
+static u64 rvtrace_ntrace_calculate_addr(struct rvtrace_decoder *decoder,
+ u64 fu_addr, int full, u64 prev_addr)
+{
+ u32 bits = decoder->msg.addr_bits;
+
+ fu_addr <<= 1; /* LSB bit is never sent */
+
+ /*
+ * After shifting the LSB back in, the most significant transmitted bit
+ * sits at position 'bits'. Extend it (0 or 1) all the way to bit 63.
+ * This is sign-extension in effect, but the spec notes it is NOT a sign
+ * bit -- it is simply the value of the skipped identical bits.
+ */
+ if (bits && bits < 64 && (fu_addr & (1ULL << bits)))
+ fu_addr |= ~0ULL << bits;
+
+ /* Update (NEW or XOR) */
+ if (!full) {
+ if (prev_addr & 1) /* Not Sync */
+ fu_addr = prev_addr;
+ else
+ fu_addr ^= prev_addr;
+ }
+ return fu_addr;
+}
+
+/*
+ * Per-message handler result. Each handler fills these and the dispatcher
+ * emits a single packet from them.
+ */
+struct rvtrace_ntrace_result {
+ int icnt; /* instruction count from emit_icnt() */
+ u64 start_addr;
+ int src;
+};
+
+/* Common prologue: switch to the message's source context. */
+static int rvtrace_ntrace_handler_prologue(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ res->src = NEX_FLDGET(SRC);
+ return rvtrace_ntrace_switch_src(decoder, res->src);
+}
+
+/*
+ * Common F-ADDR/U-ADDR handling: reconstruct the address and make it the
+ * current PC. 'full' selects full (sync) vs XOR (compressed) update.
+ */
+static void rvtrace_ntrace_update_addr(struct rvtrace_decoder *decoder, u64 addr, int full)
+{
+ struct rvtrace_src_context *ctx = decoder->current_src_ctx;
+
+ ctx->nexdeco_lastaddr = rvtrace_ntrace_calculate_addr(decoder, addr, full,
+ ctx->nexdeco_lastaddr);
+ pr_debug2(".nexdeco_lastaddr=0x%lx\n", ctx->nexdeco_lastaddr);
+ ctx->nexdeco_pc = ctx->nexdeco_lastaddr;
+}
+
+static int rvtrace_ntrace_handle_ownership(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ 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);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_direct_branch(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), 0x0);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_indirect_branch(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), 0x0);
+
+ rvtrace_ntrace_update_addr(decoder, NEX_FLDGET(UADDR), 0);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_prog_trace_sync(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), 0x0);
+
+ rvtrace_ntrace_update_addr(decoder, NEX_FLDGET(FADDR), 1);
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_direct_branch_sync(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), 0x0);
+
+ rvtrace_ntrace_update_addr(decoder, NEX_FLDGET(FADDR), 1);
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_indirect_branch_sync(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), 0x0);
+
+ rvtrace_ntrace_update_addr(decoder, NEX_FLDGET(FADDR), 1);
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_indirect_branch_hist(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), NEX_FLDGET(HIST));
+
+ rvtrace_ntrace_update_addr(decoder, NEX_FLDGET(UADDR), 0);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_indirect_branch_hist_sync(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), NEX_FLDGET(HIST));
+
+ rvtrace_ntrace_update_addr(decoder, NEX_FLDGET(FADDR), 1);
+ decoder->current_src_ctx->timestamp = NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_resource_full(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+ int rcode = NEX_FLDGET(RCODE);
+ int hrepeat = 0;
+
+ if (ret)
+ return ret;
+
+ if (rcode == 2)
+ hrepeat = NEX_FLDGET(HREPEAT);
+
+ res->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) {
+ do {
+ /* ICNT is unknown (-1), what will process only HIST bits */
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, -1, rdata);
+ if (res->icnt < 0)
+ break;
+
+ /* Consume, so next time ICNT will be adjusted */
+ decoder->current_src_ctx->resourcefull_icnt -= res->icnt;
+ hrepeat--;
+ } while (hrepeat > 0);
+ }
+ } else if (rcode == 0) {
+ decoder->current_src_ctx->resourcefull_icnt += NEX_FLDGET(RDATA);
+ }
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_prog_trace_correlation(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+ int hist = 0;
+
+ if (ret)
+ return ret;
+
+ if (NEX_FLDGET(CDF) == 1)
+ hist = NEX_FLDGET(HIST);
+
+ res->start_addr = decoder->current_src_ctx->nexdeco_pc;
+ res->icnt = rvtrace_ntrace_emit_icnt(decoder, NEX_FLDGET(ICNT), hist);
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_handle_error(struct rvtrace_decoder *decoder,
+ struct rvtrace_ntrace_result *res)
+{
+ int ret = rvtrace_ntrace_handler_prologue(decoder, res);
+
+ if (ret)
+ return ret;
+
+ decoder->current_src_ctx->timestamp += NEX_FLDGET(TSTAMP);
+ return 0;
+}
+
+static int rvtrace_ntrace_msg_handle(struct rvtrace_decoder *decoder)
+{
+ struct rvtrace_ntrace_result res = { 0 };
+ int tcode = decoder->msg.tcode;
+ int err;
+
+ switch (tcode) {
+ case NEXUS_TCODE_Ownership:
+ err = rvtrace_ntrace_handle_ownership(decoder, &res);
+ break;
+ case NEXUS_TCODE_DirectBranch:
+ err = rvtrace_ntrace_handle_direct_branch(decoder, &res);
+ break;
+ case NEXUS_TCODE_IndirectBranch:
+ err = rvtrace_ntrace_handle_indirect_branch(decoder, &res);
+ break;
+ case NEXUS_TCODE_ProgTraceSync:
+ err = rvtrace_ntrace_handle_prog_trace_sync(decoder, &res);
+ break;
+ case NEXUS_TCODE_DirectBranchSync:
+ err = rvtrace_ntrace_handle_direct_branch_sync(decoder, &res);
+ break;
+ case NEXUS_TCODE_IndirectBranchSync:
+ err = rvtrace_ntrace_handle_indirect_branch_sync(decoder, &res);
+ break;
+ case NEXUS_TCODE_IndirectBranchHist:
+ err = rvtrace_ntrace_handle_indirect_branch_hist(decoder, &res);
+ break;
+ case NEXUS_TCODE_IndirectBranchHistSync:
+ err = rvtrace_ntrace_handle_indirect_branch_hist_sync(decoder, &res);
+ break;
+ case NEXUS_TCODE_ResourceFull:
+ err = rvtrace_ntrace_handle_resource_full(decoder, &res);
+ break;
+ case NEXUS_TCODE_ProgTraceCorrelation:
+ err = rvtrace_ntrace_handle_prog_trace_correlation(decoder, &res);
+ break;
+ case NEXUS_TCODE_Error:
+ err = rvtrace_ntrace_handle_error(decoder, &res);
+ break;
+ case NEXUS_TCODE_RepeatBranch: /* Handled differently! */
+ default:
+ return -EINVAL;
+ }
+
+ if (err)
+ return err;
+
+ return rvtrace_ntrace_emit_packet(decoder, tcode, res.start_addr, res.icnt, res.src);
+}
+
+/*
+ * Decode the N-Trace byte stream.
+ *
+ * RVTRACE_DECODE_MSG - print the messages
+ * RVTRACE_DECODE_INSN - reconstruct instruction flow
+ */
+int rvtrace_ntrace_decode(struct rvtrace_decoder *decoder, enum rvtrace_decode_type type,
+ const unsigned char *buf, size_t size)
+{
+ const char *color = PERF_COLOR_BLUE;
+ struct rvtrace_ntrace_msg_parser p = { .field_def = -1 };
+ struct rvtrace_ntrace_msg *msg = &decoder->msg;
+ struct {
+ int msgs;
+ int bytes;
+ int idles;
+ int errors;
+ int invalid;
+ } stat = { 0 };
+ unsigned char prev_byte = 0;
+ bool was_resync = false;
+ size_t pos;
+ int err;
+
+ msg->nfields = 0;
+
+ for (pos = 0; pos < size; pos++) {
+ unsigned char byte = buf[pos];
+ char line[256];
+ int done = rvtrace_ntrace_msg_parse(decoder, &p, msg, byte, prev_byte);
+ int cnt = 1;
+
+ prev_byte = byte;
+ stat.bytes++;
+
+ if (p.resync && !was_resync)
+ stat.invalid++;
+ was_resync = p.resync;
+
+ if (done <= 0)
+ continue;
+
+ stat.msgs++;
+ if (msg->tcode == NEXUS_TCODE_Error)
+ stat.errors++;
+
+ rvtrace_ntrace_msg_sprint(line, sizeof(line), msg, stat.msgs, stat.bytes);
+ if (type == RVTRACE_DECODE_MSG) {
+ color_fprintf(stdout, color, "%s\n", line);
+ continue;
+ }
+ pr_debug2("%s\n", line);
+
+ if (msg->tcode == NEXUS_TCODE_RepeatBranch) {
+ cnt = msg->fields[1];
+ *msg = decoder->saved_msg;
+ } else {
+ /* Save previous message fields */
+ decoder->saved_msg = *msg;
+ }
+
+ while (cnt-- > 0) {
+ err = rvtrace_ntrace_msg_handle(decoder);
+ if (err)
+ return err;
+ }
+ }
+
+ if (type == RVTRACE_DECODE_MSG) {
+ color_fprintf(stdout, color,
+ "\nN-Trace: %d bytes, %d messages, %d error messages, %d invalid messages",
+ stat.bytes, stat.msgs, stat.errors, stat.invalid);
+ } else {
+ pr_debug2("\nN-Trace: %d bytes, %d messages, %d errors, %d invalid\n",
+ stat.bytes, stat.msgs, stat.errors, stat.invalid);
+ }
+
+ return 0;
+}
+
diff --git a/tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.h b/tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.h
new file mode 100644
index 000000000000..4575e631ba5e
--- /dev/null
+++ b/tools/perf/util/rvtrace-decoder/rvtrace-ntrace-decoder.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 IAR Systems AB.
+ * Copyright (c) 2026 Spacemit Technologies, Inc.
+ */
+
+#ifndef INCLUDE__RVTRACE_NTRACE_DECODER_H__
+#define INCLUDE__RVTRACE_NTRACE_DECODER_H__
+
+#include <stdint.h> // For uint32_t and uint64_t
+#include <stdbool.h>
+#include <linux/types.h>
+
+/****************************************************************************/
+/* Instruction information */
+#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)
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* 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
+/****************************************************************************/
+
+/*
+ * Field definition flags packed into rvtrace_ntrace_msg_field_def.def. The
+ * low 8 bits of a fixed field carry its bit width; bit 7 marks a
+ * runtime-sized (parameter) field such as SRC.
+ */
+#define NEXM_DEF_MSG 0x100 /* message header: low bits are the TCODE */
+#define NEXM_DEF_FLD 0x200 /* fixed-size field */
+#define NEXM_DEF_VAR 0x400 /* variable-size field */
+#define NEXM_DEF_ADR 0x800 /* address field (variable + addr semantics) */
+#define NEXM_DEF_PARAM 0x80 /* field size is a runtime parameter */
+
+// 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) {#n, NEXM_DEF_MSG | (NEXUS_TCODE_##n) }
+#define NEXM_FLD(n) {#n, NEXM_DEF_FLD | (NEXUS_FLDSIZE_##n) }
+#define NEXM_FLD_PAR(n) {#n, NEXM_DEF_FLD | NEXM_DEF_PARAM }
+#define NEXM_VAR(n) {#n, NEXM_DEF_VAR }
+#define NEXM_ADR(n) {#n, NEXM_DEF_VAR | NEXM_DEF_ADR }
+#define NEXM_END() {NULL, 1 }
+
+#define MSGFIELDS_MAX 10
+
+struct rvtrace_ntrace_msg_field_def {
+ const char *name; /* Name of message/field */
+ int def; /* Definition of field (see NEXM_... above) */
+};
+
+extern const struct rvtrace_ntrace_msg_field_def rvtrace_ntrace_msg_defs[];
+
+/* A single decoded N-Trace message */
+struct rvtrace_ntrace_msg {
+ u32 tcode;
+ u32 field_pos;
+ u32 nfields;
+ u32 addr_bits; /* transmitted bit width of the last F-ADDR/U-ADDR field */
+ u64 fields[MSGFIELDS_MAX];
+};
+
+/* Bit-level N-Trace message parser state */
+struct rvtrace_ntrace_msg_parser {
+ int field_def; /* index into rvtrace_ntrace_msg_defs of the current field */
+ int field_bits; /* bits accumulated for the current field */
+ u64 field_val; /* accumulated value of the current field */
+ bool resync; /* skip bytes until the next MSEO==3 terminator */
+};
+
+#endif
--
2.34.1
next prev parent reply other threads:[~2026-10-23 3:17 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 3:14 [RFC PATCH 00/24] Add Linux RISC-V trace support via the gtrace framework Zane Leung
2026-10-23 3:14 ` [PATCH 01/24] gtrace: Move connection release into component unregister Zane Leung
2026-10-23 3:14 ` [PATCH 02/24] gtrace: Initial implementation of gtrace sysfs framework Zane Leung
2026-10-23 3:14 ` [PATCH 03/24] gtrace: Drop trace format field and helper function Zane Leung
2026-10-23 3:14 ` [PATCH 04/24] gtrace: Add RISC-V trace encoder sysfs support Zane Leung
2026-10-23 3:14 ` [PATCH 05/24] gtrace: Introduce some PMU format attributes Zane Leung
2026-10-23 3:14 ` [PATCH 06/24] gtrace: Replace owner PID with perf_event pointer in gtrace_path Zane Leung
2026-10-23 3:14 ` [PATCH 07/24] gtrace: Parse RISC-V Trace encoder config from perf event Zane Leung
2026-10-23 3:14 ` [PATCH 08/24] gtrace: add perf symlink support for gtrace components Zane Leung
2026-10-23 3:14 ` [PATCH 09/24] gtrace: Add port-level start/stop driver callbacks Zane Leung
2026-10-23 3:14 ` [PATCH 10/24] gtrace: Add RISC-V trace funnel driver Zane Leung
2026-10-23 3:14 ` [PATCH 11/24] gtrace: Add RISC-V trace timestamp support Zane Leung
2026-10-23 3:14 ` [PATCH 12/24] gtrace: Add RISC-V trace ATB bridge driver Zane Leung
2026-10-23 3:14 ` [PATCH 13/24] gtrace: Add CoreSight platform core APIs Zane Leung
2026-10-23 3:14 ` [PATCH 14/24] gtrace: Add CoreSight funnel driver Zane Leung
2026-10-23 3:14 ` [PATCH 15/24] gtrace: Only copy AUX data from the path sink Zane Leung
2026-10-23 3:14 ` [PATCH 16/24] perf: Flag whether gtrace AUX data carries CoreSight framing Zane Leung
2026-10-23 3:14 ` [PATCH 17/24] gtrace: Unify default sink selection across CPUs in perf setup Zane Leung
2026-10-23 3:14 ` [PATCH 18/24] gtrace: Add sink-specific buffer alloc/free callbacks Zane Leung
2026-10-23 3:15 ` [PATCH 19/24] gtrace: Add setup_sysfs_buf callback to setup sysfs sink buffer Zane Leung
2026-10-23 3:15 ` [PATCH 20/24] gtrace: Add CoreSight TMC driver support Zane Leung
2026-10-23 3:15 ` [PATCH 21/24] perf tools: riscv: making gtrace PMU listable Zane Leung
2026-10-23 3:15 ` Zane Leung [this message]
2026-10-23 3:15 ` [PATCH 23/24] perf tools: gtrace: Record and parse trace encoder metadata Zane Leung
2026-10-23 3:15 ` [PATCH 24/24] perf tools: gtrace: Add AUX trace decoder and branch sample synthesis 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=20261023031505.2020925-23-liangzhen@linux.spacemit.com \
--to=liangzhen@linux.spacemit.com \
--cc=adrian.hunter@intel.com \
--cc=alex@ghiti.fr \
--cc=alexander.shishkin@linux.intel.com \
--cc=andrew.jones@oss.qualcomm.com \
--cc=anup@brainfault.org \
--cc=atish.patra@linux.dev \
--cc=coresight@lists.linaro.org \
--cc=gregkh@linuxfoundation.org \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=leo.yan@arm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=mark.rutland@arm.com \
--cc=mchitale@gmail.com \
--cc=mike.leach@arm.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=palmer@dabbelt.com \
--cc=peterz@infradead.org \
--cc=pjw@kernel.org \
--cc=sunilvl@oss.qualcomm.com \
--cc=suzuki.poulose@arm.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
all inboxes | Powered by JetHome®