[stm32] Add new target for stm32f429i-disc1 w/ HAL

This is a new target for the discovery board based on the in-tree
target but with stm32cubef4 HAL support.

The main differences are that it uses STM's code for initializing the
FPU, reclocks the board to its 180Mhz maximum, and uses the stm32cubef4
backends for blinky.

It does not yet support the test runner, but the existing one in-tree
should work with all STM32F4xx boards that have a ST-Link/v2.1 with
minimal changes.

Change-Id: I2bdd17d5958b5204bf9d2b44909bd8bd1e25402c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/39770
Commit-Queue: Varun Sharma <vars@google.com>
Reviewed-by: Ali Zhang <alizhang@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 04eabc5..a4bca9e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -93,6 +93,12 @@
     "//applications/strings:all(//targets/stm32f429i-disc1:stm32f429i_disc1_debug)",
   ]
 
+  # STMicroelectronics STM32F429I-DISC1 STM32Cube applications steps.
+  deps += [
+    ":applications_tests(//targets/stm32f429i-disc1-stm32cube:stm32f429i_disc1_stm32cube_debug)",
+    "//applications/blinky:blinky(//targets/stm32f429i-disc1-stm32cube:stm32f429i_disc1_stm32cube_debug)",
+  ]
+
   # Host applications steps.
   deps += [
     ":applications_tests(//targets/host:host_debug_tests)",
diff --git a/targets/stm32f429i-disc1-stm32cube/BUILD.gn b/targets/stm32f429i-disc1-stm32cube/BUILD.gn
new file mode 100644
index 0000000..56301c1
--- /dev/null
+++ b/targets/stm32f429i-disc1-stm32cube/BUILD.gn
@@ -0,0 +1,54 @@
+# Copyright 2021 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_malloc/backend.gni")
+import("$dir_pw_toolchain/generate_toolchain.gni")
+import("target_toolchains.gni")
+
+generate_toolchains("target_toolchains") {
+  toolchains = pw_target_toolchain_stm32f429i_disc1_list
+}
+
+config("pw_malloc_active") {
+  if (pw_malloc_BACKEND != "") {
+    defines = [ "PW_MALLOC_ACTIVE=1" ]
+  }
+}
+
+if (current_toolchain != default_toolchain) {
+  pw_source_set("pre_init") {
+    configs = [ ":pw_malloc_active" ]
+    public_deps = [
+      "$dir_pw_boot_armv7m",
+      "$dir_pw_sys_io_stm32cubef4",
+    ]
+    deps = [
+      "$dir_pw_malloc",
+      "$dir_pw_preprocessor",
+      "//third_party/stm32cubef4:stm32f4xx_hal",
+    ]
+    sources = [
+      "boot.cc",
+      "vector_table.c",
+    ]
+  }
+}
+
+pw_doc_group("target_docs") {
+  sources = [ "target_docs.rst" ]
+}
diff --git a/targets/stm32f429i-disc1-stm32cube/README.md b/targets/stm32f429i-disc1-stm32cube/README.md
new file mode 100644
index 0000000..b45f276
--- /dev/null
+++ b/targets/stm32f429i-disc1-stm32cube/README.md
@@ -0,0 +1,9 @@
+##Flashing
+
+Images can be flashed using the same scripts as the in-tree variant.
+
+This command can be used to flash the blinky example:
+
+```
+openocd -s ${PW_PIGWEED_CIPD_INSTALL_DIR}/share/openocd/scripts -f ${PW_ROOT}/targets/stm32f429i-disc1/py/stm32f429i_disc1_utils/openocd_stm32f4xx.cfg -c "program out/stm32f429i_disc1_stm32cube_debug/obj/applications/blinky/bin/blinky.elf reset exit"
+```
diff --git a/targets/stm32f429i-disc1-stm32cube/boot.cc b/targets/stm32f429i-disc1-stm32cube/boot.cc
new file mode 100644
index 0000000..890ec1f
--- /dev/null
+++ b/targets/stm32f429i-disc1-stm32cube/boot.cc
@@ -0,0 +1,78 @@
+// Copyright 2021 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.
+
+#include "pw_boot_armv7m/boot.h"
+
+#include "pw_malloc/malloc.h"
+#include "pw_preprocessor/compiler.h"
+#include "pw_sys_io_stm32cubef4/init.h"
+#include "stm32f4xx.h"
+
+void pw_boot_PreStaticMemoryInit() { SystemInit(); }
+
+void pw_boot_PreStaticConstructorInit() {
+#if PW_MALLOC_ACTIVE
+  pw_MallocInit();
+#endif  // PW_MALLOC_ACTIVE
+}
+
+// Initializes clock to its max, 180Mhz
+static void ClockInit() {
+  RCC_OscInitTypeDef RCC_OscInitStruct = {};
+  RCC_ClkInitTypeDef RCC_ClkInitStruct = {};
+
+  __HAL_RCC_PWR_CLK_ENABLE();
+  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
+
+  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
+  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+  RCC_OscInitStruct.PLL.PLLM = 4;
+  RCC_OscInitStruct.PLL.PLLN = 180;
+  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
+  RCC_OscInitStruct.PLL.PLLQ = 8;
+  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
+    pw_boot_PostMain();
+  }
+
+  // OverDrive required for operation > 168Mhz
+  if (HAL_PWREx_EnableOverDrive() != HAL_OK) {
+    pw_boot_PostMain();
+  }
+
+  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
+                                RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
+  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
+  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
+
+  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
+    pw_boot_PostMain();
+  }
+}
+
+void pw_boot_PreMainInit() {
+  HAL_Init();
+  ClockInit();
+  pw_sys_io_Init();
+}
+
+PW_NO_RETURN void pw_boot_PostMain() {
+  // In case main() returns, just sit here until the device is reset.
+  while (true) {
+  }
+  PW_UNREACHABLE;
+}
diff --git a/targets/stm32f429i-disc1-stm32cube/stm32f429i_executable.gni b/targets/stm32f429i-disc1-stm32cube/stm32f429i_executable.gni
new file mode 100644
index 0000000..ed59c8a
--- /dev/null
+++ b/targets/stm32f429i-disc1-stm32cube/stm32f429i_executable.gni
@@ -0,0 +1,33 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+import("$dir_pw_malloc/backend.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 += [ "//targets/stm32f429i-disc1-stm32cube:pre_init" ]
+    if (pw_malloc_BACKEND != "") {
+      if (!defined(configs)) {
+        configs = []
+      }
+      configs += [ "$dir_pw_malloc:pw_malloc_wrapper_config" ]
+    }
+  }
+}
diff --git a/targets/stm32f429i-disc1-stm32cube/target_toolchains.gni b/targets/stm32f429i-disc1-stm32cube/target_toolchains.gni
new file mode 100644
index 0000000..24d2671
--- /dev/null
+++ b/targets/stm32f429i-disc1-stm32cube/target_toolchains.gni
@@ -0,0 +1,154 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_rpc/system_server/backend.gni")
+import("$dir_pw_sys_io/backend.gni")
+import("$dir_pw_toolchain/arm_gcc/toolchains.gni")
+
+_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"
+
+  #TODO: Fix test server: likely have to fork stm32f429i-disc1 implementation
+  # 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 not yet used
+  # pw_cpu_exception_ENTRY_BACKEND =
+  #     "$dir_pw_cpu_exception_cortex_m:cpu_exception_armv7m"
+  # pw_cpu_exception_HANDLER_BACKEND = "$dir_pw_cpu_exception:basic_handler"
+  # pw_cpu_exception_SUPPORT_BACKEND =
+  #     "$dir_pw_cpu_exception_cortex_m:support_armv7m"
+  pw_sync_INTERRUPT_SPIN_LOCK_BACKEND =
+      "$dir_pw_sync_baremetal:interrupt_spin_lock"
+  pw_log_BACKEND = dir_pw_log_basic
+  pw_sys_io_BACKEND = dir_pw_sys_io_stm32cubef4
+  pw_sys_io_stm32cubef4_usart_num = "1"
+  pw_sys_io_stm32cubef4_gpio_port = "A"
+  pw_sys_io_stm32cubef4_gpio_tx_pin = "9"
+  pw_sys_io_stm32cubef4_gpio_rx_pin = "10"
+
+  #TODO: remove dependency on stm32f429i-disc1 rpc server.
+  pw_rpc_system_server_BACKEND =
+      "$dir_pigweed/targets/stm32f429i-disc1:system_rpc_server"
+  pw_malloc_BACKEND = dir_pw_malloc_freelist
+
+  pw_boot_armv7m_LINK_CONFIG_DEFINES = [
+    "PW_BOOT_FLASH_BEGIN=0x08000200",
+    "PW_BOOT_FLASH_SIZE=2048K",
+
+    # TODO(pwbug/219): Currently "pw_tokenizer/detokenize_test" requires at
+    # least 6K bytes in heap when using pw_malloc_freelist. The heap size
+    # required for tests should be investigated.
+    "PW_BOOT_HEAP_SIZE=7K",
+    "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",
+  ]
+
+  current_cpu = "arm"
+  current_os = ""
+
+  pw_board_led_BACKEND = dir_pw_board_led_stm32cubef4
+  pw_board_led_stm32cubef4_gpio_port = "G"
+  pw_board_led_stm32cubef4_gpio_pin = "13"
+
+  pw_spin_delay_BACKEND = dir_pw_spin_delay_stm32cubef4
+
+  pw_third_party_stm32cubef4_product = "STM32F429xx"
+
+  # stm32f429i-disc1 has 8Mhz external crystal
+  pw_third_party_stm32cubef4_hse_hz = "8000000"
+}
+
+_toolchain_properties = {
+  final_binary_extension = ".elf"
+}
+
+_target_default_configs = [
+  "$dir_pw_build:extra_strict_warnings",
+  "$dir_pw_toolchain/arm_gcc:enable_float_printf",
+]
+
+pw_target_toolchain_stm32f429i_disc1 = {
+  _excluded_members = [
+    "defaults",
+    "name",
+  ]
+
+  debug = {
+    name = "stm32f429i_disc1_stm32cube_debug"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m4f_debug
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    forward_variables_from(_toolchain_properties, "*")
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+      default_configs += _target_default_configs
+    }
+  }
+
+  speed_optimized = {
+    name = "stm32f429i_disc1_stm32cube_speed_optimized"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m4f_speed_optimized
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    forward_variables_from(_toolchain_properties, "*")
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+      default_configs += _target_default_configs
+    }
+  }
+
+  size_optimized = {
+    name = "stm32f429i_disc1_stm32cube_size_optimized"
+    _toolchain_base = pw_toolchain_arm_gcc.cortex_m4f_size_optimized
+    forward_variables_from(_toolchain_base, "*", _excluded_members)
+    forward_variables_from(_toolchain_properties, "*")
+    defaults = {
+      forward_variables_from(_toolchain_base.defaults, "*")
+      forward_variables_from(_target_config, "*")
+      default_configs += _target_default_configs
+    }
+  }
+}
+
+# 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,
+]
diff --git a/targets/stm32f429i-disc1-stm32cube/vector_table.c b/targets/stm32f429i-disc1-stm32cube/vector_table.c
new file mode 100644
index 0000000..fd7691e
--- /dev/null
+++ b/targets/stm32f429i-disc1-stm32cube/vector_table.c
@@ -0,0 +1,65 @@
+// 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.
+
+#include <stdbool.h>
+
+#include "pw_boot_armv7m/boot.h"
+#include "stm32f4xx.h"
+
+// Default handler to insert into the ARMv7-M vector table (below).
+// This function exists for convenience. If a device isn't doing what you
+// expect, it might have hit a fault and ended up here.
+static void DefaultFaultHandler(void) {
+  while (true) {
+    // Wait for debugger to attach.
+  }
+}
+
+// This is the device's interrupt vector table. It's not referenced in any
+// code because the platform (STM32F4xx) expects this table to be present at the
+// beginning of flash. The exact address is specified in the pw_boot_armv7m
+// configuration as part of the target config.
+//
+// For more information, see ARMv7-M Architecture Reference Manual DDI 0403E.b
+// section B1.5.3.
+
+// This typedef is for convenience when building the vector table. With the
+// exception of SP_main (0th entry in the vector table), all the entries of the
+// vector table are function pointers.
+typedef void (*InterruptHandler)(void);
+
+// This is the timer interrupt handler implemented by the stm32cubef4 timer
+// template.
+void TIM6_DAC_IRQHandler(void);
+
+PW_KEEP_IN_SECTION(".vector_table")
+const InterruptHandler vector_table[] = {
+    // The starting location of the stack pointer.
+    // This address is NOT an interrupt handler/function pointer, it is simply
+    // the address that the main stack pointer should be initialized to. The
+    // value is reinterpret casted because it needs to be in the vector table.
+    [0] = (InterruptHandler)(&pw_boot_stack_high_addr),
+
+    // Reset handler, dictates how to handle reset interrupt. This is the
+    // address that the Program Counter (PC) is initialized to at boot.
+    [1] = pw_boot_Entry,
+
+    // NMI handler.
+    [2] = DefaultFaultHandler,
+    // HardFault handler.
+    [3] = DefaultFaultHandler,
+
+    // stm32f4xx_hal sys-tick handler.
+    [TIM6_DAC_IRQn + 16] = TIM6_DAC_IRQHandler,
+};