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

Andrew Burgess
2021-02-04T14:14:20Z

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
>