Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH 1/2] sim: riscv: new port
@ 2021-01-12 11:18 Mike Frysinger via Gdb-patches
  2021-01-12 11:18 ` [PATCH 2/2] riscv: enable gdb/sim integration Mike Frysinger via Gdb-patches
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Mike Frysinger via Gdb-patches @ 2021-01-12 11:18 UTC (permalink / raw)
  To: gdb-patches

---
 sim/common/ChangeLog                  |     4 +
 sim/common/nltvals.def                |    48 +
 sim/configure                         |     8 +
 sim/configure.tgt                     |     3 +
 sim/riscv/ChangeLog                   |     5 +
 sim/riscv/Makefile.in                 |    30 +
 sim/riscv/aclocal.m4                  |   119 +
 sim/riscv/config.in                   |   242 +
 sim/riscv/configure                   | 16145 ++++++++++++++++++++++++
 sim/riscv/configure.ac                |    28 +
 sim/riscv/interp.c                    |   153 +
 sim/riscv/machs.c                     |   125 +
 sim/riscv/machs.h                     |    45 +
 sim/riscv/model_list.def              |     9 +
 sim/riscv/sim-main.c                  |  1149 ++
 sim/riscv/sim-main.h                  |    86 +
 sim/testsuite/ChangeLog               |     5 +
 sim/testsuite/configure               |     3 +
 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 |    52 +
 22 files changed, 18284 insertions(+)
 create mode 100644 sim/riscv/ChangeLog
 create mode 100644 sim/riscv/Makefile.in
 create mode 100644 sim/riscv/aclocal.m4
 create mode 100644 sim/riscv/config.in
 create mode 100755 sim/riscv/configure
 create mode 100644 sim/riscv/configure.ac
 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/model_list.def
 create mode 100644 sim/riscv/sim-main.c
 create mode 100644 sim/riscv/sim-main.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/common/ChangeLog b/sim/common/ChangeLog
index 608a0859ce89..3b5439a6afe9 100644
--- a/sim/common/ChangeLog
+++ b/sim/common/ChangeLog
@@ -1,3 +1,7 @@
+2021-01-12  Mike Frysinger  <vapier@gentoo.org>
+
+	* nltvals.def: Regenerate from the latest libgloss sources.
+
 2021-01-12  Mike Frysinger  <vapier@gentoo.org>
 
 	* sim-profile.h [!WITH_PROFILE]: Rewrite to use #error.
diff --git a/sim/common/nltvals.def b/sim/common/nltvals.def
index 92ccc9aded8b..60467f343d27 100644
--- a/sim/common/nltvals.def
+++ b/sim/common/nltvals.def
@@ -605,3 +605,51 @@
 /* end pru sys target macros */
 #endif
 #endif
+#ifdef NL_TARGET_riscv
+#ifdef sys_defs
+/* from syscall.h */
+/* begin riscv sys target macros */
+ { "SYS_access", 1033 },
+ { "SYS_brk", 214 },
+ { "SYS_chdir", 49 },
+ { "SYS_close", 57 },
+ { "SYS_dup", 23 },
+ { "SYS_exit", 93 },
+ { "SYS_exit_group", 94 },
+ { "SYS_faccessat", 48 },
+ { "SYS_fcntl", 25 },
+ { "SYS_fstat", 80 },
+ { "SYS_fstatat", 79 },
+ { "SYS_getcwd", 17 },
+ { "SYS_getdents", 61 },
+ { "SYS_getegid", 177 },
+ { "SYS_geteuid", 175 },
+ { "SYS_getgid", 176 },
+ { "SYS_getmainvars", 2011 },
+ { "SYS_getpid", 172 },
+ { "SYS_gettimeofday", 169 },
+ { "SYS_getuid", 174 },
+ { "SYS_kill", 129 },
+ { "SYS_link", 1025 },
+ { "SYS_lseek", 62 },
+ { "SYS_lstat", 1039 },
+ { "SYS_mkdir", 1030 },
+ { "SYS_mmap", 222 },
+ { "SYS_mremap", 216 },
+ { "SYS_munmap", 215 },
+ { "SYS_open", 1024 },
+ { "SYS_openat", 56 },
+ { "SYS_pread", 67 },
+ { "SYS_pwrite", 68 },
+ { "SYS_read", 63 },
+ { "SYS_rt_sigaction", 134 },
+ { "SYS_stat", 1038 },
+ { "SYS_time", 1062 },
+ { "SYS_times", 153 },
+ { "SYS_uname", 160 },
+ { "SYS_unlink", 1026 },
+ { "SYS_write", 64 },
+ { "SYS_writev", 66 },
+/* end riscv sys target macros */
+#endif
+#endif
diff --git a/sim/configure.tgt b/sim/configure.tgt
index a48c6966e8ae..5f201060f1c1 100644
--- a/sim/configure.tgt
+++ b/sim/configure.tgt
@@ -85,6 +85,9 @@ case "${target}" in
    pru*-*-*)
        SIM_ARCH(pru)
        ;;
+   riscv*-*-*)
+       SIM_ARCH(riscv)
+       ;;
    rl78-*-*)
        SIM_ARCH(rl78)
        ;;
diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
new file mode 100644
index 000000000000..f152de1e4646
--- /dev/null
+++ b/sim/riscv/ChangeLog
@@ -0,0 +1,5 @@
+2021-01-12  Mike Frysinger  <vapier@gentoo.org>
+
+	* Makefile.in, configure.ac, interp.c, machs.c, machs.h,
+	model_list.def, sim-main.c, sim-main.h: New files.
+	* aclocal.m4, config.in, configure: Regenerated.
diff --git a/sim/riscv/Makefile.in b/sim/riscv/Makefile.in
new file mode 100644
index 000000000000..17cb288eba3d
--- /dev/null
+++ b/sim/riscv/Makefile.in
@@ -0,0 +1,30 @@
+#    Makefile template for Configure for the example basic simulator.
+#    Copyright (C) 2005-2021 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-resume.o \
+	interp.o \
+	machs.o \
+	sim-main.o
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/riscv/aclocal.m4 b/sim/riscv/aclocal.m4
new file mode 100644
index 000000000000..e9f11c775c31
diff --git a/sim/riscv/config.in b/sim/riscv/config.in
new file mode 100644
index 000000000000..cb5ea1b01c95
diff --git a/sim/riscv/configure b/sim/riscv/configure
new file mode 100755
index 000000000000..8236dc35c12a
diff --git a/sim/riscv/configure.ac b/sim/riscv/configure.ac
new file mode 100644
index 000000000000..6d5dce917504
--- /dev/null
+++ b/sim/riscv/configure.ac
@@ -0,0 +1,28 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+sinclude(../common/acinclude.m4)
+
+SIM_AC_COMMON
+
+SIM_AC_OPTION_ENDIAN(LITTLE)
+SIM_AC_OPTION_ALIGNMENT(NONSTRICT_ALIGNMENT)
+SIM_AC_OPTION_ENVIRONMENT
+SIM_AC_OPTION_WARNINGS
+
+# Select the default model for the target.
+riscv_model=
+case "${target}" in
+riscv32*) riscv_model="RV32G" ;;
+riscv*) riscv_model="RV64G" ;;
+esac
+SIM_AC_OPTION_DEFAULT_MODEL(${riscv_model})
+
+# Select the bitsize of the target.
+riscv_addr_bitsize=
+case "${target}" in
+riscv32*) riscv_addr_bitsize=32 ;;
+riscv*) riscv_addr_bitsize=64 ;;
+esac
+SIM_AC_OPTION_BITSIZE($riscv_addr_bitsize)
+
+SIM_AC_OUTPUT
diff --git a/sim/riscv/interp.c b/sim/riscv/interp.c
new file mode 100644
index 000000000000..1bf60a43aec4
--- /dev/null
+++ b/sim/riscv/interp.c
@@ -0,0 +1,153 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2021 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"
+#include "sim-options.h"
+\f
+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);
+    }
+}
+\f
+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;
+
+  /* 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 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;
+}
+\f
+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_ADDR 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 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);
+    }
+
+  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 000000000000..853a3afb42f6
--- /dev/null
+++ b/sim/riscv/machs.c
@@ -0,0 +1,125 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2021 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 000000000000..903488bc7650
--- /dev/null
+++ b/sim/riscv/machs.h
@@ -0,0 +1,45 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2021 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/model_list.def b/sim/riscv/model_list.def
new file mode 100644
index 000000000000..5efd85ab280f
--- /dev/null
+++ b/sim/riscv/model_list.def
@@ -0,0 +1,9 @@
+M(G)
+M(I)
+M(IM)
+M(IMA)
+M(IA)
+M(E)
+M(EM)
+M(EMA)
+M(EA)
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
new file mode 100644
index 000000000000..15a0eb02ae26
--- /dev/null
+++ b/sim/riscv/sim-main.c
@@ -0,0 +1,1149 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2021 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 "targ-vals.h"
+\f
+#define TRACE_REG(cpu, reg) \
+  TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \
+		  cpu->regs[reg])
+\f
+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.time));
+      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.timeh));
+      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)
+{
+#ifdef __GNUC__
+  return ((__int128)a * b) >> 64;
+#else
+  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;
+#endif
+}
+
+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;
+  sim_cia pc = cpu->pc + 4;
+
+  dividend_max = -((unsigned_word) 1 << (WITH_TARGET_WORD_BITSIZE - 1));
+
+  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[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[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->xlen_requirement == 64)
+    tmp = sim_core_read_unaligned_8 (cpu, cpu->pc, read_map, cpu->regs[rs1]);
+  else
+    tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
+					       cpu->regs[rs1]));
+  store_rd (cpu, rd, tmp);
+
+  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->xlen_requirement == 64)
+    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);
+
+  if (op->xlen_requirement == 32)
+    RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
+  else if (op->xlen_requirement == 64)
+    RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+
+  switch (op->insn_class)
+    {
+    case INSN_CLASS_A:
+      return execute_a (cpu, iw, op);
+    case INSN_CLASS_I:
+      return execute_i (cpu, iw, op);
+    case INSN_CLASS_M:
+      return execute_m (cpu, iw, op);
+    default:
+      TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
+      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;
+  int xlen = RISCV_XLEN (cpu);
+
+  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);
+
+  /* NB: Same loop logic as riscv_disassemble_insn.  */
+  for (; op->name; op++)
+    {
+      /* Does the opcode match?  */
+      if (! op->match_func (op, iw))
+	continue;
+      /* Is this a pseudo-instruction and may we print it as such?  */
+      if (op->pinfo & INSN_ALIAS)
+	continue;
+      /* Is this instruction restricted to a certain value of XLEN?  */
+      if (op->xlen_requirement != 0 && op->xlen_requirement != xlen)
+	continue;
+
+      /* It's a match.  */
+      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;
+}
+\f
+/* 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;
+}
+
+/* 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;
+
+  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;
+}
+\f
+/* 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 000000000000..4a1b31ee2fec
--- /dev/null
+++ b/sim/riscv/sim-main.h
@@ -0,0 +1,86 @@
+/* RISC-V simulator.
+
+   Copyright (C) 2005-2021 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 "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, ...) 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 (64 * 1024 * 1024)
+
+#define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu))
+
+#endif
diff --git a/sim/testsuite/ChangeLog b/sim/testsuite/ChangeLog
index a32672a46bea..72cfa195542d 100644
--- a/sim/testsuite/ChangeLog
+++ b/sim/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2021-01-12  Mike Frysinger  <vapier@gentoo.org>
+
+	* configure: Regenerate.
+	* sim/riscv: New directory.
+
 2021-01-11  Mike Frysinger  <vapier@gentoo.org>
 
 	* common/alu-tst.c: Include stdlib.h.
diff --git a/sim/testsuite/sim/riscv/ChangeLog b/sim/testsuite/sim/riscv/ChangeLog
new file mode 100644
index 000000000000..05c42fc7d6e5
--- /dev/null
+++ b/sim/testsuite/sim/riscv/ChangeLog
@@ -0,0 +1,3 @@
+2021-01-12  Mike Frysinger  <vapier@gentoo.org>
+
+	* allinsn.exp, pass.s, 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 000000000000..03bec1b541e7
--- /dev/null
+++ b/sim/testsuite/sim/riscv/allinsn.exp
@@ -0,0 +1,15 @@
+# RISC-V 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 000000000000..bd428ca16772
--- /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 000000000000..138417009122
--- /dev/null
+++ b/sim/testsuite/sim/riscv/testutils.inc
@@ -0,0 +1,52 @@
+# MACRO: exit
+	.macro exit nr
+	li a0, \nr
+	# The exit utility function.
+	li a7, 93;
+	# Trigger OS trap.
+	ecall;
+	.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.28.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2] riscv: enable gdb/sim integration
  2021-01-12 11:18 [PATCH 1/2] sim: riscv: new port Mike Frysinger via Gdb-patches
@ 2021-01-12 11:18 ` Mike Frysinger via Gdb-patches
  2021-02-04 10:45   ` Andrew Burgess
  2021-02-03 23:50 ` [PATCH 1/2] sim: riscv: new port Jim Wilson
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: Mike Frysinger via Gdb-patches @ 2021-01-12 11:18 UTC (permalink / raw)
  To: gdb-patches

Now the simulator can be loaded via gdb using "target sim".

gdb/:

	* configure.tgt (riscv*-*-*): Set gdb_sim.

include/gdb/:

	* sim-riscv.h: New file.
---
 gdb/configure.tgt       |  1 +
 include/gdb/sim-riscv.h | 99 +++++++++++++++++++++++++++++++++++++++++
 sim/riscv/ChangeLog     |  6 +++
 sim/riscv/sim-main.c    | 70 +++++++++++++++++++++++++++++
 4 files changed, 176 insertions(+)
 create mode 100644 include/gdb/sim-riscv.h

diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 6e0398387482..5440780065f0 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -554,6 +554,7 @@ riscv*-*-linux*)
 riscv*-*-*)
 	# Target: RISC-V architecture
 	gdb_target_obs=""
+	gdb_sim=../sim/riscv/libsim.a
 	;;
 
 rl78-*-elf)
diff --git a/include/gdb/sim-riscv.h b/include/gdb/sim-riscv.h
new file mode 100644
index 000000000000..23e5f766aa6c
--- /dev/null
+++ b/include/gdb/sim-riscv.h
@@ -0,0 +1,99 @@
+/* This file defines the interface between the RISC-V simulator and GDB.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+/* Order has to match gdb riscv-tdep list.  */
+enum sim_riscv_regnum {
+  SIM_RISCV_ZERO_REGNUM = 0,
+  SIM_RISCV_RA_REGNUM,
+  SIM_RISCV_SP_REGNUM,
+  SIM_RISCV_GP_REGNUM,
+  SIM_RISCV_TP_REGNUM,
+  SIM_RISCV_T0_REGNUM,
+  SIM_RISCV_T1_REGNUM,
+  SIM_RISCV_T2_REGNUM,
+  SIM_RISCV_S0_REGNUM,
+#define SIM_RISCV_FP_REGNUM SIM_RISCV_S0_REGNUM
+  SIM_RISCV_S1_REGNUM,
+  SIM_RISCV_A0_REGNUM,
+  SIM_RISCV_A1_REGNUM,
+  SIM_RISCV_A2_REGNUM,
+  SIM_RISCV_A3_REGNUM,
+  SIM_RISCV_A4_REGNUM,
+  SIM_RISCV_A5_REGNUM,
+  SIM_RISCV_A6_REGNUM,
+  SIM_RISCV_A7_REGNUM,
+  SIM_RISCV_S2_REGNUM,
+  SIM_RISCV_S3_REGNUM,
+  SIM_RISCV_S4_REGNUM,
+  SIM_RISCV_S5_REGNUM,
+  SIM_RISCV_S6_REGNUM,
+  SIM_RISCV_S7_REGNUM,
+  SIM_RISCV_S8_REGNUM,
+  SIM_RISCV_S9_REGNUM,
+  SIM_RISCV_S10_REGNUM,
+  SIM_RISCV_S11_REGNUM,
+  SIM_RISCV_T3_REGNUM,
+  SIM_RISCV_T4_REGNUM,
+  SIM_RISCV_T5_REGNUM,
+  SIM_RISCV_T6_REGNUM,
+  SIM_RISCV_PC_REGNUM,
+  SIM_RISCV_FT0_REGNUM,
+#define SIM_RISCV_FIRST_FP_REGNUM SIM_RISCV_FT0_REGNUM
+  SIM_RISCV_FT1_REGNUM,
+  SIM_RISCV_FT2_REGNUM,
+  SIM_RISCV_FT3_REGNUM,
+  SIM_RISCV_FT4_REGNUM,
+  SIM_RISCV_FT5_REGNUM,
+  SIM_RISCV_FT6_REGNUM,
+  SIM_RISCV_FT7_REGNUM,
+  SIM_RISCV_FS0_REGNUM,
+  SIM_RISCV_FS1_REGNUM,
+  SIM_RISCV_FA0_REGNUM,
+  SIM_RISCV_FA1_REGNUM,
+  SIM_RISCV_FA2_REGNUM,
+  SIM_RISCV_FA3_REGNUM,
+  SIM_RISCV_FA4_REGNUM,
+  SIM_RISCV_FA5_REGNUM,
+  SIM_RISCV_FA6_REGNUM,
+  SIM_RISCV_FA7_REGNUM,
+  SIM_RISCV_FS2_REGNUM,
+  SIM_RISCV_FS3_REGNUM,
+  SIM_RISCV_FS4_REGNUM,
+  SIM_RISCV_FS5_REGNUM,
+  SIM_RISCV_FS6_REGNUM,
+  SIM_RISCV_FS7_REGNUM,
+  SIM_RISCV_FS8_REGNUM,
+  SIM_RISCV_FS9_REGNUM,
+  SIM_RISCV_FS10_REGNUM,
+  SIM_RISCV_FS11_REGNUM,
+  SIM_RISCV_FT8_REGNUM,
+  SIM_RISCV_FT9_REGNUM,
+  SIM_RISCV_FT10_REGNUM,
+  SIM_RISCV_FT11_REGNUM,
+#define SIM_RISCV_LAST_FP_REGNUM SIM_RISCV_FT11_REGNUM
+
+#define SIM_RISCV_FIRST_CSR_REGNUM SIM_RISCV_LAST_FP_REGNUM + 1
+#define DECLARE_CSR(name, num, ...) SIM_RISCV_ ## num ## _REGNUM,
+#include "opcode/riscv-opc.h"
+#undef DECLARE_CSR
+#define SIM_RISCV_LAST_CSR_REGNUM SIM_RISCV_LAST_REGNUM - 1
+
+  SIM_RISCV_LAST_REGNUM
+};
diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
index f152de1e4646..7d588c80babd 100644
--- a/sim/riscv/ChangeLog
+++ b/sim/riscv/ChangeLog
@@ -1,3 +1,9 @@
+2021-01-12  Mike Frysinger  <vapier@gentoo.org>
+
+	* sim-main.c: Include gdb/sim-riscv.h.
+	(reg_fetch, reg_store): Define.
+	(initialize_cpu): Assign reg_fetch & reg_store.
+
 2021-01-12  Mike Frysinger  <vapier@gentoo.org>
 
 	* Makefile.in, configure.ac, interp.c, machs.c, machs.h,
diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
index 15a0eb02ae26..2dd7167cc1d0 100644
--- a/sim/riscv/sim-main.c
+++ b/sim/riscv/sim-main.c
@@ -31,6 +31,8 @@
 
 #include "opcode/riscv.h"
 
+#include "gdb/sim-riscv.h"
+
 #include "targ-vals.h"
 \f
 #define TRACE_REG(cpu, reg) \
@@ -1019,6 +1021,72 @@ 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_ZERO_REGNUM:
+      memset (buf, 0, len);
+      return len;
+    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 - SIM_RISCV_FIRST_FP_REGNUM], 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_ZERO_REGNUM:
+      /* Ignore writes.  */
+      return len;
+    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 - SIM_RISCV_FIRST_FP_REGNUM], 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.  */
@@ -1031,6 +1099,8 @@ void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)
 
   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])
     {
-- 
2.28.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] sim: riscv: new port
  2021-01-12 11:18 [PATCH 1/2] sim: riscv: new port Mike Frysinger via Gdb-patches
  2021-01-12 11:18 ` [PATCH 2/2] riscv: enable gdb/sim integration Mike Frysinger via Gdb-patches
@ 2021-02-03 23:50 ` Jim Wilson
  2021-02-04  2:38   ` Mike Frysinger via Gdb-patches
  2021-02-04 10:52 ` Andrew Burgess
  2021-02-04 14:14 ` Andrew Burgess
  3 siblings, 1 reply; 9+ messages in thread
From: Jim Wilson @ 2021-02-03 23:50 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

We actually have two RISC-V gdb simulators.  The one you originally wrote,
and a cgen simulator that Embecosm wrote.  I deferred upstreaming your
simulator in the hope that the cgen simulator might be better, but
unfortunately the cgen simulator has a serious problem that it can't easily
support all architecture variations. This is a problem that will only get
worse as more extensions are added to the architecture.  I don't know if
the cgen simulator problems are fixable.  So I personally prefer your
simulator.  But there are others that would like to see the cgen simulator
go in.  I don't think we can add both of them.  I don't know how to resolve
that debate.  Plus there is the problem that I'm involved in so much stuff
that I can't get anything done anymore.  But anyways if there is a vote,
I'm voting for your simulator.

Your simulator wasn't lost.  It can be found at
    https://github.com/riscv/riscv-binutils-gdb/tree/fsf-gdb-10.1-with-sim
This includes your original port, with some minor improvements from Kito
Cheng and myself, and a number of others.  They are listed in the ChangeLog
file.  This is known to work with the gcc testsuite, though we primarily
use qemu for simulator testing so it doesn't get tested very often.  This
is part of riscv/riscv-gnu-toolchain, and if you do SIM=gdb when building
and testing a toolchain, it will use gdb sim to run the gcc testsuite.

Or if you want to start over with a new simulator that is OK too.  We can
fix it to work with the gcc testsuite just like we fixed the old one.

Jim

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] sim: riscv: new port
  2021-02-03 23:50 ` [PATCH 1/2] sim: riscv: new port Jim Wilson
@ 2021-02-04  2:38   ` Mike Frysinger via Gdb-patches
  2021-02-04 22:56     ` Jim Wilson
  0 siblings, 1 reply; 9+ messages in thread
From: Mike Frysinger via Gdb-patches @ 2021-02-04  2:38 UTC (permalink / raw)
  To: Jim Wilson; +Cc: gdb-patches

On 03 Feb 2021 15:50, Jim Wilson wrote:
> We actually have two RISC-V gdb simulators.  The one you originally wrote,
> and a cgen simulator that Embecosm wrote.  I deferred upstreaming your
> simulator in the hope that the cgen simulator might be better, but
> unfortunately the cgen simulator has a serious problem that it can't easily
> support all architecture variations. This is a problem that will only get
> worse as more extensions are added to the architecture.  I don't know if
> the cgen simulator problems are fixable.  So I personally prefer your
> simulator.  But there are others that would like to see the cgen simulator
> go in.  I don't think we can add both of them.  I don't know how to resolve
> that debate.  Plus there is the problem that I'm involved in so much stuff
> that I can't get anything done anymore.  But anyways if there is a vote,
> I'm voting for your simulator.

my plans are to merge this one even if it is simpler & not as complete,
and then drop the cgen one on top of it once we think it's in a good
state.

i have not looked into the cgen issues to say what can be addressed.
tbh, i'm not super familiar with cgen, and have been slowly peeking
at it over time.  i'm sure at this point you know way more than me
about it.

> Your simulator wasn't lost.  It can be found at
>     https://github.com/riscv/riscv-binutils-gdb/tree/fsf-gdb-10.1-with-sim
> This includes your original port, with some minor improvements from Kito
> Cheng and myself, and a number of others.  They are listed in the ChangeLog
> file.  This is known to work with the gcc testsuite, though we primarily
> use qemu for simulator testing so it doesn't get tested very often.  This
> is part of riscv/riscv-gnu-toolchain, and if you do SIM=gdb when building
> and testing a toolchain, it will use gdb sim to run the gcc testsuite.
> 
> Or if you want to start over with a new simulator that is OK too.  We can
> fix it to work with the gcc testsuite just like we fixed the old one.

i'm aware that there's a squashed one with updates not authored by me in
the riscv repos.  i specifically avoided those as the authorship is not
clear and i didn't want to untangle copyright assignments.  so i started
with the last one purely by me and updated it to work with what has been
merged into the upstream tree.  i'm happy to merge fixes you've come up
with since i last did any real work, but i want to make sure to keep them
independent for authorship & copyright assignment purposes.

it's not about me trying to maintain control over it all and refusing to
accept changes i didn't come up with by myself :).  the FSF is picky about
keeping copyrights in line (i'm not complaining, just noting), so i wanted
to start with the things i could confidently attest to.
-mike

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2] riscv: enable gdb/sim integration
  2021-01-12 11:18 ` [PATCH 2/2] riscv: enable gdb/sim integration Mike Frysinger via Gdb-patches
@ 2021-02-04 10:45   ` Andrew Burgess
  0 siblings, 0 replies; 9+ messages in thread
From: Andrew Burgess @ 2021-02-04 10:45 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

* Mike Frysinger via Gdb-patches <gdb-patches@sourceware.org> [2021-01-12 06:18:42 -0500]:

> Now the simulator can be loaded via gdb using "target sim".
> 
> gdb/:
> 
> 	* configure.tgt (riscv*-*-*): Set gdb_sim.
> 
> include/gdb/:
> 
> 	* sim-riscv.h: New file.

On a style issue, you'll have noticed I'm sure that GDB patches all
include their changelog entries inside the commit message, something
you haven't been doing for sim/ patches.

As this patch does touch gdb/ it would be appreciated if you could
include the full ChangeLog for all parts in the commit message as is
GDB style, this should also cover the sim/* changes - otherwise it's
just going to look like a mistake was made.


> ---
>  gdb/configure.tgt       |  1 +
>  include/gdb/sim-riscv.h | 99 +++++++++++++++++++++++++++++++++++++++++
>  sim/riscv/ChangeLog     |  6 +++
>  sim/riscv/sim-main.c    | 70 +++++++++++++++++++++++++++++

The gdb/ parts are approved with the above change made.  The sim/
parts look good.

Thanks,
Andrew

>  4 files changed, 176 insertions(+)
>  create mode 100644 include/gdb/sim-riscv.h
> 
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 6e0398387482..5440780065f0 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -554,6 +554,7 @@ riscv*-*-linux*)
>  riscv*-*-*)
>  	# Target: RISC-V architecture
>  	gdb_target_obs=""
> +	gdb_sim=../sim/riscv/libsim.a
>  	;;
>  
>  rl78-*-elf)
> diff --git a/include/gdb/sim-riscv.h b/include/gdb/sim-riscv.h
> new file mode 100644
> index 000000000000..23e5f766aa6c
> --- /dev/null
> +++ b/include/gdb/sim-riscv.h
> @@ -0,0 +1,99 @@
> +/* This file defines the interface between the RISC-V simulator and GDB.
> +
> +   Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +   Contributed by Mike Frysinger.
> +
> +   This file is part of GDB.
> +
> +   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/>.  */
> +
> +/* Order has to match gdb riscv-tdep list.  */
> +enum sim_riscv_regnum {
> +  SIM_RISCV_ZERO_REGNUM = 0,
> +  SIM_RISCV_RA_REGNUM,
> +  SIM_RISCV_SP_REGNUM,
> +  SIM_RISCV_GP_REGNUM,
> +  SIM_RISCV_TP_REGNUM,
> +  SIM_RISCV_T0_REGNUM,
> +  SIM_RISCV_T1_REGNUM,
> +  SIM_RISCV_T2_REGNUM,
> +  SIM_RISCV_S0_REGNUM,
> +#define SIM_RISCV_FP_REGNUM SIM_RISCV_S0_REGNUM
> +  SIM_RISCV_S1_REGNUM,
> +  SIM_RISCV_A0_REGNUM,
> +  SIM_RISCV_A1_REGNUM,
> +  SIM_RISCV_A2_REGNUM,
> +  SIM_RISCV_A3_REGNUM,
> +  SIM_RISCV_A4_REGNUM,
> +  SIM_RISCV_A5_REGNUM,
> +  SIM_RISCV_A6_REGNUM,
> +  SIM_RISCV_A7_REGNUM,
> +  SIM_RISCV_S2_REGNUM,
> +  SIM_RISCV_S3_REGNUM,
> +  SIM_RISCV_S4_REGNUM,
> +  SIM_RISCV_S5_REGNUM,
> +  SIM_RISCV_S6_REGNUM,
> +  SIM_RISCV_S7_REGNUM,
> +  SIM_RISCV_S8_REGNUM,
> +  SIM_RISCV_S9_REGNUM,
> +  SIM_RISCV_S10_REGNUM,
> +  SIM_RISCV_S11_REGNUM,
> +  SIM_RISCV_T3_REGNUM,
> +  SIM_RISCV_T4_REGNUM,
> +  SIM_RISCV_T5_REGNUM,
> +  SIM_RISCV_T6_REGNUM,
> +  SIM_RISCV_PC_REGNUM,
> +  SIM_RISCV_FT0_REGNUM,
> +#define SIM_RISCV_FIRST_FP_REGNUM SIM_RISCV_FT0_REGNUM
> +  SIM_RISCV_FT1_REGNUM,
> +  SIM_RISCV_FT2_REGNUM,
> +  SIM_RISCV_FT3_REGNUM,
> +  SIM_RISCV_FT4_REGNUM,
> +  SIM_RISCV_FT5_REGNUM,
> +  SIM_RISCV_FT6_REGNUM,
> +  SIM_RISCV_FT7_REGNUM,
> +  SIM_RISCV_FS0_REGNUM,
> +  SIM_RISCV_FS1_REGNUM,
> +  SIM_RISCV_FA0_REGNUM,
> +  SIM_RISCV_FA1_REGNUM,
> +  SIM_RISCV_FA2_REGNUM,
> +  SIM_RISCV_FA3_REGNUM,
> +  SIM_RISCV_FA4_REGNUM,
> +  SIM_RISCV_FA5_REGNUM,
> +  SIM_RISCV_FA6_REGNUM,
> +  SIM_RISCV_FA7_REGNUM,
> +  SIM_RISCV_FS2_REGNUM,
> +  SIM_RISCV_FS3_REGNUM,
> +  SIM_RISCV_FS4_REGNUM,
> +  SIM_RISCV_FS5_REGNUM,
> +  SIM_RISCV_FS6_REGNUM,
> +  SIM_RISCV_FS7_REGNUM,
> +  SIM_RISCV_FS8_REGNUM,
> +  SIM_RISCV_FS9_REGNUM,
> +  SIM_RISCV_FS10_REGNUM,
> +  SIM_RISCV_FS11_REGNUM,
> +  SIM_RISCV_FT8_REGNUM,
> +  SIM_RISCV_FT9_REGNUM,
> +  SIM_RISCV_FT10_REGNUM,
> +  SIM_RISCV_FT11_REGNUM,
> +#define SIM_RISCV_LAST_FP_REGNUM SIM_RISCV_FT11_REGNUM
> +
> +#define SIM_RISCV_FIRST_CSR_REGNUM SIM_RISCV_LAST_FP_REGNUM + 1
> +#define DECLARE_CSR(name, num, ...) SIM_RISCV_ ## num ## _REGNUM,
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +#define SIM_RISCV_LAST_CSR_REGNUM SIM_RISCV_LAST_REGNUM - 1
> +
> +  SIM_RISCV_LAST_REGNUM
> +};
> diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
> index f152de1e4646..7d588c80babd 100644
> --- a/sim/riscv/ChangeLog
> +++ b/sim/riscv/ChangeLog
> @@ -1,3 +1,9 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* sim-main.c: Include gdb/sim-riscv.h.
> +	(reg_fetch, reg_store): Define.
> +	(initialize_cpu): Assign reg_fetch & reg_store.
> +
>  2021-01-12  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* Makefile.in, configure.ac, interp.c, machs.c, machs.h,
> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
> index 15a0eb02ae26..2dd7167cc1d0 100644
> --- a/sim/riscv/sim-main.c
> +++ b/sim/riscv/sim-main.c
> @@ -31,6 +31,8 @@
>  
>  #include "opcode/riscv.h"
>  
> +#include "gdb/sim-riscv.h"
> +
>  #include "targ-vals.h"
>  \f
>  #define TRACE_REG(cpu, reg) \
> @@ -1019,6 +1021,72 @@ 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_ZERO_REGNUM:
> +      memset (buf, 0, len);
> +      return len;
> +    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 - SIM_RISCV_FIRST_FP_REGNUM], 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_ZERO_REGNUM:
> +      /* Ignore writes.  */
> +      return len;
> +    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 - SIM_RISCV_FIRST_FP_REGNUM], 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.  */
> @@ -1031,6 +1099,8 @@ void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)
>  
>    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])
>      {
> -- 
> 2.28.0
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] sim: riscv: new port
  2021-01-12 11:18 [PATCH 1/2] sim: riscv: new port Mike Frysinger via Gdb-patches
  2021-01-12 11:18 ` [PATCH 2/2] riscv: enable gdb/sim integration Mike Frysinger via Gdb-patches
  2021-02-03 23:50 ` [PATCH 1/2] sim: riscv: new port Jim Wilson
@ 2021-02-04 10:52 ` Andrew Burgess
  2021-02-04 14:14 ` Andrew Burgess
  3 siblings, 0 replies; 9+ messages in thread
From: Andrew Burgess @ 2021-02-04 10:52 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

Mike,

Sorry I didn't spot this patch earlier, I just saw it thanks to the
recent activity.

Thanks for doing this.

I've been using this simulator for a while now out of tree.  Obviously
I could never upstream this as I didn't own the copyright.

As Jim mentioned some of my colleagues do have a CGEN simulator,
though I don't know what the current state is on that.  I've reached
out to them to make them aware of your patch.  Maybe you could wait a
couple more days before committing, just to give them a chance to
respond?

I haven't tested this patch, but I'm happy to just get this committed
for now and bugs can be ironed out afterwards.

Thanks,
Andrew

* Mike Frysinger via Gdb-patches <gdb-patches@sourceware.org> [2021-01-12 06:18:41 -0500]:

> ---
>  sim/common/ChangeLog                  |     4 +
>  sim/common/nltvals.def                |    48 +
>  sim/configure                         |     8 +
>  sim/configure.tgt                     |     3 +
>  sim/riscv/ChangeLog                   |     5 +
>  sim/riscv/Makefile.in                 |    30 +
>  sim/riscv/aclocal.m4                  |   119 +
>  sim/riscv/config.in                   |   242 +
>  sim/riscv/configure                   | 16145 ++++++++++++++++++++++++
>  sim/riscv/configure.ac                |    28 +
>  sim/riscv/interp.c                    |   153 +
>  sim/riscv/machs.c                     |   125 +
>  sim/riscv/machs.h                     |    45 +
>  sim/riscv/model_list.def              |     9 +
>  sim/riscv/sim-main.c                  |  1149 ++
>  sim/riscv/sim-main.h                  |    86 +
>  sim/testsuite/ChangeLog               |     5 +
>  sim/testsuite/configure               |     3 +
>  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 |    52 +
>  22 files changed, 18284 insertions(+)
>  create mode 100644 sim/riscv/ChangeLog
>  create mode 100644 sim/riscv/Makefile.in
>  create mode 100644 sim/riscv/aclocal.m4
>  create mode 100644 sim/riscv/config.in
>  create mode 100755 sim/riscv/configure
>  create mode 100644 sim/riscv/configure.ac
>  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/model_list.def
>  create mode 100644 sim/riscv/sim-main.c
>  create mode 100644 sim/riscv/sim-main.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/common/ChangeLog b/sim/common/ChangeLog
> index 608a0859ce89..3b5439a6afe9 100644
> --- a/sim/common/ChangeLog
> +++ b/sim/common/ChangeLog
> @@ -1,3 +1,7 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* nltvals.def: Regenerate from the latest libgloss sources.
> +
>  2021-01-12  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* sim-profile.h [!WITH_PROFILE]: Rewrite to use #error.
> diff --git a/sim/common/nltvals.def b/sim/common/nltvals.def
> index 92ccc9aded8b..60467f343d27 100644
> --- a/sim/common/nltvals.def
> +++ b/sim/common/nltvals.def
> @@ -605,3 +605,51 @@
>  /* end pru sys target macros */
>  #endif
>  #endif
> +#ifdef NL_TARGET_riscv
> +#ifdef sys_defs
> +/* from syscall.h */
> +/* begin riscv sys target macros */
> + { "SYS_access", 1033 },
> + { "SYS_brk", 214 },
> + { "SYS_chdir", 49 },
> + { "SYS_close", 57 },
> + { "SYS_dup", 23 },
> + { "SYS_exit", 93 },
> + { "SYS_exit_group", 94 },
> + { "SYS_faccessat", 48 },
> + { "SYS_fcntl", 25 },
> + { "SYS_fstat", 80 },
> + { "SYS_fstatat", 79 },
> + { "SYS_getcwd", 17 },
> + { "SYS_getdents", 61 },
> + { "SYS_getegid", 177 },
> + { "SYS_geteuid", 175 },
> + { "SYS_getgid", 176 },
> + { "SYS_getmainvars", 2011 },
> + { "SYS_getpid", 172 },
> + { "SYS_gettimeofday", 169 },
> + { "SYS_getuid", 174 },
> + { "SYS_kill", 129 },
> + { "SYS_link", 1025 },
> + { "SYS_lseek", 62 },
> + { "SYS_lstat", 1039 },
> + { "SYS_mkdir", 1030 },
> + { "SYS_mmap", 222 },
> + { "SYS_mremap", 216 },
> + { "SYS_munmap", 215 },
> + { "SYS_open", 1024 },
> + { "SYS_openat", 56 },
> + { "SYS_pread", 67 },
> + { "SYS_pwrite", 68 },
> + { "SYS_read", 63 },
> + { "SYS_rt_sigaction", 134 },
> + { "SYS_stat", 1038 },
> + { "SYS_time", 1062 },
> + { "SYS_times", 153 },
> + { "SYS_uname", 160 },
> + { "SYS_unlink", 1026 },
> + { "SYS_write", 64 },
> + { "SYS_writev", 66 },
> +/* end riscv sys target macros */
> +#endif
> +#endif
> diff --git a/sim/configure.tgt b/sim/configure.tgt
> index a48c6966e8ae..5f201060f1c1 100644
> --- a/sim/configure.tgt
> +++ b/sim/configure.tgt
> @@ -85,6 +85,9 @@ case "${target}" in
>     pru*-*-*)
>         SIM_ARCH(pru)
>         ;;
> +   riscv*-*-*)
> +       SIM_ARCH(riscv)
> +       ;;
>     rl78-*-*)
>         SIM_ARCH(rl78)
>         ;;
> diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
> new file mode 100644
> index 000000000000..f152de1e4646
> --- /dev/null
> +++ b/sim/riscv/ChangeLog
> @@ -0,0 +1,5 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* Makefile.in, configure.ac, interp.c, machs.c, machs.h,
> +	model_list.def, sim-main.c, sim-main.h: New files.
> +	* aclocal.m4, config.in, configure: Regenerated.
> diff --git a/sim/riscv/Makefile.in b/sim/riscv/Makefile.in
> new file mode 100644
> index 000000000000..17cb288eba3d
> --- /dev/null
> +++ b/sim/riscv/Makefile.in
> @@ -0,0 +1,30 @@
> +#    Makefile template for Configure for the example basic simulator.
> +#    Copyright (C) 2005-2021 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-resume.o \
> +	interp.o \
> +	machs.o \
> +	sim-main.o
> +
> +## COMMON_POST_CONFIG_FRAG
> diff --git a/sim/riscv/aclocal.m4 b/sim/riscv/aclocal.m4
> new file mode 100644
> index 000000000000..e9f11c775c31
> diff --git a/sim/riscv/config.in b/sim/riscv/config.in
> new file mode 100644
> index 000000000000..cb5ea1b01c95
> diff --git a/sim/riscv/configure b/sim/riscv/configure
> new file mode 100755
> index 000000000000..8236dc35c12a
> diff --git a/sim/riscv/configure.ac b/sim/riscv/configure.ac
> new file mode 100644
> index 000000000000..6d5dce917504
> --- /dev/null
> +++ b/sim/riscv/configure.ac
> @@ -0,0 +1,28 @@
> +dnl Process this file with autoconf to produce a configure script.
> +AC_INIT(Makefile.in)
> +sinclude(../common/acinclude.m4)
> +
> +SIM_AC_COMMON
> +
> +SIM_AC_OPTION_ENDIAN(LITTLE)
> +SIM_AC_OPTION_ALIGNMENT(NONSTRICT_ALIGNMENT)
> +SIM_AC_OPTION_ENVIRONMENT
> +SIM_AC_OPTION_WARNINGS
> +
> +# Select the default model for the target.
> +riscv_model=
> +case "${target}" in
> +riscv32*) riscv_model="RV32G" ;;
> +riscv*) riscv_model="RV64G" ;;
> +esac
> +SIM_AC_OPTION_DEFAULT_MODEL(${riscv_model})
> +
> +# Select the bitsize of the target.
> +riscv_addr_bitsize=
> +case "${target}" in
> +riscv32*) riscv_addr_bitsize=32 ;;
> +riscv*) riscv_addr_bitsize=64 ;;
> +esac
> +SIM_AC_OPTION_BITSIZE($riscv_addr_bitsize)
> +
> +SIM_AC_OUTPUT
> diff --git a/sim/riscv/interp.c b/sim/riscv/interp.c
> new file mode 100644
> index 000000000000..1bf60a43aec4
> --- /dev/null
> +++ b/sim/riscv/interp.c
> @@ -0,0 +1,153 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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"
> +#include "sim-options.h"
> +\f
> +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);
> +    }
> +}
> +\f
> +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;
> +
> +  /* 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 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;
> +}
> +\f
> +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_ADDR 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 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);
> +    }
> +
> +  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 000000000000..853a3afb42f6
> --- /dev/null
> +++ b/sim/riscv/machs.c
> @@ -0,0 +1,125 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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 000000000000..903488bc7650
> --- /dev/null
> +++ b/sim/riscv/machs.h
> @@ -0,0 +1,45 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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/model_list.def b/sim/riscv/model_list.def
> new file mode 100644
> index 000000000000..5efd85ab280f
> --- /dev/null
> +++ b/sim/riscv/model_list.def
> @@ -0,0 +1,9 @@
> +M(G)
> +M(I)
> +M(IM)
> +M(IMA)
> +M(IA)
> +M(E)
> +M(EM)
> +M(EMA)
> +M(EA)
> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
> new file mode 100644
> index 000000000000..15a0eb02ae26
> --- /dev/null
> +++ b/sim/riscv/sim-main.c
> @@ -0,0 +1,1149 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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 "targ-vals.h"
> +\f
> +#define TRACE_REG(cpu, reg) \
> +  TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \
> +		  cpu->regs[reg])
> +\f
> +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.time));
> +      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.timeh));
> +      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)
> +{
> +#ifdef __GNUC__
> +  return ((__int128)a * b) >> 64;
> +#else
> +  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;
> +#endif
> +}
> +
> +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;
> +  sim_cia pc = cpu->pc + 4;
> +
> +  dividend_max = -((unsigned_word) 1 << (WITH_TARGET_WORD_BITSIZE - 1));
> +
> +  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[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[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->xlen_requirement == 64)
> +    tmp = sim_core_read_unaligned_8 (cpu, cpu->pc, read_map, cpu->regs[rs1]);
> +  else
> +    tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
> +					       cpu->regs[rs1]));
> +  store_rd (cpu, rd, tmp);
> +
> +  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->xlen_requirement == 64)
> +    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);
> +
> +  if (op->xlen_requirement == 32)
> +    RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
> +  else if (op->xlen_requirement == 64)
> +    RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +
> +  switch (op->insn_class)
> +    {
> +    case INSN_CLASS_A:
> +      return execute_a (cpu, iw, op);
> +    case INSN_CLASS_I:
> +      return execute_i (cpu, iw, op);
> +    case INSN_CLASS_M:
> +      return execute_m (cpu, iw, op);
> +    default:
> +      TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
> +      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;
> +  int xlen = RISCV_XLEN (cpu);
> +
> +  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);
> +
> +  /* NB: Same loop logic as riscv_disassemble_insn.  */
> +  for (; op->name; op++)
> +    {
> +      /* Does the opcode match?  */
> +      if (! op->match_func (op, iw))
> +	continue;
> +      /* Is this a pseudo-instruction and may we print it as such?  */
> +      if (op->pinfo & INSN_ALIAS)
> +	continue;
> +      /* Is this instruction restricted to a certain value of XLEN?  */
> +      if (op->xlen_requirement != 0 && op->xlen_requirement != xlen)
> +	continue;
> +
> +      /* It's a match.  */
> +      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;
> +}
> +\f
> +/* 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;
> +}
> +
> +/* 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;
> +
> +  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;
> +}
> +\f
> +/* 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 000000000000..4a1b31ee2fec
> --- /dev/null
> +++ b/sim/riscv/sim-main.h
> @@ -0,0 +1,86 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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 "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, ...) 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 (64 * 1024 * 1024)
> +
> +#define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu))
> +
> +#endif
> diff --git a/sim/testsuite/ChangeLog b/sim/testsuite/ChangeLog
> index a32672a46bea..72cfa195542d 100644
> --- a/sim/testsuite/ChangeLog
> +++ b/sim/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* configure: Regenerate.
> +	* sim/riscv: New directory.
> +
>  2021-01-11  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* common/alu-tst.c: Include stdlib.h.
> diff --git a/sim/testsuite/sim/riscv/ChangeLog b/sim/testsuite/sim/riscv/ChangeLog
> new file mode 100644
> index 000000000000..05c42fc7d6e5
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/ChangeLog
> @@ -0,0 +1,3 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* allinsn.exp, pass.s, 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 000000000000..03bec1b541e7
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/allinsn.exp
> @@ -0,0 +1,15 @@
> +# RISC-V 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 000000000000..bd428ca16772
> --- /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 000000000000..138417009122
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/testutils.inc
> @@ -0,0 +1,52 @@
> +# MACRO: exit
> +	.macro exit nr
> +	li a0, \nr
> +	# The exit utility function.
> +	li a7, 93;
> +	# Trigger OS trap.
> +	ecall;
> +	.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.28.0
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] sim: riscv: new port
  2021-01-12 11:18 [PATCH 1/2] sim: riscv: new port Mike Frysinger via Gdb-patches
                   ` (2 preceding siblings ...)
  2021-02-04 10:52 ` Andrew Burgess
@ 2021-02-04 14:14 ` Andrew Burgess
  2021-02-05  0:04   ` Mike Frysinger via Gdb-patches
  3 siblings, 1 reply; 9+ messages in thread
From: Andrew Burgess @ 2021-02-04 14:14 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

Mike,

I spoke with the rest of the team at Embecosm and we're happy to see
this simulator merged for now.  We'd like to leave the door open to
the idea of bringing back the CGEN simulator at some point in the
future, if we can show that it offers benefits over the bespoke
approach you have here.

I had a couple of very minor nits I spotted on glancing through the
code, but otherwise, I'm happy for this to go in.

Thanks,
Andrew


* Mike Frysinger via Gdb-patches <gdb-patches@sourceware.org> [2021-01-12 06:18:41 -0500]:

> ---
>  sim/common/ChangeLog                  |     4 +
>  sim/common/nltvals.def                |    48 +
>  sim/configure                         |     8 +
>  sim/configure.tgt                     |     3 +
>  sim/riscv/ChangeLog                   |     5 +
>  sim/riscv/Makefile.in                 |    30 +
>  sim/riscv/aclocal.m4                  |   119 +
>  sim/riscv/config.in                   |   242 +
>  sim/riscv/configure                   | 16145 ++++++++++++++++++++++++
>  sim/riscv/configure.ac                |    28 +
>  sim/riscv/interp.c                    |   153 +
>  sim/riscv/machs.c                     |   125 +
>  sim/riscv/machs.h                     |    45 +
>  sim/riscv/model_list.def              |     9 +
>  sim/riscv/sim-main.c                  |  1149 ++
>  sim/riscv/sim-main.h                  |    86 +
>  sim/testsuite/ChangeLog               |     5 +
>  sim/testsuite/configure               |     3 +
>  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 |    52 +
>  22 files changed, 18284 insertions(+)
>  create mode 100644 sim/riscv/ChangeLog
>  create mode 100644 sim/riscv/Makefile.in
>  create mode 100644 sim/riscv/aclocal.m4
>  create mode 100644 sim/riscv/config.in
>  create mode 100755 sim/riscv/configure
>  create mode 100644 sim/riscv/configure.ac
>  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/model_list.def
>  create mode 100644 sim/riscv/sim-main.c
>  create mode 100644 sim/riscv/sim-main.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/common/ChangeLog b/sim/common/ChangeLog
> index 608a0859ce89..3b5439a6afe9 100644
> --- a/sim/common/ChangeLog
> +++ b/sim/common/ChangeLog
> @@ -1,3 +1,7 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* nltvals.def: Regenerate from the latest libgloss sources.
> +
>  2021-01-12  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* sim-profile.h [!WITH_PROFILE]: Rewrite to use #error.
> diff --git a/sim/common/nltvals.def b/sim/common/nltvals.def
> index 92ccc9aded8b..60467f343d27 100644
> --- a/sim/common/nltvals.def
> +++ b/sim/common/nltvals.def
> @@ -605,3 +605,51 @@
>  /* end pru sys target macros */
>  #endif
>  #endif
> +#ifdef NL_TARGET_riscv
> +#ifdef sys_defs
> +/* from syscall.h */
> +/* begin riscv sys target macros */
> + { "SYS_access", 1033 },
> + { "SYS_brk", 214 },
> + { "SYS_chdir", 49 },
> + { "SYS_close", 57 },
> + { "SYS_dup", 23 },
> + { "SYS_exit", 93 },
> + { "SYS_exit_group", 94 },
> + { "SYS_faccessat", 48 },
> + { "SYS_fcntl", 25 },
> + { "SYS_fstat", 80 },
> + { "SYS_fstatat", 79 },
> + { "SYS_getcwd", 17 },
> + { "SYS_getdents", 61 },
> + { "SYS_getegid", 177 },
> + { "SYS_geteuid", 175 },
> + { "SYS_getgid", 176 },
> + { "SYS_getmainvars", 2011 },
> + { "SYS_getpid", 172 },
> + { "SYS_gettimeofday", 169 },
> + { "SYS_getuid", 174 },
> + { "SYS_kill", 129 },
> + { "SYS_link", 1025 },
> + { "SYS_lseek", 62 },
> + { "SYS_lstat", 1039 },
> + { "SYS_mkdir", 1030 },
> + { "SYS_mmap", 222 },
> + { "SYS_mremap", 216 },
> + { "SYS_munmap", 215 },
> + { "SYS_open", 1024 },
> + { "SYS_openat", 56 },
> + { "SYS_pread", 67 },
> + { "SYS_pwrite", 68 },
> + { "SYS_read", 63 },
> + { "SYS_rt_sigaction", 134 },
> + { "SYS_stat", 1038 },
> + { "SYS_time", 1062 },
> + { "SYS_times", 153 },
> + { "SYS_uname", 160 },
> + { "SYS_unlink", 1026 },
> + { "SYS_write", 64 },
> + { "SYS_writev", 66 },
> +/* end riscv sys target macros */
> +#endif
> +#endif
> diff --git a/sim/configure.tgt b/sim/configure.tgt
> index a48c6966e8ae..5f201060f1c1 100644
> --- a/sim/configure.tgt
> +++ b/sim/configure.tgt
> @@ -85,6 +85,9 @@ case "${target}" in
>     pru*-*-*)
>         SIM_ARCH(pru)
>         ;;
> +   riscv*-*-*)
> +       SIM_ARCH(riscv)
> +       ;;
>     rl78-*-*)
>         SIM_ARCH(rl78)
>         ;;
> diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
> new file mode 100644
> index 000000000000..f152de1e4646
> --- /dev/null
> +++ b/sim/riscv/ChangeLog
> @@ -0,0 +1,5 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* Makefile.in, configure.ac, interp.c, machs.c, machs.h,
> +	model_list.def, sim-main.c, sim-main.h: New files.
> +	* aclocal.m4, config.in, configure: Regenerated.
> diff --git a/sim/riscv/Makefile.in b/sim/riscv/Makefile.in
> new file mode 100644
> index 000000000000..17cb288eba3d
> --- /dev/null
> +++ b/sim/riscv/Makefile.in
> @@ -0,0 +1,30 @@
> +#    Makefile template for Configure for the example basic simulator.
> +#    Copyright (C) 2005-2021 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-resume.o \
> +	interp.o \
> +	machs.o \
> +	sim-main.o
> +
> +## COMMON_POST_CONFIG_FRAG
> diff --git a/sim/riscv/aclocal.m4 b/sim/riscv/aclocal.m4
> new file mode 100644
> index 000000000000..e9f11c775c31
> diff --git a/sim/riscv/config.in b/sim/riscv/config.in
> new file mode 100644
> index 000000000000..cb5ea1b01c95
> diff --git a/sim/riscv/configure b/sim/riscv/configure
> new file mode 100755
> index 000000000000..8236dc35c12a
> diff --git a/sim/riscv/configure.ac b/sim/riscv/configure.ac
> new file mode 100644
> index 000000000000..6d5dce917504
> --- /dev/null
> +++ b/sim/riscv/configure.ac
> @@ -0,0 +1,28 @@
> +dnl Process this file with autoconf to produce a configure script.
> +AC_INIT(Makefile.in)
> +sinclude(../common/acinclude.m4)
> +
> +SIM_AC_COMMON
> +
> +SIM_AC_OPTION_ENDIAN(LITTLE)
> +SIM_AC_OPTION_ALIGNMENT(NONSTRICT_ALIGNMENT)
> +SIM_AC_OPTION_ENVIRONMENT
> +SIM_AC_OPTION_WARNINGS
> +
> +# Select the default model for the target.
> +riscv_model=
> +case "${target}" in
> +riscv32*) riscv_model="RV32G" ;;
> +riscv*) riscv_model="RV64G" ;;
> +esac
> +SIM_AC_OPTION_DEFAULT_MODEL(${riscv_model})
> +
> +# Select the bitsize of the target.
> +riscv_addr_bitsize=
> +case "${target}" in
> +riscv32*) riscv_addr_bitsize=32 ;;
> +riscv*) riscv_addr_bitsize=64 ;;
> +esac
> +SIM_AC_OPTION_BITSIZE($riscv_addr_bitsize)
> +
> +SIM_AC_OUTPUT
> diff --git a/sim/riscv/interp.c b/sim/riscv/interp.c
> new file mode 100644
> index 000000000000..1bf60a43aec4
> --- /dev/null
> +++ b/sim/riscv/interp.c
> @@ -0,0 +1,153 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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"
> +#include "sim-options.h"
> +\f
> +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);
> +    }
> +}
> +\f
> +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;
> +
> +  /* 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 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;
> +}
> +\f
> +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_ADDR 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 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);
> +    }
> +
> +  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 000000000000..853a3afb42f6
> --- /dev/null
> +++ b/sim/riscv/machs.c
> @@ -0,0 +1,125 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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 000000000000..903488bc7650
> --- /dev/null
> +++ b/sim/riscv/machs.h
> @@ -0,0 +1,45 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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/model_list.def b/sim/riscv/model_list.def
> new file mode 100644
> index 000000000000..5efd85ab280f
> --- /dev/null
> +++ b/sim/riscv/model_list.def
> @@ -0,0 +1,9 @@
> +M(G)
> +M(I)
> +M(IM)
> +M(IMA)
> +M(IA)
> +M(E)
> +M(EM)
> +M(EMA)
> +M(EA)
> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
> new file mode 100644
> index 000000000000..15a0eb02ae26
> --- /dev/null
> +++ b/sim/riscv/sim-main.c
> @@ -0,0 +1,1149 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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 "targ-vals.h"
> +\f
> +#define TRACE_REG(cpu, reg) \
> +  TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \
> +		  cpu->regs[reg])
> +\f
> +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.time));
> +      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.timeh));
> +      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)
> +{
> +#ifdef __GNUC__
> +  return ((__int128)a * b) >> 64;
> +#else
> +  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;
> +#endif
> +}
> +
> +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;
> +  sim_cia pc = cpu->pc + 4;
> +
> +  dividend_max = -((unsigned_word) 1 << (WITH_TARGET_WORD_BITSIZE - 1));
> +
> +  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[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[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->xlen_requirement == 64)
> +    tmp = sim_core_read_unaligned_8 (cpu, cpu->pc, read_map, cpu->regs[rs1]);
> +  else
> +    tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
> +					       cpu->regs[rs1]));
> +  store_rd (cpu, rd, tmp);
> +
> +  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->xlen_requirement == 64)
> +    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);
> +
> +  if (op->xlen_requirement == 32)
> +    RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
> +  else if (op->xlen_requirement == 64)
> +    RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +
> +  switch (op->insn_class)
> +    {
> +    case INSN_CLASS_A:
> +      return execute_a (cpu, iw, op);
> +    case INSN_CLASS_I:
> +      return execute_i (cpu, iw, op);
> +    case INSN_CLASS_M:
> +      return execute_m (cpu, iw, op);
> +    default:
> +      TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
> +      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;
> +  int xlen = RISCV_XLEN (cpu);
> +
> +  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);
> +
> +  /* NB: Same loop logic as riscv_disassemble_insn.  */
> +  for (; op->name; op++)
> +    {
> +      /* Does the opcode match?  */
> +      if (! op->match_func (op, iw))
> +	continue;
> +      /* Is this a pseudo-instruction and may we print it as such?  */
> +      if (op->pinfo & INSN_ALIAS)
> +	continue;
> +      /* Is this instruction restricted to a certain value of XLEN?  */
> +      if (op->xlen_requirement != 0 && op->xlen_requirement != xlen)
> +	continue;
> +
> +      /* It's a match.  */
> +      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;
> +}
> +\f
> +/* 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;
> +}
> +
> +/* 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)

Missing newline after 'void'.


> +{
> +  const char *extensions;
> +  int i;
> +
> +  memset (cpu->regs, 0, sizeof (cpu->regs));
> +
> +  CPU_PC_FETCH (cpu) = pc_get;
> +  CPU_PC_STORE (cpu) = pc_set;
> +
> +  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;
> +}
> +\f
> +/* 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)

Missing newline after 'void'.

> +{
> +  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 000000000000..4a1b31ee2fec
> --- /dev/null
> +++ b/sim/riscv/sim-main.h
> @@ -0,0 +1,86 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 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 "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, ...) 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 (64 * 1024 * 1024)
> +
> +#define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu))
> +
> +#endif
> diff --git a/sim/testsuite/ChangeLog b/sim/testsuite/ChangeLog
> index a32672a46bea..72cfa195542d 100644
> --- a/sim/testsuite/ChangeLog
> +++ b/sim/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* configure: Regenerate.
> +	* sim/riscv: New directory.
> +
>  2021-01-11  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* common/alu-tst.c: Include stdlib.h.
> diff --git a/sim/testsuite/sim/riscv/ChangeLog b/sim/testsuite/sim/riscv/ChangeLog
> new file mode 100644
> index 000000000000..05c42fc7d6e5
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/ChangeLog
> @@ -0,0 +1,3 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* allinsn.exp, pass.s, 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 000000000000..03bec1b541e7
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/allinsn.exp
> @@ -0,0 +1,15 @@
> +# RISC-V 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 000000000000..bd428ca16772
> --- /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 000000000000..138417009122
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/testutils.inc
> @@ -0,0 +1,52 @@
> +# MACRO: exit
> +	.macro exit nr
> +	li a0, \nr
> +	# The exit utility function.
> +	li a7, 93;
> +	# Trigger OS trap.
> +	ecall;
> +	.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.28.0
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] sim: riscv: new port
  2021-02-04  2:38   ` Mike Frysinger via Gdb-patches
@ 2021-02-04 22:56     ` Jim Wilson
  0 siblings, 0 replies; 9+ messages in thread
From: Jim Wilson @ 2021-02-04 22:56 UTC (permalink / raw)
  To: Jim Wilson, gdb-patches

On Wed, Feb 3, 2021 at 6:38 PM Mike Frysinger <vapier@gentoo.org> wrote:

> my plans are to merge this one even if it is simpler & not as complete,
> and then drop the cgen one on top of it once we think it's in a good
> state.
>

OK.  The cgen one has trouble with extensions, as it must explicitly define
every supported arch variation last time I checked, and there are far too
many to specify.  In your simulator, it is trivial to handle extensions, we
just check a flag at runtime.

i'm aware that there's a squashed one with updates not authored by me in
> the riscv repos.  i specifically avoided those as the authorship is not
> clear and i didn't want to untangle copyright assignments.  so i started
> with the last one purely by me and updated it to work with what has been
> merged into the upstream tree.  i'm happy to merge fixes you've come up
> with since i last did any real work, but i want to make sure to keep them
> independent for authorship & copyright assignment purposes.
>

That is fine.   All of the people are Andes or SiFive (or both depending on
date) employees so they all have assignments.  But I don't really care
about which source tree got added to FSf GDB.  I just was hoping someone
else would do the work.  We can switch to your version and then add bug
fixes as necessary.

Jim

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] sim: riscv: new port
  2021-02-04 14:14 ` Andrew Burgess
@ 2021-02-05  0:04   ` Mike Frysinger via Gdb-patches
  0 siblings, 0 replies; 9+ messages in thread
From: Mike Frysinger via Gdb-patches @ 2021-02-05  0:04 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

On 04 Feb 2021 14:14, Andrew Burgess wrote:
> I spoke with the rest of the team at Embecosm and we're happy to see
> this simulator merged for now.  We'd like to leave the door open to
> the idea of bringing back the CGEN simulator at some point in the
> future, if we can show that it offers benefits over the bespoke
> approach you have here.

i covered that with Jim:
https://sourceware.org/pipermail/gdb-patches/2021-February/175724.html

> I had a couple of very minor nits I spotted on glancing through the
> code, but otherwise, I'm happy for this to go in.

merged your suggestions & pushed now
-mike

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-02-05  0:04 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-12 11:18 [PATCH 1/2] sim: riscv: new port Mike Frysinger via Gdb-patches
2021-01-12 11:18 ` [PATCH 2/2] riscv: enable gdb/sim integration Mike Frysinger via Gdb-patches
2021-02-04 10:45   ` Andrew Burgess
2021-02-03 23:50 ` [PATCH 1/2] sim: riscv: new port Jim Wilson
2021-02-04  2:38   ` Mike Frysinger via Gdb-patches
2021-02-04 22:56     ` Jim Wilson
2021-02-04 10:52 ` Andrew Burgess
2021-02-04 14:14 ` Andrew Burgess
2021-02-05  0:04   ` Mike Frysinger via Gdb-patches

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox