The C Runtime
(We provide two custom C runtime files: crt0.S
written in assembler; and crt0.c
written in C. A description of crt0.S
follows.)
crt stands for C runtime. crt0.o
is an object file with code that is prepended to object
code supplied by the user to make an executable. It initializes variables and the stack,
and starts the user's program, among other things.
Modification
We need to modify the runtime to behave differently than the usual way. We want
to start executing our code at the function OS_Init()
rather than main()
.
(Note: prior to gcc 4.x, main()
was treated differently from other functions. If you want to also include a function called main()
, then use a WinAVR release at least 20070525.)
An overall skeleton of our crt0.S file is shown below.
#include <avr/io.h> #include <avr/sfr_defs.h> #define __zero_reg__ r1 .macro vector name .weak \name .set \name, __vector_not_set jmp \name .endm /* initialize the interrupt vector table */ /* vector 0 is the "reset" vector */ .section .vectors,"ax",@progbits .global __vectors .func __vectors __vectors: jmp __init vector __vector_1 vector __vector_2 vector __vector_3 ... vector __vector_37 .endfunc .text .global __vector_not_set .func __vector_not_set __vector_not_set: jmp vectors .endfunc /* beginning of our executable code */ .section .init0,"ax",@progbits .weak __init __init: .weak __stack .weak __heap_end .set __heap_end, 0 .set __stack, RAMEND /* initialize a default stack at the end of RAM */ .section .init2,"ax",@progbits clr __zero_reg__ out _SFR_IO_ADDR(SREG), __zero_reg__ ldi r28,lo8(__stack) ldi r29,hi8(__stack) out _SFR_IO_ADDR(SPH), r29 out _SFR_IO_ADDR(SPL), r28 /* copy initial global data values from FLASH into RAM */ .section .init4,"ax",@progbits .global __do_copy_data __do_copy_data: ldi r17, hi8(__data_end) ldi r26, lo8(__data_start) ldi r27, hi8(__data_start) ldi r30, lo8(__data_load_start) ldi r31, hi8(__data_load_start) ldi r16, hh8(__data_load_start) out _SFR_IO_ADDR(RAMPZ), r16 rjmp .L__do_copy_data_start .L__do_copy_data_loop: elpm r0, Z+ st X+, r0 .L__do_copy_data_start: cpi r26, lo8(__data_end) cpc r27, r17 brne .L__do_copy_data_loop /* now, we are ready to call our application's main program */ .section .init9,"ax",@progbits call OS_Init /* changed "main" to "OS_Init" */ jmp exit /* if returns, loop forever */
To use this modified crt0.S file instead of the default file in AVR Studio, add a linker option in Project>>Configuration Options>>Custom Options. Highlight "[Linker Options]" and enter "-nostartfiles" in the box beside the Add button. Press Add.
Default Linker Script
An explanation of how this crt0.S
file is linked to our application's executable follows.
The gnu linker, ld, is invoked directly, or by executing gcc on object files. AVR Studio arranges for ld to read an appropriate linker script for the device, for example, WinAVR/avr/lib/ldscripts/avr5.x. This file describes the memory of the device, and gives the layout of the order in which the linker should place object code. It contains a list of "sections", some of which are allowed to be empty. The important sections for us are:
.text
---application's executable code, including our RTOS (note: for AVR microcontroller chips, this is stored in FLASH),.data
---all globally declared variables that have an initializer (note: they are stored in RAM but their values are stored in FLASH), and.bss
---all globally declared variables but without an initializer (note: they are stored in RAM but should all be cleared to zero by default).
The .text
section contains labels to where code thus labeled will go. An abridged version of the .text
section of avr5.x is:
*(.vectors) *(.init0) /* Start here after reset. */ *(.init1) *(.init2) /* Clear __zero_reg__, set up stack pointer. */ *(.init3) *(.init4) /* Initialize data and BSS. */ ... *(.init9) /* Call main(). */ *(.text) *(.fini9) /* _exit() starts here. */ ... *(.fini0) /* Infinite loop after program termination. */
The asterisks indicate the sections may be empty. We need a .vectors
section to indicate
where the interrupt service routines reside; some initialization sections,
.init0
to .init9
; a section for most code, called .text
; and
some finalization sections, .fini9
down to .fini0
.
The .data and .bss sections come next. The .data section contains the initialization values for variables declared with initializers in the C code. The .bss section is for uninitialized variables.
.data : AT (ADDR (.text) + SIZEOF (.text)) { PROVIDE (__data_start = .) ; *(.data) *(.gnu.linkonce.d*) . = ALIGN(2); _edata = . ; PROVIDE (__data_end = .) ; } > data .bss SIZEOF(.data) + ADDR(.data) : { PROVIDE (__bss_start = .) ; *(.bss) *(COMMON) PROVIDE (__bss_end = .) ; } > data __data_load_start = LOADADDR(.data); __data_load_end = __data_load_start + SIZEOF(.data);
After the data from the various object files are arranged in these sections, the linker creates the symbols
__data_start
, etc., by setting them equal to the address at which the symbol appears in the
layout via a statement such as __data_start = .
, or with an arithmetic assignment.
These six symbols are available for the object files to use. (We use them in our crt0.S
.)
Normally, the linker looks to find the necessary initialization functions in an object file
relevant to the hardware, such as in WinAVR/avr/lib/avr5/crtusb1287.o
.
This file is compiled from
avr-libc/crt1/gcrt1.S
with the appropriate #define
s set
by the linker. This file needs #define
s included in
avr-libc/common/macros.inc
and
avr-libc/include/avr/common.h
. Our custom crt0.S
was created by modifying these files.
The linker also looks in libgcc.a for some initialization code in the gcc compiler's libgcc.a. Here reside default implementations of the routines:
__do_copy_data
for copying the initial values of the variables from the program memory into RAM,__do_clear_bss
for setting the uninitialized variables in RAM to 0, and_exit
which is just an infinite loop to itself.
The version of __do_copy_data
given in libgcc.S
is the one for the 64 K Flash
versions of avr chips. This routine needs to be overridden for chips with 128 K of flash,
such as the AT90USB1287. There is another version of this routine in gcrt1.S
, which needs
to be duplicated in crt0.S
. The routine makes use of the locations in memory of the data
provided by the linker from the load script.
crt0.S
also needs a routine to initialize the stack pointer, and to initialize the "zero register."
The gcc compiler requires the second register, r1
, to be 0, in order to speed up some
machine instructions. These are provided in gcrt1.S
, and so in the modified crt0.S
.
__do_clear_bss
and _exit
are fine as they are in libgcc.o
.
All these routines are placed in the correct sections by virtue of a section declaration such as
.section .init4,"ax",@progbits
above the routine in the assembler file. It is also possible
to place compiled C code in particular sections other than .text
. See
http://www.nongnu.org/avr-libc/user-manual/mem_sections.html for a discussion.
(This is what we do in crt0.c
.)