From e85964a398a35a69513da07e4fcdaeed215ae79a Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Mon, 29 Oct 2018 13:51:24 +0000
Subject: [PATCH 2/3] MSP430 - Devices 2 - Add functionality to read device
 data from CSV file

2018-10-29  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

gcc/config/msp430/

	* msp430-devices.c (msp430_check_path_for_devices): New.
	(devices_csv_warning): New.
	(parse_devices_csv_1): New.
	(parse_devices_csv): New.
	(msp430_extract_mcu_data): Add argument to choose if devices.csv
	warnings should be surpressed.
	* msp430.c (msp430_mcu_name): Update invocation of msp430_extract_mcu_data.
	(msp430_option_override): Likewise.
	(msp430_use_f5_series_hwmult): Likewise.
	(use_32bit_hwmult): Likewise.
	(msp430_no_hwmult): Likewise.
	* msp430-devices.h (msp430_extract_mcu_data): Add argument to choose
	if devices.csv warnings should be surpressed.
	* msp430.h (EXTRA_SPEC_FUNCTIONS): Add msp430_check_path_for_devices.
	(DRIVER_SELF_SPECS): Likewise.
	* msp430.opt: Add -mdevices-csv-loc= and -mdisable-device-warnings
	options.
	* driver-msp430.c (msp430_select_cpu): Update invocation of
	msp430_extract_mcu_data.
	(msp430_select_hwmult_lib): Likewise.

gcc/doc/

	* invoke.texi: Document that MCU data for the -mmcu option value can be
	loaded from an external file.
	Document -mdisable-device-warnings.

gcc/testsuite/gcc.target/msp430/

	* devices/csv_msp430_{0,1,2}{0,1,2,4,8}.c: New.
	* devices/csv_msp430f5529.c: New.
	* devices/csv_msp430fr5969.c: New.
	* devices/devices.csv: New.
	* devices/msp430.h: New.
	* msp430.exp (msp430_get_multidir): Extend so MCU data is loaded from
	devices.csv for csv_* tests.
	(dg-msp-options): Update invocation to msp430_get_multidir.
	(msp430_get_supported_opts): Likewise.
	(msp430_device_permutations_runtest): Extend to handle csv_* tests.
---
 gcc/config/msp430/driver-msp430.c                  |   5 +-
 gcc/config/msp430/msp430-devices.c                 | 203 ++++++++++++++++++++-
 gcc/config/msp430/msp430-devices.h                 |   2 +-
 gcc/config/msp430/msp430.c                         |  10 +-
 gcc/config/msp430/msp430.h                         |   7 +-
 gcc/config/msp430/msp430.opt                       |   9 +
 gcc/doc/invoke.texi                                |  10 +-
 .../gcc.target/msp430/devices/csv_msp430_00.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_01.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_02.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_04.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_08.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_10.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_11.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_12.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_14.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_18.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_20.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_21.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_22.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_24.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430_28.c      |   6 +
 .../gcc.target/msp430/devices/csv_msp430f5529.c    |   8 +
 .../gcc.target/msp430/devices/csv_msp430fr5969.c   |  10 +
 .../gcc.target/msp430/devices/devices.csv          |  17 ++
 gcc/testsuite/gcc.target/msp430/devices/msp430.h   |   3 +
 gcc/testsuite/gcc.target/msp430/msp430.exp         |  35 +++-
 27 files changed, 386 insertions(+), 23 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_00.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_01.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_02.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_04.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_08.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_10.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_11.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_12.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_14.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_18.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_20.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_21.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_22.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_24.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430_28.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430f5529.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv_msp430fr5969.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/devices.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/msp430.h

diff --git a/gcc/config/msp430/driver-msp430.c b/gcc/config/msp430/driver-msp430.c
index e83c20f..4242274 100644
--- a/gcc/config/msp430/driver-msp430.c
+++ b/gcc/config/msp430/driver-msp430.c
@@ -27,13 +27,12 @@
 #include "tm.h"
 #include "msp430-devices.h"
 
-
 const char *
 msp430_select_cpu (int argc, const char ** argv)
 {
   if (argc == 0)
     return NULL;
-  msp430_extract_mcu_data (argv[0]);
+  msp430_extract_mcu_data (argv[0], true);
   /* Do not add an mcpu option onto the command line if the mcu isn't found,
      in case the user manually specified one.  */
   if (extracted_mcu_data.name != NULL)
@@ -91,7 +90,7 @@ msp430_select_hwmult_lib (int argc ATTRIBUTE_UNUSED, const char ** argv ATTRIBUT
       }
     else if (strcasecmp (argv[0], "mcu") == 0)
       {
-	msp430_extract_mcu_data (argv[argc - 1]);
+	msp430_extract_mcu_data (argv[argc - 1], true);
 	if (extracted_mcu_data.name != NULL)
 	  {
 	    switch (extracted_mcu_data.hwmpy)
diff --git a/gcc/config/msp430/msp430-devices.c b/gcc/config/msp430/msp430-devices.c
index f20b379..2f44861 100644
--- a/gcc/config/msp430/msp430-devices.c
+++ b/gcc/config/msp430/msp430-devices.c
@@ -12,19 +12,218 @@
 #include "msp430-devices.h"
 
 struct t_msp430_mcu_data extracted_mcu_data;
+
 /* Initialized at the bottom for improved readability.  */
 extern struct t_msp430_mcu_data hard_msp430_mcu_data[605];
 
+static const char * advice_string
+= "Checking the hard-coded device data...";
+
+char * derived_devices_csv_loc = NULL;
+/* Set to true by msp430_extract_mcu_data if called from the driver.  */
+bool surpress_devices_warn = false;
+
+const char *
+msp430_check_path_for_devices (int argc, const char **argv)
+{
+  /* devices_csv_loc is set by -mdevices-csv-loc.  */
+  if (devices_csv_loc)
+    return NULL;
+  else if (derived_devices_csv_loc)
+    return concat ("-mdevices-csv-loc=", derived_devices_csv_loc, NULL);
+  const char dirsep[2] = { DIR_SEPARATOR, 0 };
+  FILE * devices_csv = NULL;
+  char * local_devices_csv_loc = NULL;
+  int i;
+  for (i = 0; i < argc; i++)
+    {
+      char *inc_path = ASTRDUP (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);
+	  derived_devices_csv_loc = local_devices_csv_loc;
+	  return concat ("-mdevices-csv-loc=", local_devices_csv_loc, NULL);
+	}
+    }
+  return NULL;
+}
+
+static void
+devices_csv_warning (const char * str, const char * mcu_name)
+{
+  if (TARGET_DISABLE_DEVICE_WARN || surpress_devices_warn)
+    return;
+  warning (0, str, mcu_name);
+  inform ((location_t)0, "%s", advice_string);
+}
+
+/* Return 1 if MCU data wasn't loaded into extracted_mcu_data.  */
+static int
+parse_devices_csv_1 (const char * real_devices_csv_loc, const char * mcu_name)
+{
+  FILE * devices_csv = fopen (real_devices_csv_loc, "r");
+  /* 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;
+  bool found_headings = false;
+  bool found_mcu = false;
+  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";
+  /* devices_csv should never be NULL at this stage.  */
+  if (devices_csv == NULL)
+    {
+      devices_csv_warning ("Unexpected error opening devices.csv.", NULL);
+      return 1;
+    }
+  while (1)
+    {
+      res = fgets (line, line_buf_siz, devices_csv);
+      if (res == NULL)
+	{
+	  devices_csv_warning ("Device %s not found in devices.csv.", mcu_name);
+	  goto end;
+	}
+      else if (strncmp (line, device_name_heading,
+			strlen (device_name_heading)) == 0)
+	{
+	  found_headings = true;
+	  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)
+	    {
+	      devices_csv_warning ("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.", mcu_name);
+	      goto end;
+	    }
+	  else if (cpu_type_column == -1 || mpy_type_column == -1)
+	    {
+	      devices_csv_warning ("CPU_TYPE and/or MPY_TYPE headings not "
+				   "present in devices.csv, or format not "
+				   "as expected.", NULL);
+	      goto end;
+	    }
+	}
+      else if (strncasecmp (line, mcu_name, strlen (mcu_name)) == 0)
+	{
+	  if (!found_headings)
+	    {
+	      devices_csv_warning ("Column headings format of devices.csv "
+				   "not as expected.", NULL);
+	      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;
+	  bool found_cpu = false;
+	  bool found_mpy = false;
+	  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)
+		    {
+		      devices_csv_warning ("Invalid CPU_TYPE read from "
+					   "devices.csv.", NULL);
+		      goto end;
+		    }
+		  extracted_mcu_data.revision = cpu_type;
+		  found_cpu = true;
+		}
+	      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))
+		    {
+		      devices_csv_warning ("Invalid MPY_TYPE read from "
+					   "devices.csv.", NULL);
+		      goto end;
+		    }
+		  extracted_mcu_data.hwmpy = mpy_type;
+		  found_mpy = true;
+		}
+	      if (found_cpu && found_mpy)
+		{
+		  found_mcu = true;
+		  goto end;
+		}
+	      val = strtok (NULL, comma);
+	    }
+	  if (cpu_type == -1 || mpy_type == -1)
+	    devices_csv_warning ("Unknown error reading CPU_TYPE and/or "
+				 "MPY_TYPE from devices.csv.", NULL);
+	  goto end;
+	}
+    }
+ end:
+  fclose (devices_csv);
+  if (!found_mcu)
+    return 1;
+  return 0;
+}
+
+static int
+parse_devices_csv (const char * mcu_name)
+{
+  /* First check if the path to devices.csv was set by -mdevices-csv-loc.  */
+  if (devices_csv_loc != NULL)
+    return parse_devices_csv_1 (devices_csv_loc, 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);
+  return 1;
+}
+
 /* This function only needs to be executed once, but it can be first called
-   from a number of different locations.  */
+   from a number of different functions.  */
 void
-msp430_extract_mcu_data (const char * mcu_name)
+msp430_extract_mcu_data (const char * mcu_name, bool surpress_warn)
 {
   static int executed = 0;
   unsigned int i;
+  surpress_devices_warn = surpress_warn;
   if (mcu_name == NULL || executed == 1)
     return;
   executed = 1;
+  /* If parse_devices_csv returns non-zero we need to use the
+     hard-coded data.  */
+  if (!parse_devices_csv (mcu_name))
+    return;
   /* FIXME: This array is alpha sorted - we could use a binary search.  */
   for (i = ARRAY_SIZE (hard_msp430_mcu_data); i--;)
     if (strcasecmp (mcu_name, hard_msp430_mcu_data[i].name) == 0)
diff --git a/gcc/config/msp430/msp430-devices.h b/gcc/config/msp430/msp430-devices.h
index 2d6c1ed..9500115 100644
--- a/gcc/config/msp430/msp430-devices.h
+++ b/gcc/config/msp430/msp430-devices.h
@@ -8,4 +8,4 @@ struct t_msp430_mcu_data
 
 extern struct t_msp430_mcu_data extracted_mcu_data;
 
-void msp430_extract_mcu_data (const char * mcu_name);
+void msp430_extract_mcu_data (const char * mcu_name, bool surpress_warn);
diff --git a/gcc/config/msp430/msp430.c b/gcc/config/msp430/msp430.c
index 332d605..a112b1b 100644
--- a/gcc/config/msp430/msp430.c
+++ b/gcc/config/msp430/msp430.c
@@ -104,7 +104,7 @@ msp430_mcu_name (void)
 {
   if (target_mcu)
     {
-      msp430_extract_mcu_data (target_mcu);
+      msp430_extract_mcu_data (target_mcu, false);
       unsigned int i;
       unsigned int start_upper;
       unsigned int end_upper;
@@ -167,7 +167,7 @@ msp430_option_override (void)
 
   if (target_mcu)
     {
-      msp430_extract_mcu_data (target_mcu);
+      msp430_extract_mcu_data (target_mcu, false);
       int i;
 
       if (extracted_mcu_data.name != NULL)
@@ -2705,7 +2705,7 @@ msp430_use_f5_series_hwmult (void)
   if (strncasecmp (target_mcu, "msp430f6", 8) == 0)
     return cached_result = true;
 
-  msp430_extract_mcu_data (target_mcu);
+  msp430_extract_mcu_data (target_mcu, false);
 
   if (extracted_mcu_data.name != NULL)
     return cached_result = extracted_mcu_data.hwmpy == 8;
@@ -2733,7 +2733,7 @@ use_32bit_hwmult (void)
 
   cached_match = target_mcu;
 
-  msp430_extract_mcu_data (target_mcu);
+  msp430_extract_mcu_data (target_mcu, false);
   if (extracted_mcu_data.name != NULL)
     return cached_result = extracted_mcu_data.hwmpy == 4;
 
@@ -2764,7 +2764,7 @@ msp430_no_hwmult (void)
 
   cached_match = target_mcu;
 
-  msp430_extract_mcu_data (target_mcu);
+  msp430_extract_mcu_data (target_mcu, false);
   if (extracted_mcu_data.name != NULL)
     return cached_result = extracted_mcu_data.hwmpy == 0;
 
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index acc8d16..a8daf3f 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -46,10 +46,12 @@ extern bool msp430x;
 
 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_select_cpu", msp430_select_cpu },
+  { "msp430_select_cpu", msp430_select_cpu }, \
+  { "msp430_check_path_for_devices", msp430_check_path_for_devices },
 
 #undef  STARTFILE_SPEC
 #define STARTFILE_SPEC "%{pg:gcrt0.o%s}%{!pg:%{minrt:crt0-minrt.o%s}%{!minrt:crt0.o%s}} %{!minrt:crtbegin.o%s}"
@@ -75,7 +77,8 @@ extern const char * msp430_select_cpu (int, const char **);
   "%{mcode-region=*:--code-region=%*} %{mdata-region=*:--data-region=%*}"
 
 #define DRIVER_SELF_SPECS \
-  "%{!mcpu=*:%{mmcu=*:%:msp430_select_cpu(%{mmcu=*:%*})}}"
+  " %{I*:%:msp430_check_path_for_devices(%{I*:%*})}"       \
+  " %{!mcpu=*:%{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/doc/invoke.texi b/gcc/doc/invoke.texi
index 055e8c4..19f32bc 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -22064,7 +22064,12 @@ command line.  The script's name is the name of the MCU with
 command line defines the C preprocessor symbol @code{__XXX__} and
 cause the linker to search for a script called @file{xxx.ld}.
 
-This option is also passed on to the assembler.
+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 include paths specified
+with @code{-I}.
 
 @item -mwarn-mcu
 @itemx -mno-warn-mcu
@@ -22153,6 +22158,9 @@ the named silicon errata.
 This option passes on a request to the assembler to enable warning
 messages when a silicon errata might need to be applied.
 
+@item -mdisable-device-warnings
+Suppress warnings regarding the @samp{devices.csv} file not being found.
+
 @end table
 
 @node NDS32 Options
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_00.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_00.c
new file mode 100644
index 0000000..eb95ee6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_00.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-msp-options "-mmcu=msp430_00" } */
+/* { dg-warning "does not have hardware multiply" "" { target msp430_hwmul_not_none } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_01.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_01.c
new file mode 100644
index 0000000..746d952
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_01.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-msp-options "-mmcu=msp430_01" } */
+/* { dg-warning "supports 16bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_02.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_02.c
new file mode 100644
index 0000000..84ad95d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_02.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-msp-options "-mmcu=msp430_02" } */
+/* { dg-warning "supports 16bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_04.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_04.c
new file mode 100644
index 0000000..b692277
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_04.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-msp-options "-mmcu=msp430_04" } */
+/* { dg-warning "supports 32bit hardware multiply" "" { target msp430_hwmul_not_32bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_08.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_08.c
new file mode 100644
index 0000000..8489ea8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_08.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-msp-options "-mmcu=msp430_08" } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_10.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_10.c
new file mode 100644
index 0000000..2c88c97
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_10.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_10" } */
+/* { dg-warning "does not have hardware multiply" "" { target msp430_hwmul_not_none } 0 } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_11.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_11.c
new file mode 100644
index 0000000..161c8ac
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_11.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_11" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports 16bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_12.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_12.c
new file mode 100644
index 0000000..68b675e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_12.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_12" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports 16bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_14.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_14.c
new file mode 100644
index 0000000..ebc77e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_14.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_14" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports 32bit hardware multiply" "" { target msp430_hwmul_not_32bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_18.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_18.c
new file mode 100644
index 0000000..ad02e90
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_18.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_18" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_20.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_20.c
new file mode 100644
index 0000000..6940975
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_20.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_20" } */
+/* { dg-warning "does not have hardware multiply" "" { target msp430_hwmul_not_none } 0 } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_21.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_21.c
new file mode 100644
index 0000000..62e3f2a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_21.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_21" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports 16bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_22.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_22.c
new file mode 100644
index 0000000..bc79f0c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_22.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_22" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports 16bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_24.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_24.c
new file mode 100644
index 0000000..f83390e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_24.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_24" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports 32bit hardware multiply" "" { target msp430_hwmul_not_32bit } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_28.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_28.c
new file mode 100644
index 0000000..1e0a2be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430_28.c
@@ -0,0 +1,6 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430_28" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430f5529.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430f5529.c
new file mode 100644
index 0000000..8a42cb8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430f5529.c
@@ -0,0 +1,8 @@
+/* { dg-do link } */
+/* { dg-msp-options "-mmcu=msp430f5529" } */
+/* { dg-warning "supports 430X ISA but -mcpu option is set to 430" "" { target 430_selected } 0 } */
+/* { dg-warning "supports f5series hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+/* { dg-warning "Device msp430f5529 not found in devices.csv" "" { target msp430-*-* } 0 } */
+/* { dg-message "hard-coded device data" "" { target msp430-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv_msp430fr5969.c b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430fr5969.c
new file mode 100644
index 0000000..3994892
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv_msp430fr5969.c
@@ -0,0 +1,10 @@
+/* { dg-do link } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-msp-options "-mmcu=msp430fr5969" } */
+/* MSP430FR5969 has msp430x ISA and f5series hwmult in the hard-coded data,
+   check that the different values for this device in devices.csv override it.
+   */
+/* { dg-warning "does not have hardware multiply" "" { target msp430_hwmul_not_none } 0 } */
+
+
+#include "../devices-main.c"
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..23b38e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/devices.csv
@@ -0,0 +1,17 @@
+# 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
+msp430fr5969,0,0
diff --git a/gcc/testsuite/gcc.target/msp430/devices/msp430.h b/gcc/testsuite/gcc.target/msp430/devices/msp430.h
new file mode 100644
index 0000000..1a1c7a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/msp430.h
@@ -0,0 +1,3 @@
+#ifndef __MSP430__
+#error "__MSP430__ not defined"
+#endif
diff --git a/gcc/testsuite/gcc.target/msp430/msp430.exp b/gcc/testsuite/gcc.target/msp430/msp430.exp
index a0de3d1..2cdf436 100644
--- a/gcc/testsuite/gcc.target/msp430/msp430.exp
+++ b/gcc/testsuite/gcc.target/msp430/msp430.exp
@@ -46,7 +46,10 @@ proc msp430_reset_board_info_ldflags { } {
 
 # Return the name of the mulilib directory GCC uses when invoked with 'cmd_opts'
 # as arguments.
-proc msp430_get_multidir { cmd_opts } {
+proc msp430_get_multidir { cmd_opts test_file } {
+    if { [string match "csv_*" [file tail $test_file]] } {
+	append cmd_opts " -I[file dirname $test_file] -mdisable-device-warnings"
+    }
     set cmd "[board_info [target_info name] compiler] $cmd_opts \
 	--print-multi-dir"
     set tmp [remote_exec host $cmd]
@@ -114,6 +117,7 @@ proc dg-msp-options { args } {
     }
     global board_info
     upvar tool_flags test_tool_flags
+    upvar prog test_file
     set opts [lindex $args 1]
     if { [board_info [target_info name] exists ldflags] } {
 	msp430_reset_board_info_ldflags
@@ -123,7 +127,8 @@ proc dg-msp-options { args } {
 	if { [regexp {\-B\S+libgloss} $ldflags] } {
 	    if { [board_info [target_info name] exists multitop] } {
 		set multitop "[board_info [target_info name] multitop]"
-		set multidir [msp430_get_multidir "$opts $test_tool_flags"]
+		set multidir [msp430_get_multidir "$opts $test_tool_flags" \
+		    $test_file]
 		set newmultitop $multitop
 		set default_multidir_regex "/\.($|/$)"
 		set large_multidir_regex "/large($|/$)"
@@ -169,7 +174,6 @@ proc dg-msp-options { args } {
 # An alternative would be to check the
 # -mmcu option in the test file, but this is simpler and gives some finer
 # control over which tests make use of this functionality.
-
 proc msp430_get_supported_opts { test_file } {
     global board_info
     # If the mcu name is not recognized, run the test as normal without
@@ -196,7 +200,7 @@ proc msp430_get_supported_opts { test_file } {
 	lappend supported_opts -mcpu=msp430 
     }
     # Default multilib supported by the MCU i.e. its ISA is msp430x
-    if { [msp430_get_multidir "-mmcu=$mcu_name"] eq "." } {
+    if { [msp430_get_multidir "-mmcu=$mcu_name" $test_file] eq "." } {
 	lappend supported_opts -mcpu=msp430x -mcpu=msp430xv2 -mlarge
     }
     return $supported_opts
@@ -204,11 +208,22 @@ proc msp430_get_supported_opts { test_file } {
 
 # Run each test file in 'tests' with every supported msp430 specific option for
 # the mcu specified by the test.
-proc msp430_device_permutations_runtest { tests } {
-    global MSP430_DEFAULT_CFLAGS
+proc msp430_device_permutations_runtest { tests default_cflags } {
+    # The specific tests being run
+    global runtests
     foreach { test_file } $tests {
+	if { ![runtest_file_p $runtests $test_file] } {
+	    continue
+	}
 	foreach { mcu_flags } [msp430_get_supported_opts $test_file] {
-	    dg-runtest $test_file "$mcu_flags" "$MSP430_DEFAULT_CFLAGS"
+	    if { [string match "csv_*" [file tail $test_file]] } {
+		dg-runtest $test_file "$mcu_flags" "-I[file dirname $test_file] $default_cflags"
+	    } else {
+		# If not testing using devices.csv, then don't test if include
+		# paths are set up properly, by defining TESTING_HARD_DATA to
+		# remove the inclusion of msp430.h in devices-main.c.
+		dg-runtest $test_file "$mcu_flags" "-DTESTING_HARD_DATA $default_cflags"
+	    }
 	}
     }
 }
@@ -234,9 +249,11 @@ msp430_set_old_board_info_ldflags
 
 # Main loop.
 dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \
-    "" $MSP430_DEFAULT_CFLAGS
+    "" "$MSP430_DEFAULT_CFLAGS"
 
-msp430_device_permutations_runtest [lsort [glob -nocomplain $srcdir/$subdir/devices/*.\[cCS\]]]
+msp430_device_permutations_runtest \
+    [lsort [glob -nocomplain $srcdir/$subdir/devices/*.\[cCS\]]] \
+    "$MSP430_DEFAULT_CFLAGS"
 
 # Reset any possibly changed ldflags by dg-msp-options
 msp430_reset_board_info_ldflags
-- 
2.7.4

