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
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.
In addition to providing linker support, CS3's functionality is fully integrated with the Sourcery G++ Debug Sprite. For each supported board, CS3 provides the board file containing the memory map and initialization sequence required for debugging applications on the board via the Sprite, as documented in Section 6.7, “Supported Board Files”.
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.
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_
,
where sys
sys
is a name for the board
being initialized; for example, the reset code for
M5208EVB boards would be named
__cs3_reset_m5208evb
. 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_
in an object file or library, in a
sys
.cs3.reset
section.
This phase is responsible for initializing the stack pointer
and creating an initial stack frame.
The symbol _start
marks the entry point of the
assembly initialization code; this name lacks the
__cs3
prefix because is the symbol traditionally
used by debuggers and other integrated development environments
for the address where program execution begins.
The assembly initialization phase ends with a call or jump to
__cs3_start_c
.
Simulators typically initialize the stack pointer and initial
stack frame automatically on startup.
CS3 can also support targets running a boot monitor that likewise
initializes the stack before starting user code.
On these targets, CS3 does not perform the assembly
initialization phase at all; instead,
_start
is aliased to
__cs3_reset_
,
so that execution always starts with the hard reset phase.
The hard reset phase then ends with a jump directly to
sys
__cs3_start_c
.
On the other hand, on bare-board targets setting the stack pointer
explicitly in the assembly initialization phase is required
even if the processor itself initializes the stack pointer
automatically on reset.
This is to support restarting programs from
_start
in the debugger.
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.
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.
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
.