Based on https://github.com/noahp/pico-c-cortex-m , this project shows a simple bootloader+application configuration for an ARM Cortex-M.
The bootloader and app share a linker script, which is run through the C preprocessor to select placing code/read-only data into the bootloader or application region.
Also demonstrates how to keep a ".noinit
" region in RAM that won't be
overwritten on program initialization, and can be used to preserve data through
a warm reboot or for passing information between bootloader and application.
This is the device memory map, containing the bootloader and application.
Addresses are specific to the STM32F407VG (general idea applies to other SOCs though).
FLASH:
┌───────────────────┐08000000
│.bootloader │
├───────────────────┤08004000
│.text (application)│
├───────────────────┤080059f4 (end of .text)
│.note.gnu.build-id │
└───────────────────┘
RAM:
┌───────────────────┐20000000
│.data │
├───────────────────┤20000078
│.bss │
├───────────────────┤2001b000
│.noinit │
└───────────────────┘
The application and bootloader share a linker script, but are linked to run from different flash regions.
The correct output region in flash for .text
and other read-only sections is
specified by the following hack:
- for example, for linking the application, first generate an
app.ld
by running thesrc/common/stm32f407.ld
through the C preprocessor, replacing theFLASH__
strings withAPPLICATION_FLASH
(could also be done with egsed
). This is inspired from how zephyr composes linker scripts - link the application using the
app.ld
script, which will place everything into the correctAPPLICATION_FLASH
region.
RAM region selection is the same between bootloader and application; this means
that the memory map for both programs is in a single file, and the .noinit
section definition is shared.
The application contains the bootloader in a special output section placed at the start of flash filling exactly 1 flash sector, which means on chip reset, the bootloader runs first.
The application ELF is produced by the following steps (see
Makefile
for specifics):
- bootloader executable is linked, with the
FLASH__
regions set toBOOTLOADER_FLASH
. - bootloader ELF is converted to raw binary image
bootloader.bin
bootloader.bin
is converted to an object file,bootloader.o
, with raw image data placed in a section.bootloader
.- application executable is linked, including the
bootloader.o
file, with the.bootloader
output section placed at theBOOTLOADER_FLASH
region (start of flash), andFLASH__
regions set toAPPLICATION_FLASH
.
A region of RAM is reserved for symbols placed into any .noinit*
section. In
this example the NOINIT
region is the top 0x100 bytes of RAM.
To build + run it on an STM32F4 Discovery board:
# necessary tools (ubuntu, other systems may be different)
❯ sudo apt install openocd gcc-arm-none-eabi
# start openocd in one terminal
❯ make debug
# in another terminal, run this command to build and start gdb
❯ make gdb
# 'continue' in gdb, you should see 'Hello from Bootloader!' etc in openocd