Chapter 6. CS3™: The CodeSourcery Common Startup Code Sequence

CS3 is CodeSourcery's low-level board support library. This chapter describes the organization of the system startup code and tells you how you can customize it, such as by defining your own interrupt handlers. This chapter also documents the boards supported by Sourcery G++ Lite and the compiler and linker options you need to use with them.

Table of Contents

Startup Sequence
Exit and Embedded Systems
Memory Layout
Interrupt Vectors and Handlers
Linker Scripts
Supported Boards for ARM EABI
Interrupt Vector Tables
Regions and Memory Sections

Many developers turn to the GNU toolchain for its cross-platform consistency: having a single system support so many different processors and boards helps to limit risk and keep learning curves gentle. Historically, however, the GNU toolchain has lacked a consistent set of conventions for processor- and board-level initialization, language run-time setup, and interrupt and trap handler definition.

The CodeSourcery Common Startup Code Sequence (CS3) addresses this problem. For each supported system, CS3 provides a set of linker scripts describing the system's memory map, and a board support library providing generic reset, startup, and interrupt handlers. These scripts and libraries all follow a standard set of conventions across a range of processors and boards.

Startup Sequence

CS3 divides the startup sequence into three phases:

  • In the hard reset phase, we initialize the memory controller and set up the memory map.

  • In the assembly initialization phase, we prepare the stack to run C code, and jump to the C initialization function.

  • In the C initialization phase, we initialize the data areas, run constructors for statically-allocated objects, and call main.

The hard reset and assembly initialization phases are necessarily written in assembly language; at reset, there may not yet be stack to hold compiler temporaries, or perhaps even any RAM accessible to hold the stack. These phases do the minimum necessary to prepare the environment for running simple C code. Then, the code for the final phase may be written in C; CS3 leaves as much as possible to be done at this point.

The CodeSourcery board support library provides default code for all three phases. The hard reset phase is implemented by board-specific code. The assembly initialization phase is implemented by code specific to the processor family. The C initialization phase is implemented by generic code.

The Hard Reset Phase

This phase is responsible for initializing board-specific registers, such as memory base registers and DRAM controllers, or scanning memory to check the available size. It is written in assembler and ends with a jump to _start, which is where the assembly initialization phase begins.

The hard reset code is in a section named .cs3.reset. The section must define a symbol named __cs3_reset_sys, where sys is a name for the board being initialized; for example, the reset code for a Cyclone III Cortex-M1 board would be named __cs3_reset_cycloneiii_cm1. The linker script defines the symbol __cs3_reset to refer to this reset address. If you need to refer to the reset address from generic code, you can use this non-specific __cs3_reset name.

When using the Sourcery G++ Debug Sprite, the Sprite is responsible for carrying out the initialization done in this phase. In this case execution begins at the start of the assembly initialization phase, except for simulators as described below.

Some simulators provide a supervisory operation to determine the amount of available memory. This operation is performed in the hard reset phase. Thus for simulators, execution always begins at __cs3_reset_sys.

The CodeSourcery board support library provides reasonable default reset code, but you may provide your own reset code by defining __cs3_reset_sys in an object file or library, in a .cs3.reset section.

The Assembly Initialization Phase

This phase is responsible for initializing the stack pointer and creating an initial stack frame. It ends with a call or jump to __cs3_start_c. The symbol _start marks the entry point of the assembly initialization code.

The value of the symbol __cs3_stack provides the initial value of the stack pointer. The CodeSourcery linker scripts provide a default value for this symbol, which you may override by defining __cs3_stack yourself.

Some processors initialize the stack pointer automatically on reset. However, because the assembly initialization phase is executed during debugging, it is required to set the stack pointer explicitly here in all cases.

The initial stack frame is created for the use of ordinary C and C++ calling conventions. The stack should be initialized so that backtraces stop cleanly at this point; this might entail zeroing a dynamic link pointer, or providing hand-written DWARF call frame information.

Finally, we call the C function __cs3_start_c. This function never returns, and _start need not be prepared to handle a return from it.

As with the hard reset code, the CodeSourcery board support library provides reasonable default assembly initialization code. However, you may provide your own code by providing a definition for _start, either in an object file or a library.

The symbol _start lacks the __cs3 prefix, because many debuggers and integrated development environments assume that name is used for this purpose.

The C Initialization Phase

Finally, C code can be executed. The C startup function is declared as follows:

void __cs3_start_c (void) __attribute__ ((noreturn));

In this function we take the following steps:

  • Initialize all .data-like sections by copying their contents.

  • Clear all .bss-like sections.

  • Run constructors for statically-allocated objects, recorded using whatever conventions are usual for C++ on the target architecture.

    CS3 reserves priorities from 0 to 100 for use by initialization code. You can handle tasks like enabling interrupts, initializing coprocessors, pointing control registers at interrupt vectors, and so on by defining constructors with appropriate priorities.

  • Call main as appropriate.

  • Call exit, if it is available.

As with the hard reset and assembly initialization code, the CodeSourcery board support library provides a reasonable definition for the __cs3_start_c function. You may override this by providing a definition for __cs3_start_c, either in an object file or in a library.

The CodeSourcery-provided definition of __cs3_start_c can pass command-line arguments to main using the normal C argc and argv mechanism if the board support package provides corresponding definitions for __cs3_argc and __cs3_argv. For example:

int __cs3_argc;
char **__cs3_argv;

These variables should be initialized using a constructor function, which is run by __cs3_start_c after it initializes the data segment. Use the constructor attribute on the function definition:

__attribute__((constructor)) 
static void __cs3_init_args (void) {
   __cs3_argc = ...;
   __cs3_argv = ...;
}

The constructor function may have an arbitrary name; __cs3_init_args is used only for illustrative purposes here.

If definitions of __cs3_argc and __cs3_argv are not provided, then the default __cs3_start_c function invokes main with zero as the argc argument and a null pointer as argv.