sim: Add PRU simulator port

A simulator port for the TI PRU I/O processor.

v1: https://sourceware.org/ml/gdb-patches/2016-12/msg00143.html
v2: https://sourceware.org/ml/gdb-patches/2017-02/msg00397.html
v3: https://sourceware.org/ml/gdb-patches/2017-02/msg00516.html
v4: https://sourceware.org/ml/gdb-patches/2018-06/msg00484.html
v5: https://sourceware.org/ml/gdb-patches/2019-08/msg00584.html
v6: https://sourceware.org/ml/gdb-patches/2019-09/msg00036.html

gdb/ChangeLog:

	* NEWS: Mention new simulator port for PRU.

sim/ChangeLog:

	* MAINTAINERS: Add myself as PRU maintainer.
	* configure: Regenerated.
	* configure.tgt: Add PRU.

sim/common/ChangeLog:

	* gennltvals.sh: Add PRU libgloss target.
	* nltvals.def: Regenerate from the latest libgloss sources.

sim/pru/ChangeLog:

	* Makefile.in: New file.
	* aclocal.m4: Regenerated.
	* config.in: Regenerated.
	* configure: Regenerated.
	* configure.ac: New file.
	* interp.c: New file.
	* pru.h: New file.
	* pru.isa: New file.
	* sim-main.h: New file.
This commit is contained in:
Dimitar Dimitrov 2019-09-23 17:54:42 +01:00 committed by Andrew Burgess
parent f945dedfd3
commit ddd44b7053
19 changed files with 17775 additions and 0 deletions

View File

@ -1,3 +1,7 @@
2019-09-23 Dimitar Dimitrov <dimitar@dinux.eu>
* NEWS: Mention new simulator port for PRU.
2019-09-23 Christian Biesinger <cbiesinger@google.com>
* ada-exp.y (write_object_remaining): Update.

View File

@ -350,6 +350,10 @@ focus, winheight, +, -, >, <
both debugging standalone Cell/B.E. SPU applications and integrated debugging
of Cell/B.E. applications that use both the PPU and SPU architectures.
* New Simulators
TI PRU pru-*-elf
*** Changes in GDB 8.3
* GDB and GDBserver now support access to additional registers on

View File

@ -1,3 +1,9 @@
2019-09-23 Dimitar Dimitrov <dimitar@dinux.eu>
* MAINTAINERS: Add myself as PRU maintainer.
* configure: Regenerated.
* configure.tgt: Add PRU.
2019-09-20 Alan Modra <amodra@gmail.com>
* ppc/emul_generic.c (emul_add_tree_options): Delete old bfd code.

View File

@ -29,6 +29,7 @@ mips I-IV Maciej W. Rozycki <macro@linux-mips.org>
moxie Anthony Green <green@moxielogic.com>
msp430 Nick Clifton <nickc@redhat.com>
or1k Stafford Horne <shorne@gmail.com>
pru Dimitar Dimitrov <dimitar@dinux.eu>
sh (global maintainers)
sh64 Dave Brolley <brolley@redhat.com>
common Frank Ch. Eigler <fche@redhat.com>

View File

@ -1,3 +1,8 @@
2019-09-23 Dimitar Dimitrov <dimitar@dinux.eu>
* gennltvals.sh: Add PRU libgloss target.
* nltvals.def: Regenerate from the latest libgloss sources.
2019-06-13 Stafford Horne <shorne@gmail.com>
* cgen-accfp.c (unorderedsf, unordereddf): New functions.

View File

@ -95,3 +95,7 @@ $shell ${srccom}/gentvals.sh $target sys ${newlibroot}/$dir \
dir=libgloss target=lm32
$shell ${srccom}/gentvals.sh $target sys ${newlibroot}/$dir \
"syscall.h" 'SYS_[_[:alnum:]]*' "${cpp}"
dir=libgloss target=pru
$shell ${srccom}/gentvals.sh $target sys ${newlibroot}/$dir \
"syscall.h" 'SYS_[_[:alnum:]]*' "${cpp}"

View File

@ -574,3 +574,34 @@
/* end lm32 sys target macros */
#endif
#endif
#ifdef NL_TARGET_pru
#ifdef sys_defs
/* from syscall.h */
/* begin pru sys target macros */
{ "SYS_argc", 22 },
{ "SYS_argn", 24 },
{ "SYS_argnlen", 23 },
{ "SYS_argv", 13 },
{ "SYS_argvlen", 12 },
{ "SYS_chdir", 14 },
{ "SYS_chmod", 16 },
{ "SYS_close", 3 },
{ "SYS_exit", 1 },
{ "SYS_fstat", 10 },
{ "SYS_getpid", 8 },
{ "SYS_gettimeofday", 19 },
{ "SYS_kill", 9 },
{ "SYS_link", 21 },
{ "SYS_lseek", 6 },
{ "SYS_open", 2 },
{ "SYS_read", 4 },
{ "SYS_reconfig", 25 },
{ "SYS_stat", 15 },
{ "SYS_time", 18 },
{ "SYS_times", 20 },
{ "SYS_unlink", 7 },
{ "SYS_utime", 17 },
{ "SYS_write", 5 },
/* end pru sys target macros */
#endif
#endif

8
sim/configure vendored
View File

@ -686,6 +686,7 @@ mn10300
moxie
msp430
or1k
pru
rl78
rx
sh64
@ -3837,6 +3838,13 @@ subdirs="$subdirs aarch64"
subdirs="$subdirs or1k"
;;
pru*-*-*)
sim_arch=pru
subdirs="$subdirs pru"
;;
rl78-*-*)

View File

@ -79,6 +79,9 @@ case "${target}" in
or1k-*-* | or1knd-*-*)
SIM_ARCH(or1k)
;;
pru*-*-*)
SIM_ARCH(pru)
;;
rl78-*-*)
SIM_ARCH(rl78)
;;

11
sim/pru/ChangeLog Normal file
View File

@ -0,0 +1,11 @@
2019-09-23 Dimitar Dimitrov <dimitar@dinux.eu>
* Makefile.in: New file.
* aclocal.m4: Regenerated.
* config.in: Regenerated.
* configure: Regenerated.
* configure.ac: New file.
* interp.c: New file.
* pru.h: New file.
* pru.isa: New file.
* sim-main.h: New file.

29
sim/pru/Makefile.in Normal file
View File

@ -0,0 +1,29 @@
# Makefile template for Configure for the PRU sim library.
# Copyright (C) 1990-2019 Free Software Foundation, Inc.
# Written by Dimitar Dimitrov <dimitar@dinux.eu>
#
# Based on the MCore sim library
#
# 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/>.
## COMMON_PRE_CONFIG_FRAG
SIM_OBJS = \
$(SIM_NEW_COMMON_OBJS) \
interp.o \
sim-resume.o
NL_TARGET = -DNL_TARGET_pru
## COMMON_POST_CONFIG_FRAG

119
sim/pru/aclocal.m4 vendored Normal file
View File

@ -0,0 +1,119 @@
# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
# Copyright (C) 1996-2017 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ([2.52])dnl
m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
m4_define([_AM_COND_VALUE_$1], [$2])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 2003-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
# From Jim Meyering
# Copyright (C) 1996-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MAINTAINER_MODE([DEFAULT-MODE])
# ----------------------------------
# Control maintainer-specific portions of Makefiles.
# Default is to disable them, unless 'enable' is passed literally.
# For symmetry, 'disable' may be passed as well. Anyway, the user
# can override the default with the --enable/--disable switch.
AC_DEFUN([AM_MAINTAINER_MODE],
[m4_case(m4_default([$1], [disable]),
[enable], [m4_define([am_maintainer_other], [disable])],
[disable], [m4_define([am_maintainer_other], [enable])],
[m4_define([am_maintainer_other], [enable])
m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
dnl maintainer-mode's default is 'disable' unless 'enable' is passed
AC_ARG_ENABLE([maintainer-mode],
[AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode],
am_maintainer_other[ make rules and dependencies not useful
(and sometimes confusing) to the casual installer])],
[USE_MAINTAINER_MODE=$enableval],
[USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
AC_MSG_RESULT([$USE_MAINTAINER_MODE])
AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
MAINT=$MAINTAINER_MODE_TRUE
AC_SUBST([MAINT])dnl
]
)
# Copyright (C) 2006-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])

248
sim/pru/config.in Normal file
View File

@ -0,0 +1,248 @@
/* config.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
#undef AC_APPLE_UNIVERSAL_BUILD
/* Sim debug setting */
#undef DEBUG
/* Define to 1 if translation of program messages to the user's native
language is requested. */
#undef ENABLE_NLS
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <errno.h> header file. */
#undef HAVE_ERRNO_H
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the <fpu_control.h> header file. */
#undef HAVE_FPU_CONTROL_H
/* Define to 1 if you have the `ftruncate' function. */
#undef HAVE_FTRUNCATE
/* Define to 1 if you have the `getrusage' function. */
#undef HAVE_GETRUSAGE
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `nsl' library (-lnsl). */
#undef HAVE_LIBNSL
/* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define to 1 if you have the `lstat' function. */
#undef HAVE_LSTAT
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `mmap' function. */
#undef HAVE_MMAP
/* Define to 1 if you have the `munmap' function. */
#undef HAVE_MUNMAP
/* Define to 1 if you have the `posix_fallocate' function. */
#undef HAVE_POSIX_FALLOCATE
/* Define to 1 if you have the `sigaction' function. */
#undef HAVE_SIGACTION
/* Define to 1 if the system has the type `socklen_t'. */
#undef HAVE_SOCKLEN_T
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if `st_atime' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_ATIME
/* Define to 1 if `st_blksize' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BLKSIZE
/* Define to 1 if `st_blocks' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BLOCKS
/* Define to 1 if `st_ctime' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_CTIME
/* Define to 1 if `st_dev' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_DEV
/* Define to 1 if `st_gid' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_GID
/* Define to 1 if `st_ino' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_INO
/* Define to 1 if `st_mode' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_MODE
/* Define to 1 if `st_mtime' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_MTIME
/* Define to 1 if `st_nlink' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_NLINK
/* Define to 1 if `st_rdev' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_RDEV
/* Define to 1 if `st_size' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_SIZE
/* Define to 1 if `st_uid' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_UID
/* Define to 1 if you have the <sys/mman.h> header file. */
#undef HAVE_SYS_MMAN_H
/* Define to 1 if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/times.h> header file. */
#undef HAVE_SYS_TIMES_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the `time' function. */
#undef HAVE_TIME
/* Define to 1 if you have the <time.h> header file. */
#undef HAVE_TIME_H
/* Define to 1 if you have the `truncate' function. */
#undef HAVE_TRUNCATE
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the <windows.h> header file. */
#undef HAVE_WINDOWS_H
/* Define to 1 if you have the `__setfpucw' function. */
#undef HAVE___SETFPUCW
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Name of this package. */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Additional package description */
#undef PKGVERSION
/* Sim profile settings */
#undef PROFILE
/* Bug reporting address */
#undef REPORT_BUGS_TO
/* Define as the return type of signal handlers (`int' or `void'). */
#undef RETSIGTYPE
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Sim assert settings */
#undef WITH_ASSERT
/* Sim debug setting */
#undef WITH_DEBUG
/* Sim default environment */
#undef WITH_ENVIRONMENT
/* Sim profile settings */
#undef WITH_PROFILE
/* How to route I/O */
#undef WITH_STDIO
/* Sim trace settings */
#undef WITH_TRACE
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
# undef WORDS_BIGENDIAN
# endif
#endif
/* Define to 1 if on MINIX. */
#undef _MINIX
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
#undef _POSIX_1_SOURCE
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE

15973
sim/pru/configure vendored Executable file

File diff suppressed because it is too large Load Diff

31
sim/pru/configure.ac Normal file
View File

@ -0,0 +1,31 @@
dnl Process this file with autoconf to produce a configure script.
dnl Copyright (C) 2016-2019 Free Software Foundation, Inc.
dnl Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
dnl
dnl This file is part of the GNU simulators.
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 3 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
dnl
AC_PREREQ(2.64)dnl
AC_INIT(Makefile.in)
sinclude(../common/acinclude.m4)
SIM_AC_COMMON
SIM_AC_OPTION_ENDIAN(LITTLE)
SIM_AC_OPTION_ALIGNMENT(STRICT_ALIGNMENT,STRICT_ALIGNMENT)
SIM_AC_OPTION_WARNINGS
SIM_AC_OUTPUT

848
sim/pru/interp.c Normal file
View File

@ -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;
}

110
sim/pru/pru.h Normal file
View File

@ -0,0 +1,110 @@
/* Copyright 2016-2019 Free Software Foundation, Inc.
Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
This file is part of the PRU simulator.
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 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/>. */
#ifndef PRU_H
#define PRU_H
#include "config.h"
#include "opcode/pru.h"
/* Needed for handling the dual PRU address space. */
#define IMEM_ADDR_MASK ((1u << 23) - 1)
#define IMEM_ADDR_DEFAULT 0x20000000
/* Define memory sizes to allocate for simulated target. Sizes are
artificially large to accommodate execution of compiler test suite.
Please synchronize with the linker script for prusim target. */
#define DMEM_DEFAULT_SIZE (64 * 1024 * 1024)
/* 16-bit word addressable space. */
#define IMEM_DEFAULT_SIZE (64 * 4 * 1024)
/* For AM335x SoCs. */
#define XFRID_SCRATCH_BANK_0 10
#define XFRID_SCRATCH_BANK_1 11
#define XFRID_SCRATCH_BANK_2 12
#define XFRID_SCRATCH_BANK_PEER 14
#define XFRID_MAX 255
#define CPU (cpu->pru_cpu)
#define PC (CPU.pc)
#define PC_byteaddr ((PC << 2) | PC_ADDR_SPACE_MARKER)
/* Various opcode fields. */
#define RS1 extract_regval (CPU.regs[GET_INSN_FIELD (RS1, inst)], \
GET_INSN_FIELD (RS1SEL, inst))
#define RS2 extract_regval (CPU.regs[GET_INSN_FIELD (RS2, inst)], \
GET_INSN_FIELD (RS2SEL, inst))
#define RS2_w0 extract_regval (CPU.regs[GET_INSN_FIELD (RS2, inst)], \
RSEL_15_0)
#define XBBO_BASEREG (CPU.regs[GET_INSN_FIELD (RS1, inst)])
#define RDSEL GET_INSN_FIELD (RDSEL, inst)
#define RD_WIDTH regsel_width (RDSEL)
#define RD_REGN GET_INSN_FIELD (RD, inst)
#define IO GET_INSN_FIELD (IO, inst)
#define IMM8 GET_INSN_FIELD (IMM8, inst)
#define IMM16 GET_INSN_FIELD (IMM16, inst)
#define WAKEONSTATUS GET_INSN_FIELD (WAKEONSTATUS, inst)
#define CB GET_INSN_FIELD (CB, inst)
#define RDB GET_INSN_FIELD (RDB, inst)
#define XFR_WBA GET_INSN_FIELD (XFR_WBA, inst)
#define LOOP_JMPOFFS GET_INSN_FIELD (LOOP_JMPOFFS, inst)
#define BROFF ((uint32_t) GET_BROFF_SIGNED (inst))
#define _BURSTLEN_CALCULATE(BITFIELD) \
((BITFIELD) >= LSSBBO_BYTECOUNT_R0_BITS7_0 ? \
(CPU.regs[0] >> ((BITFIELD) - LSSBBO_BYTECOUNT_R0_BITS7_0) * 8) & 0xff \
: (BITFIELD) + 1)
#define BURSTLEN _BURSTLEN_CALCULATE (GET_BURSTLEN (inst))
#define XFR_LENGTH _BURSTLEN_CALCULATE (GET_INSN_FIELD (XFR_LENGTH, inst))
#define DO_XIN(wba,regn,rdb,l) \
pru_sim_xin (sd, cpu, (wba), (regn), (rdb), (l))
#define DO_XOUT(wba,regn,rdb,l) \
pru_sim_xout (sd, cpu, (wba), (regn), (rdb), (l))
#define DO_XCHG(wba,regn,rdb,l) \
pru_sim_xchg (sd, cpu, (wba), (regn), (rdb), (l))
#define RAISE_SIGILL(sd) sim_engine_halt ((sd), NULL, NULL, PC_byteaddr, \
sim_stopped, SIM_SIGILL)
#define RAISE_SIGINT(sd) sim_engine_halt ((sd), NULL, NULL, PC_byteaddr, \
sim_stopped, SIM_SIGINT)
#define MAC_R25_MAC_MODE_MASK (1u << 0)
#define MAC_R25_ACC_CARRY_MASK (1u << 1)
#define CARRY CPU.carry
#define CTABLE CPU.ctable
#define PC_ADDR_SPACE_MARKER CPU.pc_addr_space_marker
#define LOOPTOP CPU.loop.looptop
#define LOOPEND CPU.loop.loopend
#define LOOP_IN_PROGRESS CPU.loop.loop_in_progress
#define LOOPCNT CPU.loop.loop_counter
/* 32 GP registers plus PC. */
#define NUM_REGS 33
#endif /* PRU_H */

249
sim/pru/pru.isa Normal file
View File

@ -0,0 +1,249 @@
/* Copyright 2016-2019 Free Software Foundation, Inc.
Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
This file is part of the PRU simulator.
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 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/>. */
/*
PRU Instruction Set Architecture
INSTRUCTION (NAME,
SEMANTICS)
*/
INSTRUCTION (add,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 + OP2;
CARRY = (((uint64_t) RS1 + (uint64_t) OP2) >> RD_WIDTH) & 1;
PC++)
INSTRUCTION (adc,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 + OP2 + CARRY;
CARRY = (((uint64_t) RS1 + (uint64_t) OP2 + (uint64_t) CARRY)
>> RD_WIDTH) & 1;
PC++)
INSTRUCTION (sub,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 - OP2;
CARRY = (((uint64_t) RS1 - (uint64_t) OP2) >> RD_WIDTH) & 1;
PC++)
INSTRUCTION (suc,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 - OP2 - CARRY;
CARRY = (((uint64_t) RS1 - (uint64_t) OP2 - (uint64_t) CARRY)
>> RD_WIDTH) & 1;
PC++)
INSTRUCTION (rsb,
OP2 = (IO ? IMM8 : RS2);
RD = OP2 - RS1;
CARRY = (((uint64_t) OP2 - (uint64_t) RS1) >> RD_WIDTH) & 1;
PC++)
INSTRUCTION (rsc,
OP2 = (IO ? IMM8 : RS2);
RD = OP2 - RS1 - CARRY;
CARRY = (((uint64_t) OP2 - (uint64_t) RS1 - (uint64_t) CARRY)
>> RD_WIDTH) & 1;
PC++)
INSTRUCTION (lsl,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 << (OP2 & 0x1f);
PC++)
INSTRUCTION (lsr,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 >> (OP2 & 0x1f);
PC++)
INSTRUCTION (and,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 & OP2;
PC++)
INSTRUCTION (or,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 | OP2;
PC++)
INSTRUCTION (xor,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 ^ OP2;
PC++)
INSTRUCTION (not,
RD = ~RS1;
PC++)
INSTRUCTION (min,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 < OP2 ? RS1 : OP2;
PC++)
INSTRUCTION (max,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 > OP2 ? RS1 : OP2;
PC++)
INSTRUCTION (clr,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 & ~(1u << (OP2 & 0x1f));
PC++)
INSTRUCTION (set,
OP2 = (IO ? IMM8 : RS2);
RD = RS1 | (1u << (OP2 & 0x1f));
PC++)
INSTRUCTION (jmp,
OP2 = (IO ? IMM16 : RS2);
PC = OP2)
INSTRUCTION (jal,
OP2 = (IO ? IMM16 : RS2);
RD = PC + 1;
PC = OP2)
INSTRUCTION (ldi,
RD = IMM16;
PC++)
INSTRUCTION (halt,
pru_sim_syscall (sd, cpu);
PC++)
INSTRUCTION (slp,
if (!WAKEONSTATUS)
{
RAISE_SIGINT (sd);
}
else
{
PC++;
})
INSTRUCTION (qbgt,
OP2 = (IO ? IMM8 : RS2);
PC = (OP2 > RS1) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qbge,
OP2 = (IO ? IMM8 : RS2);
PC = (OP2 >= RS1) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qblt,
OP2 = (IO ? IMM8 : RS2);
PC = (OP2 < RS1) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qble,
OP2 = (IO ? IMM8 : RS2);
PC = (OP2 <= RS1) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qbeq,
OP2 = (IO ? IMM8 : RS2);
PC = (OP2 == RS1) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qbne,
OP2 = (IO ? IMM8 : RS2);
PC = (OP2 != RS1) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qba,
OP2 = (IO ? IMM8 : RS2);
PC = PC + BROFF)
INSTRUCTION (qbbs,
OP2 = (IO ? IMM8 : RS2);
PC = (RS1 & (1u << (OP2 & 0x1f))) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (qbbc,
OP2 = (IO ? IMM8 : RS2);
PC = !(RS1 & (1u << (OP2 & 0x1f))) ? (PC + BROFF) : (PC + 1))
INSTRUCTION (lbbo,
pru_dmem2reg (cpu, XBBO_BASEREG + (IO ? IMM8 : RS2),
BURSTLEN, RD_REGN, RDB);
PC++)
INSTRUCTION (sbbo,
pru_reg2dmem (cpu, XBBO_BASEREG + (IO ? IMM8 : RS2),
BURSTLEN, RD_REGN, RDB);
PC++)
INSTRUCTION (lbco,
pru_dmem2reg (cpu, CTABLE[CB] + (IO ? IMM8 : RS2),
BURSTLEN, RD_REGN, RDB);
PC++)
INSTRUCTION (sbco,
pru_reg2dmem (cpu, CTABLE[CB] + (IO ? IMM8 : RS2),
BURSTLEN, RD_REGN, RDB);
PC++)
INSTRUCTION (xin,
DO_XIN (XFR_WBA, RD_REGN, RDB, XFR_LENGTH);
PC++)
INSTRUCTION (xout,
DO_XOUT (XFR_WBA, RD_REGN, RDB, XFR_LENGTH);
PC++)
INSTRUCTION (xchg,
DO_XCHG (XFR_WBA, RD_REGN, RDB, XFR_LENGTH);
PC++)
INSTRUCTION (sxin,
sim_io_eprintf (sd, "SXIN instruction not supported by sim\n");
RAISE_SIGILL (sd))
INSTRUCTION (sxout,
sim_io_eprintf (sd, "SXOUT instruction not supported by sim\n");
RAISE_SIGILL (sd))
INSTRUCTION (sxchg,
sim_io_eprintf (sd, "SXCHG instruction not supported by sim\n");
RAISE_SIGILL (sd))
INSTRUCTION (loop,
OP2 = (IO ? IMM8 + 1 : RS2_w0);
if (OP2 == 0)
{
PC = LOOPEND;
}
else
{
LOOPTOP = PC + 1;
LOOPEND = PC + LOOP_JMPOFFS;
LOOPCNT = OP2;
LOOP_IN_PROGRESS = 1;
PC++;
})
INSTRUCTION (iloop,
OP2 = (IO ? IMM8 + 1 : RS2_w0);
if (OP2 == 0)
{
PC = LOOPEND;
}
else
{
LOOPTOP = PC + 1;
LOOPEND = PC + LOOP_JMPOFFS;
LOOPCNT = OP2;
LOOP_IN_PROGRESS = 1;
PC++;
})

91
sim/pru/sim-main.h Normal file
View File

@ -0,0 +1,91 @@
/* Copyright 2016-2019 Free Software Foundation, Inc.
Contributed by Dimitar Dimitrov <dimitar@dinux.eu>
This file is part of the PRU simulator.
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 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/>. */
#ifndef PRU_SIM_MAIN
#define PRU_SIM_MAIN
#include <stdint.h>
#include <stddef.h>
#include "pru.h"
#include "sim-basics.h"
#include "sim-base.h"
/* The machine state.
This state is maintained in host byte order. The
fetch/store register functions must translate between host
byte order and the target processor byte order.
Keeping this data in target byte order simplifies the register
read/write functions. Keeping this data in host order improves
the performance of the simulator. Simulation speed is deemed more
important. */
/* For clarity, please keep the same relative order in this enum as in the
corresponding group of GP registers.
In PRU ISA, Multiplier-Accumulator-Unit's registers are like "shadows" of
the GP registers. MAC registers are implicitly addressed when executing
the XIN/XOUT instructions to access them. Transfer to/from a MAC register
can happen only from/to its corresponding GP peer register. */
enum pru_macreg_id {
/* MAC register CPU GP register Description. */
PRU_MACREG_MODE, /* r25 */ /* Mode (MUL/MAC). */
PRU_MACREG_PROD_L, /* r26 */ /* Lower 32 bits of product. */
PRU_MACREG_PROD_H, /* r27 */ /* Higher 32 bits of product. */
PRU_MACREG_OP_0, /* r28 */ /* First operand. */
PRU_MACREG_OP_1, /* r29 */ /* Second operand. */
PRU_MACREG_ACC_L, /* N/A */ /* Accumulator (not exposed) */
PRU_MACREG_ACC_H, /* N/A */ /* Higher 32 bits of MAC
accumulator. */
PRU_MAC_NREGS
};
struct pru_regset
{
uint32_t regs[32]; /* Primary registers. */
uint16_t pc; /* IMEM _word_ address. */
uint32_t pc_addr_space_marker; /* IMEM virtual linker offset. This
is the artificial offset that
we invent in order to "separate"
the DMEM and IMEM memory spaces. */
unsigned int carry : 1;
uint32_t ctable[32]; /* Constant offsets table for xBCO. */
uint32_t macregs[PRU_MAC_NREGS];
uint32_t scratchpads[XFRID_MAX + 1][32];
struct {
uint16_t looptop; /* LOOP top (PC of loop instr). */
uint16_t loopend; /* LOOP end (PC of loop end label). */
int loop_in_progress; /* Whether to check for PC==loopend. */
uint32_t loop_counter; /* LOOP counter. */
} loop;
int cycles;
int insts;
};
struct _sim_cpu {
struct pru_regset pru_cpu;
sim_cpu_base base;
};
struct sim_state {
sim_cpu *cpu[MAX_NR_PROCESSORS];
sim_state_base base;
};
#endif /* PRU_SIM_MAIN */