Working example available here
The linker script
The linker script is a little different to the one used in earlier examples.
Whats new:
A new section of memory called vectors. You can define memory segments as you like and make the linker put them where you like in memory. In this case, the vectors segment will contain the interrupt vector table and initialization code so this will be placed at the beginning of flash (see the text section below).
Also added are some new symbols:
INIT_DATA_VALUES : The start address in flash where the initial values for global and static variables are stored
INIT_DATA_START : The start address in RAM where global and static variables are stored
INIT_DATA_END : The end address in RAM where global and static variables are stored
BSS_START : The start address in RAM where uninitialized global and static variables are stored
BSS_END : The end adress in RAM where uninitialized global and static variables are stored.
A good reference for all of this linker script stuff is available at linuxselfhelp.com
.
MEMORY
{
flash : org = 0x08000000, len = 64k
ram : org = 0x20000000, len = 8k
}
SECTIONS
{
. = ORIGIN(flash);
.text : {
*(.vectors); /* The interrupt vectors */
*(.text);
} >flash
. = ORIGIN(ram);
.data : {
INIT_DATA_VALUES = LOADADDR(.data);
INIT_DATA_START = .;
*(.data);
INIT_DATA_END = .;
} >ram AT>flash
BSS_START = .;
.bss : {
*(.bss);
} > ram
BSS_END = .;
}
The C based initialization routine
When the processor boots it must call on the main function but it should also copy all of the initialization values for global and static variables from flash ROM to RAM.
It is also the usual practice to zero all uninitialized global and static variables. The init routine shown below does this. It uses the addresses of the various memory
sections defined in the linker script to figure out where to put stuff. Once all of this is done, the function calls on main.
void init()
{
// do global/static data initialization
unsigned char *src;
unsigned char *dest;
unsigned len;
src= &INIT_DATA_VALUES;
dest= &INIT_DATA_START;
len= &INIT_DATA_END-&INIT_DATA_START;
while (len--)
*dest++ = *src++;
// zero out the uninitialized global/static variables
dest = &BSS_START;
len = &BSS_END - &BSS_START;
while (len--)
*dest++=0;
main();
}
The interrupt vectors
A section of the interrupt vector table is shown below. It consists of an array of pointers which hold the addresses of the interrupt service routines.
The first entry is the exception here as it holds the address of the initial stack pointer (set to the to of RAM in this case).
The interrupt vector array is defined to have a particular attribute. The attribute defines a new section of memory called ".vectors" and flags this array as belonging to this memory section.
This syntax is particular to gcc and ld and causes the linker (with the help of the linker script) to place the vectors at the beginning of flash ROm.
// the section "vectors" is placed at the beginning of flash
// by the linker script
:
:
const void * Vectors[] __attribute__((section(".vectors"))) ={
(void *)0x20002000, /* Top of stack */
init, /* Reset Handler */
Default_Handler, /* NMI */
Default_Handler, /* Hard Fault */
:
: