Browse Source
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.gdb-9-branch
committed by
Andrew Burgess
19 changed files with 17775 additions and 0 deletions
@ -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. |
|||
@ -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
|
|||
@ -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($@)]) |
|||
|
|||
@ -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 |
|||
File diff suppressed because it is too large
@ -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 |
|||
@ -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; |
|||
} |
|||
@ -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 */ |
|||
@ -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++; |
|||
}) |
|||
@ -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 */ |
|||
Loading…
Reference in new issue