STM32/ARM Cortex-M3 HOWTO: Development under Ubuntu (Debian)

Crosscompile with the gcc toochain.

How hard is it to build your own toolchain?

To build a toolchain is not that hard, but the tricky part starts when you have to prove that it is working and to find out what flags you need to use to make sure that it is optimal for your platform. Therefor most users take the easy way (and frankly the most sensible route), and download a pre fixed toolchain like this one from CodeSourcery (Sourcery G++ Lite for ARM EABI). But this is described on the next page called "CodeSourcery gcc".

But since that is not as much fun as doing it your self, and getting some gray hair, this is why I created this page.

First out it is good to know that this guide is quite close to that project so you could probably use a lot of the documentation that exists there and just apply it here, http://www.codesourcery.com/sgpp/lite/arm/portal/release1033.

The basic prototype for this guide is more or less what those guys did, and described at this pages:

But I had to improvise and add some patches to get it to work, and I also focused on using versions:

Then I created some simple scripts to the work. The scripts just goes from top to bottom, and if anything fails it stops and you have to fix the problem manually and restart again. When you read the script this break function is called did_it_work, and more or less just checks what the last command returned as exit status. That is why this script is filled with those did_it_work lines.

To make the scripts more available I put them into a github repo, so let's install some git sw, clone that repo and get going.

sudo aptitude install git-core git-doc git-gui gitk
mkdir -p ~/stm32/
cd ~/stm32/
git clone git://github.com/jsiei97/FunTechCortexMX_gcc.git FunTechCortexMX_gcc
cd FunTechCortexMX_gcc

Then we follow the instructions in the README file.

chmod 755 setup.sh
./setup.sh 

But before you execute this script, make sure that you take a couple of minutes and read what it is about to do with your pc since it is never a good idea to execute random scripts you find on the internet without understanding what they do.

github snapshot

After this point I will show a snapshot in time from the github repo, so the repo could have been improved. So please use the github one when you build.

But let's have a look at what is in there. The first one is setup.sh, and there is not much fun here. It just calls install.sh and stores a log file.

Filename: setup.sh

#!/bin/bash 

source include.sh

date=`date +"%F_%H%M%S"`

chmod 755 *.sh
did_it_work $? 

./install.sh 2>&1 | tee log_stm32_build_$date.log

install.sh will then call all of the other scripts one by one.

Filename: install.sh

#!/bin/bash 

source include.sh

echo "Install some packages..."
./solve_dependencies.sh
did_it_work $? 

echo "Create the work dirs"
./create_dirs.sh
did_it_work $? 

echo "Build binutils"
./build_00_binutils.sh
did_it_work $? 

echo "Build gcc and newlib"
./build_01_gcc_newlib.sh
did_it_work $? 

echo "Build Insight"
./build_02_insight.sh
did_it_work $? 

echo "Build GDB"
./build_03_gdb.sh
did_it_work $? 

echo "Modify .bashrc"
echo "Add a file to modify the PATH..."
./add_rc_files.sh
did_it_work $? 

echo "Done:"$0
exit 0

But at the top of all the files we include always include a script called include.sh. And this is where we store the installation paths, and other things that shall be available from all the other scripts.

Filename: include.sh

#!/bin/bash 

function did_it_work {
    code=$1
    if [ ! $code = 0 ]
    then
        echo "Error failure: code $code "
        exit 1
    fi
}

stm_dir=~/stm32/
stm_dir_bin=$stm_dir"/bin"
stm_dir_tools=$stm_dir"/stm32-tools"
stm_dir_install=/usr/local/stm32

export TOOLPATH_STM32=$stm_dir_install
export PATH=$TOOLPATH_STM32/bin:$PATH
export PARALLEL=-j`getconf _NPROCESSORS_ONLN`


Then there is two scripts that will install some packages that the installation depends on, and create the dirs where we install the stuff. Those scripts are also the only scripts that call sudo, so if you are doing this on a pc where you don't have the rights to run sudo this is the place to look for alternative solutions.

Filename: solve_dependencies.sh

#!/bin/bash 

source include.sh

echo "Install some depends (sudo may ask for your passwd)"
sudo aptitude install build-essential flex bison \
    libgmp3-dev libmpfr-dev autoconf \
    texinfo libncurses5-dev libexpat1 libexpat1-dev \
    tk tk8.4 tk8.4-dev
did_it_work $? 

echo "Done:"$0
exit 0

Filename: create_dirs.sh

#!/bin/bash 

source include.sh

echo "Dirs"
echo " work    = "$stm_dir_tools
echo " install = "$stm_dir_install
echo ""

sudo mkdir -p $stm_dir_install
did_it_work $? 
sudo chown $USER.users $stm_dir_install
did_it_work $? 

mkdir -p $stm_dir_bin
did_it_work $? 
mkdir -p $stm_dir_tools
did_it_work $? 

echo "Done:"$0
exit 0

And finally we start to build binutils, gcc and gdb.

Filename: build_00_binutils.sh

#!/bin/bash 

source include.sh

arch_url=http://ftp.gnu.org/gnu/binutils/binutils-2.20.tar.bz2
arch_dir=binutils-2.20
arch_name=binutils-2.20.tar.bz2

cd $stm_dir_tools
did_it_work $? 

if [ -f $arch_name ]; then
    echo file exists - $arch_name
else
    wget $arch_url
    did_it_work $? 
fi

if [ -d $arch_dir ]; then
    echo old dir exists - rm -rf $arch_dir
    rm -rf $arch_dir
    did_it_work $? 
fi

tar -xvjf $arch_name
did_it_work $? 

#Get patch/patches...
patch_file=binutils-2.20_tc-arm.c.patch
if [ -f $patch_file ]; then
    echo file exists - $patch_file
else 
    wget http://fun-tech.se/stm32/gcc/binutils-2.20_tc-arm.c.patch
    did_it_work $? 
fi
patch $arch_dir/gas/config/tc-arm.c $patch_file
did_it_work $? 

cd $arch_dir
did_it_work $? 

mkdir build
did_it_work $? 
cd build
did_it_work $? 
../configure --target=arm-none-eabi  \
             --prefix=$TOOLPATH_STM32  \
             --enable-interwork  \
             --enable-multilib  \
             --with-gnu-as  \
             --with-gnu-ld  \
             --disable-nls 
did_it_work $? 

make $PARALLEL 
did_it_work $? 
make install 
did_it_work $? 


echo "Done:"$0
exit 0

Filename: build_01_gcc_newlib.sh

#!/bin/bash 

source include.sh

#################
### GCC #########
#################

arch_url=ftp://ftp.sunet.se/pub/gnu/gcc/releases/gcc-4.4.4/gcc-4.4.4.tar.bz2
arch_dir=gcc-4.4.4
arch_name=gcc-4.4.4.tar.bz2

cd $stm_dir_tools
did_it_work $? 

if [ -f $arch_name ]; then
    echo file exists - $arch_name
else
    wget $arch_url
    did_it_work $? 
fi

if [ -d $arch_dir ]; then
    echo old dir exists - rm -rf $arch_dir
    rm -rf $arch_dir
    did_it_work $? 
fi

tar -xvjf $arch_name
did_it_work $? 

cd $arch_dir
did_it_work $? 

mkdir build
did_it_work $? 
cd build
did_it_work $? 
../configure --target=arm-none-eabi  \
             --prefix=$TOOLPATH_STM32  \
             --enable-interwork  \
             --enable-multilib  \
             --enable-languages="c,c++"  \
             --with-newlib  \
             --without-headers  \
             --disable-shared  \
             --with-gnu-as  \
             --with-float=soft \
             --with-cpu=cortex-m3 \
             --with-tune=cortex-m3 \
             --with-mode=thumb \
             --disable-libssp \
             --with-gnu-ld 
did_it_work $? 

make $PARALLEL all-gcc 
did_it_work $? 
make install-gcc 
did_it_work $? 


#################
## NewLib #######
#################

arch_url=ftp://sources.redhat.com/pub/newlib/newlib-1.18.0.tar.gz
arch_dir=newlib-1.18.0
arch_name=newlib-1.18.0.tar.gz

cd $stm_dir_tools
did_it_work $? 

if [ -f $arch_name ]; then
    echo file exists - $arch_name
else
    wget $arch_url
    did_it_work $? 
fi

if [ -d $arch_dir ]; then
    echo old dir exists - rm -rf $arch_dir
    rm -rf $arch_dir
    did_it_work $? 
fi

tar -xvzf $arch_name
did_it_work $? 

cd $arch_dir
did_it_work $? 
mkdir build
did_it_work $? 
cd build
did_it_work $? 
../configure --target=arm-none-eabi  \
             --prefix=$TOOLPATH_STM32  \
             --enable-interwork  \
             --disable-newlib-supplied-syscalls  \
             --with-gnu-ld  \
             --with-gnu-as  \
             --disable-shared 
did_it_work $? 


# http://gcc.gnu.org/gcc-4.4/changes.html
# GCC now supports the VFPv3 variant with 16 double-precision 
# registers with -mfpu=vfpv3-d16. The option -mfpu=vfp3 has been 
# renamed to -mfpu=vfpv3.
# GCC now supports the -mfix-cortex-m3-ldrd option to work around 
# an erratum on Cortex-M3 processors.

# http://www.codesourcery.com/sgpp/lite/arm/portal/kbentry27
# Use the compiler options -mfpu=vfp -mfloat-abi=softfp to enable VFP instructions.
# If you have a VFPv3 target you may use -mfpu=vfp3 -mfloat-abi=softfp i
# to enable VFPv3 instructions.
# Using -mfloat-abi=hard generates code that is not ABI-compatible with 
# other floating-point options. 

#-mabi=aapcs \

make $PARALLEL CFLAGS_FOR_TARGET="-ffunction-sections \
                        -fdata-sections \
                        -DPREFER_SIZE_OVER_SPEED \
                        -D__OPTIMIZE_SIZE__ \
                        -Os \
                        -fomit-frame-pointer \
                        -mcpu=cortex-m3 \
                        -mthumb \
                        -mfix-cortex-m3-ldrd \
                        -mfloat-abi=softfp \
                        -D__thumb2__ \
                        -D__BUFSIZ__=256" \
               CCASFLAGS="-mcpu=cortex-m3 \
                          -mthumb \
                          -mfix-cortex-m3-ldrd \
                          -D__thumb2__" 
did_it_work $? 

make install 
did_it_work $? 


#################
### More GCC ####
#################

cd $stm_dir_tools
did_it_work $? 
cd gcc-4.4.4/build
did_it_work $? 
make $PARALLEL CFLAGS="-mcpu=cortex-m3 -mthumb" \
     CXXFLAGS="-mcpu=cortex-m3 -mthumb" \
     LIBCXXFLAGS="-mcpu=cortex-m3 -mthumb" \
     all 
did_it_work $? 

make install 
did_it_work $? 



echo "Done:"$0
exit 0

Filename: build_02_insight.sh

#!/bin/bash 

source include.sh

#################
### insight #####
#################

arch_url=ftp://sourceware.org/pub/insight/releases/insight-6.8-1.tar.bz2
arch_dir=insight-6.8-1
arch_name=insight-6.8-1.tar.bz2
gdb_ver=6.8

cd $stm_dir_tools
did_it_work $? 

if [ -f $arch_name ]; then
    echo file exists - $arch_name
else
    wget $arch_url
    did_it_work $? 
fi

if [ -d $arch_dir ]; then
    echo old dir exists - rm -rf $arch_dir
    rm -rf $arch_dir
    did_it_work $? 
fi

tar -xvjf $arch_name
did_it_work $? 

cd $arch_dir
did_it_work $? 
mkdir build
did_it_work $? 
cd build
did_it_work $? 
../configure --target=arm-none-eabi \
                      --prefix=$TOOLPATH_STM32 \
                      --enable-languages=c,c++ \
                      --enable-thumb \
                      --enable-interwork \
                      --enable-multilib \
                      --enable-tui \
                      --with-newlib \
                      --disable-werror \
                      --disable-libada \
                      --disable-libssp \
                      --with-expat 
did_it_work $? 

make $PARALLEL 
did_it_work $? 
make install 
did_it_work $? 

cd $TOOLPATH_STM32/bin
did_it_work $? 
mv arm-none-eabi-gdb    arm-none-eabi-gdb-$gdb_ver
did_it_work $? 
mv arm-none-eabi-gdbtui arm-none-eabi-gdbtui-$gdb_ver
did_it_work $? 
mv arm-none-eabi-run    arm-none-eabi-run-$gdb_ver
did_it_work $? 

echo "Done:"$0
exit 0

Filename: build_03_gdb.sh

#!/bin/bash 

source include.sh

#################
### gdb #########
#################
arch_url=http://ftp.gnu.org/gnu/gdb/gdb-7.2.tar.bz2
arch_dir=gdb-7.2
arch_name=gdb-7.2.tar.bz2
gdb_ver=7.2

cd $stm_dir_tools
did_it_work $? 

if [ -f $arch_name ]; then
    echo file exists - $arch_name
else
    wget $arch_url
    did_it_work $? 
fi

if [ -d $arch_dir ]; then
    echo old dir exists - rm -rf $arch_dir
    rm -rf $arch_dir
    did_it_work $? 
fi

tar -xvjf $arch_name
did_it_work $? 

cd $arch_dir
did_it_work $? 

mkdir build
did_it_work $? 
cd build
did_it_work $? 
../configure --target=arm-none-eabi \
                      --prefix=$TOOLPATH_STM32  \
                      --enable-languages=c,c++ \
                      --enable-thumb \
                      --enable-interwork \
                      --enable-multilib \
                      --enable-tui \
                      --with-newlib \
                      --disable-werror \
                      --disable-libada \
                      --disable-libssp 
did_it_work $? 

make $PARALLEL 
did_it_work $? 
make install 
did_it_work $? 

cd $TOOLPATH_STM32/bin
did_it_work $? 
mv arm-none-eabi-gdb    arm-none-eabi-gdb-$gdb_ver
did_it_work $? 
mv arm-none-eabi-gdbtui arm-none-eabi-gdbtui-$gdb_ver
did_it_work $? 
mv arm-none-eabi-run    arm-none-eabi-run-$gdb_ver
did_it_work $? 

ln -s arm-none-eabi-gdb-$gdb_ver    arm-none-eabi-gdb
did_it_work $? 
ln -s arm-none-eabi-gdbtui-$gdb_ver arm-none-eabi-gdbtui
did_it_work $? 
ln -s arm-none-eabi-run-$gdb_ver    arm-none-eabi-run
did_it_work $? 


echo "Done:"$0
exit 0

Filename: add_rc_files.sh

#!/bin/bash 

source include.sh

cat >> ~/.bashrc << EOF
# STM32 BEGIN
#Multi process build 
export PARALLEL=-j\`getconf _NPROCESSORS_ONLN\`

#STM32 gcc...
export TOOLPATH_STM32=`echo $stm_dir_install`
#export PATH=\${TOOLPATH_STM32}/bin:\$PATH
# STM32 END

EOF
did_it_work $? 


cat > $stm_dir_bin/stm32_setup.sh << EOF
#Autogen do not edit...
export PATH=${TOOLPATH_STM32}/bin:\$PATH

EOF
did_it_work $? 


echo "Done:"$0
exit 0

That's it.

Ubuntu aptitude and some env

Since I kind of like vim, I always makes sure that vim is installed, and I also always install some other good dev tools.

sudo aptitude install vim vim-full vim vim-scripts vim-doc vim-gui-common vim-gnome \
    exuberant-ctags cscope \
    okteta ghex \
    meld kompare

The last two is some graphical diff program and those are hard to live without, in Ubuntu 8.04 I use kdiff3 but in Ubuntu 9.04 I mostly use meld instead (but kompare is also nice).

Then you can decide if you always would like to have this toolchain in the path, if so then add this to your ~/.bashrc.

echo '#STM32 gcc...' >> ~/.bashrc 
echo 'export PATH=${TOOLPATH_STM32}/bin:$PATH' >> ~/.bashrc
echo '' >> ~/.bashrc

Otherwise you can add it when you need it by running this.

source ~/stm32/bin/stm32_setup.sh 
Fatal error: Uncaught Error: Call to a member function link_ext() on null in /customers/9/b/5/fun-tech.se/httpd.www/stm32/class.page.php:62 Stack trace: #0 /customers/9/b/5/fun-tech.se/httpd.www/stm32/gcc/index.php(227): page->getFoot() #1 {main} thrown in /customers/9/b/5/fun-tech.se/httpd.www/stm32/class.page.php on line 62