@@ -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;
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,5 @@
+# Source file used to test the TSEN instruction.
+
+foo:
+ tsen 0
+ tsen 1
@@ -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;
@@ -66,15 +66,34 @@ 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
to by ARGPTR to determine how it print the next token or separator
character in the arguments to an instruction. */
@@ -186,6 +205,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 +231,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);
@@ -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",