diff mbox series

[MSP430] Read MCU data from external file specified with environment variable or installed into toolchain subdirectory

Message ID 20191101105347.7c61474a@jozef-kubuntu
State New
Headers show
Series [MSP430] Read MCU data from external file specified with environment variable or installed into toolchain subdirectory | expand

Commit Message

Jozef Lawrynowicz Nov. 1, 2019, 10:53 a.m. UTC
Each different MSP430 MCU has two properties that affect code generation, which
are the the CPU ISA and the hardware multiply support. These can be manually
specified with the -mcpu= and -mhwmult= options, respectively.
The existing -mmcu= option takes an MCU name as an argument and uses the CPU
ISA and hardware multiply known to be supported for that MCU, so the user does
not have to provide the -mcpu and -mhwmult options.

Currently, the MCU data can be provided in an external "devices.csv" file, and
this file is already searched for on the paths specified with -I and -L.

This patch extends the searching behaviour so the path to the directory
containing devices.csv can either be specified with the "MSP430_GCC_INCLUDE_DIR"
environment variable, or installed into the msp430-elf/include/devices
subdirectory of the toolchain.
The msp430.exp toolchain driver has been extended to ensure these new methods
can be tested properly.

Successfully regtested on trunk.

Ok to apply?

Comments

Jeff Law Nov. 1, 2019, 9:54 p.m. UTC | #1
On 11/1/19 4:53 AM, Jozef Lawrynowicz wrote:
> Each different MSP430 MCU has two properties that affect code generation, which
> are the the CPU ISA and the hardware multiply support. These can be manually
> specified with the -mcpu= and -mhwmult= options, respectively.
> The existing -mmcu= option takes an MCU name as an argument and uses the CPU
> ISA and hardware multiply known to be supported for that MCU, so the user does
> not have to provide the -mcpu and -mhwmult options.
> 
> Currently, the MCU data can be provided in an external "devices.csv" file, and
> this file is already searched for on the paths specified with -I and -L.
> 
> This patch extends the searching behaviour so the path to the directory
> containing devices.csv can either be specified with the "MSP430_GCC_INCLUDE_DIR"
> environment variable, or installed into the msp430-elf/include/devices
> subdirectory of the toolchain.
> The msp430.exp toolchain driver has been extended to ensure these new methods
> can be tested properly.
> 
> Successfully regtested on trunk.
> 
> Ok to apply?
> 
> 
> 0001-MSP430-Read-MCU-data-from-external-file-specified-wi.patch
> 
> From f781013148c2fd242af8526dbcfa2079fe30db07 Mon Sep 17 00:00:00 2001
> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
> Date: Wed, 30 Oct 2019 21:10:38 +0000
> Subject: [PATCH] MSP430: Read MCU data from external file specified with
>  environment variable or installed into toolchain subdirectory
> 
> gcc/ChangeLog:
> 
> 2019-10-31  Jozef Lawrynowicz  <jozef.l@mittosystems.com>
> 
> 	* config/msp430/driver-msp430.c (msp430_get_linker_devices_include_path):
> 	New spec function.
> 	* config/msp430/msp430-devices.c (msp430_dirname): New function.
> 	(extract_devices_dir_from_exec_prefix): New function.
> 	(extract_devices_dir_from_collect_gcc): New function.
> 	(msp430_check_env_var_for_devices): New function.
> 	(msp430_check_path_for_devices): Use xstrdup instead of ASTRDUP.
> 	(parse_devices_csv): Call msp430_check_env_var_for_devices if
> 	devices.csv was not found using other methods.
> 	* config/msp430/msp430-devices.h (msp430_check_env_var_for_devices):
> 	New prototype.
> 	(msp430_dirname): Likewise.
> 	* config/msp430/msp430.c (msp430_register_pre_includes): New function.
> 	* config/msp430/msp430.h (EXTRA_SPEC_FUNCTIONS): Add
> 	msp430_get_linker_devices_include_path.
> 	(TARGET_EXTRA_PRE_INCLUDES): Define.
> 	* doc/invoke.texi: Document new ways of searching for support files.
> 
> gcc/testsuite/ChangeLog:
> 
> 2019-10-31  Jozef Lawrynowicz  <jozef.l@mittosystems.com>
> 
> 	* gcc.target/msp430/devices/csv-using-env-var.c: New test.
> 	* gcc.target/msp430/devices/csv-using-installed.c: New test.
> 	* gcc.target/msp430/devices/csv-using-option.c: New test.
> 	* gcc.target/msp430/devices/devices-main.c: New test source file.
> 	* gcc.target/msp430/devices/msp430-devices.h: New test.
> 	* gcc.target/msp430/msp430.exp (msp430_device_permutations_runtest):
> 	Add special cases for csv-using* tests.
> 	Define TESTING_HARD_DATA when running tests that use hard-coded device
> 	data.
> 	(get_installed_device_data_path): New.
> 	(msp430_hide_installed_devices_data): New.
> 	(msp430_restore_installed_devices_data): New.
> 	(msp430_test_installed_device_data): New.
> 	(msp430_install_device_data): New.
OK
jeff
diff mbox series

Patch

From f781013148c2fd242af8526dbcfa2079fe30db07 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Wed, 30 Oct 2019 21:10:38 +0000
Subject: [PATCH] MSP430: Read MCU data from external file specified with
 environment variable or installed into toolchain subdirectory

gcc/ChangeLog:

2019-10-31  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* config/msp430/driver-msp430.c (msp430_get_linker_devices_include_path):
	New spec function.
	* config/msp430/msp430-devices.c (msp430_dirname): New function.
	(extract_devices_dir_from_exec_prefix): New function.
	(extract_devices_dir_from_collect_gcc): New function.
	(msp430_check_env_var_for_devices): New function.
	(msp430_check_path_for_devices): Use xstrdup instead of ASTRDUP.
	(parse_devices_csv): Call msp430_check_env_var_for_devices if
	devices.csv was not found using other methods.
	* config/msp430/msp430-devices.h (msp430_check_env_var_for_devices):
	New prototype.
	(msp430_dirname): Likewise.
	* config/msp430/msp430.c (msp430_register_pre_includes): New function.
	* config/msp430/msp430.h (EXTRA_SPEC_FUNCTIONS): Add
	msp430_get_linker_devices_include_path.
	(TARGET_EXTRA_PRE_INCLUDES): Define.
	* doc/invoke.texi: Document new ways of searching for support files.

gcc/testsuite/ChangeLog:

2019-10-31  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* gcc.target/msp430/devices/csv-using-env-var.c: New test.
	* gcc.target/msp430/devices/csv-using-installed.c: New test.
	* gcc.target/msp430/devices/csv-using-option.c: New test.
	* gcc.target/msp430/devices/devices-main.c: New test source file.
	* gcc.target/msp430/devices/msp430-devices.h: New test.
	* gcc.target/msp430/msp430.exp (msp430_device_permutations_runtest):
	Add special cases for csv-using* tests.
	Define TESTING_HARD_DATA when running tests that use hard-coded device
	data.
	(get_installed_device_data_path): New.
	(msp430_hide_installed_devices_data): New.
	(msp430_restore_installed_devices_data): New.
	(msp430_test_installed_device_data): New.
	(msp430_install_device_data): New.
---
 gcc/config/msp430/driver-msp430.c             |  13 ++
 gcc/config/msp430/msp430-devices.c            | 133 +++++++++++++++++-
 gcc/config/msp430/msp430-devices.h            |   2 +
 gcc/config/msp430/msp430.c                    |  23 +++
 gcc/config/msp430/msp430.h                    |  12 +-
 gcc/doc/invoke.texi                           |  29 +++-
 .../msp430/devices/csv-using-env-var.c        |  10 ++
 .../msp430/devices/csv-using-installed.c      |   9 ++
 .../msp430/devices/csv-using-option.c         |   9 ++
 .../gcc.target/msp430/devices/devices-main.c  |   4 +
 .../msp430/devices/msp430-devices.h           |   3 +
 gcc/testsuite/gcc.target/msp430/msp430.exp    |  94 ++++++++++++-
 12 files changed, 332 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv-using-env-var.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv-using-installed.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv-using-option.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430-devices.h

diff --git a/gcc/config/msp430/driver-msp430.c b/gcc/config/msp430/driver-msp430.c
index c37b169ff8b..fbe7c44ea37 100644
--- a/gcc/config/msp430/driver-msp430.c
+++ b/gcc/config/msp430/driver-msp430.c
@@ -150,6 +150,19 @@  msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED,
   return "-lmul_none";
 }
 
+/* Spec function.  Used to place the path to the MSP430-GCC support files
+   on the command line, prefixed with "-L", so the linker finds the linker
+   scripts in that directory.  */
+const char *
+msp430_get_linker_devices_include_path (int argc ATTRIBUTE_UNUSED,
+					const char **argv ATTRIBUTE_UNUSED)
+{
+  char *devices_csv_path;
+  if (msp430_check_env_var_for_devices (&devices_csv_path))
+    return NULL;
+  return concat ("-L", msp430_dirname (devices_csv_path), NULL);
+}
+
 /* Spec function.  Propagate -m{code,data}-region= to the linker, unless the
    lower region has been specified without -muse-lower-region-prefix also being
    used.  */
diff --git a/gcc/config/msp430/msp430-devices.c b/gcc/config/msp430/msp430-devices.c
index 537d438ea3b..600a111b517 100644
--- a/gcc/config/msp430/msp430-devices.c
+++ b/gcc/config/msp430/msp430-devices.c
@@ -37,7 +37,7 @@  extern struct t_msp430_mcu_data hard_msp430_mcu_data[605];
 
 /* Set to the full path to devices.csv if it is found by searching the -I and
    -L paths.  */
-char * derived_devices_csv_loc = NULL;
+char *derived_devices_csv_loc = NULL;
 
 /* This is to canonicalize the directory separators in the path.
    On Windows we could have a mix of '/' and '\' in the path.  */
@@ -52,6 +52,128 @@  canonicalize_path_dirsep (char **path)
       t_path[i] = DIR_SEPARATOR;
 }
 
+/* This function returns the enclosing directory of PATH.
+   It is inconsequential whether PATH ends in a dirsep or not.
+   It modifies the string pointed to by PATH.  */
+char *
+msp430_dirname (char *path)
+{
+  int last_elem = strlen (path) - 1;
+  int i = last_elem - (IS_DIR_SEPARATOR (path[last_elem]) ? 1 : 0);
+  for (; i >= 0; i--)
+    {
+      if (IS_DIR_SEPARATOR (path[i]))
+	{
+	  path[i] = '\0';
+	  return path;
+	}
+    }
+  return path;
+}
+
+/* devices.csv path from the toolchain root.  */
+static const char rest_of_devices_path[] = "/msp430-elf/include/devices/";
+
+/* "The default value of GCC_EXEC_PREFIX is prefix/lib/gcc". Strip lib/gcc
+   from GCC_EXEC_PREFIX to get the path to the installed toolchain.  */
+static void
+extract_devices_dir_from_exec_prefix (char **devices_loc)
+{
+  const char *temp;
+  char *gcc_exec_prefix = *devices_loc;
+  int len = strlen (gcc_exec_prefix);
+
+  /* Copied from gcc.c.  */
+  if (len > (int) sizeof ("/lib/gcc/") - 1
+      && (IS_DIR_SEPARATOR (gcc_exec_prefix[len-1])))
+    {
+      temp = gcc_exec_prefix + len - sizeof ("/lib/gcc/") + 1;
+      if (IS_DIR_SEPARATOR (*temp)
+	  && filename_ncmp (temp + 1, "lib", 3) == 0
+	  && IS_DIR_SEPARATOR (temp[4])
+	  && filename_ncmp (temp + 5, "gcc", 3) == 0)
+	{
+	  len -= sizeof ("/lib/gcc/") - 1;
+	  /* Keep the '/' from the beginning of /lib/gcc.  */
+	  gcc_exec_prefix[len + 1] = (char) 0;
+	  *devices_loc = concat (gcc_exec_prefix, rest_of_devices_path, NULL);
+	  return;
+	}
+    }
+}
+
+/* Given the path to the GCC executable, return the path to the installed
+   device data in "$TOOLCHAIN_ROOT/msp430-elf/include/devices".
+   Assumes the GCC executable is in "$TOOLCHAIN_ROOT/<somedir>/".  */
+static void
+extract_devices_dir_from_collect_gcc (char **devices_loc)
+{
+  char *t_devices_loc = *devices_loc;
+  /* Go up a directory to the toolchain root.  */
+  t_devices_loc = msp430_dirname (msp430_dirname (t_devices_loc));
+  t_devices_loc = concat (t_devices_loc, rest_of_devices_path, NULL);
+  *devices_loc = t_devices_loc;
+}
+
+/* The path to the MSP430-GCC support files can be specified with the
+   environment variable "MSP430_GCC_INCLUDE_DIR", or installed into the
+   toolchain in the msp430-elf/include/devices subdirectory.
+   We use the GCC_EXEC_PREFIX or COLLECT_GCC environment variables as a starting
+   point for the location of the toolchain, and work out the path to the
+   installed device data from there.
+   Return 0 and set LOCAL_DEVICES_CSV_LOC if we find devices.csv.  Return 1
+   if devices.csv wasn't found.  */
+int
+msp430_check_env_var_for_devices (char **local_devices_csv_loc)
+{
+  const int num_vars = 3;
+  const char dirsep[2] = { DIR_SEPARATOR, 0 };
+  /* Both GCC_EXEC_PREFIX and COLLECT_GCC should always be set to the format we
+     expect, as they are required for correct operation of the toolchain.
+     So if they are wrong the user will probably have bigger problems.
+     GCC_EXEC_PREFIX is only defined in the driver, whilst COLLECT_GCC is only
+     defined in the compiler proper, so we need both.  */
+  const char *env_vars[num_vars] = {
+      "MSP430_GCC_INCLUDE_DIR", "GCC_EXEC_PREFIX", "COLLECT_GCC" };
+  enum msp430_include_vars {
+      MSP430_GCC_INCLUDE_DIR,
+      GCC_EXEC_PREFIX,
+      COLLECT_GCC
+  };
+  FILE *devices_csv_file = NULL;
+  int i;
+
+  for (i = MSP430_GCC_INCLUDE_DIR; i <= COLLECT_GCC; i++)
+    {
+      char *t_devices_loc;
+      char *val = getenv (env_vars[i]);
+      if (val == NULL)
+	continue;
+      t_devices_loc = xstrdup (val);
+
+      if (i == MSP430_GCC_INCLUDE_DIR)
+	{
+	  if (!IS_DIR_SEPARATOR (t_devices_loc[strlen (t_devices_loc) - 1]))
+	    t_devices_loc = concat (t_devices_loc, dirsep, NULL);
+	}
+      else if (i == GCC_EXEC_PREFIX)
+	extract_devices_dir_from_exec_prefix (&t_devices_loc);
+      else if (i == COLLECT_GCC)
+	extract_devices_dir_from_collect_gcc (&t_devices_loc);
+
+      t_devices_loc = concat (t_devices_loc, "devices.csv", NULL);
+      devices_csv_file = fopen (t_devices_loc,  "r");
+      if (devices_csv_file != NULL)
+	{
+	  fclose (devices_csv_file);
+	  *local_devices_csv_loc = t_devices_loc;
+	  canonicalize_path_dirsep (local_devices_csv_loc);
+	  return 0;
+	}
+    }
+  return 1;
+}
+
 /* Spec function which searches the paths passed to the -I and -L options for
    the "devices.csv" file.  If it is found then the -mdevices-csv-loc option is
    placed on the command line so the compiler knows the location of the
@@ -69,7 +191,7 @@  msp430_check_path_for_devices (int argc, const char **argv)
     return NULL;
   for (i = 0; i < argc; i++)
     {
-      char *inc_path = ASTRDUP (argv[i]);
+      char *inc_path = xstrdup (argv[i]);
       canonicalize_path_dirsep (&inc_path);
       if (!IS_DIR_SEPARATOR (inc_path[strlen (inc_path) - 1]))
 	inc_path = concat (inc_path, dirsep, NULL);
@@ -274,8 +396,11 @@  parse_devices_csv (const char * mcu_name)
   /* Otherwise check if the path to devices.csv was found another way.  */
   else if (derived_devices_csv_loc != NULL)
     return parse_devices_csv_1 (derived_devices_csv_loc, mcu_name);
-  /* devices.csv was not found.  */
-  return 2;
+  /* Otherwise we need to use environment variables to try and find it.  */
+  if (msp430_check_env_var_for_devices (&derived_devices_csv_loc))
+    /* devices.csv was not found.  */
+    return 2;
+  return parse_devices_csv_1 (derived_devices_csv_loc, mcu_name);
 }
 
 /* Main entry point to load the MCU data for the given -mmcu into
diff --git a/gcc/config/msp430/msp430-devices.h b/gcc/config/msp430/msp430-devices.h
index 08ae1adf4ed..9e8029a32ca 100644
--- a/gcc/config/msp430/msp430-devices.h
+++ b/gcc/config/msp430/msp430-devices.h
@@ -29,3 +29,5 @@  struct t_msp430_mcu_data
 extern struct t_msp430_mcu_data extracted_mcu_data;
 
 void msp430_extract_mcu_data (const char * mcu_name);
+int msp430_check_env_var_for_devices (char **local_devices_csv_loc);
+char *msp430_dirname (char *path);
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index fe1fcc0db43..6c5d0ca3004 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -47,6 +47,8 @@ 
 #include "builtins.h"
 #include "intl.h"
 #include "msp430-devices.h"
+#include "incpath.h"
+#include "prefix.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -3558,6 +3560,27 @@  msp430_incoming_return_addr_rtx (void)
 {
   return gen_rtx_MEM (Pmode, stack_pointer_rtx);
 }
+
+/* If the path to the MSP430-GCC support files has been found by examining
+   an environment variable (see msp430_check_env_var_for_devices in
+   msp430-devices.c), or -mdevices-csv-loc=, register this path as an include
+   directory so the user can #include msp430.h without needing to specify the
+   path to the support files with -I.  */
+void
+msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
+			      const char *iprefix ATTRIBUTE_UNUSED,
+			      int stdinc ATTRIBUTE_UNUSED)
+{
+  char *include_dir;
+  if (msp430_devices_csv_loc)
+    include_dir = xstrdup (msp430_devices_csv_loc);
+  else if (msp430_check_env_var_for_devices (&include_dir))
+    return;
+  include_dir = msp430_dirname (include_dir);
+
+  include_dir = update_path (include_dir, "");
+  add_path (include_dir, INC_SYSTEM, false, false);
+}
 
 /* Instruction generation stuff.  */
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index 73afe2e2d16..2e70e0d6ab6 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -75,6 +75,7 @@  extern bool msp430x;
     "msp430_propagate_region_opt(%* %{muse-lower-region-prefix})} " \
   "%{mdata-region=*:--data-region=%:" \
     "msp430_propagate_region_opt(%* %{muse-lower-region-prefix})} " \
+  "%:msp430_get_linker_devices_include_path()"
 
 #define DRIVER_SELF_SPECS \
   " %{!mlarge:%{mcode-region=*:%{mdata-region=*:%e-mcode-region and "	\
@@ -94,6 +95,7 @@  extern const char * msp430_select_cpu (int, const char **);
 extern const char * msp430_set_driver_var (int, const char **);
 extern const char * msp430_check_path_for_devices (int, const char **);
 extern const char *msp430_propagate_region_opt (int, const char **);
+extern const char *msp430_get_linker_devices_include_path (int, const char **);
 
 /* There must be a trailing comma after the last item, see gcc.c
    "static_spec_functions".  */
@@ -102,7 +104,9 @@  extern const char *msp430_propagate_region_opt (int, const char **);
   { "msp430_select_cpu", msp430_select_cpu },		\
   { "msp430_set_driver_var", msp430_set_driver_var },		\
   { "msp430_check_path_for_devices", msp430_check_path_for_devices }, \
-  { "msp430_propagate_region_opt", msp430_propagate_region_opt },
+  { "msp430_propagate_region_opt", msp430_propagate_region_opt }, \
+  { "msp430_get_linker_devices_include_path", \
+    msp430_get_linker_devices_include_path },
 
 /* Specify the libraries to include on the linker command line.
 
@@ -496,6 +500,12 @@  typedef struct
 
 #define TARGET_HAS_NO_HW_DIVIDE (! TARGET_HWMULT)
 
+void msp430_register_pre_includes (const char *sysroot ATTRIBUTE_UNUSED,
+				   const char *iprefix ATTRIBUTE_UNUSED,
+				   int stdinc ATTRIBUTE_UNUSED);
+#undef TARGET_EXTRA_PRE_INCLUDES
+#define TARGET_EXTRA_PRE_INCLUDES msp430_register_pre_includes
+
 #undef  USE_SELECT_SECTION_FOR_FUNCTIONS
 #define USE_SELECT_SECTION_FOR_FUNCTIONS 1
 
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1407d019d14..1681cd78665 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -23200,8 +23200,33 @@  The ISA and hardware multiply supported for the different MCUs is hard-coded
 into GCC.  However, an external @samp{devices.csv} file can be used to
 extend device support beyond those that have been hard-coded.
 
-GCC searches for the @samp{devices.csv} file on the paths specified
-with the @code{-I} and @code{-L} options.
+GCC searches for the @samp{devices.csv} file using the following methods in the
+given precedence order, where the first method takes precendence over the
+second which takes precedence over the third.
+
+@table @asis
+@item Include path specified with @code{-I} and @code{-L}
+@samp{devices.csv} will be searched for in each of the directories specified by
+include paths and linker library search paths.
+@item Path specified by the environment variable @samp{MSP430_GCC_INCLUDE_DIR}
+Define the value of the global environment variable
+@samp{MSP430_GCC_INCLUDE_DIR}
+to the full path to the directory containing devices.csv, and GCC will search
+this directory for devices.csv.  If devices.csv is found, this directory will
+also be registered as an include path, and linker library path.  Header files
+and linker scripts in this directory can therefore be used without manually
+specifying @code{-I} and @code{-L} on the command line.
+@item The @samp{msp430-elf/include/devices} directory
+Finally, GCC will examine @samp{msp430-elf/include/devices} from the
+toolchain root directory.  This directory does not exist in a default
+installation, but if the user has created it and copied @samp{devices.csv}
+there, then the MCU data will be read.  As above, this directory will
+also be registered as an include path, and linker library path.
+
+@end table
+If none of the above search methods find @samp{devices.csv}, then the
+hard-coded MCU data is used.
+
 
 @item -mwarn-mcu
 @itemx -mno-warn-mcu
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv-using-env-var.c b/gcc/testsuite/gcc.target/msp430/devices/csv-using-env-var.c
new file mode 100644
index 00000000000..f2c30ee1f56
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-using-env-var.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+/* This tests that the environment variable MSP430_GCC_INCLUDE_DIR can be used
+   to specify the path to the directory containing devices.csv.
+   The variable is set in msp430.exp.  */
+
+#include "devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv-using-installed.c b/gcc/testsuite/gcc.target/msp430/devices/csv-using-installed.c
new file mode 100644
index 00000000000..11cbbabe402
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-using-installed.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+/* This tests that devices.csv can be installed into the
+   "$TOOLCHAIN_ROOT/msp430-elf/include/devices/" and used to read device data.  */
+
+#include "devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv-using-option.c b/gcc/testsuite/gcc.target/msp430/devices/csv-using-option.c
new file mode 100644
index 00000000000..bb47d536e96
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-using-option.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+/* This tests that the -mdevices-csv-loc option can be used to specify the path
+   to devices.csv.  */
+
+#include "devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/devices-main.c b/gcc/testsuite/gcc.target/msp430/devices/devices-main.c
index 20448f4f03d..8af6ec124bd 100644
--- a/gcc/testsuite/gcc.target/msp430/devices/devices-main.c
+++ b/gcc/testsuite/gcc.target/msp430/devices/devices-main.c
@@ -1,3 +1,7 @@ 
+#ifndef TESTING_HARD_DATA
+#include <msp430-devices.h>
+#endif
+
 int
 main (void)
 {
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430-devices.h b/gcc/testsuite/gcc.target/msp430/devices/msp430-devices.h
new file mode 100644
index 00000000000..105bd2bdc5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430-devices.h
@@ -0,0 +1,3 @@ 
+#ifndef __MSP430__
+#error
+#endif
diff --git a/gcc/testsuite/gcc.target/msp430/msp430.exp b/gcc/testsuite/gcc.target/msp430/msp430.exp
index bcc56861abc..37586612b9a 100644
--- a/gcc/testsuite/gcc.target/msp430/msp430.exp
+++ b/gcc/testsuite/gcc.target/msp430/msp430.exp
@@ -101,6 +101,25 @@  proc msp430_device_permutations_runtest { tests } {
 	if { ![runtest_file_p $runtests $test_file] } {
 	    continue
 	}
+	# The device name passed to -mmcu in the csv-using-* tests do not exist
+	# in the hard-coded data, to ensure the test fails if the method can't
+	# find the device data.
+	if { [file tail $test_file] eq "csv-using-installed.c" } {
+	    msp430_test_installed_device_data $test_file $MSP430_DEFAULT_CFLAGS
+	    continue
+	} elseif { [file tail $test_file] eq "csv-using-option.c" } {
+	    dg-runtest $test_file \
+		"-mdevices-csv-loc=[file dirname $test_file]/devices.csv" \
+		"$MSP430_DEFAULT_CFLAGS"
+	    continue
+	} elseif { [file tail $test_file] eq "csv-using-env-var.c" } {
+	    setenv MSP430_GCC_INCLUDE_DIR [file dirname $test_file]
+	    verbose -log "MSP430_GCC_INCLUDE_DIR=[file dirname $test_file]"
+	    dg-runtest $test_file "" "$MSP430_DEFAULT_CFLAGS"
+	    setenv MSP430_GCC_INCLUDE_DIR ""
+	    verbose -log "MSP430_GCC_INCLUDE_DIR=\"\""
+	    continue
+	}
 	foreach { mcu_flags } [msp430_get_opts $test_file] {
 	    if { [string match "csv-*" [file tail $test_file]] } {
 		# Specify the path to devices.csv for devices/csv-* tests with -I.
@@ -115,12 +134,76 @@  proc msp430_device_permutations_runtest { tests } {
 	    } elseif { [string match "bad-devices*" [file tail $test_file]] } {
 		dg-runtest $test_file "$mcu_flags" "-mdevices-csv-loc=[file dirname $test_file]/[file tail $test_file]sv $MSP430_DEFAULT_CFLAGS"
 	    } else {
-		dg-runtest $test_file "$mcu_flags" "$MSP430_DEFAULT_CFLAGS"
+		dg-runtest $test_file "$mcu_flags -DTESTING_HARD_DATA" "$MSP430_DEFAULT_CFLAGS"
 	    }
-	}
+        }
+    }
+}
+
+
+# Return $TOOLCHAIN_ROOT/msp430-elf/include/devices/
+proc get_installed_device_data_path { } {
+    set compiler [lindex [regexp -all -inline {\S+} \
+	[board_info [target_info name] compiler]] 0]
+    # $compiler is actually a file, but normalize will still get us the desired
+    # result.
+    return [file normalize \
+	"$compiler/../../msp430-elf/include/devices/devices.csv"]
+}
+
+# If the devices.csv is installed in
+# $TOOLCHAIN_ROOT/msp430-elf/include/devices/, rename it so it doesn't
+# interfere with the hard-coded device data tests.
+proc msp430_hide_installed_devices_data { } {
+    set devices_path [get_installed_device_data_path]
+    if { [file exists $devices_path] } {
+	file rename $devices_path "$devices_path.bak"
     }
 }
 
+# Restore devices.csv if renamed by msp430_hide_installed_devices_data.
+proc msp430_restore_installed_devices_data { } {
+    set devices_path [get_installed_device_data_path]
+    if { [file exists "$devices_path.bak"] } {
+	file rename "$devices_path.bak" $devices_path 
+    }
+}
+
+proc msp430_test_installed_device_data { name default_cflags } {
+    global subdir
+    global env
+
+    # The initial value for GCC_EXEC_PREFIX set by target-libpath.exp is not
+    # correct for cross-compilers so fix it here. GCC fixes the value itself,
+    # but not after spec functions are executed, which can cause a warning
+    # about missing devices.csv to be emitted.
+    set compiler [lindex [regexp -all -inline {\S+} \
+	[board_info [target_info name] compiler]] 0]
+    set real_exec_prefix "[file normalize "$compiler/../../lib/gcc"]/"
+    setenv GCC_EXEC_PREFIX $real_exec_prefix
+
+    msp430_restore_installed_devices_data 
+    set devices_path [get_installed_device_data_path]
+    if { [file exists $devices_path] } {
+	dg-runtest $name "" "$default_cflags"
+    } else {
+	set shorter_name "$subdir/[file tail $name]"
+	verbose -log "$shorter_name not supported, $devices_path doesn't exist."
+	unsupported $shorter_name
+    }
+    msp430_hide_installed_devices_data 
+}
+
+proc msp430_install_device_data { testsuite_dir } {
+    set devices_path [get_installed_device_data_path]
+    if { [file exists $devices_path] } {
+        return
+    }
+    set installed_path [file dirname $devices_path]
+    file mkdir $installed_path
+    file copy $testsuite_dir/msp430-devices.h $testsuite_dir/devices.csv $installed_path
+}
+
 # Load support procs.
 load_lib gcc-dg.exp
 
@@ -138,11 +221,18 @@  if [info exists DEFAULT_CFLAGS] then {
 # Initialize `dg'.
 dg-init
 
+# Install then hide the devices data now, in case it is already installed. We
+# don't want it to interfere with tests until we need it to.
+msp430_install_device_data $srcdir/$subdir/devices
+msp430_hide_installed_devices_data
+
 # Main loop.
 dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
 	"" $MSP430_DEFAULT_CFLAGS
 
 msp430_device_permutations_runtest [lsort [glob -nocomplain $srcdir/$subdir/devices/*.\[cCS\]]]
 
+msp430_restore_installed_devices_data 
+
 # All done.
 dg-finish
-- 
2.17.1