N. Coesel wrote:
Another good reason the have starup.S inside the project is that the user
can choose to modify startup.S and not disable the watchdog, but feed it
during clearing of bss.
Ok. It's my fault that startup sequence is not properly documented in toolchain doc files nor in
wiki. That's because my poor English. If someone can translate following text from my English into
English and add into wiki - that's to be great. I'll try to explain startup as it can be added into
wiki Tips and Tricks.
So on. Startup sequence.
The idea of startup sequence implementation is taken from avr-gcc project. All sequence is divided
into steps. Every piece of startup code corresponds to some step of startup sequence. Sequence
divided into steps by placing corresponding code into one of .initX sections. There are 9 startup
steps (.init1 - .init9) and 9 shutdown steps (.fini1 - fini9). All those .initX and .finiX sections
are linked side-by-side. So all those code pieces form a single linear code, i.e. no call/ret used
to call them separately. As it can be seen from ld scripts, the purposes of sections are:
.init0 intended for _reset_vector__ label. Reset vector points into this label. This is startup
entry point.
.init1 intended for user code. If provided, it will be the first code executed
after reset.
.init2 intended for Stack pointer initialization. Subroutines can be called in following sections.
It's necessary in C++ code, because C++ constructors are called as subroutines.
.init3 intended for hardware init routines which must be performed as fast as
possible after reset.
.init4 intended for code thats fills .data section with initial values, clears
bss.
.init5 intended for user code. This code executed after RAM is initialized, but before C++
constructors are called.
.init6 intended for C++ constructors call code
.init7 intended for user code.
.init8 intended for user code.
.init9 intended for jump to main().
.fini9 intended for label program jumps to after exit from main()
.fini8 intended for user code.
.fini7 intended for user code.
.fini6 intended for C++ destructors call code
.fini5-.fini1 intended for user code.
.fini0 intended for infinite loop after program termination.
libc library contains default code, suitable in most cases. Short description:
.init9 code contains __jump_to_main label and XBR #main macro. XBR macro expands into BR instruction
when library is compiled for generic msp430 core and into BRA instruction when library compiled for
430X core.
.init8, .init7, .init5, .init1 contains no code, user can provide it's own code for those steps
using .section directive in asm sources or __attribute__((section "section_name")) in C/C++ sources.
.init6 contains __do_global_ctors label and C++ constructors call code. Code contains XMOVA, XCMP,
XCALL macros, which are expanded to MOV, CMP, CALL / MOVA, CMPA, CALLA instructions for generic/430X
mcu's
.init4 contains two code parts: first contains __do_clear_bss label and .bss clearing routine,
second contains __do_copy_data label and .data filling routine. The same remark about XMOV, XCMP
macros as above.
.init3 contains __low_level_init label and WDT disabling code. WDTCTL sfr presented as __WDTCTL
external symbol, so actual sfr address provided at link time and the same object code can be used
for every mcu independently of actual WDTCTL location. __low_level_init name taken from IAR startup
sequence.
.init2 contains __init_stack label and mov #__stack, r1 code. __stack symbol defined in ld scripts
as end-of-RAM-address+1. __stack symbol can be redefined in ld commandline by --defsym,__stack=<address>
.init1 contains startup entry point - _reset_vector__ label and .extern references to __init_stack,
__low_level_init, __do_copy_data, __do_clear_bss, __jump_to_main symbols.
All library defined global labels (routines entry points) declared as .weak, i.e. any routine can be
redefined by user.
Interrupt vectors table provided in crt<mcu name>.o object file. gcc implicitly adds this file to
list of object when calling ld. <mcu name> and appropriate linker script are chosen based on --mmcu
commandline option.
Vectors table in crt file contains reference to _reset_vector__ symbol at reset vector position. ld
looks for _reset_vector__ symbol in object files. If symbol not found, i.e. if user code contains no
_reset_vector__ routine, ld looks for _reset_vector__ in libraries and founds it there. The whole
library module that contains _reset_vector__ routine is linked, causing references to __init_stack,
__low_level_init, __do_copy_data, __do_clear_bss, __jump_to_main to be linked too. Resolving those
symbols ld looks for them in user code first and, if not found, in libraries.
That gave user opportunity to redefine any piece of startup code he need. For example, if user don't
want to clear .bss section in startup, he just redefine __do_clear_bss in his project sources to
empty code:
.section .init4, "ax", @progbits
.global __do_clear_bss
__do_clear_bss:
or, in C source:
#ifdef __cplusplus
extern "C"
#endif
__attribute__((section(".init4"),naked)) void __do_clear_bss()
{
}
If user don't want to disable watchdog, he redefine __low_level_init() and, possibly,
__do_clear_bss() and __do_copy_data() to reset wdt periodically.
And, finally, if user redefine _reset_vector__ label, no library startup code is linked at all,
because library _reset_vector__ routine not linked and no references to other library startup
routines linked as well. So, empty startup can be defined as:
#ifdef __cplusplus
extern "C"
#endif
__attribute__((section(".init1"),naked)) void _reset_vector__()
{
// main();
asm volatile ("BR #main"::);
}
==========
Conclusion: there is no need to remove startup code from libc. Library provide startup code suitable
in most cases. The most cases are when user just want to write something like
void main()
{
Blink my led;
}
If user needs to change startup sequence - he's free to to it by simply redefining some parts of
library startup or whole startup by redefining it's entry point. User can do it in his startup.S or
in C sources without any restrictions from compiler or library.
Most of the compilers provide such default startup code. The only compiler I know which has no
library startup sequence is arm-gcc, and I think it's because of large zoo of arm-based mcu's. But
IAR and KEIL compilers has library startup for their arm compilers. I used IAR compiler for AVR,
ARM, MSP430, Keil for x51, sdcc for PIC, gcc for avr and x86(mingw), and the only thing I ever need
(if need) to change in library startup in all my projects - __low_level_init() or analogous routine.
--
Regards,
Sergey A. Borshch mailto: [email protected]
SB ELDI ltd. Riga, Latvia