STM32-P405 Startup Code – Quick Start

This example shows how to create and load startup code for the board, for further information about how the source code works please refer to STM32-P405 Startup Code – In Detail

First create a directory for the source files and build, for example:

mkdir -p $/HOME/arm/cortex/stm32p405/examples/introduction/000_simple_startup
cd $HOME/arm/cortex/stm32p405/examples/introduction/000_simple_startup

Next create a file called startup.ld with the following contents:

    flash_mem : ORIGIN = 0x08000000, LENGTH = 0x4000

 .text : { *(.text*) } > flash_mem

Then create a file called vector_table.s with the following contents:

.syntax unified

.cpu cortex-m4

/* Vector Table */

.word 0x20001000 /* Stack Pointer */
.word reset_handler /* 1 Reset Handler */
.word default_handler /* 2 NMI */
.word default_handler /* 3 Hard Fault */
.word default_handler /* 4 Mem Management Fault */
.word default_handler /* 5 Bus Fault */
.word usage_handler /* 6 Usage Fault */
.word default_handler /* 7 RESERVED */
.word default_handler /* 8 RESERVED */
.word default_handler /* 9 RESERVED*/
.word default_handler /* 10 RESERVED */
.word default_handler /* 11 SVCall */
.word default_handler /* 12 Debug Monitor */
.word default_handler /* 13 RESERVED */
.word default_handler /* 14 PendSV */
.word default_handler /* 15 SysTick */

.global reset_handler
.type reset_handler, STT_FUNC
    bl reset_handler

.global default_handler
.type default_handler, STT_FUNC
    bl default_handler

.global usage_handler
.type usage_handler, STT_FUNC
    bl usage_handler

To build the ELF file that is to be loaded onto the board type the following commands:

arm-none-eabi-as -g -o vector_table.o vector_table.s
arm-none-eabi-ld -g -o startup.elf -T startup.ld vector_table.o

This will create an ELF file called startup.elf

In another terminal window start OpenOCD, for example:

sudo openocd -f /usr/local/share/openocd/scripts/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f openocd.cfg
[sudo] password for $USER: 
Open On-Chip Debugger 0.9.0 (2016-07-28-08:43)
Licensed under GNU GPL v2
For bug reports, read
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
none separate
cortex_m reset_config sysresetreq
Info : clock speed 2000 kHz
Info : JTAG tap: stm32f4x.cpu tap/device found: 0x4ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x4)
Info : JTAG tap: tap/device found: 0x06413041 (mfg: 0x020, part: 0x6413, ver: 0x0)
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints

Leave this terminal window open and return to the terminal window where the ELF file was build, then start the GDB debugger as follows:

GNU gdb (
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:
For help, type "help".
Type "apropos word" to search for commands related to "word".

Now connect to the OpenOCD GDB interface, this is located on localhost, port number 3333

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x08000040 in ?? ()

Then load the elf image onto the board, as follows:

(gdb) load startup.elf
Loading section .text, size 0x4c lma 0x8000000
Start address 0x8000000, load size 76
Transfer rate: 117 bytes/sec, 76 bytes/write.

Warning: Loading this ELF file will overwrite any previous firmware!

Next load the ELF file into GDB so the debugger can determine symbol names and locations, as follows:

(gdb) file startup.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from startup.elf...done.

Now set a breakpoint on the function reset_handler(), as follows:

(gdb) break reset_handler
Breakpoint 1 at 0x8000040: file vector_table.s, line 29.

Now the startup code is ready to run:

(gdb) cont
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, reset_handler () at vector_table.s:29
29 bl reset_handler

The debugger has halted when the reset_handler() function was called, the registers can now be inspected, as follows:

(gdb) info reg
r0 0x0 0
r1 0x8 8
r2 0x0 0
r3 0x0 0
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
sp 0x20001000 0x20001000
lr 0xffffffff -1
pc 0x8000040 0x8000040 <reset_handler>
xPSR 0x41000000 1090519040

It can be seen from the output that the stack pointer (sp) has been set correctly and the program counter (pc) is pointing correctly to the reset_handler() function.

For a more detailed explanation about the startup code, please refer to STM32-P405 Startup Code – In Detail or go straight on to creating a build system which will automate the steps above: STM32-P405 – Startup Code Build System