STM32F4 – Build Your Own GNU ARM Cross-Toolchain From Scratch

In this tutorial I describe how to build your own GNU ARM cross-toolchain from scratch. I use the latest source available:

  • GCC 4.9.1 4.9.2;
  • embedded C libraries: newlib 2.1 and size optimized newlib-nano 2.1;
  • binutils 2.24 2.25;
  • GDB 7.7

I also provide a build script that accomplishes this task for you without breaking a sweat.

Compile-FreakOut

1. Prerequisites

cd ~
# Remove the official and the 3rd party GCC toolchain
sudo apt-get purge binutils-arm-none-eabi \
                   gcc-arm-none-eabi \
                   gdb-arm-none-eabi \
                   libnewlib-arm-none-eabi

# Install
sudo apt-get install build-essential git openocd

# Clone my git repository and init submodules
git clone https://github.com/istarc/stm32.git
cd ~/stm32
git submodule update --init

2. Install Software Dependencies

Install necessary system libraries to build the GNU Compiler Collection.

sudo apt-get install libgmp-dev libmpfr-dev \
                     libmpc-dev zlib1g-dev

3. Build GCC Automatically

Build the cross-toolchain automagically using the build script. The script builds against the newlib and the size optimized newlib nano libraries. Run the script as follows.

cd ~/stm32/build-ARM-toolchain

# Run the script and grab smth. to eat
bash build.sh

4. Build GCC Manually

You may try to build GCC yourself. In this case, you should follow the instructions below.

Note however this procedure includes only the newlib library, but not newlib-nano. Should you require both libraries, please, follow the build script procedure.

4.1 Prerequisites

  1. Create the basic directory structure (in your home folder).
    cd ~
    mkdir -p ~/arm/bin   # Here are final binary files
    mkdir -p ~/arm/build # Here are elf files
    mkdir -p ~/arm/orig  # Here are original archives
    mkdir -p ~/arm/src   # Here is the source
    
  2. Download the GCC and newlib source code.
    cd ~/arm/orig
    wget ftp://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz
    wget ftp://ftp.gnu.org/gnu/gcc/gcc-4.9.1/gcc-4.9.1.tar.gz
    wget ftp://sources.redhat.com/pub/newlib/newlib-2.1.0.tar.gz
    wget ftp://ftp.gnu.org/gnu/gdb/gdb-7.7.tar.gz
    
  3. Unpack the source code, create build directories and apply the newlib 2.1.0 patch.
    cd ~/arm/src
    tar xzf ~/arm/orig/binutils-2.24.tar.gz
    tar xzf ~/arm/orig/gcc-4.9.1.tar.gz
    tar xzf ~/arm/orig/newlib-2.1.0.tar.gz
    tar xzf ~/arm/orig/gdb-7.7.tar.gz
    
    # Create separate build directories
    cd ~/arm/build
    mkdir `ls ~/arm/src`
    
    # Apply the patch
    cd ~/arm/src/newlib-2.1.0
    patch -p0 < ~/stm32/XToolchain/newlib-2.1.0.patch
    

4.2  Build the GCC

  1. Define the target variable and update the bin path.
    export TARGET=arm-none-eabi
    export PATH=$PATH:~/arm/bin
    
  2. Build the binutils-2.24 with the following configuration:
    • ARM Cortex-M4F CPU.
    • fpv4-sp-d16 FPU.
    • Use hardware FPU instead of software simulated.
    • Use Thumb instruction set.
    • Support mixed use of Thumb and ARM instruction sets (interwork).
    • Build different library variants for various CPU options.
    • Use only GNU Assembler and Linker.
    • Disable localization support (English only).
      cd ~/arm/build/binutils-2.24
      make clean
      ../../src/binutils-2.24/configure \
       --target=$TARGET \
       --prefix=$PREFIX \
       --with-cpu=cortex-m4 \
       --with-fpu=fpv4-sp-d16 \
       --with-float=hard \
       --with-mode=thumb \
       --enable-interwork \
       --enable-multilib \
       --with-gnu-as \
       --with-gnu-ld \
       --disable-nls
      make -j4 all
      make install
      
  3. Build bootstrap GCC, i.e. C compiler only. Don’t include newlib C library headers yet, because the library has not been built yet. In addition, disable shared libraries, because they are not supported on the bare metal target.
    cd ~/arm/build/gcc-4.9.1
    make clean
    ../../src/gcc-4.9.1/configure \
     --target=$TARGET \
     --prefix=$PREFIX \
     --with-cpu=cortex-m4 \
     --with-fpu=fpv4-sp-d16 \
     --with-float=hard \
     --with-mode=thumb \
     --enable-interwork \
     --enable-multilib \
     --enable-languages="c" \
     --with-system-zlib \
     --with-newlib \
     --without-headers \
     --disable-shared \
     --disable-nls \
     --with-gnu-as \
     --with-gnu-ld
    make -j4 all-gcc
    make install-gcc
    
  4. Use the bootstrap GCC to build the C library – newlib-2.1.0. Disable system calls, because you build for bare metal target.
    cd ~/arm/build/newlib-2.1.0
    make clean
    ../../src/newlib-2.1.0/configure \
     --target=$TARGET \
     --prefix=$PREFIX \
     --with-cpu=cortex-m4 \
     --with-fpu=fpv4-sp-d16 \
     --with-float=hard \
     --with-mode=thumb \
     --enable-interwork \
     --enable-multilib \
     --with-gnu-as \
     --with-gnu-ld \
     --disable-nls \
     --disable-newlib-supplied-syscalls
    make -j4 all
    make install
    
  5. Use the boostrap GCC and newlib C library to build entire GCC (with C++ language enabled).
    cd ~/arm/build/gcc-4.9.1
    # DON'T CLEAN THE BOOTSTRAP GCC!
    ../../src/gcc-4.9.1/configure --target=$TARGET \
     --prefix=$PREFIX \
     --with-cpu=cortex-m4 \
     --with-fpu=fpv4-sp-d16 \
     --with-float=hard \
     --with-mode=thumb \
     --enable-interwork \
     --enable-multilib \
     --enable-languages="c,c++" \
     --with-system-zlib \
     --with-newlib \
     --disable-shared \
     --disable-nls \
     --with-gnu-as \
     --with-gnu-ld
    make -j4 all
    make install
    
  6. Build the GDB debugger.
    cd ~/arm/build/gdb-7.7
    make clean
    ../../src/gdb-7.7/configure \
     --target=$TARGET \
     --prefix=$PREFIX \
     --enable-interwork \
     --enable-multilib
    make -j4 all
    make install
    

That’s it! 🙂

Compile-Futurama

5. Test Your Cross-Toolchain

You may test your cross-toolchain by building and deploying one of the previous projects.

Potential pitfall. If you built the cross-toolchain manually using the above instructions, you should disable/remove the Makefile optimization directive “–specs=nano.specs”. There is a simple workaround for this. Use the build script (see the Section 3).

export PATH=~/arm/bin:$PATH
cd ~/stm32/examples/Template

make clean
make release
# text data bss dec hex filename
# 33336 1156 78376 112868 1b8e4 bin/outp.elf

make deploy

And the result.

The green LED toggles every 1.5 s. You can toggle the red LED by pressing the blue button.

The green LED toggles every 1.5 s. You can toggle the red LED by pressing the blue button.

Q1: Deployment fails repeatedly due to some OpenOCD issue. 😦 Is there a workaround?
A1: Yes, the current official Ubuntu package (Aug 2014) contains a prehistoric OpenOCD version. You should build a newer version from scratch. I provide step-by-step instructions here (2nd section, it takes less than 3 minutes to build it). When you are done, just return here and continue as nothing happened. It will work out of the box. 🙂

6. References

I heavily borrowed from these sources [1, 2, 3, 4] to make this work. I used Terry Guo‘s procedure to build and deploy the sized optimized newlib-nano libraries.

Advertisements

About istarc

Embedded Systems Developer.
This entry was posted in Embedded Systems, STM32F4 and tagged , , , , , . Bookmark the permalink.

2 Responses to STM32F4 – Build Your Own GNU ARM Cross-Toolchain From Scratch

  1. Alexander says:

    Hi (sorry for my bad english … ^^’), very good tutorial, but i have a question.
    Where did you find the list of availables flags for each configure script (for gcc/binutils/gdb and newlib) ???

    For example:
    –enable-interwork –enable-multilib –with-gnu-as –with-gnu-ld ……

    I found these flags in the gcc documentation (gcc/doc directory), but not in the binutils (just some flags like –with-gnu-ld, but not –with-gnu-as), gdb or newlib.

    I tried with: rgrep “enable-multilib” on binutils, newlib and gdb directories but no result (i don’t know where come from these flags ^^’).

    Thank you very much.

    • istarc says:

      Hi Alexander,

      I used various Internet sources including https://launchpad.net/gcc-arm-embedded (source, of course) to get appropriate build flags.

      Try to grep the “configure” file in the binutils folder to get more info than with the conventional “./configure –help” method.

      I hope this helps you further.

      Best regards, Iztok

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s