Add Pigweed GCC-ARM toolchains

Updates ARM toolchains and gets both the stm32f429i-disc1 and qemu
targets building again.

Change-Id: Ib0607bc498f8a331c3cde763da5ed3812ba595ba
diff --git a/BUILD.gn b/BUILD.gn
index 609ec95..15ad366 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -42,6 +42,8 @@
   deps = [
     ":pigweed_default($_host_target_toolchain)",
     ":pigweed_default($dir_pigweed/targets/docs)",
+    ":pigweed_default($dir_pigweed/targets/lm3s6965evb-qemu:lm3s6965evb_qemu_debug)",
+    ":pigweed_default($dir_pigweed/targets/stm32f429i-disc1:stm32f429i_disc1_debug)",
   ]
 }
 
diff --git a/pw_boot_armv7m/BUILD.gn b/pw_boot_armv7m/BUILD.gn
index 7857557..7ca4082 100644
--- a/pw_boot_armv7m/BUILD.gn
+++ b/pw_boot_armv7m/BUILD.gn
@@ -20,14 +20,16 @@
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_docgen/docs.gni")
 declare_args() {
-  # TODO(frolv): Move this into the pw_boot module.
+  # TODO(frolv): Move this into pw_boot module when it is created.
   pw_boot_BACKEND = ""
 
-  # Module configuration options for pw_boot_armv7m.
-  pw_boot_armv7m_CONFIG = {
-    # C Preprocessor defines used for linker script configuration.
-    defines = []
-  }
+  # Whether or not to include code that signals to QEMU to shut down the
+  # emulator. This should only be enabled on QEMU targets.
+  pw_boot_armv7m_QEMU_SHUTDOWN = false
+
+  # This list should contain the necessary defines for setting pw_boot linker
+  # script memory regions.
+  pw_boot_armv7m_LINK_CONFIG_DEFINES = []
 }
 
 if (pw_boot_BACKEND == dir_pw_boot_armv7m) {
@@ -36,8 +38,9 @@
   }
 
   pw_linker_script("armv7m_linker_script") {
-    # pw_boot_armv7m_CONFIG is a scope provided by the target.
-    defines = pw_boot_armv7m_CONFIG.defines
+    # pw_boot_armv7m_LINK_CONFIG_DEFINES is a list of defines provided by the
+    # target.
+    defines = pw_boot_armv7m_LINK_CONFIG_DEFINES
     linker_script = "basic_armv7m.ld"
   }
 
@@ -47,7 +50,7 @@
       ":armv7m_linker_script",
       "$dir_pw_preprocessor",
     ]
-    if (defined(pw_boot_armv7m_qemu_shutdown) && pw_boot_armv7m_qemu_shutdown) {
+    if (pw_boot_armv7m_QEMU_SHUTDOWN) {
       defines = [ "PW_BOOT_ARMV7M_QEMU_SHUTDOWN=1" ]
     }
     public = [ "public/pw_boot_armv7m/boot.h" ]
diff --git a/pw_build/linker_script.gni b/pw_build/linker_script.gni
index 3ab0527..c6e0360 100644
--- a/pw_build/linker_script.gni
+++ b/pw_build/linker_script.gni
@@ -18,13 +18,10 @@
 import("$dir_pigweed/legacy_target.gni")
 import("$dir_pw_build/exec.gni")
 import("$dir_pw_build/target_types.gni")
+import("$dir_pw_toolchain/generate_toolchain.gni")
 
 # Preprocess a linker script and turn it into a target.
 #
-# Note: to use this template, pw_cc_command must be specified in your target's
-# config. It should match the name of the C compiler your target uses (e.g.
-# arm-none-eabi-gcc).
-#
 # In lieu of direct GN support for linker scripts, this template makes it
 # possible to run the C Preprocessor on a linker script file so defines can
 # be properly evaluated before the linker script is passed to the dir_pw_build
@@ -54,11 +51,6 @@
 #
 template("pw_linker_script") {
   assert(
-      defined(pw_cc_command) && pw_cc_command != "",
-      "pw_cc_command has not been properly configured. This variable must be " +
-          "defined to enable linker script preprocessing.")
-
-  assert(
       defined(invoker.linker_script) && invoker.linker_script != "",
       "$target_name did not set `linker_script` to refer to a valid linker " +
           "script. This variable is required for linker script targets.")
@@ -68,7 +60,7 @@
   # This action invokes the C compiler provided by the target to preprocess the
   # linker script.
   pw_exec("${target_name}_preprocess") {
-    program = pw_cc_command
+    program = pw_toolchain_SCOPE.cxx
     inputs = [ invoker.linker_script ]
     args = [
       # Run compiler in preprocessor-only mode.
diff --git a/pw_build/target_types.gni b/pw_build/target_types.gni
index f714f22..8a934e6 100644
--- a/pw_build/target_types.gni
+++ b/pw_build/target_types.gni
@@ -21,6 +21,17 @@
   # be imported at the top of the target configuration file to make it globally
   # available.
   pw_build_EXECUTABLE_TARGET_TYPE = "executable"
+
+  # The path to the .gni file that defines pw_build_EXECUTABLE_TARGET_TYPE.
+  #
+  # If pw_build_EXECUTABLE_TARGET_TYPE is not the default of `executable`, this
+  # .gni file is imported to provide the template definition.
+  pw_build_EXECUTABLE_TARGET_TYPE_FILE = ""
+}
+
+if (pw_build_EXECUTABLE_TARGET_TYPE != "executable" &&
+    pw_build_EXECUTABLE_TARGET_TYPE_FILE != "") {
+  import(pw_build_EXECUTABLE_TARGET_TYPE_FILE)
 }
 
 # TODO(frolv): The code in all of the templates below is duplicated, with the
diff --git a/pw_cpu_exception_armv7m/BUILD.gn b/pw_cpu_exception_armv7m/BUILD.gn
index 186fcb4..0148248 100644
--- a/pw_cpu_exception_armv7m/BUILD.gn
+++ b/pw_cpu_exception_armv7m/BUILD.gn
@@ -45,9 +45,6 @@
     "$dir_pw_cpu_exception",
   ]
   sources = [ "exception_entry_test.cc" ]
-
-  # TODO(frolv): Fix this test on the QEMU target.
-  enable_if = pw_build_EXECUTABLE_TARGET_TYPE != "lm3s6965evb_executable"
 }
 
 pw_doc_group("docs") {
diff --git a/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn b/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn
index 8abfbf1..15255e2 100644
--- a/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn
+++ b/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn
@@ -18,20 +18,15 @@
 import("$dir_pigweed/legacy_target.gni")
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_docgen/docs.gni")
-
-# This if statement allows docs to always build even if the target isn't
-# compatible with this backend.
-if (dir_pw_sys_io_backend == dir_pw_sys_io_baremetal_lm3s6965evb) {
-  pw_source_set("pw_sys_io_baremetal_lm3s6965evb") {
-    public_deps = [ "$dir_pw_boot_armv7m" ]
-    deps = [
-      "$dir_pw_preprocessor",
-      "$dir_pw_sys_io:default_putget_bytes",
-      "$dir_pw_sys_io:facade",
-    ]
-    sources = [
-      "early_boot.c",
-      "sys_io_baremetal.cc",
-    ]
-  }
+pw_source_set("pw_sys_io_baremetal_lm3s6965evb") {
+  public_deps = [ "$dir_pw_boot_armv7m" ]
+  deps = [
+    "$dir_pw_preprocessor",
+    "$dir_pw_sys_io:default_putget_bytes",
+    "$dir_pw_sys_io:facade",
+  ]
+  sources = [
+    "early_boot.c",
+    "sys_io_baremetal.cc",
+  ]
 }
diff --git a/pw_toolchain/BUILD.gn b/pw_toolchain/BUILD.gn
index 001c986..f74d905 100644
--- a/pw_toolchain/BUILD.gn
+++ b/pw_toolchain/BUILD.gn
@@ -29,7 +29,7 @@
 
 # Generate ARM GCC toolchains
 generate_toolchains("arm_gcc_suite") {
-  toolchains = arm_gcc_toolchains
+  toolchains = pw_toolchain_arm_gcc_list
 }
 
 # Generate Host GCC toolchains
diff --git a/pw_toolchain/arm_gcc/BUILD.gn b/pw_toolchain/arm_gcc/BUILD.gn
index 6e686ed..fcf5d18 100644
--- a/pw_toolchain/arm_gcc/BUILD.gn
+++ b/pw_toolchain/arm_gcc/BUILD.gn
@@ -41,7 +41,7 @@
     "-mthumb",
     "-specs=nano.specs",
     "-specs=nosys.specs",
-    "-u _printf_float",
+    "-u_printf_float",
   ]
   ldflags = cflags + [
               "-lnosys",
diff --git a/pw_toolchain/arm_gcc/toolchains.gni b/pw_toolchain/arm_gcc/toolchains.gni
index fe90a67..beb2509 100644
--- a/pw_toolchain/arm_gcc/toolchains.gni
+++ b/pw_toolchain/arm_gcc/toolchains.gni
@@ -48,107 +48,93 @@
   "$dir_pw_toolchain/arm_gcc:cortex_hardware_fpu",
 ]
 
-arm_gcc_cortex_m4_os = {
-  name = "arm_gcc_cortex_m4_os"
-  forward_variables_from(arm_gcc_toolchain_tools, "*")
-  defaults = {
-    default_configs = _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_size" ]
-  }
-}
-
 # Describes ARM GCC toolchains for specific targets.
-arm_gcc_toolchains = [
-  {
-    name = "arm_gcc_cortex_m3_og"
+pw_toolchain_arm_gcc = {
+  cortex_m3_debug = {
+    name = "arm_gcc_cortex_m3_debug"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize_debugging" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m3_o1"
-    forward_variables_from(arm_gcc_toolchain_tools, "*")
-    defaults = {
-      default_configs = _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize" ]
-    }
-  },
-  {
-    name = "arm_gcc_cortex_m3_o2"
+  }
+  cortex_m3_speed_optimized = {
+    name = "arm_gcc_cortex_m3_speed_optimized"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize_speed" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m3_os"
+  }
+  cortex_m3_size_optimized = {
+    name = "arm_gcc_cortex_m3_size_optimized"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m3 + [ "$dir_pw_build:optimize_size" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m4_og"
+  }
+  cortex_m4_debug = {
+    name = "arm_gcc_cortex_m4_debug"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_debugging" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m4_o1"
-    forward_variables_from(arm_gcc_toolchain_tools, "*")
-    defaults = {
-      default_configs = _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize" ]
-    }
-  },
-  {
-    name = "arm_gcc_cortex_m4_o2"
+  }
+  cortex_m4_speed_optimized = {
+    name = "arm_gcc_cortex_m4_speed_optimized"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_speed" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m4_os"
+  }
+  cortex_m4_size_optimized = {
+    name = "arm_gcc_cortex_m4_size_optimized"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m4 + [ "$dir_pw_build:optimize_size" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m4f_og"
+  }
+  cortex_m4f_debug = {
+    name = "arm_gcc_cortex_m4f_debug"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize_debugging" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m4f_o1"
-    forward_variables_from(arm_gcc_toolchain_tools, "*")
-    defaults = {
-      default_configs = _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize" ]
-    }
-  },
-  {
-    name = "arm_gcc_cortex_m4f_o2"
+  }
+  cortex_m4f_speed_optimized = {
+    name = "arm_gcc_cortex_m4f_speed_optimized"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize_speed" ]
     }
-  },
-  {
-    name = "arm_gcc_cortex_m4f_os"
+  }
+  cortex_m4f_size_optimized = {
+    name = "arm_gcc_cortex_m4f_size_optimized"
     forward_variables_from(arm_gcc_toolchain_tools, "*")
     defaults = {
       default_configs =
           _arm_gcc + _cortex_m4f + [ "$dir_pw_build:optimize_size" ]
     }
-  },
+  }
+}
+
+# This list just contains the members of the above scope for convenience to make
+# it trivial to generate all the toolchains in this file via a
+# `generate_toolchains` target.
+pw_toolchain_arm_gcc_list = [
+  pw_toolchain_arm_gcc.cortex_m3_debug,
+  pw_toolchain_arm_gcc.cortex_m3_speed_optimized,
+  pw_toolchain_arm_gcc.cortex_m3_size_optimized,
+  pw_toolchain_arm_gcc.cortex_m4_debug,
+  pw_toolchain_arm_gcc.cortex_m4_speed_optimized,
+  pw_toolchain_arm_gcc.cortex_m4_size_optimized,
+  pw_toolchain_arm_gcc.cortex_m4f_debug,
+  pw_toolchain_arm_gcc.cortex_m4f_speed_optimized,
+  pw_toolchain_arm_gcc.cortex_m4f_size_optimized,
 ]
diff --git a/targets/docs/BUILD.gn b/targets/docs/BUILD.gn
index 9f1f394..72b68fe 100644
--- a/targets/docs/BUILD.gn
+++ b/targets/docs/BUILD.gn
@@ -22,14 +22,15 @@
 # Toolchain for generating upstream Pigweed documentation.
 generate_toolchain("docs") {
   # Use the Cortex M4 toolchain for regular pw_size_report targets.
-  forward_variables_from(arm_gcc_cortex_m4_os,
+  forward_variables_from(pw_toolchain_arm_gcc.cortex_m4_size_optimized,
                          "*",
                          [
                            "defaults",
                            "name",
                          ])
   defaults = {
-    forward_variables_from(arm_gcc_cortex_m4_os.defaults, "*")
+    _base_toolchain = pw_toolchain_arm_gcc.cortex_m4_size_optimized
+    forward_variables_from(_base_toolchain.defaults, "*")
 
     # This is the docs target.
     pw_docgen_BUILD_DOCS = true
@@ -42,17 +43,17 @@
     pw_bloat_TOOLCHAINS = [
       {
         name = "arm-none-eabi-gcc -Og -mcpu=cortex-m4"
-        target = "$dir_pw_toolchain:arm_gcc_cortex_m4_og"
+        target = "$dir_pw_toolchain:arm_gcc_cortex_m4_debug"
         bloaty_config = _arm_bloaty_config
       },
       {
         name = "arm-none-eabi-gcc -Os -mcpu=cortex-m4"
-        target = "$dir_pw_toolchain:arm_gcc_cortex_m4_os"
+        target = "$dir_pw_toolchain:arm_gcc_cortex_m4_size_optimized"
         bloaty_config = _arm_bloaty_config
       },
       {
         name = "arm-none-eabi-gcc -O2 -mcpu=cortex-m4"
-        target = "$dir_pw_toolchain:arm_gcc_cortex_m4_o2"
+        target = "$dir_pw_toolchain:arm_gcc_cortex_m4_speed_optimized"
         bloaty_config = _arm_bloaty_config
       },
     ]
diff --git a/targets/lm3s6965evb-qemu/BUILD.gn b/targets/lm3s6965evb-qemu/BUILD.gn
index 82d2f30..6162263 100644
--- a/targets/lm3s6965evb-qemu/BUILD.gn
+++ b/targets/lm3s6965evb-qemu/BUILD.gn
@@ -17,6 +17,12 @@
 
 import("$dir_pigweed/legacy_target.gni")
 import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_toolchain/generate_toolchain.gni")
+import("target_toolchains.gni")
+generate_toolchains("target_toolchains") {
+  toolchains = pw_target_toolchain_lm3s6965evb_qemu_list
+}
+
 pw_doc_group("target_docs") {
   sources = [ "target_docs.rst" ]
 }
diff --git a/targets/lm3s6965evb-qemu/lm3s6965evb_executable.gni b/targets/lm3s6965evb-qemu/lm3s6965evb_executable.gni
new file mode 100644
index 0000000..7576fce
--- /dev/null
+++ b/targets/lm3s6965evb-qemu/lm3s6965evb_executable.gni
@@ -0,0 +1,27 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+# Executable wrapper that includes some baremetal startup code.
+template("lm3s6965evb_executable") {
+  target("executable", target_name) {
+    forward_variables_from(invoker, "*")
+    if (!defined(deps)) {
+      deps = []
+    }
+    deps += [ dir_pw_sys_io_baremetal_lm3s6965evb ]
+  }
+}
diff --git a/targets/lm3s6965evb-qemu/target_config.gni b/targets/lm3s6965evb-qemu/target_config.gni
deleted file mode 100644
index fd83ea3..0000000
--- a/targets/lm3s6965evb-qemu/target_config.gni
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2020 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-# Target configuration for the lm3s6965evb development board, run using QEMU.
-#
-# TODO(amontanez): This target configuration treats
-# pw_sys_io_baremetal_lm3s6965evb as if it were a platform. This is for
-# testing/development and should eventually point to something more
-# sophisticated.
-
-# Get location of dir_pigweed.
-import("//build_overrides/pigweed.gni")
-
-import("$dir_pigweed/pw_vars_default.gni")
-declare_args() {
-  # Specifies the toolchain to use for this build.
-  pw_target_toolchain = "$dir_pw_toolchain:arm_gcc_cortex_m3_og"
-}
-
-# Use the logging main.
-pw_unit_test_main = "$dir_pw_unit_test:logging_main"
-
-# Expose the tool to use for preprocessing linker scripts.
-# TODO(pwbug/53): Temporary, will be removed when proper linker script support
-# is added to GN.
-pw_cc_command = "arm-none-eabi-gcc"
-
-# Executable wrapper that includes some baremetal startup code.
-template("lm3s6965evb_executable") {
-  target("executable", target_name) {
-    forward_variables_from(invoker, "*")
-    if (!defined(deps)) {
-      deps = []
-    }
-    deps += [ dir_pw_sys_io_baremetal_lm3s6965evb ]
-  }
-}
-
-# Configuration options for Pigweed executable targets.
-pw_executable_config.target_type = "lm3s6965evb_executable"
-
-pw_automatic_test_runner =
-    get_path_info("py/lm3s6965evb_qemu_utils/unit_test_runner.py", "abspath")
-
-# Path to the bloaty config file for the output binaries.
-pw_executable_config.bloaty_config_file =
-    "$dir_pw_boot_armv7m/bloaty_config.bloaty"
-
-# Facade backends
-dir_pw_assert_backend = dir_pw_assert_basic
-dir_pw_boot_backend = dir_pw_boot_armv7m
-dir_pw_log_backend = dir_pw_log_basic
-dir_pw_sys_io_backend = dir_pw_sys_io_baremetal_lm3s6965evb
-
-# pw_cpu_exception_armv7m test do not work as expected in QEMU. It does not
-# appear the divide-by-zero traps as expected when enabled, which prevents the
-# module from triggering a recoverable exception. Since pw_cpu_exception is not
-# fully set up on this target, disable it for now.
-# dir_pw_cpu_exception_backend = dir_pw_cpu_exception_armv7m
-
-# Tell QEMU to shut down after running a binary.
-pw_boot_armv7m_qemu_shutdown = true
-
-pw_boot_armv7m_config.defines += [
-  "PW_BOOT_FLASH_BEGIN=0x00000200",
-  "PW_BOOT_FLASH_SIZE=255K",
-  "PW_BOOT_HEAP_SIZE=0",
-  "PW_BOOT_MIN_STACK_SIZE=1K",
-  "PW_BOOT_RAM_BEGIN=0x20000000",
-  "PW_BOOT_RAM_SIZE=64K",
-  "PW_BOOT_VECTOR_TABLE_BEGIN=0x00000000",
-  "PW_BOOT_VECTOR_TABLE_SIZE=512",
-]
diff --git a/targets/lm3s6965evb-qemu/target_toolchains.gni b/targets/lm3s6965evb-qemu/target_toolchains.gni
new file mode 100644
index 0000000..f2e9937
--- /dev/null
+++ b/targets/lm3s6965evb-qemu/target_toolchains.gni
@@ -0,0 +1,107 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_toolchain/arm_gcc/toolchains.gni")
+_test_runner_script = "py/lm3s6965evb_qemu_utils/unit_test_runner.py"
+
+_target_config = {
+  # Use the logging main.
+  pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main"
+
+  # Configuration options for Pigweed executable targets.
+  pw_build_EXECUTABLE_TARGET_TYPE = "lm3s6965evb_executable"
+
+  pw_build_EXECUTABLE_TARGET_TYPE_FILE =
+      get_path_info("lm3s6965evb_executable.gni", "abspath")
+
+  # Path to the bloaty config file for the output binaries.
+  pw_bloat_BLOATY_CONFIG = "$dir_pw_boot_armv7m/bloaty_config.bloaty"
+
+  pw_unit_test_AUTOMATIC_RUNNER = get_path_info(_test_runner_script, "abspath")
+
+  # Tell QEMU to shut down after running a binary.
+  pw_boot_armv7m_QEMU_SHUTDOWN = true
+
+  # Facade backends
+  pw_assert_BACKEND = dir_pw_assert_basic
+  pw_boot_BACKEND = dir_pw_boot_armv7m
+  pw_log_BACKEND = dir_pw_log_basic
+  pw_sys_io_BACKEND = dir_pw_sys_io_baremetal_lm3s6965evb
+
+  # pw_cpu_exception_armv7m tests do not work as expected in QEMU. It does not
+  # appear the divide-by-zero traps as expected when enabled, which prevents the
+  # module from triggering a recoverable exception. Since pw_cpu_exception is
+  # not fully set up on this target, disable it for now.
+  # pw_cpu_exception_BACKEND = dir_pw_cpu_exception_armv7m
+
+  pw_boot_armv7m_LINK_CONFIG_DEFINES = [
+    "PW_BOOT_FLASH_BEGIN=0x00000200",
+    "PW_BOOT_FLASH_SIZE=255K",
+    "PW_BOOT_HEAP_SIZE=0",
+    "PW_BOOT_MIN_STACK_SIZE=1K",
+    "PW_BOOT_RAM_BEGIN=0x20000000",
+    "PW_BOOT_RAM_SIZE=64K",
+    "PW_BOOT_VECTOR_TABLE_BEGIN=0x00000000",
+    "PW_BOOT_VECTOR_TABLE_SIZE=512",
+  ]
+}
+
+pw_target_toolchain_lm3s6965evb_qemu = {
+  _excluded_members = [
+    "defaults",
+    "name",
+  ]
+
+  debug = {
+    name = "lm3s6965evb_qemu_debug"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m3_debug
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+    }
+  }
+
+  speed_optimized = {
+    name = "lm3s6965evb_qemu_speed_optimized"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m3_speed_optimized
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+    }
+  }
+
+  size_optimized = {
+    name = "lm3s6965evb_qemu_size_optimized"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m3_size_optimized
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+    }
+  }
+}
+
+# This list just contains the members of the above scope for convenience to make
+# it trivial to generate all the toolchains in this file via a
+# `generate_toolchains` target.
+pw_target_toolchain_lm3s6965evb_qemu_list = [
+  pw_target_toolchain_lm3s6965evb_qemu.debug,
+  pw_target_toolchain_lm3s6965evb_qemu.speed_optimized,
+  pw_target_toolchain_lm3s6965evb_qemu.size_optimized,
+]
diff --git a/targets/stm32f429i-disc1/BUILD.gn b/targets/stm32f429i-disc1/BUILD.gn
index 4e2507c..c9313fc 100644
--- a/targets/stm32f429i-disc1/BUILD.gn
+++ b/targets/stm32f429i-disc1/BUILD.gn
@@ -17,6 +17,12 @@
 
 import("$dir_pigweed/legacy_target.gni")
 import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_toolchain/generate_toolchain.gni")
+import("target_toolchains.gni")
+generate_toolchains("target_toolchains") {
+  toolchains = pw_target_toolchain_stm32f429i_disc1_list
+}
+
 pw_doc_group("target_docs") {
   sources = [ "target_docs.rst" ]
 }
diff --git a/targets/stm32f429i-disc1/stm32f429i_executable.gni b/targets/stm32f429i-disc1/stm32f429i_executable.gni
new file mode 100644
index 0000000..1600761
--- /dev/null
+++ b/targets/stm32f429i-disc1/stm32f429i_executable.gni
@@ -0,0 +1,27 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+# Executable wrapper that includes some baremetal startup code.
+template("stm32f429i_executable") {
+  target("executable", target_name) {
+    forward_variables_from(invoker, "*")
+    if (!defined(deps)) {
+      deps = []
+    }
+    deps += [ dir_pw_sys_io_baremetal_stm32f429 ]
+  }
+}
diff --git a/targets/stm32f429i-disc1/target_config.gni b/targets/stm32f429i-disc1/target_config.gni
deleted file mode 100644
index 2d03d00..0000000
--- a/targets/stm32f429i-disc1/target_config.gni
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2019 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-# Target configuration for the STM32f429I-DISC1 development board.
-#
-# TODO(amontanez): This target configuration treats
-# pw_sys_io_baremetal_stm32f429 as if it were a platform. This is for
-# testing/development and should eventually point to something more
-# sophisticated.
-
-# Get location of dir_pigweed.
-import("//build_overrides/pigweed.gni")
-import("$dir_pigweed/pw_vars_default.gni")
-declare_args() {
-  # Specifies the toolchain to use for this build.
-  pw_target_toolchain = "$dir_pw_toolchain:arm_gcc_cortex_m4f_og"
-
-  # Experimental! Enable the pw_target_runner for distributed testing.
-  pw_use_test_server = false
-}
-
-# Use the logging main.
-pw_unit_test_main = "$dir_pw_unit_test:logging_main"
-
-# Expose the tool to use for preprocessing linker scripts.
-# TODO(pwbug/53): Temporary, will be removed when proper linker script support
-# is added to GN.
-pw_cc_command = "arm-none-eabi-gcc"
-
-# Executable wrapper that includes some baremetal startup code.
-template("stm32f429i_executable") {
-  target("executable", target_name) {
-    forward_variables_from(invoker, "*")
-    if (!defined(deps)) {
-      deps = []
-    }
-    deps += [ dir_pw_sys_io_baremetal_stm32f429 ]
-  }
-}
-
-# Configuration options for Pigweed executable targets.
-pw_executable_config.target_type = "stm32f429i_executable"
-
-# Path to the bloaty config file for the output binaries.
-pw_executable_config.bloaty_config_file =
-    "$dir_pw_boot_armv7m/bloaty_config.bloaty"
-
-if (pw_use_test_server) {
-  pw_automatic_test_runner =
-      get_path_info("py/stm32f429i_disc1_utils/unit_test_client.py", "abspath")
-}
-
-# Facade backends
-dir_pw_assert_backend = dir_pw_assert_basic
-dir_pw_boot_backend = dir_pw_boot_armv7m
-dir_pw_cpu_exception_backend = dir_pw_cpu_exception_armv7m
-dir_pw_log_backend = dir_pw_log_basic
-dir_pw_sys_io_backend = dir_pw_sys_io_baremetal_stm32f429
-
-pw_boot_armv7m_config.defines += [
-  "PW_BOOT_FLASH_BEGIN=0x08000200",
-  "PW_BOOT_FLASH_SIZE=512K",
-  "PW_BOOT_HEAP_SIZE=0",
-  "PW_BOOT_MIN_STACK_SIZE=1K",
-  "PW_BOOT_RAM_BEGIN=0x20000000",
-  "PW_BOOT_RAM_SIZE=192K",
-  "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000",
-  "PW_BOOT_VECTOR_TABLE_SIZE=512",
-]
diff --git a/targets/stm32f429i-disc1/target_toolchains.gni b/targets/stm32f429i-disc1/target_toolchains.gni
new file mode 100644
index 0000000..320e8ed
--- /dev/null
+++ b/targets/stm32f429i-disc1/target_toolchains.gni
@@ -0,0 +1,106 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_toolchain/arm_gcc/toolchains.gni")
+declare_args() {
+  # Enable the pw_target_runner for on-device testing.
+  pw_use_test_server = false
+}
+
+_target_config = {
+  # Use the logging main.
+  pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main"
+
+  # Configuration options for Pigweed executable targets.
+  pw_build_EXECUTABLE_TARGET_TYPE = "stm32f429i_executable"
+
+  pw_build_EXECUTABLE_TARGET_TYPE_FILE =
+      get_path_info("stm32f429i_executable.gni", "abspath")
+
+  # Path to the bloaty config file for the output binaries.
+  pw_bloat_BLOATY_CONFIG = "$dir_pw_boot_armv7m/bloaty_config.bloaty"
+
+  if (pw_use_test_server) {
+    _test_runner_script = "py/stm32f429i_disc1_utils/unit_test_client.py"
+    pw_unit_test_AUTOMATIC_RUNNER =
+        get_path_info(_test_runner_script, "abspath")
+  }
+
+  # Facade backends
+  pw_assert_BACKEND = dir_pw_assert_basic
+  pw_boot_BACKEND = dir_pw_boot_armv7m
+  pw_cpu_exception_BACKEND = dir_pw_cpu_exception_armv7m
+  pw_log_BACKEND = dir_pw_log_basic
+  pw_sys_io_BACKEND = dir_pw_sys_io_baremetal_stm32f429
+
+  pw_boot_armv7m_LINK_CONFIG_DEFINES = [
+    "PW_BOOT_FLASH_BEGIN=0x08000200",
+    "PW_BOOT_FLASH_SIZE=512K",
+    "PW_BOOT_HEAP_SIZE=0",
+    "PW_BOOT_MIN_STACK_SIZE=1K",
+    "PW_BOOT_RAM_BEGIN=0x20000000",
+    "PW_BOOT_RAM_SIZE=192K",
+    "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000",
+    "PW_BOOT_VECTOR_TABLE_SIZE=512",
+  ]
+}
+
+pw_target_toolchain_stm32f429i_disc1 = {
+  _excluded_members = [
+    "defaults",
+    "name",
+  ]
+
+  debug = {
+    name = "stm32f429i_disc1_debug"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m4f_debug
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+    }
+  }
+
+  speed_optimized = {
+    name = "stm32f429i_disc1_speed_optimized"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m4f_speed_optimized
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+    }
+  }
+
+  size_optimized = {
+    name = "stm32f429i_disc1_size_optimized"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m4f_size_optimized
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+    }
+  }
+}
+
+# This list just contains the members of the above scope for convenience to make
+# it trivial to generate all the toolchains in this file via a
+# `generate_toolchains` target.
+pw_target_toolchain_stm32f429i_disc1_list = [
+  pw_target_toolchain_stm32f429i_disc1.debug,
+  pw_target_toolchain_stm32f429i_disc1.speed_optimized,
+  pw_target_toolchain_stm32f429i_disc1.size_optimized,
+]