From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 37295 invoked by alias); 30 Dec 2016 10:40:59 -0000 Mailing-List: contact binutils-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: binutils-owner@sourceware.org Received: (qmail 36521 invoked by uid 89); 30 Dec 2016 10:40:57 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=2.6 required=5.0 tests=AWL,BAYES_50,UNSUBSCRIBE_BODY autolearn=no version=3.3.2 spammy=14u, xin, dn, ds X-HELO: server28.host.bg Received: from server28.host.bg (HELO server28.host.bg) (87.120.40.98) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 30 Dec 2016 10:40:44 +0000 Received: from [95.87.234.74] (port=41668 helo=localhost.localdomain) by server28.host.bg with esmtpsa (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.87) (envelope-from ) id 1cMubV-003q4u-Iz; Fri, 30 Dec 2016 12:40:05 +0200 From: Dimitar Dimitrov To: binutils@sourceware.org Cc: Dimitar Dimitrov Subject: [PATCH v3 05/15] PRU Opcode Port Date: Fri, 30 Dec 2016 10:40:00 -0000 Message-Id: <20161230104000.15594-6-dimitar@dinux.eu> In-Reply-To: <20161230104000.15594-1-dimitar@dinux.eu> References: <20161230104000.15594-1-dimitar@dinux.eu> X-Get-Message-Sender-Via: server28.host.bg: authenticated_id: dimitar@dinux.eu X-IsSubscribed: yes X-SW-Source: 2016-12/txt/msg00483.txt.bz2 2016-12-30 Dimitar Dimitrov include/ * dis-asm.h: Add print_insn_pru declaration. * opcode/pru.h: New file. opcodes/ * Makefile.in: Regenerate. * configure: Regenerate. * Makefile.am: Add PRU source files. * configure.ac: Add PRU target. * disassemble.c (disassembler): Register PRU arch. * pru-dis.c: New file. * pru-opc.c: New file. Signed-off-by: Dimitar Dimitrov --- include/dis-asm.h | 1 + include/opcode/pru.h | 411 ++++++++++++++++++++++++++++++++++++++++++++++++++ opcodes/Makefile.am | 2 + opcodes/configure.ac | 1 + opcodes/disassemble.c | 7 +- opcodes/pru-dis.c | 286 +++++++++++++++++++++++++++++++++++ opcodes/pru-opc.c | 236 +++++++++++++++++++++++++++++ 7 files changed, 943 insertions(+), 1 deletion(-) create mode 100644 include/opcode/pru.h create mode 100644 opcodes/pru-dis.c create mode 100644 opcodes/pru-opc.c diff --git a/include/dis-asm.h b/include/dis-asm.h index 2cefff4115..91084f0c37 100644 --- a/include/dis-asm.h +++ b/include/dis-asm.h @@ -289,6 +289,7 @@ extern int print_insn_ns32k (bfd_vma, disassemble_info *); extern int print_insn_or1k (bfd_vma, disassemble_info *); extern int print_insn_pdp11 (bfd_vma, disassemble_info *); extern int print_insn_pj (bfd_vma, disassemble_info *); +extern int print_insn_pru (bfd_vma, disassemble_info *); extern int print_insn_rs6000 (bfd_vma, disassemble_info *); extern int print_insn_s390 (bfd_vma, disassemble_info *); extern int print_insn_sh (bfd_vma, disassemble_info *); diff --git a/include/opcode/pru.h b/include/opcode/pru.h new file mode 100644 index 0000000000..427e470fc7 --- /dev/null +++ b/include/opcode/pru.h @@ -0,0 +1,411 @@ +/* TI PRU opcode list for GAS, the GNU assembler. + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + + This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler. + + GAS/GDB is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS/GDB is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GAS or GDB; see the file COPYING3. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef _PRU_H_ +#define _PRU_H_ + +#include "bfd.h" + +/**************************************************************************** + * This file contains structures, bit masks and shift counts used + * by the GNU toolchain to define the PRU instruction set and + * access various opcode fields. + ****************************************************************************/ + +/* Identify different overflow situations for error messages. */ +enum overflow_type +{ + call_target_overflow = 0, + qbranch_target_overflow, + address_offset_overflow, + signed_immed16_overflow, + unsigned_immed32_overflow, + unsigned_immed16_overflow, + unsigned_immed8_overflow, + unsigned_immed5_overflow, + no_overflow +}; + +enum opcode_format_type { + opcode_format1, + opcode_format2ab, + opcode_format2abl, + opcode_format2c, + opcode_format2de, + opcode_format45, + opcode_format6 +}; + +/* Opcode ID listing. Used for indexing by the simulator. */ +enum pru_instr_type { + prui_add, prui_adc, prui_sub, prui_suc, prui_lsl, prui_lsr, prui_rsb, + prui_rsc, prui_and, prui_or, prui_xor, prui_min, prui_max, prui_clr, + prui_set, prui_not, prui_jmp, prui_jal, prui_ldi, 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 +}; + +/* This structure holds information for a particular instruction. + + The args field is a string describing the operands. The following + letters can appear in the args: + b - a 5.3-bit right source register index OR 8-bit unsigned immediate + B - same as 'b', but for LOOP instruction where IMM is decremented + c - a 5 bit unsigned immediate for constant table offset + d - a 5.3-bit destination register index + D - a 5.2-bit destination register index + E - for internal GAS self-tests only + i - a 32-bit immediate or label + 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 + 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 + 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 + Literal ',' character may also appear in the args as delimiter. + + Most of the macro names are from [1]. + + The pinfo field is INSN_MACRO for a macro. Otherwise, it is a collection + of bits describing the instruction, notably any relevant hazard + information. + + When assembling, the match field contains the opcode template, which + is modified by the arguments to produce the actual opcode + that is emitted. If pinfo is INSN_MACRO, then this is 0. + + If pinfo is INSN_MACRO, the mask field stores the macro identifier. + Otherwise this is a bit mask for the relevant portions of the opcode + when disassembling. If the actual opcode anded with the match field + equals the opcode field, then we have found the correct instruction. + + [1] http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit */ + +struct pru_opcode +{ + const char *name; /* The name of the instruction. */ + enum pru_instr_type type; /* Instruction type. Used for fast indexing + by the simulator. */ + const char *args; /* A string describing the arguments for this + instruction. */ + unsigned long match; /* The basic opcode for the instruction. */ + unsigned long mask; /* Mask for the opcode field of the + instruction. */ + unsigned long pinfo; /* Is this a real instruction or instruction + macro? */ + enum overflow_type overflow_msg; /* Used to generate informative + message when fixup overflows. */ +}; + +/* This value is used in the pru_opcode.pinfo field to indicate that the + instruction is a macro or pseudo-op. This requires special treatment by + the assembler, and is used by the disassembler to determine whether to + check for a nop. */ +#define PRU_INSN_MACRO 0x80000000 + +/* This macro is specially handled throughout the code because it is + the only insn to output 2 words (64 bits). */ +#define PRU_INSN_LDI32 0x40000000 + +/* Associates a register name with a 5-bit index and 3-bit regsel. */ +struct pru_reg +{ + const char *name; /* Name, e.g. "r10". */ + const unsigned int index; /* Index, e.g. 10. */ + const unsigned int regsel; /* Register field selector, .e.g RSEL_31_0. */ +}; + +/* Macros for getting and setting an instruction field. */ +#define GET_INSN_FIELD(X, i) \ + (((i) & OP_MASK_##X) >> OP_SH_##X) +#define SET_INSN_FIELD(X, i, v) \ + ((i) = (((i) & ~OP_MASK_##X) | (((v) << OP_SH_##X) & OP_MASK_##X))) + +#define CHECK_INSN_FIELD(X, i) \ + (((i) & OP_MASK_##X) == OP_MATCH_##X) + +/* Masks, values, shifts and macros for accessing the various opcode fields. */ + +#define OP_SH_FMT1_OP 29 +#define OP_MASK_FMT1_OP (0x7u << 29) +#define OP_MATCH_FMT1_OP (0x0u << 29) + +#define OP_SH_FMT2_OP 29 +#define OP_MASK_FMT2_OP (0x7u << 29) +#define OP_MATCH_FMT2_OP (0x1u << 29) + +#define OP_SH_FMT4_OP 30 +#define OP_MASK_FMT4_OP (0x3u << 30) +#define OP_MATCH_FMT4_OP (0x1u << 30) + +#define OP_SH_FMT5_OP 29 +#define OP_MASK_FMT5_OP (0x7u << 29) +#define OP_MATCH_FMT5_OP (0x6u << 29) + +#define OP_SH_FMT6AB_OP 29 +#define OP_MASK_FMT6AB_OP (0x7u << 29) +#define OP_MATCH_FMT6AB_OP (0x7u << 29) + +#define OP_SH_FMT6CD_OP 29 +#define OP_MASK_FMT6CD_OP (0x7u << 29) +#define OP_MATCH_FMT6CD_OP (0x4u << 29) + +/* Generic fields. */ +#define OP_SH_SUBOP 25 +#define OP_MASK_SUBOP (0xfu << 25) + +#define OP_SH_IO 24 +#define OP_MASK_IO (0x1u << 24) + +#define OP_SH_RS2SEL 21 +#define OP_MASK_RS2SEL (0x7u << 21) +#define OP_SH_RS2 16 +#define OP_MASK_RS2 (0x1fu << 16) +#define OP_SH_RS1SEL 13 +#define OP_MASK_RS1SEL (0x7u << 13) +#define OP_SH_RS1 8 +#define OP_MASK_RS1 (0x1fu << 8) +#define OP_SH_RDSEL 5 +#define OP_MASK_RDSEL (0x7u << 5) +#define OP_SH_RD 0 +#define OP_MASK_RD (0x1fu << 0) +#define OP_SH_IMM8 16 +#define OP_MASK_IMM8 (0xffu << 16) +#define OP_SH_IMM16 8 +#define OP_MASK_IMM16 (0xffffu << 8) + +#define RSEL_7_0 0u +#define RSEL_15_8 1u +#define RSEL_23_16 2u +#define RSEL_31_24 3u +#define RSEL_15_0 4u +#define RSEL_23_8 5u +#define RSEL_31_16 6u +#define RSEL_31_0 7u +#define RSEL_NUM_ITEMS 8u + +/* Format 1 specific fields. */ +#define SUBOP_ADD 0u +#define SUBOP_ADC 1u +#define SUBOP_SUB 2u +#define SUBOP_SUC 3u +#define SUBOP_LSL 4u +#define SUBOP_LSR 5u +#define SUBOP_RSB 6u +#define SUBOP_RSC 7u +#define SUBOP_AND 8u +#define SUBOP_OR 9u +#define SUBOP_XOR 10u +#define SUBOP_NOT 11u +#define SUBOP_MIN 12u +#define SUBOP_MAX 13u +#define SUBOP_CLR 14u +#define SUBOP_SET 15u + +/* Format 2 specific fields. */ +#define SUBOP_JMP 0u +#define SUBOP_JAL 1u +#define SUBOP_LDI 2u +#define SUBOP_LMBD 3u +#define SUBOP_SCAN 4u +#define SUBOP_HALT 5u +#define SUBOP_RSVD_FOR_MVIx 6u +#define SUBOP_XFR 7u +#define SUBOP_LOOP 8u +#define SUBOP_RSVD_FOR_RFI 14u +#define SUBOP_SLP 15u + +#define OP_SH_WAKEONSTATUS 23 +#define OP_MASK_WAKEONSTATUS (0x1u << 23) + +/* Format 2 XFR specific fields. */ +#define OP_SH_SUBOP_XFR 23 +#define OP_MASK_SUBOP_XFR (3u << 23) +#define OP_SH_XFR_WBA 15 +#define OP_MASK_XFR_WBA (0xffu << 15) +#define OP_SH_XFR_S 14 +#define OP_MASK_XFR_S (1u << 14) +#define OP_SH_XFR_LENGTH 7 +#define OP_MASK_XFR_LENGTH (0x7fu << 7) + +#define SUBOP_XFR_XIN 1u +#define SUBOP_XFR_XOUT 2u +#define SUBOP_XFR_XCHG 3u + +/* Format 2 LOOP specific fields. */ +#define OP_SH_LOOP_INTERRUPTIBLE 15 +#define OP_MASK_LOOP_INTERRUPTIBLE (1u << 15) +#define OP_SH_LOOP_JMPOFFS 0 +#define OP_MASK_LOOP_JMPOFFS (0xffu << 0) + +/* Format 4 specific fields. */ +#define OP_SH_BROFF98 25 +#define OP_MASK_BROFF98 (0x3u << 25) +#define OP_SH_BROFF70 0 +#define OP_MASK_BROFF70 (0xffu << 0) +#define OP_SH_GT 29 +#define OP_MASK_GT (0x1u << 29) +#define OP_SH_EQ 28 +#define OP_MASK_EQ (0x1u << 28) +#define OP_SH_LT 27 +#define OP_MASK_LT (0x1u << 27) +#define OP_MASK_CMP (OP_MASK_GT | OP_MASK_EQ | OP_MASK_LT) + + +/* Format 5 specific fields. */ +#define OP_SH_BS 28 +#define OP_MASK_BS (0x1u << 28) +#define OP_SH_BC 27 +#define OP_MASK_BC (0x1u << 27) +#define OP_MASK_BCMP (OP_MASK_BS | OP_MASK_BC) + +/* Format 6 specific fields. */ +#define OP_SH_LOADSTORE 28 +#define OP_MASK_LOADSTORE (0x1u << 28) +#define OP_SH_BURSTLEN64 25 +#define OP_MASK_BURSTLEN64 (0x7u << 25) +#define OP_SH_BURSTLEN31 13 +#define OP_MASK_BURSTLEN31 (0x7u << 13) +#define OP_SH_CB 8 +#define OP_MASK_CB (0x1fu << 8) +#define OP_SH_BURSTLEN0 7 +#define OP_MASK_BURSTLEN0 (0x1u << 7) +#define OP_SH_RDB 5 +#define OP_MASK_RDB (0x3u << 5) + +#define LSSBBO_BYTECOUNT_R0_BITS7_0 124u +#define LSBBO_BYTECOUNT_R0_BITS15_8 125u +#define LSBBO_BYTECOUNT_R0_BITS23_16 126u +#define LSBBO_BYTECOUNT_R0_BITS31_24 127u + +/* The following macros define the opcode matches for each + instruction code & OP_MASK_INST == OP_MATCH_INST. */ +#define OP_MATCH_ADD (OP_MATCH_FMT1_OP | (SUBOP_ADD << OP_SH_SUBOP)) +#define OP_MATCH_ADC (OP_MATCH_FMT1_OP | (SUBOP_ADC << OP_SH_SUBOP)) +#define OP_MATCH_SUB (OP_MATCH_FMT1_OP | (SUBOP_SUB << OP_SH_SUBOP)) +#define OP_MATCH_SUC (OP_MATCH_FMT1_OP | (SUBOP_SUC << OP_SH_SUBOP)) +#define OP_MATCH_LSL (OP_MATCH_FMT1_OP | (SUBOP_LSL << OP_SH_SUBOP)) +#define OP_MATCH_LSR (OP_MATCH_FMT1_OP | (SUBOP_LSR << OP_SH_SUBOP)) +#define OP_MATCH_RSB (OP_MATCH_FMT1_OP | (SUBOP_RSB << OP_SH_SUBOP)) +#define OP_MATCH_RSC (OP_MATCH_FMT1_OP | (SUBOP_RSC << OP_SH_SUBOP)) +#define OP_MATCH_AND (OP_MATCH_FMT1_OP | (SUBOP_AND << OP_SH_SUBOP)) +#define OP_MATCH_OR (OP_MATCH_FMT1_OP | (SUBOP_OR << OP_SH_SUBOP)) +#define OP_MATCH_XOR (OP_MATCH_FMT1_OP | (SUBOP_XOR << OP_SH_SUBOP)) +#define OP_MATCH_NOT (OP_MATCH_FMT1_OP | (SUBOP_NOT << OP_SH_SUBOP)) +#define OP_MATCH_MIN (OP_MATCH_FMT1_OP | (SUBOP_MIN << OP_SH_SUBOP)) +#define OP_MATCH_MAX (OP_MATCH_FMT1_OP | (SUBOP_MAX << OP_SH_SUBOP)) +#define OP_MATCH_CLR (OP_MATCH_FMT1_OP | (SUBOP_CLR << OP_SH_SUBOP)) +#define OP_MATCH_SET (OP_MATCH_FMT1_OP | (SUBOP_SET << OP_SH_SUBOP)) + +#define OP_MATCH_JMP (OP_MATCH_FMT2_OP | (SUBOP_JMP << OP_SH_SUBOP)) +#define OP_MATCH_JAL (OP_MATCH_FMT2_OP | (SUBOP_JAL << OP_SH_SUBOP)) +#define OP_MATCH_LDI (OP_MATCH_FMT2_OP | (SUBOP_LDI << OP_SH_SUBOP)) +#define OP_MATCH_LMBD (OP_MATCH_FMT2_OP | (SUBOP_LMBD << OP_SH_SUBOP)) +#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_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)) +#define OP_MATCH_XOUT (OP_MATCH_XFR | (SUBOP_XFR_XOUT << OP_SH_SUBOP_XFR)) +#define OP_MATCH_XCHG (OP_MATCH_XFR | (SUBOP_XFR_XCHG << OP_SH_SUBOP_XFR)) +#define OP_MATCH_SXIN (OP_MATCH_SXFR | (SUBOP_XFR_XIN << OP_SH_SUBOP_XFR)) +#define OP_MATCH_SXOUT (OP_MATCH_SXFR | (SUBOP_XFR_XOUT << OP_SH_SUBOP_XFR)) +#define OP_MATCH_SXCHG (OP_MATCH_SXFR | (SUBOP_XFR_XCHG << OP_SH_SUBOP_XFR)) +#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_QBGT (OP_MATCH_FMT4_OP | OP_MASK_GT) +#define OP_MATCH_QBGE (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_EQ) +#define OP_MATCH_QBLT (OP_MATCH_FMT4_OP | OP_MASK_LT) +#define OP_MATCH_QBLE (OP_MATCH_FMT4_OP | OP_MASK_LT | OP_MASK_EQ) +#define OP_MATCH_QBEQ (OP_MATCH_FMT4_OP | OP_MASK_EQ) +#define OP_MATCH_QBNE (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_LT) +#define OP_MATCH_QBA (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_LT \ + | OP_MASK_EQ) + +#define OP_MATCH_QBBS (OP_MATCH_FMT5_OP | OP_MASK_BS) +#define OP_MATCH_QBBC (OP_MATCH_FMT5_OP | OP_MASK_BC) + +#define OP_MATCH_LBBO (OP_MATCH_FMT6AB_OP | OP_MASK_LOADSTORE) +#define OP_MATCH_SBBO (OP_MATCH_FMT6AB_OP) +#define OP_MATCH_LBCO (OP_MATCH_FMT6CD_OP | OP_MASK_LOADSTORE) +#define OP_MATCH_SBCO (OP_MATCH_FMT6CD_OP) + +/* Some special extractions. */ +#define OP_MASK_BROFF (OP_MASK_BROFF98 | OP_MASK_BROFF70) + +#define GET_BROFF_URAW(i) \ + ((GET_INSN_FIELD (BROFF98, i) << 8) | (GET_INSN_FIELD (BROFF70, i) << 0)) + +#define GET_BROFF_SIGNED(i) \ + ((long)(GET_BROFF_URAW (i) - (!!(GET_BROFF_URAW (i) & (1 << 9)) << 10))) + +#define SET_BROFF_URAW(i, v) \ + do { \ + SET_INSN_FIELD (BROFF98, (i), (v) >> 8); \ + SET_INSN_FIELD (BROFF70, (i), (v) & 0xff); \ + } while (0) + +#define GET_BURSTLEN(i) \ + ( (GET_INSN_FIELD (BURSTLEN64, (i)) << 4) | \ + (GET_INSN_FIELD (BURSTLEN31, (i)) << 1) | \ + (GET_INSN_FIELD (BURSTLEN0, (i)) << 0)) + +#define SET_BURSTLEN(i, v) \ + do { \ + SET_INSN_FIELD (BURSTLEN64, (i), (v) >> 4); \ + SET_INSN_FIELD (BURSTLEN31, (i), (v) >> 1); \ + SET_INSN_FIELD (BURSTLEN0, (i), (v) >> 0); \ + } while (0) + +/* Miscellaneous helpers. */ +#define OP_MASK_XFR_OP (OP_MASK_FMT2_OP | OP_MASK_SUBOP \ + | OP_MASK_SUBOP_XFR | OP_MASK_XFR_S) + +#define OP_MASK_LOOP_OP (OP_MASK_FMT2_OP | OP_MASK_SUBOP \ + | OP_MASK_LOOP_INTERRUPTIBLE) + +/* 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; + +/* These are the data structures used to hold the register information. */ +extern const struct pru_reg pru_regs[]; +extern const int pru_num_regs; + +/* Machine-independent macro for number of opcodes. */ +#define NUMOPCODES bfd_pru_num_opcodes +#define NUMREGISTERS pru_num_regs; + +/* This is made extern so that the assembler can use it to find out + what instruction caused an error. */ +extern const struct pru_opcode *pru_find_opcode (unsigned long); + +#endif /* _PRU_H */ diff --git a/opcodes/Makefile.am b/opcodes/Makefile.am index a441febae8..fd86b46780 100644 --- a/opcodes/Makefile.am +++ b/opcodes/Makefile.am @@ -223,6 +223,8 @@ TARGET_LIBOPCODES_CFILES = \ pj-opc.c \ ppc-dis.c \ ppc-opc.c \ + pru-dis.c \ + pru-opc.c \ riscv-dis.c \ riscv-opc.c \ rl78-decode.c \ diff --git a/opcodes/configure.ac b/opcodes/configure.ac index 3475d491c5..2d3330950c 100644 --- a/opcodes/configure.ac +++ b/opcodes/configure.ac @@ -307,6 +307,7 @@ if test x${all_targets} = xfalse ; then bfd_pj_arch) ta="$ta pj-dis.lo pj-opc.lo" ;; bfd_powerpc_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;; bfd_powerpc_64_arch) ta="$ta ppc-dis.lo ppc-opc.lo" ;; + bfd_pru_arch) ta="$ta pru-dis.lo pru-opc.lo" ;; bfd_pyramid_arch) ;; bfd_romp_arch) ;; bfd_riscv_arch) ta="$ta riscv-dis.lo riscv-opc.lo" ;; diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c index e1fb65c823..aeed703aa6 100644 --- a/opcodes/disassemble.c +++ b/opcodes/disassemble.c @@ -73,6 +73,7 @@ #define ARCH_pdp11 #define ARCH_pj #define ARCH_powerpc +#define ARCH_pru #define ARCH_rs6000 #define ARCH_rl78 #define ARCH_rx @@ -375,10 +376,14 @@ disassembler (bfd *abfd) disassemble = print_insn_little_powerpc; break; #endif +#ifdef ARCH_pru + case bfd_arch_pru: + disassemble = print_insn_pru; + break; +#endif #ifdef ARCH_riscv case bfd_arch_riscv: disassemble = print_insn_riscv; - break; #endif #ifdef ARCH_rs6000 case bfd_arch_rs6000: diff --git a/opcodes/pru-dis.c b/opcodes/pru-dis.c new file mode 100644 index 0000000000..dd150c5b2c --- /dev/null +++ b/opcodes/pru-dis.c @@ -0,0 +1,286 @@ +/* TI PRU disassemble routines + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + + This file is part of the GNU opcodes library. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "sysdep.h" +#include "dis-asm.h" +#include "opcode/pru.h" +#include "libiberty.h" +#include +#include + +/* No symbol table is available when this code runs out in an embedded + system as when it is used for disassembler support in a monitor. */ +#if !defined (EMBEDDED_ENV) +#define SYMTAB_AVAILABLE 1 +#include "elf-bfd.h" +#include "elf/pru.h" +#endif + +/* Length of PRU instruction in bytes. */ +#define INSNLEN 4 + +/* Return a pointer to an pru_opcode struct for a given instruction + opcode, or NULL if there is an error. */ +const struct pru_opcode * +pru_find_opcode (unsigned long opcode) +{ + const struct pru_opcode *p; + const struct pru_opcode *op = NULL; + const struct pru_opcode *pseudo_op = NULL; + + for (p = pru_opcodes; p < &pru_opcodes[NUMOPCODES]; p++) + { + if ((p->mask & opcode) == p->match) + { + if ((p->pinfo & PRU_INSN_MACRO) == PRU_INSN_MACRO) + pseudo_op = p; + else if ((p->pinfo & PRU_INSN_LDI32) == PRU_INSN_LDI32) + /* ignore - should be caught with regular patterns */; + else + op = p; + } + } + + return pseudo_op ? pseudo_op : op; +} + +/* There are 32 regular registers, each with 8 possible subfield selectors. */ +#define NUMREGNAMES (32 * 8) + +static void +pru_print_insn_arg_reg (unsigned int r, unsigned int sel, + disassemble_info *info) +{ + 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); +} + +/* 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. */ +static int +pru_print_insn_arg (const char *argptr, + unsigned long opcode, bfd_vma address, + disassemble_info *info) +{ + long offs = 0; + unsigned long i = 0; + unsigned long io = 0; + + switch (*argptr) + { + case ',': + (*info->fprintf_func) (info->stream, "%c ", *argptr); + break; + case 'd': + pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode), + GET_INSN_FIELD (RDSEL, opcode), + info); + break; + case 'D': + /* The first 4 values for RDB and RSEL are the same, so we + can reuse some code. */ + pru_print_insn_arg_reg (GET_INSN_FIELD (RD, opcode), + GET_INSN_FIELD (RDB, opcode), + info); + break; + case 's': + pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode), + GET_INSN_FIELD (RS1SEL, opcode), + info); + break; + case 'S': + pru_print_insn_arg_reg (GET_INSN_FIELD (RS1, opcode), + RSEL_31_0, + info); + break; + case 'b': + io = GET_INSN_FIELD (IO, opcode); + + if (io) + { + i = GET_INSN_FIELD (IMM8, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + } + else + { + pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode), + GET_INSN_FIELD (RS2SEL, opcode), + info); + } + break; + case 'B': + io = GET_INSN_FIELD (IO, opcode); + + if (io) + { + i = GET_INSN_FIELD (IMM8, opcode) + 1; + (*info->fprintf_func) (info->stream, "%ld", i); + } + else + { + pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode), + GET_INSN_FIELD (RS2SEL, opcode), + info); + } + break; + case 'j': + io = GET_INSN_FIELD (IO, opcode); + + if (io) + { + /* For the sake of pretty-printing, dump text addresses with + their "virtual" offset that we use for distinguishing + PMEM vs DMEM. This is needed for printing the correct text + labels. */ + bfd_vma text_offset = address & ~0x3fffff; + i = GET_INSN_FIELD (IMM16, opcode) * 4; + (*info->print_address_func) (i + text_offset, info); + } + else + { + pru_print_insn_arg_reg (GET_INSN_FIELD (RS2, opcode), + GET_INSN_FIELD (RS2SEL, opcode), + info); + } + break; + case 'W': + i = GET_INSN_FIELD (IMM16, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + case 'o': + offs = GET_BROFF_SIGNED (opcode) * 4; + (*info->print_address_func) (address + offs, info); + break; + case 'O': + offs = GET_INSN_FIELD (LOOP_JMPOFFS, opcode) * 4; + (*info->print_address_func) (address + offs, info); + break; + case 'l': + i = GET_BURSTLEN (opcode); + if (i < LSSBBO_BYTECOUNT_R0_BITS7_0) + (*info->fprintf_func) (info->stream, "%ld", i + 1); + else + { + i -= LSSBBO_BYTECOUNT_R0_BITS7_0; + (*info->fprintf_func) (info->stream, "r0.b%ld", i); + } + break; + case 'n': + i = GET_INSN_FIELD (XFR_LENGTH, opcode); + if (i < LSSBBO_BYTECOUNT_R0_BITS7_0) + (*info->fprintf_func) (info->stream, "%ld", i + 1); + else + { + i -= LSSBBO_BYTECOUNT_R0_BITS7_0; + (*info->fprintf_func) (info->stream, "r0.b%ld", i); + } + break; + case 'c': + i = GET_INSN_FIELD (CB, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + case 'w': + i = GET_INSN_FIELD (WAKEONSTATUS, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + case 'x': + i = GET_INSN_FIELD (XFR_WBA, opcode); + (*info->fprintf_func) (info->stream, "%ld", i); + break; + default: + (*info->fprintf_func) (info->stream, "unknown"); + break; + } + return 0; +} + +/* pru_disassemble does all the work of disassembling a PRU + instruction opcode. */ +static int +pru_disassemble (bfd_vma address, unsigned long opcode, + disassemble_info *info) +{ + const struct pru_opcode *op; + + info->bytes_per_line = INSNLEN; + info->bytes_per_chunk = INSNLEN; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + /* Find the major opcode and use this to disassemble + the instruction and its arguments. */ + op = pru_find_opcode (opcode); + + if (op != NULL) + { + (*info->fprintf_func) (info->stream, "%s", op->name); + + const char *argstr = op->args; + if (argstr != NULL && *argstr != '\0') + { + (*info->fprintf_func) (info->stream, "\t"); + while (*argstr != '\0') + { + pru_print_insn_arg (argstr, opcode, address, info); + ++argstr; + } + } + } + else + { + /* Handle undefined instructions. */ + info->insn_type = dis_noninsn; + (*info->fprintf_func) (info->stream, "0x%lx", opcode); + } + /* Tell the caller how far to advance the program counter. */ + return INSNLEN; +} + + +/* print_insn_pru is the main disassemble function for PRU. */ +int +print_insn_pru (bfd_vma address, disassemble_info *info) +{ + bfd_byte buffer[INSNLEN]; + int status; + + status = (*info->read_memory_func) (address, buffer, INSNLEN, info); + if (status == 0) + { + unsigned long insn; + insn = (unsigned long) bfd_getl32 (buffer); + status = pru_disassemble (address, insn, info); + } + else + { + (*info->memory_error_func) (status, address, info); + status = -1; + } + return status; +} diff --git a/opcodes/pru-opc.c b/opcodes/pru-opc.c new file mode 100644 index 0000000000..9b240a80de --- /dev/null +++ b/opcodes/pru-opc.c @@ -0,0 +1,236 @@ +/* TI PRU opcode list. + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + + This file is part of the GNU opcodes library. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this file; see the file COPYING. If not, write to the + Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +/* Source: + http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit */ + +#include "sysdep.h" +#include +#include "opcode/pru.h" + +/* Register string table. */ + +#define DECLARE_REG(name, index) \ + { #name ".b0", (index), RSEL_7_0 }, \ + { #name ".b1", (index), RSEL_15_8 }, \ + { #name ".b2", (index), RSEL_23_16 }, \ + { #name ".b3", (index), RSEL_31_24 }, \ + { #name ".w0", (index), RSEL_15_0 }, \ + { #name ".w1", (index), RSEL_23_8 }, \ + { #name ".w2", (index), RSEL_31_16 }, \ + { #name , (index), RSEL_31_0 } + +const struct pru_reg pru_regs[] = { + /* Standard register names. */ + DECLARE_REG (r0, 0), + DECLARE_REG (r1, 1), + DECLARE_REG (sp, 2), /* Stack pointer. */ + DECLARE_REG (ra, 3), /* Return address. */ + DECLARE_REG (fp, 4), /* Frame pointer. */ + DECLARE_REG (r5, 5), + DECLARE_REG (r6, 6), + DECLARE_REG (r7, 7), + DECLARE_REG (r8, 8), + DECLARE_REG (r9, 9), + DECLARE_REG (r10, 10), + DECLARE_REG (r11, 11), + DECLARE_REG (r12, 12), + DECLARE_REG (r13, 13), + DECLARE_REG (r14, 14), + DECLARE_REG (r15, 15), + DECLARE_REG (r16, 16), + DECLARE_REG (r17, 17), + DECLARE_REG (r18, 18), + DECLARE_REG (r19, 19), + DECLARE_REG (r20, 20), + DECLARE_REG (r21, 21), + DECLARE_REG (r22, 22), + DECLARE_REG (r23, 23), + DECLARE_REG (r24, 24), + DECLARE_REG (r25, 25), + DECLARE_REG (r26, 26), + DECLARE_REG (r27, 27), + DECLARE_REG (r28, 28), + DECLARE_REG (r29, 29), + DECLARE_REG (r30, 30), + DECLARE_REG (r31, 31), + + /* Alternative names for special registers. */ + DECLARE_REG (r2, 2), + DECLARE_REG (r3, 3), + DECLARE_REG (r4, 4) +}; + +#define PRU_NUM_REGS \ + ((sizeof pru_regs) / (sizeof (pru_regs[0]))) +const int pru_num_regs = PRU_NUM_REGS; + +#undef PRU_NUM_REGS + +/* This is the opcode table used by the PRU GNU as, disassembler + and soon GDB. */ +const struct pru_opcode pru_opcodes[] = +{ + /* { name, args, + match, mask, pinfo, overflow_msg } */ +#define DECLARE_FORMAT1_OPCODE(str, subop) \ + { #str, prui_ ## str, "d,s,b", \ + OP_MATCH_ ## subop, OP_MASK_FMT1_OP | OP_MASK_SUBOP, 0, \ + unsigned_immed8_overflow } + + DECLARE_FORMAT1_OPCODE (add, ADD), + DECLARE_FORMAT1_OPCODE (adc, ADC), + DECLARE_FORMAT1_OPCODE (sub, SUB), + DECLARE_FORMAT1_OPCODE (suc, SUC), + DECLARE_FORMAT1_OPCODE (lsl, LSL), + DECLARE_FORMAT1_OPCODE (lsr, LSR), + DECLARE_FORMAT1_OPCODE (rsb, RSB), + DECLARE_FORMAT1_OPCODE (rsc, RSC), + DECLARE_FORMAT1_OPCODE (and, AND), + DECLARE_FORMAT1_OPCODE (or, OR), + DECLARE_FORMAT1_OPCODE (xor, XOR), + DECLARE_FORMAT1_OPCODE (min, MIN), + DECLARE_FORMAT1_OPCODE (max, MAX), + DECLARE_FORMAT1_OPCODE (clr, CLR), + DECLARE_FORMAT1_OPCODE (set, SET), + + { "not", prui_not, "d,s", + OP_MATCH_NOT | OP_MASK_IO, + OP_MASK_FMT1_OP | OP_MASK_SUBOP | OP_MASK_IO, 0, no_overflow}, + + { "jmp", prui_jmp, "j", + OP_MATCH_JMP, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, unsigned_immed16_overflow}, + { "jal", prui_jal, "d,j", + OP_MATCH_JAL, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, unsigned_immed16_overflow}, + { "ldi", prui_ldi, "d,W", + OP_MATCH_LDI, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, unsigned_immed16_overflow}, + { "halt", prui_halt, "", + OP_MATCH_HALT, 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}, + + { "xin", prui_xin, "x,D,n", + OP_MATCH_XIN, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow}, + { "xout", prui_xout, "x,D,n", + OP_MATCH_XOUT, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow}, + { "xchg", prui_xchg, "x,D,n", + OP_MATCH_XCHG, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow}, + { "sxin", prui_sxin, "x,D,n", + OP_MATCH_SXIN, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow}, + { "sxout", prui_sxout, "x,D,n", + OP_MATCH_SXOUT, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow}, + { "sxchg", prui_sxchg, "x,D,n", + OP_MATCH_SXCHG, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow}, + + { "loop", prui_loop, "O,B", + OP_MATCH_LOOP, OP_MASK_LOOP_OP, 0, unsigned_immed8_overflow}, + { "iloop", prui_loop, "O,B", + OP_MATCH_ILOOP, OP_MASK_LOOP_OP, 0, unsigned_immed8_overflow}, + + { "qbgt", prui_qbgt, "o,s,b", + OP_MATCH_QBGT, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + { "qbge", prui_qbge, "o,s,b", + OP_MATCH_QBGE, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + { "qblt", prui_qblt, "o,s,b", + OP_MATCH_QBLT, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + { "qble", prui_qble, "o,s,b", + OP_MATCH_QBLE, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + { "qbeq", prui_qbeq, "o,s,b", + OP_MATCH_QBEQ, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + { "qbne", prui_qbne, "o,s,b", + OP_MATCH_QBNE, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + { "qba", prui_qba, "o", + OP_MATCH_QBA, OP_MASK_FMT4_OP | OP_MASK_CMP, 0, qbranch_target_overflow}, + + { "qbbs", prui_qbbs, "o,s,b", + OP_MATCH_QBBS, OP_MASK_FMT5_OP | OP_MASK_BCMP, 0, qbranch_target_overflow}, + { "qbbc", prui_qbbc, "o,s,b", + OP_MATCH_QBBC, OP_MASK_FMT5_OP | OP_MASK_BCMP, 0, qbranch_target_overflow}, + + { "lbbo", prui_lbbo, "D,S,b,l", + OP_MATCH_LBBO, OP_MASK_FMT6AB_OP | OP_MASK_LOADSTORE, 0, + unsigned_immed8_overflow}, + { "sbbo", prui_sbbo, "D,S,b,l", + OP_MATCH_SBBO, OP_MASK_FMT6AB_OP | OP_MASK_LOADSTORE, 0, + unsigned_immed8_overflow}, + { "lbco", prui_lbco, "D,c,b,l", + OP_MATCH_LBCO, OP_MASK_FMT6CD_OP | OP_MASK_LOADSTORE, 0, + unsigned_immed8_overflow}, + { "sbco", prui_sbco, "D,c,b,l", + OP_MATCH_SBCO, OP_MASK_FMT6CD_OP | OP_MASK_LOADSTORE, 0, + unsigned_immed8_overflow}, + + /* Fill in the default values for the real-instruction arguments. + The assembler will not do it! */ + { "nop", prui_or, "", + OP_MATCH_OR + | (RSEL_31_0 << OP_SH_RS2SEL) | (0 << OP_SH_RS2) + | (RSEL_31_0 << OP_SH_RS1SEL) | (0 << OP_SH_RS1) + | (RSEL_31_0 << OP_SH_RDSEL) | (0 << OP_SH_RD), + OP_MASK_FMT1_OP | OP_MASK_SUBOP + | OP_MASK_RS2SEL | OP_MASK_RS2 | OP_MASK_RS1SEL | OP_MASK_RS1 + | OP_MASK_RDSEL | OP_MASK_RD | OP_MASK_IO, + PRU_INSN_MACRO, no_overflow}, + { "mov", prui_or, "d,s", + OP_MATCH_OR | (0 << OP_SH_IMM8) | OP_MASK_IO, + OP_MASK_FMT1_OP | OP_MASK_SUBOP | OP_MASK_IMM8 | OP_MASK_IO, + PRU_INSN_MACRO, no_overflow}, + { "ret", prui_jmp, "", + OP_MATCH_JMP + | (RSEL_31_16 << OP_SH_RS2SEL) | (3 << OP_SH_RS2), + OP_MASK_FMT2_OP | OP_MASK_SUBOP + | OP_MASK_RS2SEL | OP_MASK_RS2 | OP_MASK_IO, + PRU_INSN_MACRO, unsigned_immed16_overflow}, + { "call", prui_jal, "j", + OP_MATCH_JAL + | (RSEL_31_16 << OP_SH_RDSEL) | (3 << OP_SH_RD), + OP_MASK_FMT2_OP | OP_MASK_SUBOP + | OP_MASK_RDSEL | OP_MASK_RD, + PRU_INSN_MACRO, unsigned_immed16_overflow}, + + { "wbc", prui_qbbs, "s,b", + OP_MATCH_QBBS | (0 << OP_SH_BROFF98) | (0 << OP_SH_BROFF70), + OP_MASK_FMT5_OP | OP_MASK_BCMP | OP_MASK_BROFF, + PRU_INSN_MACRO, qbranch_target_overflow}, + { "wbs", prui_qbbc, "s,b", + OP_MATCH_QBBC | (0 << OP_SH_BROFF98) | (0 << OP_SH_BROFF70), + OP_MASK_FMT5_OP | OP_MASK_BCMP | OP_MASK_BROFF, + PRU_INSN_MACRO, qbranch_target_overflow}, + + { "fill", prui_xin, "D,n", + OP_MATCH_XIN | (254 << OP_SH_XFR_WBA), + OP_MASK_XFR_OP | OP_MASK_XFR_WBA, + PRU_INSN_MACRO, unsigned_immed8_overflow}, + { "zero", prui_xin, "D,n", + OP_MATCH_XIN | (255 << OP_SH_XFR_WBA), + OP_MASK_XFR_OP | OP_MASK_XFR_WBA, + PRU_INSN_MACRO, unsigned_immed8_overflow}, + + { "ldi32", prui_ldi, "R,i", + OP_MATCH_LDI, OP_MASK_FMT2_OP | OP_MASK_SUBOP, + PRU_INSN_LDI32, unsigned_immed32_overflow}, +}; + +#define PRU_NUM_OPCODES \ + ((sizeof pru_opcodes) / (sizeof (pru_opcodes[0]))) +const int bfd_pru_num_opcodes = PRU_NUM_OPCODES; + +#undef PRU_NUM_OPCODES -- 2.11.0