TITLE: Cross compiling from Linux for the Cortex-M
AUTHOR: Chuck McManis
LAST UPDATE: 01-Apr-2019

Introduction

I find it is not often appreciated by new engineers the change that the gcc compiler represents. Prior to the ubiquitous availability of high performance workstations based on the x86 architecture, language compilers were a thriving industry. Buying a compiler for a particular instruction set architeture could cost thousands of dollars to acquire and hundreds of dollars a year for maintenance and patches.

The conditions that allowed that situation to persist were that each operating system had its own calling conventions or application binary interface (ABI) as well as libraries. And while the languages were standardized, the code that converted them from text into machine code was not. Further, compilation and optimation required what was at the time, massive amounts of memory and compute cycles. Because microprocessors had limited amounts of memory, storage, and I/O capabilities, it was infeasible to run the compiler “natively” and so cross compilers were used. These compilers ran on one operating system and compiled code to operate on a microprocessor once it had been programmed into read only memory (ROM) or erasable read only memory (EPROM).

The AVR Targets C

As most microprocessors were programmed in assembly language, not a lot of thought was given to compiling “high level” languages, such as C, to run on them. That changed in 1997 when Atmel introduced their AVR microprocessor. It has been specifically designed to be amenable to something like C. And while many were skeptical, this was a phenomenally good choice on the part of Atmel and the AVR line gained rapid acceptance.

The popularity of the chip resulted in adding a “port” for the gcc compiler to output machine code for the AVR series of chips. Gcc was free, and it could compile C that was small enough to fit into the FLASH available on the chips. Suddenly it was a lot simpler for a lot of people to get code running on a microprocessor because assembly language was very intimidating! This also spawned a community called AVR freaks that pushed things still further. When the chip was adopted by the Arduino effort, it became a defacto standard.

But building a cross compiler, using gcc to compile C into machine language for the AVR, was still a complicated task. Pre-built versions called “gcc-avr” began to circulate and for many operating systems you could just download gcc-avr and install it.

Cross Tools and ARM

The success of gcc-avr spawned efforts to make cross compilers for other architectures, the Motorola 68HC11, the Hitachi SH4, even the Intel 8051. But a cross compiler is more than just a compiler, it is the compiler, a linker, a library manager, and a set of standard libraries built to support the calling convention or ABI of the target chip.

Dan Kegel put together a set of scripts that would “automagically” build all of the various component pieces of the compiler. This collection was called Cross Tool. It would start with the native compiler to build a cross compiler, then using the cross-compiler would build the libraries. The output was typically Intel hex records or Motorola “S” records (srec). Both formats were understood by EPROM and microprocessor programming tools at the time.

ARM took notice of the success of the AVR experiment and so when they set out to build the Cortex-M family, one of their goals was to make it “compiler” friendly. They succeeded.

An engineer, Piotr Esden-Tempski, started with the Cross Tool base and put everything needed to cross compile into a project called the Summon ARM Toolchain. It didn’t cover all of the Cortex-M variants but it did cover the major ones and in particular covered the Cortex-M4 that ST Micro Discovery board called the Butterfly which is the first Cortex-M board I got to play with.

That effort was replaced by a more concerted effort that was hosted on Launchpad as GCC Arm Embedded. And that effort was eventually taken over by ARM itself which dedicated some engineers to take on the task of insuring there were excellent cross compilers available for their microprocessors. This choice by ARM has lead to the Cortex-M rapidly becoming the most popular choice in embedded microprocessors with tens of billions of units shipped.

Easy Sauce – The easy was to cross compiling

The cross compilation toolchain is now hosted by ARM on their website.

You can download the binaries or source code from the ARM developer website. If you get the tar ball it is a simple matter of untarring it somewhere that your system has space and then adding the internal bin directory to your path. However, if you do that you won’t get regular updates so the version you are using will not change nor will you get bug fixes.

Fortunately you can get bug fixes and updates if you add the ARM PPA to your list of APT sources and then install the package from there. That starts with you doing this:

$ sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa
 This PPA is an alternative to toolchain released at
https://launchpad.net/gcc-arm-embedded. The source codes for both are same.
Currently supports Ubuntu 10.04/12.04/14.04/14.10 32 and 64 bit.

Detailed explanations to Launchpad PPA can be found at https://help.launchpad.net/Packaging/. That website explains how a PPA is set up and how to add existing PPA and install software from it.

Here are quick steps to install toolchain from this PPA on Ubuntu before 14.04:

Step1: Inside Ubuntu, open a terminal and input “sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa”

Step2: Continue to input “sudo apt-get update”

Step3: Continue to input to install toolchain “sudo apt-get install gcc-arm-embedded”

To remove installed toolchain, just input “sudo apt-get remove gnu-arm-embedded”. To update the toolchain, just repeat above step2 and step3.

If it reports error message of conflict to gcc-arm-none-eabi, which is likely if upgrading from 4.x to 5+, please uninstall it first with: “sudo apt-get remove gcc-arm-none-eabi”

Questions should be asked at https://answers.launchpad.net/gcc-arm-embedded

Bug can be filed at https://bugs.launchpad.net/gcc-arm-embedded/+filebug. It is highly encouraged to ask question first before filing a bug. More info: https://launchpad.net/team-gcc-arm-embedded/+archive/ubuntu/ppa Press [ENTER] to continue or Ctrl-c to cancel adding it. ** press the Enter key ** Hit:1 http://us.archive.ubuntu.com/ubuntu bionic InRelease Get:2 http://us.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB] Get:3 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB] Get:4 http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu bionic InRelease [15.9 kB] Get:5 http://us.archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB] Get:6 http://us.archive.ubuntu.com/ubuntu bionic-updates/main amd64 DEP-11 Metadata [278 kB] Get:7 http://us.archive.ubuntu.com/ubuntu bionic-updates/main DEP-11 48x48 Icons [66.7 kB] Get:8 http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu bionic/main i386 Packages [592 B] Get:9 http://us.archive.ubuntu.com/ubuntu bionic-updates/main DEP-11 64x64 Icons [123 kB] Get:10 http://us.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 DEP-11 Metadata [203 kB] Get:11 http://us.archive.ubuntu.com/ubuntu bionic-updates/universe DEP-11 48x48 Icons [190 kB] Get:12 http://us.archive.ubuntu.com/ubuntu bionic-updates/universe DEP-11 64x64 Icons [349 kB] Get:13 http://security.ubuntu.com/ubuntu bionic-security/main amd64 DEP-11 Metadata [204 B] Get:14 http://us.archive.ubuntu.com/ubuntu bionic-updates/multiverse amd64 DEP-11 Metadata [2468 B] Get:15 http://us.archive.ubuntu.com/ubuntu bionic-backports/universe amd64 DEP-11 Metadata [7352 B] Get:16 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 DEP-11 Metadata [20.7 kB] Get:17 http://security.ubuntu.com/ubuntu bionic-security/universe DEP-11 48x48 Icons [12.2 kB] Get:18 http://security.ubuntu.com/ubuntu bionic-security/universe DEP-11 64x64 Icons [45.2 kB] Get:19 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 DEP-11 Metadata [2464 B] Get:20 http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu bionic/main amd64 Packages [592 B] Get:21 http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu bionic/main Translation-en [232 B] Fetched 1570 kB in 2s (876 kB/s) Reading package lists… Done $

And while the text says version 10, 12, and 14 of Ubuntu are supported, in fact it supports all versions after 10.

Next steps are simple as well, apt-get-update to update your package data base and apt install gcc-arm-embedded to get the latest compiler tools.

$ sudo apt-get update
Hit:1 http://us.archive.ubuntu.com/ubuntu bionic InRelease
Hit:2 http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu bionic InRelease              
Hit:3 http://security.ubuntu.com/ubuntu bionic-security InRelease                             
Hit:4 http://us.archive.ubuntu.com/ubuntu bionic-updates InRelease       
Hit:5 http://us.archive.ubuntu.com/ubuntu bionic-backports InRelease
Reading package lists... Done                      
$ sudo apt-get install gcc-arm-embedded
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  libisl15
The following NEW packages will be installed:
  gcc-arm-embedded libisl15
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Need to get 69.4 MB of archives.
After this operation, 453 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://us.archive.ubuntu.com/ubuntu bionic/universe amd64 libisl15 amd64 0.18-4 [548 kB]
Get:2 http://ppa.launchpad.net/team-gcc-arm-embedded/ppa/ubuntu bionic/main amd64 gcc-arm-embedded amd64 7-2018q2-1~bionic1
[68.9 MB]
Fetched 69.4 MB in 1min 58s (588 kB/s)                                                                                                              
Selecting previously unselected package libisl15:amd64.
(Reading database ... 192056 files and directories currently installed.)
Preparing to unpack .../libisl15_0.18-4_amd64.deb ...
Unpacking libisl15:amd64 (0.18-4) ...
Selecting previously unselected package gcc-arm-embedded.
Preparing to unpack .../gcc-arm-embedded_7-2018q2-1~bionic1_amd64.deb ...
Unpacking gcc-arm-embedded (7-2018q2-1~bionic1) ...

dpkg: error processing archive /var/cache/apt/archives/gcc-arm-embedded_7-2018q2-1~bionic1_amd64.deb (--unpack):
 trying to overwrite '/usr/lib/libcc1.so.0.0.0', which is also in package gcc-avr 1:5.4.0+Atmel3.6.0-1build1
dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)
Errors were encountered while processing:
 /var/cache/apt/archives/gcc-arm-embedded_7-2018q2-1~bionic1_amd64.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

$

And what you can see is that in 18.04 at the moment there is a conflict between the gcc-avr and the gcc-arm-embedded packages where they both want libcc1.so. I solved this problem by removing the gcc-avr package (I am not planning on doing Arduino programming on this system) but if this is something you need then track the bug report on the Launchpad.net site.

When successful it looks like this:

$ sudo apt-get install gcc-arm-embedded
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  libisl15
The following NEW packages will be installed:
  gcc-arm-embedded libisl15
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/69.4 MB of archives.
After this operation, 453 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Selecting previously unselected package libisl15:amd64.
(Reading database ... 187116 files and directories currently installed.)
Preparing to unpack .../libisl15_0.18-4_amd64.deb ...
Unpacking libisl15:amd64 (0.18-4) ...
Preparing to unpack .../gcc-arm-embedded_7-2018q2-1~bionic1_amd64.deb ...
Unpacking gcc-arm-embedded (7-2018q2-1~bionic1) ...
Setting up libisl15:amd64 (0.18-4) ...
Processing triggers for libc-bin (2.27-3ubuntu1) ...
Setting up gcc-arm-embedded (7-2018q2-1~bionic1) ...
$

Then you can test to see that you have the latest version with:

$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2018-q3-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch
revision 261907]
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$

And there you go, ready to compile your Cortex-M code into something you can load into a chip and play with. A couple of things I find handy for bash shell users is to add the following lines into your .bashrc or .bash_aliases file:

alias arm-gdb=arm-none-eabi-gdb
alias arm-gcc=arm-none-eabi-gcc

That way you can launch the cross compiler with just arm-gcc and the debugger with just arm-gdb.