pw_boot_armv7m: Add pw_PreStaticConstructorInit()

Adds pw_PreStaticConstructorInit() to allow ARMv7-M targets that use
pw_boot to perform initialization before C++ static constructors are
run. Updates stm32f429i-disc1 to enable FPU before static constructors
so static constructors can safely perform FPU operations if necessary.

Change-Id: I66cfa82096719f59c42c84e4bafd0e1740002e09
diff --git a/pw_boot_armv7m/core_init.c b/pw_boot_armv7m/core_init.c
index b8b4223..b9a38cb 100644
--- a/pw_boot_armv7m/core_init.c
+++ b/pw_boot_armv7m/core_init.c
@@ -80,6 +80,9 @@
          0,
          &_pw_zero_init_ram_end - &_pw_zero_init_ram_start);
 
+  // Run any init that must be done before C++ static constructors.
+  pw_PreStaticConstructorInit();
+
   // Call static constructors.
   __libc_init_array();
 }
diff --git a/pw_boot_armv7m/docs.rst b/pw_boot_armv7m/docs.rst
index 7413289..1f74216 100644
--- a/pw_boot_armv7m/docs.rst
+++ b/pw_boot_armv7m/docs.rst
@@ -34,6 +34,11 @@
 This module expects two extern "C" functions to be defined outside this module.
 
  - ``int main()``: This is where applications reside.
+ - ``void pw_PreStaticConstructorInit()``: This function executes just before
+   C++ static constructors are called. At this point, other static memory has
+   been zero or data initialized. This function should set up any early
+   initialization that should be done before C++ static constructors are run
+   (e.g. enabling FPU).
  - ``void pw_PreMainInit()``: This function executes just before main, and
    can be used for any device initialization that isn't application specific.
    Depending on your platform, this might be turning on a UART, setting up
diff --git a/pw_boot_armv7m/public/pw_boot_armv7m/boot.h b/pw_boot_armv7m/public/pw_boot_armv7m/boot.h
index ab0da28..e897bd1 100644
--- a/pw_boot_armv7m/public/pw_boot_armv7m/boot.h
+++ b/pw_boot_armv7m/public/pw_boot_armv7m/boot.h
@@ -41,6 +41,8 @@
 // In pw_BootEntry():
 //   Initialize memory -> pw_PreMainInit() -> main()
 
+#include <stdint.h>
+
 #include "pw_preprocessor/compiler.h"
 #include "pw_preprocessor/util.h"
 
@@ -81,6 +83,13 @@
 // memory initialization.
 PW_NO_PROLOGUE void pw_BootEntry();
 
+// This function is called just after zero initialization of RAM and loading
+// values into static memory (commonly labeled as the .data section in an ELF
+// file). Per the naming, this function is called just before C++ static
+// constructors are initialized. It is safe to run C code, but NOT safe to call
+// out to any C++ code.
+void pw_PreStaticConstructorInit();
+
 // This function is called by pw_BootEntry() after memory initialization but
 // before main. This allows targets to have pre-main initialization of the
 // device and seamlessly swap out the main() implementation. This function is
diff --git a/pw_sys_io_baremetal_lm3s6965evb/BUILD b/pw_sys_io_baremetal_lm3s6965evb/BUILD
index e3f3d38..78d4b76 100644
--- a/pw_sys_io_baremetal_lm3s6965evb/BUILD
+++ b/pw_sys_io_baremetal_lm3s6965evb/BUILD
@@ -19,6 +19,7 @@
 filegroup(
     name = "pw_sys_io_baremetal_lm3s6965evb",
     srcs = [
+        "early_boot.c",
         "sys_io_baremetal.cc",
     ],
 )
diff --git a/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn b/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn
index 60828b5..d032612 100644
--- a/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn
+++ b/pw_sys_io_baremetal_lm3s6965evb/BUILD.gn
@@ -24,6 +24,9 @@
       "$dir_pw_sys_io:default_putget_bytes",
       "$dir_pw_sys_io:facade",
     ]
-    sources = [ "sys_io_baremetal.cc" ]
+    sources = [
+      "early_boot.c",
+      "sys_io_baremetal.cc",
+    ]
   }
 }
diff --git a/pw_sys_io_baremetal_lm3s6965evb/early_boot.c b/pw_sys_io_baremetal_lm3s6965evb/early_boot.c
new file mode 100644
index 0000000..1670b7d
--- /dev/null
+++ b/pw_sys_io_baremetal_lm3s6965evb/early_boot.c
@@ -0,0 +1,17 @@
+// 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 "pw_boot_armv7m/boot.h"
+
+void pw_PreStaticConstructorInit() {}
\ No newline at end of file
diff --git a/pw_sys_io_baremetal_stm32f429/BUILD b/pw_sys_io_baremetal_stm32f429/BUILD
index 42f5c32..3537740 100644
--- a/pw_sys_io_baremetal_stm32f429/BUILD
+++ b/pw_sys_io_baremetal_stm32f429/BUILD
@@ -19,6 +19,7 @@
 filegroup(
     name = "pw_sys_io_baremetal_stm32f429",
     srcs = [
+        "early_boot.c",
         "sys_io_baremetal.cc",
     ],
 )
diff --git a/pw_sys_io_baremetal_stm32f429/BUILD.gn b/pw_sys_io_baremetal_stm32f429/BUILD.gn
index 5bff1c2..faa9762 100644
--- a/pw_sys_io_baremetal_stm32f429/BUILD.gn
+++ b/pw_sys_io_baremetal_stm32f429/BUILD.gn
@@ -24,7 +24,10 @@
       "$dir_pw_sys_io:default_putget_bytes",
       "$dir_pw_sys_io:facade",
     ]
-    sources = [ "sys_io_baremetal.cc" ]
+    sources = [
+      "early_boot.c",
+      "sys_io_baremetal.cc",
+    ]
   }
 }
 
diff --git a/pw_sys_io_baremetal_stm32f429/early_boot.c b/pw_sys_io_baremetal_stm32f429/early_boot.c
new file mode 100644
index 0000000..afee255
--- /dev/null
+++ b/pw_sys_io_baremetal_stm32f429/early_boot.c
@@ -0,0 +1,32 @@
+// 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 <inttypes.h>
+
+#include "pw_boot_armv7m/boot.h"
+
+void pw_PreStaticConstructorInit() {
+  // TODO(pwbug/17): Optionally enable Replace when Pigweed config system is
+  // added.
+#if PW_ARMV7M_ENABLE_FPU
+  // Enable FPU if built using hardware FPU instructions.
+  // CPCAR mask that enables FPU. (ARMv7-M Section B3.2.20)
+  const uint32_t kFpuEnableMask = (0xFu << 20);
+
+  // Memory mapped register to enable FPU. (ARMv7-M Section B3.2.2, Table B3-4)
+  volatile uint32_t* arm_v7m_cpacr = (volatile uint32_t*)0xE000ED88u;
+
+  *arm_v7m_cpacr |= kFpuEnableMask;
+#endif  // PW_ARMV7M_ENABLE_FPU
+}
\ No newline at end of file
diff --git a/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc b/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc
index 3b7415a..c5f5526 100644
--- a/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc
+++ b/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc
@@ -202,19 +202,6 @@
   usart1.baud_rate = CalcBaudRegister(kSystemCoreClock, /*target_baud=*/115200);
 
   usart1.config1 = kEnableUsart | kReceiveEnable | kTransmitEnable;
-
-// TODO(pwbug/17): Replace when Pigweed config system is added.
-#if defined(PW_ARMV7M_ENABLE_FPU) && PW_ARMV7M_ENABLE_FPU == 1
-  // Enable FPU if built using hardware FPU instructions.
-  // CPCAR mask that enables FPU. (ARMv7-M Section B3.2.20)
-  constexpr uint32_t kFpuEnableMask = (0xFu << 20);
-
-  // Memory mapped register to enable FPU. (ARMv7-M Section B3.2.2, Table B3-4)
-  volatile uint32_t& arm_v7m_cpacr =
-      *reinterpret_cast<volatile uint32_t*>(0xE000ED88u);
-
-  arm_v7m_cpacr |= kFpuEnableMask;
-#endif  // defined(PW_ARMV7M_ENABLE_FPU) && PW_ARMV7M_ENABLE_FPU == 1
 }
 
 namespace pw::sys_io {