[MSP430,2/2] Read MCU data from external file

Message ID 20190808131730.3b3fc9ee@jozef-kubuntu
State New
Headers show
Series
  • Improve and extend MCU data handling
Related show

Commit Message

Jozef Lawrynowicz Aug. 8, 2019, 12:17 p.m.
This patch extends the MCU data handling so that MCU data can be provided
in an external file (devices.csv). This means the compiler doesn't have to be
updated and rebuilt to support new devices when they are released.

TI distribute devices.csv with other support files (header files, linker
scripts) on their website. I have also tested that a program builds successfully
for every MCU and the correct hwmult library is passed to the linker.
I also built the msp430 cross compiler using a trunk build of native GCC to
ensure all the error and warning messages added by my code are correct, as
checked by -Wformat-diag.

Comments

Jeff Law Aug. 12, 2019, 8:34 p.m. | #1
On 8/8/19 6:17 AM, Jozef Lawrynowicz wrote:
> This patch extends the MCU data handling so that MCU data can be provided

> in an external file (devices.csv). This means the compiler doesn't have to be

> updated and rebuilt to support new devices when they are released.

> 

> TI distribute devices.csv with other support files (header files, linker

> scripts) on their website. I have also tested that a program builds successfully

> for every MCU and the correct hwmult library is passed to the linker.

> I also built the msp430 cross compiler using a trunk build of native GCC to

> ensure all the error and warning messages added by my code are correct, as

> checked by -Wformat-diag.

> 

> 

> 0002-MSP430-Devices-2-Read-MCU-data-from-external-devices.patch

> 

> From 6f67cdd282f2351d7450e343314beeaa745f0159 Mon Sep 17 00:00:00 2001

> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

> Date: Tue, 6 Aug 2019 10:52:54 +0100

> Subject: [PATCH 2/2] MSP430: Devices [2]: Read MCU data from external

>  devices.csv file, if it exists

> 

> gcc/ChangeLog:

> 

> 2019-08-XX  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

> 

> 	* gcc/config/msp430/driver-msp430.c (msp430_set_driver_var): New.

> 	* gcc/config/msp430/msp430-devices.c (canonicalize_path_dirsep): New.

> 	(msp430_check_path_for_devices): New.

> 	(parse_devices_csv_1): New.

> 	(parse_devices_csv): New.

> 	(msp430_extract_mcu_data): Try to find devices.csv and search for the

> 	MCU data in devices.csv before using the hard-coded data.

> 	Warn if devices.csv isn't found and the MCU wasn't found in the

> 	hard-coded data either.

> 	* gcc/config/msp430/msp430.h (DRIVER_SELF_SPECS): Call

> 	msp430_set_driver_var for -mno-warn-devices-csv and -mdevices-csv-loc.

> 	Search for devices.csv on -I and -L paths.

> 	(EXTRA_SPEC_FUNCTIONS): Add msp430_check_path_for_devices and

> 	msp430_set_driver_var.

> 	* gcc/config/msp430/msp430.opt: Add -mwarn-devices-csv and

> 	-mdevices-csv-loc=.

> 	* gcc/doc/invoke.texi (-mmcu): Document that -I and -L paths are

> 	searched for devices.csv.

> 	(mwarn-devices-csv): Document option.

> 

> gcc/testsuite/ChangeLog:

> 

> 2019-08-XX  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

> 

> 	* gcc.target/msp430/msp430.exp (msp430_device_permutations_runtest):

> 	Handle csv-* and bad-devices-* tests.

> 	* gcc.target/msp430/devices/README: Document how bad-devices-* tests

> 	work.

> 	* gcc.target/msp430/devices/bad-devices-1.c: New test.

> 	* gcc.target/msp430/devices/bad-devices-2.c: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-3.c: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-4.c: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-5.c: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-6.c: Likewise.

> 	* gcc.target/msp430/devices/csv-device-order.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_00.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_01.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_02.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_04.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_08.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_10.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_11.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_12.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_14.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_18.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_20.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_21.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_22.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_24.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430_28.c: Likewise.

> 	* gcc.target/msp430/devices/csv-msp430fr5969.c: Likewise.

> 	* gcc.target/msp430/devices/hard-foo.c: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-1.csv: New test support file.

> 	* gcc.target/msp430/devices/bad-devices-2.csv: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-3.csv: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-4.csv: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-5.csv: Likewise.

> 	* gcc.target/msp430/devices/bad-devices-6.csv: Likewise.

> 	* gcc.target/msp430/devices/devices.csv: Likewise.

> ---

So it's good we don't have to do updates when a new devices.csv is
released -- except when we devices require new multilibs, but I don't
offhand see a sensible way to deal with that.

OK.

jeff
Jozef Lawrynowicz Aug. 13, 2019, 9:56 a.m. | #2
On Mon, 12 Aug 2019 14:34:39 -0600
Jeff Law <law@redhat.com> wrote:

> On 8/8/19 6:17 AM, Jozef Lawrynowicz wrote:

> > This patch extends the MCU data handling so that MCU data can be provided

> > in an external file (devices.csv). This means the compiler doesn't have to be

> > updated and rebuilt to support new devices when they are released.

> > 

> > TI distribute devices.csv with other support files (header files, linker

> > scripts) on their website. I have also tested that a program builds successfully

> > for every MCU and the correct hwmult library is passed to the linker.

> > I also built the msp430 cross compiler using a trunk build of native GCC to

> > ensure all the error and warning messages added by my code are correct, as

> > checked by -Wformat-diag.

> > 

> > 

> > 0002-MSP430-Devices-2-Read-MCU-data-from-external-devices.patch

> > 

> > From 6f67cdd282f2351d7450e343314beeaa745f0159 Mon Sep 17 00:00:00 2001

> > From: Jozef Lawrynowicz <jozef.l@mittosystems.com>

> > Date: Tue, 6 Aug 2019 10:52:54 +0100

> > Subject: [PATCH 2/2] MSP430: Devices [2]: Read MCU data from external

> >  devices.csv file, if it exists

> > 

> > gcc/ChangeLog:

> > 

> > 2019-08-XX  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

> > 

> > 	* gcc/config/msp430/driver-msp430.c (msp430_set_driver_var): New.

> > 	* gcc/config/msp430/msp430-devices.c (canonicalize_path_dirsep): New.

> > 	(msp430_check_path_for_devices): New.

> > 	(parse_devices_csv_1): New.

> > 	(parse_devices_csv): New.

> > 	(msp430_extract_mcu_data): Try to find devices.csv and search for the

> > 	MCU data in devices.csv before using the hard-coded data.

> > 	Warn if devices.csv isn't found and the MCU wasn't found in the

> > 	hard-coded data either.

> > 	* gcc/config/msp430/msp430.h (DRIVER_SELF_SPECS): Call

> > 	msp430_set_driver_var for -mno-warn-devices-csv and -mdevices-csv-loc.

> > 	Search for devices.csv on -I and -L paths.

> > 	(EXTRA_SPEC_FUNCTIONS): Add msp430_check_path_for_devices and

> > 	msp430_set_driver_var.

> > 	* gcc/config/msp430/msp430.opt: Add -mwarn-devices-csv and

> > 	-mdevices-csv-loc=.

> > 	* gcc/doc/invoke.texi (-mmcu): Document that -I and -L paths are

> > 	searched for devices.csv.

> > 	(mwarn-devices-csv): Document option.

> > 

> > gcc/testsuite/ChangeLog:

> > 

> > 2019-08-XX  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

> > 

> > 	* gcc.target/msp430/msp430.exp (msp430_device_permutations_runtest):

> > 	Handle csv-* and bad-devices-* tests.

> > 	* gcc.target/msp430/devices/README: Document how bad-devices-* tests

> > 	work.

> > 	* gcc.target/msp430/devices/bad-devices-1.c: New test.

> > 	* gcc.target/msp430/devices/bad-devices-2.c: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-3.c: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-4.c: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-5.c: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-6.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-device-order.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_00.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_01.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_02.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_04.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_08.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_10.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_11.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_12.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_14.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_18.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_20.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_21.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_22.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_24.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430_28.c: Likewise.

> > 	* gcc.target/msp430/devices/csv-msp430fr5969.c: Likewise.

> > 	* gcc.target/msp430/devices/hard-foo.c: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-1.csv: New test support file.

> > 	* gcc.target/msp430/devices/bad-devices-2.csv: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-3.csv: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-4.csv: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-5.csv: Likewise.

> > 	* gcc.target/msp430/devices/bad-devices-6.csv: Likewise.

> > 	* gcc.target/msp430/devices/devices.csv: Likewise.

> > ---  

> So it's good we don't have to do updates when a new devices.csv is

> released -- except when we devices require new multilibs, but I don't

> offhand see a sensible way to deal with that.


For a given device, the only choice to make regarding multilibs at the moment
is whether to use a 430 or 430X multilib. The large memory model multilib will
only be used if a user passes -mlarge for a 430X device.
So if a device is added which uses a new extension to the ISA (which is not
forward-compatible) and therefore needs a new multilib, we would need to update
the compiler to handle code generation for the extension anyway.

In case it wasn't clear, the hardware multiply support (which is the other
piece of data we get from devices.csv for a device) is not handled using
multilibs. Instead, the driver tells the linker which hardware multiply library
to use, of which there is only one per possible hardware multiply option.

Thanks,
Jozef
> 

> OK.

> 

> jeff

Patch

From 6f67cdd282f2351d7450e343314beeaa745f0159 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 6 Aug 2019 10:52:54 +0100
Subject: [PATCH 2/2] MSP430: Devices [2]: Read MCU data from external
 devices.csv file, if it exists

gcc/ChangeLog:

2019-08-XX  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* gcc/config/msp430/driver-msp430.c (msp430_set_driver_var): New.
	* gcc/config/msp430/msp430-devices.c (canonicalize_path_dirsep): New.
	(msp430_check_path_for_devices): New.
	(parse_devices_csv_1): New.
	(parse_devices_csv): New.
	(msp430_extract_mcu_data): Try to find devices.csv and search for the
	MCU data in devices.csv before using the hard-coded data.
	Warn if devices.csv isn't found and the MCU wasn't found in the
	hard-coded data either.
	* gcc/config/msp430/msp430.h (DRIVER_SELF_SPECS): Call
	msp430_set_driver_var for -mno-warn-devices-csv and -mdevices-csv-loc.
	Search for devices.csv on -I and -L paths.
	(EXTRA_SPEC_FUNCTIONS): Add msp430_check_path_for_devices and
	msp430_set_driver_var.
	* gcc/config/msp430/msp430.opt: Add -mwarn-devices-csv and
	-mdevices-csv-loc=.
	* gcc/doc/invoke.texi (-mmcu): Document that -I and -L paths are
	searched for devices.csv.
	(mwarn-devices-csv): Document option.

gcc/testsuite/ChangeLog:

2019-08-XX  Jozef Lawrynowicz  <jozef.l@mittosystems.com>

	* gcc.target/msp430/msp430.exp (msp430_device_permutations_runtest):
	Handle csv-* and bad-devices-* tests.
	* gcc.target/msp430/devices/README: Document how bad-devices-* tests
	work.
	* gcc.target/msp430/devices/bad-devices-1.c: New test.
	* gcc.target/msp430/devices/bad-devices-2.c: Likewise.
	* gcc.target/msp430/devices/bad-devices-3.c: Likewise.
	* gcc.target/msp430/devices/bad-devices-4.c: Likewise.
	* gcc.target/msp430/devices/bad-devices-5.c: Likewise.
	* gcc.target/msp430/devices/bad-devices-6.c: Likewise.
	* gcc.target/msp430/devices/csv-device-order.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_00.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_01.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_02.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_04.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_08.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_10.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_11.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_12.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_14.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_18.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_20.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_21.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_22.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_24.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430_28.c: Likewise.
	* gcc.target/msp430/devices/csv-msp430fr5969.c: Likewise.
	* gcc.target/msp430/devices/hard-foo.c: Likewise.
	* gcc.target/msp430/devices/bad-devices-1.csv: New test support file.
	* gcc.target/msp430/devices/bad-devices-2.csv: Likewise.
	* gcc.target/msp430/devices/bad-devices-3.csv: Likewise.
	* gcc.target/msp430/devices/bad-devices-4.csv: Likewise.
	* gcc.target/msp430/devices/bad-devices-5.csv: Likewise.
	* gcc.target/msp430/devices/bad-devices-6.csv: Likewise.
	* gcc.target/msp430/devices/devices.csv: Likewise.
---
 gcc/config/msp430/driver-msp430.c             |  23 ++
 gcc/config/msp430/msp430-devices.c            | 276 +++++++++++++++++-
 gcc/config/msp430/msp430.h                    |  10 +-
 gcc/config/msp430/msp430.opt                  |   9 +
 gcc/doc/invoke.texi                           |  14 +-
 .../gcc.target/msp430/devices/README          |   5 +
 .../gcc.target/msp430/devices/bad-devices-1.c |   5 +
 .../msp430/devices/bad-devices-1.csv          |   3 +
 .../gcc.target/msp430/devices/bad-devices-2.c |   5 +
 .../msp430/devices/bad-devices-2.csv          |   3 +
 .../gcc.target/msp430/devices/bad-devices-3.c |   5 +
 .../msp430/devices/bad-devices-3.csv          |   3 +
 .../gcc.target/msp430/devices/bad-devices-4.c |   5 +
 .../msp430/devices/bad-devices-4.csv          |   3 +
 .../gcc.target/msp430/devices/bad-devices-5.c |   5 +
 .../msp430/devices/bad-devices-5.csv          |   3 +
 .../gcc.target/msp430/devices/bad-devices-6.c |   5 +
 .../msp430/devices/bad-devices-6.csv          |   3 +
 .../msp430/devices/csv-device-order.c         |  11 +
 .../gcc.target/msp430/devices/csv-msp430_00.c |   7 +
 .../gcc.target/msp430/devices/csv-msp430_01.c |   7 +
 .../gcc.target/msp430/devices/csv-msp430_02.c |   7 +
 .../gcc.target/msp430/devices/csv-msp430_04.c |   7 +
 .../gcc.target/msp430/devices/csv-msp430_08.c |   7 +
 .../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 +
 .../msp430/devices/csv-msp430fr5969.c         |  11 +
 .../gcc.target/msp430/devices/devices.csv     |  22 ++
 .../gcc.target/msp430/devices/hard-foo.c      |   1 +
 gcc/testsuite/gcc.target/msp430/msp430.exp    |  16 +-
 38 files changed, 537 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.csv
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/csv-device-order.c
 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-msp430fr5969.c
 create mode 100644 gcc/testsuite/gcc.target/msp430/devices/devices.csv

diff --git a/gcc/config/msp430/driver-msp430.c b/gcc/config/msp430/driver-msp430.c
index 9ad05dca957..4fa70c6c2e1 100644
--- a/gcc/config/msp430/driver-msp430.c
+++ b/gcc/config/msp430/driver-msp430.c
@@ -54,6 +54,29 @@  msp430_select_cpu (int argc, const char ** argv)
   return NULL;
 }
 
+/* Spec function to set a global variable to a specific value in the driver.
+   The first argument is the variable name, and the second is the value to set
+   it to.
+   Currently only "msp430_warn_devices_csv" and "msp430_devices_csv_loc" are
+   supported.
+   The intention is that we can take a "Target" option and set the variable
+   associated with it in the driver as well.  Whilst the driver sees "Target"
+   options, it does not set the variables associated with that option.  */
+const char *
+msp430_set_driver_var (int argc, const char ** argv)
+{
+  if (argc != 2)
+    error ("%<msp430_set_driver_var%> expects 2 arguments");
+  else if (strcmp (argv[0], "msp430_warn_devices_csv") == 0)
+    msp430_warn_devices_csv = atoi (argv[1]);
+  else if (strcmp (argv[0], "msp430_devices_csv_loc") == 0)
+    msp430_devices_csv_loc = argv[1];
+  else
+    error ("unhandled arguments %qs and %qs to %<msp430_set_driver_var%>",
+	   argv[0], argv[1]);
+  return NULL;
+}
+
 /* Implement spec function `msp430_hwmult_libĀ“.  */
 
 const char *
diff --git a/gcc/config/msp430/msp430-devices.c b/gcc/config/msp430/msp430-devices.c
index c5faff80487..0488a0f197c 100644
--- a/gcc/config/msp430/msp430-devices.c
+++ b/gcc/config/msp430/msp430-devices.c
@@ -35,8 +35,254 @@  struct t_msp430_mcu_data extracted_mcu_data;
 /* Initialized at the bottom of this file.  */
 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;
+
+/* This is to canonicalize the directory separators in the path.
+   On Windows we could have a mix of '/' and '\' in the path.  */
+static void
+canonicalize_path_dirsep (char **path)
+{
+  char *t_path = *path;
+  int len = strlen (t_path);
+  int i;
+  for (i = 0; i < len; i++)
+    if (IS_DIR_SEPARATOR (t_path[i]))
+      t_path[i] = DIR_SEPARATOR;
+}
+
+/* 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
+   file.  */
+const char *
+msp430_check_path_for_devices (int argc, const char **argv)
+{
+  const char dirsep[2] = { DIR_SEPARATOR, 0 };
+  FILE * devices_file = NULL;
+  char * local_devices_csv_loc = NULL;
+  int i;
+  /* msp430_devices_csv_loc is set by -mdevices-csv-loc, derived_devices_csv_loc
+     is set by this function only.  */
+  if (msp430_devices_csv_loc || derived_devices_csv_loc)
+    return NULL;
+  for (i = 0; i < argc; i++)
+    {
+      char *inc_path = ASTRDUP (argv[i]);
+      canonicalize_path_dirsep (&inc_path);
+      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_file = fopen (local_devices_csv_loc, "r");
+      if (devices_file != NULL)
+	{
+	  fclose (devices_file);
+	  derived_devices_csv_loc = local_devices_csv_loc;
+	  return concat ("-mdevices-csv-loc=", local_devices_csv_loc, NULL);
+	}
+    }
+  return NULL;
+}
+
+/* Search the devices.csv file for the given MCU name, and load the device
+   data into extracted_mcu_data.
+   Return 1 if MCU wasn't found in devices.csv, or the data couldn't be loaded
+   into extracted_mcu_data.
+   devices.csv has a specific format.  There is a row for column headings which
+   begins with "# Device Name".  The column numbers for CPU_TYPE (MSP430 ISA)
+   and MPY_TYPE (hwmult support) are extracted from this row and used later to
+   extract the ISA and hwmult supported for the given device.
+   The rows containing the MCU data are expected to begin immediately after the
+   column headings.  */
+static int
+parse_devices_csv_1 (const char * real_devices_csv_loc, const char * mcu_name)
+{
+  FILE * devices_file = 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_size is set
+     to 200 to account for further possible additions to errata.  */
+  const size_t line_buf_size = 200;
+  char line[line_buf_size];
+  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_file should never be NULL at this stage.  */
+  if (devices_file == NULL)
+    {
+      if (msp430_warn_devices_csv)
+	warning (0, "unexpected error opening %<devices.csv%>");
+      return 1;
+    }
+  while (1)
+    {
+      res = fgets (line, line_buf_size, devices_file);
+      if (res == NULL)
+	{
+	  /* The device has not been found in devices.csv.  Don't warn now in
+	     case it is in the hard-coded data.  We will warn later if the
+	     device was not found in the hard-coded data either.  */
+	  goto end;
+	}
+      else if (!found_headings
+	       && strncmp (line, device_name_heading,
+			   strlen (device_name_heading)) == 0)
+	{
+	  int curr_column = 0;
+	  char * heading = strtok (line, ",");
+	  found_headings = true;
+	  /* Find which column MPY_TYPE and CPU_TYPE are in.  */
+	  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;
+	      if (cpu_type_column != -1 && mpy_type_column != -1)
+		break;
+	      heading = strtok (NULL, ",");
+	      curr_column++;
+	    }
+	  if (cpu_type_column == -1 || mpy_type_column == -1)
+	    {
+	      if (msp430_warn_devices_csv)
+		{
+		  if (cpu_type_column == -1 && mpy_type_column != -1)
+		    warning (0, "%<CPU_TYPE%> column heading is missing from "
+			     "%<devices.csv%>");
+		  else if (mpy_type_column == -1 && cpu_type_column != -1)
+		    warning (0, "%<MPY_TYPE%> column heading is missing from "
+			     "%<devices.csv%>");
+		  else
+		    warning (0, "%<CPU_TYPE%> and %<MPY_TYPE%> column headings "
+			     "are missing from %<devices.csv%>");
+		}
+	      goto end;
+	    }
+	}
+      else if (strncasecmp (line, mcu_name, strlen (mcu_name)) == 0
+	       && *(line + strlen (mcu_name)) == ',')
+	{
+	  if (!found_headings)
+	    {
+	      if (msp430_warn_devices_csv)
+		warning (0, "format of column headings in %<devices.csv%> "
+			 "is incorrect");
+	      goto end;
+	    }
+	  char * val = strtok (line, ",");
+	  int final_col_num = ((mpy_type_column > cpu_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; curr_col++)
+	    {
+	      /* Strip any new line characters from the last token.  */
+	      if (curr_col == final_col_num && strlen (val) > 1
+		  /* ASCII digit 10 == LF, 13 == CR.  */
+		  && (val[1] == 10 || val[1] == 13))
+		{
+		  /* Terminate the string after the first character.  */
+		  val[1] = 0;
+		}
+	      if (curr_col == cpu_type_column)
+		{
+		  cpu_type = atoi (val);
+		  /* Only a single '0', '1' or '2' is accepted.  */
+		  if (strlen (val) != 1
+		      /* atoi will return 0 if the string passed as an argument
+			 is empty or contains only whitespace characters, so we
+			 must error if 0 is returned but the first character in
+			 the original string is not '0'.  */
+		      || (cpu_type == 0 && val[0] != '0')
+		      || cpu_type > 2 || cpu_type < 0)
+		    {
+		      if (msp430_warn_devices_csv)
+			warning (0, "invalid %<CPU_TYPE%> value of %qs read "
+				 "from %<devices.csv%> for %qs", val, mcu_name);
+		      goto end;
+		    }
+		  extracted_mcu_data.revision = cpu_type;
+		  found_cpu = true;
+		}
+	      else if (curr_col == mpy_type_column)
+		{
+		  mpy_type = atoi (val);
+		  /* Only a single '0', '1', '2', '4' or '8' is accepted.  */
+		  if (strlen (val) != 1
+		      || (mpy_type == 0 && val[0] != '0')
+		      || !(mpy_type == 0
+			   || mpy_type == 1
+			   || mpy_type == 2
+			   || mpy_type == 4
+			   || mpy_type == 8))
+		    {
+		      if (msp430_warn_devices_csv)
+			warning (0, "invalid %<MPY_TYPE%> value of %qs read "
+				 "from %<devices.csv%> for %qs", val, mcu_name);
+		      goto end;
+		    }
+		  extracted_mcu_data.hwmpy = mpy_type;
+		  found_mpy = true;
+		}
+	      if (found_cpu && found_mpy)
+		{
+		  extracted_mcu_data.name = mcu_name;
+		  found_mcu = true;
+		  goto end;
+		}
+	      val = strtok (NULL, ",");
+	    }
+	  if (msp430_warn_devices_csv && (cpu_type == -1 || mpy_type == -1))
+	    warning (0, "unknown error reading %s from "
+		     "%<devices.csv%>",
+		     (cpu_type != -1 ? "%<MPY_TYPE%>"
+		      : (mpy_type != -1 ? "%<CPU_TYPE%>"
+			 : "%<CPU_TYPE%> and %<MPY_TYPE%>")));
+	  goto end;
+	}
+    }
+end:
+  fclose (devices_file);
+  if (!found_mcu)
+    return 1;
+  return 0;
+}
+
+/* Wrapper for the parse_devices_csv_1 work function.
+   A return code of 0 indicates that the MCU data has been successfully
+   extracted into extracted_mcu_data.
+   A return code of 1 indicates that the specified MCU wasn't found in
+   devices.csv.
+   A return code of 2 indicates that devices.csv wasn't found at all.  */
+static int
+parse_devices_csv (const char * mcu_name)
+{
+  /* First check if the path to devices.csv was set by -mdevices-csv-loc.  */
+  if (msp430_devices_csv_loc != NULL)
+    return parse_devices_csv_1 (msp430_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);
+  /* devices.csv was not found.  */
+  return 2;
+}
+
 /* Main entry point to load the MCU data for the given -mmcu into
-   extracted_mcu_data.  hard_msp430_mcu_data (initialized at the bottom of this
+   extracted_mcu_data.
+   First, the "devices.csv" MCU data file is searched for, if it is found, and
+   the MCU has a record in it, then that data is used.
+   Otherwise, hard_msp430_mcu_data (initialized at the bottom of this
    file) is searched for the MCU name.
    This function only needs to be executed once, but it can be first called
    from a number of different locations.  */
@@ -44,10 +290,27 @@  void
 msp430_extract_mcu_data (const char * mcu_name)
 {
   static int executed = 0;
+  int devices_csv_not_found = 0;
   int i;
   if (mcu_name == NULL || executed == 1)
     return;
   executed = 1;
+  /* If parse_devices_csv returns non-zero we need to use the
+     hard-coded data.  */
+  switch (parse_devices_csv (mcu_name))
+    {
+    case 0:
+      return;
+    case 1:
+      /* MCU not found in devices.csv.  Warn later if it's not in the
+	 hard-coded data either.  */
+      break;
+    case 2:
+      devices_csv_not_found = 1;
+      break;
+    default:
+      gcc_unreachable ();
+    }
   /* 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)
@@ -82,6 +345,17 @@  msp430_extract_mcu_data (const char * mcu_name)
 		       hard_msp430_mcu_data[i].revision);
 	}
     }
+  else if (msp430_warn_devices_csv && devices_csv_not_found)
+    warning (0, "could not locate MCU data file %<devices.csv%>");
+  else if (msp430_warn_mcu && extracted_mcu_data.name == NULL)
+    {
+      /* FIXME: We should warn here that the MCU name is unrecognized, but
+	 msp430_option_override will warn about an unrecognized MCU as well.
+	 The benefit of warning here is that this is code common to both the
+	 driver and compiler proper, so a warning will be emitted when
+	 assembling/linking via the driver, whilst msp430_option_override will
+	 only be called when preprocessing or compiling.  */
+    }
 }
 
 /* The data in this structure has been extracted from version 1.194 of the
diff --git a/gcc/config/msp430/msp430.h b/gcc/config/msp430/msp430.h
index b080dfbaca1..f23a460251f 100644
--- a/gcc/config/msp430/msp430.h
+++ b/gcc/config/msp430/msp430.h
@@ -75,14 +75,22 @@  extern bool msp430x;
     "%e-mcode-region requires the large memory model (-mlarge)}}"	\
   " %{!mlarge:%{mdata-region=*:"	\
     "%e-mdata-region requires the large memory model (-mlarge)}}"	\
+  " %{mno-warn-devices-csv:%:msp430_set_driver_var(msp430_warn_devices_csv 0)}"\
+  " %{mdevices-csv-loc=*:%:msp430_set_driver_var(msp430_devices_csv_loc %*)}"\
+  " %{I*:%:msp430_check_path_for_devices(%{I*:%*})}"       \
+  " %{L*:%:msp430_check_path_for_devices(%{L*:%*})}"       \
   " %{!mcpu=*:%{mmcu=*:%:msp430_select_cpu(%{mmcu=*:%*})}}"
 
 extern const char * msp430_select_hwmult_lib (int, const char **);
 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 **);
 
 # define EXTRA_SPEC_FUNCTIONS				\
   { "msp430_hwmult_lib", msp430_select_hwmult_lib },	\
-  { "msp430_select_cpu", msp430_select_cpu },
+  { "msp430_select_cpu", msp430_select_cpu },		\
+  { "msp430_set_driver_var", msp430_set_driver_var },		\
+  { "msp430_check_path_for_devices", msp430_check_path_for_devices },
 
 /* 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 c027201bacd..cbbe0faa6a2 100644
--- a/gcc/config/msp430/msp430.opt
+++ b/gcc/config/msp430/msp430.opt
@@ -14,6 +14,10 @@  mwarn-mcu
 Target Report Var(msp430_warn_mcu) Init(1)
 Warn if an MCU name is unrecognized or conflicts with other options (default: on).
 
+mwarn-devices-csv
+Target Report Var(msp430_warn_devices_csv) Init(1)
+Warn if devices.csv is not found or there are problem parsing it (default: on).
+
 mcpu=
 Target Report Joined RejectNegative Var(target_cpu)
 Specify the ISA to build for: msp430, msp430x, msp430xv2.
@@ -92,3 +96,8 @@  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(msp430_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.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 01aab60f895..b3841aaeb11 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -23040,7 +23040,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 paths specified
+with the @code{-I} and @code{-L} options.
 
 @item -mwarn-mcu
 @itemx -mno-warn-mcu
@@ -23129,6 +23134,13 @@  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 -mwarn-devices-csv
+@itemx -mno-warn-devices-csv
+@opindex mwarn-devices-csv
+@opindex mno-warn-devices-csv
+Warn if @samp{devices.csv} is not found or there are problem parsing it
+(default: on).
+
 @end table
 
 @node NDS32 Options
diff --git a/gcc/testsuite/gcc.target/msp430/devices/README b/gcc/testsuite/gcc.target/msp430/devices/README
index 8d8c8f26db8..9134b4c4320 100644
--- a/gcc/testsuite/gcc.target/msp430/devices/README
+++ b/gcc/testsuite/gcc.target/msp430/devices/README
@@ -10,3 +10,8 @@  The criteria for this torture style of testing is:
 
 Some of the options used to run the tests will produce warnings/errors for the
 mcus, so ensure the test has dg-warning and dg-error directives as appropriate.
+
+The "bad-device-*.c" tests expect a corresponding "bad-devices-*.csv", which
+msp430.exp will pass as the argument to -mdevices-csv-loc=.  These tests are for
+checking the warnings in msp430-devices.c about a corrupted devices.csv work as
+expected.
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.c b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.c
new file mode 100644
index 00000000000..29ef85968d5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00 -mno-warn-mcu" } */
+/* { dg-warning "'CPU_TYPE' column heading is missing from 'devices.csv'" "" { target *-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.csv b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.csv
new file mode 100644
index 00000000000..282fa7bb292
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-1.csv
@@ -0,0 +1,3 @@ 
+# Test warning for missing CPU_TYPE heading
+# Device Name,FOO,MPY_TYPE
+msp430_00,0,0
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.c b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.c
new file mode 100644
index 00000000000..32e5cebd46e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00 -mno-warn-mcu" } */
+/* { dg-warning "'MPY_TYPE' column heading is missing from 'devices.csv'" "" { target *-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.csv b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.csv
new file mode 100644
index 00000000000..3bead68a85e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-2.csv
@@ -0,0 +1,3 @@ 
+# Test warning for missing MPY_TYPE heading
+# Device Name,CPU_TYPE,FOO
+msp430_00,0,0
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.c b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.c
new file mode 100644
index 00000000000..c982c4a7de5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00 -mno-warn-mcu" } */
+/* { dg-warning "'CPU_TYPE' and 'MPY_TYPE' column headings are missing from 'devices.csv'" "" { target *-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.csv b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.csv
new file mode 100644
index 00000000000..63b203005bd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-3.csv
@@ -0,0 +1,3 @@ 
+# Test warning for missing CPU_TYPE and MPY_TYPE headings
+# Device Name,FOO,BAR
+msp430_00,0,0
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.c b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.c
new file mode 100644
index 00000000000..89ebe61d179
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00 -mno-warn-mcu" } */
+/* { dg-warning "format of column headings in 'devices.csv' is incorrect" "" { target *-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.csv b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.csv
new file mode 100644
index 00000000000..4f25f93f288
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-4.csv
@@ -0,0 +1,3 @@ 
+# Test incorrectly formatted column headings
+# DeviceName,CPU_TYPE,MPY_TYPE
+msp430_00,0,0
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.c b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.c
new file mode 100644
index 00000000000..e33bac06b42
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00 -mno-warn-mcu" } */
+/* { dg-warning "invalid 'CPU_TYPE' value of '5' read from 'devices.csv' for 'msp430_00'" "" { target *-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.csv b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.csv
new file mode 100644
index 00000000000..32b70413a22
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-5.csv
@@ -0,0 +1,3 @@ 
+# Test bad CPU_TYPE value
+# Device Name,CPU_TYPE,MPY_TYPE
+msp430_00,5,0
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.c b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.c
new file mode 100644
index 00000000000..9dd9ed9e156
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.c
@@ -0,0 +1,5 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00 -mno-warn-mcu" } */
+/* { dg-warning "invalid 'MPY_TYPE' value of '3' read from 'devices.csv' for 'msp430_00'" "" { target *-*-* } 0 } */
+
+#include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.csv b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.csv
new file mode 100644
index 00000000000..0ec7152c475
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/bad-devices-6.csv
@@ -0,0 +1,3 @@ 
+# Test bad MPY_TYPE value
+# Device Name,CPU_TYPE,MPY_TYPE
+msp430_00,0,3
diff --git a/gcc/testsuite/gcc.target/msp430/devices/csv-device-order.c b/gcc/testsuite/gcc.target/msp430/devices/csv-device-order.c
new file mode 100644
index 00000000000..172880321de
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-device-order.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-skip-if "MCU supports 430 ISA only" { *-*-* } { "-mlarge" "-mcpu=msp430x*" } { "" } } */
+/* { dg-additional-options "-mmcu=msp430f012 -mcpu=msp430 -mhwmult=16bit" } */
+
+/* Test that MCU names in devices.csv are only chosen if the full device name
+   is matched exactly.
+   msp430f0123 (with 430X ISA and f5series hwmult) appears before msp430f012 in
+   devices.csv, but should not be matched.
+   Errors and warnings will be emitted if msp430f0123 is wrongly matched.  */
+
+#include "../devices-main.c"
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 00000000000..8d56873140e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_00.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_00" } */
+/* { dg-warning "does not have hardware multiply" "" { target msp430_hwmul_not_none } 0 } */
+/* { dg-warning "supports 430 ISA but" "" { target msp430_430x_selected } 0 } */
+/* { dg-error "'-mlarge' requires a 430X-compatible '-mmcu='" "" { target msp430_mlarge_selected } 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 00000000000..154511b6fea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_01.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_01" } */
+/* { dg-warning "supports 16-bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+/* { dg-warning "supports 430 ISA but" "" { target msp430_430x_selected } 0 } */
+/* { dg-error "'-mlarge' requires a 430X-compatible '-mmcu='" "" { target msp430_mlarge_selected } 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 00000000000..4c721638903
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_02.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_02" } */
+/* { dg-warning "supports 16-bit hardware multiply" "" { target msp430_hwmul_not_16bit } 0 } */
+/* { dg-warning "supports 430 ISA but" "" { target msp430_430x_selected } 0 } */
+/* { dg-error "'-mlarge' requires a 430X-compatible '-mmcu='" "" { target msp430_mlarge_selected } 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 00000000000..c8ed711eb1d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_04.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_04" } */
+/* { dg-warning "supports 32-bit hardware multiply" "" { target msp430_hwmul_not_32bit } 0 } */
+/* { dg-warning "supports 430 ISA but" "" { target msp430_430x_selected } 0 } */
+/* { dg-error "'-mlarge' requires a 430X-compatible '-mmcu='" "" { target msp430_mlarge_selected } 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 00000000000..0214e636644
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_08.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_08" } */
+/* { dg-warning "supports 32-bit .5xx. hardware multiply" "" { target msp430_hwmul_not_f5 } 0 } */
+/* { dg-warning "supports 430 ISA but" "" { target msp430_430x_selected } 0 } */
+/* { dg-error "'-mlarge' requires a 430X-compatible '-mmcu='" "" { target msp430_mlarge_selected } 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 00000000000..86f89ed51cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_10.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-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 msp430_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 00000000000..2923238e6b8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_11.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_11" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 16-bit 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 00000000000..ed59ac890c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_12.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_12" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 16-bit 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 00000000000..fb038a0d30f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_14.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_14" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 32-bit 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 00000000000..133f984d031
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_18.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_18" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 32-bit .5xx. 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 00000000000..c7c94252b00
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_20.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-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 msp430_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 00000000000..8794f891db7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_21.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_21" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 16-bit 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 00000000000..47d90f4f730
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_22.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_22" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 16-bit 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 00000000000..0a616307e86
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_24.c
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-mmcu=msp430_24" } */
+/* { dg-warning "supports 430X ISA but '-mcpu' option is set to 430" "" { target msp430_430_selected } 0 } */
+/* { dg-warning "supports 32-bit 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 00000000000..1243f7527e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430_28.c
@@ -0,0 +1,6 @@ 
+/* { 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 32-bit .5xx. hardware multiply" "" { target msp430_hwmul_not_f5 } 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 00000000000..636460756ad
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/csv-msp430fr5969.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-additional-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 } */
+/* { dg-warning "supports 430 ISA but" "" { target msp430_430x_selected } 0 } */
+/* { dg-error "'-mlarge' requires a 430X-compatible '-mmcu='" "" { target msp430_mlarge_selected } 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 00000000000..7a13ed9dac4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/msp430/devices/devices.csv
@@ -0,0 +1,22 @@ 
+# Text before "#Device Name" is ignored.
+# We add arbitrary/empty fields in some records after the MPY_TYPE column to get
+# more varied testing.
+# Device Name,CPU_TYPE,MPY_TYPE
+msp430_00,0,0
+msp430_01,0,1,
+msp430_02,0,2,1
+msp430_04,0,4
+msp430_08,0,8,
+msp430_10,1,0,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,4,
+msp430_22,2,2
+msp430_24,2,4
+msp430_28,2,8,100,
+msp430fr5969,0,0
+msp430f0123,2,8
+msp430f012,0,1
diff --git a/gcc/testsuite/gcc.target/msp430/devices/hard-foo.c b/gcc/testsuite/gcc.target/msp430/devices/hard-foo.c
index 802b0413452..e13acb52c50 100644
--- a/gcc/testsuite/gcc.target/msp430/devices/hard-foo.c
+++ b/gcc/testsuite/gcc.target/msp430/devices/hard-foo.c
@@ -1,5 +1,6 @@ 
 /* { dg-do compile } */
 /* { dg-options "-mmcu=msp430foo" } */
+/* { dg-warning "could not locate MCU data file 'devices.csv'" "" { target *-*-* } 0 } */
 /* { dg-warning "Unrecognized MCU name 'msp430foo'.*\n.*Use the" "" { target *-*-* } 0 } */
 
 #include "../devices-main.c"
diff --git a/gcc/testsuite/gcc.target/msp430/msp430.exp b/gcc/testsuite/gcc.target/msp430/msp430.exp
index 20bfc149bd7..b97f4dcdc16 100644
--- a/gcc/testsuite/gcc.target/msp430/msp430.exp
+++ b/gcc/testsuite/gcc.target/msp430/msp430.exp
@@ -94,7 +94,21 @@  proc msp430_device_permutations_runtest { tests } {
 	    continue
 	}
 	foreach { mcu_flags } [msp430_get_opts $test_file] {
-	    dg-runtest $test_file "$mcu_flags" "$MSP430_DEFAULT_CFLAGS"
+	    if { [string match "csv-*" [file tail $test_file]] } {
+		# Specify the path to devices.csv for devices/csv-* tests with -I.
+		# Note that the csv-* tests do not have dg-options directives,
+		# they only have dg-additional-options to pass -mmcu.  This is
+		# so we can set the path to devices.csv as a "default" flag
+		# with -I, and the path won't show up in the test results
+		# summary.  If there were dg-options directives, then these
+		# default flags passed as the 3rd argument to dg-runtest would
+		# not be used.
+		dg-runtest $test_file "$mcu_flags" "-I[file dirname $test_file] $MSP430_DEFAULT_CFLAGS"
+	    } 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"
+	    }
 	}
     }
 }
-- 
2.17.1