Chapter 3. Application Start-up

The standard library includes a start-up module that prepares the environment for running applications written in C. Several versions of the start-up script are available because each processor has different set-up requirements. The compiler, avr-gcc, selects the appropriate module based upon the processor specified by command line options (see Appendix).

For the AVR processors, the start-up module is responsible for the following tasks:

The start-up module contains a default interrupt vector table. The contents of the table are filled with predefined function names which can be overridden by the programmer. This is discussed completely in Chapter 6. The first entry in the table, however, is the reset vector, which is set to jump to location _init_. _init_ is defined to be a "weak" symbol, which means that if the application doesn't define it, the linker will use the value from the library (or module). The start-up module defines _init_ to be the same location as _real_init_.

If you want to add some custom code that gets executed immediately after a reset, name your routine _init_. To avoid wasting program memory, you should define the function using the naked attribute. This tells the compiler not to generate any prologue or epilogue code in the function. It also prevents a ret instruction from being added, which allows us to end the function with a rjmp instruction. An example of how to do this is shown in Example 3-1.

Example 3-1. Code that runs immediately after RESET.

    void _real_init_(void);
    void _init_(void) __attribute__((naked));
    
    void _init_(void)
    {
        /* This must be the last line of the function. */
    
        asm ( "rjmp _real_init_" );
    }

Once execution begins at _real_init_, the system sets up the watchdog and the mcucr registers. The module uses a linker trick to allow you to modify the value without recompiling. The module takes the address of the variables __init_wdctr__ and __init_mcucr__, rather than the contents. By using the --defsym option to the linker, you set the address of the symbols, which are used as the load values for the registers. These two variables are defined as "weak" symbols, so the module will provide default values if you don't override them.

Example 3-2 shows the use of avr-gcc to link together some object files into an executable, prog.out. The executable will set the watchdog control register to 0x03.

Example 3-2. Configuring the watchdog during reset

    % avr-gcc -o prog.out -Wl,--defsym,__init_wdctr__=3 file1.o file2.o file3.o
          

Next, global variables that have initial values are loaded from program memory. The compiler creates two identically laid out sections. One will be placed in static RAM and is used during program execution. The other is placed in program ROM and contains the initial values. The start-up code copies the ROM image into the static RAM so that main() (and everything called from main()) see a properly initialized data segment.

The uninitialized data section, .bss, is then zeroed out. This section contains all non-auto variables that weren't given an initial value.

Lastly, the module jumps to main() and the application starts running. The function main() is recognized by the compiler as being special, so some prolog and epilog code is placed in this function. When entering, the stack is initialized to point to the end of static RAM. The end of the function always contains an infinite loop, so if you try to exit main(), your application will hang.

It should be noted that the start-up modules add quite a bit of bulk to an application. If you are using a smaller part, the bloat caused by the start-up module may be unacceptible. In those cases, your application would be better served by writing it entirely in assembly language. As an example, Figure 3-1 contains the hex file, generated by an empty main(), targetted for the AT90S2313 processor. The processor has only 1Kwords of ROM space and the start-up code eats up nearly 5% of it!

Figure 3-1. Hex file for empty main()

    :150000000FC027C026C025C024C023C022C021C020C01FC01E03
    :15001500C0CFEDD0E0CDBFDEBFFFCF11241FBE20E0A89521BD86
    :15002A0020E025BFE4E5F0E0A0E6B0E003C0C89531960D92A008
    :15003F0036D9F7A0E6B0E001C01D92A036E9F7E3CF1895FECF3E
    :00000001FF