[binutils-gdb] gas: pru: Add TSEN and MVI instructions
Dimitar Dimitrov
dimitar@sourceware.org
Fri Mar 6 18:08:11 GMT 2026
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=bd2f485e24365d6a5717efeeb3c6d4edb5231796
commit bd2f485e24365d6a5717efeeb3c6d4edb5231796
Author: Dimitar Dimitrov <dimitar@dinux.eu>
Date: Fri Feb 20 22:43:20 2026 +0200
gas: pru: Add TSEN and MVI instructions
Add support for TSEN and MVI instructions to GAS and libopcodes.
TSEN is available in newer PRU core revisions, and can be used to
implement multitasking. MVI allows indirectly addressing CPU registers
using a pointer in R1 register.
References:
- https://www.ti.com/lit/ug/spruij2/spruij2.pdf
Chapter "Move Register File Indirect (MVIx)"
- https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1426480/am2431-pru-assembly-instruction-user-guide
Signed-off-by: Dimitar Dimitrov <dimitar@dinux.eu>
Diff:
---
gas/config/tc-pru.c | 109 +++++++++++++++++++++++++++++++++++++++++++
gas/testsuite/gas/pru/mvi.d | 17 +++++++
gas/testsuite/gas/pru/mvi.s | 12 +++++
gas/testsuite/gas/pru/tsen.d | 10 ++++
gas/testsuite/gas/pru/tsen.s | 5 ++
include/opcode/pru.h | 39 +++++++++++++++-
opcodes/pru-dis.c | 40 ++++++++++++++--
opcodes/pru-opc.c | 9 ++++
8 files changed, 236 insertions(+), 5 deletions(-)
diff --git a/gas/config/tc-pru.c b/gas/config/tc-pru.c
index ee86511fafc..fe096f8e93c 100644
--- a/gas/config/tc-pru.c
+++ b/gas/config/tc-pru.c
@@ -1253,6 +1253,92 @@ pru_assemble_arg_n (pru_insn_infoS *insn_info, const char *argstr)
SET_INSN_FIELD (XFR_LENGTH, insn_info->insn_code, burstlen);
}
+static void
+pru_parse_mvi_operand (const char *argstr,
+ struct pru_reg **blreg,
+ unsigned int *mode)
+{
+ char *regstr;
+
+ *mode = MVI_OP_MODE_DIRECT;
+
+ if (*argstr == '*')
+ {
+ *mode = MVI_OP_MODE_INDIRECT;
+ argstr++;
+
+ if (argstr[0] == '-' && argstr[1] == '-')
+ {
+ argstr += 2;
+ *mode = MVI_OP_MODE_INDIRECT_PREDEC;
+ }
+ }
+ /* Decouple register string from the post increment operator. */
+ regstr = strdup (argstr);
+ *strchrnul (regstr, '+') = '\0';
+ *blreg = pru_reg_lookup (regstr);
+ if (*blreg == NULL)
+ as_bad (_("unknown register %s"), regstr);
+ free (regstr);
+ regstr = NULL;
+ argstr += strlen ((*blreg)->name);
+
+ if (argstr[0] == '+' && argstr[1] == '+')
+ {
+ argstr += 2;
+ if (*mode == MVI_OP_MODE_DIRECT)
+ as_bad (_("missing indirect operator for post-increment operand"));
+ if (*mode == MVI_OP_MODE_INDIRECT_PREDEC)
+ as_bad (_("cannot both pre-decrement and post-increment an operand"));
+ *mode = MVI_OP_MODE_INDIRECT_POSTINC;
+ }
+ if (argstr[0] != '\0')
+ as_bad (_("unexpected statements at and of instruction"));
+
+ if (*mode != MVI_OP_MODE_DIRECT)
+ {
+ if ((*blreg)->index != 1)
+ as_bad (_("only R1 can be used for indirect addressing"));
+
+ if ((*blreg)->regsel != RSEL_7_0
+ && (*blreg)->regsel != RSEL_15_8
+ && (*blreg)->regsel != RSEL_23_16
+ && (*blreg)->regsel != RSEL_31_24)
+ as_bad (_("only byte mode can be used for R1"));
+ }
+}
+
+static void
+pru_assemble_arg_M (pru_insn_infoS *insn_info, const char *argstr)
+{
+ unsigned int mode, rdmode;
+ struct pru_reg *blreg;
+
+ pru_parse_mvi_operand (argstr, &blreg, &mode);
+
+ SET_INSN_FIELD (MVI_RS1_MODE, insn_info->insn_code, mode);
+ SET_INSN_FIELD (RS1, insn_info->insn_code, blreg->index);
+ SET_INSN_FIELD (RS1SEL, insn_info->insn_code, blreg->regsel);
+
+ /* Assume source operand would be parsed after destination one. */
+ rdmode = GET_INSN_FIELD (MVI_RD_MODE, insn_info->insn_code);
+ if (rdmode == MVI_OP_MODE_DIRECT && mode == MVI_OP_MODE_DIRECT)
+ as_bad (_("at least one MVI operand must be indirect"));
+}
+
+static void
+pru_assemble_arg_m (pru_insn_infoS *insn_info, const char *argstr)
+{
+ unsigned int mode;
+ struct pru_reg *blreg;
+
+ pru_parse_mvi_operand (argstr, &blreg, &mode);
+
+ SET_INSN_FIELD (MVI_RD_MODE, insn_info->insn_code, mode);
+ SET_INSN_FIELD (RD, insn_info->insn_code, blreg->index);
+ SET_INSN_FIELD (RDSEL, insn_info->insn_code, blreg->regsel);
+}
+
static void
pru_assemble_arg_c (pru_insn_infoS *insn_info, const char *argstr)
{
@@ -1264,6 +1350,17 @@ pru_assemble_arg_c (pru_insn_infoS *insn_info, const char *argstr)
SET_INSN_FIELD (CB, insn_info->insn_code, cb);
}
+static void
+pru_assemble_arg_t (pru_insn_infoS *insn_info, const char *argstr)
+{
+ unsigned long val = pru_assemble_noreloc_expression (argstr);
+
+ if (val != 0 && val != 1)
+ as_bad (_("invalid task manager mode %ld"), val);
+ else
+ SET_INSN_FIELD (TSKMGR_MODE, insn_info->insn_code, val);
+}
+
static void
pru_assemble_arg_w (pru_insn_infoS *insn_info, const char *argstr)
{
@@ -1354,7 +1451,10 @@ pru_consume_arg (char *argstr, const char *parsestr)
case 'S':
case 'l':
case 'n':
+ case 'm':
+ case 'M':
case 'R':
+ case 't':
case 'w':
case 'x':
/* We can't have %pmem here. */
@@ -1680,9 +1780,18 @@ md_assemble (char *op_str)
case 'n':
pru_assemble_arg_n (insn, *argtk++);
continue;
+ case 'm':
+ pru_assemble_arg_m (insn, *argtk++);
+ continue;
+ case 'M':
+ pru_assemble_arg_M (insn, *argtk++);
+ continue;
case 'c':
pru_assemble_arg_c (insn, *argtk++);
continue;
+ case 't':
+ pru_assemble_arg_t (insn, *argtk++);
+ continue;
case 'w':
pru_assemble_arg_w (insn, *argtk++);
continue;
diff --git a/gas/testsuite/gas/pru/mvi.d b/gas/testsuite/gas/pru/mvi.d
new file mode 100644
index 00000000000..626fc0cf0c4
--- /dev/null
+++ b/gas/testsuite/gas/pru/mvi.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU mvi
+
+# Test the mvi instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 2c204198 mvib r24.w0, \*r1.b2
+0+0004 <[^>]*> 2ca20101 mvid \*r1.b0, \*r1.b0
+0+0008 <[^>]*> 2ce20101 mvid \*r1.b0, \*--r1.b0
+0+000c <[^>]*> 2dc20101 mvid \*--r1.b0, \*r1.b0\+\+
+0+0010 <[^>]*> 2cc14101 mviw \*r1.b0, \*r1.b2\+\+
+0+0014 <[^>]*> 2cc14121 mviw \*r1.b1, \*r1.b2\+\+
+0+0018 <[^>]*> 2c4261f4 mvid r20, \*r1.b3\+\+
+0+001c <[^>]*> 2c6241f4 mvid r20, \*--r1.b2
+0+0020 <[^>]*> 2d82ff41 mvid \*--r1.b2, r31
diff --git a/gas/testsuite/gas/pru/mvi.s b/gas/testsuite/gas/pru/mvi.s
new file mode 100644
index 00000000000..cf23515668f
--- /dev/null
+++ b/gas/testsuite/gas/pru/mvi.s
@@ -0,0 +1,12 @@
+# Source file used to test the MVI instruction.
+
+foo:
+ mvib r24.w0, *r1.b2
+ mvid *r1.b0, *r1.b0
+ mvid *r1.b0, *--r1.b0
+ mvid *--r1.b0, *r1.b0++
+ mviw *r1.b0, *r1.b2++
+ mviw *r1.b1, *r1.b2++
+ mvid r20, *r1.b3++
+ mvid r20, *--r1.b2
+ mvid *--r1.b2, r31
diff --git a/gas/testsuite/gas/pru/tsen.d b/gas/testsuite/gas/pru/tsen.d
new file mode 100644
index 00000000000..5c4779bb015
--- /dev/null
+++ b/gas/testsuite/gas/pru/tsen.d
@@ -0,0 +1,10 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU tsen
+
+# Test the TSEN instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 32000000 tsen 0
+0+0004 <[^>]*> 32800000 tsen 1
diff --git a/gas/testsuite/gas/pru/tsen.s b/gas/testsuite/gas/pru/tsen.s
new file mode 100644
index 00000000000..716d7f476c5
--- /dev/null
+++ b/gas/testsuite/gas/pru/tsen.s
@@ -0,0 +1,5 @@
+# Source file used to test the TSEN instruction.
+
+foo:
+ tsen 0
+ tsen 1
diff --git a/include/opcode/pru.h b/include/opcode/pru.h
index f47860a681c..379b11e56a7 100644
--- a/include/opcode/pru.h
+++ b/include/opcode/pru.h
@@ -64,7 +64,8 @@ enum pru_instr_type
prui_halt, prui_slp, prui_xin, prui_xout, prui_xchg, prui_sxin,
prui_sxout, prui_sxchg, prui_loop, prui_iloop, prui_qbgt, prui_qbge,
prui_qblt, prui_qble, prui_qbeq, prui_qbne, prui_qba, prui_qbbs,
- prui_qbbc, prui_lbbo, prui_sbbo, prui_lbco, prui_sbco
+ prui_qbbc, prui_lbbo, prui_sbbo, prui_lbco, prui_sbco, prui_tsen,
+ prui_mvib, prui_mviw, prui_mvid
};
/* This structure holds information for a particular instruction.
@@ -81,11 +82,14 @@ enum pru_instr_type
j - a 5.3-bit right source register index OR 18-bit PC address
l - burst length (unsigned 7-bit immediate or r0.b[0-3]) for xLBCO
n - burst length (unsigned 7-bit immediate or r0.b[0-3]) for XFR
+ m - MVI destination operand
+ M - MVI source operand
o - a 10-bit signed PC-relative offset
O - an 8-bit unsigned PC-relative offset for LOOP termination point
R - a 5-bit destination register index
s - a 5.3-bit left source register index
S - a 5-bit left source register index
+ t - task manager mode (0: disabled, 1: enabled)
w - a single bit for "WakeOnStatus"
W - a 16-bit unsigned immediate with IO=0 field (LDI)
x - an 8-bit XFR wide-bus address immediate
@@ -236,15 +240,19 @@ struct pru_reg
#define SUBOP_LMBD 3u
#define SUBOP_SCAN 4u
#define SUBOP_HALT 5u
-#define SUBOP_RSVD_FOR_MVIx 6u
+#define SUBOP_MVIX 6u
#define SUBOP_XFR 7u
#define SUBOP_LOOP 8u
+#define SUBOP_TSEN 9u
#define SUBOP_RSVD_FOR_RFI 14u
#define SUBOP_SLP 15u
#define OP_SH_WAKEONSTATUS 23
#define OP_MASK_WAKEONSTATUS (0x1u << 23)
+#define OP_SH_TSKMGR_MODE 23
+#define OP_MASK_TSKMGR_MODE (0x1u << 23)
+
/* Format 2 XFR specific fields. */
#define OP_SH_SUBOP_XFR 23
#define OP_MASK_SUBOP_XFR (3u << 23)
@@ -265,6 +273,23 @@ struct pru_reg
#define OP_SH_LOOP_JMPOFFS 0
#define OP_MASK_LOOP_JMPOFFS (0xffu << 0)
+/* Format 2 MVI specific fields. */
+#define OP_SH_MVI_RS1_MODE 21
+#define OP_MASK_MVI_RS1_MODE (3u << 21)
+#define OP_SH_MVI_RD_MODE 23
+#define OP_MASK_MVI_RD_MODE (3u << 23)
+#define OP_SH_MVI_LEN 16
+#define OP_MASK_MVI_LEN (3u << 16)
+
+#define MVI_OP_MODE_DIRECT 0u
+#define MVI_OP_MODE_INDIRECT 1u
+#define MVI_OP_MODE_INDIRECT_POSTINC 2u
+#define MVI_OP_MODE_INDIRECT_PREDEC 3u
+
+#define MVI_LEN_BYTE 0u
+#define MVI_LEN_WORD 1u
+#define MVI_LEN_DWORD 2u
+
/* Format 4 specific fields. */
#define OP_SH_BROFF98 25
#define OP_MASK_BROFF98 (0x3u << 25)
@@ -331,6 +356,12 @@ struct pru_reg
#define OP_MATCH_SCAN (OP_MATCH_FMT2_OP | (SUBOP_SCAN << OP_SH_SUBOP))
#define OP_MATCH_HALT (OP_MATCH_FMT2_OP | (SUBOP_HALT << OP_SH_SUBOP))
#define OP_MATCH_SLP (OP_MATCH_FMT2_OP | (SUBOP_SLP << OP_SH_SUBOP))
+#define OP_MATCH_MVIB (OP_MATCH_FMT2_OP | (SUBOP_MVIX << OP_SH_SUBOP) \
+ | (MVI_LEN_BYTE << OP_SH_MVI_LEN))
+#define OP_MATCH_MVIW (OP_MATCH_FMT2_OP | (SUBOP_MVIX << OP_SH_SUBOP) \
+ | (MVI_LEN_WORD << OP_SH_MVI_LEN))
+#define OP_MATCH_MVID (OP_MATCH_FMT2_OP | (SUBOP_MVIX << OP_SH_SUBOP) \
+ | (MVI_LEN_DWORD << OP_SH_MVI_LEN))
#define OP_MATCH_XFR (OP_MATCH_FMT2_OP | (SUBOP_XFR << OP_SH_SUBOP))
#define OP_MATCH_SXFR (OP_MATCH_XFR | OP_MASK_XFR_S)
#define OP_MATCH_XIN (OP_MATCH_XFR | (SUBOP_XFR_XIN << OP_SH_SUBOP_XFR))
@@ -342,6 +373,7 @@ struct pru_reg
#define OP_MATCH_LOOP (OP_MATCH_FMT2_OP | (SUBOP_LOOP << OP_SH_SUBOP))
#define OP_MATCH_ILOOP (OP_MATCH_FMT2_OP | (SUBOP_LOOP << OP_SH_SUBOP) \
| OP_MASK_LOOP_INTERRUPTIBLE)
+#define OP_MATCH_TSEN (OP_MATCH_FMT2_OP | (SUBOP_TSEN << OP_SH_SUBOP))
#define OP_MATCH_QBGT (OP_MATCH_FMT4_OP | OP_MASK_GT)
#define OP_MATCH_QBGE (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_EQ)
@@ -394,6 +426,9 @@ struct pru_reg
#define OP_MASK_LOOP_OP (OP_MASK_FMT2_OP | OP_MASK_SUBOP \
| OP_MASK_LOOP_INTERRUPTIBLE)
+#define OP_MASK_MVIX_OP (OP_MASK_FMT2_OP | OP_MASK_SUBOP \
+ | OP_MASK_MVI_LEN)
+
/* These are the data structures we use to hold the instruction information. */
extern const struct pru_opcode pru_opcodes[];
extern const int bfd_pru_num_opcodes;
diff --git a/opcodes/pru-dis.c b/opcodes/pru-dis.c
index 5465c26a138..5cda4e9d6b4 100644
--- a/opcodes/pru-dis.c
+++ b/opcodes/pru-dis.c
@@ -66,13 +66,31 @@ pru_find_opcode (unsigned long opcode)
#define NUMREGNAMES (32 * 8)
static void
-pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
- disassemble_info *info)
+pru_print_insn_arg_indreg (unsigned int r, unsigned int sel,
+ unsigned int mode,
+ disassemble_info *info)
{
+ const char *fmtstr;
unsigned int i = r * RSEL_NUM_ITEMS + sel;
assert (i < (unsigned int)pru_num_regs);
assert (i < NUMREGNAMES);
- (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
+
+ switch (mode)
+ {
+ case MVI_OP_MODE_DIRECT: fmtstr = "%s"; break;
+ case MVI_OP_MODE_INDIRECT: fmtstr = "*%s"; break;
+ case MVI_OP_MODE_INDIRECT_POSTINC: fmtstr = "*%s++"; break;
+ case MVI_OP_MODE_INDIRECT_PREDEC: fmtstr = "*--%s"; break;
+ default: fmtstr = "<invalid>%s"; break;
+ }
+ (*info->fprintf_func) (info->stream, fmtstr, pru_regs[i].name);
+}
+
+static void
+pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
+ disassemble_info *info)
+{
+ pru_print_insn_arg_indreg (r, sel, MVI_OP_MODE_DIRECT, info);
}
/* The function pru_print_insn_arg uses the character pointed
@@ -186,6 +204,18 @@ pru_print_insn_arg (const char *argptr,
(*info->fprintf_func) (info->stream, "r0.b%ld", i);
}
break;
+ case 'm':
+ pru_print_insn_arg_indreg (GET_INSN_FIELD (RD, opcode),
+ GET_INSN_FIELD (RDSEL, opcode),
+ GET_INSN_FIELD (MVI_RD_MODE, opcode),
+ info);
+ break;
+ case 'M':
+ pru_print_insn_arg_indreg (GET_INSN_FIELD (RS1, opcode),
+ GET_INSN_FIELD (RS1SEL, opcode),
+ GET_INSN_FIELD (MVI_RS1_MODE, opcode),
+ info);
+ break;
case 'n':
i = GET_INSN_FIELD (XFR_LENGTH, opcode);
if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
@@ -200,6 +230,10 @@ pru_print_insn_arg (const char *argptr,
i = GET_INSN_FIELD (CB, opcode);
(*info->fprintf_func) (info->stream, "%ld", i);
break;
+ case 't':
+ i = GET_INSN_FIELD (TSKMGR_MODE, opcode);
+ (*info->fprintf_func) (info->stream, "%ld", i);
+ break;
case 'w':
i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
(*info->fprintf_func) (info->stream, "%ld", i);
diff --git a/opcodes/pru-opc.c b/opcodes/pru-opc.c
index ee7fb16d8d4..aeef5224b29 100644
--- a/opcodes/pru-opc.c
+++ b/opcodes/pru-opc.c
@@ -125,9 +125,18 @@ const struct pru_opcode pru_opcodes[] =
OP_MATCH_LMBD, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, unsigned_immed8_overflow},
{ "halt", prui_halt, "",
OP_MATCH_HALT, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, no_overflow},
+ { "tsen", prui_tsen, "t",
+ OP_MATCH_TSEN, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, no_overflow},
{ "slp", prui_slp, "w",
OP_MATCH_SLP, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, no_overflow},
+ { "mvib", prui_mvib, "m,M",
+ OP_MATCH_MVIB, OP_MASK_MVIX_OP, 0, no_overflow},
+ { "mviw", prui_mviw, "m,M",
+ OP_MATCH_MVIW, OP_MASK_MVIX_OP, 0, no_overflow},
+ { "mvid", prui_mvid, "m,M",
+ OP_MATCH_MVID, OP_MASK_MVIX_OP, 0, no_overflow},
+
{ "xin", prui_xin, "x,D,n",
OP_MATCH_XIN, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow},
{ "xout", prui_xout, "x,D,n",
More information about the Binutils-cvs
mailing list