Add stm32f429i-disc1 target and pw_dumb_io
Adds both the stm32f429i-disc1 target and a pw_dumb_io uart-based
backend.
Change-Id: If24379118fc2877485653d61ca66ceeb59ef920a
diff --git a/modules.gni b/modules.gni
index 8fef82c..b0b6c6e 100644
--- a/modules.gni
+++ b/modules.gni
@@ -26,6 +26,8 @@
dir_pw_build = "$dir_pigweed/pw_build"
dir_pw_docgen = "$dir_pigweed/pw_docgen"
dir_pw_dumb_io = "$dir_pigweed/pw_dumb_io"
+dir_pw_dumb_io_baremetal_stm32f429 =
+ "$dir_pigweed/pw_dumb_io_baremetal_stm32f429"
dir_pw_dumb_io_stdio = "$dir_pigweed/pw_dumb_io_stdio"
dir_pw_preprocessor = "$dir_pigweed/pw_preprocessor"
dir_pw_span = "$dir_pigweed/pw_span"
diff --git a/pw_dumb_io_baremetal_stm32f429/BUILD.gn b/pw_dumb_io_baremetal_stm32f429/BUILD.gn
new file mode 100644
index 0000000..d467a41
--- /dev/null
+++ b/pw_dumb_io_baremetal_stm32f429/BUILD.gn
@@ -0,0 +1,40 @@
+# 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.
+
+config("linker_script_config") {
+ _linker_script = "stm32f429.ld"
+ inputs = [
+ _linker_script,
+ ]
+ ldflags = [ "-T" + rebase_path("$_linker_script") ]
+}
+
+source_set("linker_script") {
+ public_configs = [ ":linker_script_config" ]
+}
+
+source_set("pw_dumb_io_baremetal_stm32f429") {
+ public_configs = [ "$dir_pw_build:pw_default_cpp" ]
+ public_deps = [
+ ":linker_script",
+ ]
+ deps = [
+ "$dir_pw_dumb_io:facade",
+ "$dir_pw_preprocessor",
+ ]
+ sources = [
+ "core_init.c",
+ "dumb_io_baremetal.cc",
+ ]
+}
diff --git a/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty b/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty
new file mode 100644
index 0000000..30e5cfa
--- /dev/null
+++ b/pw_dumb_io_baremetal_stm32f429/bloaty_config.bloaty
@@ -0,0 +1,29 @@
+# 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.
+
+source_filter: "^SEG .+$"
+
+custom_data_source: {
+ name: "segment_names"
+ base_data_source: "segments"
+
+ rewrite: {
+ pattern: "LOAD #0"
+ replacement: "SEG FLASH"
+ }
+ rewrite: {
+ pattern: "LOAD #1"
+ replacement: "SEG RAM"
+ }
+}
diff --git a/pw_dumb_io_baremetal_stm32f429/core_init.c b/pw_dumb_io_baremetal_stm32f429/core_init.c
new file mode 100644
index 0000000..69bc16e
--- /dev/null
+++ b/pw_dumb_io_baremetal_stm32f429/core_init.c
@@ -0,0 +1,131 @@
+// 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.
+
+// !!!WARNING!!!
+//
+// Some of the code in this file is run without static initialization expected
+// by C/C++. Any accesses to statically initialized objects/variables before
+// memory is initialized will result in undefined values and violates the C
+// specification. Only code run after memory initialization is complete will be
+// compliant and truly safe to run. In general, make early initialization code
+// run AFTER memory initialization has complete unless it is ABSOLUTELY
+// NECESSARY to modify the way memory is initialized.
+//
+// This file is similar to a traditional assembly startup file. It turns out
+// that everything typically done in ARMv7-M assembly startup can be done
+// straight from C code. This makes startup code easier to maintain, modify,
+// and read.
+//
+// Core initialization is comprised of two primary parts:
+//
+// 1. Initialize ARMv7-M Vector Table: The ARMv7-M vector table (See ARMv7-M
+// Architecture Reference Manual DDI 0403E.b section B1.5) dictates the
+// starting program counter (PC) and stack pointer (SP) when the SoC powers
+// on. The vector table also contains a number of other vectors to handle
+// different exceptions. This file omits many of the vectors and only
+// configures the four most important ones.
+//
+// 2. Initialize Memory: When execution begins due to SoC power-on (or the
+// device is reset), memory must be initialized to ensure it contains the
+// expected values when code begins to run. The SoC doesn't inherently have a
+// notion of how to do this, so before ANYTHING else the memory must be
+// initialized. This is done at the beginning of pw_FirmwareInit().
+//
+//
+// The simple flow is as follows:
+// Power on -> PC and SP set (from vector_table by SoC) -> pw_FirmwareInit()
+//
+// In pw_FirmwareInit():
+// Initialize memory -> initialize board (pre-main init) -> main()
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "pw_preprocessor/compiler.h"
+
+// Extern symbols referenced in the vector table.
+extern const uint8_t _stack_end[];
+extern uint8_t _static_init_ram_start[];
+extern uint8_t _static_init_ram_end[];
+extern uint8_t _static_init_flash_start[];
+extern uint8_t _zero_init_ram_start[];
+extern uint8_t _zero_init_ram_end[];
+
+// Functions called as part of firmware initialization.
+void __libc_init_array(void);
+void pw_BoardInit(void);
+int main(void);
+
+void DefaultFaultHandler(void) {
+ while (true) {
+ // Wait for debugger to attach.
+ }
+}
+
+// WARNING: This code is run immediately upon boot, and performs initialization
+// of RAM. Note that code running before this function finishes memory
+// initialization will violate the C spec (Section 6.7.8, paragraph 10 for
+// example, which requires uninitialized static values to be zero-initialized).
+// Be EXTREMELY careful when running code before this function finishes RAM
+// initialization.
+//
+// This function runs immediately at boot because it is at index 1 of the
+// interrupt vector table.
+PW_NO_PROLOGUE void pw_FirmwareInit() {
+ // Begin memory initialization.
+ // Static-init RAM.
+ memcpy(_static_init_ram_start,
+ _static_init_flash_start,
+ _static_init_ram_end - _static_init_ram_start);
+
+ // Zero-init RAM.
+ memset(_zero_init_ram_start, 0, _zero_init_ram_end - _zero_init_ram_start);
+
+ // Call static constructors.
+ __libc_init_array();
+
+ // End memory initialization.
+
+ // Do any necessary board init.
+ pw_BoardInit();
+
+ // Run main.
+ main();
+
+ // In case main() returns, just sit here until the device is reset.
+ while (true) {
+ }
+}
+
+// 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.
+//
+// For more information, see ARMv7-M Architecture Reference Manual DDI 0403E.b
+// section B1.5.3.
+PW_KEEP_IN_SECTION(".vector_table")
+const uint32_t vector_table[] = {
+ // The starting location of the stack pointer.
+ [0] = (uint32_t)_stack_end,
+
+ // Reset handler, dictates how to handle reset interrupt. This is also run
+ // at boot.
+ [1] = (uint32_t)pw_FirmwareInit,
+
+ // NMI handler.
+ [2] = (uint32_t)DefaultFaultHandler,
+ // HardFault handler.
+ [3] = (uint32_t)DefaultFaultHandler,
+};
diff --git a/pw_dumb_io_baremetal_stm32f429/docs.rst b/pw_dumb_io_baremetal_stm32f429/docs.rst
new file mode 100644
index 0000000..5385e90
--- /dev/null
+++ b/pw_dumb_io_baremetal_stm32f429/docs.rst
@@ -0,0 +1,47 @@
+.. _chapter-dumb-io-baremetal-stm32f429:
+
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+---------------------------
+STM32F429 baremetal dumb IO
+---------------------------
+The STM32F429 baremetal dumb IO backend provides device startup code and a UART
+driver to layer that allows applications built against the ``pw_dumb_io``
+interface to run on a STM32F429 chip and do simple input/output via UART.
+The code is optimized for the STM32F429I-DISC1, using USART1 (which is connected
+to the virtual COM port on the embedded ST-LINKv2 chip). However, this should
+work with all STM32F429 variations (and even some STM32F4xx chips).
+
+This backend has no configuration options. The point of it is to provide bare-
+minimum platform code needed to do UART reads/writes.
+
+Setup
+=====
+This module requires relatively minimal setup:
+
+ 1. Write code against the ``pw_dumb_io`` facade.
+ 2. Specify the ``dir_pw_dumb_io_backend`` GN gloabal variable to point to this
+ backend.
+ 3. Build an executable with a main() function using a toolchain that
+ supports Cortex-M4.
+
+.. note::
+ This module provides early firmware init and a linker script, so it will
+ conflict with other modules that do any early device init or provide a linker
+ script.
+
+Module usage
+============
+After building a an executable that utilizes this backend, flash the
+produced .elf binary to the development board. Then, using a serial
+communication terminal (like minicom or screen), connect to the device at a
+baud rate of 115200 (8N1). If you're not using a STM32F429I-DISC1 development
+board, manually connect a USB TLL adapter to pins `A9` (TX) and
+`A10` (RX).
+
+Dependencies
+============
+ * pw_dumb_io facade
+ * pw_preprocessor module
diff --git a/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc b/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc
new file mode 100644
index 0000000..110e44c
--- /dev/null
+++ b/pw_dumb_io_baremetal_stm32f429/dumb_io_baremetal.cc
@@ -0,0 +1,193 @@
+// 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.
+
+#include <cinttypes>
+
+#include "pw_dumb_io/dumb_io.h"
+#include "pw_preprocessor/compiler.h"
+
+namespace {
+
+// Default core clock. This is technically not a constant, but since this app
+// doesn't change the system clock a constant will suffice.
+constexpr uint32_t kSystemCoreClock = 16000000;
+
+// Base address for everything peripheral-related on the STM32F4xx.
+constexpr uint32_t kPeripheralBaseAddr = 0x40000000u;
+// Base address for everything AHB1-related on the STM32F4xx.
+constexpr uint32_t kAhb1PeripheralBase = kPeripheralBaseAddr + 0x00020000U;
+// Base address for everything APB2-related on the STM32F4xx.
+constexpr uint32_t kApb2PeripheralBase = kPeripheralBaseAddr + 0x00010000U;
+
+// Reset/clock configuration block (RCC).
+// `reserved` fields are unimplemented features, and are present to ensure
+// proper alignment of registers that are in use.
+PW_PACKED(struct) RccBlock {
+ uint32_t reserved1[12];
+ uint32_t ahb1_config;
+ uint32_t reserved2[4];
+ uint32_t apb2_config;
+};
+
+// Mask for ahb1_config (AHB1ENR) to enable the "A" GPIO pins.
+constexpr uint32_t kGpioAEnable = 0x1u;
+
+// Mask for apb2_config (APB2ENR) to enable USART1.
+constexpr uint32_t kUsart1Enable = 0x1u << 4;
+
+// GPIO register block definition.
+PW_PACKED(struct) GpioBlock {
+ uint32_t modes;
+ uint32_t out_type;
+ uint32_t out_speed;
+ uint32_t pull_up_down;
+ uint32_t input_data;
+ uint32_t output_data;
+ uint32_t gpio_bit_set;
+ uint32_t port_config_lock;
+ uint32_t alt_low;
+ uint32_t alt_high;
+};
+
+// Constants related to GPIO mode register masks.
+constexpr uint32_t kGpioPortModeMask = 0x3u;
+constexpr uint32_t kGpio9PortModePos = 18;
+constexpr uint32_t kGpio10PortModePos = 20;
+constexpr uint32_t kGpioPortModeAlternate = 2;
+
+// Constants related to GPIO port speed register masks.
+constexpr uint32_t kGpioPortSpeedMask = 0x3u;
+constexpr uint32_t kGpio9PortSpeedPos = 18;
+constexpr uint32_t kGpio10PortSpeedPos = 20;
+constexpr uint32_t kGpioSpeedVeryHigh = 3;
+
+// Constants related to GPIO pull up/down resistor type masks.
+constexpr uint32_t kGpioPullTypeMask = 0x3u;
+constexpr uint32_t kGpio9PullTypePos = 18;
+constexpr uint32_t kGpio10PullTypePos = 20;
+constexpr uint32_t kPullTypePullUp = 1;
+
+// Constants related to GPIO port speed register masks.
+constexpr uint32_t kGpioAltModeMask = 0x3u;
+constexpr uint32_t kGpio9AltModeHighPos = 4;
+constexpr uint32_t kGpio10AltModeHighPos = 8;
+
+// Alternate function for pins A9 and A10 that enable USART1.
+constexpr uint8_t kGpioAlternateFunctionUsart1 = 0x07u;
+
+// USART status flags.
+constexpr uint32_t kTxRegisterEmpty = 0x1u << 7;
+
+// USART configuration flags for config1 register.
+// Note: a large number of configuration flags have been omitted as they default
+// to sane values and we don't need to change them.
+constexpr uint32_t kReceiveEnable = 0x1 << 2;
+constexpr uint32_t kTransmitEnable = 0x1 << 3;
+constexpr uint32_t kReadDataReady = 0x1u << 5;
+constexpr uint32_t kEnableUsart = 0x1 << 13;
+
+// Layout of memory mapped registers for USART blocks.
+PW_PACKED(struct) UsartBlock {
+ uint32_t status;
+ // Only the lower 8 bits are valid. Read for RX data, write to TX data.
+ uint32_t data_register;
+ uint32_t baud_rate;
+ uint32_t config1;
+ uint32_t config2;
+ uint32_t config3;
+ uint32_t config4;
+};
+
+// Sets the UART baud register using the peripheral clock and target baud rate.
+// These calculations are specific to the default oversample by 16 mode.
+// TODO(amontanez): Document magic calculations in full UART implementation.
+uint32_t CalcBaudRegister(uint32_t clock, uint32_t target_baud) {
+ uint32_t div_fac = (clock * 25) / (4 * target_baud);
+ uint32_t mantissa = div_fac / 100;
+ uint32_t fraction = ((div_fac - mantissa * 100) * 16 + 50) / 100;
+
+ return (mantissa << 4) + (fraction & 0xFFu);
+}
+
+// Declare a reference to the memory mapped RCC block.
+volatile RccBlock& platform_rcc =
+ *reinterpret_cast<volatile RccBlock*>(kAhb1PeripheralBase + 0x3800U);
+
+// Declare a reference to the 'A' GPIO memory mapped block.
+volatile GpioBlock& gpio_a =
+ *reinterpret_cast<volatile GpioBlock*>(kAhb1PeripheralBase + 0x0000U);
+
+// Declare a reference to the memory mapped block for USART1.
+volatile UsartBlock& usart1 =
+ *reinterpret_cast<volatile UsartBlock*>(kApb2PeripheralBase + 0x1000U);
+
+} // namespace
+
+extern "C" void pw_BoardInit() {
+ // Enable 'A' GIPO clocks.
+ platform_rcc.ahb1_config |= kGpioAEnable;
+
+ // Enable Uart TX pin.
+ // Output type defaults to push-pull (rather than open/drain).
+ gpio_a.modes |= kGpioPortModeAlternate << kGpio9PortModePos;
+ gpio_a.out_speed |= kGpioSpeedVeryHigh << kGpio9PortSpeedPos;
+ gpio_a.pull_up_down |= kPullTypePullUp << kGpio9PullTypePos;
+ gpio_a.alt_high |= kGpioAlternateFunctionUsart1 << kGpio9AltModeHighPos;
+
+ // Enable Uart RX pin.
+ // Output type defaults to push-pull (rather than open/drain).
+ gpio_a.modes |= kGpioPortModeAlternate << kGpio10PortModePos;
+ gpio_a.out_speed |= kGpioSpeedVeryHigh << kGpio10PortSpeedPos;
+ gpio_a.pull_up_down |= kPullTypePullUp << kGpio10PullTypePos;
+ gpio_a.alt_high |= kGpioAlternateFunctionUsart1 << kGpio10AltModeHighPos;
+
+ // Initialize USART1. Initialized to 8N1 at the specified baud rate.
+ platform_rcc.apb2_config |= kUsart1Enable;
+
+ // Warning: Normally the baud rate register calculation is based off
+ // peripheral 2 clock. For this code, the peripheral clock defaults to
+ // the system core clock so it can be used directly.
+ usart1.baud_rate = CalcBaudRegister(kSystemCoreClock, /*target_baud=*/115200);
+
+ usart1.config1 = kEnableUsart | kReceiveEnable | kTransmitEnable;
+}
+
+namespace pw::dumb_io {
+
+// Wait for a byte to read on USART1. This blocks until a byte is read. This is
+// extremely inefficient as it requires the target to burn CPU cycles polling to
+// see if a byte is ready yet.
+Status GetByte(std::byte* dest) {
+ while (true) {
+ if (usart1.status & kReadDataReady) {
+ *dest = static_cast<std::byte>(usart1.data_register);
+ }
+ }
+ return Status::OK;
+}
+
+// Send a byte over USART1. Since this blocks on every byte, it's rather
+// inefficient. At the default baud rate of 115200, one byte blocks the CPU for
+// ~87 micro seconds. This means it takes only 10 bytes to block the CPU for
+// 1ms!
+Status PutByte(std::byte b) {
+ // Wait for TX buffer to be empty. When the buffer is empty, we can write
+ // a value to be dumped out of UART.
+ while (!(usart1.status & kTxRegisterEmpty)) {
+ }
+ usart1.data_register = static_cast<uint32_t>(b);
+ return Status::OK;
+}
+
+} // namespace pw::dumb_io
diff --git a/pw_dumb_io_baremetal_stm32f429/stm32f429.ld b/pw_dumb_io_baremetal_stm32f429/stm32f429.ld
new file mode 100644
index 0000000..fbdbe77
--- /dev/null
+++ b/pw_dumb_io_baremetal_stm32f429/stm32f429.ld
@@ -0,0 +1,127 @@
+/**
+ * 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.
+ */
+
+HIDDEN(_min_stack_size = 1K);
+
+/* Note: This technically doesn't set the firmware's entry point. Setting the
+ * firmware entry point is done by setting vector_table[1] in core_init.c.
+ * However, this DOES tell the compiler how to optimize when --gc-sections
+ * is enabled.
+ */
+ENTRY(pw_FirmwareInit)
+
+MEMORY
+{
+ /* Internal Flash */
+ FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 512K
+ /* Internal SRAM */
+ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 192K
+}
+
+SECTIONS
+{
+
+ /* Main executable code. */
+ .code : ALIGN(8)
+ {
+ /* STM32F4xx expects the vector table to be at the beginning of flash. */
+ KEEP(*(.vector_table))
+
+ . = ALIGN(8);
+ /* Application code. */
+ *(.text)
+ *(.text*)
+ KEEP(*(.init))
+ KEEP(*(.fini))
+
+ . = ALIGN(8);
+ /* Constants.*/
+ *(.rodata)
+ *(.rodata*)
+
+ /* .preinit_array, .init_array, .fini_array are used by libc.
+ * Each section is a list of function pointers that are called pre-main and
+ * post-exit for object initialization and tear-down.
+ * Since the region isn't explicitly referenced, specify KEEP to prevent
+ * link-time garbage collection. SORT is used for sections that have strict
+ * init/de-init ordering requirements. */
+ . = ALIGN(8);
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array*))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array*))
+ PROVIDE_HIDDEN (__init_array_end = .);
+
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(SORT(.fini_array.*)))
+ KEEP (*(.fini_array*))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ } >FLASH
+
+ /* Used by unwind-arm/ */
+ .ARM : ALIGN(8) {
+ __exidx_start = .;
+ *(.ARM.exidx*)
+ __exidx_end = .;
+ } >FLASH
+
+ /* Explicitly initialized global and static data. (.data)*/
+ .static_init_ram : ALIGN(8)
+ {
+ *(.data)
+ *(.data*)
+ . = ALIGN(8);
+ } >RAM AT> FLASH
+
+ /* Zero initialized global/static data. (.bss)
+ * This section is zero initialized in pw_FirmwareInit(). */
+ .zero_init_ram : ALIGN(8)
+ {
+ *(.bss)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(8);
+ } >RAM
+
+ /* Link-time check for stack overlaps. */
+ .stack (NOLOAD) : ALIGN(8)
+ {
+ HIDDEN(_stack_size = ORIGIN(RAM) + LENGTH(RAM) - .);
+ ASSERT(_stack_size >= _min_stack_size, "Error: Not enough RAM for stack.");
+ . = . + _stack_size;
+ } >RAM
+}
+
+/* Symbols used by core_init.c: */
+/* Top of stack to set stack pointer. */
+_stack_end = ORIGIN(RAM) + LENGTH(RAM);
+
+/* Start of .static_init_ram in FLASH. */
+_static_init_flash_start = LOADADDR(.static_init_ram);
+
+/* Region of .static_init_ram in RAM. */
+_static_init_ram_start = ADDR(.static_init_ram);
+_static_init_ram_end = _static_init_ram_start + SIZEOF(.static_init_ram);
+
+/* Region of .zero_init_ram. */
+_zero_init_ram_start = ADDR(.zero_init_ram);
+_zero_init_ram_end = _zero_init_ram_start + SIZEOF(.zero_init_ram);
+
+/* arm-none-eabi expects `end` symbol to point to start of heap for sbrk. */
+PROVIDE (end = _zero_init_ram_end);
diff --git a/pw_vars_default.gni b/pw_vars_default.gni
index b2eed99..80a4a8a 100644
--- a/pw_vars_default.gni
+++ b/pw_vars_default.gni
@@ -24,7 +24,8 @@
# The name of the GN target type used to build Pigweed executables.
#
# If this is a custom template, the .gni file containing the template must
- # be imported at the top of this file to make it globally available.
+ # be imported at the top of the target configuration file to make it globally
+ # available.
target_type = "executable"
# Path to an instantiation of the "linker_script" GN template defining the
diff --git a/targets/stm32f429i-disc1/target_config.gni b/targets/stm32f429i-disc1/target_config.gni
new file mode 100644
index 0000000..e6e9d13
--- /dev/null
+++ b/targets/stm32f429i-disc1/target_config.gni
@@ -0,0 +1,49 @@
+# 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_dumb_io_baremetal_stm32f429 as if it were a platform. This is for
+# testing/development and should eventually point to something more
+# sophisticated.
+
+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_m4_og"
+}
+
+# Executable wrapper that includes some baremetal startup code.
+template("stm32f429i_executable") {
+ target("executable", target_name) {
+ forward_variables_from(invoker, "*")
+ deps += [ dir_pw_dumb_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_dumb_io_baremetal_stm32f429/bloaty_config.bloaty"
+
+# Path to a linker script target. This must be a target (e.g. source_set)
+# that provides a linker script (through a public config, for example).
+linker_script_target =
+ "$dir_pw_dumb_io_baremetal_stm32f429:linker_script_target"
+
+dir_pw_dumb_io_backend = "$dir_pw_dumb_io_baremetal_stm32f429"