diff options
Diffstat (limited to 'sim/pru/interp.c')
-rw-r--r-- | sim/pru/interp.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/sim/pru/interp.c b/sim/pru/interp.c new file mode 100644 index 00000000000..0e783c121cb --- /dev/null +++ b/sim/pru/interp.c @@ -0,0 +1,848 @@ +/* Simulator for the Texas Instruments PRU processor + Copyright 2009-2019 Free Software Foundation, Inc. + Inspired by the Microblaze simulator + Contributed by Dimitar Dimitrov <dimitar@dinux.eu> + + This file is part of the simulators. + + This program 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 of the License, or + (at your option) any later version. + + This program 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 program; if not, see <http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include "bfd.h" +#include "gdb/callback.h" +#include "libiberty.h" +#include "gdb/remote-sim.h" +#include "sim-main.h" +#include "sim-assert.h" +#include "sim-options.h" +#include "sim-syscall.h" +#include "pru.h" + +/* DMEM zero address is perfectly valid. But if CRT leaves the first word + alone, we can use it as a trap to catch NULL pointer access. */ +static bfd_boolean abort_on_dmem_zero_access; + +enum { + OPTION_ERROR_NULL_DEREF = OPTION_START, +}; + +/* Extract (from PRU endianess) and return an integer in HOST's endianness. */ +static uint32_t +pru_extract_unsigned_integer (uint8_t *addr, size_t len) +{ + uint32_t retval; + uint8_t *p; + uint8_t *startaddr = addr; + uint8_t *endaddr = startaddr + len; + + /* Start at the most significant end of the integer, and work towards + the least significant. */ + retval = 0; + + for (p = endaddr; p > startaddr;) + retval = (retval << 8) | * -- p; + return retval; +} + +/* Store "val" (which is in HOST's endianess) into "addr" + (using PRU's endianness). */ +static void +pru_store_unsigned_integer (uint8_t *addr, size_t len, uint32_t val) +{ + uint8_t *p; + uint8_t *startaddr = (uint8_t *)addr; + uint8_t *endaddr = startaddr + len; + + for (p = startaddr; p < endaddr;) + { + *p++ = val & 0xff; + val >>= 8; + } +} + +/* Extract a field value from CPU register using the given REGSEL selector. + + Byte number maps directly to first values of RSEL, so we can + safely use "regsel" as a register byte number (0..3). */ +static inline uint32_t +extract_regval (uint32_t val, uint32_t regsel) +{ + ASSERT (RSEL_7_0 == 0); + ASSERT (RSEL_15_8 == 1); + ASSERT (RSEL_23_16 == 2); + ASSERT (RSEL_31_24 == 3); + + switch (regsel) + { + case RSEL_7_0: return (val >> 0) & 0xff; + case RSEL_15_8: return (val >> 8) & 0xff; + case RSEL_23_16: return (val >> 16) & 0xff; + case RSEL_31_24: return (val >> 24) & 0xff; + case RSEL_15_0: return (val >> 0) & 0xffff; + case RSEL_23_8: return (val >> 8) & 0xffff; + case RSEL_31_16: return (val >> 16) & 0xffff; + case RSEL_31_0: return val; + default: sim_io_error (NULL, "invalid regsel"); + } +} + +/* Write a value into CPU subregister pointed by reg and regsel. */ +static inline void +write_regval (uint32_t val, uint32_t *reg, uint32_t regsel) +{ + uint32_t mask, sh; + + switch (regsel) + { + case RSEL_7_0: mask = (0xffu << 0); sh = 0; break; + case RSEL_15_8: mask = (0xffu << 8); sh = 8; break; + case RSEL_23_16: mask = (0xffu << 16); sh = 16; break; + case RSEL_31_24: mask = (0xffu << 24); sh = 24; break; + case RSEL_15_0: mask = (0xffffu << 0); sh = 0; break; + case RSEL_23_8: mask = (0xffffu << 8); sh = 8; break; + case RSEL_31_16: mask = (0xffffu << 16); sh = 16; break; + case RSEL_31_0: mask = 0xffffffffu; sh = 0; break; + default: sim_io_error (NULL, "invalid regsel"); + } + + *reg = (*reg & ~mask) | ((val << sh) & mask); +} + +/* Convert the given IMEM word address to a regular byte address used by the + GNU ELF container. */ +static uint32_t +imem_wordaddr_to_byteaddr (SIM_CPU *cpu, uint16_t wa) +{ + return (((uint32_t) wa << 2) & IMEM_ADDR_MASK) | PC_ADDR_SPACE_MARKER; +} + +/* Convert the given ELF text byte address to IMEM word address. */ +static uint16_t +imem_byteaddr_to_wordaddr (SIM_CPU *cpu, uint32_t ba) +{ + return (ba >> 2) & 0xffff; +} + + +/* Store "nbytes" into DMEM "addr" from CPU register file, starting with + register "regn", and byte "regb" within it. */ +static inline void +pru_reg2dmem (SIM_CPU *cpu, uint32_t addr, unsigned int nbytes, + int regn, int regb) +{ + /* GDB assumes unconditional access to all memories, so enable additional + checks only in standalone mode. */ + bool standalone = (STATE_OPEN_KIND (CPU_STATE (cpu)) == SIM_OPEN_STANDALONE); + + if (abort_on_dmem_zero_access && addr < 4) + { + sim_core_signal (CPU_STATE (cpu), cpu, PC_byteaddr, write_map, + nbytes, addr, write_transfer, + sim_core_unmapped_signal); + } + else if (standalone && ((addr >= PC_ADDR_SPACE_MARKER) + || (addr + nbytes > PC_ADDR_SPACE_MARKER))) + { + sim_core_signal (CPU_STATE (cpu), cpu, PC_byteaddr, write_map, + nbytes, addr, write_transfer, + sim_core_unmapped_signal); + } + else if ((regn * 4 + regb + nbytes) > (32 * 4)) + { + sim_io_eprintf (CPU_STATE (cpu), + "SBBO/SBCO with invalid store data length\n"); + RAISE_SIGILL (CPU_STATE (cpu)); + } + else + { + TRACE_MEMORY (cpu, "write of %d bytes to %08x", nbytes, addr); + while (nbytes--) + { + sim_core_write_1 (cpu, + PC_byteaddr, + write_map, + addr++, + extract_regval (CPU.regs[regn], regb)); + + if (++regb >= 4) + { + regb = 0; + regn++; + } + } + } +} + +/* Load "nbytes" from DMEM "addr" into CPU register file, starting with + register "regn", and byte "regb" within it. */ +static inline void +pru_dmem2reg (SIM_CPU *cpu, uint32_t addr, unsigned int nbytes, + int regn, int regb) +{ + /* GDB assumes unconditional access to all memories, so enable additional + checks only in standalone mode. */ + bool standalone = (STATE_OPEN_KIND (CPU_STATE (cpu)) == SIM_OPEN_STANDALONE); + + if (abort_on_dmem_zero_access && addr < 4) + { + sim_core_signal (CPU_STATE (cpu), cpu, PC_byteaddr, read_map, + nbytes, addr, read_transfer, + sim_core_unmapped_signal); + } + else if (standalone && ((addr >= PC_ADDR_SPACE_MARKER) + || (addr + nbytes > PC_ADDR_SPACE_MARKER))) + { + /* This check is necessary because our IMEM "address space" + is not really accessible, yet we have mapped it as a generic + memory space. */ + sim_core_signal (CPU_STATE (cpu), cpu, PC_byteaddr, read_map, + nbytes, addr, read_transfer, + sim_core_unmapped_signal); + } + else if ((regn * 4 + regb + nbytes) > (32 * 4)) + { + sim_io_eprintf (CPU_STATE (cpu), + "LBBO/LBCO with invalid load data length\n"); + RAISE_SIGILL (CPU_STATE (cpu)); + } + else + { + unsigned int b; + TRACE_MEMORY (cpu, "read of %d bytes from %08x", nbytes, addr); + while (nbytes--) + { + b = sim_core_read_1 (cpu, PC_byteaddr, read_map, addr++); + + /* Reuse the fact the Register Byte Number maps directly to RSEL. */ + ASSERT (RSEL_7_0 == 0); + write_regval (b, &CPU.regs[regn], regb); + + if (++regb >= 4) + { + regb = 0; + regn++; + } + } + } +} + +/* Set reset values of general-purpose registers. */ +static void +set_initial_gprs (SIM_CPU *cpu) +{ + int i; + + /* Set up machine just out of reset. */ + CPU_PC_SET (cpu, 0); + PC_ADDR_SPACE_MARKER = IMEM_ADDR_DEFAULT; /* from default linker script? */ + + /* Clean out the GPRs. */ + for (i = 0; i < ARRAY_SIZE (CPU.regs); i++) + CPU.regs[i] = 0; + for (i = 0; i < ARRAY_SIZE (CPU.macregs); i++) + CPU.macregs[i] = 0; + + CPU.loop.looptop = CPU.loop.loopend = 0; + CPU.loop.loop_in_progress = 0; + CPU.loop.loop_counter = 0; + + CPU.carry = 0; + CPU.insts = 0; + CPU.cycles = 0; + + /* AM335x should provide sane defaults. */ + CPU.ctable[0] = 0x00020000; + CPU.ctable[1] = 0x48040000; + CPU.ctable[2] = 0x4802a000; + CPU.ctable[3] = 0x00030000; + CPU.ctable[4] = 0x00026000; + CPU.ctable[5] = 0x48060000; + CPU.ctable[6] = 0x48030000; + CPU.ctable[7] = 0x00028000; + CPU.ctable[8] = 0x46000000; + CPU.ctable[9] = 0x4a100000; + CPU.ctable[10] = 0x48318000; + CPU.ctable[11] = 0x48022000; + CPU.ctable[12] = 0x48024000; + CPU.ctable[13] = 0x48310000; + CPU.ctable[14] = 0x481cc000; + CPU.ctable[15] = 0x481d0000; + CPU.ctable[16] = 0x481a0000; + CPU.ctable[17] = 0x4819c000; + CPU.ctable[18] = 0x48300000; + CPU.ctable[19] = 0x48302000; + CPU.ctable[20] = 0x48304000; + CPU.ctable[21] = 0x00032400; + CPU.ctable[22] = 0x480c8000; + CPU.ctable[23] = 0x480ca000; + CPU.ctable[24] = 0x00000000; + CPU.ctable[25] = 0x00002000; + CPU.ctable[26] = 0x0002e000; + CPU.ctable[27] = 0x00032000; + CPU.ctable[28] = 0x00000000; + CPU.ctable[29] = 0x49000000; + CPU.ctable[30] = 0x40000000; + CPU.ctable[31] = 0x80000000; +} + +/* Map regsel selector to subregister field width. */ +static inline unsigned int +regsel_width (uint32_t regsel) +{ + switch (regsel) + { + case RSEL_7_0: return 8; + case RSEL_15_8: return 8; + case RSEL_23_16: return 8; + case RSEL_31_24: return 8; + case RSEL_15_0: return 16; + case RSEL_23_8: return 16; + case RSEL_31_16: return 16; + case RSEL_31_0: return 32; + default: sim_io_error (NULL, "invalid regsel"); + } +} + +/* Handle XIN instruction addressing the MAC peripheral. */ +static void +pru_sim_xin_mac (SIM_DESC sd, SIM_CPU *cpu, unsigned int rd_regn, + unsigned int rdb, unsigned int length) +{ + if (rd_regn < 25 || (rd_regn * 4 + rdb + length) > (27 + 1) * 4) + sim_io_error (sd, "XIN MAC: invalid transfer regn=%u.%u, length=%u\n", + rd_regn, rdb, length); + + /* Copy from MAC to PRU regs. Ranges have been validated above. */ + while (length--) + { + write_regval (CPU.macregs[rd_regn - 25] >> (rdb * 8), + &CPU.regs[rd_regn], + rdb); + if (++rdb == 4) + { + rdb = 0; + rd_regn++; + } + } +} + +/* Handle XIN instruction. */ +static void +pru_sim_xin (SIM_DESC sd, SIM_CPU *cpu, unsigned int wba, + unsigned int rd_regn, unsigned int rdb, unsigned int length) +{ + if (wba == 0) + { + pru_sim_xin_mac (sd, cpu, rd_regn, rdb, length); + } + else if (wba == XFRID_SCRATCH_BANK_0 || wba == XFRID_SCRATCH_BANK_1 + || wba == XFRID_SCRATCH_BANK_2 || wba == XFRID_SCRATCH_BANK_PEER) + { + while (length--) + { + unsigned int val; + + val = extract_regval (CPU.scratchpads[wba][rd_regn], rdb); + write_regval (val, &CPU.regs[rd_regn], rdb); + if (++rdb == 4) + { + rdb = 0; + rd_regn++; + } + } + } + else if (wba == 254 || wba == 255) + { + /* FILL/ZERO pseudos implemented via XIN. */ + unsigned int fillbyte = (wba == 254) ? 0xff : 0x00; + while (length--) + { + write_regval (fillbyte, &CPU.regs[rd_regn], rdb); + if (++rdb == 4) + { + rdb = 0; + rd_regn++; + } + } + } + else + { + sim_io_error (sd, "XIN: XFR device %d not supported.\n", wba); + } +} + +/* Handle XOUT instruction addressing the MAC peripheral. */ +static void +pru_sim_xout_mac (SIM_DESC sd, SIM_CPU *cpu, unsigned int rd_regn, + unsigned int rdb, unsigned int length) +{ + const int modereg_accessed = (rd_regn == 25); + + /* Multiple Accumulate. */ + if (rd_regn < 25 || (rd_regn * 4 + rdb + length) > (27 + 1) * 4) + sim_io_error (sd, "XOUT MAC: invalid transfer regn=%u.%u, length=%u\n", + rd_regn, rdb, length); + + /* Copy from PRU to MAC regs. Ranges have been validated above. */ + while (length--) + { + write_regval (CPU.regs[rd_regn] >> (rdb * 8), + &CPU.macregs[rd_regn - 25], + rdb); + if (++rdb == 4) + { + rdb = 0; + rd_regn++; + } + } + + if (modereg_accessed + && (CPU.macregs[PRU_MACREG_MODE] & MAC_R25_MAC_MODE_MASK)) + { + /* MUL/MAC operands are sampled every XOUT in multiply and + accumulate mode. */ + uint64_t prod, oldsum, sum; + CPU.macregs[PRU_MACREG_OP_0] = CPU.regs[28]; + CPU.macregs[PRU_MACREG_OP_1] = CPU.regs[29]; + + prod = CPU.macregs[PRU_MACREG_OP_0]; + prod *= (uint64_t)CPU.macregs[PRU_MACREG_OP_1]; + + oldsum = CPU.macregs[PRU_MACREG_ACC_L]; + oldsum += (uint64_t)CPU.macregs[PRU_MACREG_ACC_H] << 32; + sum = oldsum + prod; + + CPU.macregs[PRU_MACREG_PROD_L] = sum & 0xfffffffful; + CPU.macregs[PRU_MACREG_PROD_H] = sum >> 32; + CPU.macregs[PRU_MACREG_ACC_L] = CPU.macregs[PRU_MACREG_PROD_L]; + CPU.macregs[PRU_MACREG_ACC_H] = CPU.macregs[PRU_MACREG_PROD_H]; + + if (oldsum > sum) + CPU.macregs[PRU_MACREG_MODE] |= MAC_R25_ACC_CARRY_MASK; + } + if (modereg_accessed + && (CPU.macregs[PRU_MACREG_MODE] & MAC_R25_ACC_CARRY_MASK)) + { + /* store 1 to clear. */ + CPU.macregs[PRU_MACREG_MODE] &= ~MAC_R25_ACC_CARRY_MASK; + CPU.macregs[PRU_MACREG_ACC_L] = 0; + CPU.macregs[PRU_MACREG_ACC_H] = 0; + } + +} + +/* Handle XOUT instruction. */ +static void +pru_sim_xout (SIM_DESC sd, SIM_CPU *cpu, unsigned int wba, + unsigned int rd_regn, unsigned int rdb, unsigned int length) +{ + if (wba == 0) + { + pru_sim_xout_mac (sd, cpu, rd_regn, rdb, length); + } + else if (wba == XFRID_SCRATCH_BANK_0 || wba == XFRID_SCRATCH_BANK_1 + || wba == XFRID_SCRATCH_BANK_2 || wba == XFRID_SCRATCH_BANK_PEER) + { + while (length--) + { + unsigned int val; + + val = extract_regval (CPU.regs[rd_regn], rdb); + write_regval (val, &CPU.scratchpads[wba][rd_regn], rdb); + if (++rdb == 4) + { + rdb = 0; + rd_regn++; + } + } + } + else + sim_io_error (sd, "XOUT: XFR device %d not supported.\n", wba); +} + +/* Handle XCHG instruction. */ +static void +pru_sim_xchg (SIM_DESC sd, SIM_CPU *cpu, unsigned int wba, + unsigned int rd_regn, unsigned int rdb, unsigned int length) +{ + if (wba == XFRID_SCRATCH_BANK_0 || wba == XFRID_SCRATCH_BANK_1 + || wba == XFRID_SCRATCH_BANK_2 || wba == XFRID_SCRATCH_BANK_PEER) + { + while (length--) + { + unsigned int valr, vals; + + valr = extract_regval (CPU.regs[rd_regn], rdb); + vals = extract_regval (CPU.scratchpads[wba][rd_regn], rdb); + write_regval (valr, &CPU.scratchpads[wba][rd_regn], rdb); + write_regval (vals, &CPU.regs[rd_regn], rdb); + if (++rdb == 4) + { + rdb = 0; + rd_regn++; + } + } + } + else + sim_io_error (sd, "XOUT: XFR device %d not supported.\n", wba); +} + +/* Handle syscall simulation. Its ABI is specific to the GNU simulator. */ +static void +pru_sim_syscall (SIM_DESC sd, SIM_CPU *cpu) +{ + /* If someday TI confirms that the "reserved" HALT opcode fields + can be used for extra arguments, then maybe we can embed + the syscall number there. Until then, let's use R1. */ + const uint32_t syscall_num = CPU.regs[1]; + long ret; + + ret = sim_syscall (cpu, syscall_num, + CPU.regs[14], CPU.regs[15], + CPU.regs[16], CPU.regs[17]); + CPU.regs[14] = ret; +} + +/* Simulate one instruction. */ +static void +sim_step_once (SIM_DESC sd) +{ + SIM_CPU *cpu = STATE_CPU (sd, 0); + const struct pru_opcode *op; + uint32_t inst; + uint32_t _RDVAL, OP2; /* intermediate values. */ + int rd_is_modified = 0; /* RD modified and must be stored back. */ + + /* Fetch the initial instruction that we'll decode. */ + inst = sim_core_read_4 (cpu, PC_byteaddr, exec_map, PC_byteaddr); + TRACE_MEMORY (cpu, "read of insn 0x%08x from %08x", inst, PC_byteaddr); + + op = pru_find_opcode (inst); + + if (!op) + { + sim_io_eprintf (sd, "Unknown instruction 0x%04x\n", inst); + RAISE_SIGILL (sd); + } + else + { + TRACE_DISASM (cpu, PC_byteaddr); + + /* In multiply-only mode, R28/R29 operands are sampled on every clock + cycle. */ + if ((CPU.macregs[PRU_MACREG_MODE] & MAC_R25_MAC_MODE_MASK) == 0) + { + CPU.macregs[PRU_MACREG_OP_0] = CPU.regs[28]; + CPU.macregs[PRU_MACREG_OP_1] = CPU.regs[29]; + } + + switch (op->type) + { +/* Helper macro to improve clarity of pru.isa. The empty while is a + guard against using RD as a left-hand side value. */ +#define RD do { } while (0); rd_is_modified = 1; _RDVAL +#define INSTRUCTION(NAME, ACTION) \ + case prui_ ## NAME: \ + ACTION; \ + break; +#include "pru.isa" +#undef INSTRUCTION +#undef RD + + default: + RAISE_SIGILL (sd); + } + + if (rd_is_modified) + write_regval (_RDVAL, &CPU.regs[RD_REGN], RDSEL); + + /* Don't treat r30 and r31 as regular registers, they are I/O! */ + CPU.regs[30] = 0; + CPU.regs[31] = 0; + + /* Handle PC match of loop end. */ + if (LOOP_IN_PROGRESS && (PC == LOOPEND)) + { + SIM_ASSERT (LOOPCNT > 0); + if (--LOOPCNT == 0) + LOOP_IN_PROGRESS = 0; + else + PC = LOOPTOP; + } + + /* In multiply-only mode, MAC does multiplication every cycle. */ + if ((CPU.macregs[PRU_MACREG_MODE] & MAC_R25_MAC_MODE_MASK) == 0) + { + uint64_t prod; + prod = CPU.macregs[PRU_MACREG_OP_0]; + prod *= (uint64_t)CPU.macregs[PRU_MACREG_OP_1]; + CPU.macregs[PRU_MACREG_PROD_L] = prod & 0xfffffffful; + CPU.macregs[PRU_MACREG_PROD_H] = prod >> 32; + + /* Clear the MAC accumulator when in normal mode. */ + CPU.macregs[PRU_MACREG_ACC_L] = 0; + CPU.macregs[PRU_MACREG_ACC_H] = 0; + } + + /* Update cycle counts. */ + CPU.insts += 1; /* One instruction completed ... */ + CPU.cycles += 1; /* ... and it takes a single cycle. */ + + /* Account for memory access latency with a reasonable estimate. + No distinction is currently made between SRAM, DRAM and generic + L3 slaves. */ + if (op->type == prui_lbbo || op->type == prui_sbbo + || op->type == prui_lbco || op->type == prui_sbco) + CPU.cycles += 2; + + } +} + +/* Implement standard sim_engine_run function. */ +void +sim_engine_run (SIM_DESC sd, + int next_cpu_nr, /* ignore */ + int nr_cpus, /* ignore */ + int siggnal) /* ignore */ +{ + while (1) + { + sim_step_once (sd); + if (sim_events_tick (sd)) + sim_events_process (sd); + } +} + + +/* Implement callback for standard CPU_PC_FETCH routine. */ +static sim_cia +pru_pc_get (sim_cpu *cpu) +{ + /* Present PC as byte address. */ + return imem_wordaddr_to_byteaddr (cpu, cpu->pru_cpu.pc); +} + +/* Implement callback for standard CPU_PC_STORE routine. */ +static void +pru_pc_set (sim_cpu *cpu, sim_cia pc) +{ + /* PC given as byte address. */ + cpu->pru_cpu.pc = imem_byteaddr_to_wordaddr (cpu, pc); +} + + +/* Implement callback for standard CPU_REG_STORE routine. */ +static int +pru_store_register (SIM_CPU *cpu, int rn, unsigned char *memory, int length) +{ + if (rn < NUM_REGS && rn >= 0) + { + if (length == 4) + { + /* Misalignment safe. */ + long ival = pru_extract_unsigned_integer (memory, 4); + if (rn < 32) + CPU.regs[rn] = ival; + else + pru_pc_set (cpu, ival); + return 4; + } + else + return 0; + } + else + return 0; +} + +/* Implement callback for standard CPU_REG_FETCH routine. */ +static int +pru_fetch_register (SIM_CPU *cpu, int rn, unsigned char *memory, int length) +{ + long ival; + + if (rn < NUM_REGS && rn >= 0) + { + if (length == 4) + { + if (rn < 32) + ival = CPU.regs[rn]; + else + ival = pru_pc_get (cpu); + + /* Misalignment-safe. */ + pru_store_unsigned_integer (memory, 4, ival); + return 4; + } + else + return 0; + } + else + return 0; +} + +static void +free_state (SIM_DESC sd) +{ + if (STATE_MODULES (sd) != NULL) + sim_module_uninstall (sd); + sim_cpu_free_all (sd); + sim_state_free (sd); +} + +/* Declare the PRU option handler. */ +static DECLARE_OPTION_HANDLER (pru_option_handler); + +/* Implement the PRU option handler. */ +static SIM_RC +pru_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt, char *arg, + int is_command) +{ + switch (opt) + { + case OPTION_ERROR_NULL_DEREF: + abort_on_dmem_zero_access = TRUE; + return SIM_RC_OK; + + default: + sim_io_eprintf (sd, "Unknown PRU option %d\n", opt); + return SIM_RC_FAIL; + } +} + +/* List of PRU-specific options. */ +static const OPTION pru_options[] = +{ + { {"error-null-deref", no_argument, NULL, OPTION_ERROR_NULL_DEREF}, + '\0', NULL, "Trap any access to DMEM address zero", + pru_option_handler, NULL }, + + { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL } +}; + +/* Implement standard sim_open function. */ +SIM_DESC +sim_open (SIM_OPEN_KIND kind, host_callback *cb, + struct bfd *abfd, char * const *argv) +{ + int i; + char c; + SIM_DESC sd = sim_state_alloc (kind, cb); + SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); + + /* The cpu data is kept in a separately allocated chunk of memory. */ + if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK) + { + free_state (sd); + return 0; + } + + if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) + { + free_state (sd); + return 0; + } + sim_add_option_table (sd, NULL, pru_options); + + /* The parser will print an error message for us, so we silently return. */ + if (sim_parse_args (sd, argv) != SIM_RC_OK) + { + free_state (sd); + return 0; + } + + /* Check for/establish a reference program image. */ + if (sim_analyze_program (sd, + (STATE_PROG_ARGV (sd) != NULL + ? *STATE_PROG_ARGV (sd) + : NULL), abfd) != SIM_RC_OK) + { + free_state (sd); + return 0; + } + + /* Configure/verify the target byte order and other runtime + configuration options. */ + if (sim_config (sd) != SIM_RC_OK) + { + sim_module_uninstall (sd); + return 0; + } + + if (sim_post_argv_init (sd) != SIM_RC_OK) + { + /* Uninstall the modules to avoid memory leaks, + file descriptor leaks, etc. */ + sim_module_uninstall (sd); + return 0; + } + + /* CPU specific initialization. */ + for (i = 0; i < MAX_NR_PROCESSORS; ++i) + { + SIM_CPU *cpu = STATE_CPU (sd, i); + + CPU_REG_STORE (cpu) = pru_store_register; + CPU_REG_FETCH (cpu) = pru_fetch_register; + CPU_PC_FETCH (cpu) = pru_pc_get; + CPU_PC_STORE (cpu) = pru_pc_set; + + set_initial_gprs (cpu); + } + + /* Allocate external memory if none specified by user. + Use address 4 here in case the user wanted address 0 unmapped. */ + if (sim_core_read_buffer (sd, NULL, read_map, &c, 4, 1) == 0) + { + sim_do_commandf (sd, "memory-region 0x%x,0x%x", + 0, + DMEM_DEFAULT_SIZE); + } + if (sim_core_read_buffer (sd, NULL, read_map, &c, IMEM_ADDR_DEFAULT, 1) == 0) + { + sim_do_commandf (sd, "memory-region 0x%x,0x%x", + IMEM_ADDR_DEFAULT, + IMEM_DEFAULT_SIZE); + } + + return sd; +} + +/* Implement standard sim_create_inferior function. */ +SIM_RC +sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd, + char * const *argv, char * const *env) +{ + SIM_CPU *cpu = STATE_CPU (sd, 0); + SIM_ADDR addr; + + addr = bfd_get_start_address (prog_bfd); + + sim_pc_set (cpu, addr); + PC_ADDR_SPACE_MARKER = addr & ~IMEM_ADDR_MASK; + + /* Standalone mode (i.e. `run`) will take care of the argv for us in + sim_open () -> sim_parse_args (). But in debug mode (i.e. 'target sim' + with `gdb`), we need to handle it because the user can change the + argv on the fly via gdb's 'run'. */ + if (STATE_PROG_ARGV (sd) != argv) + { + freeargv (STATE_PROG_ARGV (sd)); + STATE_PROG_ARGV (sd) = dupargv (argv); + } + + return SIM_RC_OK; +} |