[PATCH 1/2] sim: riscv: new port

Mike Frysinger via Gdb-patches
2021-01-12T11:19:02Z

---
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