public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH, FT32] gdb and sim support
@ 2015-02-03  4:06 James Bowman
  2015-02-10  6:27 ` James Bowman
                   ` (3 more replies)
  0 siblings, 4 replies; 30+ messages in thread
From: James Bowman @ 2015-02-03  4:06 UTC (permalink / raw)
  To: gdb-patches

FT32 is a new high performance 32-bit RISC core developed by FTDI for embedded applications.

Support for FT32 has already been added to binutils. This patch adds gdb and sim support.

Please can someone review it, and if appropriate commit it, as I do not have write access to the tree.

The FSF have acknowledged receipt of FTDI's copyright assignment papers.

Thanks very much. ChangeLog entry:

2014-02-03  James Bowman  <james.bowman@ftdichip.com>

        * gdb/Makefile.in, gdb/configure.tgt: FT32 target added
        * sim/configure.tgt: FT32 target added
        * sim/configure: Regenerated
        * sim/ft32/configure: Regenerated
        * gdb/ft32-tdep.c,h: Support FT32
        * sim/ft32/*: FT32 simulator

--
James Bowman
FTDI Open Source Liaison

From: James Bowman <james.bowman@ftdichip.com>
Date: Mon, 2 Feb 2015 19:43:24 -0800
Subject: [PATCH] FT32 support

---
 gdb/Makefile.in       |    6 +-
 gdb/configure.tgt     |    5 +
 gdb/ft32-tdep.c       | 1007 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/ft32-tdep.h       |   42 ++
 sim/configure.tgt     |    3 +
 sim/ft32/Makefile.in  |   38 ++
 sim/ft32/config.in    |  154 ++++++++
 sim/ft32/configure.ac |   18 +
 sim/ft32/interp.c     | 1014 +++++++++++++++++++++++++++++++++++++++++++++++++
 sim/ft32/sim-main.h   |   61 +++
 sim/ft32/sysdep.h     |   94 +++++
 11 files changed, 2440 insertions(+), 2 deletions(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h
 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/interp.c
 create mode 100644 sim/ft32/sim-main.h
 create mode 100644 sim/ft32/sysdep.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8addef4..d922eb2 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -647,6 +647,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-hpux-tdep.o hppa-linux-tdep.o hppa-tdep.o \
@@ -943,7 +944,7 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h ia64-hpux-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1646,7 +1647,8 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
-	glibc-tdep.c \
+	ft32-tdep.c \
+        glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c hppa-hpux-tdep.c hppa-hpux-nat.c \
 	hppa-linux-tdep.c hppa-linux-nat.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 7fdd34e..26274bd 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -621,6 +621,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..f183661
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,1007 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include <string.h>
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+// #include "record-full.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address value as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) -1 };
+
+struct ft32_frame_cache
+{
+  /* Base address.  */
+  CORE_ADDR base;
+  CORE_ADDR pc;
+  LONGEST framesize;
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  CORE_ADDR saved_sp;
+  int established;  // Has the new frame been LINKed
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+			  CORE_ADDR *pcptr, int *lenptr)
+{
+  static gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+char *ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return  builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+			 const void *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4,
+					 len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+			struct ft32_frame_cache *cache,
+			struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  // printf("(%#lx-%#lx)", start_addr, end_addr);
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+    {
+      return end_addr;
+    }
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH(inst))
+	{
+	  regnum = PUSH_REG(inst);
+	  cache->framesize += 4;
+	  cache->saved_regs[regnum] = cache->framesize;
+	  next_addr += 4;
+	}
+      else
+	break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+        cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  // It is a LINK
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK(inst))
+        {
+          cache->established = 1;
+          for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+            {
+              if (cache->saved_regs[regnum] != REG_UNAVAIL)
+                cache->saved_regs[regnum] += 4;
+            }
+          cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+          cache->saved_regs[FT32_FP_REGNUM] = 0;
+          cache->framesize += LINK_SIZE(inst);
+          next_addr += 4;
+        }
+    }
+  // printf("(Framesize=%lld,cache->saved_regs[FT32_PC_REGNUM]=%ld)\n", cache->framesize, cache->saved_regs[FT32_PC_REGNUM]);
+  return next_addr;
+
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+      else
+	{
+	  /* Can't determine prologue from the symbol table, need to examine
+	     instructions.  */
+	  struct symtab_and_line sal;
+	  struct symbol *sym;
+	  struct ft32_frame_cache cache;
+	  CORE_ADDR plg_end;
+
+	  memset (&cache, 0, sizeof cache);
+
+	  plg_end = ft32_analyze_prologue (func_addr,
+					    func_end, &cache, gdbarch);
+	  /* Found a function.  */
+	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+	  /* Don't use line number debug info for assembly source
+	     files.  */
+	  if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
+	    {
+	      sal = find_pc_line (func_addr, 0);
+	      if (sal.end && sal.end < func_end)
+		{
+		  /* Found a line number, use it as end of
+		     prologue.  */
+		  return sal.end;
+		}
+	    }
+	  /* No useable line symbol.  Use result of prologue parsing
+	     method.  */
+	  return plg_end;
+	}
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return (CORE_ADDR) pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+			   void *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *valtype, struct regcache *regcache,
+		   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+	ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+	ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  cache->base = 0;
+  cache->saved_sp = 0;
+  cache->pc = 0;
+  cache->framesize = 0;
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+        cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						   this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						   this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+    {
+      return frame_unwind_got_memory (this_frame, regnum,
+                                      0x800000 | cache->saved_regs[regnum]);
+    }
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						       this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+#if 0
+/* Read an unsigned integer from the inferior, and adjust
+   endianess.  */
+static ULONGEST
+ft32_process_readu (CORE_ADDR addr, char *buf,
+		     int length, enum bfd_endian byte_order)
+{
+  if (target_read_memory (addr, buf, length))
+    {
+      if (record_debug)
+	printf_unfiltered (_("Process record: error reading memory at "
+			     "addr 0x%s len = %d.\n"),
+			   paddress (target_gdbarch (), addr), length);
+      return -1;
+    }
+
+  return extract_unsigned_integer (buf, length, byte_order);
+}
+#endif
+
+/* Parse the current instruction and record the values of the registers and
+   memory that will be changed in current instruction to "record_arch_list".
+   Return -1 if something wrong.  */
+
+static int
+ft32_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+		      CORE_ADDR addr)
+{
+  return -1;
+#if 0
+  gdb_byte buf[4];
+  uint16_t inst;
+  uint32_t tmpu32;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog, "Process record: ft32_process_record "
+			            "addr = 0x%s\n",
+			paddress (target_gdbarch (), addr));
+
+  inst = (uint16_t) ft32_process_readu (addr, buf, 2, byte_order);
+
+  /* Decode instruction.  */
+  if (inst & (1 << 15))
+    {
+      if (inst & (1 << 14))
+	{
+	  /* This is a Form 3 instruction.  */
+	  int opcode = (inst >> 10 & 0xf);
+
+	  switch (opcode)
+	    {
+	    case 0x00: /* beq */
+	    case 0x01: /* bne */
+	    case 0x02: /* blt */
+	    case 0x03: /* bgt */
+	    case 0x04: /* bltu */
+	    case 0x05: /* bgtu */
+	    case 0x06: /* bge */
+	    case 0x07: /* ble */
+	    case 0x08: /* bgeu */
+	    case 0x09: /* bleu */
+	      /* Do nothing.  */
+	      break;
+	    default:
+	      {
+		/* Do nothing.  */
+		break;
+	      }
+	    }
+	}
+      else
+	{
+	  /* This is a Form 2 instruction.  */
+	  int opcode = (inst >> 12 & 0x3);
+	  switch (opcode)
+	    {
+	    case 0x00: /* inc */
+	    case 0x01: /* dec */
+	    case 0x02: /* gsr */
+	      {
+		int reg = (inst >> 8) & 0xf;
+		if (record_full_arch_list_add_reg (regcache, reg))
+		  return -1;
+	      }
+	      break;
+	    case 0x03: /* ssr */
+	      {
+		/* Do nothing until GDB learns about ft32's special
+		   registers.  */
+	      }
+	      break;
+	    default:
+	      /* Do nothing.  */
+	      break;
+	    }
+	}
+    }
+  else
+    {
+      /* This is a Form 1 instruction.  */
+      int opcode = inst >> 8;
+
+      switch (opcode)
+	{
+	case 0x00: /* nop */
+	  /* Do nothing.  */
+	  break;
+	case 0x01: /* ldi.l (immediate) */
+	case 0x02: /* mov (register-to-register) */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x03: /* jsra */
+	  {
+	    regcache_raw_read (regcache,
+			       FT32_SP_REGNUM, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    if (record_full_arch_list_add_reg (regcache, FT32_FP_REGNUM)
+		|| (record_full_arch_list_add_reg (regcache,
+						   FT32_SP_REGNUM))
+		|| record_full_arch_list_add_mem (tmpu32 - 12, 12))
+	      return -1;
+	  }
+	  break;
+	case 0x04: /* ret */
+	  {
+	    if (record_full_arch_list_add_reg (regcache, FT32_FP_REGNUM)
+		|| (record_full_arch_list_add_reg (regcache,
+						   FT32_SP_REGNUM)))
+	      return -1;
+	  }
+	  break;
+	case 0x05: /* add.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x06: /* push */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    if (record_full_arch_list_add_reg (regcache, reg)
+		|| record_full_arch_list_add_mem (tmpu32 - 4, 4))
+	      return -1;
+	  }
+	  break;
+	case 0x07: /* pop */
+	  {
+	    int a = (inst >> 4) & 0xf;
+	    int b = inst & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, a)
+		|| record_full_arch_list_add_reg (regcache, b))
+	      return -1;
+	  }
+	  break;
+	case 0x08: /* lda.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x09: /* sta.l */
+	  {
+	    tmpu32 = (uint32_t) ft32_process_readu (addr+2, buf,
+						     4, byte_order);
+	    if (record_full_arch_list_add_mem (tmpu32, 4))
+	      return -1;
+	  }
+	  break;
+	case 0x0a: /* ld.l (register indirect) */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x0b: /* st.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    if (record_full_arch_list_add_mem (tmpu32, 4))
+	      return -1;
+	  }
+	  break;
+	case 0x0c: /* ldo.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x0d: /* sto.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    uint32_t offset = (uint32_t) ft32_process_readu (addr+2, buf, 4,
+							      byte_order);
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    tmpu32 += offset;
+	    if (record_full_arch_list_add_mem (tmpu32, 4))
+	      return -1;
+	  }
+	  break;
+	case 0x0e: /* cmp */
+	  {
+	    if (record_full_arch_list_add_reg (regcache, FT32_CC_REGNUM))
+	      return -1;
+	  }
+	  break;
+	case 0x0f:
+	case 0x10:
+	case 0x11:
+	case 0x12:
+	case 0x13:
+	case 0x14:
+	case 0x15:
+	case 0x16:
+	case 0x17:
+	case 0x18:
+	  {
+	    /* Do nothing.  */
+	    break;
+	  }
+	case 0x19: /* jsr */
+	  {
+	    regcache_raw_read (regcache,
+			       FT32_SP_REGNUM, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    if (record_full_arch_list_add_reg (regcache, FT32_FP_REGNUM)
+		|| (record_full_arch_list_add_reg (regcache,
+						   FT32_SP_REGNUM))
+		|| record_full_arch_list_add_mem (tmpu32 - 12, 12))
+	      return -1;
+	  }
+	  break;
+	case 0x1a: /* jmpa */
+	  {
+	    /* Do nothing.  */
+	  }
+	  break;
+	case 0x1b: /* ldi.b (immediate) */
+	case 0x1c: /* ld.b (register indirect) */
+	case 0x1d: /* lda.b */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x1e: /* st.b */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    if (record_full_arch_list_add_mem (tmpu32, 1))
+	      return -1;
+	  }
+	  break;
+	case 0x1f: /* sta.b */
+	  {
+	    tmpu32 = ft32_process_readu (addr+2, (char *) buf,
+					  4, byte_order);
+	    if (record_full_arch_list_add_mem (tmpu32, 1))
+	      return -1;
+	  }
+	  break;
+	case 0x20: /* ldi.s (immediate) */
+	case 0x21: /* ld.s (register indirect) */
+	case 0x22: /* lda.s */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x23: /* st.s */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    if (record_full_arch_list_add_mem (tmpu32, 2))
+	      return -1;
+	  }
+	  break;
+	case 0x24: /* sta.s */
+	  {
+	    tmpu32 = ft32_process_readu (addr+2, (char *) buf,
+					  4, byte_order);
+	    if (record_full_arch_list_add_mem (tmpu32, 2))
+	      return -1;
+	  }
+	  break;
+	case 0x25: /* jmp */
+	  {
+	    /* Do nothing.  */
+	  }
+	  break;
+	case 0x26: /* and */
+	case 0x27: /* lshr */
+	case 0x28: /* ashl */
+	case 0x29: /* sub.l */
+	case 0x2a: /* neg */
+	case 0x2b: /* or */
+	case 0x2c: /* not */
+	case 0x2d: /* ashr */
+	case 0x2e: /* xor */
+	case 0x2f: /* mul.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x30: /* swi */
+	  {
+	    /* We currently implement support for libgloss'
+	       system calls.  */
+
+	    int inum = ft32_process_readu (addr+2, (char *) buf,
+					    4, byte_order);
+
+	    switch (inum)
+	      {
+	      case 0x1: /* SYS_exit */
+		{
+		  /* Do nothing.  */
+		}
+		break;
+	      case 0x2: /* SYS_open */
+		{
+		  if (record_full_arch_list_add_reg (regcache, RET1_REGNUM))
+		    return -1;
+		}
+		break;
+	      case 0x4: /* SYS_read */
+		{
+		  uint32_t length, ptr;
+
+		  /* Read buffer pointer is in $r1.  */
+		  regcache_raw_read (regcache, 3, (gdb_byte *) & ptr);
+		  ptr = extract_unsigned_integer ((gdb_byte *) & ptr,
+						  4, byte_order);
+
+		  /* String length is at 0x12($fp).  */
+		  regcache_raw_read (regcache,
+				     FT32_FP_REGNUM, (gdb_byte *) & tmpu32);
+		  tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+						     4, byte_order);
+		  length = ft32_process_readu (tmpu32+20, (char *) buf,
+						4, byte_order);
+
+		  if (record_full_arch_list_add_mem (ptr, length))
+		    return -1;
+		}
+		break;
+	      case 0x5: /* SYS_write */
+		{
+		  if (record_full_arch_list_add_reg (regcache, RET1_REGNUM))
+		    return -1;
+		}
+		break;
+	      default:
+		break;
+	      }
+	  }
+	  break;
+	case 0x31: /* div.l */
+	case 0x32: /* udiv.l */
+	case 0x33: /* mod.l */
+	case 0x34: /* umod.l */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x35: /* brk */
+	  /* Do nothing.  */
+	  break;
+	case 0x36: /* ldo.b */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x37: /* sto.b */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    uint32_t offset = (uint32_t) ft32_process_readu (addr+2, buf, 4,
+							      byte_order);
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    tmpu32 += offset;
+	    if (record_full_arch_list_add_mem (tmpu32, 1))
+	      return -1;
+	  }
+	  break;
+	case 0x38: /* ldo.s */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    if (record_full_arch_list_add_reg (regcache, reg))
+	      return -1;
+	  }
+	  break;
+	case 0x39: /* sto.s */
+	  {
+	    int reg = (inst >> 4) & 0xf;
+	    uint32_t offset = (uint32_t) ft32_process_readu (addr+2, buf, 4,
+							      byte_order);
+	    regcache_raw_read (regcache, reg, (gdb_byte *) & tmpu32);
+	    tmpu32 = extract_unsigned_integer ((gdb_byte *) & tmpu32,
+					       4, byte_order);
+	    tmpu32 += offset;
+	    if (record_full_arch_list_add_mem (tmpu32, 2))
+	      return -1;
+	  }
+	  break;
+	default:
+	  /* Do nothing.  */
+	  break;
+	}
+    }
+
+  if (record_full_arch_list_add_reg (regcache, FT32_PC_REGNUM))
+    return -1;
+  if (record_full_arch_list_add_end ())
+    return -1;
+  return 0;
+#endif
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  /* Support reverse debugging.  */
+  set_gdbarch_process_record (gdbarch, ft32_process_record);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..f561392
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,42 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2013 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for MOXIE.  */
+};
+
+/* Register numbers of various important registers.  */
+
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,		/* Address of executing stack frame.  */
+  FT32_SP_REGNUM,		/* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_PC_REGNUM = 32		/* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33          /* 32 real registers + PC */
+
+#endif /* ft32-tdep.h */
diff --git a/sim/configure.tgt b/sim/configure.tgt
index d112e72..443418e 100644
--- a/sim/configure.tgt
+++ b/sim/configure.tgt
@@ -111,6 +111,9 @@ case "${target}" in
    powerpc*-*-*)
        SIM_ARCH(ppc)
        ;;
+   ft32-*-*)
+       SIM_ARCH(ft32)
+       ;;
    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..d0de231
--- /dev/null
+++ b/sim/ft32/Makefile.in
@@ -0,0 +1,38 @@
+#    Makefile template for Configure for the ft32 sim library.
+#    Copyright (C) 2008-2013 Free Software Foundation, Inc.
+#    Written by Anthony Green
+#
+# 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
+
+dtbdir = @datadir@/gdb-`sed q ${srcdir}/../../gdb/version.in`/dtb
+
+SIM_OBJS = interp.o sim-load.o sim-io.o sim-config.o sim-utils.o	\
+sim-options.o sim-module.o sim-core.o sim-endian.o sim-trace.o	\
+sim-engine.o sim-fpu.o sim-bits.o sim-profile.o sim-events.o \
+sim-memopt.o
+
+SIM_EXTRA_LIBS = -lm -lz -ldl
+SIM_EXTRA_CLEAN = ft32-clean
+# SIM_EXTRA_INSTALL = install-dtb
+# SIM_CFLAGS = -DDTB="\"$(dtbdir)/ft32-gdb.dtb\""
+
+## COMMON_POST_CONFIG_FRAG
+
+all: interp.o
+
+interp.o: interp.c
+
+ft32-clean:
diff --git a/sim/ft32/config.in b/sim/ft32/config.in
new file mode 100644
index 0000000..8de933c
--- /dev/null
+++ b/sim/ft32/config.in
@@ -0,0 +1,154 @@
+/* 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 <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the `__setfpucw' function. */
+#undef HAVE___SETFPUCW
+
+/* 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
diff --git a/sim/ft32/configure.ac b/sim/ft32/configure.ac
new file mode 100644
index 0000000..4695c16
--- /dev/null
+++ b/sim/ft32/configure.ac
@@ -0,0 +1,18 @@
+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
+
+AC_CHECK_TOOL(DTC, dtc)
+
+SIM_AC_OPTION_ENDIAN(LITTLE_ENDIAN)
+SIM_AC_OPTION_ALIGNMENT(STRICT_ALIGNMENT)
+SIM_AC_OPTION_HOSTENDIAN
+SIM_AC_OPTION_ENVIRONMENT
+SIM_AC_OPTION_INLINE()
+
+AC_CHECK_HEADERS(unistd.h)
+
+SIM_AC_OUTPUT
diff --git a/sim/ft32/interp.c b/sim/ft32/interp.c
new file mode 100644
index 0000000..493706b
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,1014 @@
+/* Simulator for the FT32 processor
+   Copyright (C) 2008-2013 Free Software Foundation, Inc.
+   Contributed by FTDI (support@ftdichip.com)
+
+This file is part of GDB, the GNU debugger.
+
+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 "sysdep.h"
+#include <sys/times.h>
+#include <sys/param.h>
+#include <netinet/in.h>	/* for byte ordering macros */
+#include "bfd.h"
+#include "gdb/callback.h"
+#include "libiberty.h"
+#include "gdb/remote-sim.h"
+
+#include "sim-main.h"
+#include "sim-base.h"
+
+#include "opcode/ft32.h"
+
+extern const ft32_opc_info_t ft32_opc_info[128];
+
+typedef int word;
+typedef unsigned int uword;
+
+host_callback *       callback;
+
+FILE *tracefile;
+
+static char *myname;
+static SIM_OPEN_KIND sim_kind;
+static int issue_messages = 0;
+
+struct {
+  uint32_t regs[32];
+  uint32_t pc;
+  uint8_t pm[262144];
+  uint8_t ram[65536];
+  uint64_t num_i;
+  uint64_t cycles;
+  uint64_t next_tick_cycle;
+  enum sim_stop reason;
+  int pm_unlock;
+  uint32_t pm_addr;
+  int exception;
+} cpu;
+
+unsigned long
+ft32_extract_unsigned_integer (addr, len)
+     unsigned char * addr;
+     int len;
+{
+  unsigned long retval;
+  unsigned char * p;
+  unsigned char * startaddr = (unsigned char *)addr;
+  unsigned char * endaddr = startaddr + len;
+
+  if (len > (int) sizeof (unsigned long))
+    printf ("That operation is not available on integers of more than %ld bytes.",
+	    sizeof (unsigned long));
+
+  /* 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;
+}
+
+void
+ft32_store_unsigned_integer (addr, len, val)
+     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;
+    }
+}
+
+void
+sim_size (int s)
+{
+}
+
+static void
+set_initial_gprs ()
+{
+  int i;
+  long space;
+}
+
+static void
+interrupt ()
+{
+  // cpu.asregs.exception = SIGINT;
+}
+
+/* Write a 4 byte value to memory.  */
+
+static void INLINE
+wlat (sim_cpu *scpu, word pc, word x, word v)
+{
+  address_word cia = CIA_GET (scpu);
+
+  sim_core_write_aligned_4 (scpu, cia, write_map, x, v);
+}
+
+static int tracing = 0;
+
+uint32_t cpu_pm_read(SIM_DESC sd, int dw, uint32_t ea)
+{
+  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+  address_word cia = CIA_GET (scpu);
+  uint32_t r;
+
+  if ((ea & ~0x3ffff) != 0)
+    {
+      fprintf(stderr, "Illegal PM address %08x, pc %#x\n", ea, cpu.pc);
+      exit(0);
+    }
+
+  switch (dw)
+  {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+  }
+  r = sim_core_read_aligned_1 (scpu, cia, read_map, ea);
+  if (1 <= dw)
+    {
+      r += (sim_core_read_aligned_1 (scpu, cia, read_map, ea+1) << 8);
+      if (2 <= dw)
+        {
+          r += (sim_core_read_aligned_1 (scpu, cia, read_map, ea+2) << 16);
+          r += (sim_core_read_aligned_1 (scpu, cia, read_map, ea+3) << 24);
+        }
+    }
+  return r;
+}
+
+static uint32_t safe_addr(uint32_t dw, uint32_t ea)
+{
+  if ((ea & ~0x1ffff) != 0)
+    {
+      fprintf(stderr, "Illegal RAM address %08x, pc %#x\n", ea, cpu.pc);
+      exit(0);
+    }
+  ea &= 0x1ffff;
+  switch (dw)
+  {
+    case 1:
+      // if ((ea & 1) == 0);
+      ea &= ~1;
+      break;
+    case 2:
+      // ASSERT((ea & 3) == 0);
+      ea &= ~3;
+      break;
+    default:
+      break;
+  }
+  return ea;
+}
+
+static uint32_t cpu_mem_read(uint32_t dw, uint32_t ea)
+{
+  uint32_t r;
+
+  ea = safe_addr(dw, ea);
+  if ((ea & ~0xffff))
+    {
+      switch (ea)
+      {
+      case 0x10000:
+      case 0x10020:
+        return getchar();
+      case 0x10024:
+        return 1;
+      case 0x1fff4:
+        // printf("cycles = %lld, us = %u\n", cpu.cycles, (uint32_t)(cpu.cycles / 100));
+        return (uint32_t)(cpu.cycles / 100);
+      default:
+        fprintf(stderr, "Illegal IO read address %08x, pc %#x\n", ea, cpu.pc);
+        exit(0);
+      }
+    }
+  r = cpu.ram[ea];
+  if (1 <= dw)
+    {
+      r += (cpu.ram[ea + 1] << 8);
+      if (2 <= dw)
+        {
+          r += cpu.ram[ea + 2] << 16;
+          r += cpu.ram[ea + 3] << 24;
+        }
+    }
+  return r;
+}
+
+static void cpu_mem_write(SIM_DESC sd, uint32_t dw, uint32_t ea, uint32_t d)
+{
+  ea = safe_addr(dw, ea);
+  if (ea & 0x10000)
+    {
+      if (tracefile)
+        fprintf(tracefile, "IO write %c %08x to %0x\n", "bsl"[dw], d, ea);
+      switch (ea)
+      {
+      case 0x10000:
+        putchar(d & 0xff);
+        break;
+      case 0x1fc80:
+        cpu.pm_unlock = (d == 0x1337f7d1);
+        break;
+      case 0x1fc84:
+        cpu.pm_addr = d;
+        break;
+      case 0x1fc88:
+        {
+          sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+          address_word cia = CIA_GET (scpu);
+          sim_core_write_aligned_1 (scpu, cia, read_map, cpu.pm_addr + 0, d & 0xff);
+          sim_core_write_aligned_1 (scpu, cia, read_map, cpu.pm_addr + 1, (d >> 8) & 0xff);
+          sim_core_write_aligned_1 (scpu, cia, read_map, cpu.pm_addr + 2, (d >> 16) & 0xff);
+          sim_core_write_aligned_1 (scpu, cia, read_map, cpu.pm_addr + 3, (d >> 24) & 0xff);
+        }
+        break;
+      case 0x10024:
+      case 0x1fffc:
+        break;
+      case 0x1fff8:
+        printf("DEBUG "); // fall-though...
+      case 0x19470:
+        {
+          uint32_t d16 = (d & 0xffff) | ((d & 0xffff) << 16);
+          uint32_t d8 = (d16 & 0x00ff00ff) | ((d16 & 0x00ff00ff) << 8);
+          switch (dw)
+          {
+          case 0: d = d8; break;
+          case 1: d = d16; break;
+          }
+          printf("%08x\n", d);
+        }
+        break;
+      default:
+        fprintf(stderr, "Unknown IO write %08x to to %08x\n", d, ea);
+      }
+    }
+  else
+    {
+      cpu.ram[ea] = d & 0xff;
+      if (1 <= dw)
+        {
+          cpu.ram[ea + 1] = (d >> 8) & 0xff;
+          if (2 <= dw)
+            {
+              cpu.ram[ea + 2] = (d >> 16) & 0xff;
+              cpu.ram[ea + 3] = (d >> 24) & 0xff;
+            }
+        }
+    }
+}
+
+static void ft32_push(SIM_DESC sd, uint32_t v)
+{
+  cpu.regs[31] -= 4;
+  cpu.regs[31] &= 0xffff;
+  cpu_mem_write(sd, 2, cpu.regs[31], v);
+}
+
+static uint32_t ft32_pop()
+{
+  uint32_t r = cpu_mem_read(2, cpu.regs[31]);
+  cpu.regs[31] += 4;
+  cpu.regs[31] &= 0xffff;
+  return r;
+}
+
+static int nunsigned(int siz, int bits)
+{
+  int mask = (1L << siz) - 1;
+  return bits & mask;
+}
+
+static int nsigned(int siz, int bits)
+{
+  int shift = (sizeof(int) * 8) - siz;
+  return (bits << shift) >> shift;
+}
+
+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);
+}
+
+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);
+}
+
+static uint32_t ror(uint32_t n, uint32_t b)
+{
+  b &= 31;
+  return (n >> b) | (n << (32 - b));
+}
+
+static uint32_t bins(uint32_t d, uint32_t f, uint32_t len, uint32_t pos)
+{
+  uint32_t mask = ((1 << len) - 1) << pos;
+  return (d & ~mask) | ((f << pos) & mask);
+}
+
+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;
+}
+
+void
+sim_resume (sd, step, siggnal)
+     SIM_DESC sd;
+     int step, siggnal;
+{
+  void (* sigsave)();
+  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+  address_word cia = CIA_GET (scpu);
+
+  sigsave = signal (SIGINT, interrupt);
+  cpu.reason = sim_stopped;
+
+  do
+    {
+      if (cpu.cycles >= cpu.next_tick_cycle)
+        {
+          cpu.next_tick_cycle += 100000;
+          ft32_push(sd, cpu.pc);
+          cpu.pc = 12;  // interrupt 1
+        }
+      uint32_t inst = cpu_pm_read(sd, 2, cpu.pc);
+      cpu.cycles += 1;
+      // printf("inst at %04x: %08x\n", cpu.pc, inst);
+
+      // Handle "call 8" (which is FT32's "break" equivalent) here
+      if (inst == 0x00340002)
+        {
+          cpu.reason = sim_stopped;
+          goto escape;
+        }
+      uint32_t dw   =             (inst >> FT32_FLD_DW_BIT) & ((1U << FT32_FLD_DW_SIZ) - 1);
+      uint32_t cb   =             (inst >> FT32_FLD_CB_BIT) & ((1U << FT32_FLD_CB_SIZ) - 1);
+      uint32_t r_d  =             (inst >> FT32_FLD_R_D_BIT) & ((1U << FT32_FLD_R_D_SIZ) - 1);
+      uint32_t cr   =             (inst >> FT32_FLD_CR_BIT) & ((1U << FT32_FLD_CR_SIZ) - 1);
+      uint32_t cv   =             (inst >> FT32_FLD_CV_BIT) & ((1U << FT32_FLD_CV_SIZ) - 1);
+      uint32_t bt   =             (inst >> FT32_FLD_BT_BIT) & ((1U << FT32_FLD_BT_SIZ) - 1);
+      uint32_t r_1 =              (inst >> FT32_FLD_R_1_BIT) & ((1U << FT32_FLD_R_1_SIZ) - 1);
+      uint32_t rimm =             (inst >> FT32_FLD_RIMM_BIT) & ((1U << FT32_FLD_RIMM_SIZ) - 1);
+      uint32_t r_2  =             (inst >> FT32_FLD_R_2_BIT) & ((1U << FT32_FLD_R_2_SIZ) - 1);
+      uint32_t k20  = nsigned(20, (inst >> FT32_FLD_K20_BIT) & ((1U << FT32_FLD_K20_SIZ) - 1));
+      uint32_t pa   =             (inst >> FT32_FLD_PA_BIT) & ((1U << FT32_FLD_PA_SIZ) - 1);
+      uint32_t aa   =             (inst >> FT32_FLD_AA_BIT) & ((1U << FT32_FLD_AA_SIZ) - 1);
+      uint32_t k16  =             (inst >> FT32_FLD_K16_BIT) & ((1U << FT32_FLD_K16_SIZ) - 1);
+      uint32_t k8   = nsigned(8,  (inst >> FT32_FLD_K8_BIT) & ((1U << FT32_FLD_K8_SIZ) - 1));
+      uint32_t al   =             (inst >> FT32_FLD_AL_BIT) & ((1U << FT32_FLD_AL_SIZ) - 1);
+
+      uint32_t r_1v = cpu.regs[r_1];
+      uint32_t rimmv = (rimm & 0x400) ? nsigned(10, rimm) : cpu.regs[rimm & 0x1f];
+
+      uint32_t bit_pos = rimmv & 31;
+      uint32_t bit_len = 0xf & (rimmv >> 5);
+      if (bit_len == 0)
+        bit_len = 16;
+
+      uint32_t upper = (inst >> 27);
+
+      uint32_t insnpc = cpu.pc;
+      cpu.pc += 4;
+      switch (upper)
+      {
+        case FT32_PAT_TOC:
+        case FT32_PAT_TOCI:
+          {
+            int takeit = (cr == 3) || ((1 & (cpu.regs[28 + cr] >> cb)) == cv);
+            if (takeit)
+              {
+                cpu.cycles += 1;
+                if (bt)
+                  {
+                    /* this is a call */
+                    if (tracefile)
+                      fprintf(tracefile, "r0=%08x r1=%08x r2=%08x r3=%08x\n", cpu.regs[0], cpu.regs[1], cpu.regs[2], cpu.regs[3]);
+                    ft32_push(sd, cpu.pc);
+                  }
+                if (upper == FT32_PAT_TOC)
+                  {
+                    cpu.pc = pa << 2;
+                  }
+                else
+                  {
+                    if (tracefile)
+                      fprintf(tracefile, "indirect r%d to %#x\n", r_2, cpu.regs[r_2]);
+                    cpu.pc = cpu.regs[r_2];
+                  }
+                if (cpu.pc == 0x8)
+                  {
+                    fprintf(stderr, "PC is 8!\n");
+                    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:
+                printf("Unhandled alu %#x\n", al);
+                goto escape;
+            }
+            if (upper == FT32_PAT_ALUOP)
+              {
+                cpu.regs[r_d] = result;
+              }
+              else
+              {
+                uint32_t dwmask;
+                int dwsiz;
+                switch (dw)
+                {
+                  case 0: dwsiz = 7;  dwmask = 0xffU; break;
+                  case 1: dwsiz = 15; dwmask = 0xffffU; break;
+                  case 2: dwsiz = 31; dwmask = 0xffffffffU; break;
+                }
+
+                int zero = (0 == (result & dwmask));
+                int sign = 1 & (result >> dwsiz);
+                int ahi = 1 & (r_1v >> dwsiz);
+                int bhi = 1 & (rimmv >> dwsiz);
+                int overflow = (sign != ahi) & (ahi == !bhi);
+                int carry;
+                int bit = (dwsiz + 1);
+                uint64_t ra = r_1v & dwmask;
+                uint64_t 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;
+                }
+                int above = (!carry & !zero);
+                int greater = (sign == overflow) & !zero;
+                int greatereq = (sign == overflow);
+
+                cpu.regs[r_d] = (
+                  (above << 6) |
+                  (greater << 5) |
+                  (greatereq << 4) |
+                  (sign << 3) |
+                  (overflow << 2) |
+                  (carry << 1) |
+                  (zero << 0));
+              }
+            if (0)
+              fprintf(tracefile, "%06x: result=%08x, \n", cpu.pc, result);
+          }
+          break;
+
+        case FT32_PAT_LDK:
+          cpu.regs[r_d] = k20;
+          break;
+
+        case FT32_PAT_LPM:
+          cpu.cycles += 1;
+          cpu.regs[r_d] = cpu_pm_read(sd, dw, pa << 2);
+          break;
+
+        case FT32_PAT_LPMI:
+          cpu.cycles += 1;
+          cpu.regs[r_d] = cpu_pm_read(sd, dw, cpu.regs[r_1] + k8);
+          if (tracefile)
+            fprintf(tracefile, "lpmi %x %08x\n", cpu.regs[r_1] + k8, cpu.regs[r_d]);
+          break;
+
+        case FT32_PAT_STA:
+          cpu_mem_write(sd, dw, aa, cpu.regs[r_d]);
+          if (aa == 0x1fffc)
+            {
+              cpu.reason = sim_exited;
+              goto escape;
+            }
+          break;
+
+        case FT32_PAT_STI:
+          if (tracefile)
+            fprintf(tracefile, "STI %#x <= %08x\n", cpu.regs[r_d] + k8, cpu.regs[r_1]);
+          cpu_mem_write(sd, dw, cpu.regs[r_d] + k8, cpu.regs[r_1]);
+          break;
+
+        case FT32_PAT_LDA:
+          cpu.cycles += 1;
+          cpu.regs[r_d] = cpu_mem_read(dw, aa);
+          break;
+
+        case FT32_PAT_LDI:
+          cpu.cycles += 1;
+          cpu.regs[r_d] = cpu_mem_read(dw, cpu.regs[r_1] + k8);
+          if (tracefile)
+            fprintf(tracefile, "LDI %#x <= %08x\n", cpu.regs[r_d], cpu.regs[r_1] + k8);
+          break;
+
+        case FT32_PAT_PUSH:
+          ft32_push(sd, r_1v);
+          break;
+
+        case FT32_PAT_LINK:
+          ft32_push(sd, cpu.regs[r_d]);
+          cpu.regs[r_d] = cpu.regs[31];
+          cpu.regs[31] -= k16;
+          cpu.regs[31] &= 0xffff;
+          break;
+
+        case FT32_PAT_UNLINK:
+          cpu.regs[31] = cpu.regs[r_d];
+          cpu.regs[31] &= 0xffff;
+          cpu.regs[r_d] = ft32_pop();
+          break;
+
+        case FT32_PAT_POP:
+          cpu.cycles += 1;
+          cpu.regs[r_d] = ft32_pop();
+          break;
+
+        case FT32_PAT_RETURN:
+          cpu.pc = ft32_pop();
+          break;
+
+        case FT32_PAT_FFUOP:
+          if (tracefile)
+            fprintf(tracefile, "ffu %#x\n", al);
+          switch (al)
+          {
+          case 0x0: cpu.regs[r_d] = r_1v / rimmv; break;
+          case 0x1: cpu.regs[r_d] = r_1v % rimmv; break;
+          case 0x2: cpu.regs[r_d] = ft32sdiv(r_1v, rimmv); break;
+          case 0x3: cpu.regs[r_d] = ft32smod(r_1v, rimmv); break;
+
+          case 0x4: cpu.regs[r_d] = strcmp(cpu.ram + r_1v, cpu.ram + rimmv); break;
+          case 0x5: memcpy(cpu.ram + cpu.regs[r_d], cpu.ram + r_1v, rimmv); break;
+          case 0x6: cpu.regs[r_d] = strlen(cpu.ram + r_1v); break;
+          case 0x7: memset(cpu.ram + cpu.regs[r_d], r_1v, rimmv); break;
+          case 0x8: cpu.regs[r_d] = r_1v * rimmv; break;
+          case 0x9: cpu.regs[r_d] = ((uint64_t)r_1v * (uint64_t)rimmv) >> 32; break;
+          case 0xe:
+            {
+              // streamout
+              uint32_t i;
+              uint32_t src = cpu.regs[r_1];
+              for (i = 0; i < rimmv; i += (1 << dw))
+                {
+                  cpu_mem_write(sd, dw, cpu.regs[r_d], cpu_mem_read(dw, src));
+                  src += (1 << dw);
+                }
+            }
+            break;
+          default:
+            printf("Unhandled ffu %#x\n", al);
+            goto escape;
+          }
+          break;
+
+        default:
+          goto escape;
+      }
+      cpu.num_i++;
+      if (tracefile)
+        fprintf(tracefile, "@%lld pc=%#x inst=%08x r%d=%08x\n", cpu.num_i, insnpc, inst, r_d, cpu.regs[r_d]);
+    }
+    while (!step);
+escape:
+  cpu.exception = step ? SIGTRAP : 0;
+  if (cpu.reason == sim_exited)
+    {
+      cpu.exception = cpu.regs[0];
+      printf("[FT32 sim ending:%llu instructions executed. Exit code %d]\n", cpu.num_i, (int)cpu.exception);
+    }
+
+  signal (SIGINT, sigsave);
+}
+
+int
+sim_write (sd, addr, buffer, size)
+     SIM_DESC sd;
+     SIM_ADDR addr;
+     const unsigned char * buffer;
+     int size;
+{
+  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+  // printf("sim_write(%#x, %#x)\n", addr, size);
+
+  if (addr < 0x800000)
+    {
+      sim_core_write_buffer (sd, scpu, write_map, buffer, addr, size);
+    }
+    else
+    {
+      int i;
+
+      addr -= 0x800000;
+      for (i = 0; i < size; i++)
+        cpu_mem_write(sd, 0, addr + i, buffer[i]);
+    }
+  return size;
+}
+
+int
+sim_read (sd, addr, buffer, size)
+     SIM_DESC sd;
+     SIM_ADDR addr;
+     unsigned char * buffer;
+     int size;
+{
+  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+
+  // printf("[sim_read %#x, %d]\n", addr, size);
+  if (addr < 0x800000)
+    {
+      sim_core_read_buffer (sd, scpu, read_map, buffer, addr, size);
+    }
+    else
+    {
+      int i;
+
+      addr -= 0x800000;
+      for (i = 0; i < size; i++)
+        buffer[i] = cpu_mem_read(0, addr + i);
+    }
+
+  return size;
+}
+
+#define NUM_FT32_REGS 32
+int
+sim_store_register (sd, rn, memory, length)
+     SIM_DESC sd;
+     int rn;
+     unsigned char * memory;
+     int length;
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	{
+	  long ival;
+
+	  /* misalignment safe */
+	  ival = ft32_extract_unsigned_integer (memory, 4);
+	  if (rn == 32)
+            cpu.pc = ival;
+          else
+            cpu.regs[rn] = ival;
+	}
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+int
+sim_fetch_register (sd, rn, memory, length)
+     SIM_DESC sd;
+     int rn;
+     unsigned char * memory;
+     int length;
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	{
+	  long ival;
+          // Handle the register number translation here.
+          // Sim registers are, duh, 0-31.
+          // Other tools (gcc, gdb) uses:
+          // 0 - fp
+          // 1 - sp
+          // 2 - r0
+          // 31 - cc
+          switch (rn)
+          {
+          case 0:
+            ival = cpu.regs[29];
+            break;
+          case 1:
+            ival = cpu.regs[31];
+            break;
+          case 31:
+            ival = cpu.regs[30];
+            break;
+          case 32:
+            ival = cpu.pc;
+            break;
+          default:
+            ival = cpu.regs[rn - 2];
+          }
+          // printf("[sim_fetch_register(%d)=%08x]", rn, ival);
+
+	  /* misalignment-safe */
+	  ft32_store_unsigned_integer (memory, 4, ival);
+	}
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+
+int
+sim_trace (sd)
+     SIM_DESC sd;
+{
+  if (tracefile == 0)
+    tracefile = fopen("trace.csv", "wb");
+  tracefile = stdout;
+
+  tracing = 1;
+
+  sim_resume (sd, 0, 0);
+
+  // fclose(tracefile);
+
+  tracing = 0;
+
+  return 1;
+}
+
+void
+sim_stop_reason (sd, reason, sigrc)
+     SIM_DESC sd;
+     enum sim_stop * reason;
+     int * sigrc;
+{
+  *reason = cpu.reason;
+  *sigrc = cpu.exception;
+}
+
+
+int
+sim_stop (sd)
+     SIM_DESC sd;
+{
+  // cpu.asregs.exception = SIGINT;
+  return 1;
+}
+
+
+void
+sim_info (sd, verbose)
+     SIM_DESC sd;
+     int verbose;
+{
+  // callback->printf_filtered (callback, "\n\n# instructions executed  %llu\n", cpu.asregs.insts);
+}
+
+
+SIM_DESC
+sim_open (kind, cb, abfd, argv)
+     SIM_OPEN_KIND kind;
+     host_callback * cb;
+     struct bfd * abfd;
+     char ** argv;
+{
+  SIM_DESC sd = sim_state_alloc (kind, cb);
+  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
+
+  if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
+    return 0;
+
+  sim_do_command(sd," memory region 0x00000000,0x4000000") ;
+  sim_do_command(sd," memory region 0xE0000000,0x10000") ;
+
+  myname = argv[0];
+  callback = cb;
+
+  if (kind == SIM_OPEN_STANDALONE)
+    issue_messages = 1;
+
+  set_initial_gprs ();	/* Reset the GPR registers.  */
+
+  /* Configure/verify the target byte order and other runtime
+     configuration options.  */
+  if (sim_config (sd) != SIM_RC_OK)
+    {
+      sim_module_uninstall (sd);
+      return 0;
+    }
+
+  if (sim_post_argv_init (sd) != SIM_RC_OK)
+    {
+      /* Uninstall the modules to avoid memory leaks,
+	 file descriptor leaks, etc.  */
+      sim_module_uninstall (sd);
+      return 0;
+    }
+
+  return sd;
+}
+
+void
+sim_close (sd, quitting)
+     SIM_DESC sd;
+     int quitting;
+{
+  /* nothing to do */
+}
+
+
+/* Load the device tree blob.  */
+
+static void
+load_dtb (SIM_DESC sd, const char *filename)
+{
+  int size = 0;
+  FILE *f = fopen (filename, "rb");
+  char *buf;
+  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+ if (f == NULL)
+    {
+      printf ("WARNING: ``%s'' could not be opened.\n", filename);
+      return;
+    }
+  fseek (f, 0, SEEK_END);
+  size = ftell(f);
+  fseek (f, 0, SEEK_SET);
+  buf = alloca (size);
+  if (size != fread (buf, 1, size, f))
+    {
+      printf ("ERROR: error reading ``%s''.\n", filename);
+      return;
+    }
+  sim_core_write_buffer (sd, scpu, write_map, buf, 0xE0000000, size);
+  // cpu.asregs.sregs[9] = 0xE0000000;
+  fclose (f);
+}
+
+SIM_RC
+sim_load (sd, prog, abfd, from_tty)
+     SIM_DESC sd;
+     const char * prog;
+     bfd * abfd;
+     int from_tty;
+{
+  /* Do the right thing for ELF executables; this turns out to be
+     just about the right thing for any object format that:
+       - we crack using BFD routines
+       - follows the traditional UNIX text/data/bss layout
+       - calls the bss section ".bss".   */
+
+  extern bfd * sim_load_file (); /* ??? Don't know where this should live.  */
+  bfd * prog_bfd;
+
+  {
+    bfd * handle;
+    handle = bfd_openr (prog, 0);	/* could be "ft32" */
+
+    if (!handle)
+      {
+	printf("``%s'' could not be opened.\n", prog);
+	return SIM_RC_FAIL;
+      }
+
+    /* Makes sure that we have an object file, also cleans gets the
+       section headers in place.  */
+    if (!bfd_check_format (handle, bfd_object))
+      {
+	/* wasn't an object file */
+	bfd_close (handle);
+	printf ("``%s'' is not appropriate object file.\n", prog);
+	return SIM_RC_FAIL;
+      }
+
+    /* Clean up after ourselves.  */
+    bfd_close (handle);
+  }
+
+  memset(cpu.ram, 0, 65536);
+  /* from sh -- dac */
+  prog_bfd = sim_load_file (sd, myname, callback, prog, abfd,
+                            sim_kind == SIM_OPEN_DEBUG,
+                            0, sim_write);
+  if (prog_bfd == NULL)
+    return SIM_RC_FAIL;
+
+  if (abfd == NULL)
+    bfd_close (prog_bfd);
+
+  return SIM_RC_OK;
+}
+
+SIM_RC
+sim_create_inferior (sd, prog_bfd, argv, env)
+     SIM_DESC sd;
+     struct bfd * prog_bfd;
+     char ** argv;
+     char ** env;
+{
+  char ** avp;
+  int l, argc, i, tp;
+  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
+
+  /* Set the initial register set.  */
+  l = issue_messages;
+  issue_messages = 0;
+  set_initial_gprs ();
+  issue_messages = l;
+
+  // printf("start address %#lx\n", bfd_get_start_address (prog_bfd));
+  cpu.pc = bfd_get_start_address (prog_bfd);
+  cpu.regs[31] = 0x00000;
+  cpu.num_i = 0;
+  cpu.cycles = 0;
+  cpu.next_tick_cycle = 100000;
+
+  return SIM_RC_OK;
+}
+
+void
+sim_kill (sd)
+     SIM_DESC sd;
+{
+  if (tracefile)
+    fclose(tracefile);
+}
+
+void
+sim_do_command (sd, cmd)
+     SIM_DESC sd;
+     const char * cmd;
+{
+  if (sim_args_command (sd, cmd) != SIM_RC_OK)
+    sim_io_printf (sd,
+		   "Error: \"%s\" is not a valid ft32 simulator command.\n",
+		   cmd);
+}
+
+void
+sim_set_callbacks (ptr)
+     host_callback * ptr;
+{
+  callback = ptr;
+}
diff --git a/sim/ft32/sim-main.h b/sim/ft32/sim-main.h
new file mode 100644
index 0000000..89f0f72
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,61 @@
+/* Moxie Simulator definition.
+   Copyright (C) 2009-2013 Free Software Foundation, Inc.
+   Contributed by Anthony Green <green@moxielogic.com>
+
+This file is part of GDB, the GNU debugger.
+
+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
+
+#define SIM_HAVE_BIENDIAN
+
+#include "sim-basics.h"
+
+typedef address_word sim_cia;
+
+#include "sim-base.h"
+#include "bfd.h"
+
+#define PCIDX 17
+
+struct _sim_cpu {
+
+  /* The following are internal simulator state variables: */
+#define CIA_GET(CPU) ((CPU)->registers[PCIDX] + 0)
+#define CIA_SET(CPU,CIA) ((CPU)->registers[PCIDX] = (CIA))
+
+/* To keep this default simulator simple, and fast, we use a direct
+   vector of registers. The internal simulator engine then uses
+   manifests to access the correct slot. */
+
+  unsigned_word registers[19];
+
+  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/ft32/sysdep.h b/sim/ft32/sysdep.h
new file mode 100644
index 0000000..d82b70b
--- /dev/null
+++ b/sim/ft32/sysdep.h
@@ -0,0 +1,94 @@
+/* System includes and definitions used by the FT32 simulator.
+   Copyright (C) 2008-2013 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+This file is part of GDB, the GNU debugger.
+
+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 __SYSDEP_H
+#define __SYSDEP_H
+
+#ifndef	hosts_std_host_H
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/file.h>
+#include "ansidecl.h"
+
+#ifndef	O_ACCMODE
+#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+#ifndef	SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef	SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+/*#include <string.h>*/
+#else
+extern char * mktemp ();
+#ifndef memset
+extern PTR    memset ();
+#endif
+
+#ifndef	DONTDECLARE_MALLOC
+extern PTR   malloc ();
+extern PTR   realloc ();
+#endif
+
+#ifndef	__GNUC__
+extern PTR   memcpy ();
+#else
+/* char * memcpy (); */
+#endif
+
+#ifdef __STDC__
+extern void free ();
+#else
+extern int free();
+#endif
+
+#ifndef strchr
+extern char * strchr();
+#endif
+extern char * getenv();
+extern PTR    memchr();
+extern char * strrchr();
+
+extern char * strrchr();
+extern char * ctime();
+extern long   atol();
+extern char * getenv();
+#endif /* STDC_HEADERS */
+
+#ifndef	BYTES_IN_PRINTF_INT
+#define BYTES_IN_PRINTF_INT 4
+#endif
+
+#include "fopen-same.h"
+#define hosts_std_host_H
+#endif
+
+#ifdef	STDC_HEADERS
+#include <stddef.h>
+#endif /* STDC_HEADERS */
+
+#endif /* __SYSDEP_H */
-- 
1.7.9.5

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

* RE: [PATCH, FT32] gdb and sim support
  2015-02-03  4:06 [PATCH, FT32] gdb and sim support James Bowman
@ 2015-02-10  6:27 ` James Bowman
  2015-02-17 10:21   ` Mike Frysinger
  2015-02-19  7:06 ` Mike Frysinger
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-02-10  6:27 UTC (permalink / raw)
  To: gdb-patches

Ping.

Please can someone review this patch, and if appropriate commit it, as I do not have write access to the tree.

Thanks.

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

* Re: [PATCH, FT32] gdb and sim support
  2015-02-10  6:27 ` James Bowman
@ 2015-02-17 10:21   ` Mike Frysinger
  0 siblings, 0 replies; 30+ messages in thread
From: Mike Frysinger @ 2015-02-17 10:21 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 258 bytes --]

On 10 Feb 2015 06:27, James Bowman wrote:
> Please can someone review this patch, and if appropriate commit it, as I do not have write access to the tree.

i've started going through it.  will take some more time as i have a lot of 
feedback ;).
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-02-03  4:06 [PATCH, FT32] gdb and sim support James Bowman
  2015-02-10  6:27 ` James Bowman
@ 2015-02-19  7:06 ` Mike Frysinger
       [not found] ` <ORIGINAL-RELEASE-1424557036-20150219070610.GI544@vapier>
  2015-03-28  6:21 ` Mike Frysinger
  3 siblings, 0 replies; 30+ messages in thread
From: Mike Frysinger @ 2015-02-19  7:06 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 19744 bytes --]

On 03 Feb 2015 04:06, James Bowman wrote:
> FT32 is a new high performance 32-bit RISC core developed by FTDI for embedded applications.

ah, we def don't have enough CPUs in the market as is ;)

> Support for FT32 has already been added to binutils. This patch adds gdb and sim support.

where's the testsuite man ? :)  it should be trivial to start one with .s
files  -- just look at sim/testsuite/sim/.  otherwise there's no way to keep
regressions from slipping in.

> 2014-02-03  James Bowman  <james.bowman@ftdichip.com>
> 
>         * gdb/Makefile.in, gdb/configure.tgt: FT32 target added
>         * sim/configure.tgt: FT32 target added
>         * sim/configure: Regenerated
>         * sim/ft32/configure: Regenerated
>         * gdb/ft32-tdep.c,h: Support FT32
>         * sim/ft32/*: FT32 simulator

notes:
 - the date reflects commit time, not original creation
 - ChangeLog entries are split up across dirs
 - line items are indented with tabs
 - the notes should be full sentences; i.e. you should have a period at the end

on to the code ... a few more notes:
 - i wrote this feedback over a few "sessions", so there might be incomplete 
thoughts as i paused to do other things
 - feel free to ask questions about new things or things that aren't clear
 - as you make the adjustments below, make sure to periodically save & build & 
run & test ... the sim can be really easy to run into the weeds and trying to 
figure out what exactly you got wrong hard to trackback

> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
>
> -	glibc-tdep.c \
> +	ft32-tdep.c \
> +        glibc-tdep.c \

looks like you broke the indentation for glibc-tdep.c

> --- /dev/null
> +++ b/gdb/ft32-tdep.c
>
>    Copyright (C) 2009-2014 Free Software Foundation, Inc.

it is 2015 now.  should fix in all your files.

> +#include <string.h>

do you really need to include this ?  other tdeps don't seem to.

> +// #include "record-full.h"

dead code -> delete ?

looks like you've got a couple of dead lines in your patch.  please go through 
the whole thing and clean this up.

> +/* Use an invalid address value as 'not available' marker.  */
> +enum { REG_UNAVAIL = (CORE_ADDR) -1 };

space after the -

> +  int established;  // Has the new frame been LINKed

use /* comments */ everywhere.  seems to come up a few times -- please fix 
globally.

we've got bfd_boolean for bool fields too rather than open code it with an int

> +static const unsigned char *
> +ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
> +			  CORE_ADDR *pcptr, int *lenptr)

indentation is slightly broken

> +{
> +  static gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };

const ?

> +char *ft32_register_names[] =

static const char * const ft32_register_names[] ?

> +static struct type *
> +ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
> +{
> +  if (reg_nr == FT32_PC_REGNUM)
> +    return  builtin_type (gdbarch)->builtin_func_ptr;

only one space after the return

> #define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)

i think usually we put insn constants/etc... into the corresponding 
include/opcode/ header rather than pasting them inline.  this makes sharing 
between opcodes, gdb, sim, etc... much easier.

> +static CORE_ADDR
> +ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
> +			struct ft32_frame_cache *cache,
> +			struct gdbarch *gdbarch)

indentation is slightly off

>   if (start_addr >= end_addr)
>     {
>       return end_addr;
>     }

pretty sure you can (and should) omit braces in cases like this

> +      if (IS_PUSH(inst))

space needed between func and args:
	if (IS_PUSH (inst))

a quick scan shows this comes up multiple times -- please fix globally

>   for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
>     {
>       if (cache->saved_regs[regnum] != REG_UNAVAIL)
>         cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];

should use a tab here since you hit 8 spaces.  this comes up a couple of times 
-- please fix globally.

> +    }
> +  // printf("(Framesize=%lld,cache->saved_regs[FT32_PC_REGNUM]=%ld)\n", cache->framesize, cache->saved_regs[FT32_PC_REGNUM]);
> +  return next_addr;
> +
> +}

there should be a blank line above the return, not below it ;)

> +	  plg_end = ft32_analyze_prologue (func_addr,
> +					    func_end, &cache, gdbarch);

indentation is off

> +	  /* Don't use line number debug info for assembly source
> +	     files.  */

this doesn't need to be wrapped

> +	  /* No useable line symbol.  Use result of prologue parsing
> +	     method.  */

neither does this

> +static void
> +ft32_frame_this_id (struct frame_info *this_frame,
> +		    void **this_prologue_cache, struct frame_id *this_id)
> +{
> +  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
> +						   this_prologue_cache);

indentation is off

> +static struct value *
> +ft32_frame_prev_register (struct frame_info *this_frame,
> +			  void **this_prologue_cache, int regnum)
> +{
> +  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
> +						   this_prologue_cache);

same here

> +  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
> +    {
> +      return frame_unwind_got_memory (this_frame, regnum,
> +                                      0x800000 | cache->saved_regs[regnum]);
> +    }

drop the braces

> +static CORE_ADDR
> +ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
> +{
> +  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
> +						       this_cache);

indentation is broken

> +#if 0

drop all the #if 0 code.  this comes up a few times -- please fix globally.

> +static int
> +ft32_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> +		      CORE_ADDR addr)
> +{
> +  return -1;
> +#if 0
> ... 400 more lines ...
> +#endif
> +}

err, what now ?

> --- /dev/null
> +++ b/gdb/ft32-tdep.h
>
> +/* Register numbers of various important registers.  */
> +
> +enum ft32_regnum
> +{
> +  FT32_FP_REGNUM,		/* Address of executing stack frame.  */
> +  FT32_SP_REGNUM,		/* Address of top of stack.  */
> +  FT32_R0_REGNUM,
> +  FT32_R1_REGNUM,
> +  FT32_PC_REGNUM = 32		/* Program counter.  */
> +};
> +
> +/* Number of machine registers.  */
> +#define FT32_NUM_REGS 33          /* 32 real registers + PC */

this probably should be in include/gdb/sim-ft32.h instead so you can share it

also, define FT32_NUM_REGS in terms of FT32_PC_REGNUM ?

> +#endif /* ft32-tdep.h */

the style is to list the macro (FT32_TDEP_H), not the file

> --- /dev/null
> +++ b/sim/ft32/Makefile.in
>
> +dtbdir = @datadir@/gdb-`sed q ${srcdir}/../../gdb/version.in`/dtb

unused -> delete

> +SIM_OBJS = interp.o sim-load.o sim-io.o sim-config.o sim-utils.o	\
> +sim-options.o sim-module.o sim-core.o sim-endian.o sim-trace.o	\
> +sim-engine.o sim-fpu.o sim-bits.o sim-profile.o sim-events.o \
> +sim-memopt.o

please one-line & sort this list:
SIM_OBJS = \
	bar.o \
	foo.o

you also want to use $(SIM_NEW_COMMON_OBJS) at the start and $(SIM_EXTRA_OBJS) 
at the end

missing:
SIM_RUN_OBJS = nrun.o

> +SIM_EXTRA_LIBS = -lm -lz -ldl

you don't seem to use any of these libs -> delete

> +SIM_EXTRA_CLEAN = ft32-clean
> +# SIM_EXTRA_INSTALL = install-dtb
> +# SIM_CFLAGS = -DDTB="\"$(dtbdir)/ft32-gdb.dtb\""

unused -> delete

> +all: interp.o
> +
> +interp.o: interp.c
> +
> +ft32-clean:

don't need any of this -> delete

> --- /dev/null
> +++ b/sim/ft32/configure.ac
>
> +AC_CHECK_TOOL(DTC, dtc)

unused -> delete

> +SIM_AC_OPTION_INLINE()

drop the parens

since you're a new port, you should start with SIM_AC_OPTION_WARNINGS enabled.  
obviously that also means cleaning up all the warnings generated in the ft32/ 
subdir once you do :).

> +AC_CHECK_HEADERS(unistd.h)

unused -> delete

> --- /dev/null
> +++ b/sim/ft32/interp.c
>
> +   Contributed by FTDI (support@ftdichip.com)

use <support@ftdichip.com> instead of (support@ftdichip.com)

> +This file is part of GDB, the GNU debugger.

this isn't part of gdb ;).  please fix the indentation in this comment block.  
these comments apply to all your sim files it looks like.

> +#include <sys/times.h>
> +#include <sys/param.h>

you've got a lot of weird system header includes in your files.  please check 
each one to see if they're actually used.

> +#include <netinet/in.h>	/* for byte ordering macros */

i really hope you're not using macros (like hton/ntoh) from this header :).  
that's not how the sim handles endianness.  a quick glance shows you're not, so 
just delete the header and be done.

> +typedef int word;
> +typedef unsigned int uword;

at least uword is not used, and word looks hardly used.  i guess you should punt 
both and tweak the little code relying on them.

> +extern const ft32_opc_info_t ft32_opc_info[128];
> +
> +host_callback *       callback;
> +
> +FILE *tracefile;
> +
> +static char *myname;
> +static SIM_OPEN_KIND sim_kind;
> +static int issue_messages = 0;
> +
> +struct {
> +  uint32_t regs[32];
> +  uint32_t pc;
> +  uint8_t pm[262144];
> +  uint8_t ram[65536];
> +  uint64_t num_i;
> +  uint64_t cycles;
> +  uint64_t next_tick_cycle;
> +  enum sim_stop reason;
> +  int pm_unlock;
> +  uint32_t pm_addr;
> +  int exception;
> +} cpu;

there should be no global state.  read-only constants are OK, but that's not 
what these are.

> +unsigned long
> +ft32_extract_unsigned_integer (addr, len)

should be static

> +     unsigned char * addr;
> +     int len;

we want these old style prototypes to die ... please convert globally

> +{
> +  unsigned long retval;
> +  unsigned char * p;
> +  unsigned char * startaddr = (unsigned char *)addr;
> +  unsigned char * endaddr = startaddr + len;

there should be no space after the * as part of the decl:
	unsigned char *p;

but there should be a space after the cast:
	... = (unsigned char *) addr;

> +  if (len > (int) sizeof (unsigned long))
> +    printf ("That operation is not available on integers of more than %ld bytes.",
> +	    sizeof (unsigned long));

general rule: a sim should never write directly to stdout/stderr (i.e. use 
printf or fprintf).  look at common/sim-io.h for the funcs you should instead.

> +void
> +ft32_store_unsigned_integer (addr, len, val)

same feedback for this func as above

> +void
> +sim_size (int s)
> +{
> +}

once you convert to nrun.c (see earlier comments), you can drop this

> +static void
> +set_initial_gprs ()

funcs that take no args must use (void) in C and not ().

> +{
> +  int i;
> +  long space;
> +}

pointless func ?  delete it.

> +static void
> +interrupt ()
> +{
> +  // cpu.asregs.exception = SIGINT;
> +}

pointless func ?  delete it.

> +uint32_t cpu_pm_read(SIM_DESC sd, int dw, uint32_t ea)

static, and space before the (

> +{
> +  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
> +  address_word cia = CIA_GET (scpu);
> +  uint32_t r;
> +
> +  if ((ea & ~0x3ffff) != 0)
> +    {
> +      fprintf(stderr, "Illegal PM address %08x, pc %#x\n", ea, cpu.pc);
> +      exit(0);
> +    }

i'm not sure what this mask is trying to accomplish ... are you attemting to 
enforce some kind of address space ?  when you initialized the sim, you should 
have allocated the memory then, and let the sim itself take care of valid 
addresses.

> +void
> +sim_resume (sd, step, siggnal)

you should use sim-resume.o instead

then you'll need a sim_engine_run like so:
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);
    }
}

then declare a step_once func and put all the cpu-specific logc in there

> +int
> +sim_write (sd, addr, buffer, size)
>
> +int
> +sim_read (sd, addr, buffer, size)

i think once you switch to nrun.c, you won't need these anymore

> +int
> +sim_store_register (sd, rn, memory, length)
>
> +int
> +sim_fetch_register (sd, rn, memory, length)

you should create a helper func that does the rn<->pointer lookup so that you 
can use it here.  e.g. something like:
static unsigned_word
ft32_lookup_register (SIM_DESC sd, int nr)
{
  SIM_CPU *cpu = STATE_CPU (sd, 0);

  switch (nr)
    {
    case 0: return cpu->registers[0];
    ...
    default: return NULL;
    }
}

then the store/fetch funcs become much simpler

ideally you'd switch to sim-reg.o in your Makefile's SIM_OBJS ... that'll 
provide these entry points.  that would require also enabling sim-model.o 
& SIM_AC_OPTION_DEFAULT_MODEL support, but i don't think that'd be too hard.
if you look at bfin/machs.c and start at "sim_machs", i think you should be 
able to track it down easily enough.

> +int
> +sim_trace (sd)

once you switch to nrun.c, you can delete this

> +void
> +sim_stop_reason (sd, reason, sigrc)

add sim-reason.o to your SIM_OBJS to get this for free

> +int
> +sim_stop (sd)

add sim-stop.o to your SIM_OBJS to get this for free

> +void
> +sim_info (sd, verbose)

once you switch to nrun.c/SIM_NEW_COMMON_OBJS, you get this for free

> +SIM_DESC
> +sim_open (kind, cb, abfd, argv)

before this func, you should add:
/* 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 sd = sim_state_alloc (kind, cb);
> +  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);

i don't think you need this assert

after this alloc, you should do:
  /* 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;
    }

you'll have to add sim-cpu.o to your SIM_OMJS

> +  if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
> +    return 0;

you should call free_state in the error path here

after this section, you should call:
  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;
    }

> +  sim_do_command(sd," memory region 0x00000000,0x4000000") ;
> +  sim_do_command(sd," memory region 0xE0000000,0x10000") ;

instead, do:
  /* 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,0x4000000");
      sim_do_command(sd, "memory region 0xE0000000,0x10000");
    }

notice the style fixes too

then you'll want to do:
  /* 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;
    }

> +  myname = argv[0];

won't be needed after you drop sim_load (see below)

> +  callback = cb;

you shouldn't need this -- the call to sim_state_alloc above already assigned 
the callback

> +  if (kind == SIM_OPEN_STANDALONE)
> +    issue_messages = 1;

this variable looks pointless in this whole codebase -> delete

> +  set_initial_gprs ();	/* Reset the GPR registers.  */

you should move this to just before the return, and walk all cpus:
  /* CPU specific initialization.  */
  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
    {
      SIM_CPU *cpu = STATE_CPU (sd, i);
      set_initial_gprs (sd, cpu);
    }

> +  if (sim_config (sd) != SIM_RC_OK)
> +    {
> +      sim_module_uninstall (sd);

call your new free_state() helper instead

> +  if (sim_post_argv_init (sd) != SIM_RC_OK)
> +    {
> +      /* Uninstall the modules to avoid memory leaks,
> +	 file descriptor leaks, etc.  */
> +      sim_module_uninstall (sd);

delete the comment & call your new free_state() helper instead

> +void
> +sim_close (sd, quitting)
> +     SIM_DESC sd;
> +     int quitting;
> +{
> +  /* nothing to do */
> +}

you should do:
  sim_module_uninstall (sd);

> +static void
> +load_dtb (SIM_DESC sd, const char *filename)

unused -> delete

> +SIM_RC
> +sim_load (sd, prog, abfd, from_tty)

add sim-hload.o to your SIM_OBJS and you get this for free

> +SIM_RC
> +sim_create_inferior (sd, prog_bfd, argv, env)
> +{
> +  sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */

  SIM_CPU *cpu = STATE_CPU (sd, 0);

> +  /* Set the initial register set.  */
> +  l = issue_messages;
> +  issue_messages = 0;
> +  set_initial_gprs ();
> +  issue_messages = l;

you shouldn't need to do this.  you already handled this in sim_open.

> +  // printf("start address %#lx\n", bfd_get_start_address (prog_bfd));
> +  cpu.pc = bfd_get_start_address (prog_bfd);
> +  cpu.regs[31] = 0x00000;
> +  cpu.num_i = 0;
> +  cpu.cycles = 0;
> +  cpu.next_tick_cycle = 100000;

you should instead do:
  /* Set the PC.  */
  if (abfd != NULL)
    addr = bfd_get_start_address (abfd);
  else
    addr = 0;
  sim_pc_set (cpu, addr);

  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
    { 
      freeargv (STATE_PROG_ARGV (sd));
      STATE_PROG_ARGV (sd) = dupargv (argv);
    }

> +void
> +sim_kill (sd)

you shouldn't need this -> delete

looks like i should clean up the tree too

> +void
> +sim_do_command (sd, cmd)

once you switch to nrun.c/SIM_NEW_COMMON_OBJS, you get this for free

> +void
> +sim_set_callbacks (ptr)

once you switch to nrun.c, you can delete this

> --- /dev/null
> +++ b/sim/ft32/sim-main.h
>
> +#define SIM_HAVE_BIENDIAN

once you switch to nrun.c, you can delete this

> +  /* The following are internal simulator state variables: */
> +#define CIA_GET(CPU) ((CPU)->registers[PCIDX] + 0)
> +#define CIA_SET(CPU,CIA) ((CPU)->registers[PCIDX] = (CIA))

you should define these to CPU_PC_SET/CPU_PC_GET instead.  we should probably 
make that the default too so ports don't have to do it.

> +/* To keep this default simulator simple, and fast, we use a direct
> +   vector of registers. The internal simulator engine then uses
> +   manifests to access the correct slot. */

gnu style says to put two spaces after periods

that said, i have a hard time believing the speed thing here when your current 
store/fetch register functions are doing byte level memory accesses

> +  unsigned_word registers[19];

per above, you should probably create dedicated fields for each register.  this 
would also make it easier to debug assuming you have named registers like $pc 
and $fp and $sp.

> --- /dev/null
> +++ b/sim/ft32/sysdep.h

looks like you copied & pasted this from another port.  fairly certain you don't 
need or want any of it.  just delete the file entirely.
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* RE: [PATCH, FT32] gdb and sim support
       [not found] ` <ORIGINAL-RELEASE-1424557036-20150219070610.GI544@vapier>
@ 2015-02-23 10:40   ` James Bowman
  2015-02-24  4:52     ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-02-23 10:40 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

Thanks for the review. All is reworked here. There were so many items,
I will skip the things that I just fixed.  So now a list of notes/queries
on the remainder.

The reworked ChangeLog and patchset are below.

> > 2014-02-03  James Bowman  <james.bowman@ftdichip.com>
> > 
> >         * gdb/Makefile.in, gdb/configure.tgt: FT32 target added
> >         * sim/configure.tgt: FT32 target added
> >         * sim/configure: Regenerated
> >         * sim/ft32/configure: Regenerated
> >         * gdb/ft32-tdep.c,h: Support FT32
> >         * sim/ft32/*: FT32 simulator
> 
> notes:
>  - ChangeLog entries are split up across dirs

Do you mean I should not split? Organize in some other way?

> where's the testsuite man ? :)  it should be trivial to start one with .s
> files  -- just look at sim/testsuite/sim/.  otherwise there's no way to keep
> regressions from slipping in.

Good idea. We are currently running the gcc testsuite on the simulator
as our regression test.  Would it be OK if I defer, and add a sim
testsuite after this submit?

> > +/* Use an invalid address value as 'not available' marker.  */
> > +enum { REG_UNAVAIL = (CORE_ADDR) -1 };

This is actually the value -1 being cast. I have rewritten the line to
make this clearer.

> > #define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
> 
> i think usually we put insn constants/etc... into the corresponding 
> include/opcode/ header rather than pasting them inline.  this makes sharing 
> between opcodes, gdb, sim, etc... much easier.

Sounds good. I have submitted the relevant patch to binutils.  When it
comes through I will make the corresponding change in gdb.

> since you're a new port, you should start with SIM_AC_OPTION_WARNINGS enabled.  
> obviously that also means cleaning up all the warnings generated in the ft32/ 
> subdir once you do :).

Done - it now compiles without warnings. It would be nice to be able to
build it with warnings as errors.

> > --- /dev/null
> > +++ b/sim/ft32/interp.c
> 
> ideally you'd switch to sim-reg.o in your Makefile's SIM_OBJS ... that'll 
> provide these entry points.  that would require also enabling sim-model.o 
> & SIM_AC_OPTION_DEFAULT_MODEL support, but i don't think that'd be too hard.
> if you look at bfin/machs.c and start at "sim_machs", i think you should be 
> able to track it down easily enough.

This change is causing me some trouble.  OK to defer it until after the
main submit?

--

ChangeLog entry:

2015-02-23  James Bowman  <james.bowman@ftdichip.com>

	* gdb/Makefile.in, gdb/configure.tgt: FT32 target added.
	* include/gdb/sim-ft32.h: FT32 sim/gdb interface added.
	* sim/configure.tgt: FT32 target added.
	* sim/configure: Regenerated.
	* sim/ft32/configure: Regenerated.
	* gdb/ft32-tdep.c,h: Support FT32.
	* sim/ft32/*: FT32 simulator

From: James Bowman <james.bowman@ftdichip.com>
Date: Mon, 23 Feb 2015 18:31:18 +0800
Subject: [PATCH] FT32 Support

---
 gdb/Makefile.in        |    4 +-
 gdb/configure.tgt      |    5 +
 gdb/ft32-tdep.c        |  546 +++++++++++++++++++++++++++++++
 gdb/ft32-tdep.h        |   28 ++
 include/gdb/sim-ft32.h |   34 ++
 sim/configure.tgt      |    3 +
 sim/ft32/Makefile.in   |   49 +++
 sim/ft32/configure.ac  |   15 +
 sim/ft32/ft32-sim.h    |   39 +++
 sim/ft32/interp.c      |  831 ++++++++++++++++++++++++++++++++++++++++++++++++
 sim/ft32/sim-main.h    |   63 ++++
 11 files changed, 1616 insertions(+), 1 deletion(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h
 create mode 100644 include/gdb/sim-ft32.h
 create mode 100644 sim/ft32/Makefile.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

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 16e2f1c..62cae68 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -647,6 +647,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-hpux-tdep.o hppa-linux-tdep.o hppa-tdep.o \
@@ -945,7 +946,7 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h ia64-hpux-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1649,6 +1650,7 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
+	ft32-tdep.c \
 	glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c hppa-hpux-tdep.c hppa-hpux-nat.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 7fdd34e..26274bd 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -621,6 +621,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..f040f3b
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,546 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+#include "gdb/sim-ft32.h"
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address -1 as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
+
+struct ft32_frame_cache
+{
+  /* Base address.  */
+  CORE_ADDR base;
+  CORE_ADDR pc;
+  LONGEST framesize;
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  CORE_ADDR saved_sp;
+  bfd_boolean established;  /* Has the new frame been LINKed */
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+			 CORE_ADDR *pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+static const char * ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+			 const void *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4,
+					 len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+		       struct ft32_frame_cache *cache,
+		       struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+      return end_addr;
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH (inst))
+	{
+	  regnum = PUSH_REG (inst);
+	  cache->framesize += 4;
+	  cache->saved_regs[regnum] = cache->framesize;
+	  next_addr += 4;
+	}
+      else
+	break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+	cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  /* It is a LINK */
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK (inst))
+	{
+	  cache->established = 1;
+	  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+	    {
+	      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+		cache->saved_regs[regnum] += 4;
+	    }
+	  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+	  cache->saved_regs[FT32_FP_REGNUM] = 0;
+	  cache->framesize += LINK_SIZE (inst);
+	  next_addr += 4;
+	}
+    }
+
+  return next_addr;
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+      else
+	{
+	  /* Can't determine prologue from the symbol table, need to examine
+	     instructions.  */
+	  struct symtab_and_line sal;
+	  struct symbol *sym;
+	  struct ft32_frame_cache cache;
+	  CORE_ADDR plg_end;
+
+	  memset (&cache, 0, sizeof cache);
+
+	  plg_end = ft32_analyze_prologue (func_addr,
+					   func_end, &cache, gdbarch);
+	  /* Found a function.  */
+	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+	  /* Don't use line number debug info for assembly source files.  */
+	  if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
+	    {
+	      sal = find_pc_line (func_addr, 0);
+	      if (sal.end && sal.end < func_end)
+		{
+		  /* Found a line number, use it as end of
+		     prologue.  */
+		  return sal.end;
+		}
+	    }
+	  /* No useable line symbol.  Use result of prologue parsing method.  */
+	  return plg_end;
+	}
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return (CORE_ADDR) pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+			   void *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *valtype, struct regcache *regcache,
+		   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+	ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+	ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  cache->base = 0;
+  cache->saved_sp = 0;
+  cache->pc = 0;
+  cache->framesize = 0;
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+	cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+      return frame_unwind_got_memory (this_frame, regnum,
+				      0x800000 | cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..5c52480
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,28 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for FT32.  */
+};
+
+#endif /* FT32_TDEP_H */
diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
new file mode 100644
index 0000000..6602df3
--- /dev/null
+++ b/include/gdb/sim-ft32.h
@@ -0,0 +1,34 @@
+/* This file defines the interface between the FT32 simulator and GDB.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by FTDI.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* Register numbers of various important registers.  */
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
+  FT32_SP_REGNUM,               /* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_PC_REGNUM = 32           /* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33        /* 32 real registers + PC */
+
diff --git a/sim/configure.tgt b/sim/configure.tgt
index d112e72..443418e 100644
--- a/sim/configure.tgt
+++ b/sim/configure.tgt
@@ -111,6 +111,9 @@ case "${target}" in
    powerpc*-*-*)
        SIM_ARCH(ppc)
        ;;
+   ft32-*-*)
+       SIM_ARCH(ft32)
+       ;;
    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..322a6c9
--- /dev/null
+++ b/sim/ft32/Makefile.in
@@ -0,0 +1,49 @@
+#    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-resume.o	\
+  sim-signal.o	\
+  sim-stop.o	\
+  sim-trace.o	\
+  sim-utils.o	\
+  $(SIM_EXTRA_OBJS)
+
+SIM_RUN_OBJS = nrun.o
+
+## COMMON_POST_CONFIG_FRAG
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..d39fe7f
--- /dev/null
+++ b/sim/ft32/ft32-sim.h
@@ -0,0 +1,39 @@
+/* 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>
+
+struct ft32_cpu_state {
+  uint32_t regs[32];
+  uint32_t pc;
+  uint8_t pm[262144];
+  uint8_t ram[65536];
+  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..040c6c7
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,831 @@
+/* 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-base.h"
+
+#include "opcode/ft32.h"
+
+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;
+    }
+}
+
+static int tracing = 0;
+
+static uint32_t
+cpu_pm_read (SIM_DESC sd, int dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint32_t r;
+
+  switch (dw)
+  {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+  }
+  r = sim_core_read_aligned_1 (cpu, cia, read_map, ea);
+  if (1 <= dw)
+    {
+      r += (sim_core_read_aligned_1 (cpu, cia, read_map, ea+1) << 8);
+      if (2 <= dw)
+	{
+	  r += (sim_core_read_aligned_1 (cpu, cia, read_map, ea+2) << 16);
+	  r += (sim_core_read_aligned_1 (cpu, cia, read_map, ea+3) << 24);
+	}
+    }
+  return r;
+}
+
+static uint32_t safe_addr (uint32_t dw, uint32_t ea)
+{
+  ea &= 0x1ffff;
+  switch (dw)
+  {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+  }
+  return ea;
+}
+
+static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  uint32_t r;
+
+  ea = safe_addr (dw, ea);
+  if ((ea & ~0xffff))
+    {
+      switch (ea)
+      {
+      case 0x10000:
+      case 0x10020:
+	return getchar ();
+      case 0x10024:
+	return 1;
+      case 0x1fff4:
+	return (uint32_t)(cpu->state.cycles / 100);
+      default:
+	sim_io_eprintf (sd, "Illegal IO read address %08x, pc %#x\n", ea, cpu->state.pc);
+	exit (0);
+      }
+    }
+  r = cpu->state.ram[ea];
+  if (1 <= dw)
+    {
+      r += (cpu->state.ram[ea + 1] << 8);
+      if (2 <= dw)
+	{
+	  r += cpu->state.ram[ea + 2] << 16;
+	  r += cpu->state.ram[ea + 3] << 24;
+	}
+    }
+  return r;
+}
+
+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 = safe_addr (dw, ea);
+  if (ea & 0x10000)
+    {
+      switch (ea)
+      {
+      case 0x10000:
+	putchar (d & 0xff);
+	break;
+      case 0x1fc80:
+	cpu->state.pm_unlock = (d == 0x1337f7d1);
+	break;
+      case 0x1fc84:
+	cpu->state.pm_addr = d;
+	break;
+      case 0x1fc88:
+	{
+	  address_word cia = CIA_GET (cpu);
+	  sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 0, d & 0xff);
+	  sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 1, (d >> 8) & 0xff);
+	  sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 2, (d >> 16) & 0xff);
+	  sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 3, (d >> 24) & 0xff);
+	}
+	break;
+      case 0x10024:
+	break;
+      case 0x1fffc:
+	/* Normal exit */
+	sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
+	break;
+      case 0x1fff8:
+      case 0x19470:
+	{
+	  uint32_t d16 = (d & 0xffff) | ((d & 0xffff) << 16);
+	  uint32_t d8 = (d16 & 0x00ff00ff) | ((d16 & 0x00ff00ff) << 8);
+	  switch (dw)
+	  {
+	  case 0: d = d8; break;
+	  case 1: d = d16; break;
+	  }
+	  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
+    {
+      cpu->state.ram[ea] = d & 0xff;
+      if (1 <= dw)
+	{
+	  cpu->state.ram[ea + 1] = (d >> 8) & 0xff;
+	  if (2 <= dw)
+	    {
+	      cpu->state.ram[ea + 2] = (d >> 16) & 0xff;
+	      cpu->state.ram[ea + 3] = (d >> 24) & 0xff;
+	    }
+	}
+    }
+}
+
+static void ft32_push (SIM_DESC sd, uint32_t v)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  cpu->state.regs[31] -= 4;
+  cpu->state.regs[31] &= 0xffff;
+  cpu_mem_write (sd, 2, cpu->state.regs[31], 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[31]);
+  cpu->state.regs[31] += 4;
+  cpu->state.regs[31] &= 0xffff;
+  return r;
+}
+
+static int nunsigned (int siz, int bits)
+{
+  int mask = (1L << siz) - 1;
+  return bits & mask;
+}
+
+static int nsigned (int siz, int bits)
+{
+  int shift = (sizeof (int) * 8) - siz;
+  return (bits << shift) >> shift;
+}
+
+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);
+}
+
+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);
+}
+
+static uint32_t ror (uint32_t n, uint32_t b)
+{
+  b &= 31;
+  return (n >> b) | (n << (32 - b));
+}
+
+static uint32_t bins (uint32_t d, uint32_t f, uint32_t len, uint32_t pos)
+{
+  uint32_t mask = ((1 << len) - 1) << pos;
+  return (d & ~mask) | ((f << pos) & mask);
+}
+
+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);
+  const char *ram_char = (const char *)cpu->state.ram;
+
+#define ILLEGAL_INSTRUCTION() \
+  sim_engine_halt (sd, cpu, NULL, insnpc, sim_signalled, SIM_SIGILL)
+
+    {
+      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 = cpu_pm_read (sd, 2, cpu->state.pc);
+      cpu->state.cycles += 1;
+
+      /* Handle "call 8" (which is FT32's "break" equivalent) here */
+      if (inst == 0x00340002)
+	{
+	  goto escape;
+	}
+      dw   =              (inst >> FT32_FLD_DW_BIT) & ((1U << FT32_FLD_DW_SIZ) - 1);
+      cb   =              (inst >> FT32_FLD_CB_BIT) & ((1U << FT32_FLD_CB_SIZ) - 1);
+      r_d  =              (inst >> FT32_FLD_R_D_BIT) & ((1U << FT32_FLD_R_D_SIZ) - 1);
+      cr   =              (inst >> FT32_FLD_CR_BIT) & ((1U << FT32_FLD_CR_SIZ) - 1);
+      cv   =              (inst >> FT32_FLD_CV_BIT) & ((1U << FT32_FLD_CV_SIZ) - 1);
+      bt   =              (inst >> FT32_FLD_BT_BIT) & ((1U << FT32_FLD_BT_SIZ) - 1);
+      r_1 =               (inst >> FT32_FLD_R_1_BIT) & ((1U << FT32_FLD_R_1_SIZ) - 1);
+      rimm =              (inst >> FT32_FLD_RIMM_BIT) & ((1U << FT32_FLD_RIMM_SIZ) - 1);
+      r_2  =              (inst >> FT32_FLD_R_2_BIT) & ((1U << FT32_FLD_R_2_SIZ) - 1);
+      k20  = nsigned (20, (inst >> FT32_FLD_K20_BIT) & ((1U << FT32_FLD_K20_SIZ) - 1));
+      pa   =              (inst >> FT32_FLD_PA_BIT) & ((1U << FT32_FLD_PA_SIZ) - 1);
+      aa   =              (inst >> FT32_FLD_AA_BIT) & ((1U << FT32_FLD_AA_SIZ) - 1);
+      k16  =              (inst >> FT32_FLD_K16_BIT) & ((1U << FT32_FLD_K16_SIZ) - 1);
+      k8   = nsigned (8,  (inst >> FT32_FLD_K8_BIT) & ((1U << FT32_FLD_K8_SIZ) - 1));
+      al   =              (inst >> FT32_FLD_AL_BIT) & ((1U << FT32_FLD_AL_SIZ) - 1);
+
+      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 takeit = (cr == 3) || ((1 & (cpu->state.regs[28 + cr] >> cb)) == cv);
+	    if (takeit)
+	      {
+		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_INSTRUCTION ();
+	    }
+	    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.cycles += 1;
+	  cpu->state.regs[r_d] = cpu_pm_read (sd, dw, pa << 2);
+	  break;
+
+	case FT32_PAT_LPMI:
+	  cpu->state.cycles += 1;
+	  cpu->state.regs[r_d] = cpu_pm_read (sd, dw, cpu->state.regs[r_1] + k8);
+	  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.cycles += 1;
+	  cpu->state.regs[r_d] = cpu_mem_read (sd, dw, aa);
+	  break;
+
+	case FT32_PAT_LDI:
+	  cpu->state.cycles += 1;
+	  cpu->state.regs[r_d] = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+	  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;
+	  }
+	  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;
+	  }
+	  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[31];
+	  cpu->state.regs[31] -= k16;
+	  cpu->state.regs[31] &= 0xffff;
+	  break;
+
+	case FT32_PAT_UNLINK:
+	  cpu->state.regs[31] = cpu->state.regs[r_d];
+	  cpu->state.regs[31] &= 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: cpu->state.regs[r_d] = strcmp (ram_char + r_1v, ram_char + rimmv); break;
+	  case 0x5: memcpy ((char*)(cpu->state.ram) + cpu->state.regs[r_d], ram_char + r_1v, rimmv); break;
+	  case 0x6: cpu->state.regs[r_d] = strlen (ram_char + r_1v); break;
+	  case 0x7: memset ((char *)(cpu->state.ram) + cpu->state.regs[r_d], r_1v, rimmv); 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: {
+	    const char *s = stpcpy ((char*)(cpu->state.ram) + cpu->state.regs[r_d], ram_char + r_1v);
+	    cpu->state.regs[r_d] = (uint32_t)(s - (const char*)(cpu->state.ram + cpu->state.regs[r_d]));
+	    break;
+	  }
+	  case 0xe:
+	    {
+	      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_INSTRUCTION ();
+	  }
+	  break;
+
+	default:
+	  sim_io_eprintf (sd, "Unhandled pattern %d at %08x\n", upper, insnpc);
+	  ILLEGAL_INSTRUCTION ();
+      }
+      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);
+
+  if (addr < 0x800000)
+    {
+      sim_core_write_buffer (sd, cpu, write_map, buffer, addr, size);
+    }
+    else
+    {
+      int i;
+
+      addr -= 0x800000;
+      for (i = 0; i < size; i++)
+	cpu_mem_write (sd, 0, addr + i, buffer[i]);
+    }
+  return size;
+}
+
+int
+sim_read (SIM_DESC sd,
+	  SIM_ADDR addr,
+	  unsigned char *buffer,
+	  int size)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  if (addr < 0x800000)
+    {
+      sim_core_read_buffer (sd, cpu, read_map, buffer, addr, size);
+    }
+    else
+    {
+      int i;
+
+      addr -= 0x800000;
+      for (i = 0; i < size; i++)
+	buffer[i] = cpu_mem_read (sd, 0, addr + i);
+    }
+
+  return size;
+}
+
+static uint32_t*
+ft32_lookup_register (SIM_DESC sd, int nr)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  /* Handle the register number translation here.
+   * Sim registers are, duh, 0-31.
+   * Other tools (gcc, gdb) uses:
+   * 0 - fp
+   * 1 - sp
+   * 2 - r0
+   * 31 - cc
+   */
+
+  switch (nr)
+  {
+  case 0:
+    return &cpu->state.regs[29];
+    break;
+  case 1:
+    return &cpu->state.regs[31];
+    break;
+  case 31:
+    return &cpu->state.regs[30];
+    break;
+  case 32:
+    return &cpu->state.pc;
+    break;
+  default:
+    return &cpu->state.regs[nr - 2];
+  }
+}
+
+int
+sim_store_register (SIM_DESC sd,
+		    int rn,
+		    unsigned char *memory,
+		    int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	*ft32_lookup_register (sd, rn) = ft32_extract_unsigned_integer (memory, 4);
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+int
+sim_fetch_register (SIM_DESC sd,
+		    int rn,
+		    unsigned char *memory,
+		    int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	ft32_store_unsigned_integer (memory, 4, *ft32_lookup_register (sd, rn));
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+/* 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;
+  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;
+    }
+
+  /* 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,0x4000000");
+      sim_do_command (sd, "memory region 0xE0000000,0x10000");
+    }
+
+  /* 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;
+    }
+
+  sd->base.prog_argv = argv + 1;
+
+  /* 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;
+    }
+
+  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[31] = 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..7b011e0
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,63 @@
+/* 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"
+
+#define PCIDX 17
+
+#include "ft32-sim.h"
+
+struct _sim_cpu {
+
+  /* The following are internal simulator state variables: */
+#define CIA_GET(CPU) ((CPU)->registers[PCIDX] + 0)
+#define CIA_SET(CPU,CIA) ((CPU)->registers[PCIDX] = (CIA))
+
+/* To keep this default simulator simple, and fast, we use a direct
+   vector of registers.  The internal simulator engine then uses
+   manifests to access the correct slot. */
+
+  struct ft32_cpu_state state;
+  unsigned_word registers[33];
+
+  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
-- 
1.7.9.5

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

* Re: [PATCH, FT32] gdb and sim support
  2015-02-23 10:40   ` James Bowman
@ 2015-02-24  4:52     ` Mike Frysinger
  2015-03-10 16:56       ` James Bowman
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-02-24  4:52 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 2745 bytes --]

On 23 Feb 2015 10:40, James Bowman wrote:
> > > 2014-02-03  James Bowman  <james.bowman@ftdichip.com>
> > > 
> > >         * gdb/Makefile.in, gdb/configure.tgt: FT32 target added
> > >         * sim/configure.tgt: FT32 target added
> > >         * sim/configure: Regenerated
> > >         * sim/ft32/configure: Regenerated
> > >         * gdb/ft32-tdep.c,h: Support FT32
> > >         * sim/ft32/*: FT32 simulator
> > 
> > notes:
> >  - ChangeLog entries are split up across dirs
> 
> Do you mean I should not split? Organize in some other way?

see my patch here as an example:
https://sourceware.org/ml/gdb-patches/2014-03/msg00173.html

> > where's the testsuite man ? :)  it should be trivial to start one with .s
> > files  -- just look at sim/testsuite/sim/.  otherwise there's no way to keep
> > regressions from slipping in.
> 
> Good idea. We are currently running the gcc testsuite on the simulator
> as our regression test.  Would it be OK if I defer, and add a sim
> testsuite after this submit?

that's what everyone says ;).  the trouble with having no sim testsuite is that 
people (like me) only use the sim testsuite to verify common/arch changes don't 
break things.  you don't need a comphrensive one, just one or two basic things.  
that way we at least know the sim isn't completely hosed.

> > > +/* Use an invalid address value as 'not available' marker.  */
> > > +enum { REG_UNAVAIL = (CORE_ADDR) -1 };
> 
> This is actually the value -1 being cast. I have rewritten the line to
> make this clearer.

ah sorry about that

> > since you're a new port, you should start with SIM_AC_OPTION_WARNINGS enabled.  
> > obviously that also means cleaning up all the warnings generated in the ft32/ 
> > subdir once you do :).
> 
> Done - it now compiles without warnings. It would be nice to be able to
> build it with warnings as errors.

the functionality is in place, but it's disabled atm because the sim code has a 
lot of legacy that needs cleaning.  although might be good to bite that bullet 
now and uncomment the lines in common/acinclude.m4 that disables it.

> > > --- /dev/null
> > > +++ b/sim/ft32/interp.c
> > 
> > ideally you'd switch to sim-reg.o in your Makefile's SIM_OBJS ... that'll 
> > provide these entry points.  that would require also enabling sim-model.o 
> > & SIM_AC_OPTION_DEFAULT_MODEL support, but i don't think that'd be too hard.
> > if you look at bfin/machs.c and start at "sim_machs", i think you should be 
> > able to track it down easily enough.
> 
> This change is causing me some trouble.  OK to defer it until after the
> main submit?

mach/model support can wait

i'll follow up once i review the actual code ;)
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* RE: [PATCH, FT32] gdb and sim support
  2015-02-24  4:52     ` Mike Frysinger
@ 2015-03-10 16:56       ` James Bowman
  2015-03-16  8:25         ` Mike Frysinger
                           ` (2 more replies)
  0 siblings, 3 replies; 30+ messages in thread
From: James Bowman @ 2015-03-10 16:56 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

Just pinging on this.

Attached is the FT32 patchset, updated for the current tree.
Also new in this patchset is a testsuite for the FT32 simulator.

--
James Bowman
FTDI Open Source Liaison

FT32 is a new high performance 32-bit RISC core developed by FTDI for embedded applications.

Support for FT32 has already been added to binutils. This patch adds gdb and sim support.

Please can someone review it, and if appropriate commit it, as I do not have write access to the tree.

The FSF have acknowledged receipt of FTDI's copyright assignment papers.

Thanks very much. ChangeLog entry:

gdb/:
2015-03-10  James Bowman  <james.bowman@ftdichip.com>

	* Mainfile.in: Add FT32 entries.
	* configure.tgt: Add FT32 entry.
	* ft32-tdep.c,h: New file, FT32 target-dependent code.

include/gdb/:
2015-03-10  James Bowman  <james.bowman@ftdichip.com>

	* sim-ft32.h: New file, FT32 simulator/GDB interface.

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

	* configure.tgt: Add FT32 entry.

sim/ft32/:
2015-03-10  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-10  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: Tue, 10 Mar 2015 09:29:36 -0700
Subject: [PATCH] FT32 Support

---
 gdb/Makefile.in                      |    4 +-
 gdb/configure.tgt                    |    5 +
 gdb/ft32-tdep.c                      |  546 +++++++++++++++++++++
 gdb/ft32-tdep.h                      |   28 ++
 include/gdb/sim-ft32.h               |   34 ++
 sim/configure.tgt                    |    4 +
 sim/ft32/Makefile.in                 |   49 ++
 sim/ft32/config.in                   |  154 ++++++
 sim/ft32/configure.ac                |   15 +
 sim/ft32/ft32-sim.h                  |   39 ++
 sim/ft32/interp.c                    |  831 ++++++++++++++++++++++++++++++++
 sim/ft32/sim-main.h                  |   63 +++
 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 +++
 16 files changed, 2716 insertions(+), 1 deletion(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h
 create mode 100644 include/gdb/sim-ft32.h
 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

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8c2a4de..bc9eb79 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -651,6 +651,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-hpux-tdep.o hppa-linux-tdep.o hppa-tdep.o \
@@ -948,7 +949,7 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h ia64-hpux-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1652,6 +1653,7 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
+	ft32-tdep.c \
 	glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c hppa-hpux-tdep.c hppa-hpux-nat.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 7fdd34e..26274bd 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -621,6 +621,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..a18db8f
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,546 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+#include "gdb/sim-ft32.h"
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address -1 as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
+
+struct ft32_frame_cache
+{
+  /* Base address.  */
+  CORE_ADDR base;
+  CORE_ADDR pc;
+  LONGEST framesize;
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  CORE_ADDR saved_sp;
+  bfd_boolean established;  /* Has the new frame been LINKed */
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+                         CORE_ADDR *pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+static const char * ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+                         const void *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4,
+                                         len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+                       struct ft32_frame_cache *cache,
+                       struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+      return end_addr;
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH (inst))
+        {
+          regnum = PUSH_REG (inst);
+          cache->framesize += 4;
+          cache->saved_regs[regnum] = cache->framesize;
+          next_addr += 4;
+        }
+      else
+        break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+        cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  /* It is a LINK */
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK (inst))
+        {
+          cache->established = 1;
+          for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+            {
+              if (cache->saved_regs[regnum] != REG_UNAVAIL)
+                cache->saved_regs[regnum] += 4;
+            }
+          cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+          cache->saved_regs[FT32_FP_REGNUM] = 0;
+          cache->framesize += LINK_SIZE (inst);
+          next_addr += 4;
+        }
+    }
+
+  return next_addr;
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+        = skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+        return max (pc, post_prologue_pc);
+      else
+        {
+          /* Can't determine prologue from the symbol table, need to examine
+             instructions.  */
+          struct symtab_and_line sal;
+          struct symbol *sym;
+          struct ft32_frame_cache cache;
+          CORE_ADDR plg_end;
+
+          memset (&cache, 0, sizeof cache);
+
+          plg_end = ft32_analyze_prologue (func_addr,
+                                           func_end, &cache, gdbarch);
+          /* Found a function.  */
+          sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+          /* Don't use line number debug info for assembly source files.  */
+          if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
+            {
+              sal = find_pc_line (func_addr, 0);
+              if (sal.end && sal.end < func_end)
+                {
+                  /* Found a line number, use it as end of
+                     prologue.  */
+                  return sal.end;
+                }
+            }
+          /* No useable line symbol.  Use result of prologue parsing method.  */
+          return plg_end;
+        }
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return (CORE_ADDR) pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+                           void *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+                   struct type *valtype, struct regcache *regcache,
+                   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+        ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+        ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  cache->base = 0;
+  cache->saved_sp = 0;
+  cache->pc = 0;
+  cache->framesize = 0;
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+        cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+                    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+                                                     this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+                          void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+                                                     this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+      return frame_unwind_got_memory (this_frame, regnum,
+                                      0x800000 | cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+                                                     this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..5c52480
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,28 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for FT32.  */
+};
+
+#endif /* FT32_TDEP_H */
diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
new file mode 100644
index 0000000..6602df3
--- /dev/null
+++ b/include/gdb/sim-ft32.h
@@ -0,0 +1,34 @@
+/* This file defines the interface between the FT32 simulator and GDB.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by FTDI.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* Register numbers of various important registers.  */
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
+  FT32_SP_REGNUM,               /* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_PC_REGNUM = 32           /* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33        /* 32 real registers + PC */
+
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..322a6c9
--- /dev/null
+++ b/sim/ft32/Makefile.in
@@ -0,0 +1,49 @@
+#    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-resume.o	\
+  sim-signal.o	\
+  sim-stop.o	\
+  sim-trace.o	\
+  sim-utils.o	\
+  $(SIM_EXTRA_OBJS)
+
+SIM_RUN_OBJS = nrun.o
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/ft32/config.in b/sim/ft32/config.in
new file mode 100644
index 0000000..8de933c
--- /dev/null
+++ b/sim/ft32/config.in
@@ -0,0 +1,154 @@
+/* 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 <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the `__setfpucw' function. */
+#undef HAVE___SETFPUCW
+
+/* 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
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..d39fe7f
--- /dev/null
+++ b/sim/ft32/ft32-sim.h
@@ -0,0 +1,39 @@
+/* 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>
+
+struct ft32_cpu_state {
+  uint32_t regs[32];
+  uint32_t pc;
+  uint8_t pm[262144];
+  uint8_t ram[65536];
+  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..1603b5c
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,831 @@
+/* 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-base.h"
+
+#include "opcode/ft32.h"
+
+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;
+    }
+}
+
+static int tracing = 0;
+
+static uint32_t
+cpu_pm_read (SIM_DESC sd, int dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint32_t r;
+
+  switch (dw)
+  {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+  }
+  r = sim_core_read_aligned_1 (cpu, cia, read_map, ea);
+  if (1 <= dw)
+    {
+      r += (sim_core_read_aligned_1 (cpu, cia, read_map, ea+1) << 8);
+      if (2 <= dw)
+        {
+          r += (sim_core_read_aligned_1 (cpu, cia, read_map, ea+2) << 16);
+          r += (sim_core_read_aligned_1 (cpu, cia, read_map, ea+3) << 24);
+        }
+    }
+  return r;
+}
+
+static uint32_t safe_addr (uint32_t dw, uint32_t ea)
+{
+  ea &= 0x1ffff;
+  switch (dw)
+  {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+  }
+  return ea;
+}
+
+static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  uint32_t r;
+
+  ea = safe_addr (dw, ea);
+  if ((ea & ~0xffff))
+    {
+      switch (ea)
+      {
+      case 0x10000:
+      case 0x10020:
+        return getchar ();
+      case 0x10024:
+        return 1;
+      case 0x1fff4:
+        return (uint32_t)(cpu->state.cycles / 100);
+      default:
+        sim_io_eprintf (sd, "Illegal IO read address %08x, pc %#x\n", ea, cpu->state.pc);
+        exit (0);
+      }
+    }
+  r = cpu->state.ram[ea];
+  if (1 <= dw)
+    {
+      r += (cpu->state.ram[ea + 1] << 8);
+      if (2 <= dw)
+        {
+          r += cpu->state.ram[ea + 2] << 16;
+          r += cpu->state.ram[ea + 3] << 24;
+        }
+    }
+  return r;
+}
+
+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 = safe_addr (dw, ea);
+  if (ea & 0x10000)
+    {
+      switch (ea)
+      {
+      case 0x10000:
+        putchar (d & 0xff);
+        break;
+      case 0x1fc80:
+        cpu->state.pm_unlock = (d == 0x1337f7d1);
+        break;
+      case 0x1fc84:
+        cpu->state.pm_addr = d;
+        break;
+      case 0x1fc88:
+        {
+          address_word cia = CIA_GET (cpu);
+          sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 0, d & 0xff);
+          sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 1, (d >> 8) & 0xff);
+          sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 2, (d >> 16) & 0xff);
+          sim_core_write_aligned_1 (cpu, cia, read_map, cpu->state.pm_addr + 3, (d >> 24) & 0xff);
+        }
+        break;
+      case 0x10024:
+        break;
+      case 0x1fffc:
+        /* Normal exit */
+        sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
+        break;
+      case 0x1fff8:
+      case 0x19470:
+        {
+          uint32_t d16 = (d & 0xffff) | ((d & 0xffff) << 16);
+          uint32_t d8 = (d16 & 0x00ff00ff) | ((d16 & 0x00ff00ff) << 8);
+          switch (dw)
+          {
+          case 0: d = d8; break;
+          case 1: d = d16; break;
+          }
+          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
+    {
+      cpu->state.ram[ea] = d & 0xff;
+      if (1 <= dw)
+        {
+          cpu->state.ram[ea + 1] = (d >> 8) & 0xff;
+          if (2 <= dw)
+            {
+              cpu->state.ram[ea + 2] = (d >> 16) & 0xff;
+              cpu->state.ram[ea + 3] = (d >> 24) & 0xff;
+            }
+        }
+    }
+}
+
+static void ft32_push (SIM_DESC sd, uint32_t v)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  cpu->state.regs[31] -= 4;
+  cpu->state.regs[31] &= 0xffff;
+  cpu_mem_write (sd, 2, cpu->state.regs[31], 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[31]);
+  cpu->state.regs[31] += 4;
+  cpu->state.regs[31] &= 0xffff;
+  return r;
+}
+
+static int nunsigned (int siz, int bits)
+{
+  int mask = (1L << siz) - 1;
+  return bits & mask;
+}
+
+static int nsigned (int siz, int bits)
+{
+  int shift = (sizeof (int) * 8) - siz;
+  return (bits << shift) >> shift;
+}
+
+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);
+}
+
+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);
+}
+
+static uint32_t ror (uint32_t n, uint32_t b)
+{
+  b &= 31;
+  return (n >> b) | (n << (32 - b));
+}
+
+static uint32_t bins (uint32_t d, uint32_t f, uint32_t len, uint32_t pos)
+{
+  uint32_t mask = ((1 << len) - 1) << pos;
+  return (d & ~mask) | ((f << pos) & mask);
+}
+
+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);
+  const char *ram_char = (const char *)cpu->state.ram;
+
+#define ILLEGAL_INSTRUCTION() \
+  sim_engine_halt (sd, cpu, NULL, insnpc, sim_signalled, SIM_SIGILL)
+
+    {
+      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 = cpu_pm_read (sd, 2, cpu->state.pc);
+      cpu->state.cycles += 1;
+
+      /* Handle "call 8" (which is FT32's "break" equivalent) here */
+      if (inst == 0x00340002)
+        {
+          goto escape;
+        }
+      dw   =              (inst >> FT32_FLD_DW_BIT) & ((1U << FT32_FLD_DW_SIZ) - 1);
+      cb   =              (inst >> FT32_FLD_CB_BIT) & ((1U << FT32_FLD_CB_SIZ) - 1);
+      r_d  =              (inst >> FT32_FLD_R_D_BIT) & ((1U << FT32_FLD_R_D_SIZ) - 1);
+      cr   =              (inst >> FT32_FLD_CR_BIT) & ((1U << FT32_FLD_CR_SIZ) - 1);
+      cv   =              (inst >> FT32_FLD_CV_BIT) & ((1U << FT32_FLD_CV_SIZ) - 1);
+      bt   =              (inst >> FT32_FLD_BT_BIT) & ((1U << FT32_FLD_BT_SIZ) - 1);
+      r_1 =               (inst >> FT32_FLD_R_1_BIT) & ((1U << FT32_FLD_R_1_SIZ) - 1);
+      rimm =              (inst >> FT32_FLD_RIMM_BIT) & ((1U << FT32_FLD_RIMM_SIZ) - 1);
+      r_2  =              (inst >> FT32_FLD_R_2_BIT) & ((1U << FT32_FLD_R_2_SIZ) - 1);
+      k20  = nsigned (20, (inst >> FT32_FLD_K20_BIT) & ((1U << FT32_FLD_K20_SIZ) - 1));
+      pa   =              (inst >> FT32_FLD_PA_BIT) & ((1U << FT32_FLD_PA_SIZ) - 1);
+      aa   =              (inst >> FT32_FLD_AA_BIT) & ((1U << FT32_FLD_AA_SIZ) - 1);
+      k16  =              (inst >> FT32_FLD_K16_BIT) & ((1U << FT32_FLD_K16_SIZ) - 1);
+      k8   = nsigned (8,  (inst >> FT32_FLD_K8_BIT) & ((1U << FT32_FLD_K8_SIZ) - 1));
+      al   =              (inst >> FT32_FLD_AL_BIT) & ((1U << FT32_FLD_AL_SIZ) - 1);
+
+      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 takeit = (cr == 3) || ((1 & (cpu->state.regs[28 + cr] >> cb)) == cv);
+            if (takeit)
+              {
+                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_INSTRUCTION ();
+            }
+            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.cycles += 1;
+          cpu->state.regs[r_d] = cpu_pm_read (sd, dw, pa << 2);
+          break;
+
+        case FT32_PAT_LPMI:
+          cpu->state.cycles += 1;
+          cpu->state.regs[r_d] = cpu_pm_read (sd, dw, cpu->state.regs[r_1] + k8);
+          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.cycles += 1;
+          cpu->state.regs[r_d] = cpu_mem_read (sd, dw, aa);
+          break;
+
+        case FT32_PAT_LDI:
+          cpu->state.cycles += 1;
+          cpu->state.regs[r_d] = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+          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;
+          }
+          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;
+          }
+          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[31];
+          cpu->state.regs[31] -= k16;
+          cpu->state.regs[31] &= 0xffff;
+          break;
+
+        case FT32_PAT_UNLINK:
+          cpu->state.regs[31] = cpu->state.regs[r_d];
+          cpu->state.regs[31] &= 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: cpu->state.regs[r_d] = strcmp (ram_char + r_1v, ram_char + rimmv); break;
+          case 0x5: memcpy ((char*)(cpu->state.ram) + cpu->state.regs[r_d], ram_char + r_1v, rimmv); break;
+          case 0x6: cpu->state.regs[r_d] = strlen (ram_char + r_1v); break;
+          case 0x7: memset ((char *)(cpu->state.ram) + cpu->state.regs[r_d], r_1v, rimmv); 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: {
+            const char *s = stpcpy ((char*)(cpu->state.ram) + cpu->state.regs[r_d], ram_char + r_1v);
+            cpu->state.regs[r_d] = (uint32_t)(s - (const char*)(cpu->state.ram + cpu->state.regs[r_d]));
+            break;
+          }
+          case 0xe:
+            {
+              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_INSTRUCTION ();
+          }
+          break;
+
+        default:
+          sim_io_eprintf (sd, "Unhandled pattern %d at %08x\n", upper, insnpc);
+          ILLEGAL_INSTRUCTION ();
+      }
+      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);
+
+  if (addr < 0x800000)
+    {
+      sim_core_write_buffer (sd, cpu, write_map, buffer, addr, size);
+    }
+    else
+    {
+      int i;
+
+      addr -= 0x800000;
+      for (i = 0; i < size; i++)
+        cpu_mem_write (sd, 0, addr + i, buffer[i]);
+    }
+  return size;
+}
+
+int
+sim_read (SIM_DESC sd,
+          SIM_ADDR addr,
+          unsigned char *buffer,
+          int size)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  if (addr < 0x800000)
+    {
+      sim_core_read_buffer (sd, cpu, read_map, buffer, addr, size);
+    }
+    else
+    {
+      int i;
+
+      addr -= 0x800000;
+      for (i = 0; i < size; i++)
+        buffer[i] = cpu_mem_read (sd, 0, addr + i);
+    }
+
+  return size;
+}
+
+static uint32_t*
+ft32_lookup_register (SIM_DESC sd, int nr)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  /* Handle the register number translation here.
+   * Sim registers are, duh, 0-31.
+   * Other tools (gcc, gdb) uses:
+   * 0 - fp
+   * 1 - sp
+   * 2 - r0
+   * 31 - cc
+   */
+
+  switch (nr)
+  {
+  case 0:
+    return &cpu->state.regs[29];
+    break;
+  case 1:
+    return &cpu->state.regs[31];
+    break;
+  case 31:
+    return &cpu->state.regs[30];
+    break;
+  case 32:
+    return &cpu->state.pc;
+    break;
+  default:
+    return &cpu->state.regs[nr - 2];
+  }
+}
+
+int
+sim_store_register (SIM_DESC sd,
+                    int rn,
+                    unsigned char *memory,
+                    int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+        *ft32_lookup_register (sd, rn) = ft32_extract_unsigned_integer (memory, 4);
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+int
+sim_fetch_register (SIM_DESC sd,
+                    int rn,
+                    unsigned char *memory,
+                    int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+        ft32_store_unsigned_integer (memory, 4, *ft32_lookup_register (sd, rn));
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+/* 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;
+  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;
+    }
+
+  /* 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,0x4000000");
+      sim_do_command (sd, "memory region 0xE0000000,0x10000");
+    }
+
+  /* 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;
+    }
+
+  sd->base.prog_argv = argv + 1;
+
+  /* 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;
+    }
+
+  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[31] = 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..7b011e0
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,63 @@
+/* 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"
+
+#define PCIDX 17
+
+#include "ft32-sim.h"
+
+struct _sim_cpu {
+
+  /* The following are internal simulator state variables: */
+#define CIA_GET(CPU) ((CPU)->registers[PCIDX] + 0)
+#define CIA_SET(CPU,CIA) ((CPU)->registers[PCIDX] = (CIA))
+
+/* To keep this default simulator simple, and fast, we use a direct
+   vector of registers.  The internal simulator engine then uses
+   manifests to access the correct slot. */
+
+  struct ft32_cpu_state state;
+  unsigned_word registers[33];
+
+  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..8b9e9c8
--- /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..af0997b
--- /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
-- 
1.7.9.5

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-10 16:56       ` James Bowman
@ 2015-03-16  8:25         ` Mike Frysinger
  2015-03-19 15:26           ` James Bowman
  2015-03-16 16:38         ` Mike Frysinger
  2015-03-17 17:36         ` Joel Brobecker
  2 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-16  8:25 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 9823 bytes --]

On 10 Mar 2015 16:56, James Bowman wrote:
> gdb/:
> 2015-03-10  James Bowman  <james.bowman@ftdichip.com>
> 
> 	* Mainfile.in: Add FT32 entries.
> 	* configure.tgt: Add FT32 entry.
> 	* ft32-tdep.c,h: New file, FT32 target-dependent code.

the file names should be spelled out

someone else will have to approve the gdb/ parts

> --- /dev/null
> +++ b/gdb/ft32-tdep.c
>
> +struct ft32_frame_cache
> +{
> +  /* Base address.  */
> +  CORE_ADDR base;
> +  CORE_ADDR pc;
> +  LONGEST framesize;
> +  CORE_ADDR saved_regs[FT32_NUM_REGS];
> +  CORE_ADDR saved_sp;
> +  bfd_boolean established;  /* Has the new frame been LINKed */

comment needs to be tweaked -- punctuation and two spaces at the end

i'd personally prefer it to be above the member rather than inline, but that's 
me ...

> +static const char * ft32_register_names[] =

one more const:
static const char * const ft32_register_names[] =

> +static CORE_ADDR
> +ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
> +                       struct ft32_frame_cache *cache,
> +                       struct gdbarch *gdbarch)
> +{
> ...
> +  /* It is a LINK */

style needs tweaking

> +static CORE_ADDR
> +ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
> +{
> ...
> +  /* No function symbol -- just return the PC.  */
> +  return (CORE_ADDR) pc;

why the cast ?  pc is already of type CORE_ADDR ...

> +static struct value *
> +ft32_frame_prev_register (struct frame_info *this_frame,
> +                          void **this_prologue_cache, int regnum)
> +{
> ...
> +  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
> +      return frame_unwind_got_memory (this_frame, regnum,
> +                                      0x800000 | cache->saved_regs[regnum]);

documenting that constant would be nice

> --- /dev/null
> +++ b/sim/ft32/Makefile.in
>
> +SIM_OBJS = \
> +  $(SIM_NEW_COMMON_OBJS) \

indent the entries in this var with tabs

> +SIM_RUN_OBJS = nrun.o

i flipped the default in the latest tree so you can delete this now

> --- /dev/null
> +++ b/sim/ft32/interp.c
>
> +static uint32_t safe_addr (uint32_t dw, uint32_t ea)
> +{
> +  ea &= 0x1ffff;
> +  switch (dw)
> +  {

the braces on the switch should be indented by two spaces -- the same as the 
case labels.  should fix in the whole file.

  switch (foo)
    {
    case 0:
      ...
      break;
    ...
    }

> +static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
> +{
> ...
> +      switch (ea)
> +      {
> +      case 0x10000:
> +      case 0x10020:
> +        return getchar ();

err what's this for ?  you trying to simulate a UART or something ?

> +      case 0x1fff4:
> +        return (uint32_t)(cpu->state.cycles / 100);

no need for the cast

> +      default:
> +        sim_io_eprintf (sd, "Illegal IO read address %08x, pc %#x\n", ea, cpu->state.pc);
> +        exit (0);

the sim should never exit directly.  you should use sim_engine_halt instead.

also, we generally want code to wrap at 80 cols.  i'm not super strict on this 
(like in the code later on with the packed bit shifts / case statements), but 
you should wrap in simple cases like this printf.  you should scan the whole 
file for other long lines.

> +  r = cpu->state.ram[ea];
> +  if (1 <= dw)
> +    {
> +      r += (cpu->state.ram[ea + 1] << 8);
> +      if (2 <= dw)
> +        {
> +          r += cpu->state.ram[ea + 2] << 16;
> +          r += cpu->state.ram[ea + 3] << 24;
> +        }
> +    }

you shouldn't have a state.ram member ... the sim core takes care of that for 
you.  when you called "memory region" in sim_open, that initialized a chunk of 
ram for you.  you can access that by using the sim_core_write_buffer and 
sim_core_read_buffer helpers.

> +static void ft32_push (SIM_DESC sd, uint32_t v)
> +{
> +  sim_cpu *cpu = STATE_CPU (sd, 0);
> +  cpu->state.regs[31] -= 4;

rather than hardcode constants, you probably want to use symbols like 
FT32_SP_REGNUM.  or maybe use a union in your state:
	union {
		uint32_t raw[32];
		struct {
			uint32_t r0;
			uint32_t r1;
			...
			uint32_t fp;
			uint32_t sp;
			uint32_t pc;
		};
	} regs;

i'm just guessing at the reg names ... i have no idea what's going on :)

> +  cpu->state.regs[31] &= 0xffff;

the CPU itself will automatically wrap the SP to 16bits ?  that's kind of weird.

> +static int nunsigned (int siz, int bits)
> +{
> +  int mask = (1L << siz) - 1;
> +  return bits & mask;
> +}

for all these little utility functions, it'd be nice to have a one line sentence 
above them explaining what they're for.

> +static
> +void step_once (SIM_DESC sd)
> +{
> +  sim_cpu *cpu = STATE_CPU (sd, 0);
> +  address_word cia = CIA_GET (cpu);
> +  const char *ram_char = (const char *)cpu->state.ram;
> +
> +#define ILLEGAL_INSTRUCTION() \
> +  sim_engine_halt (sd, cpu, NULL, insnpc, sim_signalled, SIM_SIGILL)
> +
> +    {
> +      uint32_t inst;

what is with this indented block ?  doesn't seem like you need this at all.  
delete the braces and unindent everything by one level.

> +      /* Handle "call 8" (which is FT32's "break" equivalent) here */

use GNU style comments

> +      if (inst == 0x00340002)
> +        {
> +          goto escape;
> +        }

drop the braces for single statements

> +      dw   =              (inst >> FT32_FLD_DW_BIT) & ((1U << FT32_FLD_DW_SIZ) - 1);
> +      cb   =              (inst >> FT32_FLD_CB_BIT) & ((1U << FT32_FLD_CB_SIZ) - 1);
> +      r_d  =              (inst >> FT32_FLD_R_D_BIT) & ((1U << FT32_FLD_R_D_SIZ) - 1);
> +      cr   =              (inst >> FT32_FLD_CR_BIT) & ((1U << FT32_FLD_CR_SIZ) - 1);
> +      cv   =              (inst >> FT32_FLD_CV_BIT) & ((1U << FT32_FLD_CV_SIZ) - 1);
> +      bt   =              (inst >> FT32_FLD_BT_BIT) & ((1U << FT32_FLD_BT_SIZ) - 1);
> +      r_1 =               (inst >> FT32_FLD_R_1_BIT) & ((1U << FT32_FLD_R_1_SIZ) - 1);

looks like you were trying to align, but the = here is slightly off ;)

> +      cpu->state.pc += 4;
> +      switch (upper)
> +      {
> +        case FT32_PAT_TOC:

when you hit 8 spaces of indentation, you're supposed to replace with a tab

> +            if (upper == FT32_PAT_ALUOP)
> +              {
> +                cpu->state.regs[r_d] = result;
> +              }

drop the braces

> +              else

incorrect indentation for this else -- delete two spaces

this seems to come up a couple of times ... you should search & fix them all

> +int
> +sim_write (SIM_DESC sd,
> +           SIM_ADDR addr,
> +           const unsigned char *buffer,
> +           int size)
> +{
> +  sim_cpu *cpu = STATE_CPU (sd, 0);
> +
> +  if (addr < 0x800000)
> +    {
> +      sim_core_write_buffer (sd, cpu, write_map, buffer, addr, size);
> +    }
> +    else
> +    {
> +      int i;
> +
> +      addr -= 0x800000;
> +      for (i = 0; i < size; i++)
> +        cpu_mem_write (sd, 0, addr + i, buffer[i]);
> +    }
> +  return size;
> +}
> +
> +int
> +sim_read (SIM_DESC sd,
> +          SIM_ADDR addr,
> +          unsigned char *buffer,
> +          int size)
> +{
> +  sim_cpu *cpu = STATE_CPU (sd, 0);
> +
> +  if (addr < 0x800000)
> +    {
> +      sim_core_read_buffer (sd, cpu, read_map, buffer, addr, size);
> +    }
> +    else
> +    {
> +      int i;
> +
> +      addr -= 0x800000;
> +      for (i = 0; i < size; i++)
> +        buffer[i] = cpu_mem_read (sd, 0, addr + i);
> +    }
> +
> +  return size;
> +}

what's going on here with the magic 0x800000 address ?

> +static uint32_t*
> +ft32_lookup_register (SIM_DESC sd, int nr)

spae before the *

> +  switch (nr)
> +  {
> +  case 0:
> +    return &cpu->state.regs[29];
> +    break;
> +  case 1:
> +    return &cpu->state.regs[31];
> +    break;
> +  case 31:
> +    return &cpu->state.regs[30];
> +    break;
> +  case 32:
> +    return &cpu->state.pc;
> +    break;
> +  default:
> +    return &cpu->state.regs[nr - 2];

you probably want to check the value of nr to make sure it's in range and 
abort() when it isn't

> +sim_store_register (SIM_DESC sd,
> +sim_fetch_register (SIM_DESC sd,

you should change these to ft32_reg_{store,fetch}, and at the end of sim_open, 
do something like:
  /* 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;
    }

then add sim-reg.o to your Makefile.in

you probably should also implement ft32_pc_{fetch,store} and assign them:
  CPU_PC_FETCH (cpu) = ft32_pc_fetch;
  CPU_PC_STORE (cpu) = ft32_pc_store;

> +SIM_DESC
> +sim_open (SIM_OPEN_KIND kind,
> +          host_callback *cb,
> +          struct bfd *abfd,
> +          char **argv)
> +{
> +  char c;
> +  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;
> +    }

after this, you should do:
  /* 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;
    }

> +  sd->base.prog_argv = argv + 1;

why do you need to do this ?
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-10 16:56       ` James Bowman
  2015-03-16  8:25         ` Mike Frysinger
@ 2015-03-16 16:38         ` Mike Frysinger
  2015-03-17 17:36         ` Joel Brobecker
  2 siblings, 0 replies; 30+ messages in thread
From: Mike Frysinger @ 2015-03-16 16:38 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 525 bytes --]

On 10 Mar 2015 16:56, James Bowman wrote:
> Also new in this patchset is a testsuite for the FT32 simulator.

it looks like there's one test file that exercises a lot of diff insns.  
usually tests are directed at one thing.  this lets the tests run faster
(in parallel) and makes it more obvious when something fails.  but there
is no requirement that they be split up, so if the current one is fast,
and you prefer to keep it that way, then OK.  if it starts growing larger
then we might revisit splitting.
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-10 16:56       ` James Bowman
  2015-03-16  8:25         ` Mike Frysinger
  2015-03-16 16:38         ` Mike Frysinger
@ 2015-03-17 17:36         ` Joel Brobecker
  2015-03-19 15:21           ` James Bowman
  2 siblings, 1 reply; 30+ messages in thread
From: Joel Brobecker @ 2015-03-17 17:36 UTC (permalink / raw)
  To: James Bowman; +Cc: Mike Frysinger, gdb-patches

> gdb/:
> 2015-03-10  James Bowman  <james.bowman@ftdichip.com>
> 
> 	* Mainfile.in: Add FT32 entries.
> 	* configure.tgt: Add FT32 entry.
> 	* ft32-tdep.c,h: New file, FT32 target-dependent code.

Why don't you re-send the GDB patch separately, since it should
be independent of it anyway? I'll try to review it as soon as I have
a moment.

Thank you,
-- 
Joel

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-17 17:36         ` Joel Brobecker
@ 2015-03-19 15:21           ` James Bowman
  2015-03-19 18:54             ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-03-19 15:21 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: Mike Frysinger, gdb-patches

Hi Joel,

Thanks. This is the gdb part of the submission.

--
James Bowman
FTDI Open Source Liaison



FT32 is a new high performance 32-bit RISC core developed by FTDI for embedded applications.

Support for FT32 has already been added to binutils. This patch adds gdb support.

Please can someone review it, and if appropriate commit it, as I do not have write access to the tree.

The FSF have acknowledged receipt of FTDI's copyright assignment papers.

Thanks very much. ChangeLog entry:

gdb/:
2015-03-19  James Bowman  <james.bowman@ftdichip.com>

	* Mainfile.in: Add FT32 entries.
	* configure.tgt: Add FT32 entry.
	* ft32-tdep.c: New file, FT32 target-dependent code.
	* ft32-tdep.h: New file, FT32 target-dependent code.

include/gdb/:
2015-03-19  James Bowman  <james.bowman@ftdichip.com>

	* sim-ft32.h: New file, FT32 simulator/GDB interface.


From: James Bowman <james.bowman@ftdichip.com>
Date: Thu, 19 Mar 2015 08:19:49 -0700
Subject: [PATCH] FT32 Support

---
 gdb/Makefile.in        |   24 ++-
 gdb/configure.tgt      |   19 +-
 gdb/ft32-tdep.c        |  548 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/ft32-tdep.h        |   28 +++
 include/gdb/sim-ft32.h |   34 +++
 5 files changed, 635 insertions(+), 18 deletions(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h
 create mode 100644 include/gdb/sim-ft32.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8c2a4de..177fbc4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -651,6 +651,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-hpux-tdep.o hppa-linux-tdep.o hppa-tdep.o \
@@ -948,7 +949,7 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h ia64-hpux-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1339,9 +1340,9 @@ init.c: $(INIT_FILES)
 	done | \
 	while read f; do \
 	    case " $$fs " in \
-	        *" $$f "* ) ;; \
-	        * ) echo $$f ; fs="$$fs $$f";; \
-            esac; \
+		*" $$f "* ) ;; \
+		* ) echo $$f ; fs="$$fs $$f";; \
+	    esac; \
 	done >> init.l-tmp
 	@echo '/* Do not modify this file.  */' >>init.c-tmp
 	@echo '/* It is created automatically by the Makefile.  */'>>init.c-tmp
@@ -1652,6 +1653,7 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
+	ft32-tdep.c \
 	glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c hppa-hpux-tdep.c hppa-hpux-nat.c \
@@ -1870,13 +1872,13 @@ po/$(PACKAGE).pot: force
 	    $(FLEX) -o$@ $< && \
 	    rm -f $@.new && \
 	    sed -e '/extern.*malloc/d' \
-	        -e '/extern.*realloc/d' \
-	        -e '/extern.*free/d' \
-	        -e '/include.*malloc.h/d' \
-	        -e 's/\([^x]\)malloc/\1xmalloc/g' \
-	        -e 's/\([^x]\)realloc/\1xrealloc/g' \
-	        -e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
-	        -e 's/\([ \t;,(]\)free$$/\1xfree/g' \
+		-e '/extern.*realloc/d' \
+		-e '/extern.*free/d' \
+		-e '/include.*malloc.h/d' \
+		-e 's/\([^x]\)malloc/\1xmalloc/g' \
+		-e 's/\([^x]\)realloc/\1xrealloc/g' \
+		-e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
+		-e 's/\([ \t;,(]\)free$$/\1xfree/g' \
 		-e 's/yy_flex_xrealloc/yyxrealloc/g' \
 	      < $@ > $@.new && \
 	    rm -f $@ && \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 7fdd34e..324fc9c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -410,7 +410,7 @@ nios2*-*-*)
 powerpc*-*-freebsd*)
 	# Target: FreeBSD/powerpc
 	gdb_target_obs="rs6000-tdep.o ppc-sysv-tdep.o ppc64-tdep.o \
-		        ppcfbsd-tdep.o fbsd-tdep.o solib-svr4.o \
+			ppcfbsd-tdep.o fbsd-tdep.o solib-svr4.o \
 			ravenscar-thread.o ppc-ravenscar-thread.o"
 	;;
 
@@ -621,6 +621,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
@@ -644,7 +649,7 @@ x86_64-*-darwin*)
 	# Target: Darwin/x86-64
 	gdb_target_obs="amd64-tdep.o i386-tdep.o i387-tdep.o \
 			i386-darwin-tdep.o amd64-darwin-tdep.o \
-                        solib-darwin.o"
+			solib-darwin.o"
 	;;
 
 x86_64-*-dicos*)
@@ -666,12 +671,12 @@ x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
 			bsd-uthread.o fbsd-tdep.o solib-svr4.o"
 	;;
 x86_64-*-mingw* | x86_64-*-cygwin*)
-        # Target: MingW/amd64
+	# Target: MingW/amd64
 	gdb_target_obs="amd64-tdep.o amd64-windows-tdep.o \
-                        i386-tdep.o i386-cygwin-tdep.o i387-tdep.o \
-                        windows-tdep.o"
+			i386-tdep.o i386-cygwin-tdep.o i387-tdep.o \
+			windows-tdep.o"
 	build_gdbserver=yes
-        ;;
+	;;
 x86_64-*-netbsd* | x86_64-*-knetbsd*-gnu)
 	# Target: NetBSD/amd64
 	gdb_target_obs="amd64-tdep.o amd64nbsd-tdep.o i386-tdep.o i387-tdep.o \
@@ -716,7 +721,7 @@ m68*-*-openbsd* | m88*-*-openbsd* | vax-*-openbsd*) ;;
 *-*-symbianelf*)
 		gdb_osabi=GDB_OSABI_SYMBIAN ;;
 powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*)
-                gdb_osabi=GDB_OSABI_AIX ;;
+		gdb_osabi=GDB_OSABI_AIX ;;
 esac
 
 # Check whether this target supports gcore.
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..6aac8be
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,548 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+#include "gdb/sim-ft32.h"
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address -1 as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
+
+struct ft32_frame_cache
+{
+  /* Base address.  */
+  CORE_ADDR base;
+  CORE_ADDR pc;
+  LONGEST framesize;
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  CORE_ADDR saved_sp;
+  bfd_boolean established;  /* Has the new frame been LINKed.  */
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+			 CORE_ADDR *pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+static const char * const ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+			 const void *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4,
+					 len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+		       struct ft32_frame_cache *cache,
+		       struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+      return end_addr;
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH (inst))
+	{
+	  regnum = PUSH_REG (inst);
+	  cache->framesize += 4;
+	  cache->saved_regs[regnum] = cache->framesize;
+	  next_addr += 4;
+	}
+      else
+	break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+	cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  /* It is a LINK?  */
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK (inst))
+	{
+	  cache->established = 1;
+	  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+	    {
+	      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+		cache->saved_regs[regnum] += 4;
+	    }
+	  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+	  cache->saved_regs[FT32_FP_REGNUM] = 0;
+	  cache->framesize += LINK_SIZE (inst);
+	  next_addr += 4;
+	}
+    }
+
+  return next_addr;
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+      else
+	{
+	  /* Can't determine prologue from the symbol table, need to examine
+	     instructions.  */
+	  struct symtab_and_line sal;
+	  struct symbol *sym;
+	  struct ft32_frame_cache cache;
+	  CORE_ADDR plg_end;
+
+	  memset (&cache, 0, sizeof cache);
+
+	  plg_end = ft32_analyze_prologue (func_addr,
+					   func_end, &cache, gdbarch);
+	  /* Found a function.  */
+	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+	  /* Don't use line number debug info for assembly source files.  */
+	  if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
+	    {
+	      sal = find_pc_line (func_addr, 0);
+	      if (sal.end && sal.end < func_end)
+		{
+		  /* Found a line number, use it as end of
+		     prologue.  */
+		  return sal.end;
+		}
+	    }
+	  /* No useable line symbol.  Use result of prologue parsing method.  */
+	  return plg_end;
+	}
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+			   void *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *valtype, struct regcache *regcache,
+		   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+	ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+	ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  cache->base = 0;
+  cache->saved_sp = 0;
+  cache->pc = 0;
+  cache->framesize = 0;
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+	cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+      return frame_unwind_got_memory (this_frame, regnum,
+				      RAM_BIAS | cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..5c52480
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,28 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for FT32.  */
+};
+
+#endif /* FT32_TDEP_H */
diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
new file mode 100644
index 0000000..6602df3
--- /dev/null
+++ b/include/gdb/sim-ft32.h
@@ -0,0 +1,34 @@
+/* This file defines the interface between the FT32 simulator and GDB.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by FTDI.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* Register numbers of various important registers.  */
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
+  FT32_SP_REGNUM,               /* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_PC_REGNUM = 32           /* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33        /* 32 real registers + PC */
+
-- 
1.7.9.5

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-16  8:25         ` Mike Frysinger
@ 2015-03-19 15:26           ` James Bowman
  2015-03-19 18:52             ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-03-19 15:26 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

Thanks,

Have reworked the simulator, and the revised patches are attached below.
everything done except for a couple of queries:

> > +SIM_RUN_OBJS = nrun.o
> 
> i flipped the default in the latest tree so you can delete this now

Deleting this line gives:

run.o: In function `main':
... /ft32/../common/run.c:116: undefined reference to `sim_set_callbacks'
... /ft32/../common/run.c:163: undefined reference to `sim_size'
... /ft32/../common/run.c:271: undefined reference to `sim_trace'

> > +  sd->base.prog_argv = argv + 1;
> 
> why do you need to do this ?

Because if this is omitted, no command line arguments are processed.


Thanks again, J.


--
James Bowman
FTDI Open Source Liaison


FT32 is a new high performance 32-bit RISC core developed by FTDI for embedded applications.

Support for FT32 has already been added to binutils. This patch adds sim support.

Please can someone review it, and if appropriate commit it, as I do not have write access to the tree.

The FSF have acknowledged receipt of FTDI's copyright assignment papers.

Thanks very much. ChangeLog entry:

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

	* configure.tgt: Add FT32 entry.

sim/ft32/:
2015-03-19  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-19  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: Thu, 19 Mar 2015 08:22:36 -0700
Subject: [PATCH] FT32 Support

---
 sim/configure.tgt                    |    4 +
 sim/ft32/Makefile.in                 |   50 ++
 sim/ft32/config.in                   |  154 ++++++
 sim/ft32/configure.ac                |   15 +
 sim/ft32/ft32-sim.h                  |   41 ++
 sim/ft32/interp.c                    |  865 ++++++++++++++++++++++++++++++++++
 sim/ft32/sim-main.h                  |   62 +++
 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, 2136 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

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..b44a5c2
--- /dev/null
+++ b/sim/ft32/Makefile.in
@@ -0,0 +1,50 @@
+#    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)
+
+SIM_RUN_OBJS = nrun.o
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/ft32/config.in b/sim/ft32/config.in
new file mode 100644
index 0000000..8de933c
--- /dev/null
+++ b/sim/ft32/config.in
@@ -0,0 +1,154 @@
+/* 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 <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the `__setfpucw' function. */
+#undef HAVE___SETFPUCW
+
+/* 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
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..e98206e
--- /dev/null
+++ b/sim/ft32/ft32-sim.h
@@ -0,0 +1,41 @@
+/* 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>
+
+#define FT32_FP_REGNUM 29
+#define FT32_CC_REGNUM 30
+#define FT32_SP_REGNUM 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..71d2464
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,865 @@
+/* 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-base.h"
+
+#include "opcode/ft32.h"
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+/* Forward declarations.  */
+int ft32_reg_store (SIM_CPU *cpu, int rn, unsigned char *memory, int length);
+int ft32_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length);
+
+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.  */
+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);
+  uint8_t byte[4];
+  uint32_t r;
+
+  ea = ft32_align (dw, ea);
+  sim_core_read_buffer (sd, cpu, read_map, byte, ea, 1 << dw);
+  r = byte[0];
+  if (1 <= dw)
+    {
+      r += (byte[1] << 8);
+      if (2 <= dw)
+	{
+	  r += byte[2] << 16;
+	  r += byte[3] << 24;
+	}
+    }
+  return r;
+}
+
+/* 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);
+  uint8_t byte[4];
+
+  ea = ft32_align (dw, ea);
+
+  byte[0] = v & 0xff;
+  byte[1] = (v >> 8) & 0xff;
+  byte[2] = (v >> 16) & 0xff;
+  byte[3] = (v >> 24) & 0xff;
+  sim_core_write_buffer (sd, cpu, write_map, byte, ea, 1 << dw);
+}
+
+#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))
+    {
+      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)
+    {
+      switch (ea)
+	{
+	case 0x10000:
+	  putchar (d & 0xff);
+	  break;
+	case 0x1fc80:
+	  cpu->state.pm_unlock = (d == 0x1337f7d1);
+	  break;
+	case 0x1fc84:
+	  cpu->state.pm_addr = d;
+	  break;
+	case 0x1fc88:
+	  ft32_write_item (sd, dw, cpu->state.pm_addr, d);
+	  break;
+	case 0x10024:
+	  break;
+	case 0x1fffc:
+	  /* Normal exit.  */
+	  sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
+	  break;
+	case 0x1fff8:
+	case 0x19470:
+	  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_SP_REGNUM] -= 4;
+  cpu->state.regs[FT32_SP_REGNUM] &= 0xffff;
+  cpu_mem_write (sd, 2, cpu->state.regs[FT32_SP_REGNUM], 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_SP_REGNUM]);
+  cpu->state.regs[FT32_SP_REGNUM] += 4;
+  cpu->state.regs[FT32_SP_REGNUM] &= 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)
+      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.cycles += 1;
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, pa << 2);
+      break;
+
+    case FT32_PAT_LPMI:
+      cpu->state.cycles += 1;
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, cpu->state.regs[r_1] + k8);
+      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.cycles += 1;
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, aa);
+      break;
+
+    case FT32_PAT_LDI:
+      cpu->state.cycles += 1;
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+      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;
+      }
+      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;
+      }
+      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_SP_REGNUM];
+      cpu->state.regs[FT32_SP_REGNUM] -= k16;
+      cpu->state.regs[FT32_SP_REGNUM] &= 0xffff;
+      break;
+
+    case FT32_PAT_UNLINK:
+      cpu->state.regs[FT32_SP_REGNUM] = cpu->state.regs[r_d];
+      cpu->state.regs[FT32_SP_REGNUM] &= 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 *current_cpu, int nr)
+{
+  sim_cpu *cpu = current_cpu;
+
+  /* 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 > 31))
+    abort ();
+
+  switch (nr)
+    {
+    case 0:
+      return &cpu->state.regs[FT32_FP_REGNUM];
+      break;
+    case 1:
+      return &cpu->state.regs[FT32_SP_REGNUM];
+      break;
+    case 31:
+      return &cpu->state.regs[FT32_CC_REGNUM];
+      break;
+    case 32:
+      return &cpu->state.pc;
+      break;
+    default:
+      return &cpu->state.regs[nr - 2];
+    }
+}
+
+int
+ft32_reg_store (SIM_CPU *current_cpu,
+		int rn,
+		unsigned char *memory,
+		int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	*ft32_lookup_register (current_cpu, rn) = ft32_extract_unsigned_integer (memory, 4);
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+int
+ft32_reg_fetch (SIM_CPU *current_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 (current_cpu, rn));
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+/* 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;
+    }
+
+  /* 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;
+    }
+
+  sd->base.prog_argv = argv + 1;
+
+  /* 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;
+    }
+
+  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_SP_REGNUM] = 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..2dd0bdc
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,62 @@
+/* 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))
+
+/* To keep this default simulator simple, and fast, we use a direct
+   vector of registers.  The internal simulator engine then uses
+   manifests to access the correct slot. */
+
+  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
-- 
1.7.9.5

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-19 15:26           ` James Bowman
@ 2015-03-19 18:52             ` Mike Frysinger
  2015-03-20  2:57               ` James Bowman
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-19 18:52 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 812 bytes --]

On 19 Mar 2015 15:26, James Bowman wrote:
> > > +SIM_RUN_OBJS = nrun.o
> > 
> > i flipped the default in the latest tree so you can delete this now
> 
> Deleting this line gives:
> 
> run.o: In function `main':
> ... /ft32/../common/run.c:116: undefined reference to `sim_set_callbacks'
> ... /ft32/../common/run.c:163: undefined reference to `sim_size'
> ... /ft32/../common/run.c:271: undefined reference to `sim_trace'

you probably need to:
 - make sure you're using the latest tree
 - make sure you've regenerated the autotool files
 - rebuild the sim dir from scratch

> > > +  sd->base.prog_argv = argv + 1;
> > 
> > why do you need to do this ?
> 
> Because if this is omitted, no command line arguments are processed.

if you use nrun (see above) then you don't need this
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-19 15:21           ` James Bowman
@ 2015-03-19 18:54             ` Mike Frysinger
  2015-03-20  3:01               ` James Bowman
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-19 18:54 UTC (permalink / raw)
  To: James Bowman; +Cc: Joel Brobecker, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1566 bytes --]

On 19 Mar 2015 15:21, James Bowman wrote:
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -1339,9 +1340,9 @@ init.c: $(INIT_FILES)
>  	done | \
>  	while read f; do \
>  	    case " $$fs " in \
> -	        *" $$f "* ) ;; \
> -	        * ) echo $$f ; fs="$$fs $$f";; \
> -            esac; \
> +		*" $$f "* ) ;; \
> +		* ) echo $$f ; fs="$$fs $$f";; \
> +	    esac; \
>  	done >> init.l-tmp
>  	@echo '/* Do not modify this file.  */' >>init.c-tmp
>  	@echo '/* It is created automatically by the Makefile.  */'>>init.c-tmp

you should delete unrelated whitespace changes from your submission

> @@ -1870,13 +1872,13 @@ po/$(PACKAGE).pot: force
>  	    $(FLEX) -o$@ $< && \
>  	    rm -f $@.new && \
>  	    sed -e '/extern.*malloc/d' \
> -	        -e '/extern.*realloc/d' \
> -	        -e '/extern.*free/d' \
> -	        -e '/include.*malloc.h/d' \
> -	        -e 's/\([^x]\)malloc/\1xmalloc/g' \
> -	        -e 's/\([^x]\)realloc/\1xrealloc/g' \
> -	        -e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
> -	        -e 's/\([ \t;,(]\)free$$/\1xfree/g' \
> +		-e '/extern.*realloc/d' \
> +		-e '/extern.*free/d' \
> +		-e '/include.*malloc.h/d' \
> +		-e 's/\([^x]\)malloc/\1xmalloc/g' \
> +		-e 's/\([^x]\)realloc/\1xrealloc/g' \
> +		-e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
> +		-e 's/\([ \t;,(]\)free$$/\1xfree/g' \
>  		-e 's/yy_flex_xrealloc/yyxrealloc/g' \
>  	      < $@ > $@.new && \
>  	    rm -f $@ && \

this too

> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt

you've included a lot of unrelated changes in this too
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-19 18:52             ` Mike Frysinger
@ 2015-03-20  2:57               ` James Bowman
  2015-03-20  5:18                 ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-03-20  2:57 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

> > > > +SIM_RUN_OBJS = nrun.o
> > > 
> > > i flipped the default in the latest tree so you can delete this now
> > 
> > Deleting this line gives:
> > 
> > run.o: In function `main':
> > ... /ft32/../common/run.c:116: undefined reference to `sim_set_callbacks'
> > ... /ft32/../common/run.c:163: undefined reference to `sim_size'
> > ... /ft32/../common/run.c:271: undefined reference to `sim_trace'
> 
> you probably need to:
>  - make sure you're using the latest tree
>  - make sure you've regenerated the autotool files
>  - rebuild the sim dir from scratch
> 
> > > > +  sd->base.prog_argv = argv + 1;
> > > 
> > > why do you need to do this ?
> > 
> > Because if this is omitted, no command line arguments are processed.
> 
> if you use nrun (see above) then you don't need this

OK, thanks.
These are both fixed now. The argv trouble I had was caused by a missing
call to sim_parse_args.

Revised patch below.

--
James Bowman
FTDI Open Source Liaison

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

	* configure.tgt: Add FT32 entry.

sim/ft32/:
2015-03-19  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-19  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: Thu, 19 Mar 2015 19:50:55 -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                  |   41 ++
 sim/ft32/interp.c                    |  872 ++++++++++++++++++++++++++++++++++
 sim/ft32/sim-main.h                  |   62 +++
 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, 2150 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

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..e98206e
--- /dev/null
+++ b/sim/ft32/ft32-sim.h
@@ -0,0 +1,41 @@
+/* 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>
+
+#define FT32_FP_REGNUM 29
+#define FT32_CC_REGNUM 30
+#define FT32_SP_REGNUM 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..3a28c8a
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,872 @@
+/* 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-base.h"
+
+#include "opcode/ft32.h"
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+/* Forward declarations.  */
+int ft32_reg_store (SIM_CPU *cpu, int rn, unsigned char *memory, int length);
+int ft32_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length);
+
+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.  */
+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);
+  uint8_t byte[4];
+  uint32_t r;
+
+  ea = ft32_align (dw, ea);
+  sim_core_read_buffer (sd, cpu, read_map, byte, ea, 1 << dw);
+  r = byte[0];
+  if (1 <= dw)
+    {
+      r += (byte[1] << 8);
+      if (2 <= dw)
+	{
+	  r += byte[2] << 16;
+	  r += byte[3] << 24;
+	}
+    }
+  return r;
+}
+
+/* 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);
+  uint8_t byte[4];
+
+  ea = ft32_align (dw, ea);
+
+  byte[0] = v & 0xff;
+  byte[1] = (v >> 8) & 0xff;
+  byte[2] = (v >> 16) & 0xff;
+  byte[3] = (v >> 24) & 0xff;
+  sim_core_write_buffer (sd, cpu, write_map, byte, ea, 1 << dw);
+}
+
+#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))
+    {
+      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)
+    {
+      switch (ea)
+	{
+	case 0x10000:
+	  putchar (d & 0xff);
+	  break;
+	case 0x1fc80:
+	  cpu->state.pm_unlock = (d == 0x1337f7d1);
+	  break;
+	case 0x1fc84:
+	  cpu->state.pm_addr = d;
+	  break;
+	case 0x1fc88:
+	  ft32_write_item (sd, dw, cpu->state.pm_addr, d);
+	  break;
+	case 0x10024:
+	  break;
+	case 0x1fffc:
+	  /* Normal exit.  */
+	  sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
+	  break;
+	case 0x1fff8:
+	case 0x19470:
+	  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_SP_REGNUM] -= 4;
+  cpu->state.regs[FT32_SP_REGNUM] &= 0xffff;
+  cpu_mem_write (sd, 2, cpu->state.regs[FT32_SP_REGNUM], 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_SP_REGNUM]);
+  cpu->state.regs[FT32_SP_REGNUM] += 4;
+  cpu->state.regs[FT32_SP_REGNUM] &= 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)
+      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.cycles += 1;
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, pa << 2);
+      break;
+
+    case FT32_PAT_LPMI:
+      cpu->state.cycles += 1;
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, cpu->state.regs[r_1] + k8);
+      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.cycles += 1;
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, aa);
+      break;
+
+    case FT32_PAT_LDI:
+      cpu->state.cycles += 1;
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+      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;
+      }
+      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;
+      }
+      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_SP_REGNUM];
+      cpu->state.regs[FT32_SP_REGNUM] -= k16;
+      cpu->state.regs[FT32_SP_REGNUM] &= 0xffff;
+      break;
+
+    case FT32_PAT_UNLINK:
+      cpu->state.regs[FT32_SP_REGNUM] = cpu->state.regs[r_d];
+      cpu->state.regs[FT32_SP_REGNUM] &= 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 *current_cpu, int nr)
+{
+  sim_cpu *cpu = current_cpu;
+
+  /* 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 > 31))
+    abort ();
+
+  switch (nr)
+    {
+    case 0:
+      return &cpu->state.regs[FT32_FP_REGNUM];
+      break;
+    case 1:
+      return &cpu->state.regs[FT32_SP_REGNUM];
+      break;
+    case 31:
+      return &cpu->state.regs[FT32_CC_REGNUM];
+      break;
+    case 32:
+      return &cpu->state.pc;
+      break;
+    default:
+      return &cpu->state.regs[nr - 2];
+    }
+}
+
+int
+ft32_reg_store (SIM_CPU *current_cpu,
+		int rn,
+		unsigned char *memory,
+		int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	*ft32_lookup_register (current_cpu, rn) = ft32_extract_unsigned_integer (memory, 4);
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+int
+ft32_reg_fetch (SIM_CPU *current_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 (current_cpu, rn));
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+/* 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;
+    }
+
+  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_SP_REGNUM] = 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..2dd0bdc
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,62 @@
+/* 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))
+
+/* To keep this default simulator simple, and fast, we use a direct
+   vector of registers.  The internal simulator engine then uses
+   manifests to access the correct slot. */
+
+  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
-- 
1.7.9.5

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-19 18:54             ` Mike Frysinger
@ 2015-03-20  3:01               ` James Bowman
  2015-03-20  3:47                 ` Mike Frysinger
  2015-03-20 12:46                 ` Joel Brobecker
  0 siblings, 2 replies; 30+ messages in thread
From: James Bowman @ 2015-03-20  3:01 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: Joel Brobecker, gdb-patches

Apologies. Tabs are fixed now. Revised GDB Changelist and patches are below.

--
James Bowman
FTDI Open Source Liaison


gdb/:
2015-03-19  James Bowman  <james.bowman@ftdichip.com>

        * Mainfile.in: Add FT32 entries.
        * configure.tgt: Add FT32 entry.
        * ft32-tdep.c: New file, FT32 target-dependent code.
        * ft32-tdep.h: New file, FT32 target-dependent code.

include/gdb/:
2015-03-19  James Bowman  <james.bowman@ftdichip.com>

        * sim-ft32.h: New file, FT32 simulator/GDB interface.


From: James Bowman <james.bowman@ftdichip.com>
Date: Thu, 19 Mar 2015 19:57:36 -0700
Subject: [PATCH] FT32 Support

---
 gdb/Makefile.in        |    4 +-
 gdb/configure.tgt      |    5 +
 gdb/ft32-tdep.c        |  548 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/ft32-tdep.h        |   28 +++
 include/gdb/sim-ft32.h |   34 +++
 5 files changed, 618 insertions(+), 1 deletion(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h
 create mode 100644 include/gdb/sim-ft32.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index dbace2d..9f52c76 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -651,6 +651,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-linux-tdep.o hppa-tdep.o \
@@ -949,7 +950,7 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1655,6 +1656,7 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
+	ft32-tdep.c \
 	glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index c97ebdd..8feda7c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -615,6 +615,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..6aac8be
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,548 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+#include "gdb/sim-ft32.h"
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address -1 as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
+
+struct ft32_frame_cache
+{
+  /* Base address.  */
+  CORE_ADDR base;
+  CORE_ADDR pc;
+  LONGEST framesize;
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  CORE_ADDR saved_sp;
+  bfd_boolean established;  /* Has the new frame been LINKed.  */
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+			 CORE_ADDR *pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+static const char * const ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+			 const void *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4,
+					 len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+		       struct ft32_frame_cache *cache,
+		       struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+      return end_addr;
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH (inst))
+	{
+	  regnum = PUSH_REG (inst);
+	  cache->framesize += 4;
+	  cache->saved_regs[regnum] = cache->framesize;
+	  next_addr += 4;
+	}
+      else
+	break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+	cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  /* It is a LINK?  */
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK (inst))
+	{
+	  cache->established = 1;
+	  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+	    {
+	      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+		cache->saved_regs[regnum] += 4;
+	    }
+	  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+	  cache->saved_regs[FT32_FP_REGNUM] = 0;
+	  cache->framesize += LINK_SIZE (inst);
+	  next_addr += 4;
+	}
+    }
+
+  return next_addr;
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+      else
+	{
+	  /* Can't determine prologue from the symbol table, need to examine
+	     instructions.  */
+	  struct symtab_and_line sal;
+	  struct symbol *sym;
+	  struct ft32_frame_cache cache;
+	  CORE_ADDR plg_end;
+
+	  memset (&cache, 0, sizeof cache);
+
+	  plg_end = ft32_analyze_prologue (func_addr,
+					   func_end, &cache, gdbarch);
+	  /* Found a function.  */
+	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+	  /* Don't use line number debug info for assembly source files.  */
+	  if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
+	    {
+	      sal = find_pc_line (func_addr, 0);
+	      if (sal.end && sal.end < func_end)
+		{
+		  /* Found a line number, use it as end of
+		     prologue.  */
+		  return sal.end;
+		}
+	    }
+	  /* No useable line symbol.  Use result of prologue parsing method.  */
+	  return plg_end;
+	}
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+			   void *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *valtype, struct regcache *regcache,
+		   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+	ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+	ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  cache->base = 0;
+  cache->saved_sp = 0;
+  cache->pc = 0;
+  cache->framesize = 0;
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+	cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+      return frame_unwind_got_memory (this_frame, regnum,
+				      RAM_BIAS | cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..5c52480
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,28 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for FT32.  */
+};
+
+#endif /* FT32_TDEP_H */
diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
new file mode 100644
index 0000000..6602df3
--- /dev/null
+++ b/include/gdb/sim-ft32.h
@@ -0,0 +1,34 @@
+/* This file defines the interface between the FT32 simulator and GDB.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by FTDI.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* Register numbers of various important registers.  */
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
+  FT32_SP_REGNUM,               /* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_PC_REGNUM = 32           /* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33        /* 32 real registers + PC */
+
-- 
1.7.9.5



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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20  3:01               ` James Bowman
@ 2015-03-20  3:47                 ` Mike Frysinger
  2015-03-20 12:46                 ` Joel Brobecker
  1 sibling, 0 replies; 30+ messages in thread
From: Mike Frysinger @ 2015-03-20  3:47 UTC (permalink / raw)
  To: James Bowman; +Cc: Joel Brobecker, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 23 bytes --]

looks fine to me
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20  2:57               ` James Bowman
@ 2015-03-20  5:18                 ` Mike Frysinger
  2015-03-20  5:19                   ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-20  5:18 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 5006 bytes --]

it mostly looks good.  i made some fixes you should apply (see attached).

there's some outstanding issues related to the memory handling.  i think the 
disconnect here is how the sim works.  the core sim logic just processes the
ISA and manages the core of the cpu -- it executes insns and handles the regs
and sends requests to the memory.  there are devices you can attach to the main 
memory bus (see common/dv-*.c for examples) so that when requests are made to 
addresses that a device is attached to, they automatically get routed to the 
right code for you.

this is where the model layers come into play.  you declare specific cpu models 
that have devices attached to known addresses.  at runtime, you can select from 
the different models, and the sim will build that up.

the power of the sim comes into play when you want to try out ideas for new cpu 
models.  you can easily move device address attachments around, add more/less 
memory, and create models for hardware that doesn't yet exist.  then run code in 
that simulation and see how things work.

> +#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */

what's with this ?  if the code requests address 0, it should go to address 0.

> +/* Align EA according to its size DW.  */
> +static uint32_t ft32_align (uint32_t dw, uint32_t ea)

what's with this ?  is this how the hardware works ?  e.g. if the PC is set to 
0x101, it'll mask off the low bit ?  or will it throw an exception ?  same for 
if it reads/writes address 0x101.

> +/* 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);
> +  uint8_t byte[4];
> +  uint32_t r;
> +
> +  ea = ft32_align (dw, ea);
> +  sim_core_read_buffer (sd, cpu, read_map, byte, ea, 1 << dw);
> +  r = byte[0];
> +  if (1 <= dw)
> +    {
> +      r += (byte[1] << 8);
> +      if (2 <= dw)
> +	{
> +	  r += byte[2] << 16;
> +	  r += byte[3] << 24;
> +	}
> +    }
> +  return r;
> +}

you should be able to use sim_core_read_aligned_{1,2,4} here instead of reading 
and unpacking the byte array yourself

you should check the return value of the sim core read function

> +/* 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);
> +  uint8_t byte[4];
> +
> +  ea = ft32_align (dw, ea);
> +
> +  byte[0] = v & 0xff;
> +  byte[1] = (v >> 8) & 0xff;
> +  byte[2] = (v >> 16) & 0xff;
> +  byte[3] = (v >> 24) & 0xff;
> +  sim_core_write_buffer (sd, cpu, write_map, byte, ea, 1 << dw);
> +}

this looks like assumes the memory is little endian.  should it be using 
sim_core_write_unaligned_{1,2,4} instead ?

you should check the return value of the sim core write function

> +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))
> +    {
> +      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)
> +    {
> +      switch (ea)
> +	{
> +	case 0x10000:
> +	  putchar (d & 0xff);
> +	  break;
> +	case 0x1fc80:
> +	  cpu->state.pm_unlock = (d == 0x1337f7d1);
> +	  break;
> +	case 0x1fc84:
> +	  cpu->state.pm_addr = d;
> +	  break;
> +	case 0x1fc88:
> +	  ft32_write_item (sd, dw, cpu->state.pm_addr, d);
> +	  break;
> +	case 0x10024:
> +	  break;
> +	case 0x1fffc:
> +	  /* Normal exit.  */
> +	  sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
> +	  break;
> +	case 0x1fff8:
> +	case 0x19470:
> +	  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);
> +}

what's with these addresses ?  are you trying to simulate attached hardware 
devices ?

> +  /* 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;
> +    }

still should implement the CPU_PC_FETCH/etc... stuff i suggested earlier.
that way the builtin profile functions will work automatically.
$ ./run -p -v ./some-elf
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20  5:18                 ` Mike Frysinger
@ 2015-03-20  5:19                   ` Mike Frysinger
  2015-03-23 19:21                     ` James Bowman
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-20  5:19 UTC (permalink / raw)
  To: James Bowman, gdb-patches


[-- Attachment #1.1: Type: text/plain, Size: 148 bytes --]

On 20 Mar 2015 01:18, Mike Frysinger wrote:
> it mostly looks good.  i made some fixes you should apply (see attached).

forgot to attach ...
-mike

[-- Attachment #1.2: ft32-sim.patch --]
[-- Type: text/x-diff, Size: 3768 bytes --]

--- a/sim/ft32/interp.c
+++ b/sim/ft32/interp.c
@@ -30,16 +30,11 @@
 #include "gdb/remote-sim.h"
 
 #include "sim-main.h"
-#include "sim-base.h"
 
 #include "opcode/ft32.h"
 
 #define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
 
-/* Forward declarations.  */
-int ft32_reg_store (SIM_CPU *cpu, int rn, unsigned char *memory, int length);
-int ft32_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length);
-
 static unsigned long
 ft32_extract_unsigned_integer (unsigned char *addr, int len)
 {
@@ -139,7 +134,7 @@ static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
   uint8_t byte[4];
 
   ea &= 0x1ffff;
-  if ((ea & ~0xffff))
+  if (ea & ~0xffff)
     {
       switch (ea)
 	{
@@ -147,10 +142,8 @@ static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
 	  /* 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);
+	  sim_io_eprintf (sd, "Illegal IO read address %08x, pc %#x\n",
+			  ea, insnpc);
 	  ILLEGAL ();
 	}
     }
@@ -281,8 +274,8 @@ static uint32_t flip (uint32_t x, uint32_t b)
   return x;
 }
 
-static
-void step_once (SIM_DESC sd)
+static void
+step_once (SIM_DESC sd)
 {
   sim_cpu *cpu = STATE_CPU (sd, 0);
   address_word cia = CIA_GET (cpu);
@@ -366,9 +359,7 @@ void step_once (SIM_DESC sd)
 	    else
 	      cpu->state.pc = cpu->state.regs[r_2];
 	    if (cpu->state.pc == 0x8)
-	      {
 		goto escape;
-	      }
 	  }
       }
       break;
@@ -680,10 +671,8 @@ sim_read (SIM_DESC sd,
 }
 
 static uint32_t *
-ft32_lookup_register (SIM_CPU *current_cpu, int nr)
+ft32_lookup_register (SIM_CPU *cpu, int nr)
 {
-  sim_cpu *cpu = current_cpu;
-
   /* Handle the register number translation here.
    * Sim registers are 0-31.
    * Other tools (gcc, gdb) use:
@@ -694,29 +683,28 @@ ft32_lookup_register (SIM_CPU *current_cpu, int nr)
    */
 
   if ((nr < 0) || (nr > 31))
-    abort ();
+    {
+      sim_io_eprintf (CPU_STATE (cpu), "unknown register %i\n", nr);
+      abort ();
+    }
 
   switch (nr)
     {
     case 0:
       return &cpu->state.regs[FT32_FP_REGNUM];
-      break;
     case 1:
       return &cpu->state.regs[FT32_SP_REGNUM];
-      break;
     case 31:
       return &cpu->state.regs[FT32_CC_REGNUM];
-      break;
     case 32:
       return &cpu->state.pc;
-      break;
     default:
       return &cpu->state.regs[nr - 2];
     }
 }
 
-int
-ft32_reg_store (SIM_CPU *current_cpu,
+static int
+ft32_reg_store (SIM_CPU *cpu,
 		int rn,
 		unsigned char *memory,
 		int length)
@@ -724,7 +712,7 @@ ft32_reg_store (SIM_CPU *current_cpu,
   if (0 <= rn && rn <= 32)
     {
       if (length == 4)
-	*ft32_lookup_register (current_cpu, rn) = ft32_extract_unsigned_integer (memory, 4);
+	*ft32_lookup_register (cpu, rn) = ft32_extract_unsigned_integer (memory, 4);
 
       return 4;
     }
@@ -732,8 +720,8 @@ ft32_reg_store (SIM_CPU *current_cpu,
     return 0;
 }
 
-int
-ft32_reg_fetch (SIM_CPU *current_cpu,
+static int
+ft32_reg_fetch (SIM_CPU *cpu,
 		int rn,
 		unsigned char *memory,
 		int length)
@@ -741,7 +729,7 @@ ft32_reg_fetch (SIM_CPU *current_cpu,
   if (0 <= rn && rn <= 32)
     {
       if (length == 4)
-	ft32_store_unsigned_integer (memory, 4, *ft32_lookup_register (current_cpu, rn));
+	ft32_store_unsigned_integer (memory, 4, *ft32_lookup_register (cpu, rn));
 
       return 4;
     }
@@ -837,8 +825,7 @@ sim_open (SIM_OPEN_KIND kind,
 }
 
 void
-sim_close (SIM_DESC sd,
-	   int quitting)
+sim_close (SIM_DESC sd, int quitting)
 {
   sim_module_uninstall (sd);
 }

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20  3:01               ` James Bowman
  2015-03-20  3:47                 ` Mike Frysinger
@ 2015-03-20 12:46                 ` Joel Brobecker
  2015-03-20 15:47                   ` Mike Frysinger
  1 sibling, 1 reply; 30+ messages in thread
From: Joel Brobecker @ 2015-03-20 12:46 UTC (permalink / raw)
  To: James Bowman; +Cc: Mike Frysinger, gdb-patches

Overall, a very good submission!

Comments below.

> gdb/:
> 2015-03-19  James Bowman  <james.bowman@ftdichip.com>
> 
>         * Mainfile.in: Add FT32 entries.
>         * configure.tgt: Add FT32 entry.
>         * ft32-tdep.c: New file, FT32 target-dependent code.
>         * ft32-tdep.h: New file, FT32 target-dependent code.

For Makefile.in, you need to specify which entities you are modifying.
For instance:

        * Makefile.in (ALL_TARGET_OBS): Add ft32-tdep.o.
        (HFILES_NO_SRCDIR): Add ft32-tdep.h.
        (ALLDEPFILES): Add ft32-tdep.c.

> include/gdb/:
> 2015-03-19  James Bowman  <james.bowman@ftdichip.com>
> 
>         * sim-ft32.h: New file, FT32 simulator/GDB interface.

Shouldn't this part of the "sim" patch? (which means that the GDB
part would be conditional on the "sim" part being pushed first).

The reason why I think the sim part should be pushed first, is that
I think the sim can be built standalone. So, even if GDB support isn't
there, you should still be able to build the sim. Moving that file
to the sim patch makes the sim port self-sufficient/complete.

> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index dbace2d..9f52c76 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -651,6 +651,7 @@ ALL_TARGET_OBS = \
>  	dicos-tdep.o \
>  	fbsd-tdep.o \
>  	frv-linux-tdep.o frv-tdep.o \
> +	ft32-tdep.o \
>  	h8300-tdep.o \
>  	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
>  	hppa-linux-tdep.o hppa-tdep.o \
> @@ -949,7 +950,7 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h \
>  amd64-darwin-tdep.h charset-list.h \
>  config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
>  dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
> -i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
> +i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h ft32-tdep.h \

This line is now too long. Can you please split it?

>  osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
>  python/python-internal.h python/python.h ravenscar-thread.h record.h \
>  record-full.h solib-aix.h \
> @@ -1655,6 +1656,7 @@ ALLDEPFILES = \
>  	fbsd-nat.c \
>  	fbsd-tdep.c \
>  	fork-child.c \
> +	ft32-tdep.c \
>  	glibc-tdep.c \
>  	go32-nat.c h8300-tdep.c \
>  	hppa-tdep.c \


> diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
> new file mode 100644
> index 0000000..6aac8be
> --- /dev/null
> +++ b/gdb/ft32-tdep.c
> @@ -0,0 +1,548 @@
> +/* Target-dependent code for FT32.
> +
> +   Copyright (C) 2009-2015 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "defs.h"
> +#include "frame.h"
> +#include "frame-unwind.h"
> +#include "frame-base.h"
> +#include "symtab.h"
> +#include "gdbtypes.h"
> +#include "gdbcmd.h"
> +#include "gdbcore.h"
> +#include "value.h"
> +#include "inferior.h"
> +#include "symfile.h"
> +#include "objfiles.h"
> +#include "osabi.h"
> +#include "language.h"
> +#include "arch-utils.h"
> +#include "regcache.h"
> +#include "trad-frame.h"
> +#include "dis-asm.h"
> +#include "record.h"
> +
> +#include "gdb_assert.h"
> +
> +#include "ft32-tdep.h"
> +#include "gdb/sim-ft32.h"
> +
> +#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
> +
> +/* Local functions.  */
> +
> +extern void _initialize_ft32_tdep (void);
> +
> +/* Use an invalid address -1 as 'not available' marker.  */
> +enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
> +
> +struct ft32_frame_cache
> +{
> +  /* Base address.  */
> +  CORE_ADDR base;
> +  CORE_ADDR pc;
> +  LONGEST framesize;
> +  CORE_ADDR saved_regs[FT32_NUM_REGS];
> +  CORE_ADDR saved_sp;
> +  bfd_boolean established;  /* Has the new frame been LINKed.  */
> +};

Can you describe each field, please? A quick short description is
often sufficient, but all fields should have some documentation
saying what the component is about. In terms of style for those,
we usually avoid comments on the same line, as they have a tendency
to get longer over time, and we often eventually move them to be
on their own on the line(s) before.  I suggest you follow what
you have done with your "Base address" comment.

(I am curious: why did you capitalize "LINK" in "LINKed"?)

> +/* Implement the "frame_align" gdbarch method.  */
> +
> +static CORE_ADDR
> +ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
> +{
> +  /* Align to the size of an instruction (so that they can safely be
> +     pushed onto the stack.  */
> +  return sp & ~1;
> +}
> +
> +/* Implement the "breakpoint_from_pc" gdbarch method.  */
> +
> +static const unsigned char *
> +ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
> +			 CORE_ADDR *pcptr, int *lenptr)
> +{
> +  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
> +
> +  *lenptr = sizeof (breakpoint);
> +  return breakpoint;
> +}
> +
> +/* FT32 register names.  */
> +
> +static const char * const ft32_register_names[] =

Can you remove the space after the '*', please.

> +{
> +    "fp", "sp",
> +    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
> +    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
> +    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
> +    "r24", "r25", "r26", "r27", "r28", "cc",
> +    "pc"
> +};
> +
> +/* Implement the "register_name" gdbarch method.  */
> +
> +static const char *
> +ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
> +{
> +  if (reg_nr < 0)
> +    return NULL;
> +  if (reg_nr >= FT32_NUM_REGS)
> +    return NULL;
> +  return ft32_register_names[reg_nr];
> +}
> +
> +/* Implement the "register_type" gdbarch method.  */
> +
> +static struct type *
> +ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
> +{
> +  if (reg_nr == FT32_PC_REGNUM)
> +    return builtin_type (gdbarch)->builtin_func_ptr;
> +  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
> +    return builtin_type (gdbarch)->builtin_data_ptr;
> +  else
> +    return builtin_type (gdbarch)->builtin_int32;
> +}
> +
> +/* Write into appropriate registers a function return value
> +   of type TYPE, given in virtual format.  */
> +
> +static void
> +ft32_store_return_value (struct type *type, struct regcache *regcache,
> +			 const void *valbuf)

valbuf should probably be a "const gdb_byte *" instead of "const void *".

> +{
> +  struct gdbarch *gdbarch = get_regcache_arch (regcache);
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  CORE_ADDR regval;
> +  int len = TYPE_LENGTH (type);
> +
> +  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
> +  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
> +  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
> +  if (len > 4)
> +    {
> +      regval = extract_unsigned_integer ((gdb_byte *) valbuf + 4,

The above will actually allow you to remove this cast.

> +					 len - 4, byte_order);
> +      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
> +    }
> +}
> +
> +/* Decode the instructions within the given address range.  Decide
> +   when we must have reached the end of the function prologue.  If a
> +   frame_info pointer is provided, fill in its saved_regs etc.
> +
> +   Returns the address of the first instruction after the prologue.  */
> +
> +#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
> +#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
> +#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
> +#define LINK_SIZE(inst) ((inst) & 0xffff)
> +
> +static CORE_ADDR
> +ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
> +		       struct ft32_frame_cache *cache,
> +		       struct gdbarch *gdbarch)
> +{
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  CORE_ADDR next_addr;
> +  ULONGEST inst, inst2;
> +  LONGEST offset;
> +  int regnum;
> +
> +  cache->saved_regs[FT32_PC_REGNUM] = 0;
> +  cache->framesize = 0;
> +
> +  if (start_addr >= end_addr)
> +      return end_addr;
> +
> +  cache->established = 0;
> +  for (next_addr = start_addr; next_addr < end_addr; )
> +    {
> +      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
> +
> +      if (IS_PUSH (inst))
> +	{
> +	  regnum = PUSH_REG (inst);
> +	  cache->framesize += 4;
> +	  cache->saved_regs[regnum] = cache->framesize;
> +	  next_addr += 4;
> +	}
> +      else
> +	break;
> +    }
> +  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
> +    {
> +      if (cache->saved_regs[regnum] != REG_UNAVAIL)
> +	cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
> +    }
> +  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
> +
> +  /* It is a LINK?  */
> +  if (next_addr < end_addr)
> +    {
> +      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
> +      if (IS_LINK (inst))
> +	{
> +	  cache->established = 1;
> +	  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
> +	    {
> +	      if (cache->saved_regs[regnum] != REG_UNAVAIL)
> +		cache->saved_regs[regnum] += 4;
> +	    }
> +	  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
> +	  cache->saved_regs[FT32_FP_REGNUM] = 0;
> +	  cache->framesize += LINK_SIZE (inst);
> +	  next_addr += 4;
> +	}
> +    }
> +
> +  return next_addr;
> +}
> +
> +/* Find the end of function prologue.  */
> +
> +static CORE_ADDR
> +ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
> +{
> +  CORE_ADDR func_addr = 0, func_end = 0;
> +  const char *func_name;
> +
> +  /* See if we can determine the end of the prologue via the symbol table.
> +     If so, then return either PC, or the PC after the prologue, whichever
> +     is greater.  */
> +  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
> +    {
> +      CORE_ADDR post_prologue_pc
> +	= skip_prologue_using_sal (gdbarch, func_addr);
> +      if (post_prologue_pc != 0)
> +	return max (pc, post_prologue_pc);
> +      else
> +	{
> +	  /* Can't determine prologue from the symbol table, need to examine
> +	     instructions.  */
> +	  struct symtab_and_line sal;
> +	  struct symbol *sym;
> +	  struct ft32_frame_cache cache;
> +	  CORE_ADDR plg_end;
> +
> +	  memset (&cache, 0, sizeof cache);
> +
> +	  plg_end = ft32_analyze_prologue (func_addr,
> +					   func_end, &cache, gdbarch);
> +	  /* Found a function.  */
> +	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
> +	  /* Don't use line number debug info for assembly source files.  */
> +	  if (sym && SYMBOL_LANGUAGE (sym) != language_asm)

Small GDB-ism: We have a rule that says we should try to be explictly
checking against NULL (now). So can you replace "sym &&" by "sym != NULL
&&"?


> +	    {
> +	      sal = find_pc_line (func_addr, 0);
> +	      if (sal.end && sal.end < func_end)
> +		{
> +		  /* Found a line number, use it as end of
> +		     prologue.  */

Nit-pick, but, ISTM that you can easily join those two lines and have
the comment be on a single line?

> +		  return sal.end;
> +		}
> +	    }
> +	  /* No useable line symbol.  Use result of prologue parsing method.  */
> +	  return plg_end;
> +	}
> +    }
> +
> +  /* No function symbol -- just return the PC.  */
> +  return pc;
> +}
> +
> +/* Implement the "read_pc" gdbarch method.  */
> +
> +static CORE_ADDR
> +ft32_read_pc (struct regcache *regcache)
> +{
> +  ULONGEST pc;
> +
> +  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
> +  return pc;
> +}
> +
> +/* Implement the "write_pc" gdbarch method.  */
> +
> +static void
> +ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
> +{
> +  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
> +}
> +
> +/* Implement the "unwind_sp" gdbarch method.  */
> +
> +static CORE_ADDR
> +ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
> +{
> +  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
> +}
> +
> +/* Given a return value in `regbuf' with a type `valtype',
> +   extract and copy its value into `valbuf'.  */
> +
> +static void
> +ft32_extract_return_value (struct type *type, struct regcache *regcache,
> +			   void *dst)

I suspect that dst should also be a "gdb_byte *".

> +{
> +  struct gdbarch *gdbarch = get_regcache_arch (regcache);
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  bfd_byte *valbuf = dst;
> +  int len = TYPE_LENGTH (type);
> +  ULONGEST tmp;
> +
> +  /* By using store_unsigned_integer we avoid having to do
> +     anything special for small big-endian values.  */
> +  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
> +  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
> +
> +  /* Ignore return values more than 8 bytes in size because the ft32
> +     returns anything more than 8 bytes in the stack.  */
> +  if (len > 4)
> +    {
> +      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
> +      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
> +    }
> +}
> +
> +/* Implement the "return_value" gdbarch method.  */
> +
> +static enum return_value_convention
> +ft32_return_value (struct gdbarch *gdbarch, struct value *function,
> +		   struct type *valtype, struct regcache *regcache,
> +		   gdb_byte *readbuf, const gdb_byte *writebuf)
> +{
> +  if (TYPE_LENGTH (valtype) > 8)
> +    return RETURN_VALUE_STRUCT_CONVENTION;
> +  else
> +    {
> +      if (readbuf != NULL)
> +	ft32_extract_return_value (valtype, regcache, readbuf);
> +      if (writebuf != NULL)
> +	ft32_store_return_value (valtype, regcache, writebuf);
> +      return RETURN_VALUE_REGISTER_CONVENTION;
> +    }
> +}
> +
> +/* Allocate and initialize a ft32_frame_cache object.  */
> +
> +static struct ft32_frame_cache *
> +ft32_alloc_frame_cache (void)
> +{
> +  struct ft32_frame_cache *cache;
> +  int i;
> +
> +  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
> +
> +  cache->base = 0;
> +  cache->saved_sp = 0;
> +  cache->pc = 0;
> +  cache->framesize = 0;

FRAME_OBSTACK_ZALLOC already zero'es the data being allocated, so
the above is really unnecessary.

> +  for (i = 0; i < FT32_NUM_REGS; ++i)
> +    cache->saved_regs[i] = REG_UNAVAIL;
> +
> +  return cache;
> +}
> +
> +/* Populate a ft32_frame_cache object for this_frame.  */
> +
> +static struct ft32_frame_cache *
> +ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
> +{
> +  struct ft32_frame_cache *cache;
> +  CORE_ADDR current_pc;
> +  int i;
> +
> +  if (*this_cache)
> +    return *this_cache;
> +
> +  cache = ft32_alloc_frame_cache ();
> +  *this_cache = cache;
> +
> +  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
> +  if (cache->base == 0)
> +    return cache;
> +
> +  cache->pc = get_frame_func (this_frame);
> +  current_pc = get_frame_pc (this_frame);
> +  if (cache->pc)
> +    {
> +      struct gdbarch *gdbarch = get_frame_arch (this_frame);
> +      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
> +      if (!cache->established)
> +	cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
> +    }

Small nit - in the block above, please add an empty line after
local variable declarations (gdbarch). It is another one of
the coding-style rules specific to GDB.

> +  cache->saved_sp = cache->base - 4;
> +
> +  for (i = 0; i < FT32_NUM_REGS; ++i)
> +    if (cache->saved_regs[i] != REG_UNAVAIL)
> +      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
> +
> +  return cache;
> +}
> +
> +/* Implement the "unwind_pc" gdbarch method.  */
> +
> +static CORE_ADDR
> +ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
> +{
> +  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
> +}
> +
> +/* Given a GDB frame, determine the address of the calling function's
> +   frame.  This will be used to create a new GDB frame struct.  */
> +
> +static void
> +ft32_frame_this_id (struct frame_info *this_frame,
> +		    void **this_prologue_cache, struct frame_id *this_id)
> +{
> +  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
> +						     this_prologue_cache);
> +
> +  /* This marks the outermost frame.  */
> +  if (cache->base == 0)
> +    return;
> +
> +  *this_id = frame_id_build (cache->saved_sp, cache->pc);
> +}
> +
> +/* Get the value of register regnum in the previous stack frame.  */
> +static struct value *
> +ft32_frame_prev_register (struct frame_info *this_frame,
> +			  void **this_prologue_cache, int regnum)
> +{
> +  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
> +						     this_prologue_cache);
> +
> +  gdb_assert (regnum >= 0);
> +
> +  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
> +    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
> +
> +  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
> +      return frame_unwind_got_memory (this_frame, regnum,
> +				      RAM_BIAS | cache->saved_regs[regnum]);
> +
> +  return frame_unwind_got_register (this_frame, regnum, regnum);
> +}
> +
> +static const struct frame_unwind ft32_frame_unwind =
> +{
> +  NORMAL_FRAME,
> +  default_frame_unwind_stop_reason,
> +  ft32_frame_this_id,
> +  ft32_frame_prev_register,
> +  NULL,
> +  default_frame_sniffer
> +};
> +
> +/* Return the base address of this_frame.  */
> +
> +static CORE_ADDR
> +ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
> +{
> +  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
> +						     this_cache);
> +
> +  return cache->base;
> +}
> +
> +static const struct frame_base ft32_frame_base =
> +{
> +  &ft32_frame_unwind,
> +  ft32_frame_base_address,
> +  ft32_frame_base_address,
> +  ft32_frame_base_address
> +};
> +
> +static struct frame_id
> +ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
> +{
> +  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
> +
> +  return frame_id_build (sp, get_frame_pc (this_frame));
> +}
> +
> +/* Allocate and initialize the ft32 gdbarch object.  */
> +
> +static struct gdbarch *
> +ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
> +{
> +  struct gdbarch *gdbarch;
> +  struct gdbarch_tdep *tdep;
> +
> +  /* If there is already a candidate, use it.  */
> +  arches = gdbarch_list_lookup_by_info (arches, &info);
> +  if (arches != NULL)
> +    return arches->gdbarch;
> +
> +  /* Allocate space for the new architecture.  */
> +  tdep = XNEW (struct gdbarch_tdep);
> +  gdbarch = gdbarch_alloc (&info, tdep);
> +
> +  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
> +  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
> +  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
> +
> +  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
> +  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
> +  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
> +  set_gdbarch_register_name (gdbarch, ft32_register_name);
> +  set_gdbarch_register_type (gdbarch, ft32_register_type);
> +
> +  set_gdbarch_return_value (gdbarch, ft32_return_value);
> +
> +  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
> +  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
> +  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
> +  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
> +
> +  frame_base_set_default (gdbarch, &ft32_frame_base);
> +
> +  /* Methods for saving / extracting a dummy frame's ID.  The ID's
> +     stack address must match the SP value returned by
> +     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
> +  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
> +
> +  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
> +
> +  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
> +
> +  /* Hook in ABI-specific overrides, if they have been registered.  */
> +  gdbarch_init_osabi (info, gdbarch);
> +
> +  /* Hook in the default unwinders.  */
> +  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
> +
> +  /* Support simple overlay manager.  */
> +  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
> +
> +  return gdbarch;
> +}
> +
> +/* Register this machine's init routine.  */
> +
> +void
> +_initialize_ft32_tdep (void)
> +{
> +  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
> +}
> diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
> new file mode 100644
> index 0000000..5c52480
> --- /dev/null
> +++ b/gdb/ft32-tdep.h
> @@ -0,0 +1,28 @@
> +/* Target-dependent code for the FT32.
> +
> +   Copyright (C) 2002-2015 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef FT32_TDEP_H
> +#define FT32_TDEP_H
> +
> +struct gdbarch_tdep
> +{
> +  /* gdbarch target dependent data here.  Currently unused for FT32.  */
> +};
> +
> +#endif /* FT32_TDEP_H */
> diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
> new file mode 100644
> index 0000000..6602df3
> --- /dev/null
> +++ b/include/gdb/sim-ft32.h
> @@ -0,0 +1,34 @@
> +/* This file defines the interface between the FT32 simulator and GDB.
> +
> +   Copyright (C) 2005-2015 Free Software Foundation, Inc.
> +   Contributed by FTDI.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +
> +/* Register numbers of various important registers.  */
> +enum ft32_regnum
> +{
> +  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
> +  FT32_SP_REGNUM,               /* Address of top of stack.  */
> +  FT32_R0_REGNUM,
> +  FT32_R1_REGNUM,
> +  FT32_PC_REGNUM = 32           /* Program counter.  */
> +};
> +
> +/* Number of machine registers.  */
> +#define FT32_NUM_REGS 33        /* 32 real registers + PC */
> +
> -- 
> 1.7.9.5
> 
> 

-- 
Joel

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20 12:46                 ` Joel Brobecker
@ 2015-03-20 15:47                   ` Mike Frysinger
  2015-03-20 15:50                     ` Joel Brobecker
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-20 15:47 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: James Bowman, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 437 bytes --]

On 20 Mar 2015 08:46, Joel Brobecker wrote:
> > include/gdb/:
> > 2015-03-19  James Bowman  <james.bowman@ftdichip.com>
> > 
> >         * sim-ft32.h: New file, FT32 simulator/GDB interface.
> 
> Shouldn't this part of the "sim" patch? (which means that the GDB
> part would be conditional on the "sim" part being pushed first).

only gdb uses this header to interface with the sim directly, so it could go 
either way
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20 15:47                   ` Mike Frysinger
@ 2015-03-20 15:50                     ` Joel Brobecker
  2015-03-22 22:33                       ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: Joel Brobecker @ 2015-03-20 15:50 UTC (permalink / raw)
  To: James Bowman, gdb-patches

> > > 2015-03-19  James Bowman  <james.bowman@ftdichip.com>
> > > 
> > >         * sim-ft32.h: New file, FT32 simulator/GDB interface.
> > 
> > Shouldn't this part of the "sim" patch? (which means that the GDB
> > part would be conditional on the "sim" part being pushed first).
> 
> only gdb uses this header to interface with the sim directly, so it could go 
> either way

Doesn't the sim need that header too - I would think the point of
having the definitions be there would be to make sure that sim and
GDB are in sync?

-- 
Joel

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-20 15:50                     ` Joel Brobecker
@ 2015-03-22 22:33                       ` Mike Frysinger
  2015-03-23 12:36                         ` Joel Brobecker
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-22 22:33 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: James Bowman, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 701 bytes --]

On 20 Mar 2015 11:50, Joel Brobecker wrote:
> > > > 2015-03-19  James Bowman  <james.bowman@ftdichip.com>
> > > > 
> > > >         * sim-ft32.h: New file, FT32 simulator/GDB interface.
> > > 
> > > Shouldn't this part of the "sim" patch? (which means that the GDB
> > > part would be conditional on the "sim" part being pushed first).
> > 
> > only gdb uses this header to interface with the sim directly, so it could go 
> > either way
> 
> Doesn't the sim need that header too - I would think the point of
> having the definitions be there would be to make sure that sim and
> GDB are in sync?

the sim currently duplicates the defines, but it prob should include it directly
-mike

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-22 22:33                       ` Mike Frysinger
@ 2015-03-23 12:36                         ` Joel Brobecker
  2015-03-23 19:15                           ` James Bowman
  0 siblings, 1 reply; 30+ messages in thread
From: Joel Brobecker @ 2015-03-23 12:36 UTC (permalink / raw)
  To: James Bowman, gdb-patches

> > Doesn't the sim need that header too - I would think the point of
> > having the definitions be there would be to make sure that sim and
> > GDB are in sync?
> 
> the sim currently duplicates the defines, but it prob should include
> it directly

Oy! I didn't realize that the definitions were duplicated. Yes, indeed,
let's make sure the sim and gdb both use the same defines.

-- 
Joel

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-23 12:36                         ` Joel Brobecker
@ 2015-03-23 19:15                           ` James Bowman
  2015-03-23 23:04                             ` Joel Brobecker
  0 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-03-23 19:15 UTC (permalink / raw)
  To: Joel Brobecker, gdb-patches


> Oy! I didn't realize that the definitions were duplicated. Yes, indeed,
> let's make sure the sim and gdb both use the same defines.

OK, all done. I have updated the patch for your review. sim-ft32.h is
still part of gdb, and the sim uses its definitions for the register
read interface.

--
James Bowman
FTDI Open Source Liaison


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

	* Makefile.in (ALL_TARGET_OBS): Add ft32-tdep.o.
	(HFILES_NO_SRCDIR): Add ft32-tdep.h.
	(ALLDEPFILES): Add ft32-tdep.c.
	* configure.tgt: Add FT32 entry.
	* ft32-tdep.c: New file, FT32 target-dependent code.
	* ft32-tdep.h: New file, FT32 target-dependent code.

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

	* sim-ft32.h: New file, FT32 simulator/GDB interface.


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

---
 gdb/Makefile.in        |    5 +-
 gdb/configure.tgt      |    5 +
 gdb/ft32-tdep.c        |  549 ++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/ft32-tdep.h        |   28 +++
 include/gdb/sim-ft32.h |   35 +++
 5 files changed, 621 insertions(+), 1 deletion(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h
 create mode 100644 include/gdb/sim-ft32.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index dbace2d..c844d38 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -651,6 +651,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-linux-tdep.o hppa-tdep.o \
@@ -949,7 +950,8 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1655,6 +1657,7 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
+	ft32-tdep.c \
 	glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index c97ebdd..8feda7c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -615,6 +615,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..41a7191
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,549 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+#include "gdb/sim-ft32.h"
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address -1 as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
+
+struct ft32_frame_cache
+{
+  /* Base address of the frame */
+  CORE_ADDR base;
+  /* Function this frame belongs to */
+  CORE_ADDR pc;
+  /* Total size of this frame */
+  LONGEST framesize;
+  /* Saved registers in this frame */
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  /* Saved SP in this frame */
+  CORE_ADDR saved_sp;
+  /* Has the new frame been LINKed.  */
+  bfd_boolean established;
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+			 CORE_ADDR *pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+static const char *const ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+			 const gdb_byte *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer (valbuf + 4,
+					 len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+		       struct ft32_frame_cache *cache,
+		       struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+      return end_addr;
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH (inst))
+	{
+	  regnum = PUSH_REG (inst);
+	  cache->framesize += 4;
+	  cache->saved_regs[regnum] = cache->framesize;
+	  next_addr += 4;
+	}
+      else
+	break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+	cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  /* It is a LINK?  */
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK (inst))
+	{
+	  cache->established = 1;
+	  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+	    {
+	      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+		cache->saved_regs[regnum] += 4;
+	    }
+	  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+	  cache->saved_regs[FT32_FP_REGNUM] = 0;
+	  cache->framesize += LINK_SIZE (inst);
+	  next_addr += 4;
+	}
+    }
+
+  return next_addr;
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+      else
+	{
+	  /* Can't determine prologue from the symbol table, need to examine
+	     instructions.  */
+	  struct symtab_and_line sal;
+	  struct symbol *sym;
+	  struct ft32_frame_cache cache;
+	  CORE_ADDR plg_end;
+
+	  memset (&cache, 0, sizeof cache);
+
+	  plg_end = ft32_analyze_prologue (func_addr,
+					   func_end, &cache, gdbarch);
+	  /* Found a function.  */
+	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+	  /* Don't use line number debug info for assembly source files.  */
+	  if ((sym != NULL) && SYMBOL_LANGUAGE (sym) != language_asm)
+	    {
+	      sal = find_pc_line (func_addr, 0);
+	      if (sal.end && sal.end < func_end)
+		{
+		  /* Found a line number, use it as end of prologue.  */
+		  return sal.end;
+		}
+	    }
+	  /* No useable line symbol.  Use result of prologue parsing method.  */
+	  return plg_end;
+	}
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+			   gdb_byte *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *valtype, struct regcache *regcache,
+		   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+	ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+	ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+	cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+      return frame_unwind_got_memory (this_frame, regnum,
+				      RAM_BIAS | cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..5c52480
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,28 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for FT32.  */
+};
+
+#endif /* FT32_TDEP_H */
diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
new file mode 100644
index 0000000..73c4f63
--- /dev/null
+++ b/include/gdb/sim-ft32.h
@@ -0,0 +1,35 @@
+/* This file defines the interface between the FT32 simulator and GDB.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by FTDI.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* Register numbers of various important registers.  */
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
+  FT32_SP_REGNUM,               /* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_CC_REGNUM = 31,
+  FT32_PC_REGNUM = 32           /* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33        /* 32 real registers + PC */
+
-- 
1.7.9.5

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-20  5:19                   ` Mike Frysinger
@ 2015-03-23 19:21                     ` James Bowman
  2015-03-26  7:10                       ` Mike Frysinger
  0 siblings, 1 reply; 30+ messages in thread
From: James Bowman @ 2015-03-23 19:21 UTC (permalink / raw)
  To: Mike Frysinger, gdb-patches

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

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

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-23 19:15                           ` James Bowman
@ 2015-03-23 23:04                             ` Joel Brobecker
  0 siblings, 0 replies; 30+ messages in thread
From: Joel Brobecker @ 2015-03-23 23:04 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

On Mon, Mar 23, 2015 at 07:15:32PM +0000, James Bowman wrote:
> 
> > Oy! I didn't realize that the definitions were duplicated. Yes, indeed,
> > let's make sure the sim and gdb both use the same defines.
> 
> OK, all done. I have updated the patch for your review. sim-ft32.h is
> still part of gdb, and the sim uses its definitions for the register
> read interface.

OK - If prefer to do it this way, then you will need to merge
the GDB and sim part into the same patch. This is because you can't
commit the GDB part without the sim (because of the configure bit
that pulls in libsim.a), and you can't commit the sim part first,
because it depends on sim-ft32.h which is brought in by the GDB
part. That's why I thought it would make better sense for this file
to be part of the sim patch, but if both patches get approved, and
you merge them before pushing them, then I'm happy.

> gdb/:
> 2015-03-23  James Bowman  <james.bowman@ftdichip.com>
> 
> 	* Makefile.in (ALL_TARGET_OBS): Add ft32-tdep.o.
> 	(HFILES_NO_SRCDIR): Add ft32-tdep.h.
> 	(ALLDEPFILES): Add ft32-tdep.c.
> 	* configure.tgt: Add FT32 entry.
> 	* ft32-tdep.c: New file, FT32 target-dependent code.
> 	* ft32-tdep.h: New file, FT32 target-dependent code.
> 
> include/gdb/:
> 2015-03-23  James Bowman  <james.bowman@ftdichip.com>
> 
> 	* sim-ft32.h: New file, FT32 simulator/GDB interface.

This part of the submission is pre-approved, with a couple of
very minor comments below.

When you do push to sourceware.org, would you mind sending a copy
of the patch that actually got pushed? (standard protocol for all
patches, for our records on this mailing-list)

(if you need write-after-approval access to the repository, please
send me an email privately)

Thank you,
-- 
Joel

> +static void
> +ft32_store_return_value (struct type *type, struct regcache *regcache,
> +			 const gdb_byte *valbuf)
> +{
> +  struct gdbarch *gdbarch = get_regcache_arch (regcache);
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  CORE_ADDR regval;
> +  int len = TYPE_LENGTH (type);
> +
> +  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
> +  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
> +  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
> +  if (len > 4)
> +    {
> +      regval = extract_unsigned_integer (valbuf + 4,
> +					 len - 4, byte_order);

You don't have to change it if you prefer it this way, but
I think you can join those 2 lines, now.

> +	  /* Don't use line number debug info for assembly source files.  */
> +	  if ((sym != NULL) && SYMBOL_LANGUAGE (sym) != language_asm)

You don't need the parentheses around "sym != NULL".

-- 
Joel

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

* Re: [PATCH, FT32] gdb and sim support
  2015-03-23 19:21                     ` James Bowman
@ 2015-03-26  7:10                       ` Mike Frysinger
  2015-03-26 19:05                         ` James Bowman
  0 siblings, 1 reply; 30+ messages in thread
From: Mike Frysinger @ 2015-03-26  7:10 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1598 bytes --]

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

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* RE: [PATCH, FT32] gdb and sim support
  2015-03-26  7:10                       ` Mike Frysinger
@ 2015-03-26 19:05                         ` James Bowman
  0 siblings, 0 replies; 30+ messages in thread
From: James Bowman @ 2015-03-26 19:05 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: gdb-patches

> > +/*
> > + * 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

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

* Re: [PATCH, FT32] gdb and sim support
  2015-02-03  4:06 [PATCH, FT32] gdb and sim support James Bowman
                   ` (2 preceding siblings ...)
       [not found] ` <ORIGINAL-RELEASE-1424557036-20150219070610.GI544@vapier>
@ 2015-03-28  6:21 ` Mike Frysinger
  3 siblings, 0 replies; 30+ messages in thread
From: Mike Frysinger @ 2015-03-28  6:21 UTC (permalink / raw)
  To: James Bowman; +Cc: gdb-patches


[-- Attachment #1.1: Type: text/plain, Size: 255 bytes --]

i moved the sim header to the sim patch and pushed that first, then the gdb one.  
i don't like having commits that are not bisectable, nor having to squash large 
ones to avoid.

see attached patches for what went in.

thanks for the port!
-mike

[-- Attachment #1.2: 0001-sim-ft32-new-port.patch --]
[-- Type: text/x-diff, Size: 55332 bytes --]

From f46e4eb78ba55e8bf8dedd98d7fe354729181710 Mon Sep 17 00:00:00 2001
From: James Bowman <james.bowman@ftdichip.com>
Date: Mon, 23 Mar 2015 19:20:59 +0000
Subject: [PATCH 1/2] sim: ft32: new port

FT32 is a new high performance 32-bit RISC core developed by FTDI for
embedded applications.
---
 include/gdb/ChangeLog                |     4 +
 include/gdb/sim-ft32.h               |    35 +
 sim/ChangeLog                        |     5 +
 sim/configure                        |     9 +
 sim/configure.tgt                    |     4 +
 sim/ft32/ChangeLog                   |     8 +
 sim/ft32/Makefile.in                 |    48 +
 sim/ft32/aclocal.m4                  |   129 +
 sim/ft32/config.in                   |   163 +
 sim/ft32/configure                   | 15740 +++++++++++++++++++++++++++++++++
 sim/ft32/configure.ac                |    15 +
 sim/ft32/ft32-sim.h                  |    43 +
 sim/ft32/interp.c                    |   913 ++
 sim/ft32/sim-main.h                  |    58 +
 sim/testsuite/ChangeLog              |     4 +
 sim/testsuite/configure              |     4 +
 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 +
 20 files changed, 18127 insertions(+)
 create mode 100644 include/gdb/sim-ft32.h
 create mode 100644 sim/ft32/ChangeLog
 create mode 100644 sim/ft32/Makefile.in
 create mode 100644 sim/ft32/aclocal.m4
 create mode 100644 sim/ft32/config.in
 create mode 100755 sim/ft32/configure
 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

diff --git a/include/gdb/ChangeLog b/include/gdb/ChangeLog
index 88bd15c..d36d6b8 100644
--- a/include/gdb/ChangeLog
+++ b/include/gdb/ChangeLog
@@ -1,3 +1,7 @@
+2015-03-28  James Bowman  <james.bowman@ftdichip.com>
+
+	* sim-ft32.h: New file.
+
 2015-05-15  Mike Frysinger  <vapier@gentoo.org>
 
 	* remote-sim.h (struct host_callback_struct): Define.
diff --git a/include/gdb/sim-ft32.h b/include/gdb/sim-ft32.h
new file mode 100644
index 0000000..73c4f63
--- /dev/null
+++ b/include/gdb/sim-ft32.h
@@ -0,0 +1,35 @@
+/* This file defines the interface between the FT32 simulator and GDB.
+
+   Copyright (C) 2005-2015 Free Software Foundation, Inc.
+   Contributed by FTDI.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* Register numbers of various important registers.  */
+enum ft32_regnum
+{
+  FT32_FP_REGNUM,               /* Address of executing stack frame.  */
+  FT32_SP_REGNUM,               /* Address of top of stack.  */
+  FT32_R0_REGNUM,
+  FT32_R1_REGNUM,
+  FT32_CC_REGNUM = 31,
+  FT32_PC_REGNUM = 32           /* Program counter.  */
+};
+
+/* Number of machine registers.  */
+#define FT32_NUM_REGS 33        /* 32 real registers + PC */
+
diff --git a/sim/ChangeLog b/sim/ChangeLog
index 37ee409..8b7fcb3 100644
--- a/sim/ChangeLog
+++ b/sim/ChangeLog
@@ -1,3 +1,8 @@
+2015-03-28  James Bowman  <james.bowman@ftdichip.com>
+
+	* configure.tgt: Add FT32 entry.
+	* configure: Regenerate.
+
 2015-03-16  Mike Frysinger  <vapier@gentoo.org>
 
 	* .gitignore: Delete tconfig.h.
diff --git a/sim/configure b/sim/configure
index 36d356e..740042a 100755
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/ChangeLog b/sim/ft32/ChangeLog
new file mode 100644
index 0000000..1479658
--- /dev/null
+++ b/sim/ft32/ChangeLog
@@ -0,0 +1,8 @@
+2015-03-28  James Bowman  <james.bowman@ftdichip.com>
+
+	* Makefile.in: New file, FT32 sim Makefile.
+	* configure.ac: 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.
+	* aclocal.m4, config.in, configure: Regenerate.
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/aclocal.m4 b/sim/ft32/aclocal.m4
new file mode 100644
index 0000000..517bc73
diff --git a/sim/ft32/config.in b/sim/ft32/config.in
new file mode 100644
index 0000000..6003e58
diff --git a/sim/ft32/configure b/sim/ft32/configure
new file mode 100755
index 0000000..1d18263
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..931ad2b
--- /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..6cb7c7e
--- /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/ChangeLog b/sim/testsuite/ChangeLog
index be7ad56..7580586 100644
--- a/sim/testsuite/ChangeLog
+++ b/sim/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2015-03-28  James Bowman  <james.bowman@ftdichip.com>
+
+	* configure: Regenerate.
+
 2014-03-10  Mike Frysinger  <vapier@gentoo.org>
 
 	* configure: Regenerate.
diff --git a/sim/testsuite/configure b/sim/testsuite/configure
index f90bd47..1dcaa41 100755
diff --git a/sim/testsuite/sim/ft32/ChangeLog b/sim/testsuite/sim/ft32/ChangeLog
new file mode 100644
index 0000000..a0cfa18
--- /dev/null
+++ b/sim/testsuite/sim/ft32/ChangeLog
@@ -0,0 +1,3 @@
+2015-02-28  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..c92f295
--- /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
-- 
2.3.3


[-- Attachment #1.3: 0002-gdb-ft32-new-port.patch --]
[-- Type: text/x-diff, Size: 20728 bytes --]

From 49d45b20c01da11b7493a5c28bdced7558999d6d Mon Sep 17 00:00:00 2001
From: James Bowman <james.bowman@ftdichip.com>
Date: Mon, 23 Mar 2015 19:15:32 +0000
Subject: [PATCH 2/2] gdb: ft32: new port

FT32 is a new high performance 32-bit RISC core developed by FTDI for
embedded applications.
---
 gdb/ChangeLog     |   9 +
 gdb/Makefile.in   |   5 +-
 gdb/configure.tgt |   5 +
 gdb/ft32-tdep.c   | 549 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/ft32-tdep.h   |  28 +++
 5 files changed, 595 insertions(+), 1 deletion(-)
 create mode 100644 gdb/ft32-tdep.c
 create mode 100644 gdb/ft32-tdep.h

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6c6b94e..fe6811e 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,12 @@
+2015-03-28  James Bowman  <james.bowman@ftdichip.com>
+
+	* Makefile.in (ALL_TARGET_OBS): Add ft32-tdep.o.
+	(HFILES_NO_SRCDIR): Add ft32-tdep.h.
+	(ALLDEPFILES): Add ft32-tdep.c.
+	* configure.tgt: Add FT32 entry.
+	* ft32-tdep.c: New file, FT32 target-dependent code.
+	* ft32-tdep.h: New file, FT32 target-dependent code.
+
 2015-03-27  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	Revert:
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ae50041..907997b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -651,6 +651,7 @@ ALL_TARGET_OBS = \
 	dicos-tdep.o \
 	fbsd-tdep.o \
 	frv-linux-tdep.o frv-tdep.o \
+	ft32-tdep.o \
 	h8300-tdep.o \
 	hppabsd-tdep.o hppanbsd-tdep.o hppaobsd-tdep.o \
 	hppa-linux-tdep.o hppa-tdep.o \
@@ -949,7 +950,8 @@ psymtab.h psympriv.h progspace.h bfin-tdep.h \
 amd64-darwin-tdep.h charset-list.h \
 config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \
 dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
-i386-darwin-tdep.h x86-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h nios2-tdep.h \
+ft32-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 record-full.h solib-aix.h \
@@ -1655,6 +1657,7 @@ ALLDEPFILES = \
 	fbsd-nat.c \
 	fbsd-tdep.c \
 	fork-child.c \
+	ft32-tdep.c \
 	glibc-tdep.c \
 	go32-nat.c h8300-tdep.c \
 	hppa-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index c97ebdd..8feda7c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -615,6 +615,11 @@ xstormy16-*-*)
 	# No simulator libraries are needed -- target uses SID.
 	;;
 
+ft32-*-elf)
+	gdb_target_obs="ft32-tdep.o"
+	gdb_sim=../sim/ft32/libsim.a
+	;;
+
 v850*-*-elf | v850*-*-rtems*)
 	# Target: NEC V850 processor
 	gdb_target_obs="v850-tdep.o"
diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c
new file mode 100644
index 0000000..41a7191
--- /dev/null
+++ b/gdb/ft32-tdep.c
@@ -0,0 +1,549 @@
+/* Target-dependent code for FT32.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "value.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "osabi.h"
+#include "language.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "dis-asm.h"
+#include "record.h"
+
+#include "gdb_assert.h"
+
+#include "ft32-tdep.h"
+#include "gdb/sim-ft32.h"
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+/* Local functions.  */
+
+extern void _initialize_ft32_tdep (void);
+
+/* Use an invalid address -1 as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) (-1) };
+
+struct ft32_frame_cache
+{
+  /* Base address of the frame */
+  CORE_ADDR base;
+  /* Function this frame belongs to */
+  CORE_ADDR pc;
+  /* Total size of this frame */
+  LONGEST framesize;
+  /* Saved registers in this frame */
+  CORE_ADDR saved_regs[FT32_NUM_REGS];
+  /* Saved SP in this frame */
+  CORE_ADDR saved_sp;
+  /* Has the new frame been LINKed.  */
+  bfd_boolean established;
+};
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+ft32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* Align to the size of an instruction (so that they can safely be
+     pushed onto the stack.  */
+  return sp & ~1;
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.  */
+
+static const unsigned char *
+ft32_breakpoint_from_pc (struct gdbarch *gdbarch,
+			 CORE_ADDR *pcptr, int *lenptr)
+{
+  static const gdb_byte breakpoint[] = { 0x02, 0x00, 0x34, 0x00 };
+
+  *lenptr = sizeof (breakpoint);
+  return breakpoint;
+}
+
+/* FT32 register names.  */
+
+static const char *const ft32_register_names[] =
+{
+    "fp", "sp",
+    "r0", "r1", "r2", "r3",  "r4", "r5", "r6", "r7",
+    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+    "r16", "r17", "r18", "r19",  "r20", "r21", "r22", "r23",
+    "r24", "r25", "r26", "r27", "r28", "cc",
+    "pc"
+};
+
+/* Implement the "register_name" gdbarch method.  */
+
+static const char *
+ft32_register_name (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr < 0)
+    return NULL;
+  if (reg_nr >= FT32_NUM_REGS)
+    return NULL;
+  return ft32_register_names[reg_nr];
+}
+
+/* Implement the "register_type" gdbarch method.  */
+
+static struct type *
+ft32_register_type (struct gdbarch *gdbarch, int reg_nr)
+{
+  if (reg_nr == FT32_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_func_ptr;
+  else if (reg_nr == FT32_SP_REGNUM || reg_nr == FT32_FP_REGNUM)
+    return builtin_type (gdbarch)->builtin_data_ptr;
+  else
+    return builtin_type (gdbarch)->builtin_int32;
+}
+
+/* Write into appropriate registers a function return value
+   of type TYPE, given in virtual format.  */
+
+static void
+ft32_store_return_value (struct type *type, struct regcache *regcache,
+			 const gdb_byte *valbuf)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR regval;
+  int len = TYPE_LENGTH (type);
+
+  /* Things always get returned in RET1_REGNUM, RET2_REGNUM.  */
+  regval = extract_unsigned_integer (valbuf, len > 4 ? 4 : len, byte_order);
+  regcache_cooked_write_unsigned (regcache, FT32_R0_REGNUM, regval);
+  if (len > 4)
+    {
+      regval = extract_unsigned_integer (valbuf + 4,
+					 len - 4, byte_order);
+      regcache_cooked_write_unsigned (regcache, FT32_R1_REGNUM, regval);
+    }
+}
+
+/* Decode the instructions within the given address range.  Decide
+   when we must have reached the end of the function prologue.  If a
+   frame_info pointer is provided, fill in its saved_regs etc.
+
+   Returns the address of the first instruction after the prologue.  */
+
+#define IS_PUSH(inst)   (((inst) & 0xfff00000) == 0x84000000)
+#define PUSH_REG(inst)  (FT32_R0_REGNUM + (((inst) >> 15) & 0x1f))
+#define IS_LINK(inst)   (((inst) & 0xffff0000) == 0x95d00000)
+#define LINK_SIZE(inst) ((inst) & 0xffff)
+
+static CORE_ADDR
+ft32_analyze_prologue (CORE_ADDR start_addr, CORE_ADDR end_addr,
+		       struct ft32_frame_cache *cache,
+		       struct gdbarch *gdbarch)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR next_addr;
+  ULONGEST inst, inst2;
+  LONGEST offset;
+  int regnum;
+
+  cache->saved_regs[FT32_PC_REGNUM] = 0;
+  cache->framesize = 0;
+
+  if (start_addr >= end_addr)
+      return end_addr;
+
+  cache->established = 0;
+  for (next_addr = start_addr; next_addr < end_addr; )
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+
+      if (IS_PUSH (inst))
+	{
+	  regnum = PUSH_REG (inst);
+	  cache->framesize += 4;
+	  cache->saved_regs[regnum] = cache->framesize;
+	  next_addr += 4;
+	}
+      else
+	break;
+    }
+  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+    {
+      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+	cache->saved_regs[regnum] = cache->framesize - cache->saved_regs[regnum];
+    }
+  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize;
+
+  /* It is a LINK?  */
+  if (next_addr < end_addr)
+    {
+      inst = read_memory_unsigned_integer (next_addr, 4, byte_order);
+      if (IS_LINK (inst))
+	{
+	  cache->established = 1;
+	  for (regnum = FT32_R0_REGNUM; regnum < FT32_PC_REGNUM; regnum++)
+	    {
+	      if (cache->saved_regs[regnum] != REG_UNAVAIL)
+		cache->saved_regs[regnum] += 4;
+	    }
+	  cache->saved_regs[FT32_PC_REGNUM] = cache->framesize + 4;
+	  cache->saved_regs[FT32_FP_REGNUM] = 0;
+	  cache->framesize += LINK_SIZE (inst);
+	  next_addr += 4;
+	}
+    }
+
+  return next_addr;
+}
+
+/* Find the end of function prologue.  */
+
+static CORE_ADDR
+ft32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr = 0, func_end = 0;
+  const char *func_name;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+      else
+	{
+	  /* Can't determine prologue from the symbol table, need to examine
+	     instructions.  */
+	  struct symtab_and_line sal;
+	  struct symbol *sym;
+	  struct ft32_frame_cache cache;
+	  CORE_ADDR plg_end;
+
+	  memset (&cache, 0, sizeof cache);
+
+	  plg_end = ft32_analyze_prologue (func_addr,
+					   func_end, &cache, gdbarch);
+	  /* Found a function.  */
+	  sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
+	  /* Don't use line number debug info for assembly source files.  */
+	  if ((sym != NULL) && SYMBOL_LANGUAGE (sym) != language_asm)
+	    {
+	      sal = find_pc_line (func_addr, 0);
+	      if (sal.end && sal.end < func_end)
+		{
+		  /* Found a line number, use it as end of prologue.  */
+		  return sal.end;
+		}
+	    }
+	  /* No useable line symbol.  Use result of prologue parsing method.  */
+	  return plg_end;
+	}
+    }
+
+  /* No function symbol -- just return the PC.  */
+  return pc;
+}
+
+/* Implement the "read_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_read_pc (struct regcache *regcache)
+{
+  ULONGEST pc;
+
+  regcache_cooked_read_unsigned (regcache, FT32_PC_REGNUM, &pc);
+  return pc;
+}
+
+/* Implement the "write_pc" gdbarch method.  */
+
+static void
+ft32_write_pc (struct regcache *regcache, CORE_ADDR val)
+{
+  regcache_cooked_write_unsigned (regcache, FT32_PC_REGNUM, val);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_SP_REGNUM);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+   extract and copy its value into `valbuf'.  */
+
+static void
+ft32_extract_return_value (struct type *type, struct regcache *regcache,
+			   gdb_byte *dst)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  bfd_byte *valbuf = dst;
+  int len = TYPE_LENGTH (type);
+  ULONGEST tmp;
+
+  /* By using store_unsigned_integer we avoid having to do
+     anything special for small big-endian values.  */
+  regcache_cooked_read_unsigned (regcache, FT32_R0_REGNUM, &tmp);
+  store_unsigned_integer (valbuf, (len > 4 ? len - 4 : len), byte_order, tmp);
+
+  /* Ignore return values more than 8 bytes in size because the ft32
+     returns anything more than 8 bytes in the stack.  */
+  if (len > 4)
+    {
+      regcache_cooked_read_unsigned (regcache, FT32_R1_REGNUM, &tmp);
+      store_unsigned_integer (valbuf + len - 4, 4, byte_order, tmp);
+    }
+}
+
+/* Implement the "return_value" gdbarch method.  */
+
+static enum return_value_convention
+ft32_return_value (struct gdbarch *gdbarch, struct value *function,
+		   struct type *valtype, struct regcache *regcache,
+		   gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (valtype) > 8)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    {
+      if (readbuf != NULL)
+	ft32_extract_return_value (valtype, regcache, readbuf);
+      if (writebuf != NULL)
+	ft32_store_return_value (valtype, regcache, writebuf);
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Allocate and initialize a ft32_frame_cache object.  */
+
+static struct ft32_frame_cache *
+ft32_alloc_frame_cache (void)
+{
+  struct ft32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct ft32_frame_cache);
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Populate a ft32_frame_cache object for this_frame.  */
+
+static struct ft32_frame_cache *
+ft32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  int i;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = ft32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->base = get_frame_register_unsigned (this_frame, FT32_FP_REGNUM);
+  if (cache->base == 0)
+    return cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  if (cache->pc)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+      ft32_analyze_prologue (cache->pc, current_pc, cache, gdbarch);
+      if (!cache->established)
+	cache->base = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+    }
+
+  cache->saved_sp = cache->base - 4;
+
+  for (i = 0; i < FT32_NUM_REGS; ++i)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->base + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+ft32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, FT32_PC_REGNUM);
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+   frame.  This will be used to create a new GDB frame struct.  */
+
+static void
+ft32_frame_this_id (struct frame_info *this_frame,
+		    void **this_prologue_cache, struct frame_id *this_id)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->base == 0)
+    return;
+
+  *this_id = frame_id_build (cache->saved_sp, cache->pc);
+}
+
+/* Get the value of register regnum in the previous stack frame.  */
+
+static struct value *
+ft32_frame_prev_register (struct frame_info *this_frame,
+			  void **this_prologue_cache, int regnum)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_prologue_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (regnum == FT32_SP_REGNUM && cache->saved_sp)
+    return frame_unwind_got_constant (this_frame, regnum, cache->saved_sp);
+
+  if (regnum < FT32_NUM_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+      return frame_unwind_got_memory (this_frame, regnum,
+				      RAM_BIAS | cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind ft32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  ft32_frame_this_id,
+  ft32_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Return the base address of this_frame.  */
+
+static CORE_ADDR
+ft32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct ft32_frame_cache *cache = ft32_frame_cache (this_frame,
+						     this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base ft32_frame_base =
+{
+  &ft32_frame_unwind,
+  ft32_frame_base_address,
+  ft32_frame_base_address,
+  ft32_frame_base_address
+};
+
+static struct frame_id
+ft32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, FT32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Allocate and initialize the ft32 gdbarch object.  */
+
+static struct gdbarch *
+ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
+
+  /* Allocate space for the new architecture.  */
+  tdep = XNEW (struct gdbarch_tdep);
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  set_gdbarch_read_pc (gdbarch, ft32_read_pc);
+  set_gdbarch_write_pc (gdbarch, ft32_write_pc);
+  set_gdbarch_unwind_sp (gdbarch, ft32_unwind_sp);
+
+  set_gdbarch_num_regs (gdbarch, FT32_NUM_REGS);
+  set_gdbarch_sp_regnum (gdbarch, FT32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, FT32_PC_REGNUM);
+  set_gdbarch_register_name (gdbarch, ft32_register_name);
+  set_gdbarch_register_type (gdbarch, ft32_register_type);
+
+  set_gdbarch_return_value (gdbarch, ft32_return_value);
+
+  set_gdbarch_skip_prologue (gdbarch, ft32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, ft32_breakpoint_from_pc);
+  set_gdbarch_frame_align (gdbarch, ft32_frame_align);
+
+  frame_base_set_default (gdbarch, &ft32_frame_base);
+
+  /* Methods for saving / extracting a dummy frame's ID.  The ID's
+     stack address must match the SP value returned by
+     PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos.  */
+  set_gdbarch_dummy_id (gdbarch, ft32_dummy_id);
+
+  set_gdbarch_unwind_pc (gdbarch, ft32_unwind_pc);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_ft32);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Hook in the default unwinders.  */
+  frame_unwind_append_unwinder (gdbarch, &ft32_frame_unwind);
+
+  /* Support simple overlay manager.  */
+  set_gdbarch_overlay_update (gdbarch, simple_overlay_update);
+
+  return gdbarch;
+}
+
+/* Register this machine's init routine.  */
+
+void
+_initialize_ft32_tdep (void)
+{
+  register_gdbarch_init (bfd_arch_ft32, ft32_gdbarch_init);
+}
diff --git a/gdb/ft32-tdep.h b/gdb/ft32-tdep.h
new file mode 100644
index 0000000..5c52480
--- /dev/null
+++ b/gdb/ft32-tdep.h
@@ -0,0 +1,28 @@
+/* Target-dependent code for the FT32.
+
+   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef FT32_TDEP_H
+#define FT32_TDEP_H
+
+struct gdbarch_tdep
+{
+  /* gdbarch target dependent data here.  Currently unused for FT32.  */
+};
+
+#endif /* FT32_TDEP_H */
-- 
2.3.3


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

end of thread, other threads:[~2015-03-28  6:21 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-03  4:06 [PATCH, FT32] gdb and sim support James Bowman
2015-02-10  6:27 ` James Bowman
2015-02-17 10:21   ` Mike Frysinger
2015-02-19  7:06 ` Mike Frysinger
     [not found] ` <ORIGINAL-RELEASE-1424557036-20150219070610.GI544@vapier>
2015-02-23 10:40   ` James Bowman
2015-02-24  4:52     ` Mike Frysinger
2015-03-10 16:56       ` James Bowman
2015-03-16  8:25         ` Mike Frysinger
2015-03-19 15:26           ` James Bowman
2015-03-19 18:52             ` Mike Frysinger
2015-03-20  2:57               ` James Bowman
2015-03-20  5:18                 ` Mike Frysinger
2015-03-20  5:19                   ` Mike Frysinger
2015-03-23 19:21                     ` James Bowman
2015-03-26  7:10                       ` Mike Frysinger
2015-03-26 19:05                         ` James Bowman
2015-03-16 16:38         ` Mike Frysinger
2015-03-17 17:36         ` Joel Brobecker
2015-03-19 15:21           ` James Bowman
2015-03-19 18:54             ` Mike Frysinger
2015-03-20  3:01               ` James Bowman
2015-03-20  3:47                 ` Mike Frysinger
2015-03-20 12:46                 ` Joel Brobecker
2015-03-20 15:47                   ` Mike Frysinger
2015-03-20 15:50                     ` Joel Brobecker
2015-03-22 22:33                       ` Mike Frysinger
2015-03-23 12:36                         ` Joel Brobecker
2015-03-23 19:15                           ` James Bowman
2015-03-23 23:04                             ` Joel Brobecker
2015-03-28  6:21 ` Mike Frysinger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).