Building Bare-Metal ARM Systems with GNU: Part 4 - Embedded.com

Building Bare-Metal ARM Systems with GNU: Part 4

Editor's note: Inthis series of ten articles Miro Samek of Quantum Leaps detailsdeveloping apps on the ARM processor using QNU, complete with sourcecode in C and C++.

Earlier in this series he provided the basics of the design (Part1), asimple LED blinker application; the generic startup code and low levelinitialization (Part2) and the linker script for the C/C++ ARM-9 basedBlinky application that causes four user LEDs to light up on the AtmelAT91SAM7S-EK (Part3).

In this part I describe the C and C++ compiler options that allowfreely mixing ARM and Thumb code, as well as supportingfine-granularity code sections for functions. The code accompanyingthis 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 thec_blinky subdirectory. The Makefile specifies different options forbuilding debug and release configurations and allows compiling to ARMor Thumb on the module-by-module basis.

Listing1. Compiler options used for C project, debug configuration (a) andrelease configuration (b).

Listing 1 above shows themost important compiler options for C, which are:

(1) “mcpu option specifiesthe name of the target ARM processor. GCC uses this name to determinewhat kind of instructions it can emit when generating assembly code.Currently, the ARM_CPU symbol is set to arm7tdmi.

(2) “mthumb-interwork allowsfreely mixing ARM and Thumb code

(3) “mlong-calls tells thecompiler to perform function calls by first loading the address of thefunction into a register and then performing a subroutine call on thisregister (BX instruction). This allows the called function to belocated anywhere in the 32-bit address space, which is sometimesnecessary for control transfer between ROM- and RAM-based code.

Note: The need for long callsreally depends on the memory map of a given ARM-based MCU. For example,the Atmel AT91SAM7 family actually does not require long calls betweenROM and RAM, because the memories are less than 25-bits apart. On theother hand, the NXP LPC2xxx family requires long calls because the ROMand RAM are mapped to addresses 0x0 and 0x40000000, respectively. Thelong-calls option is safe for any memory map.

(4) “ffunction-sectionsinstructs the compiler to place each function into its own section inthe output file. The name of the function determines the section's namein the output file. For example, the function Blinky_shift() is placedin the section .text.Blinky_shift. You can then choose to locate justthis section in the most appropriate memory, such as RAM.

(5) “O chooses theoptimization level. The release configuration has a higher optimizationlevel (5b).

(6) the releaseconfiguration defines the macro NDEBUG.

Compiler Options for C++
The compiler options for C++ are defined in the Makefile located in thecpp_blinky subdirectory. The Makefile specifies different options forbuilding the Debug and Release configurations and allows compiling toARM or Thumb on the module-by-module basis.

Listing2. Compiler options used for C++ project.

The C++ Makefile located in the directory cpp_blinky uses the sameoptions as C discussed in the previous section plus two options thatcontrol the C++ dialect:

(1) “fno-rtti disablesgeneration of information about every class with virtual functions foruse by the C++ runtime type identification features (dynamic_cast andtypeid).

Disabling RTTI eliminates several KB of support code from the C++runtime library (assuming that you don't link with code that usesRTTI). Note that the dynamic_cast operator can still be used for caststhat do not require runtime type information, i.e. casts to void * orto unambiguous base classes.

(1) “fno-exceptions stopsgenerating extra code needed to propagate exceptions, which can producesignificant data size overhead. Disabling exception handling eliminatesseveral KB of support code from the C++ runtime library (assuming thatyou don't link external code that uses exception handling).

Reducing the Overhead of C++
The compiler options controlling the C++ dialect are closely related toreducing the overhead of C++. However, disabling RTTI and exceptionhandling at the compiler level is still not enough to prevent the GNUlinker from pulling in some 50KB of library code.

This is because the standard new and delete operators throwexceptions and therefore require the library support for exceptionhandling. (The new and delete operators are used in the staticconstructor/destructor invocation code, so are linked in even if youdon't use the heap anywhere in your application.)

Most low-end ARM-based MCUs cannot tolerate 50KB code overhead. Toeliminate that code you need to define your own, non-throwing versionsof global new and delete, which is done in the module mini_cpp.cpplocated in the directory cpp_blinky1.

Listing3 The mini_cpp.cpp module with non-throwing new and delete as well asdummy version of __aeabi_atexit().

Listing 3 above shows theminimal C++ support that eliminates entirely the exception handlingcode. The highlights are as follows:

(1) The standard version ofthe operator new throws std::bad_alloc exception. This versionexplicitly throws no exceptions. This minimal implementation uses thestandard malloc().

(2) This minimalimplementation uses the standard free().

(3) The function__aeabi_atexit() handles the static destructors. In a bare-metal systemthis function can be empty because application has no operating systemto 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 evenfurther. The module no_heap.cpp provides dummy empty definitions ofmalloc() and free():

Next, in Part 5, I'll describe the optionsfor fine-tuning the application by selective ARM/Thumb compilation andby placing hot-spot parts of the code in RAM. Stay tuned.

To read Part 1, go to What'sneed to get started.
To read Part 2, go to Startup code and the low level initialization
To read Part 3, go to TheLinker Script.

To download the C and C++ sourcecode 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 theZipfiles.

MiroSamek, Ph.D., is president of QuantumLeaps, LLC. He can be contacted at miro@quantum-leaps.com.

References
[1] GNU Assembler (as) HTMLdocumentation 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 thefree EWARM KickStart edition
[3] Lewin A.R.W. Edwards, “EmbeddedSystem Design on a Shoestring“, Elsevier 2003.
[4] ARMProjects.

[5] GNU ARMtoolchain.

1 thought on “Building Bare-Metal ARM Systems with GNU: Part 4

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.