Editor's note: In
this series of ten articles Miro Samek of Quantum Leaps details
developing apps on the ARM processor using QNU, complete with source
code in C and C++.
Earlier in this series he provided the basics of the design (Part
1), a
simple LED blinker application; the generic startup code and low level
initialization (Part
2) and the linker script for the C/C++ ARM-9 based
Blinky application that causes four user LEDs to light up on the Atmel
AT91SAM7S-EK (Part
3).
In this part I describe the C and C++ compiler options that allow
freely mixing ARM and Thumb code, as well as supporting
fine-granularity code sections for functions. The code accompanying
this article is available online at Embedded.com's Downloadable Code page.
Compiler Options for C
The compiler options for C are defined in the Makefile located in the
c_blinky subdirectory. The Makefile specifies different options for
building debug and release configurations and allows compiling to ARM
or Thumb on the module-by-module basis.
 |
| Listing
1. Compiler options used for C project, debug configuration (a) and
release configuration (b). |
Listing 1 above shows the
most important compiler options for C, which are:
(1) "mcpu option specifies
the name of the target ARM processor. GCC uses this name to determine
what kind of instructions it can emit when generating assembly code.
Currently, the ARM_CPU symbol is set to arm7tdmi.
(2) "mthumb-interwork allows
freely mixing ARM and Thumb code
(3) "mlong-calls tells the
compiler to perform function calls by first loading the address of the
function into a register and then performing a subroutine call on this
register (BX instruction). This allows the called function to be
located anywhere in the 32-bit address space, which is sometimes
necessary for control transfer between ROM- and RAM-based code.
Note: The need for long calls
really depends on the memory map of a given ARM-based MCU. For example,
the Atmel AT91SAM7 family actually does not require long calls between
ROM and RAM, because the memories are less than 25-bits apart. On the
other hand, the NXP LPC2xxx family requires long calls because the ROM
and RAM are mapped to addresses 0x0 and 0x40000000, respectively. The
long-calls option is safe for any memory map.
(4) "ffunction-sections
instructs the compiler to place each function into its own section in
the output file. The name of the function determines the section's name
in the output file. For example, the function Blinky_shift() is placed
in the section .text.Blinky_shift. You can then choose to locate just
this section in the most appropriate memory, such as RAM.
(5) "O chooses the
optimization level. The release configuration has a higher optimization
level (5b).
(6) the release
configuration defines the macro NDEBUG.
Compiler Options for C++
The compiler options for C++ are defined in the Makefile located in the
cpp_blinky subdirectory. The Makefile specifies different options for
building the Debug and Release configurations and allows compiling to
ARM or Thumb on the module-by-module basis.
 |
| Listing
2. Compiler options used for C++ project. |
The C++ Makefile located in the directory cpp_blinky uses the same
options as C discussed in the previous section plus two options that
control the C++ dialect:
(1) "fno-rtti disables
generation of information about every class with virtual functions for
use by the C++ runtime type identification features (dynamic_cast and
typeid).
Disabling RTTI eliminates several KB of support code from the C++
runtime library (assuming that you don't link with code that uses
RTTI). Note that the dynamic_cast operator can still be used for casts
that do not require runtime type information, i.e. casts to void * or
to unambiguous base classes.
(1) "fno-exceptions stops
generating extra code needed to propagate exceptions, which can produce
significant data size overhead. Disabling exception handling eliminates
several KB of support code from the C++ runtime library (assuming that
you don't link external code that uses exception handling).
Reducing the Overhead of C++
The compiler options controlling the C++ dialect are closely related to
reducing the overhead of C++. However, disabling RTTI and exception
handling at the compiler level is still not enough to prevent the GNU
linker from pulling in some 50KB of library code.
This is because the standard new and delete operators throw
exceptions and therefore require the library support for exception
handling. (The new and delete operators are used in the static
constructor/destructor invocation code, so are linked in even if you
don't use the heap anywhere in your application.)
Most low-end ARM-based MCUs cannot tolerate 50KB code overhead. To
eliminate that code you need to define your own, non-throwing versions
of global new and delete, which is done in the module mini_cpp.cpp
located in the directory cpp_blinky1.
 |
| Listing
3 The mini_cpp.cpp module with non-throwing new and delete as well as
dummy version of __aeabi_atexit(). |
Listing 3 above shows the
minimal C++ support that eliminates entirely the exception handling
code. The highlights are as follows:
(1) The standard version of
the operator new throws std::bad_alloc exception. This version
explicitly throws no exceptions. This minimal implementation uses the
standard malloc().
(2) This minimal
implementation uses the standard free().
(3) The function
__aeabi_atexit() handles the static destructors. In a bare-metal system
this function can be empty because application has no operating system
to return to, and consequently the static destructors are never called.
Finally, if you don't use the heap, which you shouldn't in robust,
deterministic applications, you can reduce the C++ overhead even
further. The module no_heap.cpp provides dummy empty definitions of
malloc() and free():

Next, in Part 5, I'll describe the options
for fine-tuning the application by selective ARM/Thumb compilation and
by placing hot-spot parts of the code in RAM. Stay tuned.
To read Part 1, go to What's
need to get started.
To read Part 2, go to
Startup code and the low level initialization
To read Part 3, go to The
Linker Script.
To download the C and C++
source code associated with this article series, go to Embedded.com's Downloadable Code page,
or go to Blinky for C and Blinky for C++ to download the
Zip files.
Miro
Samek, Ph.D., is president of Quantum
Leaps, LLC. He can be contacted at miro@quantum-leaps.com.
References
[1] GNU Assembler (as) HTML
documentation included in the CodeSourcery Toolchain for ARM.
[2] IAR Systems, "ARM IAR C/C++
Compiler Reference Guide for Advanced RISC Machines Ltd's ARM Cores",
Part number: CARM-13, Thirteenth edition: June 2006. Included in the
free EWARM KickStart edition
[3] Lewin A.R.W. Edwards, "Embedded
System Design on a Shoestring", Elsevier 2003.
[4] ARM
Projects.
[5] GNU ARM
toolchain.