[FT32] gdb and sim support

Message ID CA9BBF0458F83C4F9051448B941B57D117234AD6@glaexch1
State Committed
Headers

Commit Message

James Bowman March 23, 2015, 7:20 p.m. UTC
  OK, have applied your patches.
Also commented the IO port simulation, and described the memory map.
As discussed in the other thread, the sim now uses
gdb's sim-ft32.h for its register numbers.

Thanks very much. ChangeLog entry and patches follow.


sim/:
2015-03-23  James Bowman  <james.bowman@ftdichip.com>

	* configure.tgt: Add FT32 entry.

sim/ft32/:
2015-03-23  James Bowman  <james.bowman@ftdichip.com>

	* Makefile.in: New file, FT32 sim Makefile.
	* configure.ac: New file, FT32 sim config.
	* config.in: New file, FT32 sim config.
	* ft32-sim.h: New file, FT32 sim CPU object.
	* interp.c: New file, FT32 simulator.
	* sim-main.h: New file, FT32 simulator wrapper.

sim/testsuite/sim/ft32/
2015-03-23  James Bowman  <james.bowman@ftdichip.com>

	* ChangeLog: New file, FT32 sim testsuite changelog.
	* allinsn.exp: New file, FT32 sim testsuite.
	* basic.s: New file, FT32 sim testsuite basic test.
	* testutils.inc: New file, FT32 sim testsuite utility macros.


From: James Bowman <james.bowman@ftdichip.com>
Date: Mon, 23 Mar 2015 12:17:02 -0700
Subject: [PATCH] FT32 Support

---
 sim/configure.tgt                    |    4 +
 sim/ft32/Makefile.in                 |   48 ++
 sim/ft32/config.in                   |  163 ++++++
 sim/ft32/configure.ac                |   15 +
 sim/ft32/ft32-sim.h                  |   43 ++
 sim/ft32/interp.c                    |  913 ++++++++++++++++++++++++++++++++++
 sim/ft32/sim-main.h                  |   58 +++
 sim/testsuite/sim/ft32/ChangeLog     |    3 +
 sim/testsuite/sim/ft32/allinsn.exp   |   15 +
 sim/testsuite/sim/ft32/basic.s       |  862 ++++++++++++++++++++++++++++++++
 sim/testsuite/sim/ft32/testutils.inc |   65 +++
 11 files changed, 2189 insertions(+)
 create mode 100644 sim/ft32/Makefile.in
 create mode 100644 sim/ft32/config.in
 create mode 100644 sim/ft32/configure.ac
 create mode 100644 sim/ft32/ft32-sim.h
 create mode 100644 sim/ft32/interp.c
 create mode 100644 sim/ft32/sim-main.h
 create mode 100644 sim/testsuite/sim/ft32/ChangeLog
 create mode 100644 sim/testsuite/sim/ft32/allinsn.exp
 create mode 100644 sim/testsuite/sim/ft32/basic.s
 create mode 100644 sim/testsuite/sim/ft32/testutils.inc
  

Comments

Mike Frysinger March 26, 2015, 7:10 a.m. UTC | #1
On 23 Mar 2015 19:20, James Bowman wrote:
> +/*
> + * FT32 is a Harvard architecture: RAM and code occupy
> + * different address spaces.
> + *
> + * sim and gdb model FT32 memory by adding 0x800000 to RAM
> + * addresses. This means that sim/gdb can treat all addresses
> + * similarly.
> + * 
> + * The address space looks like:
> + *
> + *    00000   start of code memory
> + *    3ffff   end of code memory
> + *   800000   start of RAM
> + *   80ffff   end of RAM
> + */

do they actually occupy different address spaces ?  that is to say, if you put 
address 0 into a register and try to do a data load/store, does it throw an 
exception ?  or is it transparently routed to 800000 ?

Blackfin cpus are described as Harvard because it has on-chip memory that can 
only be accessed either as insn or as data, but it's a unified memory map.  for 
example, if you were to execute (jump to) 0xffa00000, it'd work, but if you 
tried to do a load/store with 0xffa00000, the hardware would throw an exception.  
if you were to execute (jump to) 0xff800000, the hardware would throw an 
exception, but you could do a load/store just fine.

i'm not sure gdb or the sim generally support processors with overlapping 
memory.  i.e. a $pc of 0 is completely different from a $r0 (data register) with 
the address of 0.

i tried to grab some datasheets from ftdichip.com, but (1) couldn't seem to find 
anything on the architecture (just datasheets for specific parts which covered 
more system devices than the ISA/core) and (2) it's crazy slow to download from.
-mike
  
James Bowman March 26, 2015, 7:05 p.m. UTC | #2
> > +/*
> > + * FT32 is a Harvard architecture: RAM and code occupy
> > + * different address spaces.
> > + *
> > + * sim and gdb model FT32 memory by adding 0x800000 to RAM
> > + * addresses. This means that sim/gdb can treat all addresses
> > + * similarly.
> > + *
> > + * The address space looks like:
> > + *
> > + *    00000   start of code memory
> > + *    3ffff   end of code memory
> > + *   800000   start of RAM
> > + *   80ffff   end of RAM
> > + */
>
> do they actually occupy different address spaces ?  that is to say, if you put
> address 0 into a register and try to do a data load/store, does it throw an
> exception ?  or is it transparently routed to 800000 ?

At the hardware level, there are two things that have address 0: a piece
of RAM and an instruction. The CPU uses different instructions to
read RAM and instruction memory.

> Blackfin cpus are described as Harvard because it has on-chip memory that can
> only be accessed either as insn or as data, but it's a unified memory map.  for
> example, if you were to execute (jump to) 0xffa00000, it'd work, but if you
> tried to do a load/store with 0xffa00000, the hardware would throw an exception.
> if you were to execute (jump to) 0xff800000, the hardware would throw an
> exception, but you could do a load/store just fine.

FT32 is closer to the AVR. Both memory spaces start at address zero.

> i'm not sure gdb or the sim generally support processors with overlapping
> memory.  i.e. a $pc of 0 is completely different from a $r0 (data register) with
> the address of 0.

I believe that offsetting RAM at 800000 is the same way the AVR gdb port works.

The FT32 gdb uses the same approach. We generate a link map that declares 
the RAM at address 800000. Then GDB can use a single pointer to represent
either an instruction or a RAM address.  FT32's gdb interface understands
that bit 23 of an address distinguishes betweeen instruction and RAM.

This all seems to work quite well, and of course following the same 
model in the simulator has made the gdb-sim interface quite straightforward.

> i tried to grab some datasheets from ftdichip.com, but (1) couldn't seem to find
> anything on the architecture (just datasheets for specific parts which covered
> more system devices than the ISA/core) and (2) it's crazy slow to download from.

I will mention this to the people at FTDI. The FT900 (which contains
the FT32 is still new).

--
James Bowman
FTDI Open Source Liaison
  

Patch

diff --git a/sim/configure.tgt b/sim/configure.tgt
index d112e72..026b81f 100644
--- a/sim/configure.tgt
+++ b/sim/configure.tgt
@@ -111,6 +111,10 @@  case "${target}" in
    powerpc*-*-*)
        SIM_ARCH(ppc)
        ;;
+   ft32-*-*)
+       SIM_ARCH(ft32)
+       sim_testsuite=yes
+       ;;
    v850*-*-*)
        SIM_ARCH(v850)
        sim_igen=yes
diff --git a/sim/ft32/Makefile.in b/sim/ft32/Makefile.in
new file mode 100644
index 0000000..f16039a
--- /dev/null
+++ b/sim/ft32/Makefile.in
@@ -0,0 +1,48 @@ 
+#    Makefile template for Configure for the ft32 sim library.
+#    Copyright (C) 2008-2015 Free Software Foundation, Inc.
+#    Written by FTDI
+#
+# 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/>.
+
+## COMMON_PRE_CONFIG_FRAG
+
+
+SIM_OBJS = \
+	$(SIM_NEW_COMMON_OBJS) \
+	interp.o	\
+	sim-bits.o	\
+	sim-config.o	\
+	sim-core.o	\
+	sim-cpu.o	\
+	sim-endian.o	\
+	sim-engine.o	\
+	sim-events.o	\
+	sim-fpu.o	\
+	sim-hload.o	\
+	sim-io.o	\
+	sim-load.o	\
+	sim-memopt.o	\
+	sim-module.o	\
+	sim-options.o	\
+	sim-profile.o	\
+	sim-reason.o	\
+	sim-reg.o	\
+	sim-resume.o	\
+	sim-signal.o	\
+	sim-stop.o	\
+	sim-trace.o	\
+	sim-utils.o	\
+	$(SIM_EXTRA_OBJS)
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/ft32/config.in b/sim/ft32/config.in
new file mode 100644
index 0000000..6003e58
--- /dev/null
+++ b/sim/ft32/config.in
@@ -0,0 +1,163 @@ 
+/* config.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#undef ENABLE_NLS
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the <fpu_control.h> header file. */
+#undef HAVE_FPU_CONTROL_H
+
+/* Define to 1 if you have the `getrusage' function. */
+#undef HAVE_GETRUSAGE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the `time' function. */
+#undef HAVE_TIME
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the `__setfpucw' function. */
+#undef HAVE___SETFPUCW
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Name of this package. */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Additional package description */
+#undef PKGVERSION
+
+/* Bug reporting address */
+#undef REPORT_BUGS_TO
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+#include "tconfig.h"
diff --git a/sim/ft32/configure.ac b/sim/ft32/configure.ac
new file mode 100644
index 0000000..2107f74
--- /dev/null
+++ b/sim/ft32/configure.ac
@@ -0,0 +1,15 @@ 
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.64)dnl
+AC_INIT(Makefile.in)
+sinclude(../common/acinclude.m4)
+
+SIM_AC_COMMON
+
+SIM_AC_OPTION_ENDIAN(LITTLE_ENDIAN)
+SIM_AC_OPTION_ALIGNMENT(STRICT_ALIGNMENT)
+SIM_AC_OPTION_HOSTENDIAN
+SIM_AC_OPTION_ENVIRONMENT
+SIM_AC_OPTION_INLINE
+SIM_AC_OPTION_WARNINGS
+
+SIM_AC_OUTPUT
diff --git a/sim/ft32/ft32-sim.h b/sim/ft32/ft32-sim.h
new file mode 100644
index 0000000..1d664ba
--- /dev/null
+++ b/sim/ft32/ft32-sim.h
@@ -0,0 +1,43 @@ 
+/* Simulator for the FT32 processor
+
+   Copyright (C) 2008-2015 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+   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 _FT32_SIM_H_
+#define _FT32_SIM_H_
+
+#include <stdint.h>
+
+#include "gdb/sim-ft32.h"
+
+#define FT32_HARD_FP 29
+#define FT32_HARD_CC 30
+#define FT32_HARD_SP 31
+
+struct ft32_cpu_state {
+  uint32_t regs[32];
+  uint32_t pc;
+  uint64_t num_i;
+  uint64_t cycles;
+  uint64_t next_tick_cycle;
+  int pm_unlock;
+  uint32_t pm_addr;
+  int exception;
+};
+
+#endif  /* _FT32_SIM_H_ */
diff --git a/sim/ft32/interp.c b/sim/ft32/interp.c
new file mode 100644
index 0000000..0c88e69
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,913 @@ 
+/* Simulator for the FT32 processor
+
+   Copyright (C) 2008-2015 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+   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 <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "bfd.h"
+#include "gdb/callback.h"
+#include "libiberty.h"
+#include "gdb/remote-sim.h"
+
+#include "sim-main.h"
+#include "sim-options.h"
+
+#include "opcode/ft32.h"
+
+/*
+ * FT32 is a Harvard architecture: RAM and code occupy
+ * different address spaces.
+ *
+ * sim and gdb model FT32 memory by adding 0x800000 to RAM
+ * addresses. This means that sim/gdb can treat all addresses
+ * similarly.
+ * 
+ * The address space looks like:
+ *
+ *    00000   start of code memory
+ *    3ffff   end of code memory
+ *   800000   start of RAM
+ *   80ffff   end of RAM
+ */
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+static unsigned long
+ft32_extract_unsigned_integer (unsigned char *addr, int len)
+{
+  unsigned long retval;
+  unsigned char *p;
+  unsigned char *startaddr = (unsigned char *) addr;
+  unsigned char *endaddr = startaddr + len;
+
+  /* Start at the most significant end of the integer, and work towards
+     the least significant.  */
+  retval = 0;
+
+  for (p = endaddr; p > startaddr;)
+    retval = (retval << 8) | * -- p;
+
+  return retval;
+}
+
+static void
+ft32_store_unsigned_integer (unsigned char *addr, int len, unsigned long val)
+{
+  unsigned char *p;
+  unsigned char *startaddr = (unsigned char *)addr;
+  unsigned char *endaddr = startaddr + len;
+
+  for (p = startaddr; p < endaddr; p++)
+    {
+      *p = val & 0xff;
+      val >>= 8;
+    }
+}
+
+/*
+ * Align EA according to its size DW.
+ * The FT32 ignores the low bit of a 16-bit addresss,
+ * and the low two bits of a 32-bit address.
+ */
+static uint32_t ft32_align (uint32_t dw, uint32_t ea)
+{
+  switch (dw)
+    {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+    }
+  return ea;
+}
+
+/* Read an item from memory address EA, sized DW.  */
+static uint32_t
+ft32_read_item (SIM_DESC sd, int dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint8_t byte[4];
+  uint32_t r;
+
+  ea = ft32_align (dw, ea);
+
+  switch (dw) {
+    case 0:
+      return sim_core_read_aligned_1 (cpu, cia, read_map, ea);
+    case 1:
+      return sim_core_read_aligned_2 (cpu, cia, read_map, ea);
+    case 2:
+      return sim_core_read_aligned_4 (cpu, cia, read_map, ea);
+    default:
+      abort ();
+  }
+}
+
+/* Write item V to memory address EA, sized DW.  */
+static void
+ft32_write_item (SIM_DESC sd, int dw, uint32_t ea, uint32_t v)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint8_t byte[4];
+
+  ea = ft32_align (dw, ea);
+
+  switch (dw) {
+    case 0:
+      sim_core_write_aligned_1 (cpu, cia, write_map, ea, v);
+      break;
+    case 1:
+      sim_core_write_aligned_2 (cpu, cia, write_map, ea, v);
+      break;
+    case 2:
+      sim_core_write_aligned_4 (cpu, cia, write_map, ea, v);
+      break;
+    default:
+      abort();
+  }
+}
+
+#define ILLEGAL() \
+  sim_engine_halt (sd, cpu, NULL, insnpc, sim_signalled, SIM_SIGILL)
+
+static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  uint32_t insnpc = cpu->state.pc;
+  uint32_t r;
+  uint8_t byte[4];
+
+  ea &= 0x1ffff;
+  if (ea & ~0xffff)
+    {
+      /* Simulate some IO devices */
+      switch (ea)
+	{
+	case 0x1fff4:
+	  /* Read the simulator cycle timer.  */
+	  return cpu->state.cycles / 100;
+	default:
+	  sim_io_eprintf (sd, "Illegal IO read address %08x, pc %#x\n",
+			  ea, insnpc);
+	  ILLEGAL ();
+	}
+    }
+  return ft32_read_item (sd, dw, RAM_BIAS + ea);
+}
+
+static void cpu_mem_write (SIM_DESC sd, uint32_t dw, uint32_t ea, uint32_t d)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  ea &= 0x1ffff;
+  if (ea & 0x10000)
+    {
+      /* Simulate some IO devices */
+      switch (ea)
+	{
+	case 0x10000:
+	  /* Console output */
+	  putchar (d & 0xff);
+	  break;
+	case 0x1fc80:
+	  /* Unlock the PM write port */
+	  cpu->state.pm_unlock = (d == 0x1337f7d1);
+	  break;
+	case 0x1fc84:
+	  /* Set the PM write address register */
+	  cpu->state.pm_addr = d;
+	  break;
+	case 0x1fc88:
+	  /* Write to PM */
+	  ft32_write_item (sd, dw, cpu->state.pm_addr, d);
+	  break;
+	case 0x1fffc:
+	  /* Normal exit.  */
+	  sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
+	  break;
+	case 0x1fff8:
+	  sim_io_printf (sd, "Debug write %08x\n", d);
+	  break;
+	default:
+	  sim_io_eprintf (sd, "Unknown IO write %08x to to %08x\n", d, ea);
+	}
+    }
+  else
+    ft32_write_item (sd, dw, RAM_BIAS + ea, d);
+}
+
+#define GET_BYTE(ea)    cpu_mem_read (sd, 0, (ea))
+#define PUT_BYTE(ea, d) cpu_mem_write (sd, 0, (ea), (d))
+
+/* LSBS (n) is a mask of the least significant N bits.  */
+#define LSBS(n) ((1U << (n)) - 1)
+
+static void ft32_push (SIM_DESC sd, uint32_t v)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  cpu->state.regs[FT32_HARD_SP] -= 4;
+  cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+  cpu_mem_write (sd, 2, cpu->state.regs[FT32_HARD_SP], v);
+}
+
+static uint32_t ft32_pop (SIM_DESC sd)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  uint32_t r = cpu_mem_read (sd, 2, cpu->state.regs[FT32_HARD_SP]);
+  cpu->state.regs[FT32_HARD_SP] += 4;
+  cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+  return r;
+}
+
+/* Extract the low SIZ bits of N as an unsigned number.  */
+static int nunsigned (int siz, int n)
+{
+  return n & LSBS (siz);
+}
+
+/* Extract the low SIZ bits of N as a signed number.  */
+static int nsigned (int siz, int n)
+{
+  int shift = (sizeof (int) * 8) - siz;
+  return (n << shift) >> shift;
+}
+
+/* Signed division N / D, matching hw behavior for (MIN_INT, -1).  */
+static uint32_t ft32sdiv (uint32_t n, uint32_t d)
+{
+  if (n == 0x80000000UL && d == 0xffffffffUL)
+    return 0x80000000UL;
+  else
+    return (uint32_t)((int)n / (int)d);
+}
+
+/* Signed modulus N % D, matching hw behavior for (MIN_INT, -1).  */
+static uint32_t ft32smod (uint32_t n, uint32_t d)
+{
+  if (n == 0x80000000UL && d == 0xffffffffUL)
+    return 0;
+  else
+    return (uint32_t)((int)n % (int)d);
+}
+
+/* Circular rotate right N by B bits.  */
+static uint32_t ror (uint32_t n, uint32_t b)
+{
+  b &= 31;
+  return (n >> b) | (n << (32 - b));
+}
+
+/* Implement the BINS machine instruction.
+   See FT32 Programmer's Reference for details.  */
+static uint32_t bins (uint32_t d, uint32_t f, uint32_t len, uint32_t pos)
+{
+  uint32_t bitmask = LSBS (len) << pos;
+  return (d & ~bitmask) | ((f << pos) & bitmask);
+}
+
+/* Implement the FLIP machine instruction.
+   See FT32 Programmer's Reference for details.  */
+static uint32_t flip (uint32_t x, uint32_t b)
+{
+  if (b & 1)
+    x = (x & 0x55555555) <<  1 | (x & 0xAAAAAAAA) >>  1;
+  if (b & 2)
+    x = (x & 0x33333333) <<  2 | (x & 0xCCCCCCCC) >>  2;
+  if (b & 4)
+    x = (x & 0x0F0F0F0F) <<  4 | (x & 0xF0F0F0F0) >>  4;
+  if (b & 8)
+    x = (x & 0x00FF00FF) <<  8 | (x & 0xFF00FF00) >>  8;
+  if (b & 16)
+    x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+  return x;
+}
+
+static void
+step_once (SIM_DESC sd)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint32_t inst;
+  uint32_t dw;
+  uint32_t cb;
+  uint32_t r_d;
+  uint32_t cr;
+  uint32_t cv;
+  uint32_t bt;
+  uint32_t r_1;
+  uint32_t rimm;
+  uint32_t r_2;
+  uint32_t k20;
+  uint32_t pa;
+  uint32_t aa;
+  uint32_t k16;
+  uint32_t k8;
+  uint32_t al;
+  uint32_t r_1v;
+  uint32_t rimmv;
+  uint32_t bit_pos;
+  uint32_t bit_len;
+  uint32_t upper;
+  uint32_t insnpc;
+
+  if (cpu->state.cycles >= cpu->state.next_tick_cycle)
+    {
+      cpu->state.next_tick_cycle += 100000;
+      ft32_push (sd, cpu->state.pc);
+      cpu->state.pc = 12;  /* interrupt 1.  */
+    }
+  inst = ft32_read_item (sd, 2, cpu->state.pc);
+  cpu->state.cycles += 1;
+
+  /* Handle "call 8" (which is FT32's "break" equivalent) here.  */
+  if (inst == 0x00340002)
+    {
+      sim_engine_halt (sd, cpu, NULL,
+		       cpu->state.pc,
+		       sim_stopped, SIM_SIGTRAP);
+      goto escape;
+    }
+
+  dw   =              (inst >> FT32_FLD_DW_BIT) & LSBS (FT32_FLD_DW_SIZ);
+  cb   =              (inst >> FT32_FLD_CB_BIT) & LSBS (FT32_FLD_CB_SIZ);
+  r_d  =              (inst >> FT32_FLD_R_D_BIT) & LSBS (FT32_FLD_R_D_SIZ);
+  cr   =              (inst >> FT32_FLD_CR_BIT) & LSBS (FT32_FLD_CR_SIZ);
+  cv   =              (inst >> FT32_FLD_CV_BIT) & LSBS (FT32_FLD_CV_SIZ);
+  bt   =              (inst >> FT32_FLD_BT_BIT) & LSBS (FT32_FLD_BT_SIZ);
+  r_1  =              (inst >> FT32_FLD_R_1_BIT) & LSBS (FT32_FLD_R_1_SIZ);
+  rimm =              (inst >> FT32_FLD_RIMM_BIT) & LSBS (FT32_FLD_RIMM_SIZ);
+  r_2  =              (inst >> FT32_FLD_R_2_BIT) & LSBS (FT32_FLD_R_2_SIZ);
+  k20  = nsigned (20, (inst >> FT32_FLD_K20_BIT) & LSBS (FT32_FLD_K20_SIZ));
+  pa   =              (inst >> FT32_FLD_PA_BIT) & LSBS (FT32_FLD_PA_SIZ);
+  aa   =              (inst >> FT32_FLD_AA_BIT) & LSBS (FT32_FLD_AA_SIZ);
+  k16  =              (inst >> FT32_FLD_K16_BIT) & LSBS (FT32_FLD_K16_SIZ);
+  k8   = nsigned (8,  (inst >> FT32_FLD_K8_BIT) & LSBS (FT32_FLD_K8_SIZ));
+  al   =              (inst >> FT32_FLD_AL_BIT) & LSBS (FT32_FLD_AL_SIZ);
+
+  r_1v = cpu->state.regs[r_1];
+  rimmv = (rimm & 0x400) ? nsigned (10, rimm) : cpu->state.regs[rimm & 0x1f];
+
+  bit_pos = rimmv & 31;
+  bit_len = 0xf & (rimmv >> 5);
+  if (bit_len == 0)
+    bit_len = 16;
+
+  upper = (inst >> 27);
+
+  insnpc = cpu->state.pc;
+  cpu->state.pc += 4;
+  switch (upper)
+    {
+    case FT32_PAT_TOC:
+    case FT32_PAT_TOCI:
+      {
+	int take = (cr == 3) || ((1 & (cpu->state.regs[28 + cr] >> cb)) == cv);
+	if (take)
+	  {
+	    cpu->state.cycles += 1;
+	    if (bt)
+	      ft32_push (sd, cpu->state.pc); /* this is a call.  */
+	    if (upper == FT32_PAT_TOC)
+	      cpu->state.pc = pa << 2;
+	    else
+	      cpu->state.pc = cpu->state.regs[r_2];
+	    if (cpu->state.pc == 0x8)
+		goto escape;
+	  }
+      }
+      break;
+
+    case FT32_PAT_ALUOP:
+    case FT32_PAT_CMPOP:
+      {
+	uint32_t result;
+	switch (al)
+	  {
+	  case 0x0: result = r_1v + rimmv; break;
+	  case 0x1: result = ror (r_1v, rimmv); break;
+	  case 0x2: result = r_1v - rimmv; break;
+	  case 0x3: result = (r_1v << 10) | (1023 & rimmv); break;
+	  case 0x4: result = r_1v & rimmv; break;
+	  case 0x5: result = r_1v | rimmv; break;
+	  case 0x6: result = r_1v ^ rimmv; break;
+	  case 0x7: result = ~(r_1v ^ rimmv); break;
+	  case 0x8: result = r_1v << rimmv; break;
+	  case 0x9: result = r_1v >> rimmv; break;
+	  case 0xa: result = (int32_t)r_1v >> rimmv; break;
+	  case 0xb: result = bins (r_1v, rimmv >> 10, bit_len, bit_pos); break;
+	  case 0xc: result = nsigned (bit_len, r_1v >> bit_pos); break;
+	  case 0xd: result = nunsigned (bit_len, r_1v >> bit_pos); break;
+	  case 0xe: result = flip (r_1v, rimmv); break;
+	  default:
+	    sim_io_eprintf (sd, "Unhandled alu %#x\n", al);
+	    ILLEGAL ();
+	  }
+	if (upper == FT32_PAT_ALUOP)
+	  cpu->state.regs[r_d] = result;
+	else
+	  {
+	    uint32_t dwmask = 0;
+	    int dwsiz = 0;
+	    int zero;
+	    int sign;
+	    int ahi;
+	    int bhi;
+	    int overflow;
+	    int carry;
+	    int bit;
+	    uint64_t ra;
+	    uint64_t rb;
+	    int above;
+	    int greater;
+	    int greatereq;
+
+	    switch (dw)
+	      {
+	      case 0: dwsiz = 7;  dwmask = 0xffU; break;
+	      case 1: dwsiz = 15; dwmask = 0xffffU; break;
+	      case 2: dwsiz = 31; dwmask = 0xffffffffU; break;
+	      }
+
+	    zero = (0 == (result & dwmask));
+	    sign = 1 & (result >> dwsiz);
+	    ahi = 1 & (r_1v >> dwsiz);
+	    bhi = 1 & (rimmv >> dwsiz);
+	    overflow = (sign != ahi) & (ahi == !bhi);
+	    bit = (dwsiz + 1);
+	    ra = r_1v & dwmask;
+	    rb = rimmv & dwmask;
+	    switch (al)
+	      {
+	      case 0x0: carry = 1 & ((ra + rb) >> bit); break;
+	      case 0x2: carry = 1 & ((ra - rb) >> bit); break;
+	      default:  carry = 0; break;
+	      }
+	    above = (!carry & !zero);
+	    greater = (sign == overflow) & !zero;
+	    greatereq = (sign == overflow);
+
+	    cpu->state.regs[r_d] = (
+	      (above << 6) |
+	      (greater << 5) |
+	      (greatereq << 4) |
+	      (sign << 3) |
+	      (overflow << 2) |
+	      (carry << 1) |
+	      (zero << 0));
+	  }
+      }
+      break;
+
+    case FT32_PAT_LDK:
+      cpu->state.regs[r_d] = k20;
+      break;
+
+    case FT32_PAT_LPM:
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, pa << 2);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_LPMI:
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, cpu->state.regs[r_1] + k8);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_STA:
+      cpu_mem_write (sd, dw, aa, cpu->state.regs[r_d]);
+      break;
+
+    case FT32_PAT_STI:
+      cpu_mem_write (sd, dw, cpu->state.regs[r_d] + k8, cpu->state.regs[r_1]);
+      break;
+
+    case FT32_PAT_LDA:
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, aa);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_LDI:
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_EXA:
+      {
+	uint32_t tmp;
+	tmp = cpu_mem_read (sd, dw, aa);
+	cpu_mem_write (sd, dw, aa, cpu->state.regs[r_d]);
+	cpu->state.regs[r_d] = tmp;
+	cpu->state.cycles += 1;
+      }
+      break;
+
+    case FT32_PAT_EXI:
+      {
+	uint32_t tmp;
+	tmp = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+	cpu_mem_write (sd, dw, cpu->state.regs[r_1] + k8, cpu->state.regs[r_d]);
+	cpu->state.regs[r_d] = tmp;
+	cpu->state.cycles += 1;
+      }
+      break;
+
+    case FT32_PAT_PUSH:
+      ft32_push (sd, r_1v);
+      break;
+
+    case FT32_PAT_LINK:
+      ft32_push (sd, cpu->state.regs[r_d]);
+      cpu->state.regs[r_d] = cpu->state.regs[FT32_HARD_SP];
+      cpu->state.regs[FT32_HARD_SP] -= k16;
+      cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+      break;
+
+    case FT32_PAT_UNLINK:
+      cpu->state.regs[FT32_HARD_SP] = cpu->state.regs[r_d];
+      cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+      cpu->state.regs[r_d] = ft32_pop (sd);
+      break;
+
+    case FT32_PAT_POP:
+      cpu->state.cycles += 1;
+      cpu->state.regs[r_d] = ft32_pop (sd);
+      break;
+
+    case FT32_PAT_RETURN:
+      cpu->state.pc = ft32_pop (sd);
+      break;
+
+    case FT32_PAT_FFUOP:
+      switch (al)
+	{
+	case 0x0:
+	  cpu->state.regs[r_d] = r_1v / rimmv;
+	  break;
+	case 0x1:
+	  cpu->state.regs[r_d] = r_1v % rimmv;
+	  break;
+	case 0x2:
+	  cpu->state.regs[r_d] = ft32sdiv (r_1v, rimmv);
+	  break;
+	case 0x3:
+	  cpu->state.regs[r_d] = ft32smod (r_1v, rimmv);
+	  break;
+
+	case 0x4:
+	  {
+	    /* strcmp instruction.  */
+	    uint32_t a = r_1v;
+	    uint32_t b = rimmv;
+	    uint32_t i = 0;
+	    while ((GET_BYTE (a + i) != 0) &&
+		   (GET_BYTE (a + i) == GET_BYTE (b + i)))
+	      i++;
+	    cpu->state.regs[r_d] = GET_BYTE (a + i) - GET_BYTE (b + i);
+	  }
+	  break;
+
+	case 0x5:
+	  {
+	    /* memcpy instruction.  */
+	    uint32_t src = r_1v;
+	    uint32_t dst = cpu->state.regs[r_d];
+	    uint32_t i;
+	    for (i = 0; i < rimmv; i++)
+	      PUT_BYTE (dst + i, GET_BYTE (src + i));
+	  }
+	  break;
+	case 0x6:
+	  {
+	    /* strlen instruction.  */
+	    uint32_t src = r_1v;
+	    uint32_t i;
+	    for (i = 0; GET_BYTE (src + i) != 0; i++)
+	      ;
+	    cpu->state.regs[r_d] = i;
+	  }
+	  break;
+	case 0x7:
+	  {
+	    /* memset instruction.  */
+	    uint32_t dst = cpu->state.regs[r_d];
+	    uint32_t i;
+	    for (i = 0; i < rimmv; i++)
+	      PUT_BYTE (dst + i, r_1v);
+	  }
+	  break;
+	case 0x8:
+	  cpu->state.regs[r_d] = r_1v * rimmv;
+	  break;
+	case 0x9:
+	  cpu->state.regs[r_d] = ((uint64_t)r_1v * (uint64_t)rimmv) >> 32;
+	  break;
+	case 0xa:
+	  {
+	    /* stpcpy instruction.  */
+	    uint32_t src = r_1v;
+	    uint32_t dst = cpu->state.regs[r_d];
+	    uint32_t i;
+	    for (i = 0; GET_BYTE (src + i) != 0; i++)
+	      PUT_BYTE (dst + i, GET_BYTE (src + i));
+	    PUT_BYTE (dst + i, 0);
+	    cpu->state.regs[r_d] = dst + i;
+	  }
+	  break;
+	case 0xe:
+	  {
+	    /* streamout instruction.  */
+	    uint32_t i;
+	    uint32_t src = cpu->state.regs[r_1];
+	    for (i = 0; i < rimmv; i += (1 << dw))
+	      {
+		cpu_mem_write (sd,
+			       dw,
+			       cpu->state.regs[r_d],
+			       cpu_mem_read (sd, dw, src));
+		src += (1 << dw);
+	      }
+	  }
+	  break;
+	default:
+	  sim_io_eprintf (sd, "Unhandled ffu %#x at %08x\n", al, insnpc);
+	  ILLEGAL ();
+	}
+      break;
+
+    default:
+      sim_io_eprintf (sd, "Unhandled pattern %d at %08x\n", upper, insnpc);
+      ILLEGAL ();
+    }
+  cpu->state.num_i++;
+
+escape:
+  ;
+}
+
+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 (sd);
+      if (sim_events_tick (sd))
+	sim_events_process (sd);
+    }
+}
+
+int
+sim_write (SIM_DESC sd,
+	   SIM_ADDR addr,
+	   const unsigned char *buffer,
+	   int size)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  return sim_core_write_buffer (sd, cpu, write_map, buffer, addr, size);
+}
+
+int
+sim_read (SIM_DESC sd,
+	  SIM_ADDR addr,
+	  unsigned char *buffer,
+	  int size)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  return sim_core_read_buffer (sd, cpu, read_map, buffer, addr, size);
+}
+
+static uint32_t *
+ft32_lookup_register (SIM_CPU *cpu, int nr)
+{
+  /* Handle the register number translation here.
+   * Sim registers are 0-31.
+   * Other tools (gcc, gdb) use:
+   * 0 - fp
+   * 1 - sp
+   * 2 - r0
+   * 31 - cc
+   */
+
+  if ((nr < 0) || (nr > 32))
+    {
+      sim_io_eprintf (CPU_STATE (cpu), "unknown register %i\n", nr);
+      abort ();
+    }
+
+  switch (nr)
+    {
+    case FT32_FP_REGNUM:
+      return &cpu->state.regs[FT32_HARD_FP];
+    case FT32_SP_REGNUM:
+      return &cpu->state.regs[FT32_HARD_SP];
+    case FT32_CC_REGNUM:
+      return &cpu->state.regs[FT32_HARD_CC];
+    case FT32_PC_REGNUM:
+      return &cpu->state.pc;
+    default:
+      return &cpu->state.regs[nr - 2];
+    }
+}
+
+static int
+ft32_reg_store (SIM_CPU *cpu,
+		int rn,
+		unsigned char *memory,
+		int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	*ft32_lookup_register (cpu, rn) = ft32_extract_unsigned_integer (memory, 4);
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+static int
+ft32_reg_fetch (SIM_CPU *cpu,
+		int rn,
+		unsigned char *memory,
+		int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	ft32_store_unsigned_integer (memory, 4, *ft32_lookup_register (cpu, rn));
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+static sim_cia
+ft32_pc_get (SIM_CPU *cpu)
+{
+  return 32;
+}
+
+static void
+ft32_pc_set (SIM_CPU *cpu, sim_cia newpc)
+{
+  cpu->state.pc = newpc;
+}
+
+/* Cover function of sim_state_free to free the cpu buffers as well.  */
+
+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 *cb,
+	  struct bfd *abfd,
+	  char **argv)
+{
+  char c;
+  size_t i;
+  SIM_DESC sd = sim_state_alloc (kind, cb);
+
+  /* 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;
+    }
+
+  /* getopt will print the error message so we just have to exit if this fails.
+     FIXME: Hmmm...  in the case of gdb we need getopt to call
+     print_filtered.  */
+  if (sim_parse_args (sd, argv) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* 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_command (sd, "memory region 0x00000000,0x40000");
+      sim_do_command (sd, "memory region 0x800000,0x10000");
+    }
+
+  /* Check for/establish the 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;
+    }
+
+  /* Configure/verify the target byte order and other runtime
+     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);
+
+      CPU_REG_FETCH (cpu) = ft32_reg_fetch;
+      CPU_REG_STORE (cpu) = ft32_reg_store;
+      CPU_PC_FETCH (cpu) = ft32_pc_get;
+      CPU_PC_STORE (cpu) = ft32_pc_set;
+    }
+
+  return sd;
+}
+
+void
+sim_close (SIM_DESC sd, int quitting)
+{
+  sim_module_uninstall (sd);
+}
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd,
+		     struct bfd *abfd,
+		     char **argv,
+		     char **env)
+{
+  uint32_t addr;
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  /* Set the PC.  */
+  if (abfd != NULL)
+    addr = bfd_get_start_address (abfd);
+  else
+    addr = 0;
+
+  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+    {
+      freeargv (STATE_PROG_ARGV (sd));
+      STATE_PROG_ARGV (sd) = dupargv (argv);
+    }
+  cpu->state.regs[FT32_HARD_SP] = addr;
+  cpu->state.num_i = 0;
+  cpu->state.cycles = 0;
+  cpu->state.next_tick_cycle = 100000;
+
+  return SIM_RC_OK;
+}
diff --git a/sim/ft32/sim-main.h b/sim/ft32/sim-main.h
new file mode 100644
index 0000000..369dc52
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,58 @@ 
+/* Simulator for FTDI FT32 processor.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+   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"
+
+typedef address_word sim_cia;
+
+#include "sim-base.h"
+#include "bfd.h"
+
+typedef struct _sim_cpu SIM_CPU;
+
+#include "ft32-sim.h"
+
+struct _sim_cpu {
+
+  /* The following are internal simulator state variables: */
+#define CIA_GET(CPU) ((CPU)->state.pc + 0)
+#define CIA_SET(CPU,CIA) ((CPU)->state.pc = (CIA))
+
+  struct ft32_cpu_state state;
+
+  sim_cpu_base base;
+};
+
+struct sim_state {
+
+  sim_cpu *cpu[MAX_NR_PROCESSORS];
+#if (WITH_SMP)
+#define STATE_CPU(sd,n) ((sd)->cpu[n])
+#else
+#define STATE_CPU(sd,n) ((sd)->cpu[0])
+#endif
+
+  sim_state_base base;
+};
+
+#endif
diff --git a/sim/testsuite/sim/ft32/ChangeLog b/sim/testsuite/sim/ft32/ChangeLog
new file mode 100644
index 0000000..592fdd5
--- /dev/null
+++ b/sim/testsuite/sim/ft32/ChangeLog
@@ -0,0 +1,3 @@ 
+2015-02-23  James Bowman  <james.bowman@ftdichip.com>
+
+	* basic.s, allinsn.exp, testutils.inc: New files.
diff --git a/sim/testsuite/sim/ft32/allinsn.exp b/sim/testsuite/sim/ft32/allinsn.exp
new file mode 100644
index 0000000..730b422
--- /dev/null
+++ b/sim/testsuite/sim/ft32/allinsn.exp
@@ -0,0 +1,15 @@ 
+# ft32 simulator testsuite
+
+if [istarget ft32-*] {
+    # all machines
+    set all_machs "ft32"
+
+    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/ft32/basic.s b/sim/testsuite/sim/ft32/basic.s
new file mode 100644
index 0000000..5bba2a5
--- /dev/null
+++ b/sim/testsuite/sim/ft32/basic.s
@@ -0,0 +1,862 @@ 
+# check that basic insns work.
+# mach: ft32
+
+.include "testutils.inc"
+
+	start
+
+	ldk     $r4,10
+	add     $r4,$r4,23
+	EXPECT  $r4,33
+
+# lda, sta
+	.data
+tmp:    .long     0
+	.text
+
+	xor.l     $r0,$r0,$r0
+	EXPECT    $r0,0x00000000
+	xor.l     $r0,$r0,$r0
+	add.l     $r0,$r0,1
+	EXPECT    $r0,0x00000001
+ 
+	ldk.l     $r0,0x4567
+	EXPECT    $r0,0x00004567
+
+	lpm.l     $r0,k_12345678
+	EXPECT    $r0,0x12345678
+
+	sta.l     tmp,$r0
+	lda.l     $r1,tmp
+	EXPECT    $r1,0x12345678
+
+	lda.b     $r1,tmp
+	EXPECT    $r1,0x00000078
+
+	lda.b     $r1,tmp+1
+	EXPECT    $r1,0x00000056
+
+	lda.b     $r1,tmp+2
+	EXPECT    $r1,0x00000034
+
+	lda.b     $r1,tmp+3
+	EXPECT    $r1,0x00000012
+
+	sta.b     tmp+1,$r0
+	lda.l     $r1,tmp+0
+	EXPECT    $r1,0x12347878
+
+# immediate
+	ldk.l     $r1,12
+	add.l     $r1,$r1,4
+	EXPECT    $r1,0x00000010
+	add.l     $r1,$r1,0x1ff
+	EXPECT    $r1,0x0000020f
+	add.l     $r1,$r1,-0x200
+	EXPECT    $r1,0x0000000f
+      
+# addk
+	xor.l     $r1,$r0,$r0
+	add.l     $r2,$r1,127
+	EXPECT    $r2,0x0000007f
+
+	add.l     $r2,$r2,127
+	EXPECT    $r2,0x000000fe
+
+	add.l     $r2,$r2,-127
+	EXPECT    $r2,0x0000007f
+
+	add.l     $r2,$r2,-128
+	EXPECT    $r2,0xffffffff
+
+	add.l     $r2,$r2,1
+	EXPECT    $r2,0x00000000
+
+# mul
+	ldk.l     $r1,100
+	ldk.l     $r2,77
+	mul.l     $r3,$r1,$r2
+	EXPECT    $r3,0x00001e14
+
+	# 0x12345678 ** 2 = 0x14b66dc1df4d840L
+	mul.l     $r3,$r0,$r0
+	EXPECT    $r3,0x1df4d840
+	muluh.l   $r3,$r0,$r0
+	EXPECT    $r3,0x014b66dc
+
+# push and pop
+	push.l    $r0
+	EXPECT    $sp,0x0000fffc
+	ldi.l     $r3,$sp,0
+	EXPECT    $r3,0x12345678
+
+	pop.l     $r4
+	EXPECT    $sp,0x00000000
+	EXPECT    $r4,0x12345678
+
+	ldk.l     $r1,0x1111
+	push.l    $r1
+	ldk.l     $r1,0x2222
+	push.l    $r1
+	ldk.l     $r1,0x3333
+	push.l    $r1
+	ldk.l     $r1,0x4444
+	push.l    $r1
+	EXPECT    $sp,0x0000fff0
+	pop.l     $r1
+	EXPECT    $r1,0x00004444
+	pop.l     $r1
+	EXPECT    $r1,0x00003333
+	pop.l     $r1
+	EXPECT    $r1,0x00002222
+	pop.l     $r1
+	EXPECT    $r1,0x00001111
+
+# push and pop with $sp changes
+	ldk.l     $r1,0xa111
+	push.l    $r1
+	sub.l     $sp,$sp,4
+	ldk.l     $r1,0xa222
+	push.l    $r1
+	add.l     $sp,$sp,-36
+	add.l     $sp,$sp,36
+	pop.l     $r1
+	EXPECT    $r1,0x0000a222
+	add.l     $sp,$sp,4
+	pop.l     $r1
+	EXPECT    $r1,0x0000a111
+
+# sti
+	ldk.l     $r2,80
+	EXPECT    $r2,0x00000050
+	sti.l     $r2,0,$r0
+	lda.l     $r1,80
+	EXPECT    $r1,0x12345678
+
+	ldk.l     $r3,0xF0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x123456f0
+
+	add.l     $r2,$r2,1
+	sti.l     $r2,0,$r0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x1234f078
+
+	add.l     $r2,$r2,1
+	sti.l     $r2,0,$r0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x12f05678
+
+	add.l     $r2,$r2,1
+	sti.l     $r2,0,$r0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0xf0345678
+
+	ldk.l     $r2,80
+	sti.l     $r2,0,$r0
+	ldk.s     $r3,0xbeef
+	sti.s     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x1234beef
+	add.l     $r2,$r2,2
+	sti.s     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0xbeefbeef
+
+# lpmi
+
+	ldk.l     $r1,k_12345678
+	lpmi.l    $r2,$r1,0
+	EXPECT    $r2,0x12345678
+
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000078
+
+	add.l     $r1,$r1,1
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000056
+
+	add.l     $r1,$r1,1
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000034
+
+	add.l     $r1,$r1,1
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000012
+
+	lpmi.l    $r2,$r1,4
+	EXPECT    $r2,0xabcdef01
+
+	lpmi.l    $r2,$r1,-4
+	EXPECT    $r2,0x10111213
+
+	lpmi.b    $r2,$r1,-4
+	EXPECT    $r2,0x00000010
+
+	ldk.l     $r1,k_12345678
+	lpmi.s    $r2,$r1,0
+	EXPECT    $r2,0x00005678
+	lpmi.s    $r2,$r1,2
+	EXPECT    $r2,0x00001234
+	lpmi.b    $r2,$r1,6
+	EXPECT    $r2,0x000000cd
+	lpmi.b    $r2,$r1,7
+	EXPECT    $r2,0x000000ab
+	lpmi.b    $r2,$r1,-1
+	EXPECT    $r2,0x00000010
+	lpmi.s    $r2,$r1,-2
+	EXPECT    $r2,0x00001011
+
+	ldk.l     $r1,k_12345678-127
+	lpmi.b    $r2,$r1,127
+	EXPECT    $r2,0x00000078
+
+	ldk.l     $r1,k_12345678+128
+	lpmi.b    $r2,$r1,-128
+	EXPECT    $r2,0x00000078
+
+# shifts
+
+	lpm.l     $r0,k_12345678
+	ldk.l     $r2,4
+	ashl.l    $r1,$r0,$r2
+	EXPECT    $r1,0x23456780
+	lshr.l    $r1,$r0,$r2
+	EXPECT    $r1,0x01234567
+	ashr.l    $r1,$r0,$r2
+	EXPECT    $r1,0x01234567
+
+	lpm.l     $r0,k_abcdef01
+	ashl.l    $r1,$r0,$r2
+	EXPECT    $r1,0xbcdef010
+	lshr.l    $r1,$r0,$r2
+	EXPECT    $r1,0x0abcdef0
+	ashr.l    $r1,$r0,$r2
+	EXPECT    $r1,0xfabcdef0
+
+# rotate right
+      
+	lpm.l     $r0,k_12345678
+	ror.l     $r1,$r0,0
+	EXPECT    $r1,0x12345678
+	ror.l     $r1,$r0,12
+	EXPECT    $r1,0x67812345
+	ror.l     $r1,$r0,-4
+	EXPECT    $r1,0x23456781
+
+# jmpx
+	ldk       $r28,0xaaaaa
+	jmpx      0,$r28,1,failcase
+	jmpx      1,$r28,0,failcase
+	jmpx      2,$r28,1,failcase
+	jmpx      3,$r28,0,failcase
+	jmpx      4,$r28,1,failcase
+	jmpx      5,$r28,0,failcase
+	jmpx      6,$r28,1,failcase
+	jmpx      7,$r28,0,failcase
+	jmpx      8,$r28,1,failcase
+	jmpx      9,$r28,0,failcase
+	jmpx      10,$r28,1,failcase
+	jmpx      11,$r28,0,failcase
+	jmpx      12,$r28,1,failcase
+	jmpx      13,$r28,0,failcase
+	jmpx      14,$r28,1,failcase
+	jmpx      15,$r28,0,failcase
+	jmpx      16,$r28,1,failcase
+	jmpx      17,$r28,0,failcase
+	jmpx      18,$r28,1,failcase
+	jmpx      19,$r28,0,failcase
+
+	move      $r29,$r28
+	ldk       $r28,0
+	jmpx      0,$r29,1,failcase
+	jmpx      1,$r29,0,failcase
+	jmpx      2,$r29,1,failcase
+	jmpx      3,$r29,0,failcase
+	jmpx      4,$r29,1,failcase
+	jmpx      5,$r29,0,failcase
+	jmpx      6,$r29,1,failcase
+	jmpx      7,$r29,0,failcase
+	jmpx      8,$r29,1,failcase
+	jmpx      9,$r29,0,failcase
+	jmpx      10,$r29,1,failcase
+	jmpx      11,$r29,0,failcase
+	jmpx      12,$r29,1,failcase
+	jmpx      13,$r29,0,failcase
+	jmpx      14,$r29,1,failcase
+	jmpx      15,$r29,0,failcase
+	jmpx      16,$r29,1,failcase
+	jmpx      17,$r29,0,failcase
+	jmpx      18,$r29,1,failcase
+	jmpx      19,$r29,0,failcase
+
+	move      $r30,$r29
+	ldk       $r29,0
+	jmpx      0,$r30,1,failcase
+	jmpx      1,$r30,0,failcase
+	jmpx      2,$r30,1,failcase
+	jmpx      3,$r30,0,failcase
+	jmpx      4,$r30,1,failcase
+	jmpx      5,$r30,0,failcase
+	jmpx      6,$r30,1,failcase
+	jmpx      7,$r30,0,failcase
+	jmpx      8,$r30,1,failcase
+	jmpx      9,$r30,0,failcase
+	jmpx      10,$r30,1,failcase
+	jmpx      11,$r30,0,failcase
+	jmpx      12,$r30,1,failcase
+	jmpx      13,$r30,0,failcase
+	jmpx      14,$r30,1,failcase
+	jmpx      15,$r30,0,failcase
+	jmpx      16,$r30,1,failcase
+	jmpx      17,$r30,0,failcase
+	jmpx      18,$r30,1,failcase
+	jmpx      19,$r30,0,failcase
+
+# callx
+	ldk       $r30,0xaaaaa
+	callx     0,$r30,0,skip1
+	jmp       failcase
+	callx     1,$r30,1,skip1
+	jmp       failcase
+	callx     2,$r30,0,skip1
+	jmp       failcase
+	callx     3,$r30,1,skip1
+	jmp       failcase
+
+	callx     0,$r30,1,skip1
+	ldk       $r30,0x123
+	EXPECT    $r30,0x123
+
+#define BIT(N,M)  ((((N) & 15) << 5) | (M))
+# bextu
+	bextu.l   $r1,$r0,(0<<5)|0
+	EXPECT    $r1,0x00005678
+	bextu.l   $r1,$r0,(4<<5)|0
+	EXPECT    $r1,0x00000008
+	bextu.l   $r1,$r0,(4<<5)|4
+	EXPECT    $r1,0x00000007
+	bextu.l   $r1,$r0,(4<<5)|28
+	EXPECT    $r1,0x00000001
+	bextu.l   $r1,$r0,(8<<5)|16
+	EXPECT    $r1,0x00000034
+	ldk.l     $r2,-1
+	bextu.l   $r1,$r2,(6<<5)|(3)
+	EXPECT    $r1,0x0000003f
+
+# bexts
+	bexts.l   $r1,$r0,(8<<5)|0
+	EXPECT    $r1,0x00000078
+	bexts.l   $r1,$r0,(0<<5)|16
+	EXPECT    $r1,0x00001234
+	bexts.l   $r1,$r0,(4<<5)|0
+	EXPECT    $r1,0xfffffff8
+	# extract the '5' digit in widths 4-1
+	bexts.l   $r1,$r0,(4<<5)|12
+	EXPECT    $r1,0x00000005
+	bexts.l   $r1,$r0,(3<<5)|12
+	EXPECT    $r1,0xfffffffd
+	bexts.l   $r1,$r0,(2<<5)|12
+	EXPECT    $r1,0x00000001
+	bexts.l   $r1,$r0,(1<<5)|12
+	EXPECT    $r1,0xffffffff
+
+# btst
+	# low four bits should be 0,0,0,1
+	btst.l    $r0,(1<<5)|0
+	jmpc      nz,failcase
+	btst.l    $r0,(1<<5)|1
+	jmpc      nz,failcase
+	btst.l    $r0,(1<<5)|2
+	jmpc      nz,failcase
+	btst.l    $r0,(1<<5)|3
+	jmpc      z,failcase
+
+	# the 6 bit field starting at position 24 is positive
+	btst.l    $r0,(6<<5)|24
+	jmpc      s,failcase
+	# the 5 bit field starting at position 24 is negative
+	btst.l    $r0,(5<<5)|24
+	jmpc      ns,failcase
+
+	EXPECT    $r0,0x12345678
+
+# bins
+	bins.l    $r1,$r0,(8 << 5) | (0)
+	EXPECT    $r1,0x12345600
+
+	bins.l    $r1,$r0,(0 << 5) | (8)
+	EXPECT    $r1,0x12000078
+
+	ldk.l     $r1,(0xff << 10) | (8 << 5) | (8)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x1234ff78
+
+	call      litr1
+	.long     (0x8dd1 << 10) | (0 << 5) | (0)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x12348dd1
+
+	call      litr1
+	.long     (0x8dd1 << 10) | (0 << 5) | (16)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x8dd15678
+
+	ldk.l     $r1,(0xde << 10) | (8 << 5) | (0)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x123456de
+
+# ldl
+	ldk.l     $r0,0
+	ldl.l     $r3,$r0,0
+	EXPECT    $r3,0x00000000
+	ldk.l     $r0,-1
+	ldl.l     $r3,$r0,-1
+	EXPECT    $r3,0xffffffff
+	ldk.l     $r0,(0x12345678 >> 10)
+	ldl.l     $r3,$r0,(0x12345678 & 0x3ff)
+	EXPECT    $r3,0x12345678
+	ldk.l     $r0,(0xe2345678 >> 10)
+	ldl.l     $r3,$r0,(0xe2345678 & 0x3ff)
+	EXPECT    $r3,0xe2345678
+
+# flip
+	ldk.l     $r0,0x0000001
+	flip.l    $r1,$r0,0
+	EXPECT    $r1,0x00000001
+
+	lpm.l     $r0,k_12345678
+	flip.l    $r1,$r0,0
+	EXPECT    $r1,0x12345678
+	flip.l    $r1,$r0,24
+	EXPECT    $r1,0x78563412
+	flip.l    $r1,$r0,31
+	EXPECT    $r1,0x1e6a2c48
+
+# stack push pop
+
+	EXPECT    $sp,0x00000000
+	ldk.l     $r6,0x6666
+	push.l    $r6
+	or.l      $r0,$r0,$r0      # xxx
+	EXPECT    $sp,0x0000fffc
+	ldi.l     $r1,$sp,0
+	EXPECT    $r1,0x00006666
+	pop.l     $r1
+	EXPECT    $r1,0x00006666
+	EXPECT    $sp,0x00000000
+
+# call/return
+	call      fowia
+	push.l    $r1
+	call      fowia
+	pop.l     $r2
+	sub.l     $r1,$r1,$r2
+	EXPECT    $r1,0x00000008
+
+# add,carry
+
+	ldk.l     $r0,0
+	ldk.l     $r1,0
+	call      add64
+	EXPECT    $r1,0x00000000
+	EXPECT    $r0,0x00000000
+
+	lpm.l     $r0,k_abcdef01
+	lpm.l     $r1,k_abcdef01
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0x579bde02
+
+	ldk.l     $r0,4
+	ldk.l     $r1,-5
+	call      add64
+	EXPECT    $r1,0x00000000
+	EXPECT    $r0,0xffffffff
+
+	ldk.l     $r0,5
+	ldk.l     $r1,-5
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0x00000000
+
+	lpm.l     $r0,k_12345678
+	ldk.l     $r1,-1
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0x12345677
+
+	ldk.l     $r0,-1
+	ldk.l     $r1,-1
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0xfffffffe
+
+# inline literal
+	call      lit
+	.long     0xdecafbad
+	EXPECT    $r0,0xdecafbad
+
+	ldk.l     $r1,0xee
+	call      lit
+	ldk.l     $r1,0xfe
+	EXPECT    $r1,0x000000ee
+
+	call      lit
+	.long     0x01020304
+	EXPECT    $r0,0x01020304
+
+	call      lit
+	.long     lit
+	calli     $r0
+	.long     0xffaa55aa
+	EXPECT    $r0,0xffaa55aa
+
+# comparisons
+	ldk.l     $r0,-100
+	ldk.l     $r1,100
+	cmp.l     $r0,$r1
+
+	ldk.l     $r2,0
+	jmpc      lt,.c1
+	ldk.l     $r2,1
+.c1:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      gt,.c2
+	ldk.l     $r2,1
+.c2:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      a,.c3
+	ldk.l     $r2,1
+.c3:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      b,.c4
+	ldk.l     $r2,1
+.c4:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      be,.c5
+	ldk.l     $r2,1
+.c5:
+	EXPECT    $r2,0x00000001
+
+# 8-bit comparisons
+	ldk.l     $r0,0x8fe
+	ldk.l     $r1,0x708
+	cmp.b     $r0,$r1
+
+	ldk.l     $r2,0
+	jmpc      lt,.8c1
+	ldk.l     $r2,1
+.8c1:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      gt,.8c2
+	ldk.l     $r2,1
+.8c2:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      a,.8c3
+	ldk.l     $r2,1
+.8c3:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      b,.8c4
+	ldk.l     $r2,1
+.8c4:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      be,.8c5
+	ldk.l     $r2,1
+.8c5:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r0,0x8aa
+	ldk.l     $r1,0x7aa
+	cmp.b     $r0,$r1
+
+	ldk.l     $r2,0
+	jmpc      z,.8c6
+	ldk.l     $r2,1
+.8c6:
+	EXPECT    $r2,0x00000000
+
+	ldk.b     $r0,1
+	ldk.b     $r2,0xe0
+	cmp.b     $r2,0x1c0
+	jmpc      a,.8c7
+	ldk.b     $r0,0
+.8c7:
+	EXPECT    $r0,0x00000001
+
+# conditional call
+	cmp.l     $r0,$r0
+	callc     z,lit
+	.long     0xccddeeff
+	callc     nz,zr0
+	EXPECT    $r0,0xccddeeff
+
+# modify return address
+	ldk.l     $r0,0x66
+	call      skip1
+	ldk.l     $r0,0xAA
+	EXPECT    $r0,0x00000066
+
+	ldk.l     $r0,0x77
+	call      skip2
+	ldk.l     $r0,0xBB
+	EXPECT    $r0,0x00000077
+
+# simple recursive function
+	ldk.l     $r0,1
+	call      factorial
+	EXPECT    $r0,0x00000001
+	ldk.l     $r0,2
+	call      factorial
+	EXPECT    $r0,0x00000002
+	ldk.l     $r0,3
+	call      factorial
+	EXPECT    $r0,0x00000006
+	ldk.l     $r0,4
+	call      factorial
+	EXPECT    $r0,0x00000018
+	ldk.l     $r0,5
+	call      factorial
+	EXPECT    $r0,0x00000078
+	ldk.l     $r0,6
+	call      factorial
+	EXPECT    $r0,0x000002d0
+	ldk.l     $r0,7
+	call      factorial
+	EXPECT    $r0,0x000013b0
+	ldk.l     $r0,12
+	call      factorial
+	EXPECT    $r0,0x1c8cfc00
+
+# read sp after a call
+	call      nullfunc
+	EXPECT    $sp,0x00000000 
+
+# CALLI->RETURN
+	ldk.l     $r4,nullfunc
+	calli     $r4
+	EXPECT    $sp,0x00000000 
+
+# Link/unlink
+	ldk.l     $r14,0x17566
+
+	link      $r14,48
+	EXPECT    $r14,0x0000fffc
+	sub.l     $sp,$sp,200
+	unlink    $r14
+	EXPECT    $r14,0x00017566
+
+# LINK->UNLINK
+	link      $r14,48
+	unlink    $r14
+	EXPECT    $r14,0x00017566
+
+# LINK->JUMPI
+	ldk.l     $r3,.here
+	link      $r14,48
+	jmpi      $r3
+	jmp       failcase
+.here:
+	unlink    $r14
+	EXPECT    $r14,0x00017566
+
+# LINK->RETURN
+# (This is a nonsense combination, but can still exericse it by
+# using a negative parameter for the link.  "link $r14,-4" leaves
+# $sp exactly unchanged.)
+	ldk.l     $r0,.returnhere
+	push.l    $r0
+	link      $r14,0xfffc
+	return
+.returnhere:
+	EXPECT    $sp,0x00000000 
+
+# LPMI->CALLI
+	ldk.l     $r0,k_abcdef01
+	ldk.l     $r1,increment
+	lpmi.l    $r0,$r0,0
+	calli     $r1
+	EXPECT    $r0,0xabcdef02
+
+# STRLen
+	lpm.l     $r4,str3
+	sta.l     tmp,$r4
+	ldk.l     $r0,tmp
+	strlen.b  $r1,$r0
+	EXPECT    $r1,0x00000003
+	strlen.s  $r1,$r0
+	EXPECT    $r1,0x00000003
+	strlen.l  $r1,$r0
+	EXPECT    $r1,0x00000003
+
+	ldk.l     $r4,0
+	sta.b     4,$r4
+	strlen.l  $r1,$r0
+	EXPECT    $r1,0x00000000
+
+	ldk.l     $r4,-1
+	sta.l     4,$r4
+	lpm.l     $r4,str3
+	sta.l     8,$r4
+	strlen.l  $r1,$r0
+	EXPECT    $r1,0x00000007
+
+# MEMSet
+	ldk.l     $r0,4
+	ldk.l     $r1,0xaa
+	memset.s  $r0,$r1,8
+	ldk.l     $r1,0x55
+	memset.b  $r0,$r1,5
+	lda.l     $r0,4
+	EXPECT    $r0,0x55555555
+	lda.l     $r0,8
+	EXPECT    $r0,0xaaaaaa55
+
+# first cycle after mispredict
+	ldk.l     $r0,3
+	cmp.l     $r0,$r0
+	jmpc      nz,failcase
+	add.l     $r0,$r0,7
+	EXPECT    $r0,0x0000000a 
+	jmpc      nz,failcase
+	push.l    $r0
+	EXPECT    $sp,0x0000fffc 
+	pop.l     $r0
+
+# $sp access after stall
+	lpm.l     $r13,0
+	push.l    $r0
+	EXPECT    $sp,0x0000fffc
+	pop.l     $r0
+
+	push.l    $r0
+	add.l     $sp,$sp,-484
+	EXPECT    $sp,0x0000fe18
+	EXPECT    $sp,0x0000fe18
+	EXPECT    $sp,0x0000fe18
+	add.l     $sp,$sp,484
+	EXPECT    $sp,0x0000fffc
+	pop.l     $r0
+
+# atomic exchange
+	lpm.l     $r0,k_12345678
+	lpm.l     $r1,k_abcdef01
+	sta.l     100,$r1
+	exa.l     $r0,100
+	EXPECT    $r0,0xabcdef01
+	lda.l     $r0,100
+	EXPECT    $r0,0x12345678
+
+	lpm.l     $r0,k_12345678
+	lpm.l     $r1,k_abcdef01
+	sta.l     144,$r1
+	ldk.l     $r7,20
+	exi.l     $r0,$r7,124
+	EXPECT    $r0,0xabcdef01
+	lda.l     $r0,144
+	EXPECT    $r0,0x12345678
+
+	lpm.l     $r0,k_12345678
+	lpm.l     $r1,k_abcdef01
+	push      $r1
+	exi.l     $r0,$sp,0
+	EXPECT    $r0,0xabcdef01
+	pop.l     $r0
+	EXPECT    $r0,0x12345678
+
+# final stack check
+	EXPECT    $sp,0x00000000
+
+	PASS
+
+# --------------------------------------------------
+
+skip1:          # skip the instruction after the call
+	pop.l     $r1
+	add.l     $r1,$r1,4
+	push.l    $r1
+	return
+
+skipparent:     # skip the instruction after the caller's call
+	ldi.l     $r1,$sp,4
+	add.l     $r1,$r1,4
+	sti.l     $sp,4,$r1
+	return
+skip2:
+	call      skipparent
+	return
+
+add64:
+	addcc.l   $r0,$r1
+	add.l     $r0,$r0,$r1
+	ldk.l     $r1,0
+	jmpc      nc,.done
+	ldk.l     $r1,1
+.done:
+	return
+
+fowia:  # find out where I'm at
+	ldi.l     $r1,$sp,0
+	return
+
+lit:    # load literal to $r0
+	pop.l     $r14
+	lpmi.l    $r0,$r14,0
+	add.l     $r14,$r14,4
+	jmpi      $r14
+zr0:
+	ldk.l     $r0,0
+	return
+litr1:
+	ldi.l     $r1,$sp,0
+	add.l     $r1,$r1,4
+	sti.l     $sp,0,$r1
+	lpmi.l    $r1,$r1,-4
+	return
+
+factorial:
+	ldk.l     $r1,1
+	cmp.l     $r0,$r1
+	jmpc      z,.factdone
+	push.l    $r0
+	add.l     $r0,$r0,-1
+	call      factorial
+	pop.l     $r1
+	mul.l     $r0,$r0,$r1
+.factdone:
+	return
+
+nullfunc:
+	return
+
+increment:
+	add.l     $r0,$r0,1
+	return
+
+	.long   0x10111213
+k_12345678:
+	.long   0x12345678
+k_abcdef01:
+	.long   0xabcdef01
+str3:
+	.string   "abc"
diff --git a/sim/testsuite/sim/ft32/testutils.inc b/sim/testsuite/sim/ft32/testutils.inc
new file mode 100644
index 0000000..c07811f
--- /dev/null
+++ b/sim/testsuite/sim/ft32/testutils.inc
@@ -0,0 +1,65 @@ 
+
+# Write ch to the standard output
+	.macro outch  ch
+	ldk   $r0,\ch
+	sta   0x10000,$r0
+	.endm
+
+# End the test with return code c
+	.macro  exit c
+	ldk   $r0,\c
+	sta   0x1fffc,$r0
+	.endm
+
+# All assembler tests should start with this macro "start"
+	.macro start
+	.text
+
+	jmp     __start
+	jmp     __start
+	reti
+
+	.data
+ccsave: .long   0
+	.text
+
+# Fiddling to load $cc from the following word in program memory
+loadcc:
+	exi     $r29,$sp,0
+	lpmi    $cc,$r29,0
+	add     $r29,$r29,4
+	exi     $r29,$sp,0
+	return
+
+failcase:
+	outch 'f'
+	outch 'a'
+	outch 'i'
+	outch 'l'
+	outch '\n'
+	exit  1
+
+__start:
+
+	.endm
+
+# At the end of the test, the code should reach this macro PASS
+	.macro PASS
+	outch 'p'
+	outch 'a'
+	outch 's'
+	outch 's'
+	outch '\n'
+	exit  0
+	.endm
+
+# Confirm that reg has value, and fail immediately if not
+# Preserves all registers
+	.macro EXPECT reg,value
+	sta   ccsave,$cc
+	call  loadcc
+	.long \value
+	cmp   \reg,$cc
+	jmpc  nz,failcase
+	lda   $cc,ccsave
+	.endm