STM32F3 GCC Build

Also see stm32 discovery template, Mike Szczys and Jeremy Herbert’s github repos.

CodeSourcery’s Lite version doesn’t include hardware FPU support.

Building gcc for stm32f3

Based off of this, with the following modifications, since the files specified on the HOWTO weren’t available:

  • gdb-6.8a not 6.8
  • binutils-2.19.1a not 2.19

I’m copying the instructions here for posterity.

Changes from original HOWTO

  • had to add CFLAGS=-Wno-error to the make line for binutils since Ubuntu 12.10’s compiler default is to add -Werror. Not needed for newlib or gcc.
  • had to manually edit WARN_CFLAGS to disable a warning for gdb, since it turns on -Werror explicitly
  • had to install libgmp-dev, libmpc-dev and libmpfr-dev to build gcc, and texinfo for newlib

For newer versions of gcc, you’ll probably get “configure: error: Link tests are not allowed after GCC_NO_EXECUTABLES” – try adding the following flags when configuring GCC: "--with-system-zlib --disable-shared” (that tip from the stm32 discovery template github

1. Create the base structure and download the archives

Assuming /mnt/data/stm32f3 is where everything should go (I also created a gcc433 subdir off that since I want to try to build a newer compiler after):

mkdir -p /mnt/data/stm32f3/gcc433 && cd /mnt/data/stm32f3/gcc433 && mkdir orig src build site
export MYTOOLS=/mnt/data/stm32f3/gcc433/site
cd orig

# Grab the GNU C Compiler source files

# Grab the GNU Debugger source files

# Grab the GNU binutils source files

# Grab the Newlib source files

cd ../src
tar xzvf ../orig/gcc-4.3.3.tar.gz
tar xzvf ../orig/gcc-core-4.3.3.tar.gz
tar xzvf ../orig/gcc-g++-4.3.3.tar.gz
tar xzvf ../orig/gdb-6.8a.tar.gz
tar xjvf ../orig/binutils-2.19.1a.tar.bz2
tar xzvf ../orig/newlib-1.17.0.tar.gz

cd ..

2. Build bintools

mkdir build/bintools-2.19.1a
cd build/bintools-2.19.1a
../../src/binutils-2.19.1a/configure --target=arm-none-eabi --prefix=$MYTOOLS --enable-interwork --enable-multilib
make all CFLAGS=-Wno-error
make install
cd ..


  • --target=arm-none-eabi - specify the target platform
  • --enable-interwork - allows for assembling Thumb and ARM code mixed into the same binaries (for those chips that support that)
  • --enable-multilib - multilib allows the use of libraries that are compiled multiple times for different targets/build types

3. Build (only) gcc

mkdir build/gcc-4.3.3
cd build/gcc-4.3.3
../../src/gcc-4.3.3/configure --target=arm-none-eabi --prefix=$MYTOOLS --enable-interwork --enable-multilib --enable-languages="c,c++" \
        --with-newlib --with-headers=../../src/newlib-1.16.0/newlib/libc/include
make all-gcc
make install-gcc
cd ..


  • --with-newlib - we will be using newlib instead of libc
  • --with-headers ../../blahblah - specify the newlib headers for the gcc build process

4. Build newlib

mkdir build/newlib-1.17.0
cd build/newlib-1.17.0
../../src/newlib-1.17.0/configure --target=arm-none-eabi --prefix=$MYTOOLS --enable-interwork --enable-multilib
make all
make install
cd ..

5. Go back and finish gcc now

cd build/gcc-4.3.3
make all
make install

6. Build gdb

mkdir build/gdb-6.8
cd build/gdb-6.8
../../src/gdb-6.8/configure --target=arm-none-eabi --prefix=$MYTOOLS --enable-interwork --enable-multilib
make install
cd ..

Now I had to go and hand-edit gdb/Makefile, and add -Wno-enum-compare to the end of the WARN_CFLAGS line; gdb specifically turns on -Werror and turning it off like for binutils doesn’t work. This is the only warning that wasn’t ignored by gdb by default.

7. Done.

The tools should now be built and ready to use.

Floating Point Support

By building the compiler ourselves, we have FPU support. However the STM32F4 needs to have the FPU enabled:

SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */


  • The FPU only supports 32 bit float and no 64 bit double. You have to make sure every constant stays a float. I recommend writing the “f” suffix consistently (e.g. pi=3.14f;) and also pass the compiler option -fsingle-precision-constant in case you do forget the suffix somewhere.
  • For math functions, you need to include math.h as usual, but only use the single precision versions with the “f” suffix (sqrtf(), sinf(), cosf(),...)
  • You need the appropriate compiler flags for hardware floating point. Use: -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 (The last one is not strictly required.)
  • When using any of the math functions (sqrtf,...) you need to link to the math library which also depends on the C library. These libraries must have been compiled with the same floating point related settings. To do this, use arm-none-eabi-gcc instead of arm-none-eabi-ld for linking and pass -lm -lc at the end of the call after all object files. If the compiler still complains, give it a hint where to find the correct libraries by adding a path -L/tool_prefix/lib/thumb/cortex-m4/float-abi-hard/fpuv4-sp-d16/

I saved a copy of Wolfgang Weiser’s stm32f4-bit-float-test tarball. It blinks all the LEDs, computes the Mandelbrot set and blinks the yellow/red LEDs. It then computes a million square roots and sets the blue LED on for each sqrtf(). You can use a scope to see how long it takes (20 cycles). His code links in the STM32F4-Discovery code so you’ll need that too.


Note that as of September 2012 OpenOCD has supported ST-Link. JTAG’s not necessary.

An example openocd.cfg file:

# openocd.cfg file for STM32F4Discovery board via integrated ST-Link/V2.
source [find interface/stlink-v2.cfg]
source [find target/stm32f4x_stlink.cfg]
reset_config srst_only srst_nogate

I found Wolfgang Weiser’s page on STM32. He mentions a patch to OpenOCD if it’s complaining about “remote 'g’ packet reply is too long”. This is the patch, to be applied to the OpenOCD git master:

diff --git a/src/target/armv7m.c b/src/target/armv7m.c
index 258653e..bbffffd 100644
--- a/src/target/armv7m.c
+++ b/src/target/armv7m.c
@@ -267,7 +267,7 @@ int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int
     struct armv7m_common *armv7m = target_to_armv7m(target);
     int i;

-    *reg_list_size = 26;
+    *reg_list_size = 17;
     *reg_list = malloc(sizeof(struct reg *) * (*reg_list_size));

@@ -280,19 +280,15 @@ int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int
     for (i = 0; i < 16; i++)
         (*reg_list)[i] = &armv7m->core_cache->reg_list[i];

-    for (i = 16; i < 24; i++)
-        (*reg_list)[i] = &arm_gdb_dummy_fp_reg;
-    (*reg_list)[24] = &arm_gdb_dummy_fps_reg;
     /* use dummy cpsr reg otherwise gdb may try and set the thumb bit */
-    (*reg_list)[25] = &armv7m_gdb_dummy_cpsr_reg;
+    (*reg_list)[16] = &armv7m_gdb_dummy_cpsr_reg;

     /* ARMV7M is always in thumb mode, try to make GDB understand this
      * if it does not support this arch */
     *((char *)armv7m->arm.pc->value) |= 1;
-    (*reg_list)[25] = &armv7m->core_cache->reg_list[ARMV7M_xPSR];
+    (*reg_list)[16] = &armv7m->core_cache->reg_list[ARMV7M_xPSR];

     return ERROR_OK;

He uses JTAGKEY2 JTAG adapter, but any FT2232-based one will work the same. You need to open the two ST-LINK jumpers near the Discovery’s USB port. The pin headers for JTAG are as follows (plus VCC and GND of course):

JTAG Header

OpenOCD udev stuff

# ST-Link/V2 as on STM32Discovery board.
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", MODE="660", GROUP="plugdev" 

# Segger J-Link JTAG.
SUBSYSTEM=="usb", ATTR{idVendor}=="1366", ATTR{idProduct}=="0101", MODE="660", GROUP="hackers" 

Don’t forget to execute udevadm control --reload-rules and udevadm trigger afterward.

Hello World

 1// By Wolfgang Wieser, heavily based on:
 4#define STACK_TOP 0x20000800   // just a tiny stack for demo
 6static void nmi_handler(void);
 7static void hardfault_handler(void);
 8int main(void);
10// Define the vector table
11unsigned int *myvectors[4]
12__attribute__ ((section("vectors"))) = {
13    (unsigned int *) STACK_TOP,         // stack pointer
14    (unsigned int *) main,              // code entry point
15    (unsigned int *) nmi_handler,       // NMI handler (not really)
16    (unsigned int *) hardfault_handler  // hard fault handler
19int main(void)
21    int i=0;
23    for(;;)
24    {
25        i++;
26    }
29void nmi_handler(void)
31    for(;;);
34void hardfault_handler(void)
36    for(;;);

and the linker file:

/* This will work with STM32 type of microcontrollers.    *
 * The sizes of RAM and flash are specified smaller than  *
 * what most of the STM32 provide to ensure that the demo *
 * program will run on ANY STM32.                         */
    ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
    rom (rx)  : ORIGIN = 0x00000000, LENGTH = 128K

    .  = 0x0;         /* From 0x00000000 */
    .text :
        *(vectors)    /* Vector table */
        *(.text)      /* Program code */
        *(.rodata)    /* Read only data */
    } >rom

    .  = 0x20000000;  /* From 0x20000000 */
    .data :
        *(.data)      /* Data memory */
    } >ram AT > rom

    .bss :
        *(.bss)       /* Zero-filled run time allocate data memory */
    } >ram AT > rom

An example Makefile:

TCPREFIX = /opt/ARM/arm-eabi/bin/arm-none-eabi-
CC      = $(TCPREFIX)gcc
LD      = $(TCPREFIX)ld -v
CP      = $(TCPREFIX)objcopy
OD      = $(TCPREFIX)objdump


# -mfix-cortex-m3-ldrd should be enabled by default for Cortex M3.
CFLAGS  =  -I. -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb
LFLAGS  = -Tstm32.ld -nostartfiles
CPFLAGS = -Obinary

all: run

    -rm -f main.lst *.o main.elf main.lst main.bin

run: main.bin
    $(STM32FLASH) main.bin

main.bin: main.elf
    @echo "...copying" 
    $(CP) $(CPFLAGS) main.elf main.bin
    $(OD) $(ODFLAGS) main.elf> main.lst

main.elf: main.o stm32.ld
    @echo "..linking" 
    $(LD) $(LFLAGS) -o main.elf main.o

main.o: main.c
    @echo ".compiling" 
    $(CC) $(CFLAGS) main.c

    $(GDBTUI) -ex "target remote localhost:3333" \ 
        -ex "set remote hardware-breakpoint-limit 6" \ 
        -ex "set remote hardware-watchpoint-limit 4" main.elf

Compile with

$ arm-none-eabi-gcc -I. -c -fno-common -O0 -g -mcpu=cortex-m3 -mthumb main.c
$ arm-none-eabi-ld -Tstm32.ld -nostartfiles -o main.elf main.o
$ arm-none-eabi-objcopy -Obinary main.elf main.bin

cortex-m3 works with both the M3 and M4.


telnet localhost 4444
reset halt
flash probe 0
flash write_image erase main.bin 0x08000000

You can also use some scripting to do it:

 2# NOTE: needs libnet-telnet-perl package.
 3use Net::Telnet;
 4use Cwd 'abs_path';
 6my $numArgs = $#ARGV + 1;
 7if($numArgs != 1) {
 8    die( "Usage ./ [main.bin] \n");
11my $file = abs_path($ARGV[0]);
13my $ip = "";   # localhost
14my $port = 4444;
16my $telnet = new Net::Telnet (
17    Port   => $port,
18    Timeout=> 30,
19    Errmode=> 'die',
20    Prompt => '/>/');
24print $telnet->cmd('reset halt');
25print $telnet->cmd('flash probe 0');
26print $telnet->cmd('flash write_image erase '.$file.' 0x08000000');
27print $telnet->cmd('reset');
28print $telnet->cmd('exit');
30print "\n";


use arm-none-eabi-gdb. OpenOCD has a GDB interface. Note that these processors have hardware break and watchpoints. Tell gdb about them:

set remote hardware-breakpoint-limit 6
set remote hardware-watchpoint-limit 4

Now you can use gdb to debug:

arm-none-eabi-gdb main.elf
target remote :3333

picture647-1.png (332 KB) Andrew Kohlsmith, 04/29/2013 04:06 PM

stm32-minimalistic-hello-world-1.0.tar.gz (1.94 KB) Andrew Kohlsmith, 04/29/2013 04:26 PM

stm32f4-bit-float_test.tar.gz (6.25 KB) Andrew Kohlsmith, 04/29/2013 04:26 PM

Add picture from clipboard (Maximum size: 1 GB)