This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/2] RISC-V sim Port


---
 sim/riscv/ChangeLog                   |    6 +
 sim/riscv/Makefile.in                 |   35 +
 sim/riscv/config.in                   |  248 +++++++
 sim/riscv/interp.c                    |  177 +++++
 sim/riscv/machs.c                     |  125 ++++
 sim/riscv/machs.h                     |   45 ++
 sim/riscv/sim-main.c                  | 1189 +++++++++++++++++++++++++++++++++
 sim/riscv/sim-main.h                  |   87 +++
 sim/riscv/tconfig.h                   |    4 +
 sim/testsuite/sim/riscv/ChangeLog     |    3 +
 sim/testsuite/sim/riscv/allinsn.exp   |   15 +
 sim/testsuite/sim/riscv/pass.s        |    7 +
 sim/testsuite/sim/riscv/testutils.inc |   50 ++
 13 files changed, 1991 insertions(+)
 create mode 100644 sim/riscv/ChangeLog
 create mode 100644 sim/riscv/Makefile.in
 create mode 100644 sim/riscv/config.in
 create mode 100644 sim/riscv/interp.c
 create mode 100644 sim/riscv/machs.c
 create mode 100644 sim/riscv/machs.h
 create mode 100644 sim/riscv/sim-main.c
 create mode 100644 sim/riscv/sim-main.h
 create mode 100644 sim/riscv/tconfig.h
 create mode 100644 sim/testsuite/sim/riscv/ChangeLog
 create mode 100644 sim/testsuite/sim/riscv/allinsn.exp
 create mode 100644 sim/testsuite/sim/riscv/pass.s
 create mode 100644 sim/testsuite/sim/riscv/testutils.inc

diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
new file mode 100644
index 0000000..a960da8
--- /dev/null
+++ b/sim/riscv/ChangeLog
@@ -0,0 +1,6 @@
+2015-04-27  Mike Frysinger  <vapier@gentoo.org>
+
+	* configure.ac, interp.c, Makefile.in, README, README-ISA,
+	sim-main.c, sim-main.h: New files for example simulator.
+	* aclocal.m4, config.in, configure: Regenerated.
+
diff --git a/sim/riscv/Makefile.in b/sim/riscv/Makefile.in
new file mode 100644
index 0000000..2902cf6
--- /dev/null
+++ b/sim/riscv/Makefile.in
@@ -0,0 +1,35 @@
+#    Makefile template for Configure for the example basic simulator.
+#    Copyright (C) 2005-2015 Free Software Foundation, Inc.
+#    Written by Mike Frysinger.
+# 
+# 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/>.
+
+# This selects the newlib/libgloss syscall definitions.
+NL_TARGET = -DNL_TARGET_riscv
+
+## COMMON_PRE_CONFIG_FRAG
+
+SIM_OBJS = \
+	$(SIM_NEW_COMMON_OBJS) \
+	sim-hload.o \
+	sim-model.o \
+	sim-reason.o \
+	sim-reg.o \
+	sim-resume.o \
+	sim-stop.o \
+	interp.o \
+	machs.o \
+	sim-main.o
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/riscv/config.in b/sim/riscv/config.in
new file mode 100644
index 0000000..aa3e45c
--- /dev/null
+++ b/sim/riscv/config.in
@@ -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 `struct stat' is a member of `st_atime'. */
+#undef HAVE_STRUCT_STAT_ST_ATIME
+
+/* Define to 1 if `struct stat' is a member of `st_blksize'. */
+#undef HAVE_STRUCT_STAT_ST_BLKSIZE
+
+/* Define to 1 if `struct stat' is a member of `st_blocks'. */
+#undef HAVE_STRUCT_STAT_ST_BLOCKS
+
+/* Define to 1 if `struct stat' is a member of `st_ctime'. */
+#undef HAVE_STRUCT_STAT_ST_CTIME
+
+/* Define to 1 if `struct stat' is a member of `st_dev'. */
+#undef HAVE_STRUCT_STAT_ST_DEV
+
+/* Define to 1 if `struct stat' is a member of `st_gid'. */
+#undef HAVE_STRUCT_STAT_ST_GID
+
+/* Define to 1 if `struct stat' is a member of `st_ino'. */
+#undef HAVE_STRUCT_STAT_ST_INO
+
+/* Define to 1 if `struct stat' is a member of `st_mode'. */
+#undef HAVE_STRUCT_STAT_ST_MODE
+
+/* Define to 1 if `struct stat' is a member of `st_mtime'. */
+#undef HAVE_STRUCT_STAT_ST_MTIME
+
+/* Define to 1 if `struct stat' is a member of `st_nlink'. */
+#undef HAVE_STRUCT_STAT_ST_NLINK
+
+/* Define to 1 if `struct stat' is a member of `st_rdev'. */
+#undef HAVE_STRUCT_STAT_ST_RDEV
+
+/* Define to 1 if `struct stat' is a member of `st_size'. */
+#undef HAVE_STRUCT_STAT_ST_SIZE
+
+/* Define to 1 if `struct stat' is a member of `st_uid'. */
+#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
diff --git a/sim/riscv/interp.c b/sim/riscv/interp.c
new file mode 100644
index 0000000..8e6d454
--- /dev/null
+++ b/sim/riscv/interp.c
@@ -0,0 +1,177 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   This file is part of 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/>.  */
+
+/* This file contains the main glue logic between the sim core and the target
+   specific simulator.  Normally this file will be kept small and the target
+   details will live in other files.
+
+   For more specific details on these functions, see the gdb/remote-sim.h
+   header file.  */
+
+#include "config.h"
+
+#include "sim-main.h"
+#include "sim-options.h"
+
+/* This function is the main loop.  It should process ticks and decode+execute
+   a single instruction.
+
+   Usually you do not need to change things here.  */
+
+void
+sim_engine_run (SIM_DESC sd,
+		int next_cpu_nr, /* ignore  */
+		int nr_cpus, /* ignore  */
+		int siggnal) /* ignore  */
+{
+  SIM_CPU *cpu;
+
+  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
+
+  cpu = STATE_CPU (sd, 0);
+
+  while (1)
+    {
+      step_once (cpu);
+      if (sim_events_tick (sd))
+	sim_events_process (sd);
+    }
+}
+
+/* Initialize the simulator from scratch.  This is called once per lifetime of
+   the simulation.  Think of it as a processor reset.
+
+   Usually all cpu-specific setup is handled in the initialize_cpu callback.
+   If you want to do cpu-independent stuff, then it should go at the end (see
+   where memory is initialized).  */
+
+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);
+}
+
+SIM_DESC
+sim_open (SIM_OPEN_KIND kind, host_callback *callback,
+	  struct bfd *abfd, char * const *argv)
+{
+  char c;
+  int i;
+  SIM_DESC sd = sim_state_alloc (kind, callback);
+
+  /* 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;
+    }
+
+  /* XXX: Default to the Virtual environment.  */
+  if (STATE_ENVIRONMENT (sd) == ALL_ENVIRONMENT)
+    STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT;
+
+  /* getopt will print the error message so we just have to exit if this fails.
+     FIXME: Hmmm...  in the case of gdb we need getopt to call
+     print_filtered.  */
+  if (sim_parse_args (sd, argv) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Check for/establish the 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;
+    }
+
+  /* Establish any remaining configuration options.  */
+  if (sim_config (sd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  if (sim_post_argv_init (sd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* CPU specific initialization.  */
+  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
+    {
+      SIM_CPU *cpu = STATE_CPU (sd, i);
+
+      initialize_cpu (sd, cpu, i);
+    }
+
+  /* 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-size %#x", DEFAULT_MEM_SIZE);
+
+  return sd;
+}
+
+/* Prepare to run a program that has already been loaded into memory.
+
+   Usually you do not need to change things here.  */
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd, struct bfd *abfd,
+		     char * const *argv, char * const *env)
+{
+  SIM_CPU *cpu = STATE_CPU (sd, 0);
+  sim_cia addr;
+
+  /* Set the PC.  */
+  if (abfd != NULL)
+    addr = bfd_get_start_address (abfd);
+  else
+    addr = 0;
+  sim_pc_set (cpu, addr);
+
+  /* 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.  */
+  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+    {
+      freeargv (STATE_PROG_ARGV (sd));
+      STATE_PROG_ARGV (sd) = dupargv (argv);
+    }
+
+  initialize_env (sd, (void *)argv, (void *)env);
+
+  return SIM_RC_OK;
+}
diff --git a/sim/riscv/machs.c b/sim/riscv/machs.c
new file mode 100644
index 0000000..78ed28d
--- /dev/null
+++ b/sim/riscv/machs.c
@@ -0,0 +1,125 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   This file is part of 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 "sim-main.h"
+
+static void
+riscv_model_init (SIM_CPU *cpu)
+{
+}
+
+static void
+riscv_init_cpu (SIM_CPU *cpu)
+{
+}
+
+static void
+riscv_prepare_run (SIM_CPU *cpu)
+{
+}
+
+static const SIM_MACH_IMP_PROPERTIES riscv_imp_properties =
+{
+  sizeof (SIM_CPU),
+  0,
+};
+
+#if WITH_TARGET_WORD_BITSIZE >= 32
+
+static const SIM_MACH rv32i_mach;
+
+static const SIM_MODEL rv32_models[] =
+{
+#define M(ext) { "RV32"#ext, &rv32i_mach, MODEL_RV32##ext, NULL, riscv_model_init },
+#include "model_list.def"
+#undef M
+  { 0, NULL, 0, NULL, NULL, }
+};
+
+static const SIM_MACH rv32i_mach =
+{
+  "rv32i", "riscv:rv32", MACH_RV32I,
+  32, 32, &rv32_models[0], &riscv_imp_properties,
+  riscv_init_cpu,
+  riscv_prepare_run
+};
+
+#endif
+
+#if WITH_TARGET_WORD_BITSIZE >= 64
+
+static const SIM_MACH rv64i_mach;
+
+static const SIM_MODEL rv64_models[] =
+{
+#define M(ext) { "RV64"#ext, &rv64i_mach, MODEL_RV64##ext, NULL, riscv_model_init },
+#include "model_list.def"
+#undef M
+  { 0, NULL, 0, NULL, NULL, }
+};
+
+static const SIM_MACH rv64i_mach =
+{
+  "rv64i", "riscv:rv64", MACH_RV64I,
+  64, 64, &rv64_models[0], &riscv_imp_properties,
+  riscv_init_cpu,
+  riscv_prepare_run
+};
+
+#endif
+
+#if WITH_TARGET_WORD_BITSIZE >= 128
+
+static const SIM_MACH rv128i_mach;
+
+static const SIM_MODEL rv128_models[] =
+{
+#define M(ext) { "RV128"#ext, &rv128i_mach, MODEL_RV128##ext, NULL, riscv_model_init },
+#include "model_list.def"
+#undef M
+  { 0, NULL, 0, NULL, NULL, }
+};
+
+static const SIM_MACH rv128i_mach =
+{
+  "rv128i", "riscv:rv128", MACH_RV128I,
+  128, 128, &rv128_models[0], &riscv_imp_properties,
+  riscv_init_cpu,
+  riscv_prepare_run
+};
+
+#endif
+
+/* Order matters here.  */
+const SIM_MACH *sim_machs[] =
+{
+#if WITH_TARGET_WORD_BITSIZE >= 128
+  &rv128i_mach,
+#endif
+#if WITH_TARGET_WORD_BITSIZE >= 64
+  &rv64i_mach,
+#endif
+#if WITH_TARGET_WORD_BITSIZE >= 32
+  &rv32i_mach,
+#endif
+  NULL
+};
diff --git a/sim/riscv/machs.h b/sim/riscv/machs.h
new file mode 100644
index 0000000..6c8cd43
--- /dev/null
+++ b/sim/riscv/machs.h
@@ -0,0 +1,45 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2014 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   This file is part of 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/>.  */
+
+#ifndef RISCV_SIM_MACHS_H
+#define RISCV_SIM_MACHS_H
+
+typedef enum model_type {
+#define M(ext) MODEL_RV32##ext,
+#include "model_list.def"
+#undef M
+#define M(ext) MODEL_RV64##ext,
+#include "model_list.def"
+#undef M
+#define M(ext) MODEL_RV128##ext,
+#include "model_list.def"
+#undef M
+  MODEL_MAX
+} MODEL_TYPE;
+
+typedef enum mach_attr {
+  MACH_BASE,
+  MACH_RV32I,
+  MACH_RV64I,
+  MACH_RV128I,
+  MACH_MAX
+} MACH_ATTR;
+
+#endif
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
new file mode 100644
index 0000000..97e37d2
--- /dev/null
+++ b/sim/riscv/sim-main.c
@@ -0,0 +1,1189 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2014 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   This file is part of 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/>.  */
+
+/* This file contains the main simulator decoding logic.  i.e. everything that
+   is architecture specific.  */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <time.h>
+
+#include "sim-main.h"
+#include "sim-syscall.h"
+
+#include "opcode/riscv.h"
+
+#include "gdb/sim-riscv.h"
+
+#include "targ-vals.h"
+
+
+#define TRACE_REG(cpu, reg) TRACE_REGISTER (cpu, "wrote %s = %#"PRIxTW, riscv_gpr_names_abi[reg], cpu->regs[reg])
+
+static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
+#define OP_HASH_IDX(i) ((i) & (riscv_insn_length (i) == 2 ? 0x3 : 0x7f))
+
+#define RISCV_ASSERT_RV32(cpu, fmt, args...) \
+  do { \
+    if (RISCV_XLEN (cpu) != 32) \
+      { \
+	SIM_DESC sd = CPU_STATE (cpu); \
+	TRACE_INSN (cpu, "RV32I-only " fmt, ## args); \
+	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL); \
+      } \
+  } while (0)
+
+#define RISCV_ASSERT_RV64(cpu, fmt, args...) \
+  do { \
+    if (RISCV_XLEN (cpu) != 64) \
+      { \
+	SIM_DESC sd = CPU_STATE (cpu); \
+	TRACE_INSN (cpu, "RV64I-only " fmt, ## args); \
+	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL); \
+      } \
+  } while (0)
+
+static INLINE void
+store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
+{
+  if (rd)
+    {
+      cpu->regs[rd] = val;
+      TRACE_REG (cpu, rd);
+    }
+}
+
+static INLINE unsigned_word
+fetch_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg)
+{
+  /* Handle pseudo registers.  */
+  switch (csr)
+    {
+    /* Allow certain registers only in respective modes.  */
+    case CSR_CYCLEH:
+    case CSR_INSTRETH:
+    case CSR_TIMEH:
+      RISCV_ASSERT_RV32 (cpu, "CSR: %s", name);
+      break;
+    }
+
+  return *reg;
+}
+
+static INLINE void
+store_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg,
+	   unsigned_word val)
+{
+  switch (csr)
+    {
+    /* These are pseudo registers that modify sub-fields of fcsr.  */
+    case CSR_FRM:
+      val &= 0x7;
+      *reg = val;
+      cpu->csr.fcsr = (cpu->csr.fcsr & ~0xe0) | (val << 5);
+      break;
+    case CSR_FFLAGS:
+      val &= 0x1f;
+      *reg = val;
+      cpu->csr.fcsr = (cpu->csr.fcsr & ~0x1f) | val;
+      break;
+    /* Keep the sub-fields in sync.  */
+    case CSR_FCSR:
+      *reg = val;
+      cpu->csr.frm = (val >> 5) & 0x7;
+      cpu->csr.fflags = val & 0x1f;
+      break;
+
+    /* Allow certain registers only in respective modes.  */
+    case CSR_CYCLEH:
+    case CSR_INSTRETH:
+    case CSR_TIMEH:
+      RISCV_ASSERT_RV32 (cpu, "CSR: %s", name);
+
+    /* All the rest are immutable.  */
+    default:
+      val = *reg;
+      break;
+    }
+
+  TRACE_REGISTER (cpu, "wrote CSR %s = %#"PRIxTW, name, val);
+}
+
+static inline unsigned_word
+ashiftrt (unsigned_word val, unsigned_word shift)
+{
+  unsigned32 sign = (val & 0x80000000) ? ~(0xfffffffful >> shift) : 0;
+  return (val >> shift) | sign;
+}
+
+static inline unsigned_word
+ashiftrt64 (unsigned_word val, unsigned_word shift)
+{
+  unsigned64 sign = (val & 0x8000000000000000ull) ? ~(0xffffffffffffffffull >> shift) : 0;
+  return (val >> shift) | sign;
+}
+
+static sim_cia
+execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
+  int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
+  int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
+  const char *rd_name = riscv_gpr_names_abi[rd];
+  const char *rs1_name = riscv_gpr_names_abi[rs1];
+  const char *rs2_name = riscv_gpr_names_abi[rs2];
+  unsigned int csr = (iw >> OP_SH_CSR) & OP_MASK_CSR;
+  unsigned_word i_imm = EXTRACT_ITYPE_IMM (iw);
+  unsigned_word u_imm = EXTRACT_UTYPE_IMM ((unsigned64) iw);
+  unsigned_word s_imm = EXTRACT_STYPE_IMM (iw);
+  unsigned_word sb_imm = EXTRACT_SBTYPE_IMM (iw);
+  unsigned_word shamt_imm = ((iw >> OP_SH_SHAMT) & OP_MASK_SHAMT);
+  unsigned_word tmp;
+  sim_cia pc = cpu->pc + 4;
+
+  TRACE_EXTRACT (cpu, "rd:%-2i:%-4s  rs1:%-2i:%-4s %0*"PRIxTW"  rs2:%-2i:%-4s %0*"PRIxTW"  match:%#x mask:%#x",
+		 rd, rd_name,
+		 rs1, rs1_name, (int)sizeof (unsigned_word) * 2, cpu->regs[rs1],
+		 rs2, rs2_name, (int)sizeof (unsigned_word) * 2, cpu->regs[rs2],
+		 (unsigned) op->match, (unsigned) op->mask);
+
+  switch (op->match)
+    {
+    case MATCH_ADD:
+      TRACE_INSN (cpu, "add %s, %s, %s;  // %s = %s + %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      store_rd (cpu, rd, cpu->regs[rs1] + cpu->regs[rs2]);
+      break;
+    case MATCH_ADDW:
+      TRACE_INSN (cpu, "addw %s, %s, %s;  // %s = %s + %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] + cpu->regs[rs2]));
+      break;
+    case MATCH_ADDI:
+      TRACE_INSN (cpu, "addi %s, %s, %#"PRIxTW";  // %s = %s + %#"PRIxTW,
+		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
+      store_rd (cpu, rd, cpu->regs[rs1] + i_imm);
+      break;
+    case MATCH_ADDIW:
+      TRACE_INSN (cpu, "addiw %s, %s, %#"PRIxTW";  // %s = %s + %#"PRIxTW,
+		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] + i_imm));
+      break;
+    case MATCH_AND:
+      TRACE_INSN (cpu, "and %s, %s, %s;  // %s = %s & %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      store_rd (cpu, rd, cpu->regs[rs1] & cpu->regs[rs2]);
+      break;
+    case MATCH_ANDI:
+      TRACE_INSN (cpu, "andi %s, %s, %"PRIiTW";  // %s = %s & %#"PRIxTW,
+		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
+      store_rd (cpu, rd, cpu->regs[rs1] & i_imm);
+      break;
+    case MATCH_OR:
+      TRACE_INSN (cpu, "or %s, %s, %s;  // %s = %s | %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      store_rd (cpu, rd, cpu->regs[rs1] | cpu->regs[rs2]);
+      break;
+    case MATCH_ORI:
+      TRACE_INSN (cpu, "ori %s, %s, %"PRIiTW";  // %s = %s | %#"PRIxTW,
+		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
+      store_rd (cpu, rd, cpu->regs[rs1] | i_imm);
+      break;
+    case MATCH_XOR:
+      TRACE_INSN (cpu, "xor %s, %s, %s;  // %s = %s ^ %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      store_rd (cpu, rd, cpu->regs[rs1] ^ cpu->regs[rs2]);
+      break;
+    case MATCH_XORI:
+      TRACE_INSN (cpu, "xori %s, %s, %"PRIiTW";  // %s = %s ^ %#"PRIxTW,
+		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
+      store_rd (cpu, rd, cpu->regs[rs1] ^ i_imm);
+      break;
+    case MATCH_SUB:
+      TRACE_INSN (cpu, "sub %s, %s, %s;  // %s = %s - %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      store_rd (cpu, rd, cpu->regs[rs1] - cpu->regs[rs2]);
+      break;
+    case MATCH_SUBW:
+      TRACE_INSN (cpu, "subw %s, %s, %s;  // %s = %s - %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] - cpu->regs[rs2]));
+      break;
+    case MATCH_LUI:
+      TRACE_INSN (cpu, "lui %s, %#"PRIxTW";", rd_name, u_imm);
+      store_rd (cpu, rd, u_imm);
+      break;
+    case MATCH_SLL:
+      TRACE_INSN (cpu, "sll %s, %s, %s;  // %s = %s << %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f;
+      store_rd (cpu, rd, cpu->regs[rs1] << (cpu->regs[rs2] & u_imm));
+      break;
+    case MATCH_SLLW:
+      TRACE_INSN (cpu, "sllw %s, %s, %s;  // %s = %s << %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 ((unsigned32)cpu->regs[rs1] << (cpu->regs[rs2] & 0x1f)));
+      break;
+    case MATCH_SLLI:
+      TRACE_INSN (cpu, "slli %s, %s, %"PRIiTW";  // %s = %s << %#"PRIxTW,
+		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
+      if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f)
+	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+      store_rd (cpu, rd, cpu->regs[rs1] << shamt_imm);
+      break;
+    case MATCH_SLLIW:
+      TRACE_INSN (cpu, "slliw %s, %s, %"PRIiTW";  // %s = %s << %#"PRIxTW,
+		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 ((unsigned32)cpu->regs[rs1] << shamt_imm));
+      break;
+    case MATCH_SRL:
+      TRACE_INSN (cpu, "srl %s, %s, %s;  // %s = %s >> %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f;
+      store_rd (cpu, rd, cpu->regs[rs1] >> (cpu->regs[rs2] & u_imm));
+      break;
+    case MATCH_SRLW:
+      TRACE_INSN (cpu, "srlw %s, %s, %s;  // %s = %s >> %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 ((unsigned32)cpu->regs[rs1] >> (cpu->regs[rs2] & 0x1f)));
+      break;
+    case MATCH_SRLI:
+      TRACE_INSN (cpu, "srli %s, %s, %"PRIiTW";  // %s = %s >> %#"PRIxTW,
+		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
+      if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f)
+	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+      store_rd (cpu, rd, cpu->regs[rs1] >> shamt_imm);
+      break;
+    case MATCH_SRLIW:
+      TRACE_INSN (cpu, "srliw %s, %s, %"PRIiTW";  // %s = %s >> %#"PRIxTW,
+		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 ((unsigned32)cpu->regs[rs1] >> shamt_imm));
+      break;
+    case MATCH_SRA:
+      TRACE_INSN (cpu, "sra %s, %s, %s;  // %s = %s >>> %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (RISCV_XLEN (cpu) == 32)
+	tmp = ashiftrt (cpu->regs[rs1], cpu->regs[rs2] & 0x1f);
+      else
+	tmp = ashiftrt64 (cpu->regs[rs1], cpu->regs[rs2] & 0x3f);
+      store_rd (cpu, rd, tmp);
+      break;
+    case MATCH_SRAW:
+      TRACE_INSN (cpu, "sraw %s, %s, %s;  // %s = %s >>> %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 (ashiftrt ((signed32)cpu->regs[rs1], cpu->regs[rs2] & 0x1f)));
+      break;
+    case MATCH_SRAI:
+      TRACE_INSN (cpu, "srai %s, %s, %"PRIiTW";  // %s = %s >>> %#"PRIxTW,
+		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
+      if (RISCV_XLEN (cpu) == 32)
+	{
+	  if (shamt_imm > 0x1f)
+	    sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+	  tmp = ashiftrt (cpu->regs[rs1], shamt_imm);
+	}
+      else
+	tmp = ashiftrt64 (cpu->regs[rs1], shamt_imm);
+      store_rd (cpu, rd, tmp);
+      break;
+    case MATCH_SRAIW:
+      TRACE_INSN (cpu, "sraiw %s, %s, %"PRIiTW";  // %s = %s >>> %#"PRIxTW,
+		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 (ashiftrt ((signed32)cpu->regs[rs1], shamt_imm)));
+      break;
+    case MATCH_SLT:
+      TRACE_INSN (cpu, "slt");
+      store_rd (cpu, rd, !!((signed_word)cpu->regs[rs1] < (signed_word)cpu->regs[rs2]));
+      break;
+    case MATCH_SLTU:
+      TRACE_INSN (cpu, "sltu");
+      store_rd (cpu, rd, !!((unsigned_word)cpu->regs[rs1] < (unsigned_word)cpu->regs[rs2]));
+      break;
+    case MATCH_SLTI:
+      TRACE_INSN (cpu, "slti");
+      store_rd (cpu, rd, !!((signed_word)cpu->regs[rs1] < (signed_word)i_imm));
+      break;
+    case MATCH_SLTIU:
+      TRACE_INSN (cpu, "sltiu");
+      store_rd (cpu, rd, !!((unsigned_word)cpu->regs[rs1] < (unsigned_word)i_imm));
+      break;
+    case MATCH_AUIPC:
+      TRACE_INSN (cpu, "auipc %s, %"PRIiTW";  // %s = pc + %"PRIiTW,
+		  rd_name, u_imm, rd_name, u_imm);
+      store_rd (cpu, rd, cpu->pc + u_imm);
+      break;
+    case MATCH_BEQ:
+      TRACE_INSN (cpu, "beq %s, %s, %#"PRIxTW";  // if (%s == %s) goto %#"PRIxTW,
+		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
+      if (cpu->regs[rs1] == cpu->regs[rs2])
+	{
+	  pc = cpu->pc + sb_imm;
+	  TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+	}
+      break;
+    case MATCH_BLT:
+      TRACE_INSN (cpu, "blt %s, %s, %#"PRIxTW";  // if (%s < %s) goto %#"PRIxTW,
+		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
+      if ((signed_word)cpu->regs[rs1] < (signed_word)cpu->regs[rs2])
+	{
+	  pc = cpu->pc + sb_imm;
+	  TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+	}
+      break;
+    case MATCH_BLTU:
+      TRACE_INSN (cpu, "bltu %s, %s, %#"PRIxTW";  // if (%s < %s) goto %#"PRIxTW,
+		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
+      if ((unsigned_word)cpu->regs[rs1] < (unsigned_word)cpu->regs[rs2])
+	{
+	  pc = cpu->pc + sb_imm;
+	  TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+	}
+      break;
+    case MATCH_BGE:
+      TRACE_INSN (cpu, "bge %s, %s, %#"PRIxTW";  // if (%s >= %s) goto %#"PRIxTW,
+		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
+      if ((signed_word)cpu->regs[rs1] >= (signed_word)cpu->regs[rs2])
+	{
+	  pc = cpu->pc + sb_imm;
+	  TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+	}
+      break;
+    case MATCH_BGEU:
+      TRACE_INSN (cpu, "bgeu %s, %s, %#"PRIxTW";  // if (%s >= %s) goto %#"PRIxTW,
+		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
+      if ((unsigned_word)cpu->regs[rs1] >= (unsigned_word)cpu->regs[rs2])
+	{
+	  pc = cpu->pc + sb_imm;
+	  TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+	}
+      break;
+    case MATCH_BNE:
+      TRACE_INSN (cpu, "bne %s, %s, %#"PRIxTW";  // if (%s != %s) goto %#"PRIxTW,
+		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
+      if (cpu->regs[rs1] != cpu->regs[rs2])
+	{
+	  pc = cpu->pc + sb_imm;
+	  TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+	}
+      break;
+    case MATCH_JAL:
+      TRACE_INSN (cpu, "jal %s, %"PRIiTW";", rd_name, EXTRACT_UJTYPE_IMM (iw));
+      store_rd (cpu, rd, cpu->pc + 4);
+      pc = cpu->pc + EXTRACT_UJTYPE_IMM (iw);
+      TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+      break;
+    case MATCH_JALR:
+      TRACE_INSN (cpu, "jalr %s, %s, %"PRIiTW";", rd_name, rs1_name, i_imm);
+      store_rd (cpu, rd, cpu->pc + 4);
+      pc = cpu->regs[rs1] + i_imm;
+      TRACE_BRANCH (cpu, "to %#"PRIxTW, pc);
+      break;
+
+    case MATCH_LD:
+      TRACE_INSN (cpu, "ld %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd,
+	sim_core_read_unaligned_8 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm));
+      break;
+    case MATCH_LW:
+      TRACE_INSN (cpu, "lw %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      store_rd (cpu, rd, EXTEND32 (
+	sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm)));
+      break;
+    case MATCH_LWU:
+      TRACE_INSN (cpu, "lwu %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      store_rd (cpu, rd,
+	sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm));
+      break;
+    case MATCH_LH:
+      TRACE_INSN (cpu, "lh %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      store_rd (cpu, rd, EXTEND16 (
+	sim_core_read_unaligned_2 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm)));
+      break;
+    case MATCH_LHU:
+      TRACE_INSN (cpu, "lbu %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      store_rd (cpu, rd,
+	sim_core_read_unaligned_2 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm));
+      break;
+    case MATCH_LB:
+      TRACE_INSN (cpu, "lb %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      store_rd (cpu, rd, EXTEND8 (
+	sim_core_read_unaligned_1 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm)));
+      break;
+    case MATCH_LBU:
+      TRACE_INSN (cpu, "lbu %s, %"PRIiTW"(%s); // ",
+		  rd_name, i_imm, rs1_name);
+      store_rd (cpu, rd,
+	sim_core_read_unaligned_1 (cpu, cpu->pc, read_map,
+				   cpu->regs[rs1] + i_imm));
+      break;
+    case MATCH_SD:
+      TRACE_INSN (cpu, "sd %s, %"PRIiTW"(%s); // ",
+		  rs2_name, s_imm, rs1_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      sim_core_write_unaligned_8 (cpu, cpu->pc, write_map,
+				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
+      break;
+    case MATCH_SW:
+      TRACE_INSN (cpu, "sw %s, %"PRIiTW"(%s); // ",
+		  rs2_name, s_imm, rs1_name);
+      sim_core_write_unaligned_4 (cpu, cpu->pc, write_map,
+				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
+      break;
+    case MATCH_SH:
+      TRACE_INSN (cpu, "sh %s, %"PRIiTW"(%s); // ",
+		  rs2_name, s_imm, rs1_name);
+      sim_core_write_unaligned_2 (cpu, cpu->pc, write_map,
+				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
+      break;
+    case MATCH_SB:
+      TRACE_INSN (cpu, "sb %s, %"PRIiTW"(%s); // ",
+		  rs2_name, s_imm, rs1_name);
+      sim_core_write_unaligned_1 (cpu, cpu->pc, write_map,
+				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
+      break;
+
+    case MATCH_CSRRC:
+      TRACE_INSN (cpu, "csrrc");
+      switch (csr)
+	{
+#define DECLARE_CSR(name, num) \
+	case num: \
+	  store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
+	  store_csr (cpu, #name, num, &cpu->csr.name, \
+		     cpu->csr.name & !cpu->regs[rs1]); \
+	  break;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+	}
+      break;
+    case MATCH_CSRRS:
+      TRACE_INSN (cpu, "csrrs");
+      switch (csr)
+	{
+#define DECLARE_CSR(name, num) \
+	case num: \
+	  store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
+	  store_csr (cpu, #name, num, &cpu->csr.name, \
+		     cpu->csr.name | cpu->regs[rs1]); \
+	  break;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+	}
+      break;
+    case MATCH_CSRRW:
+      TRACE_INSN (cpu, "csrrw");
+      switch (csr)
+	{
+#define DECLARE_CSR(name, num) \
+	case num: \
+	  store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
+	  store_csr (cpu, #name, num, &cpu->csr.name, cpu->regs[rs1]); \
+	  break;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+	}
+      break;
+
+    case MATCH_RDCYCLE:
+      TRACE_INSN (cpu, "rdcycle %s;", rd_name);
+      store_rd (cpu, rd, fetch_csr (cpu, "cycle", CSR_CYCLE, &cpu->csr.cycle));
+      break;
+    case MATCH_RDCYCLEH:
+      TRACE_INSN (cpu, "rdcycleh %s;", rd_name);
+      RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, fetch_csr (cpu, "cycleh", CSR_CYCLEH, &cpu->csr.cycleh));
+      break;
+    case MATCH_RDINSTRET:
+      TRACE_INSN (cpu, "rdinstret %s;", rd_name);
+      store_rd (cpu, rd, fetch_csr (cpu, "instret", CSR_INSTRET, &cpu->csr.instret));
+      break;
+    case MATCH_RDINSTRETH:
+      TRACE_INSN (cpu, "rdinstreth %s;", rd_name);
+      RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, fetch_csr (cpu, "instreth", CSR_INSTRETH, &cpu->csr.instreth));
+      break;
+    case MATCH_RDTIME:
+      TRACE_INSN (cpu, "rdtime %s;", rd_name);
+      store_rd (cpu, rd, fetch_csr (cpu, "time", CSR_TIME, &cpu->csr.cycle));
+      break;
+    case MATCH_RDTIMEH:
+      TRACE_INSN (cpu, "rdtimeh %s;", rd_name);
+      RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, fetch_csr (cpu, "timeh", CSR_TIMEH, &cpu->csr.cycleh));
+      break;
+
+    case MATCH_FENCE:
+      TRACE_INSN (cpu, "fence;");
+      break;
+    case MATCH_FENCE_I:
+      TRACE_INSN (cpu, "fence.i;");
+      break;
+    case MATCH_SBREAK:
+      TRACE_INSN (cpu, "sbreak;");
+      /* GDB expects us to step over SBREAK.  */
+      sim_engine_halt (sd, cpu, NULL, cpu->pc + 4, sim_stopped, SIM_SIGTRAP);
+      break;
+    case MATCH_ECALL:
+      TRACE_INSN (cpu, "ecall;");
+      cpu->a0 = sim_syscall (cpu, cpu->a7, cpu->a0, cpu->a1, cpu->a2, cpu->a3);
+      break;
+    default:
+      TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+    }
+
+  return pc;
+}
+
+static unsigned64
+mulhu (unsigned64 a, unsigned64 b)
+{
+  uint64_t t;
+  uint32_t y1, y2, y3;
+  uint64_t a0 = (uint32_t)a, a1 = a >> 32;
+  uint64_t b0 = (uint32_t)b, b1 = b >> 32;
+
+  t = a1*b0 + ((a0*b0) >> 32);
+  y1 = t;
+  y2 = t >> 32;
+
+  t = a0*b1 + y1;
+  y1 = t;
+
+  t = a1*b1 + y2 + (t >> 32);
+  y2 = t;
+  y3 = t >> 32;
+
+  return ((uint64_t)y3 << 32) | y2;
+}
+
+static unsigned64
+mulh (signed64 a, signed64 b)
+{
+  int negate = (a < 0) != (b < 0);
+  uint64_t res = mulhu (a < 0 ? -a : a, b < 0 ? -b : b);
+  return negate ? ~res + (a * b == 0) : res;
+}
+
+static unsigned64
+mulhsu (signed64 a, unsigned64 b)
+{
+  int negate = a < 0;
+  uint64_t res = mulhu (a < 0 ? -a : a, b);
+  return negate ? ~res + (a * b == 0) : res;
+}
+
+static sim_cia
+execute_m (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
+  int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
+  int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
+  const char *rd_name = riscv_gpr_names_abi[rd];
+  const char *rs1_name = riscv_gpr_names_abi[rs1];
+  const char *rs2_name = riscv_gpr_names_abi[rs2];
+  unsigned_word tmp, dividend_max;
+  signed_word dividend32_max;
+  sim_cia pc = cpu->pc + 4;
+
+  dividend_max = -((unsigned_word)1 << (WITH_TARGET_WORD_BITSIZE - 1));
+  dividend32_max = INT32_MIN;
+
+  switch (op->match)
+    {
+    case MATCH_DIV:
+      TRACE_INSN (cpu, "div %s, %s, %s;  // %s = %s / %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (cpu->regs[rs1] == dividend_max && cpu->regs[rs2] == -1)
+	tmp = dividend_max;
+      else if (cpu->regs[rs2])
+	tmp = (signed_word)cpu->regs[rs1] / (signed_word)cpu->regs[rs2];
+      else
+	tmp = -1;
+      store_rd (cpu, rd, tmp);
+      break;
+    case MATCH_DIVW:
+      TRACE_INSN (cpu, "divw %s, %s, %s;  // %s = %s / %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      if (EXTEND32 (cpu->regs[rs1]) == dividend32_max
+	  && EXTEND32 (cpu->regs[rs2]) == -1)
+	tmp = 1 << 31;
+      else if (EXTEND32 (cpu->regs[rs2]))
+	tmp = EXTEND32 (cpu->regs[rs1]) / EXTEND32 (cpu->regs[rs2]);
+      else
+	tmp = -1;
+      store_rd (cpu, rd, EXTEND32 (tmp));
+      break;
+    case MATCH_DIVU:
+      TRACE_INSN (cpu, "divu %s, %s, %s;  // %s = %s / %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (cpu->regs[rs2])
+	store_rd (cpu, rd, (unsigned_word)cpu->regs[rs1]
+			   / (unsigned_word)cpu->regs[rs2]);
+      else
+	store_rd (cpu, rd, -1);
+      break;
+    case MATCH_DIVUW:
+      TRACE_INSN (cpu, "divuw %s, %s, %s;  // %s = %s / %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      if ((unsigned32)cpu->regs[rs2])
+	tmp = (unsigned32)cpu->regs[rs1] / (unsigned32)cpu->regs[rs2];
+      else
+	tmp = -1;
+      store_rd (cpu, rd, EXTEND32 (tmp));
+      break;
+    case MATCH_MUL:
+      TRACE_INSN (cpu, "mul %s, %s, %s;  // %s = %s * %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      store_rd (cpu, rd, cpu->regs[rs1] * cpu->regs[rs2]);
+      break;
+    case MATCH_MULW:
+      TRACE_INSN (cpu, "mulw %s, %s, %s;  // %s = %s * %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      store_rd (cpu, rd, EXTEND32 ((signed32)cpu->regs[rs1]
+				   * (signed32)cpu->regs[rs2]));
+      break;
+    case MATCH_MULH:
+      TRACE_INSN (cpu, "mulh %s, %s, %s;  // %s = %s * %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (RISCV_XLEN (cpu) == 32)
+	store_rd (cpu, rd, ((signed64)(signed_word)cpu->regs[rs1]
+			    * (signed64)(signed_word)cpu->regs[rs2]) >> 32);
+      else
+	store_rd (cpu, rd, mulh (cpu->regs[rs1], cpu->regs[rs2]));
+      break;
+    case MATCH_MULHU:
+      TRACE_INSN (cpu, "mulhu %s, %s, %s;  // %s = %s * %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (RISCV_XLEN (cpu) == 32)
+	store_rd (cpu, rd, ((unsigned64)cpu->regs[rs1]
+			    * (unsigned64)cpu->regs[rs2]) >> 32);
+      else
+	store_rd (cpu, rd, mulhu (cpu->regs[rs1], cpu->regs[rs2]));
+      break;
+    case MATCH_MULHSU:
+      TRACE_INSN (cpu, "mulhsu %s, %s, %s;  // %s = %s * %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (RISCV_XLEN (cpu) == 32)
+	store_rd (cpu, rd, ((signed64)(signed_word)cpu->regs[rs1]
+			    * (unsigned64)cpu->regs[rs2]) >> 32);
+      else
+	store_rd (cpu, rd, mulhsu (cpu->regs[rs1], cpu->regs[rs2]));
+      break;
+    case MATCH_REM:
+      TRACE_INSN (cpu, "rem %s, %s, %s;  // %s = %s %% %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (cpu->regs[rs1] == dividend_max && cpu->regs[rs2] == -1)
+	tmp = 0;
+      else if (cpu->regs[rs2])
+	tmp = (signed_word)cpu->regs[rs1] % (signed_word)cpu->regs[rs2];
+      else
+	tmp = cpu->regs[rs1];
+      store_rd (cpu, rd, tmp);
+      break;
+    case MATCH_REMW:
+      TRACE_INSN (cpu, "remw %s, %s, %s;  // %s = %s %% %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      if (EXTEND32 (cpu->regs[rs1]) == dividend32_max
+	  && EXTEND32 (cpu->regs[rs2]) == -1)
+	tmp = 0;
+      else if (EXTEND32 (cpu->regs[rs2]))
+	tmp = EXTEND32 (cpu->regs[rs1]) % EXTEND32 (cpu->regs[rs2]);
+      else
+	tmp = cpu->regs[rs1];
+      store_rd (cpu, rd, EXTEND32 (tmp));
+      break;
+    case MATCH_REMU:
+      TRACE_INSN (cpu, "remu %s, %s, %s;  // %s = %s %% %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      if (cpu->regs[rs2])
+	store_rd (cpu, rd, cpu->regs[rs1] % cpu->regs[rs2]);
+      else
+	store_rd (cpu, rd, cpu->regs[rs1]);
+      break;
+    case MATCH_REMUW:
+      TRACE_INSN (cpu, "remuw %s, %s, %s;  // %s = %s %% %s",
+		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
+      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+      if ((unsigned32)cpu->regs[rs2])
+	tmp = (unsigned32)cpu->regs[rs1] % (unsigned32)cpu->regs[rs2];
+      else
+	tmp = cpu->regs[rs1];
+      store_rd (cpu, rd, EXTEND32 (tmp));
+      break;
+    default:
+      TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+    }
+
+  return pc;
+}
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+static sim_cia
+execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
+  int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
+  int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
+  const char *rd_name = riscv_gpr_names_abi[rd];
+  const char *rs1_name = riscv_gpr_names_abi[rs1];
+  const char *rs2_name = riscv_gpr_names_abi[rs2];
+  struct atomic_mem_reserved_list *amo_prev, *amo_curr;
+  unsigned_word tmp;
+  sim_cia pc = cpu->pc + 4;
+
+  /* Handle these two load/store operations specifically.  */
+  switch (op->match)
+    {
+    case MATCH_LR_W:
+      TRACE_INSN (cpu, "%s %s, (%s);", op->name, rd_name, rs1_name);
+      store_rd (cpu, rd,
+	sim_core_read_unaligned_4 (cpu, cpu->pc, read_map, cpu->regs[rs1]));
+
+      /* Walk the reservation list to find an existing match.  */
+      amo_curr = sd->amo_reserved_list;
+      while (amo_curr)
+	{
+	  if (amo_curr->addr == cpu->regs[rs1])
+	    goto done;
+	  amo_curr = amo_curr->next;
+	}
+
+      /* No reservation exists, so add one.  */
+      amo_curr = xmalloc (sizeof (*amo_curr));
+      amo_curr->addr = cpu->regs[rs1];
+      amo_curr->next = sd->amo_reserved_list;
+      sd->amo_reserved_list = amo_curr;
+      goto done;
+    case MATCH_SC_W:
+      TRACE_INSN (cpu, "%s %s, %s, (%s);", op->name, rd_name, rs2_name, rs1_name);
+
+      /* Walk the reservation list to find a match.  */
+      amo_curr = amo_prev = sd->amo_reserved_list;
+      while (amo_curr)
+	{
+	  if (amo_curr->addr == cpu->regs[rs1])
+	    {
+	      /* We found a reservation, so operate it.  */
+	      sim_core_write_unaligned_4 (cpu, cpu->pc, write_map,
+					  cpu->regs[rs1], cpu->regs[rs2]);
+	      store_rd (cpu, rd, 0);
+	      if (amo_curr == sd->amo_reserved_list)
+		sd->amo_reserved_list = amo_curr->next;
+	      else
+		amo_prev->next = amo_curr->next;
+	      free (amo_curr);
+	      goto done;
+	    }
+	  amo_prev = amo_curr;
+	  amo_curr = amo_curr->next;
+	}
+
+      /* If we're still here, then no reservation exists, so mark as failed.  */
+      store_rd (cpu, rd, 1);
+      goto done;
+    }
+
+  /* Handle the rest of the atomic insns with common code paths.  */
+  TRACE_INSN (cpu, "%s %s, %s, (%s);",
+	      op->name, rd_name, rs2_name, rs1_name);
+  if (op->subset[0] == '6')
+    store_rd (cpu, rd,
+      sim_core_read_unaligned_8 (cpu, cpu->pc, read_map, cpu->regs[rs1]));
+  else
+    store_rd (cpu, rd, EXTEND32 (
+      sim_core_read_unaligned_4 (cpu, cpu->pc, read_map, cpu->regs[rs1])));
+
+  switch (op->match)
+    {
+    case MATCH_AMOADD_D:
+    case MATCH_AMOADD_W:
+      tmp = cpu->regs[rd] + cpu->regs[rs2];
+      break;
+    case MATCH_AMOAND_D:
+    case MATCH_AMOAND_W:
+      tmp = cpu->regs[rd] & cpu->regs[rs2];
+      break;
+    case MATCH_AMOMAX_D:
+    case MATCH_AMOMAX_W:
+      tmp = MAX ((signed_word)cpu->regs[rd], (signed_word)cpu->regs[rs2]);
+      break;
+    case MATCH_AMOMAXU_D:
+    case MATCH_AMOMAXU_W:
+      tmp = MAX ((unsigned_word)cpu->regs[rd], (unsigned_word)cpu->regs[rs2]);
+      break;
+    case MATCH_AMOMIN_D:
+    case MATCH_AMOMIN_W:
+      tmp = MIN ((signed_word)cpu->regs[rd], (signed_word)cpu->regs[rs2]);
+      break;
+    case MATCH_AMOMINU_D:
+    case MATCH_AMOMINU_W:
+      tmp = MIN ((unsigned_word)cpu->regs[rd], (unsigned_word)cpu->regs[rs2]);
+      break;
+    case MATCH_AMOOR_D:
+    case MATCH_AMOOR_W:
+      tmp = cpu->regs[rd] | cpu->regs[rs2];
+      break;
+    case MATCH_AMOSWAP_D:
+    case MATCH_AMOSWAP_W:
+      tmp = cpu->regs[rs2];
+      break;
+    case MATCH_AMOXOR_D:
+    case MATCH_AMOXOR_W:
+      tmp = cpu->regs[rd] ^ cpu->regs[rs2];
+      break;
+    default:
+      TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
+      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+    }
+
+  if (op->subset[0] == '6')
+    sim_core_write_unaligned_8 (cpu, cpu->pc, write_map, cpu->regs[rs1], tmp);
+  else
+    sim_core_write_unaligned_4 (cpu, cpu->pc, write_map, cpu->regs[rs1], tmp);
+
+ done:
+  return pc;
+}
+
+static sim_cia
+execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  const char *subset = op->subset;
+
+ rescan:
+  switch (subset[0])
+    {
+    case 'A':
+      return execute_a (cpu, iw, op);
+    case 'I':
+      return execute_i (cpu, iw, op);
+    case 'M':
+      return execute_m (cpu, iw, op);
+    case '3':
+      if (subset[1] == '2')
+	{
+	  RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
+	  subset += 2;
+	  goto rescan;
+	}
+      goto case_default;
+    case '6':
+      if (subset[1] == '4')
+	{
+	  RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+	  subset += 2;
+	  goto rescan;
+	}
+      goto case_default;
+    case_default:
+    default:
+      TRACE_INSN (cpu, "UNHANDLED EXTENSION: %s", op->subset);
+      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+    }
+
+  return cpu->pc + riscv_insn_length (iw);
+}
+
+/* Decode & execute a single instruction.  */
+void step_once (SIM_CPU *cpu)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  unsigned_word iw;
+  unsigned int len;
+  sim_cia pc = cpu->pc;
+  const struct riscv_opcode *op;
+
+  if (TRACE_ANY_P (cpu))
+    trace_prefix (sd, cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu),
+		  NULL, 0, " "); /* Use a space for gcc warnings.  */
+
+  iw = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);
+
+  /* Reject non-32-bit opcodes first.  */
+  len = riscv_insn_length (iw);
+  if (len != 4)
+    {
+      sim_io_printf (sd, "sim: bad insn len %#x @ %#"PRIxTA": %#"PRIxTW"\n",
+		     len, pc, iw);
+      sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
+    }
+
+  iw |= ((unsigned_word)sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2) << 16);
+
+  TRACE_CORE (cpu, "0x%08"PRIxTW, iw);
+
+  op = riscv_hash[OP_HASH_IDX (iw)];
+  if (!op)
+    sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
+
+  for (; op->name; op++)
+    if ((op->match_func) (op, iw) && !(op->pinfo & INSN_ALIAS))
+      {
+	pc = execute_one (cpu, iw, op);
+	break;
+      }
+
+  /* TODO: Handle overflow into high 32 bits.  */
+  /* TODO: Try to use a common counter and only update on demand (reads).  */
+  ++cpu->csr.cycle;
+  ++cpu->csr.instret;
+
+  cpu->pc = pc;
+}
+
+/* Return the program counter for this cpu. */
+static sim_cia
+pc_get (sim_cpu *cpu)
+{
+  return cpu->pc;
+}
+
+/* Set the program counter for this cpu to the new pc value. */
+static void
+pc_set (sim_cpu *cpu, sim_cia pc)
+{
+  cpu->pc = pc;
+}
+
+static int
+reg_fetch (sim_cpu *cpu, int rn, unsigned char *buf, int len)
+{
+  if (len <= 0 || len > sizeof (unsigned_word))
+    return -1;
+
+  switch (rn)
+    {
+    case SIM_RISCV_RA_REGNUM ... SIM_RISCV_T6_REGNUM:
+      memcpy (buf, &cpu->regs[rn], len);
+      return len;
+    case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM:
+      memcpy (buf, &cpu->fpregs[rn], len);
+      return len;
+    case SIM_RISCV_PC_REGNUM:
+      memcpy (buf, &cpu->pc, len);
+      return len;
+
+#define DECLARE_CSR(name, num) \
+    case SIM_RISCV_ ## num ## _REGNUM: \
+      memcpy (buf, &cpu->csr.name, len); \
+      return len;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+
+    default:
+      return -1;
+    }
+}
+
+static int
+reg_store (sim_cpu *cpu, int rn, unsigned char *buf, int len)
+{
+  if (len <= 0 || len > sizeof (unsigned_word))
+    return -1;
+
+  switch (rn)
+    {
+    case SIM_RISCV_RA_REGNUM ... SIM_RISCV_T6_REGNUM:
+      memcpy (&cpu->regs[rn], buf, len);
+      return len;
+    case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM:
+      memcpy (&cpu->fpregs[rn], buf, len);
+      return len;
+    case SIM_RISCV_PC_REGNUM:
+      memcpy (&cpu->pc, buf, len);
+      return len;
+
+#define DECLARE_CSR(name, num) \
+    case SIM_RISCV_ ## num ## _REGNUM: \
+      memcpy (&cpu->csr.name, buf, len); \
+      return len;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+
+    default:
+      return -1;
+    }
+}
+
+/* Initialize the state for a single cpu.  Usuaully this involves clearing all
+   registers back to their reset state.  Should also hook up the fetch/store
+   helper functions too.  */
+void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)
+{
+  const char *extensions;
+  int i;
+
+  memset (cpu->regs, 0, sizeof (cpu->regs));
+
+  CPU_PC_FETCH (cpu) = pc_get;
+  CPU_PC_STORE (cpu) = pc_set;
+  CPU_REG_FETCH (cpu) = reg_fetch;
+  CPU_REG_STORE (cpu) = reg_store;
+
+  if (!riscv_hash[0])
+    {
+      const struct riscv_opcode *op;
+
+      for (op = riscv_opcodes; op->name; op++)
+	if (!riscv_hash[OP_HASH_IDX (op->match)])
+	  riscv_hash[OP_HASH_IDX (op->match)] = op;
+    }
+
+  cpu->csr.misa = 0;
+  /* RV32 sets this field to 0, and we don't really support RV128 yet.  */
+  if (RISCV_XLEN (cpu) == 64)
+    cpu->csr.misa |= (unsigned64)2 << 62;
+
+  /* Skip the leading "rv" prefix and the two numbers.  */
+  extensions = MODEL_NAME (CPU_MODEL (cpu)) + 4;
+  for (i = 0; i < 26; ++i)
+    {
+      char ext = 'A' + i;
+
+      if (ext == 'X')
+	continue;
+      else if (strchr (extensions, ext) != NULL)
+	{
+	  if (ext == 'G')
+	    cpu->csr.misa |= 0x1129;  /* G = IMAFD.  */
+	  else
+	    cpu->csr.misa |= (1 << i);
+	}
+    }
+
+  cpu->csr.mimpid = 0x8000;
+  cpu->csr.mhartid = mhartid;
+}
+
+/* Some utils don't like having a NULL environ.  */
+static const char * const simple_env[] = { "HOME=/", "PATH=/bin", NULL };
+
+/* Count the number of arguments in an argv.  */
+static int
+count_argv (const char * const *argv)
+{
+  int i;
+
+  if (!argv)
+    return -1;
+
+  for (i = 0; argv[i] != NULL; ++i)
+    continue;
+  return i;
+}
+
+void initialize_env (SIM_DESC sd, const char * const *argv,
+		     const char * const *env)
+{
+  SIM_CPU *cpu = STATE_CPU (sd, 0);
+  int i;
+  int argc, argv_flat;
+  int envc, env_flat;
+  address_word sp, sp_flat;
+  unsigned char null[8] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+
+  /* Figure out how many bytes the argv strings take up.  */
+  argc = count_argv (argv);
+  if (argc == -1)
+    argc = 0;
+  argv_flat = argc; /* NUL bytes.  */
+  for (i = 0; i < argc; ++i)
+    argv_flat += strlen (argv[i]);
+
+  /* Figure out how many bytes the environ strings take up.  */
+  if (!env)
+    env = simple_env;
+  envc = count_argv (env);
+  env_flat = envc; /* NUL bytes.  */
+  for (i = 0; i < envc; ++i)
+    env_flat += strlen (env[i]);
+
+  /* Make space for the strings themselves.  */
+  sp_flat = (DEFAULT_MEM_SIZE - argv_flat - env_flat) & -sizeof (address_word);
+  /* Then the pointers to the strings.  */
+  sp = sp_flat - ((argc + 1 + envc + 1) * sizeof (address_word));
+  /* Then the argc.  */
+  sp -= sizeof (unsigned_word);
+
+  /* Set up the regs the libgloss crt0 expects.  */
+  cpu->a0 = argc;
+  cpu->sp = sp;
+
+  /* First push the argc value.  */
+  sim_write (sd, sp, (void *)&argc, sizeof (unsigned_word));
+  sp += sizeof (unsigned_word);
+
+  /* Then the actual argv strings so we know where to point argv[].  */
+  for (i = 0; i < argc; ++i)
+    {
+      unsigned len = strlen (argv[i]) + 1;
+      sim_write (sd, sp_flat, (void *)argv[i], len);
+      sim_write (sd, sp, (void *)&sp_flat, sizeof (address_word));
+      sp_flat += len;
+      sp += sizeof (address_word);
+    }
+  sim_write (sd, sp, null, sizeof (address_word));
+  sp += sizeof (address_word);
+
+  /* Then the actual env strings so we know where to point env[].  */
+  for (i = 0; i < envc; ++i)
+    {
+      unsigned len = strlen (env[i]) + 1;
+      sim_write (sd, sp_flat, (void *)env[i], len);
+      sim_write (sd, sp, (void *)&sp_flat, sizeof (address_word));
+      sp_flat += len;
+      sp += sizeof (address_word);
+    }
+}
diff --git a/sim/riscv/sim-main.h b/sim/riscv/sim-main.h
new file mode 100644
index 0000000..6e53587
--- /dev/null
+++ b/sim/riscv/sim-main.h
@@ -0,0 +1,87 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2014 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   This file is part of 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/>.  */
+
+#ifndef SIM_MAIN_H
+#define SIM_MAIN_H
+
+#include "tconfig.h"
+#include "sim-basics.h"
+#include "machs.h"
+#include "sim-base.h"
+
+struct _sim_cpu {
+  union {
+    unsigned_word regs[32];
+    struct {
+      /* These are the ABI names.  */
+      unsigned_word zero, ra, sp, gp, tp;
+      unsigned_word t0, t1, t2;
+      unsigned_word s0, s1;
+      unsigned_word a0, a1, a2, a3, a4, a5, a6, a7;
+      unsigned_word s2, s3, s4, s5, s6, s7, s8, s9, s10, s11;
+      unsigned_word t3, t4, t5, t6;
+    };
+  };
+  union {
+    unsigned_word fpregs[32];
+    struct {
+      /* These are the ABI names.  */
+      unsigned_word ft0, ft1, ft2, ft3, ft4, ft5, ft6, ft7;
+      unsigned_word fs0, fs1;
+      unsigned_word fa0, fa1, fa2, fa3, fa4, fa5, fa6, fa7;
+      unsigned_word fs2, fs3, fs4, fs5, fs6, fs7, fs8, fs9, fs10, fs11;
+      unsigned_word ft8, ft9, ft10, ft11;
+    };
+  };
+  sim_cia pc;
+
+  struct {
+#define DECLARE_CSR(name, num) unsigned_word name;
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+  } csr;
+
+  sim_cpu_base base;
+};
+
+struct atomic_mem_reserved_list;
+struct atomic_mem_reserved_list {
+  struct atomic_mem_reserved_list *next;
+  address_word addr;
+};
+
+struct sim_state {
+  sim_cpu *cpu[MAX_NR_PROCESSORS];
+  struct atomic_mem_reserved_list *amo_reserved_list;
+
+  /* ... simulator specific members ... */
+  sim_state_base base;
+};
+
+extern void step_once (SIM_CPU *);
+extern void initialize_cpu (SIM_DESC, SIM_CPU *, int);
+extern void initialize_env (SIM_DESC, const char * const *argv,
+			    const char * const *env);
+
+#define DEFAULT_MEM_SIZE (16 * 1024 * 1024)
+
+#define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu))
+
+#endif
diff --git a/sim/riscv/tconfig.h b/sim/riscv/tconfig.h
new file mode 100644
index 0000000..25e6a79
--- /dev/null
+++ b/sim/riscv/tconfig.h
@@ -0,0 +1,4 @@
+/* RISC-V target configuration file.  -*- C -*- */
+
+/* ??? Temporary hack until model support unified.  */
+#define SIM_HAVE_MODEL
diff --git a/sim/testsuite/sim/riscv/ChangeLog b/sim/testsuite/sim/riscv/ChangeLog
new file mode 100644
index 0000000..72dd0c3
--- /dev/null
+++ b/sim/testsuite/sim/riscv/ChangeLog
@@ -0,0 +1,3 @@
+2015-03-29  Mike Frysinger  <vapier@gentoo.org>
+
+	* allinsn.exp, exit-0.s, exit-7.s, isa.inc, testutils.inc: New files.
diff --git a/sim/testsuite/sim/riscv/allinsn.exp b/sim/testsuite/sim/riscv/allinsn.exp
new file mode 100644
index 0000000..4ed7cff
--- /dev/null
+++ b/sim/testsuite/sim/riscv/allinsn.exp
@@ -0,0 +1,15 @@
+# mcore simulator testsuite
+
+if [istarget riscv-*] {
+    # all machines
+    set all_machs "riscv"
+
+    foreach src [lsort [glob -nocomplain $srcdir/$subdir/*.s]] {
+	# If we're only testing specific files and this isn't one of them,
+	# skip it.
+	if ![runtest_file_p $runtests $src] {
+	    continue
+	}
+	run_sim_test $src $all_machs
+    }
+}
diff --git a/sim/testsuite/sim/riscv/pass.s b/sim/testsuite/sim/riscv/pass.s
new file mode 100644
index 0000000..bd428ca
--- /dev/null
+++ b/sim/testsuite/sim/riscv/pass.s
@@ -0,0 +1,7 @@
+# check that the sim doesn't die immediately.
+# mach: riscv
+
+.include "testutils.inc"
+
+	start
+	pass
diff --git a/sim/testsuite/sim/riscv/testutils.inc b/sim/testsuite/sim/riscv/testutils.inc
new file mode 100644
index 0000000..bb92ac0
--- /dev/null
+++ b/sim/testsuite/sim/riscv/testutils.inc
@@ -0,0 +1,50 @@
+# MACRO: exit
+	.macro exit nr
+	li a0, \nr
+	# The exit utility function.
+	li a7, 93;
+	.endm
+
+# MACRO: pass
+# Write 'pass' to stdout and quit
+	.macro pass
+	# syscall write().
+	li a7, 64;
+	# Use stdout.
+	li a0, 1;
+	# Point to the string.
+	lla a1, 1f;
+	# Number of bytes to write.
+	li a2, 5;
+	# Trigger OS trap.
+	ecall;
+	exit 0;
+	.data
+	1: .asciz "pass\n"
+	.endm
+
+# MACRO: fail
+# Write 'fail' to stdout and quit
+	.macro fail
+	# syscall write();
+	li a7, 64;
+	# Use stdout.
+	li a0, 1;
+	# Point to the string.
+	lla a1, 1f;
+	# Number of bytes to write.
+	li a2, 5;
+	# Trigger OS trap.
+	ecall;
+	exit 0;
+	.data
+	1: .asciz "fail\n"
+	.endm
+
+# MACRO: start
+# All assembler tests should start with a call to "start"
+	.macro start
+	.text
+.global _start
+_start:
+	.endm
-- 
2.10.2


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]