Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <andrew.burgess@embecosm.com>
To: Mike Frysinger <vapier@gentoo.org>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH 1/2] sim: riscv: new port
Date: Thu, 4 Feb 2021 10:52:15 +0000	[thread overview]
Message-ID: <20210204105215.GY265215@embecosm.com> (raw)
In-Reply-To: <20210112111842.17223-1-vapier@gentoo.org>

Mike,

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

Thanks for doing this.

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

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

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

Thanks,
Andrew

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

> ---
>  sim/common/ChangeLog                  |     4 +
>  sim/common/nltvals.def                |    48 +
>  sim/configure                         |     8 +
>  sim/configure.tgt                     |     3 +
>  sim/riscv/ChangeLog                   |     5 +
>  sim/riscv/Makefile.in                 |    30 +
>  sim/riscv/aclocal.m4                  |   119 +
>  sim/riscv/config.in                   |   242 +
>  sim/riscv/configure                   | 16145 ++++++++++++++++++++++++
>  sim/riscv/configure.ac                |    28 +
>  sim/riscv/interp.c                    |   153 +
>  sim/riscv/machs.c                     |   125 +
>  sim/riscv/machs.h                     |    45 +
>  sim/riscv/model_list.def              |     9 +
>  sim/riscv/sim-main.c                  |  1149 ++
>  sim/riscv/sim-main.h                  |    86 +
>  sim/testsuite/ChangeLog               |     5 +
>  sim/testsuite/configure               |     3 +
>  sim/testsuite/sim/riscv/ChangeLog     |     3 +
>  sim/testsuite/sim/riscv/allinsn.exp   |    15 +
>  sim/testsuite/sim/riscv/pass.s        |     7 +
>  sim/testsuite/sim/riscv/testutils.inc |    52 +
>  22 files changed, 18284 insertions(+)
>  create mode 100644 sim/riscv/ChangeLog
>  create mode 100644 sim/riscv/Makefile.in
>  create mode 100644 sim/riscv/aclocal.m4
>  create mode 100644 sim/riscv/config.in
>  create mode 100755 sim/riscv/configure
>  create mode 100644 sim/riscv/configure.ac
>  create mode 100644 sim/riscv/interp.c
>  create mode 100644 sim/riscv/machs.c
>  create mode 100644 sim/riscv/machs.h
>  create mode 100644 sim/riscv/model_list.def
>  create mode 100644 sim/riscv/sim-main.c
>  create mode 100644 sim/riscv/sim-main.h
>  create mode 100644 sim/testsuite/sim/riscv/ChangeLog
>  create mode 100644 sim/testsuite/sim/riscv/allinsn.exp
>  create mode 100644 sim/testsuite/sim/riscv/pass.s
>  create mode 100644 sim/testsuite/sim/riscv/testutils.inc
> 
> diff --git a/sim/common/ChangeLog b/sim/common/ChangeLog
> index 608a0859ce89..3b5439a6afe9 100644
> --- a/sim/common/ChangeLog
> +++ b/sim/common/ChangeLog
> @@ -1,3 +1,7 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* nltvals.def: Regenerate from the latest libgloss sources.
> +
>  2021-01-12  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* sim-profile.h [!WITH_PROFILE]: Rewrite to use #error.
> diff --git a/sim/common/nltvals.def b/sim/common/nltvals.def
> index 92ccc9aded8b..60467f343d27 100644
> --- a/sim/common/nltvals.def
> +++ b/sim/common/nltvals.def
> @@ -605,3 +605,51 @@
>  /* end pru sys target macros */
>  #endif
>  #endif
> +#ifdef NL_TARGET_riscv
> +#ifdef sys_defs
> +/* from syscall.h */
> +/* begin riscv sys target macros */
> + { "SYS_access", 1033 },
> + { "SYS_brk", 214 },
> + { "SYS_chdir", 49 },
> + { "SYS_close", 57 },
> + { "SYS_dup", 23 },
> + { "SYS_exit", 93 },
> + { "SYS_exit_group", 94 },
> + { "SYS_faccessat", 48 },
> + { "SYS_fcntl", 25 },
> + { "SYS_fstat", 80 },
> + { "SYS_fstatat", 79 },
> + { "SYS_getcwd", 17 },
> + { "SYS_getdents", 61 },
> + { "SYS_getegid", 177 },
> + { "SYS_geteuid", 175 },
> + { "SYS_getgid", 176 },
> + { "SYS_getmainvars", 2011 },
> + { "SYS_getpid", 172 },
> + { "SYS_gettimeofday", 169 },
> + { "SYS_getuid", 174 },
> + { "SYS_kill", 129 },
> + { "SYS_link", 1025 },
> + { "SYS_lseek", 62 },
> + { "SYS_lstat", 1039 },
> + { "SYS_mkdir", 1030 },
> + { "SYS_mmap", 222 },
> + { "SYS_mremap", 216 },
> + { "SYS_munmap", 215 },
> + { "SYS_open", 1024 },
> + { "SYS_openat", 56 },
> + { "SYS_pread", 67 },
> + { "SYS_pwrite", 68 },
> + { "SYS_read", 63 },
> + { "SYS_rt_sigaction", 134 },
> + { "SYS_stat", 1038 },
> + { "SYS_time", 1062 },
> + { "SYS_times", 153 },
> + { "SYS_uname", 160 },
> + { "SYS_unlink", 1026 },
> + { "SYS_write", 64 },
> + { "SYS_writev", 66 },
> +/* end riscv sys target macros */
> +#endif
> +#endif
> diff --git a/sim/configure.tgt b/sim/configure.tgt
> index a48c6966e8ae..5f201060f1c1 100644
> --- a/sim/configure.tgt
> +++ b/sim/configure.tgt
> @@ -85,6 +85,9 @@ case "${target}" in
>     pru*-*-*)
>         SIM_ARCH(pru)
>         ;;
> +   riscv*-*-*)
> +       SIM_ARCH(riscv)
> +       ;;
>     rl78-*-*)
>         SIM_ARCH(rl78)
>         ;;
> diff --git a/sim/riscv/ChangeLog b/sim/riscv/ChangeLog
> new file mode 100644
> index 000000000000..f152de1e4646
> --- /dev/null
> +++ b/sim/riscv/ChangeLog
> @@ -0,0 +1,5 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* Makefile.in, configure.ac, interp.c, machs.c, machs.h,
> +	model_list.def, sim-main.c, sim-main.h: New files.
> +	* aclocal.m4, config.in, configure: Regenerated.
> diff --git a/sim/riscv/Makefile.in b/sim/riscv/Makefile.in
> new file mode 100644
> index 000000000000..17cb288eba3d
> --- /dev/null
> +++ b/sim/riscv/Makefile.in
> @@ -0,0 +1,30 @@
> +#    Makefile template for Configure for the example basic simulator.
> +#    Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +#    Written by Mike Frysinger.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# This selects the newlib/libgloss syscall definitions.
> +NL_TARGET = -DNL_TARGET_riscv
> +
> +## COMMON_PRE_CONFIG_FRAG
> +
> +SIM_OBJS = \
> +	$(SIM_NEW_COMMON_OBJS) \
> +	sim-resume.o \
> +	interp.o \
> +	machs.o \
> +	sim-main.o
> +
> +## COMMON_POST_CONFIG_FRAG
> diff --git a/sim/riscv/aclocal.m4 b/sim/riscv/aclocal.m4
> new file mode 100644
> index 000000000000..e9f11c775c31
> diff --git a/sim/riscv/config.in b/sim/riscv/config.in
> new file mode 100644
> index 000000000000..cb5ea1b01c95
> diff --git a/sim/riscv/configure b/sim/riscv/configure
> new file mode 100755
> index 000000000000..8236dc35c12a
> diff --git a/sim/riscv/configure.ac b/sim/riscv/configure.ac
> new file mode 100644
> index 000000000000..6d5dce917504
> --- /dev/null
> +++ b/sim/riscv/configure.ac
> @@ -0,0 +1,28 @@
> +dnl Process this file with autoconf to produce a configure script.
> +AC_INIT(Makefile.in)
> +sinclude(../common/acinclude.m4)
> +
> +SIM_AC_COMMON
> +
> +SIM_AC_OPTION_ENDIAN(LITTLE)
> +SIM_AC_OPTION_ALIGNMENT(NONSTRICT_ALIGNMENT)
> +SIM_AC_OPTION_ENVIRONMENT
> +SIM_AC_OPTION_WARNINGS
> +
> +# Select the default model for the target.
> +riscv_model=
> +case "${target}" in
> +riscv32*) riscv_model="RV32G" ;;
> +riscv*) riscv_model="RV64G" ;;
> +esac
> +SIM_AC_OPTION_DEFAULT_MODEL(${riscv_model})
> +
> +# Select the bitsize of the target.
> +riscv_addr_bitsize=
> +case "${target}" in
> +riscv32*) riscv_addr_bitsize=32 ;;
> +riscv*) riscv_addr_bitsize=64 ;;
> +esac
> +SIM_AC_OPTION_BITSIZE($riscv_addr_bitsize)
> +
> +SIM_AC_OUTPUT
> diff --git a/sim/riscv/interp.c b/sim/riscv/interp.c
> new file mode 100644
> index 000000000000..1bf60a43aec4
> --- /dev/null
> +++ b/sim/riscv/interp.c
> @@ -0,0 +1,153 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +   Contributed by Mike Frysinger.
> +
> +   This file is part of simulators.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +
> +#include "sim-main.h"
> +#include "sim-options.h"
> +\f
> +void
> +sim_engine_run (SIM_DESC sd,
> +		int next_cpu_nr, /* ignore  */
> +		int nr_cpus, /* ignore  */
> +		int siggnal) /* ignore  */
> +{
> +  SIM_CPU *cpu;
> +
> +  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
> +
> +  cpu = STATE_CPU (sd, 0);
> +
> +  while (1)
> +    {
> +      step_once (cpu);
> +      if (sim_events_tick (sd))
> +	sim_events_process (sd);
> +    }
> +}
> +\f
> +static void
> +free_state (SIM_DESC sd)
> +{
> +  if (STATE_MODULES (sd) != NULL)
> +    sim_module_uninstall (sd);
> +  sim_cpu_free_all (sd);
> +  sim_state_free (sd);
> +}
> +
> +SIM_DESC
> +sim_open (SIM_OPEN_KIND kind, host_callback *callback,
> +	  struct bfd *abfd, char * const *argv)
> +{
> +  char c;
> +  int i;
> +  SIM_DESC sd = sim_state_alloc (kind, callback);
> +
> +  /* The cpu data is kept in a separately allocated chunk of memory.  */
> +  if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK)
> +    {
> +      free_state (sd);
> +      return 0;
> +    }
> +
> +  if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
> +    {
> +      free_state (sd);
> +      return 0;
> +    }
> +
> +  /* XXX: Default to the Virtual environment.  */
> +  if (STATE_ENVIRONMENT (sd) == ALL_ENVIRONMENT)
> +    STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT;
> +
> +  /* The parser will print an error message for us, so we silently return.  */
> +  if (sim_parse_args (sd, argv) != SIM_RC_OK)
> +    {
> +      free_state (sd);
> +      return 0;
> +    }
> +
> +  /* Check for/establish the a reference program image.  */
> +  if (sim_analyze_program (sd,
> +			   (STATE_PROG_ARGV (sd) != NULL
> +			    ? *STATE_PROG_ARGV (sd)
> +			    : NULL), abfd) != SIM_RC_OK)
> +    {
> +      free_state (sd);
> +      return 0;
> +    }
> +
> +  /* Establish any remaining configuration options.  */
> +  if (sim_config (sd) != SIM_RC_OK)
> +    {
> +      free_state (sd);
> +      return 0;
> +    }
> +
> +  if (sim_post_argv_init (sd) != SIM_RC_OK)
> +    {
> +      free_state (sd);
> +      return 0;
> +    }
> +
> +  /* CPU specific initialization.  */
> +  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
> +    {
> +      SIM_CPU *cpu = STATE_CPU (sd, i);
> +
> +      initialize_cpu (sd, cpu, i);
> +    }
> +
> +  /* Allocate external memory if none specified by user.
> +     Use address 4 here in case the user wanted address 0 unmapped.  */
> +  if (sim_core_read_buffer (sd, NULL, read_map, &c, 4, 1) == 0)
> +    sim_do_commandf (sd, "memory-size %#x", DEFAULT_MEM_SIZE);
> +
> +  return sd;
> +}
> +\f
> +SIM_RC
> +sim_create_inferior (SIM_DESC sd, struct bfd *abfd,
> +		     char * const *argv, char * const *env)
> +{
> +  SIM_CPU *cpu = STATE_CPU (sd, 0);
> +  SIM_ADDR addr;
> +
> +  /* Set the PC.  */
> +  if (abfd != NULL)
> +    addr = bfd_get_start_address (abfd);
> +  else
> +    addr = 0;
> +  sim_pc_set (cpu, addr);
> +
> +  /* Standalone mode (i.e. `run`) will take care of the argv for us in
> +     sim_open() -> sim_parse_args().  But in debug mode (i.e. 'target sim'
> +     with `gdb`), we need to handle it because the user can change the
> +     argv on the fly via gdb's 'run'.  */
> +  if (STATE_PROG_ARGV (sd) != argv)
> +    {
> +      freeargv (STATE_PROG_ARGV (sd));
> +      STATE_PROG_ARGV (sd) = dupargv (argv);
> +    }
> +
> +  initialize_env (sd, (void *)argv, (void *)env);
> +
> +  return SIM_RC_OK;
> +}
> diff --git a/sim/riscv/machs.c b/sim/riscv/machs.c
> new file mode 100644
> index 000000000000..853a3afb42f6
> --- /dev/null
> +++ b/sim/riscv/machs.c
> @@ -0,0 +1,125 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +   Contributed by Mike Frysinger.
> +
> +   This file is part of simulators.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +
> +#include "sim-main.h"
> +
> +static void
> +riscv_model_init (SIM_CPU *cpu)
> +{
> +}
> +
> +static void
> +riscv_init_cpu (SIM_CPU *cpu)
> +{
> +}
> +
> +static void
> +riscv_prepare_run (SIM_CPU *cpu)
> +{
> +}
> +
> +static const SIM_MACH_IMP_PROPERTIES riscv_imp_properties =
> +{
> +  sizeof (SIM_CPU),
> +  0,
> +};
> +
> +#if WITH_TARGET_WORD_BITSIZE >= 32
> +
> +static const SIM_MACH rv32i_mach;
> +
> +static const SIM_MODEL rv32_models[] =
> +{
> +#define M(ext) { "RV32"#ext, &rv32i_mach, MODEL_RV32##ext, NULL, riscv_model_init },
> +#include "model_list.def"
> +#undef M
> +  { 0, NULL, 0, NULL, NULL, }
> +};
> +
> +static const SIM_MACH rv32i_mach =
> +{
> +  "rv32i", "riscv:rv32", MACH_RV32I,
> +  32, 32, &rv32_models[0], &riscv_imp_properties,
> +  riscv_init_cpu,
> +  riscv_prepare_run
> +};
> +
> +#endif
> +
> +#if WITH_TARGET_WORD_BITSIZE >= 64
> +
> +static const SIM_MACH rv64i_mach;
> +
> +static const SIM_MODEL rv64_models[] =
> +{
> +#define M(ext) { "RV64"#ext, &rv64i_mach, MODEL_RV64##ext, NULL, riscv_model_init },
> +#include "model_list.def"
> +#undef M
> +  { 0, NULL, 0, NULL, NULL, }
> +};
> +
> +static const SIM_MACH rv64i_mach =
> +{
> +  "rv64i", "riscv:rv64", MACH_RV64I,
> +  64, 64, &rv64_models[0], &riscv_imp_properties,
> +  riscv_init_cpu,
> +  riscv_prepare_run
> +};
> +
> +#endif
> +
> +#if WITH_TARGET_WORD_BITSIZE >= 128
> +
> +static const SIM_MACH rv128i_mach;
> +
> +static const SIM_MODEL rv128_models[] =
> +{
> +#define M(ext) { "RV128"#ext, &rv128i_mach, MODEL_RV128##ext, NULL, riscv_model_init },
> +#include "model_list.def"
> +#undef M
> +  { 0, NULL, 0, NULL, NULL, }
> +};
> +
> +static const SIM_MACH rv128i_mach =
> +{
> +  "rv128i", "riscv:rv128", MACH_RV128I,
> +  128, 128, &rv128_models[0], &riscv_imp_properties,
> +  riscv_init_cpu,
> +  riscv_prepare_run
> +};
> +
> +#endif
> +
> +/* Order matters here.  */
> +const SIM_MACH *sim_machs[] =
> +{
> +#if WITH_TARGET_WORD_BITSIZE >= 128
> +  &rv128i_mach,
> +#endif
> +#if WITH_TARGET_WORD_BITSIZE >= 64
> +  &rv64i_mach,
> +#endif
> +#if WITH_TARGET_WORD_BITSIZE >= 32
> +  &rv32i_mach,
> +#endif
> +  NULL
> +};
> diff --git a/sim/riscv/machs.h b/sim/riscv/machs.h
> new file mode 100644
> index 000000000000..903488bc7650
> --- /dev/null
> +++ b/sim/riscv/machs.h
> @@ -0,0 +1,45 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +   Contributed by Mike Frysinger.
> +
> +   This file is part of simulators.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef RISCV_SIM_MACHS_H
> +#define RISCV_SIM_MACHS_H
> +
> +typedef enum model_type {
> +#define M(ext) MODEL_RV32##ext,
> +#include "model_list.def"
> +#undef M
> +#define M(ext) MODEL_RV64##ext,
> +#include "model_list.def"
> +#undef M
> +#define M(ext) MODEL_RV128##ext,
> +#include "model_list.def"
> +#undef M
> +  MODEL_MAX
> +} MODEL_TYPE;
> +
> +typedef enum mach_attr {
> +  MACH_BASE,
> +  MACH_RV32I,
> +  MACH_RV64I,
> +  MACH_RV128I,
> +  MACH_MAX
> +} MACH_ATTR;
> +
> +#endif
> diff --git a/sim/riscv/model_list.def b/sim/riscv/model_list.def
> new file mode 100644
> index 000000000000..5efd85ab280f
> --- /dev/null
> +++ b/sim/riscv/model_list.def
> @@ -0,0 +1,9 @@
> +M(G)
> +M(I)
> +M(IM)
> +M(IMA)
> +M(IA)
> +M(E)
> +M(EM)
> +M(EMA)
> +M(EA)
> diff --git a/sim/riscv/sim-main.c b/sim/riscv/sim-main.c
> new file mode 100644
> index 000000000000..15a0eb02ae26
> --- /dev/null
> +++ b/sim/riscv/sim-main.c
> @@ -0,0 +1,1149 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +   Contributed by Mike Frysinger.
> +
> +   This file is part of simulators.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +/* This file contains the main simulator decoding logic.  i.e. everything that
> +   is architecture specific.  */
> +
> +#include "config.h"
> +
> +#include <inttypes.h>
> +#include <time.h>
> +
> +#include "sim-main.h"
> +#include "sim-syscall.h"
> +
> +#include "opcode/riscv.h"
> +
> +#include "targ-vals.h"
> +\f
> +#define TRACE_REG(cpu, reg) \
> +  TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \
> +		  cpu->regs[reg])
> +\f
> +static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
> +#define OP_HASH_IDX(i) ((i) & (riscv_insn_length (i) == 2 ? 0x3 : 0x7f))
> +
> +#define RISCV_ASSERT_RV32(cpu, fmt, args...) \
> +  do { \
> +    if (RISCV_XLEN (cpu) != 32) \
> +      { \
> +	SIM_DESC sd = CPU_STATE (cpu); \
> +	TRACE_INSN (cpu, "RV32I-only " fmt, ## args); \
> +	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL); \
> +      } \
> +  } while (0)
> +
> +#define RISCV_ASSERT_RV64(cpu, fmt, args...) \
> +  do { \
> +    if (RISCV_XLEN (cpu) != 64) \
> +      { \
> +	SIM_DESC sd = CPU_STATE (cpu); \
> +	TRACE_INSN (cpu, "RV64I-only " fmt, ## args); \
> +	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL); \
> +      } \
> +  } while (0)
> +
> +static INLINE void
> +store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
> +{
> +  if (rd)
> +    {
> +      cpu->regs[rd] = val;
> +      TRACE_REG (cpu, rd);
> +    }
> +}
> +
> +static INLINE unsigned_word
> +fetch_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg)
> +{
> +  /* Handle pseudo registers.  */
> +  switch (csr)
> +    {
> +    /* Allow certain registers only in respective modes.  */
> +    case CSR_CYCLEH:
> +    case CSR_INSTRETH:
> +    case CSR_TIMEH:
> +      RISCV_ASSERT_RV32 (cpu, "CSR: %s", name);
> +      break;
> +    }
> +
> +  return *reg;
> +}
> +
> +static INLINE void
> +store_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg,
> +	   unsigned_word val)
> +{
> +  switch (csr)
> +    {
> +    /* These are pseudo registers that modify sub-fields of fcsr.  */
> +    case CSR_FRM:
> +      val &= 0x7;
> +      *reg = val;
> +      cpu->csr.fcsr = (cpu->csr.fcsr & ~0xe0) | (val << 5);
> +      break;
> +    case CSR_FFLAGS:
> +      val &= 0x1f;
> +      *reg = val;
> +      cpu->csr.fcsr = (cpu->csr.fcsr & ~0x1f) | val;
> +      break;
> +    /* Keep the sub-fields in sync.  */
> +    case CSR_FCSR:
> +      *reg = val;
> +      cpu->csr.frm = (val >> 5) & 0x7;
> +      cpu->csr.fflags = val & 0x1f;
> +      break;
> +
> +    /* Allow certain registers only in respective modes.  */
> +    case CSR_CYCLEH:
> +    case CSR_INSTRETH:
> +    case CSR_TIMEH:
> +      RISCV_ASSERT_RV32 (cpu, "CSR: %s", name);
> +
> +    /* All the rest are immutable.  */
> +    default:
> +      val = *reg;
> +      break;
> +    }
> +
> +  TRACE_REGISTER (cpu, "wrote CSR %s = %#" PRIxTW, name, val);
> +}
> +
> +static inline unsigned_word
> +ashiftrt (unsigned_word val, unsigned_word shift)
> +{
> +  unsigned32 sign = (val & 0x80000000) ? ~(0xfffffffful >> shift) : 0;
> +  return (val >> shift) | sign;
> +}
> +
> +static inline unsigned_word
> +ashiftrt64 (unsigned_word val, unsigned_word shift)
> +{
> +  unsigned64 sign =
> +    (val & 0x8000000000000000ull) ? ~(0xffffffffffffffffull >> shift) : 0;
> +  return (val >> shift) | sign;
> +}
> +
> +static sim_cia
> +execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
> +{
> +  SIM_DESC sd = CPU_STATE (cpu);
> +  int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
> +  int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
> +  int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
> +  const char *rd_name = riscv_gpr_names_abi[rd];
> +  const char *rs1_name = riscv_gpr_names_abi[rs1];
> +  const char *rs2_name = riscv_gpr_names_abi[rs2];
> +  unsigned int csr = (iw >> OP_SH_CSR) & OP_MASK_CSR;
> +  unsigned_word i_imm = EXTRACT_ITYPE_IMM (iw);
> +  unsigned_word u_imm = EXTRACT_UTYPE_IMM ((unsigned64) iw);
> +  unsigned_word s_imm = EXTRACT_STYPE_IMM (iw);
> +  unsigned_word sb_imm = EXTRACT_SBTYPE_IMM (iw);
> +  unsigned_word shamt_imm = ((iw >> OP_SH_SHAMT) & OP_MASK_SHAMT);
> +  unsigned_word tmp;
> +  sim_cia pc = cpu->pc + 4;
> +
> +  TRACE_EXTRACT (cpu,
> +		 "rd:%-2i:%-4s  "
> +		 "rs1:%-2i:%-4s %0*" PRIxTW "  "
> +		 "rs2:%-2i:%-4s %0*" PRIxTW "  "
> +		 "match:%#x mask:%#x",
> +		 rd, rd_name,
> +		 rs1, rs1_name, (int) sizeof (unsigned_word) * 2, cpu->regs[rs1],
> +		 rs2, rs2_name, (int) sizeof (unsigned_word) * 2, cpu->regs[rs2],
> +		 (unsigned) op->match, (unsigned) op->mask);
> +
> +  switch (op->match)
> +    {
> +    case MATCH_ADD:
> +      TRACE_INSN (cpu, "add %s, %s, %s;  // %s = %s + %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      store_rd (cpu, rd, cpu->regs[rs1] + cpu->regs[rs2]);
> +      break;
> +    case MATCH_ADDW:
> +      TRACE_INSN (cpu, "addw %s, %s, %s;  // %s = %s + %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] + cpu->regs[rs2]));
> +      break;
> +    case MATCH_ADDI:
> +      TRACE_INSN (cpu, "addi %s, %s, %#" PRIxTW ";  // %s = %s + %#" PRIxTW,
> +		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
> +      store_rd (cpu, rd, cpu->regs[rs1] + i_imm);
> +      break;
> +    case MATCH_ADDIW:
> +      TRACE_INSN (cpu, "addiw %s, %s, %#" PRIxTW ";  // %s = %s + %#" PRIxTW,
> +		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] + i_imm));
> +      break;
> +    case MATCH_AND:
> +      TRACE_INSN (cpu, "and %s, %s, %s;  // %s = %s & %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      store_rd (cpu, rd, cpu->regs[rs1] & cpu->regs[rs2]);
> +      break;
> +    case MATCH_ANDI:
> +      TRACE_INSN (cpu, "andi %s, %s, %" PRIiTW ";  // %s = %s & %#" PRIxTW,
> +		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
> +      store_rd (cpu, rd, cpu->regs[rs1] & i_imm);
> +      break;
> +    case MATCH_OR:
> +      TRACE_INSN (cpu, "or %s, %s, %s;  // %s = %s | %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      store_rd (cpu, rd, cpu->regs[rs1] | cpu->regs[rs2]);
> +      break;
> +    case MATCH_ORI:
> +      TRACE_INSN (cpu, "ori %s, %s, %" PRIiTW ";  // %s = %s | %#" PRIxTW,
> +		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
> +      store_rd (cpu, rd, cpu->regs[rs1] | i_imm);
> +      break;
> +    case MATCH_XOR:
> +      TRACE_INSN (cpu, "xor %s, %s, %s;  // %s = %s ^ %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      store_rd (cpu, rd, cpu->regs[rs1] ^ cpu->regs[rs2]);
> +      break;
> +    case MATCH_XORI:
> +      TRACE_INSN (cpu, "xori %s, %s, %" PRIiTW ";  // %s = %s ^ %#" PRIxTW,
> +		  rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
> +      store_rd (cpu, rd, cpu->regs[rs1] ^ i_imm);
> +      break;
> +    case MATCH_SUB:
> +      TRACE_INSN (cpu, "sub %s, %s, %s;  // %s = %s - %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      store_rd (cpu, rd, cpu->regs[rs1] - cpu->regs[rs2]);
> +      break;
> +    case MATCH_SUBW:
> +      TRACE_INSN (cpu, "subw %s, %s, %s;  // %s = %s - %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] - cpu->regs[rs2]));
> +      break;
> +    case MATCH_LUI:
> +      TRACE_INSN (cpu, "lui %s, %#" PRIxTW ";", rd_name, u_imm);
> +      store_rd (cpu, rd, u_imm);
> +      break;
> +    case MATCH_SLL:
> +      TRACE_INSN (cpu, "sll %s, %s, %s;  // %s = %s << %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f;
> +      store_rd (cpu, rd, cpu->regs[rs1] << (cpu->regs[rs2] & u_imm));
> +      break;
> +    case MATCH_SLLW:
> +      TRACE_INSN (cpu, "sllw %s, %s, %s;  // %s = %s << %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (
> +	(unsigned32) cpu->regs[rs1] << (cpu->regs[rs2] & 0x1f)));
> +      break;
> +    case MATCH_SLLI:
> +      TRACE_INSN (cpu, "slli %s, %s, %" PRIiTW ";  // %s = %s << %#" PRIxTW,
> +		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
> +      if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f)
> +	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +      store_rd (cpu, rd, cpu->regs[rs1] << shamt_imm);
> +      break;
> +    case MATCH_SLLIW:
> +      TRACE_INSN (cpu, "slliw %s, %s, %" PRIiTW ";  // %s = %s << %#" PRIxTW,
> +		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 ((unsigned32) cpu->regs[rs1] << shamt_imm));
> +      break;
> +    case MATCH_SRL:
> +      TRACE_INSN (cpu, "srl %s, %s, %s;  // %s = %s >> %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f;
> +      store_rd (cpu, rd, cpu->regs[rs1] >> (cpu->regs[rs2] & u_imm));
> +      break;
> +    case MATCH_SRLW:
> +      TRACE_INSN (cpu, "srlw %s, %s, %s;  // %s = %s >> %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (
> +	(unsigned32) cpu->regs[rs1] >> (cpu->regs[rs2] & 0x1f)));
> +      break;
> +    case MATCH_SRLI:
> +      TRACE_INSN (cpu, "srli %s, %s, %" PRIiTW ";  // %s = %s >> %#" PRIxTW,
> +		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
> +      if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f)
> +	sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +      store_rd (cpu, rd, cpu->regs[rs1] >> shamt_imm);
> +      break;
> +    case MATCH_SRLIW:
> +      TRACE_INSN (cpu, "srliw %s, %s, %" PRIiTW ";  // %s = %s >> %#" PRIxTW,
> +		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 ((unsigned32) cpu->regs[rs1] >> shamt_imm));
> +      break;
> +    case MATCH_SRA:
> +      TRACE_INSN (cpu, "sra %s, %s, %s;  // %s = %s >>> %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (RISCV_XLEN (cpu) == 32)
> +	tmp = ashiftrt (cpu->regs[rs1], cpu->regs[rs2] & 0x1f);
> +      else
> +	tmp = ashiftrt64 (cpu->regs[rs1], cpu->regs[rs2] & 0x3f);
> +      store_rd (cpu, rd, tmp);
> +      break;
> +    case MATCH_SRAW:
> +      TRACE_INSN (cpu, "sraw %s, %s, %s;  // %s = %s >>> %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (
> +	ashiftrt ((signed32) cpu->regs[rs1], cpu->regs[rs2] & 0x1f)));
> +      break;
> +    case MATCH_SRAI:
> +      TRACE_INSN (cpu, "srai %s, %s, %" PRIiTW ";  // %s = %s >>> %#" PRIxTW,
> +		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
> +      if (RISCV_XLEN (cpu) == 32)
> +	{
> +	  if (shamt_imm > 0x1f)
> +	    sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +	  tmp = ashiftrt (cpu->regs[rs1], shamt_imm);
> +	}
> +      else
> +	tmp = ashiftrt64 (cpu->regs[rs1], shamt_imm);
> +      store_rd (cpu, rd, tmp);
> +      break;
> +    case MATCH_SRAIW:
> +      TRACE_INSN (cpu, "sraiw %s, %s, %" PRIiTW ";  // %s = %s >>> %#" PRIxTW,
> +		  rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 (
> +	ashiftrt ((signed32) cpu->regs[rs1], shamt_imm)));
> +      break;
> +    case MATCH_SLT:
> +      TRACE_INSN (cpu, "slt");
> +      store_rd (cpu, rd,
> +		!!((signed_word) cpu->regs[rs1] < (signed_word) cpu->regs[rs2]));
> +      break;
> +    case MATCH_SLTU:
> +      TRACE_INSN (cpu, "sltu");
> +      store_rd (cpu, rd, !!((unsigned_word) cpu->regs[rs1] <
> +			    (unsigned_word) cpu->regs[rs2]));
> +      break;
> +    case MATCH_SLTI:
> +      TRACE_INSN (cpu, "slti");
> +      store_rd (cpu, rd, !!((signed_word) cpu->regs[rs1] <
> +			    (signed_word) i_imm));
> +      break;
> +    case MATCH_SLTIU:
> +      TRACE_INSN (cpu, "sltiu");
> +      store_rd (cpu, rd, !!((unsigned_word) cpu->regs[rs1] <
> +			    (unsigned_word) i_imm));
> +      break;
> +    case MATCH_AUIPC:
> +      TRACE_INSN (cpu, "auipc %s, %" PRIiTW ";  // %s = pc + %" PRIiTW,
> +		  rd_name, u_imm, rd_name, u_imm);
> +      store_rd (cpu, rd, cpu->pc + u_imm);
> +      break;
> +    case MATCH_BEQ:
> +      TRACE_INSN (cpu, "beq %s, %s, %#" PRIxTW ";  "
> +		       "// if (%s == %s) goto %#" PRIxTW,
> +		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
> +      if (cpu->regs[rs1] == cpu->regs[rs2])
> +	{
> +	  pc = cpu->pc + sb_imm;
> +	  TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +	}
> +      break;
> +    case MATCH_BLT:
> +      TRACE_INSN (cpu, "blt %s, %s, %#" PRIxTW ";  "
> +		       "// if (%s < %s) goto %#" PRIxTW,
> +		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
> +      if ((signed_word) cpu->regs[rs1] < (signed_word) cpu->regs[rs2])
> +	{
> +	  pc = cpu->pc + sb_imm;
> +	  TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +	}
> +      break;
> +    case MATCH_BLTU:
> +      TRACE_INSN (cpu, "bltu %s, %s, %#" PRIxTW ";  "
> +		       "// if (%s < %s) goto %#" PRIxTW,
> +		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
> +      if ((unsigned_word) cpu->regs[rs1] < (unsigned_word) cpu->regs[rs2])
> +	{
> +	  pc = cpu->pc + sb_imm;
> +	  TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +	}
> +      break;
> +    case MATCH_BGE:
> +      TRACE_INSN (cpu, "bge %s, %s, %#" PRIxTW ";  "
> +		       "// if (%s >= %s) goto %#" PRIxTW,
> +		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
> +      if ((signed_word) cpu->regs[rs1] >= (signed_word) cpu->regs[rs2])
> +	{
> +	  pc = cpu->pc + sb_imm;
> +	  TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +	}
> +      break;
> +    case MATCH_BGEU:
> +      TRACE_INSN (cpu, "bgeu %s, %s, %#" PRIxTW ";  "
> +		       "// if (%s >= %s) goto %#" PRIxTW,
> +		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
> +      if ((unsigned_word) cpu->regs[rs1] >= (unsigned_word) cpu->regs[rs2])
> +	{
> +	  pc = cpu->pc + sb_imm;
> +	  TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +	}
> +      break;
> +    case MATCH_BNE:
> +      TRACE_INSN (cpu, "bne %s, %s, %#" PRIxTW ";  "
> +		       "// if (%s != %s) goto %#" PRIxTW,
> +		  rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
> +      if (cpu->regs[rs1] != cpu->regs[rs2])
> +	{
> +	  pc = cpu->pc + sb_imm;
> +	  TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +	}
> +      break;
> +    case MATCH_JAL:
> +      TRACE_INSN (cpu, "jal %s, %" PRIiTW ";", rd_name,
> +		  EXTRACT_UJTYPE_IMM (iw));
> +      store_rd (cpu, rd, cpu->pc + 4);
> +      pc = cpu->pc + EXTRACT_UJTYPE_IMM (iw);
> +      TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +      break;
> +    case MATCH_JALR:
> +      TRACE_INSN (cpu, "jalr %s, %s, %" PRIiTW ";", rd_name, rs1_name, i_imm);
> +      store_rd (cpu, rd, cpu->pc + 4);
> +      pc = cpu->regs[rs1] + i_imm;
> +      TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
> +      break;
> +
> +    case MATCH_LD:
> +      TRACE_INSN (cpu, "ld %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd,
> +	sim_core_read_unaligned_8 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm));
> +      break;
> +    case MATCH_LW:
> +      TRACE_INSN (cpu, "lw %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      store_rd (cpu, rd, EXTEND32 (
> +	sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm)));
> +      break;
> +    case MATCH_LWU:
> +      TRACE_INSN (cpu, "lwu %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      store_rd (cpu, rd,
> +	sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm));
> +      break;
> +    case MATCH_LH:
> +      TRACE_INSN (cpu, "lh %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      store_rd (cpu, rd, EXTEND16 (
> +	sim_core_read_unaligned_2 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm)));
> +      break;
> +    case MATCH_LHU:
> +      TRACE_INSN (cpu, "lbu %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      store_rd (cpu, rd,
> +	sim_core_read_unaligned_2 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm));
> +      break;
> +    case MATCH_LB:
> +      TRACE_INSN (cpu, "lb %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      store_rd (cpu, rd, EXTEND8 (
> +	sim_core_read_unaligned_1 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm)));
> +      break;
> +    case MATCH_LBU:
> +      TRACE_INSN (cpu, "lbu %s, %" PRIiTW "(%s);",
> +		  rd_name, i_imm, rs1_name);
> +      store_rd (cpu, rd,
> +	sim_core_read_unaligned_1 (cpu, cpu->pc, read_map,
> +				   cpu->regs[rs1] + i_imm));
> +      break;
> +    case MATCH_SD:
> +      TRACE_INSN (cpu, "sd %s, %" PRIiTW "(%s);",
> +		  rs2_name, s_imm, rs1_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      sim_core_write_unaligned_8 (cpu, cpu->pc, write_map,
> +				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
> +      break;
> +    case MATCH_SW:
> +      TRACE_INSN (cpu, "sw %s, %" PRIiTW "(%s);",
> +		  rs2_name, s_imm, rs1_name);
> +      sim_core_write_unaligned_4 (cpu, cpu->pc, write_map,
> +				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
> +      break;
> +    case MATCH_SH:
> +      TRACE_INSN (cpu, "sh %s, %" PRIiTW "(%s);",
> +		  rs2_name, s_imm, rs1_name);
> +      sim_core_write_unaligned_2 (cpu, cpu->pc, write_map,
> +				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
> +      break;
> +    case MATCH_SB:
> +      TRACE_INSN (cpu, "sb %s, %" PRIiTW "(%s);",
> +		  rs2_name, s_imm, rs1_name);
> +      sim_core_write_unaligned_1 (cpu, cpu->pc, write_map,
> +				  cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
> +      break;
> +
> +    case MATCH_CSRRC:
> +      TRACE_INSN (cpu, "csrrc");
> +      switch (csr)
> +	{
> +#define DECLARE_CSR(name, num, ...) \
> +	case num: \
> +	  store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
> +	  store_csr (cpu, #name, num, &cpu->csr.name, \
> +		     cpu->csr.name & !cpu->regs[rs1]); \
> +	  break;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +	}
> +      break;
> +    case MATCH_CSRRS:
> +      TRACE_INSN (cpu, "csrrs");
> +      switch (csr)
> +	{
> +#define DECLARE_CSR(name, num, ...) \
> +	case num: \
> +	  store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
> +	  store_csr (cpu, #name, num, &cpu->csr.name, \
> +		     cpu->csr.name | cpu->regs[rs1]); \
> +	  break;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +	}
> +      break;
> +    case MATCH_CSRRW:
> +      TRACE_INSN (cpu, "csrrw");
> +      switch (csr)
> +	{
> +#define DECLARE_CSR(name, num, ...) \
> +	case num: \
> +	  store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
> +	  store_csr (cpu, #name, num, &cpu->csr.name, cpu->regs[rs1]); \
> +	  break;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +	}
> +      break;
> +
> +    case MATCH_RDCYCLE:
> +      TRACE_INSN (cpu, "rdcycle %s;", rd_name);
> +      store_rd (cpu, rd, fetch_csr (cpu, "cycle", CSR_CYCLE, &cpu->csr.cycle));
> +      break;
> +    case MATCH_RDCYCLEH:
> +      TRACE_INSN (cpu, "rdcycleh %s;", rd_name);
> +      RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd,
> +		fetch_csr (cpu, "cycleh", CSR_CYCLEH, &cpu->csr.cycleh));
> +      break;
> +    case MATCH_RDINSTRET:
> +      TRACE_INSN (cpu, "rdinstret %s;", rd_name);
> +      store_rd (cpu, rd,
> +		fetch_csr (cpu, "instret", CSR_INSTRET, &cpu->csr.instret));
> +      break;
> +    case MATCH_RDINSTRETH:
> +      TRACE_INSN (cpu, "rdinstreth %s;", rd_name);
> +      RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd,
> +		fetch_csr (cpu, "instreth", CSR_INSTRETH, &cpu->csr.instreth));
> +      break;
> +    case MATCH_RDTIME:
> +      TRACE_INSN (cpu, "rdtime %s;", rd_name);
> +      store_rd (cpu, rd, fetch_csr (cpu, "time", CSR_TIME, &cpu->csr.time));
> +      break;
> +    case MATCH_RDTIMEH:
> +      TRACE_INSN (cpu, "rdtimeh %s;", rd_name);
> +      RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, fetch_csr (cpu, "timeh", CSR_TIMEH, &cpu->csr.timeh));
> +      break;
> +
> +    case MATCH_FENCE:
> +      TRACE_INSN (cpu, "fence;");
> +      break;
> +    case MATCH_FENCE_I:
> +      TRACE_INSN (cpu, "fence.i;");
> +      break;
> +    case MATCH_SBREAK:
> +      TRACE_INSN (cpu, "sbreak;");
> +      /* GDB expects us to step over SBREAK.  */
> +      sim_engine_halt (sd, cpu, NULL, cpu->pc + 4, sim_stopped, SIM_SIGTRAP);
> +      break;
> +    case MATCH_ECALL:
> +      TRACE_INSN (cpu, "ecall;");
> +      cpu->a0 = sim_syscall (cpu, cpu->a7, cpu->a0, cpu->a1, cpu->a2, cpu->a3);
> +      break;
> +    default:
> +      TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
> +      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +    }
> +
> +  return pc;
> +}
> +
> +static unsigned64
> +mulhu (unsigned64 a, unsigned64 b)
> +{
> +#ifdef __GNUC__
> +  return ((__int128)a * b) >> 64;
> +#else
> +  uint64_t t;
> +  uint32_t y1, y2, y3;
> +  uint64_t a0 = (uint32_t)a, a1 = a >> 32;
> +  uint64_t b0 = (uint32_t)b, b1 = b >> 32;
> +
> +  t = a1*b0 + ((a0*b0) >> 32);
> +  y1 = t;
> +  y2 = t >> 32;
> +
> +  t = a0*b1 + y1;
> +  y1 = t;
> +
> +  t = a1*b1 + y2 + (t >> 32);
> +  y2 = t;
> +  y3 = t >> 32;
> +
> +  return ((uint64_t)y3 << 32) | y2;
> +#endif
> +}
> +
> +static unsigned64
> +mulh (signed64 a, signed64 b)
> +{
> +  int negate = (a < 0) != (b < 0);
> +  uint64_t res = mulhu (a < 0 ? -a : a, b < 0 ? -b : b);
> +  return negate ? ~res + (a * b == 0) : res;
> +}
> +
> +static unsigned64
> +mulhsu (signed64 a, unsigned64 b)
> +{
> +  int negate = a < 0;
> +  uint64_t res = mulhu (a < 0 ? -a : a, b);
> +  return negate ? ~res + (a * b == 0) : res;
> +}
> +
> +static sim_cia
> +execute_m (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
> +{
> +  SIM_DESC sd = CPU_STATE (cpu);
> +  int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
> +  int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
> +  int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
> +  const char *rd_name = riscv_gpr_names_abi[rd];
> +  const char *rs1_name = riscv_gpr_names_abi[rs1];
> +  const char *rs2_name = riscv_gpr_names_abi[rs2];
> +  unsigned_word tmp, dividend_max;
> +  sim_cia pc = cpu->pc + 4;
> +
> +  dividend_max = -((unsigned_word) 1 << (WITH_TARGET_WORD_BITSIZE - 1));
> +
> +  switch (op->match)
> +    {
> +    case MATCH_DIV:
> +      TRACE_INSN (cpu, "div %s, %s, %s;  // %s = %s / %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (cpu->regs[rs1] == dividend_max && cpu->regs[rs2] == -1)
> +	tmp = dividend_max;
> +      else if (cpu->regs[rs2])
> +	tmp = (signed_word) cpu->regs[rs1] / (signed_word) cpu->regs[rs2];
> +      else
> +	tmp = -1;
> +      store_rd (cpu, rd, tmp);
> +      break;
> +    case MATCH_DIVW:
> +      TRACE_INSN (cpu, "divw %s, %s, %s;  // %s = %s / %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      if (EXTEND32 (cpu->regs[rs2]) == -1)
> +	tmp = 1 << 31;
> +      else if (EXTEND32 (cpu->regs[rs2]))
> +	tmp = EXTEND32 (cpu->regs[rs1]) / EXTEND32 (cpu->regs[rs2]);
> +      else
> +	tmp = -1;
> +      store_rd (cpu, rd, EXTEND32 (tmp));
> +      break;
> +    case MATCH_DIVU:
> +      TRACE_INSN (cpu, "divu %s, %s, %s;  // %s = %s / %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (cpu->regs[rs2])
> +	store_rd (cpu, rd, (unsigned_word) cpu->regs[rs1]
> +			   / (unsigned_word) cpu->regs[rs2]);
> +      else
> +	store_rd (cpu, rd, -1);
> +      break;
> +    case MATCH_DIVUW:
> +      TRACE_INSN (cpu, "divuw %s, %s, %s;  // %s = %s / %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      if ((unsigned32) cpu->regs[rs2])
> +	tmp = (unsigned32) cpu->regs[rs1] / (unsigned32) cpu->regs[rs2];
> +      else
> +	tmp = -1;
> +      store_rd (cpu, rd, EXTEND32 (tmp));
> +      break;
> +    case MATCH_MUL:
> +      TRACE_INSN (cpu, "mul %s, %s, %s;  // %s = %s * %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      store_rd (cpu, rd, cpu->regs[rs1] * cpu->regs[rs2]);
> +      break;
> +    case MATCH_MULW:
> +      TRACE_INSN (cpu, "mulw %s, %s, %s;  // %s = %s * %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      store_rd (cpu, rd, EXTEND32 ((signed32) cpu->regs[rs1]
> +				   * (signed32) cpu->regs[rs2]));
> +      break;
> +    case MATCH_MULH:
> +      TRACE_INSN (cpu, "mulh %s, %s, %s;  // %s = %s * %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (RISCV_XLEN (cpu) == 32)
> +	store_rd (cpu, rd, ((signed64)(signed_word) cpu->regs[rs1]
> +			    * (signed64)(signed_word) cpu->regs[rs2]) >> 32);
> +      else
> +	store_rd (cpu, rd, mulh (cpu->regs[rs1], cpu->regs[rs2]));
> +      break;
> +    case MATCH_MULHU:
> +      TRACE_INSN (cpu, "mulhu %s, %s, %s;  // %s = %s * %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (RISCV_XLEN (cpu) == 32)
> +	store_rd (cpu, rd, ((unsigned64)cpu->regs[rs1]
> +			    * (unsigned64)cpu->regs[rs2]) >> 32);
> +      else
> +	store_rd (cpu, rd, mulhu (cpu->regs[rs1], cpu->regs[rs2]));
> +      break;
> +    case MATCH_MULHSU:
> +      TRACE_INSN (cpu, "mulhsu %s, %s, %s;  // %s = %s * %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (RISCV_XLEN (cpu) == 32)
> +	store_rd (cpu, rd, ((signed64)(signed_word) cpu->regs[rs1]
> +			    * (unsigned64)cpu->regs[rs2]) >> 32);
> +      else
> +	store_rd (cpu, rd, mulhsu (cpu->regs[rs1], cpu->regs[rs2]));
> +      break;
> +    case MATCH_REM:
> +      TRACE_INSN (cpu, "rem %s, %s, %s;  // %s = %s %% %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (cpu->regs[rs1] == dividend_max && cpu->regs[rs2] == -1)
> +	tmp = 0;
> +      else if (cpu->regs[rs2])
> +	tmp = (signed_word) cpu->regs[rs1] % (signed_word) cpu->regs[rs2];
> +      else
> +	tmp = cpu->regs[rs1];
> +      store_rd (cpu, rd, tmp);
> +      break;
> +    case MATCH_REMW:
> +      TRACE_INSN (cpu, "remw %s, %s, %s;  // %s = %s %% %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      if (EXTEND32 (cpu->regs[rs2]) == -1)
> +	tmp = 0;
> +      else if (EXTEND32 (cpu->regs[rs2]))
> +	tmp = EXTEND32 (cpu->regs[rs1]) % EXTEND32 (cpu->regs[rs2]);
> +      else
> +	tmp = cpu->regs[rs1];
> +      store_rd (cpu, rd, EXTEND32 (tmp));
> +      break;
> +    case MATCH_REMU:
> +      TRACE_INSN (cpu, "remu %s, %s, %s;  // %s = %s %% %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      if (cpu->regs[rs2])
> +	store_rd (cpu, rd, cpu->regs[rs1] % cpu->regs[rs2]);
> +      else
> +	store_rd (cpu, rd, cpu->regs[rs1]);
> +      break;
> +    case MATCH_REMUW:
> +      TRACE_INSN (cpu, "remuw %s, %s, %s;  // %s = %s %% %s",
> +		  rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
> +      RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +      if ((unsigned32) cpu->regs[rs2])
> +	tmp = (unsigned32) cpu->regs[rs1] % (unsigned32) cpu->regs[rs2];
> +      else
> +	tmp = cpu->regs[rs1];
> +      store_rd (cpu, rd, EXTEND32 (tmp));
> +      break;
> +    default:
> +      TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
> +      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +    }
> +
> +  return pc;
> +}
> +
> +#define MAX(a, b) ((a) > (b) ? (a) : (b))
> +#define MIN(a, b) ((a) < (b) ? (a) : (b))
> +
> +static sim_cia
> +execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
> +{
> +  SIM_DESC sd = CPU_STATE (cpu);
> +  int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
> +  int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
> +  int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
> +  const char *rd_name = riscv_gpr_names_abi[rd];
> +  const char *rs1_name = riscv_gpr_names_abi[rs1];
> +  const char *rs2_name = riscv_gpr_names_abi[rs2];
> +  struct atomic_mem_reserved_list *amo_prev, *amo_curr;
> +  unsigned_word tmp;
> +  sim_cia pc = cpu->pc + 4;
> +
> +  /* Handle these two load/store operations specifically.  */
> +  switch (op->match)
> +    {
> +    case MATCH_LR_W:
> +      TRACE_INSN (cpu, "%s %s, (%s);", op->name, rd_name, rs1_name);
> +      store_rd (cpu, rd,
> +	sim_core_read_unaligned_4 (cpu, cpu->pc, read_map, cpu->regs[rs1]));
> +
> +      /* Walk the reservation list to find an existing match.  */
> +      amo_curr = sd->amo_reserved_list;
> +      while (amo_curr)
> +	{
> +	  if (amo_curr->addr == cpu->regs[rs1])
> +	    goto done;
> +	  amo_curr = amo_curr->next;
> +	}
> +
> +      /* No reservation exists, so add one.  */
> +      amo_curr = xmalloc (sizeof (*amo_curr));
> +      amo_curr->addr = cpu->regs[rs1];
> +      amo_curr->next = sd->amo_reserved_list;
> +      sd->amo_reserved_list = amo_curr;
> +      goto done;
> +    case MATCH_SC_W:
> +      TRACE_INSN (cpu, "%s %s, %s, (%s);", op->name, rd_name, rs2_name,
> +		  rs1_name);
> +
> +      /* Walk the reservation list to find a match.  */
> +      amo_curr = amo_prev = sd->amo_reserved_list;
> +      while (amo_curr)
> +	{
> +	  if (amo_curr->addr == cpu->regs[rs1])
> +	    {
> +	      /* We found a reservation, so operate it.  */
> +	      sim_core_write_unaligned_4 (cpu, cpu->pc, write_map,
> +					  cpu->regs[rs1], cpu->regs[rs2]);
> +	      store_rd (cpu, rd, 0);
> +	      if (amo_curr == sd->amo_reserved_list)
> +		sd->amo_reserved_list = amo_curr->next;
> +	      else
> +		amo_prev->next = amo_curr->next;
> +	      free (amo_curr);
> +	      goto done;
> +	    }
> +	  amo_prev = amo_curr;
> +	  amo_curr = amo_curr->next;
> +	}
> +
> +      /* If we're still here, then no reservation exists, so mark as failed.  */
> +      store_rd (cpu, rd, 1);
> +      goto done;
> +    }
> +
> +  /* Handle the rest of the atomic insns with common code paths.  */
> +  TRACE_INSN (cpu, "%s %s, %s, (%s);",
> +	      op->name, rd_name, rs2_name, rs1_name);
> +  if (op->xlen_requirement == 64)
> +    tmp = sim_core_read_unaligned_8 (cpu, cpu->pc, read_map, cpu->regs[rs1]);
> +  else
> +    tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
> +					       cpu->regs[rs1]));
> +  store_rd (cpu, rd, tmp);
> +
> +  switch (op->match)
> +    {
> +    case MATCH_AMOADD_D:
> +    case MATCH_AMOADD_W:
> +      tmp = cpu->regs[rd] + cpu->regs[rs2];
> +      break;
> +    case MATCH_AMOAND_D:
> +    case MATCH_AMOAND_W:
> +      tmp = cpu->regs[rd] & cpu->regs[rs2];
> +      break;
> +    case MATCH_AMOMAX_D:
> +    case MATCH_AMOMAX_W:
> +      tmp = MAX ((signed_word) cpu->regs[rd], (signed_word) cpu->regs[rs2]);
> +      break;
> +    case MATCH_AMOMAXU_D:
> +    case MATCH_AMOMAXU_W:
> +      tmp = MAX ((unsigned_word) cpu->regs[rd], (unsigned_word) cpu->regs[rs2]);
> +      break;
> +    case MATCH_AMOMIN_D:
> +    case MATCH_AMOMIN_W:
> +      tmp = MIN ((signed_word) cpu->regs[rd], (signed_word) cpu->regs[rs2]);
> +      break;
> +    case MATCH_AMOMINU_D:
> +    case MATCH_AMOMINU_W:
> +      tmp = MIN ((unsigned_word) cpu->regs[rd], (unsigned_word) cpu->regs[rs2]);
> +      break;
> +    case MATCH_AMOOR_D:
> +    case MATCH_AMOOR_W:
> +      tmp = cpu->regs[rd] | cpu->regs[rs2];
> +      break;
> +    case MATCH_AMOSWAP_D:
> +    case MATCH_AMOSWAP_W:
> +      tmp = cpu->regs[rs2];
> +      break;
> +    case MATCH_AMOXOR_D:
> +    case MATCH_AMOXOR_W:
> +      tmp = cpu->regs[rd] ^ cpu->regs[rs2];
> +      break;
> +    default:
> +      TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
> +      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +    }
> +
> +  if (op->xlen_requirement == 64)
> +    sim_core_write_unaligned_8 (cpu, cpu->pc, write_map, cpu->regs[rs1], tmp);
> +  else
> +    sim_core_write_unaligned_4 (cpu, cpu->pc, write_map, cpu->regs[rs1], tmp);
> +
> + done:
> +  return pc;
> +}
> +
> +static sim_cia
> +execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
> +{
> +  SIM_DESC sd = CPU_STATE (cpu);
> +
> +  if (op->xlen_requirement == 32)
> +    RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
> +  else if (op->xlen_requirement == 64)
> +    RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
> +
> +  switch (op->insn_class)
> +    {
> +    case INSN_CLASS_A:
> +      return execute_a (cpu, iw, op);
> +    case INSN_CLASS_I:
> +      return execute_i (cpu, iw, op);
> +    case INSN_CLASS_M:
> +      return execute_m (cpu, iw, op);
> +    default:
> +      TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
> +      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
> +    }
> +
> +  return cpu->pc + riscv_insn_length (iw);
> +}
> +
> +/* Decode & execute a single instruction.  */
> +void step_once (SIM_CPU *cpu)
> +{
> +  SIM_DESC sd = CPU_STATE (cpu);
> +  unsigned_word iw;
> +  unsigned int len;
> +  sim_cia pc = cpu->pc;
> +  const struct riscv_opcode *op;
> +  int xlen = RISCV_XLEN (cpu);
> +
> +  if (TRACE_ANY_P (cpu))
> +    trace_prefix (sd, cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu),
> +		  NULL, 0, " "); /* Use a space for gcc warnings.  */
> +
> +  iw = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);
> +
> +  /* Reject non-32-bit opcodes first.  */
> +  len = riscv_insn_length (iw);
> +  if (len != 4)
> +    {
> +      sim_io_printf (sd, "sim: bad insn len %#x @ %#" PRIxTA ": %#" PRIxTW "\n",
> +		     len, pc, iw);
> +      sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
> +    }
> +
> +  iw |= ((unsigned_word) sim_core_read_aligned_2 (
> +    cpu, pc, exec_map, pc + 2) << 16);
> +
> +  TRACE_CORE (cpu, "0x%08" PRIxTW, iw);
> +
> +  op = riscv_hash[OP_HASH_IDX (iw)];
> +  if (!op)
> +    sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
> +
> +  /* NB: Same loop logic as riscv_disassemble_insn.  */
> +  for (; op->name; op++)
> +    {
> +      /* Does the opcode match?  */
> +      if (! op->match_func (op, iw))
> +	continue;
> +      /* Is this a pseudo-instruction and may we print it as such?  */
> +      if (op->pinfo & INSN_ALIAS)
> +	continue;
> +      /* Is this instruction restricted to a certain value of XLEN?  */
> +      if (op->xlen_requirement != 0 && op->xlen_requirement != xlen)
> +	continue;
> +
> +      /* It's a match.  */
> +      pc = execute_one (cpu, iw, op);
> +      break;
> +    }
> +
> +  /* TODO: Handle overflow into high 32 bits.  */
> +  /* TODO: Try to use a common counter and only update on demand (reads).  */
> +  ++cpu->csr.cycle;
> +  ++cpu->csr.instret;
> +
> +  cpu->pc = pc;
> +}
> +\f
> +/* Return the program counter for this cpu. */
> +static sim_cia
> +pc_get (sim_cpu *cpu)
> +{
> +  return cpu->pc;
> +}
> +
> +/* Set the program counter for this cpu to the new pc value. */
> +static void
> +pc_set (sim_cpu *cpu, sim_cia pc)
> +{
> +  cpu->pc = pc;
> +}
> +
> +/* Initialize the state for a single cpu.  Usuaully this involves clearing all
> +   registers back to their reset state.  Should also hook up the fetch/store
> +   helper functions too.  */
> +void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)
> +{
> +  const char *extensions;
> +  int i;
> +
> +  memset (cpu->regs, 0, sizeof (cpu->regs));
> +
> +  CPU_PC_FETCH (cpu) = pc_get;
> +  CPU_PC_STORE (cpu) = pc_set;
> +
> +  if (!riscv_hash[0])
> +    {
> +      const struct riscv_opcode *op;
> +
> +      for (op = riscv_opcodes; op->name; op++)
> +	if (!riscv_hash[OP_HASH_IDX (op->match)])
> +	  riscv_hash[OP_HASH_IDX (op->match)] = op;
> +    }
> +
> +  cpu->csr.misa = 0;
> +  /* RV32 sets this field to 0, and we don't really support RV128 yet.  */
> +  if (RISCV_XLEN (cpu) == 64)
> +    cpu->csr.misa |= (unsigned64)2 << 62;
> +
> +  /* Skip the leading "rv" prefix and the two numbers.  */
> +  extensions = MODEL_NAME (CPU_MODEL (cpu)) + 4;
> +  for (i = 0; i < 26; ++i)
> +    {
> +      char ext = 'A' + i;
> +
> +      if (ext == 'X')
> +	continue;
> +      else if (strchr (extensions, ext) != NULL)
> +	{
> +	  if (ext == 'G')
> +	    cpu->csr.misa |= 0x1129;  /* G = IMAFD.  */
> +	  else
> +	    cpu->csr.misa |= (1 << i);
> +	}
> +    }
> +
> +  cpu->csr.mimpid = 0x8000;
> +  cpu->csr.mhartid = mhartid;
> +}
> +\f
> +/* Some utils don't like having a NULL environ.  */
> +static const char * const simple_env[] = { "HOME=/", "PATH=/bin", NULL };
> +
> +/* Count the number of arguments in an argv.  */
> +static int
> +count_argv (const char * const *argv)
> +{
> +  int i;
> +
> +  if (!argv)
> +    return -1;
> +
> +  for (i = 0; argv[i] != NULL; ++i)
> +    continue;
> +  return i;
> +}
> +
> +void initialize_env (SIM_DESC sd, const char * const *argv,
> +		     const char * const *env)
> +{
> +  SIM_CPU *cpu = STATE_CPU (sd, 0);
> +  int i;
> +  int argc, argv_flat;
> +  int envc, env_flat;
> +  address_word sp, sp_flat;
> +  unsigned char null[8] = { 0, 0, 0, 0, 0, 0, 0, 0, };
> +
> +  /* Figure out how many bytes the argv strings take up.  */
> +  argc = count_argv (argv);
> +  if (argc == -1)
> +    argc = 0;
> +  argv_flat = argc; /* NUL bytes.  */
> +  for (i = 0; i < argc; ++i)
> +    argv_flat += strlen (argv[i]);
> +
> +  /* Figure out how many bytes the environ strings take up.  */
> +  if (!env)
> +    env = simple_env;
> +  envc = count_argv (env);
> +  env_flat = envc; /* NUL bytes.  */
> +  for (i = 0; i < envc; ++i)
> +    env_flat += strlen (env[i]);
> +
> +  /* Make space for the strings themselves.  */
> +  sp_flat = (DEFAULT_MEM_SIZE - argv_flat - env_flat) & -sizeof (address_word);
> +  /* Then the pointers to the strings.  */
> +  sp = sp_flat - ((argc + 1 + envc + 1) * sizeof (address_word));
> +  /* Then the argc.  */
> +  sp -= sizeof (unsigned_word);
> +
> +  /* Set up the regs the libgloss crt0 expects.  */
> +  cpu->a0 = argc;
> +  cpu->sp = sp;
> +
> +  /* First push the argc value.  */
> +  sim_write (sd, sp, (void *)&argc, sizeof (unsigned_word));
> +  sp += sizeof (unsigned_word);
> +
> +  /* Then the actual argv strings so we know where to point argv[].  */
> +  for (i = 0; i < argc; ++i)
> +    {
> +      unsigned len = strlen (argv[i]) + 1;
> +      sim_write (sd, sp_flat, (void *)argv[i], len);
> +      sim_write (sd, sp, (void *)&sp_flat, sizeof (address_word));
> +      sp_flat += len;
> +      sp += sizeof (address_word);
> +    }
> +  sim_write (sd, sp, null, sizeof (address_word));
> +  sp += sizeof (address_word);
> +
> +  /* Then the actual env strings so we know where to point env[].  */
> +  for (i = 0; i < envc; ++i)
> +    {
> +      unsigned len = strlen (env[i]) + 1;
> +      sim_write (sd, sp_flat, (void *)env[i], len);
> +      sim_write (sd, sp, (void *)&sp_flat, sizeof (address_word));
> +      sp_flat += len;
> +      sp += sizeof (address_word);
> +    }
> +}
> diff --git a/sim/riscv/sim-main.h b/sim/riscv/sim-main.h
> new file mode 100644
> index 000000000000..4a1b31ee2fec
> --- /dev/null
> +++ b/sim/riscv/sim-main.h
> @@ -0,0 +1,86 @@
> +/* RISC-V simulator.
> +
> +   Copyright (C) 2005-2021 Free Software Foundation, Inc.
> +   Contributed by Mike Frysinger.
> +
> +   This file is part of simulators.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef SIM_MAIN_H
> +#define SIM_MAIN_H
> +
> +#include "sim-basics.h"
> +#include "machs.h"
> +#include "sim-base.h"
> +
> +struct _sim_cpu {
> +  union {
> +    unsigned_word regs[32];
> +    struct {
> +      /* These are the ABI names.  */
> +      unsigned_word zero, ra, sp, gp, tp;
> +      unsigned_word t0, t1, t2;
> +      unsigned_word s0, s1;
> +      unsigned_word a0, a1, a2, a3, a4, a5, a6, a7;
> +      unsigned_word s2, s3, s4, s5, s6, s7, s8, s9, s10, s11;
> +      unsigned_word t3, t4, t5, t6;
> +    };
> +  };
> +  union {
> +    unsigned_word fpregs[32];
> +    struct {
> +      /* These are the ABI names.  */
> +      unsigned_word ft0, ft1, ft2, ft3, ft4, ft5, ft6, ft7;
> +      unsigned_word fs0, fs1;
> +      unsigned_word fa0, fa1, fa2, fa3, fa4, fa5, fa6, fa7;
> +      unsigned_word fs2, fs3, fs4, fs5, fs6, fs7, fs8, fs9, fs10, fs11;
> +      unsigned_word ft8, ft9, ft10, ft11;
> +    };
> +  };
> +  sim_cia pc;
> +
> +  struct {
> +#define DECLARE_CSR(name, ...) unsigned_word name;
> +#include "opcode/riscv-opc.h"
> +#undef DECLARE_CSR
> +  } csr;
> +
> +  sim_cpu_base base;
> +};
> +
> +struct atomic_mem_reserved_list;
> +struct atomic_mem_reserved_list {
> +  struct atomic_mem_reserved_list *next;
> +  address_word addr;
> +};
> +
> +struct sim_state {
> +  sim_cpu *cpu[MAX_NR_PROCESSORS];
> +  struct atomic_mem_reserved_list *amo_reserved_list;
> +
> +  /* ... simulator specific members ... */
> +  sim_state_base base;
> +};
> +
> +extern void step_once (SIM_CPU *);
> +extern void initialize_cpu (SIM_DESC, SIM_CPU *, int);
> +extern void initialize_env (SIM_DESC, const char * const *argv,
> +			    const char * const *env);
> +
> +#define DEFAULT_MEM_SIZE (64 * 1024 * 1024)
> +
> +#define RISCV_XLEN(cpu) MACH_WORD_BITSIZE (CPU_MACH (cpu))
> +
> +#endif
> diff --git a/sim/testsuite/ChangeLog b/sim/testsuite/ChangeLog
> index a32672a46bea..72cfa195542d 100644
> --- a/sim/testsuite/ChangeLog
> +++ b/sim/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* configure: Regenerate.
> +	* sim/riscv: New directory.
> +
>  2021-01-11  Mike Frysinger  <vapier@gentoo.org>
>  
>  	* common/alu-tst.c: Include stdlib.h.
> diff --git a/sim/testsuite/sim/riscv/ChangeLog b/sim/testsuite/sim/riscv/ChangeLog
> new file mode 100644
> index 000000000000..05c42fc7d6e5
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/ChangeLog
> @@ -0,0 +1,3 @@
> +2021-01-12  Mike Frysinger  <vapier@gentoo.org>
> +
> +	* allinsn.exp, pass.s, testutils.inc: New files.
> diff --git a/sim/testsuite/sim/riscv/allinsn.exp b/sim/testsuite/sim/riscv/allinsn.exp
> new file mode 100644
> index 000000000000..03bec1b541e7
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/allinsn.exp
> @@ -0,0 +1,15 @@
> +# RISC-V simulator testsuite.
> +
> +if [istarget riscv*-*] {
> +    # all machines
> +    set all_machs "riscv"
> +
> +    foreach src [lsort [glob -nocomplain $srcdir/$subdir/*.s]] {
> +	# If we're only testing specific files and this isn't one of them,
> +	# skip it.
> +	if ![runtest_file_p $runtests $src] {
> +	    continue
> +	}
> +	run_sim_test $src $all_machs
> +    }
> +}
> diff --git a/sim/testsuite/sim/riscv/pass.s b/sim/testsuite/sim/riscv/pass.s
> new file mode 100644
> index 000000000000..bd428ca16772
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/pass.s
> @@ -0,0 +1,7 @@
> +# check that the sim doesn't die immediately.
> +# mach: riscv
> +
> +.include "testutils.inc"
> +
> +	start
> +	pass
> diff --git a/sim/testsuite/sim/riscv/testutils.inc b/sim/testsuite/sim/riscv/testutils.inc
> new file mode 100644
> index 000000000000..138417009122
> --- /dev/null
> +++ b/sim/testsuite/sim/riscv/testutils.inc
> @@ -0,0 +1,52 @@
> +# MACRO: exit
> +	.macro exit nr
> +	li a0, \nr
> +	# The exit utility function.
> +	li a7, 93;
> +	# Trigger OS trap.
> +	ecall;
> +	.endm
> +
> +# MACRO: pass
> +# Write 'pass' to stdout and quit
> +	.macro pass
> +	# syscall write().
> +	li a7, 64;
> +	# Use stdout.
> +	li a0, 1;
> +	# Point to the string.
> +	lla a1, 1f;
> +	# Number of bytes to write.
> +	li a2, 5;
> +	# Trigger OS trap.
> +	ecall;
> +	exit 0;
> +	.data
> +	1: .asciz "pass\n"
> +	.endm
> +
> +# MACRO: fail
> +# Write 'fail' to stdout and quit
> +	.macro fail
> +	# syscall write();
> +	li a7, 64;
> +	# Use stdout.
> +	li a0, 1;
> +	# Point to the string.
> +	lla a1, 1f;
> +	# Number of bytes to write.
> +	li a2, 5;
> +	# Trigger OS trap.
> +	ecall;
> +	exit 0;
> +	.data
> +	1: .asciz "fail\n"
> +	.endm
> +
> +# MACRO: start
> +# All assembler tests should start with a call to "start"
> +	.macro start
> +	.text
> +.global _start
> +_start:
> +	.endm
> -- 
> 2.28.0
> 

  parent reply	other threads:[~2021-02-04 10:52 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-12 11:18 Mike Frysinger via Gdb-patches
2021-01-12 11:18 ` [PATCH 2/2] riscv: enable gdb/sim integration Mike Frysinger via Gdb-patches
2021-02-04 10:45   ` Andrew Burgess
2021-02-03 23:50 ` [PATCH 1/2] sim: riscv: new port Jim Wilson
2021-02-04  2:38   ` Mike Frysinger via Gdb-patches
2021-02-04 22:56     ` Jim Wilson
2021-02-04 10:52 ` Andrew Burgess [this message]
2021-02-04 14:14 ` Andrew Burgess
2021-02-05  0:04   ` Mike Frysinger via Gdb-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210204105215.GY265215@embecosm.com \
    --to=andrew.burgess@embecosm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=vapier@gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox