This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] gdb: new AndesTech NDS32 port


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a28d8e5037333291991f7b0036b273e8ca1ffc34

commit a28d8e5037333291991f7b0036b273e8ca1ffc34
Author: Yan-Ting Lin <currygt52@gmail.com>
Date:   Fri Jun 17 15:25:08 2016 +0800

    gdb: new AndesTech NDS32 port
    
    gdb/ChangeLog:
    
    	* Makefile.in (ALL_TARGET_OBS): Add nds32-tdep.o.
    	(HFILES_NO_SRCDIR): Add nds32-tdep.h.
    	(ALLDEPFILES): Add nds32-tdep.c.
    	* NEWS: Mention new NDS32 port.
    	* configure.tgt: Add NDS32.
    	* nds32-tdep.c: New file.
    	* nds32-tdep.h: New file.
    	* features/Makefile (XMLTOC): Add nds32.xml.
    	* features/nds32-core.xml: New file.
    	* features/nds32-fpu.xml: New file.
    	* features/nds32-system.xml: New file.
    	* features/nds32.c: New file (generated).
    	* features/nds32.xml: New file.
    
    gdb/doc/ChangeLog:
    
    	* gdb.texinfo (Standard Target Features): Document NDS32 features.
    	(NDS32 Features): New Section.
    
    gdb/testsuite/ChangeLog:
    
    	* gdb.base/float.exp: Add target check for nds32*-*-*.
    	* gdb.xml/tdesc-regs.exp: Set core-regs for nds32*-*-*.

Diff:
---
 gdb/ChangeLog                        |   16 +
 gdb/Makefile.in                      |    4 +-
 gdb/NEWS                             |    4 +
 gdb/configure.tgt                    |    5 +
 gdb/doc/ChangeLog                    |    5 +
 gdb/doc/gdb.texinfo                  |   23 +
 gdb/features/Makefile                |    1 +
 gdb/features/nds32-core.xml          |   44 +
 gdb/features/nds32-fpu.xml           |   42 +
 gdb/features/nds32-system.xml        |   14 +
 gdb/features/nds32.c                 |   92 ++
 gdb/features/nds32.xml               |   14 +
 gdb/nds32-tdep.c                     | 2184 ++++++++++++++++++++++++++++++++++
 gdb/nds32-tdep.h                     |   54 +
 gdb/testsuite/ChangeLog              |    5 +
 gdb/testsuite/gdb.base/float.exp     |    9 +
 gdb/testsuite/gdb.xml/tdesc-regs.exp |    3 +
 17 files changed, 2518 insertions(+), 1 deletion(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9e57431..faa9e9f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,19 @@
+2016-06-17  Yan-Ting Lin  <currygt52@gmail.com>
+
+	* Makefile.in (ALL_TARGET_OBS): Add nds32-tdep.o.
+	(HFILES_NO_SRCDIR): Add nds32-tdep.h.
+	(ALLDEPFILES): Add nds32-tdep.c.
+	* NEWS: Mention new NDS32 port.
+	* configure.tgt: Add NDS32.
+	* nds32-tdep.c: New file.
+	* nds32-tdep.h: New file.
+	* features/Makefile (XMLTOC): Add nds32.xml.
+	* features/nds32-core.xml: New file.
+	* features/nds32-fpu.xml: New file.
+	* features/nds32-system.xml: New file.
+	* features/nds32.c: New file (generated).
+	* features/nds32.xml: New file.
+
 2016-06-14  John Baldwin  <jhb@FreeBSD.org>
 
 	* v850-tdep.c (v850_use_struct_convention): Trim type length checks.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 02eb57f..16d5f27 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -691,6 +691,7 @@ ALL_TARGET_OBS = \
 	moxie-tdep.o \
 	msp430-tdep.o \
 	mt-tdep.o \
+	nds32-tdep.o \
 	nios2-tdep.o nios2-linux-tdep.o \
 	nto-tdep.o \
 	ppc-linux-tdep.o ppcfbsd-tdep.o ppcnbsd-tdep.o ppcobsd-tdep.o  \
@@ -967,7 +968,7 @@ 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 \
-ft32-tdep.h \
+ft32-tdep.h nds32-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 \
@@ -1726,6 +1727,7 @@ ALLDEPFILES = \
 	mipsnbsd-nat.c mipsnbsd-tdep.c \
 	mips64obsd-nat.c mips64obsd-tdep.c \
 	msp430-tdep.c \
+	nds32-tdep.c \
 	nios2-tdep.c nios2-linux-tdep.c \
 	nbsd-nat.c nbsd-tdep.c obsd-nat.c obsd-tdep.c \
 	posix-hdep.c common/posix-strerror.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index a3852ca..340a751 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -86,6 +86,10 @@ maint selftest
 
     =record-started,thread-group="i1",method="btrace",format="bts"
 
+* New targets
+
+Andes NDS32			nds32*-*-elf
+
 *** Changes in GDB 7.11
 
 * GDB now supports debugging kernel-based threads on FreeBSD.
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index cd8e05d..7f1aac3 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -394,6 +394,11 @@ mt-*-*)
 	gdb_target_obs="mt-tdep.o"
 	;;
 
+nds32*-*-elf)
+	# Target: AndesTech NDS32 core
+	gdb_target_obs="nds32-tdep.o"
+	;;
+
 nios2*-*-linux*)
 	# Target: Altera Nios II running Linux
 	gdb_target_obs="nios2-tdep.o nios2-linux-tdep.o solib-svr4.o \
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index a01d545..1af71e5 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2016-06-17  Yan-Ting Lin  <currygt52@gmail.com>
+
+	* gdb.texinfo (Standard Target Features): Document NDS32 features.
+	(NDS32 Features): New Section.
+
 2016-06-09  Toshihito Kikuchi  <k.toshihito@yahoo.de>
 
 	* gdb.texinfo (Examining Memory): Document negative repeat
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 6e2b493..795a70b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40817,6 +40817,7 @@ registers using the capitalization used in the description.
 * MicroBlaze Features::
 * MIPS Features::
 * M68K Features::
+* NDS32 Features::
 * Nios II Features::
 * PowerPC Features::
 * S/390 and System z Features::
@@ -41025,6 +41026,28 @@ This feature is optional.  If present, it should contain registers
 @samp{fpiaddr}.
 @end table
 
+@node NDS32 Features
+@subsection NDS32 Features
+@cindex target descriptions, NDS32 features
+
+The @samp{org.gnu.gdb.nds32.core} feature is required for NDS32
+targets.  It should contain at least registers @samp{r0} through
+@samp{r10}, @samp{r15}, @samp{fp}, @samp{gp}, @samp{lp}, @samp{sp},
+and @samp{pc}.
+
+The @samp{org.gnu.gdb.nds32.fpu} feature is optional.  If present,
+it should contain 64-bit double-precision floating-point registers
+@samp{fd0} through @emph{fdN}, which should be @samp{fd3}, @samp{fd7},
+@samp{fd15}, or @samp{fd31} based on the FPU configuration implemented.
+
+@emph{Note:} The first sixteen 64-bit double-precision floating-point
+registers are overlapped with the thirty-two 32-bit single-precision
+floating-point registers.  The 32-bit single-precision registers, if
+not being listed explicitly, will be synthesized from halves of the
+overlapping 64-bit double-precision registers.  Listing 32-bit
+single-precision registers explicitly is deprecated, and the
+support to it could be totally removed some day.
+
 @node Nios II Features
 @subsection Nios II Features
 @cindex target descriptions, Nios II features
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index e5c5154..809c811 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -189,6 +189,7 @@ XMLTOC = \
 	mips-linux.xml \
 	mips64-dsp-linux.xml \
 	mips64-linux.xml \
+	nds32.xml \
 	nios2-linux.xml \
 	nios2.xml \
 	rs6000/powerpc-32.xml \
diff --git a/gdb/features/nds32-core.xml b/gdb/features/nds32-core.xml
new file mode 100644
index 0000000..9ea1640
--- /dev/null
+++ b/gdb/features/nds32-core.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.nds32.core">
+  <reg name="r0" bitsize="32" regnum="0"/>
+  <reg name="r1" bitsize="32"/>
+  <reg name="r2" bitsize="32"/>
+  <reg name="r3" bitsize="32"/>
+  <reg name="r4" bitsize="32"/>
+  <reg name="r5" bitsize="32"/>
+  <reg name="r6" bitsize="32"/>
+  <reg name="r7" bitsize="32"/>
+  <reg name="r8" bitsize="32"/>
+  <reg name="r9" bitsize="32"/>
+  <reg name="r10" bitsize="32"/>
+  <reg name="r11" bitsize="32"/>
+  <reg name="r12" bitsize="32"/>
+  <reg name="r13" bitsize="32"/>
+  <reg name="r14" bitsize="32"/>
+  <reg name="r15" bitsize="32"/>
+  <reg name="r16" bitsize="32"/>
+  <reg name="r17" bitsize="32"/>
+  <reg name="r18" bitsize="32"/>
+  <reg name="r19" bitsize="32"/>
+  <reg name="r20" bitsize="32"/>
+  <reg name="r21" bitsize="32"/>
+  <reg name="r22" bitsize="32"/>
+  <reg name="r23" bitsize="32"/>
+  <reg name="r24" bitsize="32"/>
+  <reg name="r25" bitsize="32"/>
+  <reg name="r26" bitsize="32"/>
+  <reg name="r27" bitsize="32"/>
+  <reg name="fp" bitsize="32" type="data_ptr"/>
+  <reg name="gp" bitsize="32" type="data_ptr"/>
+  <reg name="lp" bitsize="32" type="code_ptr"/>
+  <reg name="sp" bitsize="32" type="data_ptr"/>
+
+  <reg name="pc" bitsize="32" type="code_ptr"/>
+</feature>
diff --git a/gdb/features/nds32-fpu.xml b/gdb/features/nds32-fpu.xml
new file mode 100644
index 0000000..b01a4c0
--- /dev/null
+++ b/gdb/features/nds32-fpu.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.nds32.fpu">
+  <reg name="fd0" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd1" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd2" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd3" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd4" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd5" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd6" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd7" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd8" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd9" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd10" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd11" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd12" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd13" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd14" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd15" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd16" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd17" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd18" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd19" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd20" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd21" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd22" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd23" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd24" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd25" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd26" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd27" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd28" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd29" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd30" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="fd31" bitsize="64" type="ieee_double" group="float"/>
+</feature>
diff --git a/gdb/features/nds32-system.xml b/gdb/features/nds32-system.xml
new file mode 100644
index 0000000..c2ea8ac
--- /dev/null
+++ b/gdb/features/nds32-system.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.nds32.system">
+  <reg name="ir0" bitsize="32"/>
+
+  <reg name="itb" bitsize="32"/>
+  <reg name="ifc_lp" bitsize="32"/>
+</feature>
diff --git a/gdb/features/nds32.c b/gdb/features/nds32.c
new file mode 100644
index 0000000..21f63f5
--- /dev/null
+++ b/gdb/features/nds32.c
@@ -0,0 +1,92 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: nds32.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_nds32;
+static void
+initialize_tdesc_nds32 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("n1h"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.nds32.core");
+  tdesc_create_reg (feature, "r0", 0, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r1", 1, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r2", 2, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r3", 3, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r4", 4, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r5", 5, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r6", 6, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r7", 7, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r8", 8, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r9", 9, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r10", 10, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r11", 11, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r12", 12, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r13", 13, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r14", 14, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r15", 15, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r16", 16, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r17", 17, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r18", 18, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r19", 19, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r20", 20, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r21", 21, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r22", 22, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r23", 23, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r24", 24, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r25", 25, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r26", 26, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "r27", 27, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "fp", 28, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "gp", 29, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "lp", 30, 1, NULL, 32, "code_ptr");
+  tdesc_create_reg (feature, "sp", 31, 1, NULL, 32, "data_ptr");
+  tdesc_create_reg (feature, "pc", 32, 1, NULL, 32, "code_ptr");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.nds32.fpu");
+  tdesc_create_reg (feature, "fd0", 33, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd1", 34, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd2", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd3", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd4", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd5", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd6", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd7", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd8", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd9", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd10", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd11", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd12", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd13", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd14", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd15", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd16", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd17", 50, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd18", 51, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd19", 52, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd20", 53, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd21", 54, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd22", 55, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd23", 56, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd24", 57, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd25", 58, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd26", 59, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd27", 60, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd28", 61, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd29", 62, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd30", 63, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "fd31", 64, 1, "float", 64, "ieee_double");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.nds32.system");
+  tdesc_create_reg (feature, "ir0", 65, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "itb", 66, 1, NULL, 32, "int");
+  tdesc_create_reg (feature, "ifc_lp", 67, 1, NULL, 32, "int");
+
+  tdesc_nds32 = result;
+}
diff --git a/gdb/features/nds32.xml b/gdb/features/nds32.xml
new file mode 100644
index 0000000..4819317
--- /dev/null
+++ b/gdb/features/nds32.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>nds32</architecture>
+  <xi:include href="nds32-core.xml"/>
+  <xi:include href="nds32-fpu.xml"/>
+  <xi:include href="nds32-system.xml"/>
+</target>
diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
new file mode 100644
index 0000000..de72656
--- /dev/null
+++ b/gdb/nds32-tdep.c
@@ -0,0 +1,2184 @@
+/* Target-dependent code for the NDS32 architecture, for GDB.
+
+   Copyright (C) 2013-2016 Free Software Foundation, Inc.
+   Contributed by Andes Technology Corporation.
+
+   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 "gdbcore.h"
+#include "value.h"
+#include "reggroups.h"
+#include "inferior.h"
+#include "osabi.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "dis-asm.h"
+#include "user-regs.h"
+#include "elf-bfd.h"
+#include "dwarf2-frame.h"
+#include "remote.h"
+#include "target-descriptions.h"
+
+#include "nds32-tdep.h"
+#include "elf/nds32.h"
+#include "opcode/nds32.h"
+#include "features/nds32.c"
+
+/* Simple macros for instruction analysis.  */
+#define CHOP_BITS(insn, n)	(insn & ~__MASK (n))
+#define N32_LSMW_ENABLE4(insn)	(((insn) >> 6) & 0xf)
+#define N32_SMW_ADM \
+	N32_TYPE4 (LSMW, 0, 0, 0, 1, (N32_LSMW_ADM << 2) | N32_LSMW_LSMW)
+#define N32_LMW_BIM \
+	N32_TYPE4 (LSMW, 0, 0, 0, 0, (N32_LSMW_BIM << 2) | N32_LSMW_LSMW)
+#define N32_FLDI_SP \
+	N32_TYPE2 (LDC, 0, REG_SP, 0)
+
+extern void _initialize_nds32_tdep (void);
+
+/* Use an invalid address value as 'not available' marker.  */
+enum { REG_UNAVAIL = (CORE_ADDR) -1 };
+
+/* Use an impossible value as invalid offset.  */
+enum { INVALID_OFFSET = (CORE_ADDR) -1 };
+
+/* Instruction groups for NDS32 epilogue analysis.  */
+enum
+{
+  /* Instructions used everywhere, not only in epilogue.  */
+  INSN_NORMAL,
+  /* Instructions used to reset sp for local vars, arguments, etc.  */
+  INSN_RESET_SP,
+  /* Instructions used to recover saved regs and to recover padding.  */
+  INSN_RECOVER,
+  /* Instructions used to return to the caller.  */
+  INSN_RETURN,
+  /* Instructions used to recover saved regs and to return to the caller.  */
+  INSN_RECOVER_RETURN,
+};
+
+static const char *const nds32_register_names[] =
+{
+  /* 32 GPRs.  */
+  "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", "fp", "gp", "lp", "sp",
+  /* PC.  */
+  "pc",
+};
+
+static const char *const nds32_fdr_register_names[] =
+{
+  "fd0", "fd1", "fd2", "fd3", "fd4", "fd5", "fd6", "fd7",
+  "fd8", "fd9", "fd10", "fd11", "fd12", "fd13", "fd14", "fd15",
+  "fd16", "fd17", "fd18", "fd19", "fd20", "fd21", "fd22", "fd23",
+  "fd24", "fd25", "fd26", "fd27", "fd28", "fd29", "fd30", "fd31"
+};
+
+static const char *const nds32_fsr_register_names[] =
+{
+  "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
+  "fs8", "fs9", "fs10", "fs11", "fs12", "fs13", "fs14", "fs15",
+  "fs16", "fs17", "fs18", "fs19", "fs20", "fs21", "fs22", "fs23",
+  "fs24", "fs25", "fs26", "fs27", "fs28", "fs29", "fs30", "fs31"
+};
+
+/* The number of registers for four FPU configuration options.  */
+const int num_fdr_map[] = { 4, 8, 16, 32 };
+const int num_fsr_map[] = { 8, 16, 32, 32 };
+
+/* Aliases for registers.  */
+static const struct
+{
+  const char *name;
+  const char *alias;
+} nds32_register_aliases[] =
+{
+  {"r15", "ta"},
+  {"r26", "p0"},
+  {"r27", "p1"},
+  {"fp", "r28"},
+  {"gp", "r29"},
+  {"lp", "r30"},
+  {"sp", "r31"},
+
+  {"cr0", "cpu_ver"},
+  {"cr1", "icm_cfg"},
+  {"cr2", "dcm_cfg"},
+  {"cr3", "mmu_cfg"},
+  {"cr4", "msc_cfg"},
+  {"cr5", "core_id"},
+  {"cr6", "fucop_exist"},
+  {"cr7", "msc_cfg2"},
+
+  {"ir0", "psw"},
+  {"ir1", "ipsw"},
+  {"ir2", "p_psw"},
+  {"ir3", "ivb"},
+  {"ir4", "eva"},
+  {"ir5", "p_eva"},
+  {"ir6", "itype"},
+  {"ir7", "p_itype"},
+  {"ir8", "merr"},
+  {"ir9", "ipc"},
+  {"ir10", "p_ipc"},
+  {"ir11", "oipc"},
+  {"ir12", "p_p0"},
+  {"ir13", "p_p1"},
+  {"ir14", "int_mask"},
+  {"ir15", "int_pend"},
+  {"ir16", "sp_usr"},
+  {"ir17", "sp_priv"},
+  {"ir18", "int_pri"},
+  {"ir19", "int_ctrl"},
+  {"ir20", "sp_usr1"},
+  {"ir21", "sp_priv1"},
+  {"ir22", "sp_usr2"},
+  {"ir23", "sp_priv2"},
+  {"ir24", "sp_usr3"},
+  {"ir25", "sp_priv3"},
+  {"ir26", "int_mask2"},
+  {"ir27", "int_pend2"},
+  {"ir28", "int_pri2"},
+  {"ir29", "int_trigger"},
+
+  {"mr0", "mmu_ctl"},
+  {"mr1", "l1_pptb"},
+  {"mr2", "tlb_vpn"},
+  {"mr3", "tlb_data"},
+  {"mr4", "tlb_misc"},
+  {"mr5", "vlpt_idx"},
+  {"mr6", "ilmb"},
+  {"mr7", "dlmb"},
+  {"mr8", "cache_ctl"},
+  {"mr9", "hsmp_saddr"},
+  {"mr10", "hsmp_eaddr"},
+  {"mr11", "bg_region"},
+
+  {"dr0", "bpc0"},
+  {"dr1", "bpc1"},
+  {"dr2", "bpc2"},
+  {"dr3", "bpc3"},
+  {"dr4", "bpc4"},
+  {"dr5", "bpc5"},
+  {"dr6", "bpc6"},
+  {"dr7", "bpc7"},
+  {"dr8", "bpa0"},
+  {"dr9", "bpa1"},
+  {"dr10", "bpa2"},
+  {"dr11", "bpa3"},
+  {"dr12", "bpa4"},
+  {"dr13", "bpa5"},
+  {"dr14", "bpa6"},
+  {"dr15", "bpa7"},
+  {"dr16", "bpam0"},
+  {"dr17", "bpam1"},
+  {"dr18", "bpam2"},
+  {"dr19", "bpam3"},
+  {"dr20", "bpam4"},
+  {"dr21", "bpam5"},
+  {"dr22", "bpam6"},
+  {"dr23", "bpam7"},
+  {"dr24", "bpv0"},
+  {"dr25", "bpv1"},
+  {"dr26", "bpv2"},
+  {"dr27", "bpv3"},
+  {"dr28", "bpv4"},
+  {"dr29", "bpv5"},
+  {"dr30", "bpv6"},
+  {"dr31", "bpv7"},
+  {"dr32", "bpcid0"},
+  {"dr33", "bpcid1"},
+  {"dr34", "bpcid2"},
+  {"dr35", "bpcid3"},
+  {"dr36", "bpcid4"},
+  {"dr37", "bpcid5"},
+  {"dr38", "bpcid6"},
+  {"dr39", "bpcid7"},
+  {"dr40", "edm_cfg"},
+  {"dr41", "edmsw"},
+  {"dr42", "edm_ctl"},
+  {"dr43", "edm_dtr"},
+  {"dr44", "bpmtc"},
+  {"dr45", "dimbr"},
+  {"dr46", "tecr0"},
+  {"dr47", "tecr1"},
+
+  {"hspr0", "hsp_ctl"},
+  {"hspr1", "sp_bound"},
+  {"hspr2", "sp_bound_priv"},
+
+  {"pfr0", "pfmc0"},
+  {"pfr1", "pfmc1"},
+  {"pfr2", "pfmc2"},
+  {"pfr3", "pfm_ctl"},
+  {"pfr4", "pft_ctl"},
+
+  {"dmar0", "dma_cfg"},
+  {"dmar1", "dma_gcsw"},
+  {"dmar2", "dma_chnsel"},
+  {"dmar3", "dma_act"},
+  {"dmar4", "dma_setup"},
+  {"dmar5", "dma_isaddr"},
+  {"dmar6", "dma_esaddr"},
+  {"dmar7", "dma_tcnt"},
+  {"dmar8", "dma_status"},
+  {"dmar9", "dma_2dset"},
+  {"dmar10", "dma_2dsctl"},
+  {"dmar11", "dma_rcnt"},
+  {"dmar12", "dma_hstatus"},
+
+  {"racr0", "prusr_acc_ctl"},
+  {"fucpr", "fucop_ctl"},
+
+  {"idr0", "sdz_ctl"},
+  {"idr1", "misc_ctl"},
+  {"idr2", "ecc_misc"},
+
+  {"secur0", "sfcr"},
+  {"secur1", "sign"},
+  {"secur2", "isign"},
+  {"secur3", "p_isign"},
+};
+
+/* Value of a register alias.  BATON is the regnum of the corresponding
+   register.  */
+
+static struct value *
+value_of_nds32_reg (struct frame_info *frame, const void *baton)
+{
+  return value_of_register ((int) (intptr_t) baton, frame);
+}
+
+/* Implement the "frame_align" gdbarch method.  */
+
+static CORE_ADDR
+nds32_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+  /* 8-byte aligned.  */
+  return align_down (sp, 8);
+}
+
+/* Implement the "breakpoint_from_pc" gdbarch method.
+
+   Use the program counter to determine the contents and size of a
+   breakpoint instruction.  Return a pointer to a string of bytes that
+   encode a breakpoint instruction, store the length of the string in
+   *LENPTR and optionally adjust *PCPTR to point to the correct memory
+   location for inserting the breakpoint.  */
+
+static const gdb_byte *
+nds32_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
+			  int *lenptr)
+{
+  /* The same insn machine code is used for little-endian and big-endian.  */
+  static const gdb_byte break_insn[] = { 0xEA, 0x00 };
+
+  *lenptr = sizeof (break_insn);
+  return break_insn;
+}
+
+/* Implement the "dwarf2_reg_to_regnum" gdbarch method.  */
+
+static int
+nds32_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  const int FSR = 38;
+  const int FDR = FSR + 32;
+
+  if (num >= 0 && num < 32)
+    {
+      /* General-purpose registers (R0 - R31).  */
+      return num;
+    }
+  else if (num >= FSR && num < FSR + 32)
+    {
+      /* Single precision floating-point registers (FS0 - FS31).  */
+      return num - FSR + tdep->fs0_regnum;
+    }
+  else if (num >= FDR && num < FDR + 32)
+    {
+      /* Double precision floating-point registers (FD0 - FD31).  */
+      return num - FDR + NDS32_FD0_REGNUM;
+    }
+
+  /* No match, return a inaccessible register number.  */
+  return -1;
+}
+
+/* NDS32 register groups.  */
+static struct reggroup *nds32_cr_reggroup;
+static struct reggroup *nds32_ir_reggroup;
+static struct reggroup *nds32_mr_reggroup;
+static struct reggroup *nds32_dr_reggroup;
+static struct reggroup *nds32_pfr_reggroup;
+static struct reggroup *nds32_hspr_reggroup;
+static struct reggroup *nds32_dmar_reggroup;
+static struct reggroup *nds32_racr_reggroup;
+static struct reggroup *nds32_idr_reggroup;
+static struct reggroup *nds32_secur_reggroup;
+
+static void
+nds32_init_reggroups (void)
+{
+  nds32_cr_reggroup = reggroup_new ("cr", USER_REGGROUP);
+  nds32_ir_reggroup = reggroup_new ("ir", USER_REGGROUP);
+  nds32_mr_reggroup = reggroup_new ("mr", USER_REGGROUP);
+  nds32_dr_reggroup = reggroup_new ("dr", USER_REGGROUP);
+  nds32_pfr_reggroup = reggroup_new ("pfr", USER_REGGROUP);
+  nds32_hspr_reggroup = reggroup_new ("hspr", USER_REGGROUP);
+  nds32_dmar_reggroup = reggroup_new ("dmar", USER_REGGROUP);
+  nds32_racr_reggroup = reggroup_new ("racr", USER_REGGROUP);
+  nds32_idr_reggroup = reggroup_new ("idr", USER_REGGROUP);
+  nds32_secur_reggroup = reggroup_new ("secur", USER_REGGROUP);
+}
+
+static void
+nds32_add_reggroups (struct gdbarch *gdbarch)
+{
+  /* Add pre-defined register groups.  */
+  reggroup_add (gdbarch, general_reggroup);
+  reggroup_add (gdbarch, float_reggroup);
+  reggroup_add (gdbarch, system_reggroup);
+  reggroup_add (gdbarch, all_reggroup);
+  reggroup_add (gdbarch, save_reggroup);
+  reggroup_add (gdbarch, restore_reggroup);
+
+  /* Add NDS32 register groups.  */
+  reggroup_add (gdbarch, nds32_cr_reggroup);
+  reggroup_add (gdbarch, nds32_ir_reggroup);
+  reggroup_add (gdbarch, nds32_mr_reggroup);
+  reggroup_add (gdbarch, nds32_dr_reggroup);
+  reggroup_add (gdbarch, nds32_pfr_reggroup);
+  reggroup_add (gdbarch, nds32_hspr_reggroup);
+  reggroup_add (gdbarch, nds32_dmar_reggroup);
+  reggroup_add (gdbarch, nds32_racr_reggroup);
+  reggroup_add (gdbarch, nds32_idr_reggroup);
+  reggroup_add (gdbarch, nds32_secur_reggroup);
+}
+
+/* Implement the "register_reggroup_p" gdbarch method.  */
+
+static int
+nds32_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+			   struct reggroup *reggroup)
+{
+  const char *reg_name;
+  const char *group_name;
+  int ret;
+
+  if (reggroup == all_reggroup)
+    return 1;
+
+  /* General reggroup contains only GPRs and PC.  */
+  if (reggroup == general_reggroup)
+    return regnum <= NDS32_PC_REGNUM;
+
+  if (reggroup == float_reggroup || reggroup == save_reggroup
+      || reggroup == restore_reggroup)
+    {
+      ret = tdesc_register_in_reggroup_p (gdbarch, regnum, reggroup);
+      if (ret != -1)
+	return ret;
+
+      return default_register_reggroup_p (gdbarch, regnum, reggroup);
+    }
+
+  if (reggroup == system_reggroup)
+    return (regnum > NDS32_PC_REGNUM)
+	    && !nds32_register_reggroup_p (gdbarch, regnum, float_reggroup);
+
+  /* The NDS32 reggroup contains registers whose name is prefixed
+     by reggroup name.  */
+  reg_name = gdbarch_register_name (gdbarch, regnum);
+  group_name = reggroup_name (reggroup);
+  return !strncmp (reg_name, group_name, strlen (group_name));
+}
+
+/* Implement the "pseudo_register_type" tdesc_arch_data method.  */
+
+static struct type *
+nds32_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
+{
+  regnum -= gdbarch_num_regs (gdbarch);
+
+  /* Currently, only FSRs could be defined as pseudo registers.  */
+  if (regnum < gdbarch_num_pseudo_regs (gdbarch))
+    return arch_float_type (gdbarch, -1, "builtin_type_ieee_single",
+			    floatformats_ieee_single);
+
+  warning (_("Unknown nds32 pseudo register %d."), regnum);
+  return NULL;
+}
+
+/* Implement the "pseudo_register_name" tdesc_arch_data method.  */
+
+static const char *
+nds32_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
+{
+  regnum -= gdbarch_num_regs (gdbarch);
+
+  /* Currently, only FSRs could be defined as pseudo registers.  */
+  if (regnum < gdbarch_num_pseudo_regs (gdbarch))
+    return nds32_fsr_register_names[regnum];
+
+  warning (_("Unknown nds32 pseudo register %d."), regnum);
+  return NULL;
+}
+
+/* Implement the "pseudo_register_read" gdbarch method.  */
+
+static enum register_status
+nds32_pseudo_register_read (struct gdbarch *gdbarch,
+			    struct regcache *regcache, int regnum,
+			    gdb_byte *buf)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  gdb_byte reg_buf[8];
+  int offset, fdr_regnum;
+  enum register_status status = REG_UNKNOWN;
+
+  /* Sanity check.  */
+  if (tdep->fpu_freg == -1 || tdep->use_pseudo_fsrs == 0)
+    return status;
+
+  regnum -= gdbarch_num_regs (gdbarch);
+
+  /* Currently, only FSRs could be defined as pseudo registers.  */
+  if (regnum < gdbarch_num_pseudo_regs (gdbarch))
+    {
+      /* fs0 is always the most significant half of fd0.  */
+      if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+	offset = (regnum & 1) ? 4 : 0;
+      else
+	offset = (regnum & 1) ? 0 : 4;
+
+      fdr_regnum = NDS32_FD0_REGNUM + (regnum >> 1);
+      status = regcache_raw_read (regcache, fdr_regnum, reg_buf);
+      if (status == REG_VALID)
+	memcpy (buf, reg_buf + offset, 4);
+    }
+
+  return status;
+}
+
+/* Implement the "pseudo_register_write" gdbarch method.  */
+
+static void
+nds32_pseudo_register_write (struct gdbarch *gdbarch,
+			     struct regcache *regcache, int regnum,
+			     const gdb_byte *buf)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  gdb_byte reg_buf[8];
+  int offset, fdr_regnum;
+
+  /* Sanity check.  */
+  if (tdep->fpu_freg == -1 || tdep->use_pseudo_fsrs == 0)
+    return;
+
+  regnum -= gdbarch_num_regs (gdbarch);
+
+  /* Currently, only FSRs could be defined as pseudo registers.  */
+  if (regnum < gdbarch_num_pseudo_regs (gdbarch))
+    {
+      /* fs0 is always the most significant half of fd0.  */
+      if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+	offset = (regnum & 1) ? 4 : 0;
+      else
+	offset = (regnum & 1) ? 0 : 4;
+
+      fdr_regnum = NDS32_FD0_REGNUM + (regnum >> 1);
+      regcache_raw_read (regcache, fdr_regnum, reg_buf);
+      memcpy (reg_buf + offset, buf, 4);
+      regcache_raw_write (regcache, fdr_regnum, reg_buf);
+    }
+}
+
+/* Helper function for NDS32 ABI.  Return true if FPRs can be used
+   to pass function arguments and return value.  */
+
+static int
+nds32_abi_use_fpr (int elf_abi)
+{
+  return elf_abi == E_NDS_ABI_V2FP_PLUS;
+}
+
+/* Helper function for NDS32 ABI.  Return true if GPRs and stack
+   can be used together to pass an argument.  */
+
+static int
+nds32_abi_split (int elf_abi)
+{
+  return elf_abi == E_NDS_ABI_AABI;
+}
+
+#define NDS32_NUM_SAVED_REGS (NDS32_LP_REGNUM + 1)
+
+struct nds32_frame_cache
+{
+  /* The previous frame's inner most stack address.  Used as this
+     frame ID's stack_addr.  */
+  CORE_ADDR prev_sp;
+
+  /* The frame's base, optionally used by the high-level debug info.  */
+  CORE_ADDR base;
+
+  /* During prologue analysis, keep how far the SP and FP have been offset
+     from the start of the stack frame (as defined by the previous frame's
+     stack pointer).
+     During epilogue analysis, keep how far the SP has been offset from the
+     current stack pointer.  */
+  CORE_ADDR sp_offset;
+  CORE_ADDR fp_offset;
+
+  /* The address of the first instruction in this function.  */
+  CORE_ADDR pc;
+
+  /* Saved registers.  */
+  CORE_ADDR saved_regs[NDS32_NUM_SAVED_REGS];
+};
+
+/* Allocate and initialize a frame cache.  */
+
+static struct nds32_frame_cache *
+nds32_alloc_frame_cache (void)
+{
+  struct nds32_frame_cache *cache;
+  int i;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct nds32_frame_cache);
+
+  /* Initialize fp_offset to check if FP is set in prologue.  */
+  cache->fp_offset = INVALID_OFFSET;
+
+  /* Saved registers.  We initialize these to -1 since zero is a valid
+     offset.  */
+  for (i = 0; i < NDS32_NUM_SAVED_REGS; i++)
+    cache->saved_regs[i] = REG_UNAVAIL;
+
+  return cache;
+}
+
+/* Helper function for instructions used to push multiple words.  */
+
+static void
+nds32_push_multiple_words (struct nds32_frame_cache *cache, int rb, int re,
+			   int enable4)
+{
+  CORE_ADDR sp_offset = cache->sp_offset;
+  int i;
+
+  /* Check LP, GP, FP in enable4.  */
+  for (i = 1; i <= 3; i++)
+    {
+      if ((enable4 >> i) & 0x1)
+	{
+	  sp_offset += 4;
+	  cache->saved_regs[NDS32_SP_REGNUM - i] = sp_offset;
+	}
+    }
+
+  /* Skip case where re == rb == sp.  */
+  if ((rb < REG_FP) && (re < REG_FP))
+    {
+      for (i = re; i >= rb; i--)
+	{
+	  sp_offset += 4;
+	  cache->saved_regs[i] = sp_offset;
+	}
+    }
+
+  /* For sp, update the offset.  */
+  cache->sp_offset = sp_offset;
+}
+
+/* Analyze the instructions within the given address range.  If CACHE
+   is non-NULL, fill it in.  Return the first address beyond the given
+   address range.  If CACHE is NULL, return the first address not
+   recognized as a prologue instruction.  */
+
+static CORE_ADDR
+nds32_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc,
+			CORE_ADDR limit_pc, struct nds32_frame_cache *cache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi);
+  /* Current scanning status.  */
+  int in_prologue_bb = 0;
+  int val_ta = 0;
+  uint32_t insn, insn_len;
+
+  for (; pc < limit_pc; pc += insn_len)
+    {
+      insn = read_memory_unsigned_integer (pc, 4, BFD_ENDIAN_BIG);
+
+      if ((insn & 0x80000000) == 0)
+	{
+	  /* 32-bit instruction */
+	  insn_len = 4;
+
+	  if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_SP, 0))
+	    {
+	      /* addi $sp, $sp, imm15s */
+	      int imm15s = N32_IMM15S (insn);
+
+	      if (imm15s < 0)
+		{
+		  if (cache != NULL)
+		    cache->sp_offset += -imm15s;
+
+		  in_prologue_bb = 1;
+		  continue;
+		}
+	    }
+	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_FP, REG_SP, 0))
+	    {
+	      /* addi $fp, $sp, imm15s */
+	      int imm15s = N32_IMM15S (insn);
+
+	      if (imm15s > 0)
+		{
+		  if (cache != NULL)
+		    cache->fp_offset = cache->sp_offset - imm15s;
+
+		  in_prologue_bb = 1;
+		  continue;
+		}
+	    }
+	  else if ((insn & ~(__MASK (19) << 6)) == N32_SMW_ADM
+		   && N32_RA5 (insn) == REG_SP)
+	    {
+	      /* smw.adm Rb, [$sp], Re, enable4 */
+	      if (cache != NULL)
+		nds32_push_multiple_words (cache, N32_RT5 (insn),
+					   N32_RB5 (insn),
+					   N32_LSMW_ENABLE4 (insn));
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else if (insn == N32_ALU1 (ADD, REG_SP, REG_SP, REG_TA)
+		   || insn == N32_ALU1 (ADD, REG_SP, REG_TA, REG_SP))
+	    {
+	      /* add $sp, $sp, $ta */
+	      /* add $sp, $ta, $sp */
+	      if (val_ta < 0)
+		{
+		  if (cache != NULL)
+		    cache->sp_offset += -val_ta;
+
+		  in_prologue_bb = 1;
+		  continue;
+		}
+	    }
+	  else if (CHOP_BITS (insn, 20) == N32_TYPE1 (MOVI, REG_TA, 0))
+	    {
+	      /* movi $ta, imm20s */
+	      if (cache != NULL)
+		val_ta = N32_IMM20S (insn);
+
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 20) == N32_TYPE1 (SETHI, REG_TA, 0))
+	    {
+	      /* sethi $ta, imm20u */
+	      if (cache != NULL)
+		val_ta = N32_IMM20U (insn) << 12;
+
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ORI, REG_TA, REG_TA, 0))
+	    {
+	      /* ori $ta, $ta, imm15u */
+	      if (cache != NULL)
+		val_ta |= N32_IMM15U (insn);
+
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_TA, REG_TA, 0))
+	    {
+	      /* addi $ta, $ta, imm15s */
+	      if (cache != NULL)
+		val_ta += N32_IMM15S (insn);
+
+	      continue;
+	    }
+	  if (insn == N32_ALU1 (ADD, REG_GP, REG_TA, REG_GP)
+	      || insn == N32_ALU1 (ADD, REG_GP, REG_GP, REG_TA))
+	    {
+	      /* add $gp, $ta, $gp */
+	      /* add $gp, $gp, $ta */
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 20) == N32_TYPE1 (MOVI, REG_GP, 0))
+	    {
+	      /* movi $gp, imm20s */
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 20) == N32_TYPE1 (SETHI, REG_GP, 0))
+	    {
+	      /* sethi $gp, imm20u */
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ORI, REG_GP, REG_GP, 0))
+	    {
+	      /* ori $gp, $gp, imm15u */
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else
+	    {
+	      /* Jump/Branch insns never appear in prologue basic block.
+		 The loop can be escaped early when these insns are met.  */
+	      if (in_prologue_bb == 1)
+		{
+		  int op = N32_OP6 (insn);
+
+		  if (op == N32_OP6_JI
+		      || op == N32_OP6_JREG
+		      || op == N32_OP6_BR1
+		      || op == N32_OP6_BR2
+		      || op == N32_OP6_BR3)
+		    break;
+		}
+	    }
+
+	  if (abi_use_fpr && N32_OP6 (insn) == N32_OP6_SDC
+	      && __GF (insn, 12, 3) == 0)
+	    {
+	      /* For FPU insns, CP (bit [13:14]) should be CP0,  and only
+		 normal form (bit [12] == 0) is used.  */
+
+	      /* fsdi FDt, [$sp + (imm12s << 2)] */
+	      if (N32_RA5 (insn) == REG_SP)
+		continue;
+	    }
+
+	  /* The optimizer might shove anything into the prologue, if
+	     we build up cache (cache != NULL) from analyzing prologue,
+	     we just skip what we don't recognize and analyze further to
+	     make cache as complete as possible.  However, if we skip
+	     prologue, we'll stop immediately on unrecognized
+	     instruction.  */
+	  if (cache == NULL)
+	    break;
+	}
+      else
+	{
+	  /* 16-bit instruction */
+	  insn_len = 2;
+
+	  insn >>= 16;
+
+	  if (CHOP_BITS (insn, 10) == N16_TYPE10 (ADDI10S, 0))
+	    {
+	      /* addi10s.sp */
+	      int imm10s = N16_IMM10S (insn);
+
+	      if (imm10s < 0)
+		{
+		  if (cache != NULL)
+		    cache->sp_offset += -imm10s;
+
+		  in_prologue_bb = 1;
+		  continue;
+		}
+	    }
+	  else if (__GF (insn, 7, 8) == N16_T25_PUSH25)
+	    {
+	      /* push25 */
+	      if (cache != NULL)
+		{
+		  int imm8u = (insn & 0x1f) << 3;
+		  int re = (insn >> 5) & 0x3;
+		  const int reg_map[] = { 6, 8, 10, 14 };
+
+		  /* Operation 1 -- smw.adm R6, [$sp], Re, #0xe */
+		  nds32_push_multiple_words (cache, 6, reg_map[re], 0xe);
+
+		  /* Operation 2 -- sp = sp - (imm5u << 3) */
+		  cache->sp_offset += imm8u;
+		}
+
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else if (insn == N16_TYPE5 (ADD5PC, REG_GP))
+	    {
+	      /* add5.pc $gp */
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else if (CHOP_BITS (insn, 5) == N16_TYPE55 (MOVI55, REG_GP, 0))
+	    {
+	      /* movi55 $gp, imm5s */
+	      in_prologue_bb = 1;
+	      continue;
+	    }
+	  else
+	    {
+	      /* Jump/Branch insns never appear in prologue basic block.
+		 The loop can be escaped early when these insns are met.  */
+	      if (in_prologue_bb == 1)
+		{
+		  uint32_t insn5 = CHOP_BITS (insn, 5);
+		  uint32_t insn8 = CHOP_BITS (insn, 8);
+		  uint32_t insn38 = CHOP_BITS (insn, 11);
+
+		  if (insn5 == N16_TYPE5 (JR5, 0)
+		      || insn5 == N16_TYPE5 (JRAL5, 0)
+		      || insn5 == N16_TYPE5 (RET5, 0)
+		      || insn8 == N16_TYPE8 (J8, 0)
+		      || insn8 == N16_TYPE8 (BEQZS8, 0)
+		      || insn8 == N16_TYPE8 (BNEZS8, 0)
+		      || insn38 == N16_TYPE38 (BEQZ38, 0, 0)
+		      || insn38 == N16_TYPE38 (BNEZ38, 0, 0)
+		      || insn38 == N16_TYPE38 (BEQS38, 0, 0)
+		      || insn38 == N16_TYPE38 (BNES38, 0, 0))
+		    break;
+		}
+	    }
+
+	  /* The optimizer might shove anything into the prologue, if
+	     we build up cache (cache != NULL) from analyzing prologue,
+	     we just skip what we don't recognize and analyze further to
+	     make cache as complete as possible.  However, if we skip
+	     prologue, we'll stop immediately on unrecognized
+	     instruction.  */
+	  if (cache == NULL)
+	    break;
+	}
+    }
+
+  return pc;
+}
+
+/* Implement the "skip_prologue" gdbarch method.
+
+   Find the end of function prologue.  */
+
+static CORE_ADDR
+nds32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr, limit_pc;
+
+  /* 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, NULL, &func_addr, NULL))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+    }
+
+  /* Can't determine prologue from the symbol table, need to examine
+     instructions.  */
+
+  /* Find an upper limit on the function prologue using the debug
+     information.  If the debug information could not be used to provide
+     that bound, then use an arbitrary large number as the upper bound.  */
+  limit_pc = skip_prologue_using_sal (gdbarch, pc);
+  if (limit_pc == 0)
+    limit_pc = pc + 128;	/* Magic.  */
+
+  /* Find the end of prologue.  */
+  return nds32_analyze_prologue (gdbarch, pc, limit_pc, NULL);
+}
+
+/* Allocate and fill in *THIS_CACHE with information about the prologue of
+   *THIS_FRAME.  Do not do this if *THIS_CACHE was already allocated.  Return
+   a pointer to the current nds32_frame_cache in *THIS_CACHE.  */
+
+static struct nds32_frame_cache *
+nds32_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct nds32_frame_cache *cache;
+  CORE_ADDR current_pc;
+  ULONGEST prev_sp;
+  ULONGEST this_base;
+  int i;
+
+  if (*this_cache)
+    return (struct nds32_frame_cache *) *this_cache;
+
+  cache = nds32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  nds32_analyze_prologue (gdbarch, cache->pc, current_pc, cache);
+
+  /* Compute the previous frame's stack pointer (which is also the
+     frame's ID's stack address), and this frame's base pointer.  */
+  if (cache->fp_offset != INVALID_OFFSET)
+    {
+      /* FP is set in prologue, so it can be used to calculate other info.  */
+      this_base = get_frame_register_unsigned (this_frame, NDS32_FP_REGNUM);
+      prev_sp = this_base + cache->fp_offset;
+    }
+  else
+    {
+      this_base = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM);
+      prev_sp = this_base + cache->sp_offset;
+    }
+
+  cache->prev_sp = prev_sp;
+  cache->base = this_base;
+
+  /* Adjust all the saved registers such that they contain addresses
+     instead of offsets.  */
+  for (i = 0; i < NDS32_NUM_SAVED_REGS; i++)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = cache->prev_sp - cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "this_id" frame_unwind method.
+
+   Our frame ID for a normal frame is the current function's starting
+   PC and the caller's SP when we were called.  */
+
+static void
+nds32_frame_this_id (struct frame_info *this_frame,
+		     void **this_cache, struct frame_id *this_id)
+{
+  struct nds32_frame_cache *cache = nds32_frame_cache (this_frame, this_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->prev_sp == 0)
+    return;
+
+  *this_id = frame_id_build (cache->prev_sp, cache->pc);
+}
+
+/* Implement the "prev_register" frame_unwind method.  */
+
+static struct value *
+nds32_frame_prev_register (struct frame_info *this_frame, void **this_cache,
+			   int regnum)
+{
+  struct nds32_frame_cache *cache = nds32_frame_cache (this_frame, this_cache);
+
+  if (regnum == NDS32_SP_REGNUM)
+    return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp);
+
+  /* The PC of the previous frame is stored in the LP register of
+     the current frame.  */
+  if (regnum == NDS32_PC_REGNUM)
+    regnum = NDS32_LP_REGNUM;
+
+  if (regnum < NDS32_NUM_SAVED_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+    return frame_unwind_got_memory (this_frame, regnum,
+				    cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind nds32_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  nds32_frame_this_id,
+  nds32_frame_prev_register,
+  NULL,
+  default_frame_sniffer,
+};
+
+/* Return the frame base address of *THIS_FRAME.  */
+
+static CORE_ADDR
+nds32_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+  struct nds32_frame_cache *cache = nds32_frame_cache (this_frame, this_cache);
+
+  return cache->base;
+}
+
+static const struct frame_base nds32_frame_base =
+{
+  &nds32_frame_unwind,
+  nds32_frame_base_address,
+  nds32_frame_base_address,
+  nds32_frame_base_address
+};
+
+/* Helper function for instructions used to pop multiple words.  */
+
+static void
+nds32_pop_multiple_words (struct nds32_frame_cache *cache, int rb, int re,
+			  int enable4)
+{
+  CORE_ADDR sp_offset = cache->sp_offset;
+  int i;
+
+  /* Skip case where re == rb == sp.  */
+  if ((rb < REG_FP) && (re < REG_FP))
+    {
+      for (i = rb; i <= re; i++)
+	{
+	  cache->saved_regs[i] = sp_offset;
+	  sp_offset += 4;
+	}
+    }
+
+  /* Check FP, GP, LP in enable4.  */
+  for (i = 3; i >= 1; i--)
+    {
+      if ((enable4 >> i) & 0x1)
+	{
+	  cache->saved_regs[NDS32_SP_REGNUM - i] = sp_offset;
+	  sp_offset += 4;
+	}
+    }
+
+  /* For sp, update the offset.  */
+  cache->sp_offset = sp_offset;
+}
+
+/* The instruction sequences in NDS32 epilogue are
+
+   INSN_RESET_SP  (optional)
+		  (If exists, this must be the first instruction in epilogue
+		   and the stack has not been destroyed.).
+   INSN_RECOVER  (optional).
+   INSN_RETURN/INSN_RECOVER_RETURN  (required).  */
+
+/* Helper function for analyzing the given 32-bit INSN.  If CACHE is non-NULL,
+   the necessary information will be recorded.  */
+
+static inline int
+nds32_analyze_epilogue_insn32 (int abi_use_fpr, uint32_t insn,
+			       struct nds32_frame_cache *cache)
+{
+  if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_SP, 0)
+      && N32_IMM15S (insn) > 0)
+    /* addi $sp, $sp, imm15s */
+    return INSN_RESET_SP;
+  else if (CHOP_BITS (insn, 15) == N32_TYPE2 (ADDI, REG_SP, REG_FP, 0)
+	   && N32_IMM15S (insn) < 0)
+    /* addi $sp, $fp, imm15s */
+    return INSN_RESET_SP;
+  else if ((insn & ~(__MASK (19) << 6)) == N32_LMW_BIM
+	   && N32_RA5 (insn) == REG_SP)
+    {
+      /* lmw.bim Rb, [$sp], Re, enable4 */
+      if (cache != NULL)
+	nds32_pop_multiple_words (cache, N32_RT5 (insn),
+				  N32_RB5 (insn), N32_LSMW_ENABLE4 (insn));
+
+      return INSN_RECOVER;
+    }
+  else if (insn == N32_JREG (JR, 0, REG_LP, 0, 1))
+    /* ret $lp */
+    return INSN_RETURN;
+  else if (insn == N32_ALU1 (ADD, REG_SP, REG_SP, REG_TA)
+	   || insn == N32_ALU1 (ADD, REG_SP, REG_TA, REG_SP))
+    /* add $sp, $sp, $ta */
+    /* add $sp, $ta, $sp */
+    return INSN_RESET_SP;
+  else if (abi_use_fpr
+	   && (insn & ~(__MASK (5) << 20 | __MASK (13))) == N32_FLDI_SP)
+    {
+      if (__GF (insn, 12, 1) == 0)
+	/* fldi FDt, [$sp + (imm12s << 2)] */
+	return INSN_RECOVER;
+      else
+	{
+	  /* fldi.bi FDt, [$sp], (imm12s << 2) */
+	  int offset = N32_IMM12S (insn) << 2;
+
+	  if (offset == 8 || offset == 12)
+	    {
+	      if (cache != NULL)
+		cache->sp_offset += offset;
+
+	      return INSN_RECOVER;
+	    }
+	}
+    }
+
+  return INSN_NORMAL;
+}
+
+/* Helper function for analyzing the given 16-bit INSN.  If CACHE is non-NULL,
+   the necessary information will be recorded.  */
+
+static inline int
+nds32_analyze_epilogue_insn16 (uint32_t insn, struct nds32_frame_cache *cache)
+{
+  if (insn == N16_TYPE5 (RET5, REG_LP))
+    /* ret5 $lp */
+    return INSN_RETURN;
+  else if (CHOP_BITS (insn, 10) == N16_TYPE10 (ADDI10S, 0))
+    {
+      /* addi10s.sp */
+      int imm10s = N16_IMM10S (insn);
+
+      if (imm10s > 0)
+	{
+	  if (cache != NULL)
+	    cache->sp_offset += imm10s;
+
+	  return INSN_RECOVER;
+	}
+    }
+  else if (__GF (insn, 7, 8) == N16_T25_POP25)
+    {
+      /* pop25 */
+      if (cache != NULL)
+	{
+	  int imm8u = (insn & 0x1f) << 3;
+	  int re = (insn >> 5) & 0x3;
+	  const int reg_map[] = { 6, 8, 10, 14 };
+
+	  /* Operation 1 -- sp = sp + (imm5u << 3) */
+	  cache->sp_offset += imm8u;
+
+	  /* Operation 2 -- lmw.bim R6, [$sp], Re, #0xe */
+	  nds32_pop_multiple_words (cache, 6, reg_map[re], 0xe);
+	}
+
+      /* Operation 3 -- ret $lp */
+      return INSN_RECOVER_RETURN;
+    }
+
+  return INSN_NORMAL;
+}
+
+/* Analyze a reasonable amount of instructions from the given PC to find
+   the instruction used to return to the caller.  Return 1 if the 'return'
+   instruction could be found, 0 otherwise.
+
+   If CACHE is non-NULL, fill it in.  */
+
+static int
+nds32_analyze_epilogue (struct gdbarch *gdbarch, CORE_ADDR pc,
+			struct nds32_frame_cache *cache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi);
+  CORE_ADDR limit_pc;
+  uint32_t insn, insn_len;
+  int insn_type = INSN_NORMAL;
+
+  if (abi_use_fpr)
+    limit_pc = pc + 48;
+  else
+    limit_pc = pc + 16;
+
+  for (; pc < limit_pc; pc += insn_len)
+    {
+      insn = read_memory_unsigned_integer (pc, 4, BFD_ENDIAN_BIG);
+
+      if ((insn & 0x80000000) == 0)
+	{
+	  /* 32-bit instruction */
+	  insn_len = 4;
+
+	  insn_type = nds32_analyze_epilogue_insn32 (abi_use_fpr, insn, cache);
+	  if (insn_type == INSN_RETURN)
+	    return 1;
+	  else if (insn_type == INSN_RECOVER)
+	    continue;
+	}
+      else
+	{
+	  /* 16-bit instruction */
+	  insn_len = 2;
+
+	  insn >>= 16;
+	  insn_type = nds32_analyze_epilogue_insn16 (insn, cache);
+	  if (insn_type == INSN_RETURN || insn_type == INSN_RECOVER_RETURN)
+	    return 1;
+	  else if (insn_type == INSN_RECOVER)
+	    continue;
+	}
+
+      /* Stop the scan if this is an unexpected instruction.  */
+      break;
+    }
+
+  return 0;
+}
+
+/* Implement the "stack_frame_destroyed_p" gdbarch method.  */
+
+static int
+nds32_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi);
+  int insn_type = INSN_NORMAL;
+  int ret_found = 0;
+  uint32_t insn;
+
+  insn = read_memory_unsigned_integer (addr, 4, BFD_ENDIAN_BIG);
+
+  if ((insn & 0x80000000) == 0)
+    {
+      /* 32-bit instruction */
+
+      insn_type = nds32_analyze_epilogue_insn32 (abi_use_fpr, insn, NULL);
+    }
+  else
+    {
+      /* 16-bit instruction */
+
+      insn >>= 16;
+      insn_type = nds32_analyze_epilogue_insn16 (insn, NULL);
+    }
+
+  if (insn_type == INSN_NORMAL || insn_type == INSN_RESET_SP)
+    return 0;
+
+  /* Search the required 'return' instruction within the following reasonable
+     instructions.  */
+  ret_found = nds32_analyze_epilogue (gdbarch, addr, NULL);
+  if (ret_found == 0)
+    return 0;
+
+  /* Scan backwards to make sure that the last instruction has adjusted
+     stack.  Both a 16-bit and a 32-bit instruction will be tried.  This is
+     just a heuristic, so the false positives will be acceptable.  */
+  insn = read_memory_unsigned_integer (addr - 2, 4, BFD_ENDIAN_BIG);
+
+  /* Only 16-bit instructions are possible at addr - 2.  */
+  if ((insn & 0x80000000) != 0)
+    {
+      /* This may be a 16-bit instruction or part of a 32-bit instruction.  */
+
+      insn_type = nds32_analyze_epilogue_insn16 (insn >> 16, NULL);
+      if (insn_type == INSN_RECOVER)
+	return 1;
+    }
+
+  insn = read_memory_unsigned_integer (addr - 4, 4, BFD_ENDIAN_BIG);
+
+  /* If this is a 16-bit instruction at addr - 4, then there must be another
+     16-bit instruction at addr - 2, so only 32-bit instructions need to
+     be analyzed here.  */
+  if ((insn & 0x80000000) == 0)
+    {
+      /* This may be a 32-bit instruction or part of a 32-bit instruction.  */
+
+      insn_type = nds32_analyze_epilogue_insn32 (abi_use_fpr, insn, NULL);
+      if (insn_type == INSN_RECOVER || insn_type == INSN_RESET_SP)
+	return 1;
+    }
+
+  return 0;
+}
+
+/* Implement the "sniffer" frame_unwind method.  */
+
+static int
+nds32_epilogue_frame_sniffer (const struct frame_unwind *self,
+			      struct frame_info *this_frame, void **this_cache)
+{
+  if (frame_relative_level (this_frame) == 0)
+    return nds32_stack_frame_destroyed_p (get_frame_arch (this_frame),
+					  get_frame_pc (this_frame));
+  else
+    return 0;
+}
+
+/* Allocate and fill in *THIS_CACHE with information needed to unwind
+   *THIS_FRAME within epilogue.  Do not do this if *THIS_CACHE was already
+   allocated.  Return a pointer to the current nds32_frame_cache in
+   *THIS_CACHE.  */
+
+static struct nds32_frame_cache *
+nds32_epilogue_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct nds32_frame_cache *cache;
+  CORE_ADDR current_pc, current_sp;
+  int i;
+
+  if (*this_cache)
+    return (struct nds32_frame_cache *) *this_cache;
+
+  cache = nds32_alloc_frame_cache ();
+  *this_cache = cache;
+
+  cache->pc = get_frame_func (this_frame);
+  current_pc = get_frame_pc (this_frame);
+  nds32_analyze_epilogue (gdbarch, current_pc, cache);
+
+  current_sp = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM);
+  cache->prev_sp = current_sp + cache->sp_offset;
+
+  /* Adjust all the saved registers such that they contain addresses
+     instead of offsets.  */
+  for (i = 0; i < NDS32_NUM_SAVED_REGS; i++)
+    if (cache->saved_regs[i] != REG_UNAVAIL)
+      cache->saved_regs[i] = current_sp + cache->saved_regs[i];
+
+  return cache;
+}
+
+/* Implement the "this_id" frame_unwind method.  */
+
+static void
+nds32_epilogue_frame_this_id (struct frame_info *this_frame,
+			      void **this_cache, struct frame_id *this_id)
+{
+  struct nds32_frame_cache *cache
+    = nds32_epilogue_frame_cache (this_frame, this_cache);
+
+  /* This marks the outermost frame.  */
+  if (cache->prev_sp == 0)
+    return;
+
+  *this_id = frame_id_build (cache->prev_sp, cache->pc);
+}
+
+/* Implement the "prev_register" frame_unwind method.  */
+
+static struct value *
+nds32_epilogue_frame_prev_register (struct frame_info *this_frame,
+				    void **this_cache, int regnum)
+{
+  struct nds32_frame_cache *cache
+    = nds32_epilogue_frame_cache (this_frame, this_cache);
+
+  if (regnum == NDS32_SP_REGNUM)
+    return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp);
+
+  /* The PC of the previous frame is stored in the LP register of
+     the current frame.  */
+  if (regnum == NDS32_PC_REGNUM)
+    regnum = NDS32_LP_REGNUM;
+
+  if (regnum < NDS32_NUM_SAVED_REGS && cache->saved_regs[regnum] != REG_UNAVAIL)
+    return frame_unwind_got_memory (this_frame, regnum,
+				    cache->saved_regs[regnum]);
+
+  return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static const struct frame_unwind nds32_epilogue_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  nds32_epilogue_frame_this_id,
+  nds32_epilogue_frame_prev_register,
+  NULL,
+  nds32_epilogue_frame_sniffer
+};
+
+/* Implement the "dummy_id" gdbarch method.  */
+
+static struct frame_id
+nds32_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, NDS32_SP_REGNUM);
+
+  return frame_id_build (sp, get_frame_pc (this_frame));
+}
+
+/* Implement the "unwind_pc" gdbarch method.  */
+
+static CORE_ADDR
+nds32_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, NDS32_PC_REGNUM);
+}
+
+/* Implement the "unwind_sp" gdbarch method.  */
+
+static CORE_ADDR
+nds32_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_unsigned (next_frame, NDS32_SP_REGNUM);
+}
+
+/* Floating type and struct type that has only one floating type member
+   can pass value using FPU registers (when FPU ABI is used).  */
+
+static int
+nds32_check_calling_use_fpr (struct type *type)
+{
+  struct type *t;
+  enum type_code typecode;
+
+  t = type;
+  while (1)
+    {
+      t = check_typedef (t);
+      typecode = TYPE_CODE (t);
+      if (typecode != TYPE_CODE_STRUCT)
+	break;
+      else if (TYPE_NFIELDS (t) != 1)
+	return 0;
+      else
+	t = TYPE_FIELD_TYPE (t, 0);
+    }
+
+  return typecode == TYPE_CODE_FLT;
+}
+
+/* Return the alignment (in bytes) of the given type.  */
+
+static int
+nds32_type_align (struct type *type)
+{
+  int n;
+  int align;
+  int falign;
+
+  type = check_typedef (type);
+  switch (TYPE_CODE (type))
+    {
+    default:
+      /* Should never happen.  */
+      internal_error (__FILE__, __LINE__, _("unknown type alignment"));
+      return 4;
+
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_ENUM:
+    case TYPE_CODE_INT:
+    case TYPE_CODE_FLT:
+    case TYPE_CODE_SET:
+    case TYPE_CODE_RANGE:
+    case TYPE_CODE_REF:
+    case TYPE_CODE_CHAR:
+    case TYPE_CODE_BOOL:
+      return TYPE_LENGTH (type);
+
+    case TYPE_CODE_ARRAY:
+    case TYPE_CODE_COMPLEX:
+      return nds32_type_align (TYPE_TARGET_TYPE (type));
+
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_UNION:
+      align = 1;
+      for (n = 0; n < TYPE_NFIELDS (type); n++)
+	{
+	  falign = nds32_type_align (TYPE_FIELD_TYPE (type, n));
+	  if (falign > align)
+	    align = falign;
+	}
+      return align;
+    }
+}
+
+/* Implement the "push_dummy_call" gdbarch method.  */
+
+static CORE_ADDR
+nds32_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
+		       struct regcache *regcache, CORE_ADDR bp_addr,
+		       int nargs, struct value **args, CORE_ADDR sp,
+		       int struct_return, CORE_ADDR struct_addr)
+{
+  const int REND = 6;		/* End for register offset.  */
+  int goff = 0;			/* Current gpr offset for argument.  */
+  int foff = 0;			/* Current fpr offset for argument.  */
+  int soff = 0;			/* Current stack offset for argument.  */
+  int i;
+  ULONGEST regval;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  struct type *func_type = value_type (function);
+  int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi);
+  int abi_split = nds32_abi_split (tdep->elf_abi);
+
+  /* Set the return address.  For the NDS32, the return breakpoint is
+     always at BP_ADDR.  */
+  regcache_cooked_write_unsigned (regcache, NDS32_LP_REGNUM, bp_addr);
+
+  /* If STRUCT_RETURN is true, then the struct return address (in
+     STRUCT_ADDR) will consume the first argument-passing register.
+     Both adjust the register count and store that value.  */
+  if (struct_return)
+    {
+      regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, struct_addr);
+      goff++;
+    }
+
+  /* Now make sure there's space on the stack */
+  for (i = 0; i < nargs; i++)
+    {
+      struct type *type = value_type (args[i]);
+      int align = nds32_type_align (type);
+
+      /* If align is zero, it may be an empty struct.
+	 Just ignore the argument of empty struct.  */
+      if (align == 0)
+	continue;
+
+      sp -= TYPE_LENGTH (type);
+      sp = align_down (sp, align);
+    }
+
+  /* Stack must be 8-byte aligned.  */
+  sp = align_down (sp, 8);
+
+  soff = 0;
+  for (i = 0; i < nargs; i++)
+    {
+      const gdb_byte *val;
+      int align, len;
+      struct type *type;
+      int calling_use_fpr;
+      int use_fpr = 0;
+
+      type = value_type (args[i]);
+      calling_use_fpr = nds32_check_calling_use_fpr (type);
+      len = TYPE_LENGTH (type);
+      align = nds32_type_align (type);
+      val = value_contents (args[i]);
+
+      /* The size of a composite type larger than 4 bytes will be rounded
+	 up to the nearest multiple of 4.  */
+      if (len > 4)
+	len = align_up (len, 4);
+
+      /* Variadic functions are handled differently between AABI and ABI2FP+.
+
+	 For AABI, the caller pushes arguments in registers, callee stores
+	 unnamed arguments in stack, and then va_arg fetch arguments in stack.
+	 Therefore, we don't have to handle variadic functions specially.
+
+	 For ABI2FP+, the caller pushes only named arguments in registers
+	 and pushes all unnamed arguments in stack.  */
+
+      if (abi_use_fpr && TYPE_VARARGS (func_type)
+	  && i >= TYPE_NFIELDS (func_type))
+	goto use_stack;
+
+      /* Try to use FPRs to pass arguments only when
+	 1. The program is built using toolchain with FPU support.
+	 2. The type of this argument can use FPR to pass value.  */
+      use_fpr = abi_use_fpr && calling_use_fpr;
+
+      if (use_fpr)
+	{
+	  if (tdep->fpu_freg == -1)
+	    goto error_no_fpr;
+
+	  /* Adjust alignment.  */
+	  if ((align >> 2) > 0)
+	    foff = align_up (foff, align >> 2);
+
+	  if (foff < REND)
+	    {
+	      switch (len)
+		{
+		case 4:
+		  regcache_cooked_write (regcache,
+					 tdep->fs0_regnum + foff, val);
+		  foff++;
+		  break;
+		case 8:
+		  regcache_cooked_write (regcache,
+					 NDS32_FD0_REGNUM + (foff >> 1), val);
+		  foff += 2;
+		  break;
+		default:
+		  /* Long double?  */
+		  internal_error (__FILE__, __LINE__,
+				  "Do not know how to handle %d-byte double.\n",
+				  len);
+		  break;
+		}
+	      continue;
+	    }
+	}
+      else
+	{
+	  /*
+	     When passing arguments using GPRs,
+
+	     * A composite type not larger than 4 bytes is passed in $rN.
+	       The format is as if the value is loaded with load instruction
+	       of corresponding size (e.g., LB, LH, LW).
+
+	       For example,
+
+		       r0
+		       31      0
+	       LITTLE: [x x b a]
+		  BIG: [x x a b]
+
+	     * Otherwise, a composite type is passed in consecutive registers.
+	       The size is rounded up to the nearest multiple of 4.
+	       The successive registers hold the parts of the argument as if
+	       were loaded using lmw instructions.
+
+	       For example,
+
+		       r0	 r1
+		       31      0 31      0
+	       LITTLE: [d c b a] [x x x e]
+		  BIG: [a b c d] [e x x x]
+	   */
+
+	  /* Adjust alignment.  */
+	  if ((align >> 2) > 0)
+	    goff = align_up (goff, align >> 2);
+
+	  if (len <= (REND - goff) * 4)
+	    {
+	      /* This argument can be passed wholly via GPRs.  */
+	      while (len > 0)
+		{
+		  regval = extract_unsigned_integer (val, (len > 4) ? 4 : len,
+						     byte_order);
+		  regcache_cooked_write_unsigned (regcache,
+						  NDS32_R0_REGNUM + goff,
+						  regval);
+		  len -= 4;
+		  val += 4;
+		  goff++;
+		}
+	      continue;
+	    }
+	  else if (abi_split)
+	    {
+	      /* Some parts of this argument can be passed via GPRs.  */
+	      while (goff < REND)
+		{
+		  regval = extract_unsigned_integer (val, (len > 4) ? 4 : len,
+						     byte_order);
+		  regcache_cooked_write_unsigned (regcache,
+						  NDS32_R0_REGNUM + goff,
+						  regval);
+		  len -= 4;
+		  val += 4;
+		  goff++;
+		}
+	    }
+	}
+
+use_stack:
+      /*
+	 When pushing (split parts of) an argument into stack,
+
+	 * A composite type not larger than 4 bytes is copied to different
+	   base address.
+	   In little-endian, the first byte of this argument is aligned
+	   at the low address of the next free word.
+	   In big-endian, the last byte of this argument is aligned
+	   at the high address of the next free word.
+
+	   For example,
+
+	   sp [ - ]  [ c ] hi
+	      [ c ]  [ b ]
+	      [ b ]  [ a ]
+	      [ a ]  [ - ] lo
+	     LITTLE   BIG
+       */
+
+      /* Adjust alignment.  */
+      soff = align_up (soff, align);
+
+      while (len > 0)
+	{
+	  int rlen = (len > 4) ? 4 : len;
+
+	  if (byte_order == BFD_ENDIAN_BIG)
+	    write_memory (sp + soff + 4 - rlen, val, rlen);
+	  else
+	    write_memory (sp + soff, val, rlen);
+
+	  len -= 4;
+	  val += 4;
+	  soff += 4;
+	}
+    }
+
+  /* Finally, update the SP register.  */
+  regcache_cooked_write_unsigned (regcache, NDS32_SP_REGNUM, sp);
+
+  return sp;
+
+error_no_fpr:
+  /* If use_fpr, but no floating-point register exists,
+     then it is an error.  */
+  error (_("Fail to call. FPU registers are required."));
+}
+
+/* Read, for architecture GDBARCH, a function return value of TYPE
+   from REGCACHE, and copy that into VALBUF.  */
+
+static void
+nds32_extract_return_value (struct gdbarch *gdbarch, struct type *type,
+			    struct regcache *regcache, gdb_byte *valbuf)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi);
+  int calling_use_fpr;
+  int len;
+
+  calling_use_fpr = nds32_check_calling_use_fpr (type);
+  len = TYPE_LENGTH (type);
+
+  if (abi_use_fpr && calling_use_fpr)
+    {
+      if (len == 4)
+	regcache_cooked_read (regcache, tdep->fs0_regnum, valbuf);
+      else if (len == 8)
+	regcache_cooked_read (regcache, NDS32_FD0_REGNUM, valbuf);
+      else
+	internal_error (__FILE__, __LINE__,
+			_("Cannot extract return value of %d bytes "
+			  "long floating-point."), len);
+    }
+  else
+    {
+      /*
+	 When returning result,
+
+	 * A composite type not larger than 4 bytes is returned in $r0.
+	   The format is as if the result is loaded with load instruction
+	   of corresponding size (e.g., LB, LH, LW).
+
+	   For example,
+
+		   r0
+		   31      0
+	   LITTLE: [x x b a]
+	      BIG: [x x a b]
+
+	 * Otherwise, a composite type not larger than 8 bytes is returned
+	   in $r0 and $r1.
+	   In little-endian, the first word is loaded in $r0.
+	   In big-endian, the last word is loaded in $r1.
+
+	   For example,
+
+		   r0	     r1
+		   31      0 31      0
+	   LITTLE: [d c b a] [x x x e]
+	      BIG: [x x x a] [b c d e]
+       */
+
+      ULONGEST tmp;
+
+      if (len < 4)
+	{
+	  /* By using store_unsigned_integer we avoid having to do
+	     anything special for small big-endian values.  */
+	  regcache_cooked_read_unsigned (regcache, NDS32_R0_REGNUM, &tmp);
+	  store_unsigned_integer (valbuf, len, byte_order, tmp);
+	}
+      else if (len == 4)
+	{
+	  regcache_cooked_read (regcache, NDS32_R0_REGNUM, valbuf);
+	}
+      else if (len < 8)
+	{
+	  int len1, len2;
+
+	  len1 = byte_order == BFD_ENDIAN_BIG ? len - 4 : 4;
+	  len2 = len - len1;
+
+	  regcache_cooked_read_unsigned (regcache, NDS32_R0_REGNUM, &tmp);
+	  store_unsigned_integer (valbuf, len1, byte_order, tmp);
+
+	  regcache_cooked_read_unsigned (regcache, NDS32_R0_REGNUM + 1, &tmp);
+	  store_unsigned_integer (valbuf + len1, len2, byte_order, tmp);
+	}
+      else
+	{
+	  regcache_cooked_read (regcache, NDS32_R0_REGNUM, valbuf);
+	  regcache_cooked_read (regcache, NDS32_R0_REGNUM + 1, valbuf + 4);
+	}
+    }
+}
+
+/* Write, for architecture GDBARCH, a function return value of TYPE
+   from VALBUF into REGCACHE.  */
+
+static void
+nds32_store_return_value (struct gdbarch *gdbarch, struct type *type,
+			  struct regcache *regcache, const gdb_byte *valbuf)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int abi_use_fpr = nds32_abi_use_fpr (tdep->elf_abi);
+  int calling_use_fpr;
+  int len;
+
+  calling_use_fpr = nds32_check_calling_use_fpr (type);
+  len = TYPE_LENGTH (type);
+
+  if (abi_use_fpr && calling_use_fpr)
+    {
+      if (len == 4)
+	regcache_cooked_write (regcache, tdep->fs0_regnum, valbuf);
+      else if (len == 8)
+	regcache_cooked_write (regcache, NDS32_FD0_REGNUM, valbuf);
+      else
+	internal_error (__FILE__, __LINE__,
+			_("Cannot store return value of %d bytes "
+			  "long floating-point."), len);
+    }
+  else
+    {
+      ULONGEST regval;
+
+      if (len < 4)
+	{
+	  regval = extract_unsigned_integer (valbuf, len, byte_order);
+	  regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, regval);
+	}
+      else if (len == 4)
+	{
+	  regcache_cooked_write (regcache, NDS32_R0_REGNUM, valbuf);
+	}
+      else if (len < 8)
+	{
+	  int len1, len2;
+
+	  len1 = byte_order == BFD_ENDIAN_BIG ? len - 4 : 4;
+	  len2 = len - len1;
+
+	  regval = extract_unsigned_integer (valbuf, len1, byte_order);
+	  regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM, regval);
+
+	  regval = extract_unsigned_integer (valbuf + len1, len2, byte_order);
+	  regcache_cooked_write_unsigned (regcache, NDS32_R0_REGNUM + 1,
+					  regval);
+	}
+      else
+	{
+	  regcache_cooked_write (regcache, NDS32_R0_REGNUM, valbuf);
+	  regcache_cooked_write (regcache, NDS32_R0_REGNUM + 1, valbuf + 4);
+	}
+    }
+}
+
+/* Implement the "return_value" gdbarch method.
+
+   Determine, for architecture GDBARCH, how a return value of TYPE
+   should be returned.  If it is supposed to be returned in registers,
+   and READBUF is non-zero, read the appropriate value from REGCACHE,
+   and copy it into READBUF.  If WRITEBUF is non-zero, write the value
+   from WRITEBUF into REGCACHE.  */
+
+static enum return_value_convention
+nds32_return_value (struct gdbarch *gdbarch, struct value *func_type,
+		    struct type *type, struct regcache *regcache,
+		    gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  if (TYPE_LENGTH (type) > 8)
+    {
+      return RETURN_VALUE_STRUCT_CONVENTION;
+    }
+  else
+    {
+      if (readbuf != NULL)
+	nds32_extract_return_value (gdbarch, type, regcache, readbuf);
+      if (writebuf != NULL)
+	nds32_store_return_value (gdbarch, type, regcache, writebuf);
+
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+}
+
+/* Implement the "get_longjmp_target" gdbarch method.  */
+
+static int
+nds32_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
+{
+  gdb_byte buf[4];
+  CORE_ADDR jb_addr;
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  jb_addr = get_frame_register_unsigned (frame, NDS32_R0_REGNUM);
+
+  if (target_read_memory (jb_addr + 11 * 4, buf, 4))
+    return 0;
+
+  *pc = extract_unsigned_integer (buf, 4, byte_order);
+  return 1;
+}
+
+/* Validate the given TDESC, and fixed-number some registers in it.
+   Return 0 if the given TDESC does not contain the required feature
+   or not contain required registers.  */
+
+static int
+nds32_validate_tdesc_p (const struct target_desc *tdesc,
+			struct tdesc_arch_data *tdesc_data,
+			int *fpu_freg, int *use_pseudo_fsrs)
+{
+  const struct tdesc_feature *feature;
+  int i, valid_p;
+
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.nds32.core");
+  if (feature == NULL)
+    return 0;
+
+  valid_p = 1;
+  /* Validate and fixed-number R0-R10.  */
+  for (i = NDS32_R0_REGNUM; i <= NDS32_R0_REGNUM + 10; i++)
+    valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
+					nds32_register_names[i]);
+
+  /* Validate R15.  */
+  valid_p &= tdesc_unnumbered_register (feature,
+					nds32_register_names[NDS32_TA_REGNUM]);
+
+  /* Validate and fixed-number FP, GP, LP, SP, PC.  */
+  for (i = NDS32_FP_REGNUM; i <= NDS32_PC_REGNUM; i++)
+    valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
+					nds32_register_names[i]);
+
+  if (!valid_p)
+    return 0;
+
+  /* Fixed-number R11-R27.  */
+  for (i = NDS32_R0_REGNUM + 11; i <= NDS32_R0_REGNUM + 27; i++)
+    tdesc_numbered_register (feature, tdesc_data, i, nds32_register_names[i]);
+
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.nds32.fpu");
+  if (feature != NULL)
+    {
+      int num_fdr_regs, num_fsr_regs, fs0_regnum, num_listed_fsr;
+      int freg = -1;
+
+      /* Guess FPU configuration via listed registers.  */
+      if (tdesc_unnumbered_register (feature, "fd31"))
+	freg = 3;
+      else if (tdesc_unnumbered_register (feature, "fd15"))
+	freg = 2;
+      else if (tdesc_unnumbered_register (feature, "fd7"))
+	freg = 1;
+      else if (tdesc_unnumbered_register (feature, "fd3"))
+	freg = 0;
+
+      if (freg == -1)
+	/* Required FDR is not found.  */
+	return 0;
+      else
+	*fpu_freg = freg;
+
+      /* Validate and fixed-number required FDRs.  */
+      num_fdr_regs = num_fdr_map[freg];
+      for (i = 0; i < num_fdr_regs; i++)
+	valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					    NDS32_FD0_REGNUM + i,
+					    nds32_fdr_register_names[i]);
+      if (!valid_p)
+	return 0;
+
+      /* Count the number of listed FSRs, and fixed-number them if present.  */
+      num_fsr_regs = num_fsr_map[freg];
+      fs0_regnum = NDS32_FD0_REGNUM + num_fdr_regs;
+      num_listed_fsr = 0;
+      for (i = 0; i < num_fsr_regs; i++)
+	num_listed_fsr += tdesc_numbered_register (feature, tdesc_data,
+						   fs0_regnum + i,
+						   nds32_fsr_register_names[i]);
+
+      if (num_listed_fsr == 0)
+	/* No required FSRs are listed explicitly,  make them pseudo registers
+	   of FDRs.  */
+	*use_pseudo_fsrs = 1;
+      else if (num_listed_fsr == num_fsr_regs)
+	/* All required FSRs are listed explicitly.  */
+	*use_pseudo_fsrs = 0;
+      else
+	/* Some required FSRs are missing.  */
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Initialize the current architecture based on INFO.  If possible,
+   re-use an architecture from ARCHES, which is a list of
+   architectures already created during this debugging session.
+
+   Called e.g. at program startup, when reading a core file, and when
+   reading a binary file.  */
+
+static struct gdbarch *
+nds32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep *tdep;
+  struct gdbarch_list *best_arch;
+  struct tdesc_arch_data *tdesc_data = NULL;
+  const struct target_desc *tdesc = info.target_desc;
+  int elf_abi = E_NDS_ABI_AABI;
+  int fpu_freg = -1;
+  int use_pseudo_fsrs = 0;
+  int i, num_regs, maxregs;
+
+  /* Extract the elf_flags if available.  */
+  if (info.abfd && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
+    elf_abi = elf_elfheader (info.abfd)->e_flags & EF_NDS_ABI;
+
+  /* If there is already a candidate, use it.  */
+  for (best_arch = gdbarch_list_lookup_by_info (arches, &info);
+       best_arch != NULL;
+       best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info))
+    {
+      struct gdbarch_tdep *idep = gdbarch_tdep (best_arch->gdbarch);
+
+      if (idep->elf_abi != elf_abi)
+	continue;
+
+      /* Found a match.  */
+      break;
+    }
+
+  if (best_arch != NULL)
+    return best_arch->gdbarch;
+
+  if (!tdesc_has_registers (tdesc))
+    tdesc = tdesc_nds32;
+
+  tdesc_data = tdesc_data_alloc ();
+
+  if (!nds32_validate_tdesc_p (tdesc, tdesc_data, &fpu_freg, &use_pseudo_fsrs))
+    {
+      tdesc_data_cleanup (tdesc_data);
+      return NULL;
+    }
+
+  /* Allocate space for the new architecture.  */
+  tdep = XCNEW (struct gdbarch_tdep);
+  tdep->fpu_freg = fpu_freg;
+  tdep->use_pseudo_fsrs = use_pseudo_fsrs;
+  tdep->fs0_regnum = -1;
+  tdep->elf_abi = elf_abi;
+
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  if (fpu_freg == -1)
+    num_regs = NDS32_NUM_REGS;
+  else if (use_pseudo_fsrs == 1)
+    {
+      set_gdbarch_pseudo_register_read (gdbarch, nds32_pseudo_register_read);
+      set_gdbarch_pseudo_register_write (gdbarch, nds32_pseudo_register_write);
+      set_tdesc_pseudo_register_name (gdbarch, nds32_pseudo_register_name);
+      set_tdesc_pseudo_register_type (gdbarch, nds32_pseudo_register_type);
+      set_gdbarch_num_pseudo_regs (gdbarch, num_fsr_map[fpu_freg]);
+
+      num_regs = NDS32_NUM_REGS + num_fdr_map[fpu_freg];
+    }
+  else
+    num_regs = NDS32_NUM_REGS + num_fdr_map[fpu_freg] + num_fsr_map[fpu_freg];
+
+  set_gdbarch_num_regs (gdbarch, num_regs);
+  tdesc_use_registers (gdbarch, tdesc, tdesc_data);
+
+  /* Cache the register number of fs0.  */
+  if (fpu_freg != -1)
+    tdep->fs0_regnum = user_reg_map_name_to_regnum (gdbarch, "fs0", -1);
+
+  /* Add NDS32 register aliases.  To avoid search in user register name space,
+     user_reg_map_name_to_regnum is not used.  */
+  maxregs = (gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch));
+  for (i = 0; i < ARRAY_SIZE (nds32_register_aliases); i++)
+    {
+      int regnum, j;
+
+      regnum = -1;
+      /* Search register name space.  */
+      for (j = 0; j < maxregs; j++)
+	{
+	  const char *regname = gdbarch_register_name (gdbarch, j);
+
+	  if (regname != NULL
+	      && strcmp (regname, nds32_register_aliases[i].name) == 0)
+	    {
+	      regnum = j;
+	      break;
+	    }
+	}
+
+      /* Try next alias entry if the given name can not be found in register
+	 name space.  */
+      if (regnum == -1)
+	continue;
+
+      user_reg_add (gdbarch, nds32_register_aliases[i].alias,
+		    value_of_nds32_reg, (const void *) (intptr_t) regnum);
+    }
+
+  nds32_add_reggroups (gdbarch);
+
+  /* Hook in ABI-specific overrides, if they have been registered.  */
+  info.tdep_info = (void *) tdesc_data;
+  gdbarch_init_osabi (info, gdbarch);
+
+  /* Override tdesc_register callbacks for system registers.  */
+  set_gdbarch_register_reggroup_p (gdbarch, nds32_register_reggroup_p);
+
+  set_gdbarch_sp_regnum (gdbarch, NDS32_SP_REGNUM);
+  set_gdbarch_pc_regnum (gdbarch, NDS32_PC_REGNUM);
+  set_gdbarch_unwind_sp (gdbarch, nds32_unwind_sp);
+  set_gdbarch_unwind_pc (gdbarch, nds32_unwind_pc);
+  set_gdbarch_stack_frame_destroyed_p (gdbarch, nds32_stack_frame_destroyed_p);
+  set_gdbarch_dwarf2_reg_to_regnum (gdbarch, nds32_dwarf2_reg_to_regnum);
+
+  set_gdbarch_push_dummy_call (gdbarch, nds32_push_dummy_call);
+  set_gdbarch_return_value (gdbarch, nds32_return_value);
+  set_gdbarch_dummy_id (gdbarch, nds32_dummy_id);
+
+  set_gdbarch_skip_prologue (gdbarch, nds32_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_breakpoint_from_pc (gdbarch, nds32_breakpoint_from_pc);
+
+  set_gdbarch_frame_align (gdbarch, nds32_frame_align);
+  frame_base_set_default (gdbarch, &nds32_frame_base);
+
+  set_gdbarch_print_insn (gdbarch, print_insn_nds32);
+
+  /* Handle longjmp.  */
+  set_gdbarch_get_longjmp_target (gdbarch, nds32_get_longjmp_target);
+
+  /* The order of appending is the order it check frame.  */
+  dwarf2_append_unwinders (gdbarch);
+  frame_unwind_append_unwinder (gdbarch, &nds32_epilogue_frame_unwind);
+  frame_unwind_append_unwinder (gdbarch, &nds32_frame_unwind);
+
+  return gdbarch;
+}
+
+void
+_initialize_nds32_tdep (void)
+{
+  /* Initialize gdbarch.  */
+  register_gdbarch_init (bfd_arch_nds32, nds32_gdbarch_init);
+
+  initialize_tdesc_nds32 ();
+  nds32_init_reggroups ();
+}
diff --git a/gdb/nds32-tdep.h b/gdb/nds32-tdep.h
new file mode 100644
index 0000000..78ad7b2
--- /dev/null
+++ b/gdb/nds32-tdep.h
@@ -0,0 +1,54 @@
+/* Target-dependent code for the NDS32 architecture, for GDB.
+
+   Copyright (C) 2013-2016 Free Software Foundation, Inc.
+   Contributed by Andes Technology Corporation.
+
+   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 NDS32_TDEP_H
+#define NDS32_TDEP_H
+
+enum nds32_regnum
+{
+  /* General purpose registers.  */
+  NDS32_R0_REGNUM = 0,
+  NDS32_R5_REGNUM = 5,
+  NDS32_TA_REGNUM = 15,		/* Temporary register.  */
+  NDS32_FP_REGNUM = 28,		/* Frame pointer.  */
+  NDS32_GP_REGNUM = 29,		/* Global pointer.  */
+  NDS32_LP_REGNUM = 30,		/* Link pointer.  */
+  NDS32_SP_REGNUM = 31,		/* Stack pointer.  */
+
+  NDS32_PC_REGNUM = 32,		/* Program counter.  */
+
+  NDS32_NUM_REGS,
+
+  /* The first double precision floating-point register.  */
+  NDS32_FD0_REGNUM = NDS32_NUM_REGS,
+};
+
+struct gdbarch_tdep
+{
+  /* The guessed FPU configuration.  */
+  int fpu_freg;
+  /* FSRs are defined as pseudo registers.  */
+  int use_pseudo_fsrs;
+  /* Cached regnum of the first FSR (FS0).  */
+  int fs0_regnum;
+  /* ELF ABI info.  */
+  int elf_abi;
+};
+#endif /* NDS32_TDEP_H */
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ba7dd39..3dad273 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2016-06-17  Yan-Ting Lin  <currygt52@gmail.com>
+
+	* gdb.base/float.exp: Add target check for nds32*-*-*.
+	* gdb.xml/tdesc-regs.exp: Set core-regs for nds32*-*-*.
+
 2016-06-13  Andrew Burgess  <andrew.burgess@embecosm.com>
 
 	* gdb.base/call-ar-st.exp: Report unsupported rather than xfail
diff --git a/gdb/testsuite/gdb.base/float.exp b/gdb/testsuite/gdb.base/float.exp
index 939f07f..bc3e230 100644
--- a/gdb/testsuite/gdb.base/float.exp
+++ b/gdb/testsuite/gdb.base/float.exp
@@ -76,6 +76,15 @@ if { [is_aarch64_target] } then {
 	      pass "info float (with FPU)"
 	  }
     }
+} elseif [istarget "nds32*-*-*"] then {
+    gdb_test_multiple "info float" "info_float" {
+        -re "fd0.*fd3.*$gdb_prompt $" {
+            pass "info float (with FPU)"
+	}
+        -re "No floating.point info available for this processor.*" {
+            pass "info float (without FPU)"
+	}
+    }
 } elseif [istarget "powerpc*-*-*"] then {
     gdb_test_multiple "info float" "info_float" {
         -re "f0.*f1.*f31.*fpscr.*$gdb_prompt $" {
diff --git a/gdb/testsuite/gdb.xml/tdesc-regs.exp b/gdb/testsuite/gdb.xml/tdesc-regs.exp
index 48204cd..c197e28 100644
--- a/gdb/testsuite/gdb.xml/tdesc-regs.exp
+++ b/gdb/testsuite/gdb.xml/tdesc-regs.exp
@@ -39,6 +39,9 @@ switch -glob -- [istarget] {
     "mips*-*-*" {
 	set core-regs {mips-cpu.xml mips-cp0.xml mips-fpu.xml mips-dsp.xml}
     }
+    "nds32*-*-*" {
+	set core-regs {nds32-core.xml}
+    }
     "nios2-*-*" {
 	set core-regs {nios2-cpu.xml}
     }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]