diff mbox

[MSP430] Read mcu data from file instead of hardcoded data

Message ID 38b985e8-5698-956e-1d1a-daaf4a6c73da@somniumtech.com
State New
Headers show

Commit Message

Jozef Lawrynowicz Aug. 24, 2017, 3:18 p.m. UTC
GCC for the MSP430 target uses hard-coded data to choose the correct CPU
ISA and hardware multiply version for a given MCU. Since the data is
hard-coded, this data can only be updated by updating the compiler
itself.

The following patch changes the mechanism for processing device data to
instead look for a "devices.csv" file (provided by TI) on the include
and linker search paths specified by the user.

If the file is found then it is parsed, and the device passed to the
mmcu option is searched for. If the file or device aren't found, then
GCC uses the old hard-coded data instead.

If the patch is acceptable, I would appreciate if someone could commit
it for me, as I do not have write access.

Thanks,
Jozef
From 4ea2f4393929b70eb6e56e1d761b584f65d39248 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@somniumtech.com>
Date: Thu, 24 Aug 2017 13:44:11 +0000
Subject: [PATCH] MSP430: Read mcu data from file instead of hardcoded data

2017-08-XX	Jozef Lawrynowicz	<jozef.l@somniumtech.com>

	* gcc/config.gcc (msp430*-*-*): Add msp430-devices.o to extra objs.
	* gcc/config/msp430/driver-msp430.c (parse_devices_csv_wrapper): New
	function.
	(msp430_check_path_for_devices): New function.
	(msp430_select_cpu): New function.
	(msp430_select_hwmult_lib): Check for devices.csv data before using
	hardcoded data.
	* gcc/config/msp430/msp430-devices.c (find_devices_csv): New function.
	(parse_devices_csv): New function.
	* gcc/config/msp430/msp430-devices.h: Move definition of msp430_mcu_data struct
	here and rename	to t_msp430_mcu_data.
	* gcc/config/msp430/msp430.c (msp430_mcu_name): Look for devices.csv before
	using hardcoded data.
	(msp430_option_override): Likewise.
	(msp430_use_f5_series_hwmult): Likewise.
	(use_32bit_hwmult): Likewise.
	(msp430_no_hwmult): Likewise.
	* gcc/config/msp430/msp430.h: Don't pass -mmcu to the assembler.
	Call msp430_check_path_for_devices on -I and -L paths.
	Select multilib by transforming -mmcu to -mcpu.
	* gcc/config/msp430/msp430.opt: Add -mdevices-csv-loc= and
	-mdisable-device-warnings options.
	* gcc/config/msp430/t-msp430: Add rule to build msp430-devices.o.
	Remove MULTILIB_MATCHES for hardcoded mcu names.

gcc/testsuite
2017-08-XX	Jozef Lawrynowicz	<jozef.l@somniumtech.com>

	* gcc.target/msp430/devices/devices.csv: Add dummy devices.csv.
	* gcc.target/msp430/devices/main.c: Add dummy source file.
	* gcc.target/msp430/devices/msp430_00.c: New test.
	* gcc.target/msp430/devices/msp430_01.c: New test.
	* gcc.target/msp430/devices/msp430_02.c: New test.
	* gcc.target/msp430/devices/msp430_04.c: New test.
	* gcc.target/msp430/devices/msp430_08.c: New test.
	* gcc.target/msp430/devices/msp430_10.c: New test.
	* gcc.target/msp430/devices/msp430_11.c: New test.
	* gcc.target/msp430/devices/msp430_12.c: New test.
	* gcc.target/msp430/devices/msp430_14.c: New test.
	* gcc.target/msp430/devices/msp430_18.c: New test.
	* gcc.target/msp430/devices/msp430_20.c: New test.
	* gcc.target/msp430/devices/msp430_21.c: New test.
	* gcc.target/msp430/devices/msp430_22.c: New test.
	* gcc.target/msp430/devices/msp430_24.c: New test.
	* gcc.target/msp430/devices/msp430_28.c: New test.
	* gcc.target/msp430/devices/msp430_20_bad_cpu.c: New test.
	* gcc.target/msp430/devices/msp430_20_bad_hwmult.c: New test.
	* gcc.target/msp430/devices/msp430_28_bad_hwmult.c: New test.
	* gcc.target/msp430/interrupt_fn_placement.c: Skip if -mcpu=msp430.
	* gcc.target/msp430/msp430.exp: Search in devices directory for tests.
	* gcc.target/msp430/no_devices_warn_msp430.c: New test.
	* gcc.target/msp430/no_devices_warn_msp430x.c: New test.
---
 gcc/config.gcc                                     |   3 +-
 gcc/config/msp430/driver-msp430.c                  | 114 +++++++---
 gcc/config/msp430/msp430-devices.c                 | 181 ++++++++++++++++
 gcc/config/msp430/msp430-devices.h                 |  12 ++
 gcc/config/msp430/msp430.c                         | 146 ++++++++-----
 gcc/config/msp430/msp430.h                         |  14 +-
 gcc/config/msp430/msp430.opt                       |   9 +
 gcc/config/msp430/t-msp430                         | 235 +--------------------
 .../gcc.target/msp430/devices/devices.csv          |  16 ++
 gcc/testsuite/gcc.target/msp430/devices/main.c     |   5 +
 .../gcc.target/msp430/devices/msp430_00.c          |   5 +
 .../gcc.target/msp430/devices/msp430_01.c          |   5 +
 .../gcc.target/msp430/devices/msp430_02.c          |   5 +
 .../gcc.target/msp430/devices/msp430_04.c          |   5 +
 .../gcc.target/msp430/devices/msp430_08.c          |   5 +
 .../gcc.target/msp430/devices/msp430_10.c          |   5 +
 .../gcc.target/msp430/devices/msp430_11.c          |   5 +
 .../gcc.target/msp430/devices/msp430_12.c          |   5 +
 .../gcc.target/msp430/devices/msp430_14.c          |   5 +
 .../gcc.target/msp430/devices/msp430_18.c          |   5 +
 .../gcc.target/msp430/devices/msp430_20.c          |   5 +
 .../gcc.target/msp430/devices/msp430_20_bad_cpu.c  |   5 +
 .../msp430/devices/msp430_20_bad_hwmult.c          |   6 +
 .../gcc.target/msp430/devices/msp430_21.c          |   5 +
 .../gcc.target/msp430/devices/msp430_22.c          |   5 +
 .../gcc.target/msp430/devices/msp430_24.c          |   5 +
 .../gcc.target/msp430/devices/msp430_28.c          |   5 +
 .../msp430/devices/msp430_28_bad_hwmult.c          |   6 +
 .../gcc.target/msp430/interrupt_fn_placement.c     |   1 +
 gcc/testsuite/gcc.target/msp430/msp430.exp         |   3 +
 .../gcc.target/msp430/no_devices_warn_msp430.c     |   9 +
 .../gcc.target/msp430/no_devices_warn_msp430x.c    |   9 +
 32 files changed, 536 insertions(+), 313 deletions(-)
 create mode 100644 gcc/config/msp430/msp430-devices.c
 create mode 100644 gcc/config/msp430/msp430-devices.h
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/devices.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/main.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_00.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_01.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_02.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_04.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_08.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_10.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_11.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_12.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_14.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_18.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_20.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_cpu.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_hwmult.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_21.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_22.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_24.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_28.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430_28_bad_hwmult.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430x.c

Comments

Nick Clifton Aug. 29, 2017, 2 p.m. UTC | #1
Hi Jozef,

> If the file is found then it is parsed, and the device passed to the
> mmcu option is searched for. If the file or device aren't found, then
> GCC uses the old hard-coded data instead.

Whilst testing this patch with -mlarge enabled on the command line, I
encountered this (new) failure:

  cc1: error: -mlarge requires a 430X-compatible -mmcu=

  compiler exited with status 1
  FAIL: gcc.target/msp430/no_devices_warn_msp430.c (test for excess errors)

I think that you might need to extend the skip list in the test...

Cheers
  Nick
diff mbox

Patch

diff --git a/gcc/config.gcc b/gcc/config.gcc
index 446cab3..fec18f6 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -2303,7 +2303,8 @@  msp430*-*-*)
 	c_target_objs="msp430-c.o"
 	cxx_target_objs="msp430-c.o"
 	tmake_file="${tmake_file} msp430/t-msp430"
-	extra_gcc_objs="driver-msp430.o"
+	extra_objs="${extra_objs} msp430-devices.o"
+	extra_gcc_objs="driver-msp430.o msp430-devices.o"
 	;;
 nds32le-*-*)
 	target_cpu_default="0"
diff --git a/gcc/config/msp430/driver-msp430.c b/gcc/config/msp430/driver-msp430.c
index bad70bb..3c60dd9 100644
--- a/gcc/config/msp430/driver-msp430.c
+++ b/gcc/config/msp430/driver-msp430.c
@@ -23,20 +23,14 @@ 
 #include "coretypes.h"
 #include "diagnostic.h"
 #include "tm.h"
+#include "msp430-devices.h"
 
-/* This is a copy of the same data structure found in gas/config/tc-msp430.c
-   Also another (sort-of) copy can be found in gcc/config/msp430/msp430.c
-   Keep these three structures in sync.
+/* This structure is no longer being maintained.  Up-to-date data is read from
+   a devices.csv file on the user's system.
    The data in this structure has been extracted from version 1.194 of the
    devices.csv file released by TI in September 2016.  */
 
-struct msp430_mcu_data
-{
-  const char * name;
-  unsigned int revision; /* 0=> MSP430, 1=>MSP430X, 2=> MSP430Xv2.  */
-  unsigned int hwmpy;    /* 0=>none, 1=>16-bit, 2=>16-bit w/sign extend, 4=>32-bit, 8=> 32-bit (5xx).  */
-}
-msp430_mcu_data [] =
+struct t_msp430_mcu_data hard_msp430_mcu_data[] =
 {
   { "cc430f5123",2,8 },
   { "cc430f5125",2,8 },
@@ -643,7 +637,70 @@  msp430_mcu_data [] =
   { "rf430frl153h_rom",0,0 },
   { "rf430frl154h",0,0 },
   { "rf430frl154h_rom",0,0 }
-};  
+};
+
+char * driver_devices_csv_loc = NULL;
+
+static int
+parse_devices_csv_wrapper (const char * mcu_name)
+{
+  if (driver_devices_csv_loc)
+    parse_devices_csv (driver_devices_csv_loc, mcu_name);
+  else if (devices_csv_loc)
+    parse_devices_csv (devices_csv_loc, mcu_name);
+  else
+    return 1;
+  return 0;
+}
+
+const char *
+msp430_check_path_for_devices (int argc, const char **argv)
+{
+  /* devices_csv_loc is set by the option.  */
+  if (devices_csv_loc)
+    return NULL;
+  else if (driver_devices_csv_loc)
+    return concat ("-mdevices-csv-loc=", driver_devices_csv_loc, NULL);
+  const char dirsep[2] = { DIR_SEPARATOR, 0 };
+  FILE * devices_csv = NULL;
+  char * local_devices_csv_loc = NULL;
+  char * inc_path = NULL;
+  int i;
+  for (i = 0; i < argc; i++)
+    {
+      inc_path = (char *)argv[i];
+      if (!IS_DIR_SEPARATOR (inc_path[strlen (inc_path) - 1]))
+	inc_path = concat (inc_path, dirsep, NULL);
+      local_devices_csv_loc = concat (inc_path, "devices.csv", NULL);
+      devices_csv = fopen (local_devices_csv_loc, "r");
+      if (devices_csv != NULL)
+	{
+	  fclose (devices_csv);
+	  driver_devices_csv_loc = local_devices_csv_loc;
+	  return concat ("-mdevices-csv-loc=", local_devices_csv_loc, NULL);
+	}
+    }
+  return NULL;
+}
+
+const char *
+msp430_select_cpu (int argc, const char ** argv)
+{
+  if (argc == 0 || parse_devices_csv_wrapper (argv[0]))
+    return NULL;
+  switch (extracted_mcu_data.revision)
+    {
+    case 0:
+      return "-mcpu=msp430";
+    case 1:
+      return "-mcpu=msp430x";
+    case 2:
+      return "-mcpu=msp430xv2";
+    default:
+      error ("Unexpected error get msp430 cpu type\n");
+      return NULL;
+    }
+}
 
 /* Implement spec function `msp430_hwmult_libĀ“.  */
 
@@ -651,7 +708,6 @@  const char *
 msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED, const char ** argv ATTRIBUTE_UNUSED)
 {
   int i;
-
   switch (argc)
   {
   case 1:
@@ -686,22 +742,26 @@  msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED, const char ** argv ATTRIBUT
       }
     else if (strcasecmp (argv[0], "mcu") == 0)
       {
-	for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
-	  if (strcasecmp (argv[argc - 1], msp430_mcu_data[i].name) == 0)
-	    {
-	      switch (msp430_mcu_data[i].hwmpy)
-		{
-		case 0: return "-lmul_none";
-		case 2:
-		case 1: return "-lmul_16";
-		case 4: return "-lmul_32";
-		case 8: return "-lmul_f5";
-		default:
-		  error ("unrecognized hwpy field in msp430_mcu_data[%d]: %d",
-			 i, msp430_mcu_data[i].hwmpy);
+	parse_devices_csv_wrapper (argv[argc - 1]);
+	if (extracted_mcu_data.name == NULL)
+	  {
+	    for (i = ARRAY_SIZE (hard_msp430_mcu_data); i--;)
+	      if (strcasecmp (argv[argc - 1],
+			      hard_msp430_mcu_data[i].name) == 0)
+		extracted_mcu_data = hard_msp430_mcu_data[i];
+	  }
+	switch (extracted_mcu_data.hwmpy)
+	  {
+	  case 0: return "-lmul_none";
+	  case 2:
+	  case 1: return "-lmul_16";
+	  case 4: return "-lmul_32";
+	  case 8: return "-lmul_f5";
+	  default:
+		  error ("unrecognized hwpy field in hard_msp430_mcu_data[%d]: %d",
+			 i, hard_msp430_mcu_data[i].hwmpy);
 		  break;
-		}
-	    }
+	  }
       }
     else
       error ("unexpected first argument to msp430_select_hwmult_lib: %s", argv[0]);
diff --git a/gcc/config/msp430/msp430-devices.c b/gcc/config/msp430/msp430-devices.c
new file mode 100644
index 0000000..8f6c06e
--- /dev/null
+++ b/gcc/config/msp430/msp430-devices.c
@@ -0,0 +1,181 @@ 
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "df.h"
+#include "tm_p.h"
+#include "regs.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "output.h"
+#include "explow.h"
+#include "expr.h"
+#include "langhooks.h"
+#include "builtins.h"
+#include "intl.h"
+#include "msp430-devices.h"
+
+struct t_msp430_mcu_data extracted_mcu_data;
+
+/* If modifying this string check uses of it in this file where characters at
+   the start are skipped.  */
+static const char * advice_string
+  = "Please obtain the latest version of the msp430-gcc-support-files archive "
+  "from: \n\"http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/latest/index_FDS.html\"\n"
+  "and place the full path to the \"include\" directory containing devices.csv "
+  "on an include path specified with -I\n"
+  "Defaulting to the hard-coded device data...";
+
+void
+find_devices_csv (const char * mcu_name)
+{
+  /* This function only needs to be executed once, but it can be first called
+     from a number of different locations.  */
+  static int executed = 0;
+  if (executed == 1)
+    return;
+  executed = 1;
+  if (devices_csv_loc)
+    parse_devices_csv (devices_csv_loc, mcu_name);
+  else if (!TARGET_DISABLE_DEVICE_WARN)
+    warning (0, "devices.csv not found on any include paths.\n%s",
+	     advice_string);
+}
+
+void
+parse_devices_csv (const char * real_devices_csv_loc, const char * mcu_name)
+{
+  FILE * devices_csv = fopen (real_devices_csv_loc, "r");
+  /* devices_csv should never be NULL at this stage.  */
+  if (devices_csv == NULL)
+    {
+      if (!TARGET_DISABLE_DEVICE_WARN)
+	warning (0, "Unexpected error opening devices.csv\n");
+      return;
+    }
+  /* Some devices have a large number of errata, which means that MPY_TYPE
+     isn't found until the ~100th character in the line.  line_buf_siz set to
+     200 to be safe and hopefully future proof.  */
+  const int line_buf_siz = 200;
+  char line[line_buf_siz];
+  const char comma[2] = ",";
+  char * res;
+  int found_headings = 0;
+  int cpu_type = -1;
+  int mpy_type = -1;
+  int cpu_type_column = -1;
+  int mpy_type_column = -1;
+  const char * device_name_heading = "# Device Name";
+  const char * cpu_type_heading = "CPU_TYPE";
+  const char * mpy_type_heading = "MPY_TYPE";
+  while (1)
+    {
+      res = fgets (line, line_buf_siz, devices_csv);
+      if (res == NULL)
+	{
+	  if (!TARGET_DISABLE_DEVICE_WARN)
+	    warning (0, "Device %s not found in devices.csv.  Check the device "
+		     "name is correct or\n%s", mcu_name, advice_string+6);
+	  goto end;
+	}
+      else if (strncmp (line, device_name_heading,
+			strlen (device_name_heading)) == 0)
+	{
+	  found_headings = 1;
+	  int curr_column = 0;
+	  char * heading = strtok (line, comma);
+	  while (heading != NULL)
+	    {
+	      if (strncmp (heading, cpu_type_heading,
+			   strlen (cpu_type_heading)) == 0)
+		  cpu_type_column = curr_column;
+	      else if (strncmp (heading, mpy_type_heading,
+			   strlen (mpy_type_heading)) == 0)
+		  mpy_type_column = curr_column;
+	      heading = strtok (NULL, comma);
+	      curr_column++;
+	    }
+	  if (curr_column < cpu_type_column || curr_column < mpy_type_column)
+	    {
+	      if (!TARGET_DISABLE_DEVICE_WARN)
+		warning (0, "Couldn't read the required data from devices.csv "
+			 "into a buffer.  There may be too many CPU_Bugs in "
+			 "the row for %s.  Try removing these so CPU_TYPE and "
+			 "MPY_TYPE fit in the buffer.", target_mcu);
+	      goto end;
+	    }
+	  else if (cpu_type_column == -1 || mpy_type_column == -1)
+	    {
+	      if (!TARGET_DISABLE_DEVICE_WARN)
+		warning (0, "CPU_TYPE and/or MPY_TYPE headings not present "
+			 "in devices.csv, or format not as expected.\n%s",
+			 advice_string);
+	      goto end;
+	    }
+	}
+      else if (strncasecmp (line, mcu_name, strlen (mcu_name)) == 0)
+	{
+	  if (found_headings == 0)
+	    {
+	      if (!TARGET_DISABLE_DEVICE_WARN)
+		warning (0, "Column headings format of devices.csv not as "
+			 "expected.\n%s", advice_string);
+	      goto end;
+	    }
+	  extracted_mcu_data.name = mcu_name;
+	  char * val = strtok (line, comma);
+	  int final_col_num = (cpu_type_column < mpy_type_column)
+	    ? mpy_type_column : cpu_type_column;
+	  int curr_col;
+	  for (curr_col = 0; curr_col < final_col_num + 1; curr_col++)
+	    {
+	      if (curr_col == cpu_type_column)
+		{
+		  cpu_type = atoi (val);
+		  if (strlen (val) != 1 || (cpu_type == 0 && val[0] != '0')
+		      || cpu_type > 2 || cpu_type < 0)
+		    {
+		      if (!TARGET_DISABLE_DEVICE_WARN)
+			warning (0, "Invalid CPU_TYPE read from devices.csv.\n"
+				 "%s", advice_string);
+		      goto end;
+		    }
+		  extracted_mcu_data.revision = cpu_type;
+		}
+	      else if (curr_col == mpy_type_column)
+		{
+		  mpy_type = atoi (val);
+		  if ((mpy_type == 0 && val[0] != '0')
+		      || !(mpy_type == 0
+			   || mpy_type == 1
+			   || mpy_type == 2
+			   || mpy_type == 4
+			   || mpy_type == 8))
+		    {
+		      if (!TARGET_DISABLE_DEVICE_WARN)
+			warning (0, "Invalid MPY_TYPE read from devices.csv.\n"
+				 "%s", advice_string);
+		      goto end;
+		    }
+		  extracted_mcu_data.hwmpy = mpy_type;
+		}
+	      val = strtok (NULL, comma);
+	    }
+	  if (cpu_type == -1 || mpy_type == -1)
+	    if (!TARGET_DISABLE_DEVICE_WARN)
+	      warning (0, "Unknown error reading CPU_TYPE and/or MPY_TYPE from "
+		       "devices.csv.\n%s\n", advice_string);
+	  goto end;
+	}
+    }
+ end:
+  fclose (devices_csv);
+}
diff --git a/gcc/config/msp430/msp430-devices.h b/gcc/config/msp430/msp430-devices.h
new file mode 100644
index 0000000..9562a33
--- /dev/null
+++ b/gcc/config/msp430/msp430-devices.h
@@ -0,0 +1,12 @@ 
+struct t_msp430_mcu_data
+{
+  const char * name;
+  unsigned int revision; /* 0=> MSP430, 1=>MSP430X, 2=> MSP430Xv2.  */
+  unsigned int hwmpy;    /* 0=>none, 1=>16-bit, 2=>16-bit w/sign extend, 4=>32-bit, 8=> 32-bit (5xx).  */
+};
+
+extern struct t_msp430_mcu_data extracted_mcu_data;
+
+void find_devices_csv (const char * mcu_name);
+void parse_devices_csv (const char * real_devices_csv_loc,
+			const char * mcu_name);
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 2b4427d..a0bd7c4 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -43,6 +43,7 @@ 
 #include "langhooks.h"
 #include "builtins.h"
 #include "intl.h"
+#include "msp430-devices.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -93,19 +94,12 @@  msp430_init_machine_status (void)
 #undef  TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE		msp430_option_override
 
-/* This is a copy of the same data structure found in gas/config/tc-msp430.c
-   Also another (sort-of) copy can be found in gcc/config/msp430/t-msp430
-   Keep these three structures in sync.
+/* This structure is no longer being maintained.  Up-to-date data is read from
+   a devices.csv file on the user's system.
    The data in this structure has been extracted from version 1.194 of the
    devices.csv file released by TI in September 2016.  */
 
-struct msp430_mcu_data
-{
-  const char * name;
-  unsigned int revision; /* 0=> MSP430, 1=>MSP430X, 2=> MSP430Xv2.  */
-  unsigned int hwmpy;    /* 0=>none, 1=>16-bit, 2=>16-bit w/sign extend, 4=>32-bit, 8=> 32-bit (5xx).  */
-}
-msp430_mcu_data [] =
+struct t_msp430_mcu_data hard_msp430_mcu_data[] =
 {
   { "cc430f5123",2,8 },
   { "cc430f5125",2,8 },
@@ -722,6 +716,7 @@  msp430_mcu_name (void)
 {
   if (target_mcu)
     {
+      find_devices_csv (target_mcu);
       unsigned int i;
       static char mcu_name [64];
 
@@ -765,45 +760,60 @@  msp430_option_override (void)
 
   if (target_mcu)
     {
+      find_devices_csv (target_mcu);
       int i;
 
       /* FIXME: If the array were alpha sorted, we could use a binary search.  */
-      for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
-	if (strcasecmp (msp430_mcu_data[i].name, target_mcu) == 0)
+      if (extracted_mcu_data.name == NULL)
+	for (i = ARRAY_SIZE (hard_msp430_mcu_data); i--;)
 	  {
-	    bool xisa = msp430_mcu_data[i].revision >= 1; 
-
-	    if (msp430_warn_mcu)
+	    if (strcasecmp (hard_msp430_mcu_data[i].name, target_mcu) == 0)
 	      {
-		if (target_cpu&& msp430x != xisa)
-		  warning (0, "MCU '%s' supports %s ISA but -mcpu option is set to %s",
-			   target_mcu, xisa ? "430X" : "430", msp430x ? "430X" : "430");
-
-		if (msp430_mcu_data[i].hwmpy == 0
-		    && msp430_hwmult_type != MSP430_HWMULT_AUTO
-		    && msp430_hwmult_type != MSP430_HWMULT_NONE)
-		  warning (0, "MCU '%s' does not have hardware multiply support, but -mhwmult is set to %s",
-			   target_mcu,
-			   msp430_hwmult_type == MSP430_HWMULT_SMALL ? "16-bit"
-			   : msp430_hwmult_type == MSP430_HWMULT_LARGE ? "32-bit" : "f5series");
-		else if (msp430_hwmult_type == MSP430_HWMULT_SMALL
-		    && msp430_mcu_data[i].hwmpy != 1
-		    && msp430_mcu_data[i].hwmpy != 2 )
-		  warning (0, "MCU '%s' supports %s hardware multiply, but -mhwmult is set to 16-bit",
-			   target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
-		else if (msp430_hwmult_type == MSP430_HWMULT_LARGE && msp430_mcu_data[i].hwmpy != 4)
-		  warning (0, "MCU '%s' supports %s hardware multiply, but -mhwmult is set to 32-bit",
-			   target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
-		else if (msp430_hwmult_type == MSP430_HWMULT_F5SERIES && msp430_mcu_data[i].hwmpy != 8)
-		  warning (0, "MCU '%s' supports %s hardware multiply, but -mhwmult is set to f5series",
-			   target_mcu, hwmult_name (msp430_mcu_data[i].hwmpy));
+		extracted_mcu_data = hard_msp430_mcu_data[i];
+		break;
 	      }
-
-	    msp430x = xisa;
-	    break;
 	  }
+      if (extracted_mcu_data.name != NULL)
+	{
+	  bool xisa = extracted_mcu_data.revision >= 1;
 
-      if (i < 0)
+	  if (msp430_warn_mcu)
+	    {
+	      if (target_cpu && msp430x != xisa)
+		warning (0, "MCU '%s' supports %s ISA but -mcpu option is set "
+			 "to %s", target_mcu, xisa ? "430X" : "430",
+			 msp430x ? "430X" : "430");
+
+	      if (extracted_mcu_data.hwmpy == 0
+		  && msp430_hwmult_type != MSP430_HWMULT_AUTO
+		  && msp430_hwmult_type != MSP430_HWMULT_NONE)
+		warning (0, "MCU '%s' does not have hardware multiply support, "
+			 "but -mhwmult is set to %s",
+			 target_mcu,
+			 msp430_hwmult_type == MSP430_HWMULT_SMALL ? "16-bit"
+			 : msp430_hwmult_type == MSP430_HWMULT_LARGE ? "32-bit"
+			 : "f5series");
+	      else if (msp430_hwmult_type == MSP430_HWMULT_SMALL
+		       && extracted_mcu_data.hwmpy != 1
+		       && extracted_mcu_data.hwmpy != 2)
+		warning (0, "MCU '%s' supports %s hardware multiply, "
+			 "but -mhwmult is set to 16-bit",
+			 target_mcu, hwmult_name (extracted_mcu_data.hwmpy));
+	      else if (msp430_hwmult_type == MSP430_HWMULT_LARGE
+		       && extracted_mcu_data.hwmpy != 4)
+		warning (0, "MCU '%s' supports %s hardware multiply, "
+			 "but -mhwmult is set to 32-bit",
+			 target_mcu, hwmult_name (extracted_mcu_data.hwmpy));
+	      else if (msp430_hwmult_type == MSP430_HWMULT_F5SERIES
+		       && extracted_mcu_data.hwmpy != 8)
+		warning (0, "MCU '%s' supports %s hardware multiply, "
+			 "but -mhwmult is set to f5series",
+			 target_mcu, hwmult_name (extracted_mcu_data.hwmpy));
+	    }
+
+	  msp430x = xisa;
+	}
+      else
 	{
 	  if (msp430_hwmult_type == MSP430_HWMULT_AUTO)
 	    {
@@ -843,7 +853,8 @@  msp430_option_override (void)
     }
 
   /* The F5 series are all able to support the 430X ISA.  */
-  if (target_cpu == NULL && target_mcu == NULL && msp430_hwmult_type == MSP430_HWMULT_F5SERIES)
+  if (target_cpu == NULL && target_mcu == NULL
+      && msp430_hwmult_type == MSP430_HWMULT_F5SERIES)
     msp430x = true;
 
   if (TARGET_LARGE && !msp430x)
@@ -3271,12 +3282,21 @@  msp430_use_f5_series_hwmult (void)
   if (strncasecmp (target_mcu, "msp430f6", 8) == 0)
     return cached_result = true;
 
+  find_devices_csv (target_mcu);
   int i;
 
-  /* FIXME: This array is alpha sorted - we could use a binary search.  */
-  for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
-    if (strcasecmp (target_mcu, msp430_mcu_data[i].name) == 0)
-      return cached_result = msp430_mcu_data[i].hwmpy == 8;
+  if (extracted_mcu_data.name == NULL)
+    {
+      /* FIXME: This array is alpha sorted - we could use a binary search.  */
+      for (i = ARRAY_SIZE (hard_msp430_mcu_data); i--;)
+	if (strcasecmp (target_mcu, hard_msp430_mcu_data[i].name) == 0)
+	  {
+	    extracted_mcu_data = hard_msp430_mcu_data[i];
+	    return cached_result = extracted_mcu_data.hwmpy == 8;
+	  }
+    }
+  else
+    return cached_result = extracted_mcu_data.hwmpy == 8;
 
   return cached_result = false;
 }
@@ -3302,10 +3322,19 @@  use_32bit_hwmult (void)
 
   cached_match = target_mcu;
 
-  /* FIXME: This array is alpha sorted - we could use a binary search.  */
-  for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
-    if (strcasecmp (target_mcu, msp430_mcu_data[i].name) == 0)
-      return cached_result = msp430_mcu_data[i].hwmpy == 4;
+  find_devices_csv (target_mcu);
+  if (extracted_mcu_data.name == NULL)
+    {
+      /* FIXME: This array is alpha sorted - we could use a binary search.  */
+      for (i = ARRAY_SIZE (hard_msp430_mcu_data); i--;)
+	if (strcasecmp (target_mcu, hard_msp430_mcu_data[i].name) == 0)
+	  {
+	    extracted_mcu_data = hard_msp430_mcu_data[i];
+	    return cached_result = extracted_mcu_data.hwmpy == 4;
+	  }
+    }
+  else
+    return cached_result = extracted_mcu_data.hwmpy == 4;
 
   return cached_result = false;
 }
@@ -3334,10 +3363,19 @@  msp430_no_hwmult (void)
 
   cached_match = target_mcu;
 
-  /* FIXME: This array is alpha sorted - we could use a binary search.  */
-  for (i = ARRAY_SIZE (msp430_mcu_data); i--;)
-    if (strcasecmp (target_mcu, msp430_mcu_data[i].name) == 0)
-      return cached_result = msp430_mcu_data[i].hwmpy == 0;
+  find_devices_csv (target_mcu);
+  if (extracted_mcu_data.name == NULL)
+    {
+      /* FIXME: This array is alpha sorted - we could use a binary search.  */
+      for (i = ARRAY_SIZE (hard_msp430_mcu_data); i--;)
+	if (strcasecmp (target_mcu, hard_msp430_mcu_data[i].name) == 0)
+	  {
+	    extracted_mcu_data = hard_msp430_mcu_data[i];
+	    return cached_result = extracted_mcu_data.hwmpy == 0;
+	  }
+    }
+  else
+    return cached_result = extracted_mcu_data.hwmpy == 0;
 
   /* If we do not recognise the MCU name, we assume that it does not support
      any kind of hardware multiply - this is the safest assumption to make.  */
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index 2bff249..ec9ddf8 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -52,7 +52,7 @@  extern bool msp430x;
 #define ENDFILE_SPEC "%{!minrt:crtend.o%s} %{minrt:crtn-minrt.o%s}%{!minrt:crtn.o%s} -lgcc"
 
 #define ASM_SPEC "-mP " /* Enable polymorphic instructions.  */ \
-  "%{mcpu=*:-mcpu=%*}%{!mcpu=*:%{mmcu=*:-mmcu=%*}} " /* Pass the CPU type on to the assembler.  */ \
+  "%{mcpu=*:-mcpu=%*} " /* Pass the CPU type on to the assembler.  */ \
   "%{mrelax=-mQ} " /* Pass the relax option on to the assembler.  */ \
   "%{mlarge:-ml} " /* Tell the assembler if we are building for the LARGE pointer model.  */ \
   "%{!msim:-md} %{msim:%{mlarge:-md}} " /* Copy data from ROM to RAM if necessary.  */ \
@@ -66,8 +66,18 @@  extern bool msp430x;
 #define LINK_SPEC "%{mrelax:--relax} %{mlarge:%{!r:%{!g:--gc-sections}}}"
 
 extern const char * msp430_select_hwmult_lib (int, const char **);
+extern const char * msp430_select_cpu (int, const char **);
+extern const char * msp430_check_path_for_devices (int, const char **);
 # define EXTRA_SPEC_FUNCTIONS				\
-  { "msp430_hwmult_lib", msp430_select_hwmult_lib },
+  { "msp430_hwmult_lib", msp430_select_hwmult_lib }, \
+  { "msp430_select_cpu", msp430_select_cpu }, \
+  { "msp430_check_path_for_devices", msp430_check_path_for_devices },
+
+/* Look for devices.csv in the folders specified by include paths.  */
+#define DRIVER_SELF_SPECS	      \
+  " %{I*:%:msp430_check_path_for_devices(%{I*:%*})}"       \
+  " %{L*:%:msp430_check_path_for_devices(%{L*:%*})}"       \
+  " %{mmcu=*:%:msp430_select_cpu(%{mmcu=*:%*})}"
 
 /* Specify the libraries to include on the linker command line.
 
diff --git a/gcc/config/msp430/msp430.opt b/gcc/config/msp430/msp430.opt
index c027201..99195ae 100644
--- a/gcc/config/msp430/msp430.opt
+++ b/gcc/config/msp430/msp430.opt
@@ -92,3 +92,12 @@  Passes on a request to the assembler to enable fixes for various silicon errata.
 msilicon-errata-warn=
 Target Joined RejectNegative Report ToLower
 Passes on a request to the assembler to warn about various silicon errata.
+
+mdevices-csv-loc=
+Target Joined Var(devices_csv_loc) RejectNegative Report
+The path to devices.csv.  The GCC driver can normally locate devices.csv itself
+and pass this option to the compiler, so the user shouldn't need to pass this.
+
+mdisable-device-warnings
+Target RejectNegative Mask(DISABLE_DEVICE_WARN) Report
+Suppress warnings regarding devices.csv not being found on an include path.
diff --git a/gcc/config/msp430/t-msp430 b/gcc/config/msp430/t-msp430
index 8e6f193..27ce646 100644
--- a/gcc/config/msp430/t-msp430
+++ b/gcc/config/msp430/t-msp430
@@ -22,6 +22,10 @@  driver-msp430.o: $(srcdir)/config/msp430/driver-msp430.c \
   $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H)
 	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
 
+msp430-devices.o: $(srcdir)/config/msp430/msp430-devices.c \
+  $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H)
+	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
 # Enable multilibs:
 
 MULTILIB_OPTIONS    = mcpu=msp430 mlarge 
@@ -30,235 +34,8 @@  MULTILIB_DIRNAMES   = 430          large
 # Match -mcpu=430
 MULTILIB_MATCHES    = mcpu?msp430=mcpu?430
 
-# Match the known 430 ISA mcu names.
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c091
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c092
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c111
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1111
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c112
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1121
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1331
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c1351
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c311s
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c312
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c313
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c314
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c315
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c323
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c325
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c412
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c413
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e112
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e313
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e315
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e325
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f110
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1101
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1101a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1111
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1111a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f112
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1121
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1121a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1122
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1132
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f122
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1222
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f123
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1232
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f133
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f135
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f155
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f156
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f157
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2001
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2002
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2003
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2011
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2012
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2013
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2101
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2111
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2112
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2121
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2122
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2131
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2132
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2232
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2234
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2252
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2254
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2272
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2274
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f412
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f413
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4132
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f415
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4152
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f417
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4250
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4260
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4270
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f435
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4351
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f436
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4361
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f437
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4371
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f438
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f439
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f477
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f478
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f479
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe423
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4232
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe423a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4242
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe425
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4252
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe425a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe427
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe4272
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fe427a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg4250
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg4260
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg4270
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg437
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg438
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg439
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg477
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg478
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fg479
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw423
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw425
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw427
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw428
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430fw429
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2001
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2101
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2102
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2111
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2112
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2113
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2121
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2131
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2132
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2152
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2153
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2201
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2202
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2203
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2210
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2211
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2212
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2213
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2221
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2230
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2231
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2232
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2233
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2252
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2253
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2302
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2303
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2312
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2313
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2332
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2333
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2352
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2353
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2402
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2403
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2412
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2413
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2432
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2433
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2444
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2452
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2453
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2513
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2533
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2544
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2553
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2744
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2755
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2855
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430g2955
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430l092
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p112
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p313
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p315
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p315s
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p325
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430tch5e
-MULTILIB_MATCHES += mcpu?msp430=mmcu?rf430frl152h
-MULTILIB_MATCHES += mcpu?msp430=mmcu?rf430frl152h_rom
-MULTILIB_MATCHES += mcpu?msp430=mmcu?rf430frl153h
-MULTILIB_MATCHES += mcpu?msp430=mmcu?rf430frl153h_rom
-MULTILIB_MATCHES += mcpu?msp430=mmcu?rf430frl154h
-MULTILIB_MATCHES += mcpu?msp430=mmcu?rf430frl154h_rom
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c336
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430c337
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430e337
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f147
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1471
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f148
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1481
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f149
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1491
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1610
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1611
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f1612
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f167
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f168
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f169
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f423
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f423a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f425
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f425a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f427
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f427a
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f447
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f448
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4481
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f449
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4491
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430p337
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe221
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe222
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe223
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe231
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe232
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe233
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe251
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe252
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430afe253
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f233
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2330
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f235
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2350
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2370
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2410
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f247
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2471
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f248
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2481
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f249
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f2491
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2020
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2021
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2030
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2031
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2040
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2041
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430i2xxgeneric
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4783
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4784
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4793
-MULTILIB_MATCHES += mcpu?msp430=mmcu?msp430f4794
-
-# Add additional MCU matches like this:
-# MULTILIB_MATCHES += mcpu?msp430x=mmcu?xxxxxxxxxx
+# Multilibs for specific cpu options are chosen by making the GCC driver parse
+# the -mmcu option and add the correct -mcpu option for the chosen device
 
 MULTILIB_EXCEPTIONS = mcpu=msp430/mlarge
 
diff --git a/gcc/testsuite/gcc.target/msp430/devices/devices.csv b/gcc/testsuite/gcc.target/msp430/devices/devices.csv
new file mode 100644
index 0000000..d692d47
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/devices.csv
@@ -0,0 +1,16 @@ 
+# Device Name,CPU_TYPE,MPY_TYPE
+msp430_00,0,0
+msp430_01,0,1
+msp430_02,0,2
+msp430_04,0,4
+msp430_08,0,8
+msp430_10,1,0
+msp430_11,1,1
+msp430_12,1,2
+msp430_14,1,4
+msp430_18,1,8
+msp430_20,2,0
+msp430_21,2,1
+msp430_22,2,2
+msp430_24,2,4
+msp430_28,2,8
diff --git a/gcc/testsuite/gcc.target/msp430/devices/main.c b/gcc/testsuite/gcc.target/msp430/devices/main.c
new file mode 100644
index 0000000..b72eacd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/main.c
@@ -0,0 +1,5 @@ 
+int main (void)
+{
+  while (1);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_00.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_00.c
new file mode 100644
index 0000000..00ce0a6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_00.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mlarge" "-mcpu=msp430x" "-mcpu=msp430xv2" } { "" } } */
+/* { dg-options "-I. -mhwmult=none -mcpu=msp430 -mmcu=msp430_00" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_01.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_01.c
new file mode 100644
index 0000000..d4b4e87
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_01.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mlarge" "-mcpu=msp430x" "-mcpu=msp430xv2" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430 -mmcu=msp430_01" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_02.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_02.c
new file mode 100644
index 0000000..0a209b0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_02.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mlarge" "-mcpu=msp430x" "-mcpu=msp430xv2" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430 -mmcu=msp430_02" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_04.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_04.c
new file mode 100644
index 0000000..124c143
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_04.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mlarge" "-mcpu=msp430x" "-mcpu=msp430xv2" } { "" } } */
+/* { dg-options "-I. -mhwmult=32bit -mcpu=msp430 -mmcu=msp430_04" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_08.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_08.c
new file mode 100644
index 0000000..cd89587
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_08.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mlarge" "-mcpu=msp430x" "-mcpu=msp430xv2" } { "" } } */
+/* { dg-options "-I. -mhwmult=f5series -mcpu=msp430 -mmcu=msp430_08" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_10.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_10.c
new file mode 100644
index 0000000..70d47a5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_10.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=none -mcpu=msp430x -mmcu=msp430_10" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_11.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_11.c
new file mode 100644
index 0000000..a888fd6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_11.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430x -mmcu=msp430_11" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_12.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_12.c
new file mode 100644
index 0000000..fc0c0ab
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_12.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430x -mmcu=msp430_12" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_14.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_14.c
new file mode 100644
index 0000000..100a9fc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_14.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=32bit -mcpu=msp430x -mmcu=msp430_14" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_18.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_18.c
new file mode 100644
index 0000000..9a31b39
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_18.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=f5series -mcpu=msp430x -mmcu=msp430_18" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_20.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_20.c
new file mode 100644
index 0000000..3625ece
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_20.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=none -mcpu=msp430xv2 -mmcu=msp430_20" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_cpu.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_cpu.c
new file mode 100644
index 0000000..ad0be9a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_cpu.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-options "-I. -mhwmult=none -mcpu=msp430 -mmcu=msp430_20" } */
+/* { dg-excess-errors "" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_hwmult.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_hwmult.c
new file mode 100644
index 0000000..bd2bd46
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_20_bad_hwmult.c
@@ -0,0 +1,6 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { "*-*-*" } { "-mhwmult=*" } { "" } } */
+/* { dg-options "-I. -mhwmult=f5series -mcpu=msp430xv2 -mmcu=msp430_20" } */
+/* { dg-excess-errors "does not have hardware multiply support" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_21.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_21.c
new file mode 100644
index 0000000..b414b35
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_21.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430xv2 -mmcu=msp430_21" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_22.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_22.c
new file mode 100644
index 0000000..618d3ac
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_22.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430xv2 -mmcu=msp430_22" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_24.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_24.c
new file mode 100644
index 0000000..39c71c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_24.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=32bit -mcpu=msp430xv2 -mmcu=msp430_24" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_28.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_28.c
new file mode 100644
index 0000000..5bf77ca
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_28.c
@@ -0,0 +1,5 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mhwmult=*" "-mcpu=msp430" } { "" } } */
+/* { dg-options "-I. -mhwmult=f5series -mcpu=msp430xv2 -mmcu=msp430_28" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430_28_bad_hwmult.c b/gcc/testsuite/gcc.target/msp430/devices/msp430_28_bad_hwmult.c
new file mode 100644
index 0000000..08a4e26
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430_28_bad_hwmult.c
@@ -0,0 +1,6 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { "*-*-*" } { "-mhwmult=*" } { "" } } */
+/* { dg-options "-I. -mhwmult=16bit -mcpu=msp430xv2 -mmcu=msp430_28" } */
+/* { dg-excess-errors "supports 32-bit (5xx) hardware multiply" } */
+
+#include "main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/interrupt_fn_placement.c b/gcc/testsuite/gcc.target/msp430/interrupt_fn_placement.c
index c88bfc3..b8fd5e6 100644
--- a/gcc/testsuite/gcc.target/msp430/interrupt_fn_placement.c
+++ b/gcc/testsuite/gcc.target/msp430/interrupt_fn_placement.c
@@ -1,4 +1,5 @@ 
 /* { dg-do compile } */
+/* { dg-skip-if "" { "*-*-*" } { "-mcpu=msp430" } { "" } } */
 /* { dg-options "-mlarge -mcode-region=either -ffunction-sections" } */
 /* { dg-final { scan-assembler-not "\\.either\\.lowtext" } } */
 
diff --git a/gcc/testsuite/gcc.target/msp430/msp430.exp b/gcc/testsuite/gcc.target/msp430/msp430.exp
index e54d531..fc485f9 100644
--- a/gcc/testsuite/gcc.target/msp430/msp430.exp
+++ b/gcc/testsuite/gcc.target/msp430/msp430.exp
@@ -37,5 +37,8 @@  dg-init
 dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
 	"" $DEFAULT_CFLAGS
 
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/devices/*.\[cCS\]]] \
+	"-I$srcdir/$subdir/devices" $DEFAULT_CFLAGS
+
 # All done.
 dg-finish
diff --git a/gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430.c b/gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430.c
new file mode 100644
index 0000000..e07f5d5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430.c
@@ -0,0 +1,9 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430x" "-mcpu=msp430xv2" "-mhwmult=*" } { "-mhwmult=none" "-mhwmult=32bit" } } */
+/* { dg-options "-mmcu=msp430f4783 -mdisable-device-warnings" } */
+
+int main (void)
+{
+  while (1);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430x.c b/gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430x.c
new file mode 100644
index 0000000..3ede38c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/no_devices_warn_msp430x.c
@@ -0,0 +1,9 @@ 
+/* { dg-do link } */
+/* { dg-skip-if "" { *-*-* } { "-mcpu=msp430" "-mhwmult=*" } { "-mhwmult=none" "-mhwmult=f5series" } } */
+/* { dg-options "-mmcu=msp430fr5969 -mdisable-device-warnings" } */
+
+int main (void)
+{
+  while (1);
+  return 0;
+}