GCC Compiler for Cortex-M3

From EdWiki

GCC Compiler for Cortex-M3


This documentation demonstrates how to use and optimize the GCC compiler settings for Cortex-M3 (Thumb2) code generation including detailed examples on eh-frame, stubs and vectors.

The usage of the GCC compiler for ARM/Cortex version 4.4.0 and higher is a complex issue and sometimes not quite easy to handle for the beginners. Some settings may result in erroneous outputs of the compiler or linker when used for different target platforms. This article is intended to give support to the beginners on how to set and optimize some common parameters.

Compiler Settings for Cortex-M3 Derivatives

The common settings of the GNU GCC compiler are divided into:

  • Assembler-related
  • C-compiler-related
  • Linker-related

Assembler Parameters

In general, the assembler is running fine with the following options:

arm-none-eabi-as -mcpu=cortex-m3 -gdwarf2 –mthumb filename.s -o filename.o
Specifies the core architecture used.
Specifies the code generation. It should be applied even if the architecture only specifies the Thumb2 instruction set.
Specifies the debug information output. Useful if you want to debug the code.

Assembler Code

Specific entries must be set in the assembler code:

 .text                         /* to specify that code is following */
 .syntax unified
 .global _MyASMRoutine
 .type _ MyASMRoutine, function
 .equ pattern1, 0xAAAAAAAA
 /*.bit pattern */
 .thumb                         /* this causes thumb code generation */
 _ MyASMRoutine:
 /* Push ALL registers to stack */
 Push {r0-r12,r14}
 /* some instructions */
 movw r0, #0x00AA
 cmp r0, #0xAA
 bne _ exitLabel
 /* some more instructions */
 movw r0, #0xAA00
 lsr r0, #8
 cmp r0, #0xAA
 bne _ exitLabel
 /* Pop the stack back */
 pop {r0-r12,r14}
 /* Branch back */
 bx lr

If the functions are called from a C file without taking care of the architecture, the code may produce warnings with the linker, e.g. that "ARM calls" are enabled or that the thumb-interworking is not enabled.

This behavior occurs because of the addressing method used by the C compiler according to the Cortex-M3 architecture.

Normally, the C compiler does not produce any long calls so that the addressing is limited. In other ARM architectures this is fixed by using the thumb-interworking to enable the compiler to build long veneers.

C Compiler Parameters

-c -gdwarf-2 -MD -O0 -trigraphs -mcpu=cortex-m3 -mthumb -Wall -fsigned-char -mlittle-endian 
-xc -mno-thumb-interwork -mno-tpcs-frame -mlong-calls
Compiles or assembles the source files, but does not link.
Produces debugging information in DWARF version 2 format.
The compiler generates intermediate files with the suffix '.d'.
Do not optimize (best for debugging).
Supports ISO C trigraphs.
Specifies the core architecture used.
Specifies the code generation. This option should be applied even if the architecture only specifies the Thumb2 instruction set.
Prints all warnings.
Lets the type char be signed, like signed char.
Generates code for a processor running in 'Little Endian' mode.
Specifies the source language; here: C
Does not use the interworking.
Generates no stack frame that is compliant with the Thumb Procedure Call Standard for all non-leaf functions.
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. (This parameter is used to generate a call over the whole address area which solves the problem to call the assembler routines.)

C Code

Normally, the C code does not include any arguments for the linker or compiler. This will generate sections information for the sections .text, .data and .bss. If modules or variables are intended to be treated in a special way, the code must include this information.

Using Linker-Generated Address Labels

extern unsigned long _etext;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;
extern unsigned long _stacktop;

The labels are available for usage in the code. The label _stacktop is defined in the linker file and defines the upper end of the stack area in the SRAM of the device. The label will be filled by the linker.

All other labels are also generated by the linker and provide information on the SRAM usage.

Placing Constant Data to a Specified Location

typedef void (* const FuncPointer)(void);

A type FuncPointer is defined and a table of entries is generated which builds the vector table of a Cortex-M3 controller. This table must be located at the address 0x0000.0000 on each Cortex-M3 controller.

The address is well-known but the linker must be informed about the way to handle this. The section .vectors which is defined here, is used by the linker file to specify the placement.

void Reset(void);
FuncPointer interrupts[] __attribute__ ((section(".vectors"))) =
     ( IntFctPointer)&_stacktop,  // The initial stack pointer (see above)
      Reset,                     // first vector to Reset routine

Copying the Initialized Data

unsigned long *Src, *Dest;

The pointers defined are used to simply copy the ".data" section to the SRAM at the start of the application.

// Initialize data
// Copy the data segment initializers from flash to SRAM.
Src = &_etext;
for(Dest = &_data; Dest < &_edata; )
   *Dest++ = *Src++;

Clearing the Uninitialized Data

When deleting the uninitialized SRAM segment, the start and end must be known. The linker can provide this information.

// Zero fill the bss segment.
for(Dest = &_bss; Dest < &_ebss; )
    *Dest++ = 0;

In this case, the application must be called from here:

// Call main.

Linker Parameters

The linker can be called with the following options:

--cref -t -static -nostartfiles -Map=.\objects\ -stats -lc -lgcc

Note:If the linker is being invoked indirectly, via a compiler driver (e.g. gcc), then all the linker command line options should be prefixed by -Wl,.

Output a cross reference table.
Traces linked files.
On systems supporting dynamic linking, this prevents linking with the shared libraries.
Does not use the standard system startup files when linking.
Generates a map file.
Displays statistical outputs.
Uses the C library.
Uses the GCC library.

In addition, we have to provide the linker script file option (-T[file]) to the calling options. The linker file describes further code segmentation and memory usage.

Linker Description File (ld File)

To enable the linker to take care of the derivative-specific memory layout and passing the code optimally into the FLASH and SRAM segments, the linker has to know about this.

The input is specified by the file names:


The target-specific parameters contain information about the physical memory layout of the target derivative:

/* Target Specific Parameters */
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00000000
    SRAM (rwx) : ORIGIN = 0x10000000, LENGTH = 0x20000000

Sections define physical opcode or data areas which are connected to specified or default code, constants or variables. In general the sections .text, .data and .bss are defined by default. Additional sections can be defined and located in the physical memory:

/* Layout Information */
/* Section Definitions */

The first section is the .text section containing the startup code and all other opcode. The start address is the first address defined or zero:

.text :
KEEP(*(.vectors .vector.*))

The label vectors must be located at the very beginning and must not be changed.

Next is the startup code and an address label is generated (_startup_code_end__):

__startup_code__ = .;
$(TargetDir)startup.o (.text)    /* Startup code */
__startup_code_end__ = .;

Then the rest of the code is put into the .text section and is aligned to 4 bytes:

*(.text .text.*)
. = ALIGN(4);

.Eh_frame is recommended to be placed by the linker:

*(.rodata .rodata*)
. = ALIGN(4);

With the address label _etext, the .text section is completed and all is located in the physical memory area defined as FLASH:

_etext = .;

The next section is defined as .data containing all the initializing data for the pre-initialized variables:

.data : AT (_etext)

Start the section at the end of the .text section to reside in the non-volatile memory. The startup code must copy this to the RAM before using any initialized variables. Add all segments defined as .data:

_data = .;
*(.data .data.*)

The linker generates an address label _edata and locates the heap with size 0x2000 followed by the stack:

. = ALIGN(4);
_edata = . ;
/* HEAP!!! */
. = . + 0x2000; /* reserved for heap */

On the target segment (SRAM) at the very end, the _stacktop address is set to an absolute address of 0x20000000-4:

*(.stack .stack.*)
. = ALIGN(4);
_stacktop = 0x20000000-4; /* Top of Stack */

The address model for SRAM space is used here:

} > SRAM

Uninitialized data are located in the .bss section. The section can be cleared by the startup code if it is located and the address labels are known in the code.

An address label _bss is built and all elements are filled into this section:

/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
_bss = . ;

Generate an address label _ebss:

*(.bss .bss.*)
. = ALIGN(4);
_ebss = . ;
} > SRAM

Before ending the SECTION part of the linker file, the overall (natural) alignment of the device is specified to 4 byte:

. = ALIGN(4);
_end = . ;
PROVIDE (end = .);

Taking into account the fact that GNU compilers are under permanent development, the version and the changes of newer compilers should be carefully checked and evaluated before updating.