How do LPCOpen applications handle system startup?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How do LPCOpen applications handle system startup?

4,963 Views
lpcware-support
Senior Contributor I

All LPCOpen applications, regardless of the platform, device, or tool chain they run on, use a similar application startup sequence. Understanding how this sequence works and it's requirements can help prevent problems when developing new applications, developing a new board layer, or customizing early initialization of a board or device prior to entering main().

LPCOpen application startup sequence

The common LPCOpen application startiup sequence is shown below:

Device is powered on or reset

The board is powered on or reset. The initial device stack is setup and reset vector is fetched and the control of the device is transferred to the reset handler in the startup code.

SystemInit() is called

The startup code first calls SystemInit(), which performs low level early initialization of the device and board. SystemInit(), as implemented in LPCOpen, performs 3 functions: initial board level pin muxing, system clock setup, and external memory setup (if applicable). SystemInit() usually calls Board_SystemInit(), which is part of the board layer code.

Initial board level pin muxing

Most board level pin muxing is perform as the first function of SystemInit(). Doing the pin muxing in one location allows the pin mux data to be organized in a common table and use less code space. The LPCOpen board layer handles pin muxing and sets up most, but not necessarily all, pins to the functions as they are connected on the board.

System clocking

System clocking is setup after pin muxing. This step usually performs oscillator enable, PLL setup, main clock selection, etc. Clock setup for the same device may vary per board based in external clock input rate, oscillator rate, etc.

External memory setup

This step only applies to boards that have external memory such as SDRAM, NOR FLASH, external SRAM, etc. If this setup step is required, it is setup here by setting up the correct timing, initializing SDRAM, etc. If special pin muxing is required to use the external memory, it is usually done as part of the board level pin muxing functions.

External memory is setup in the application early and prior to initializing the data segments.

Code and data segments are initialized

After SystemInit(), the code and data segments are initialized. This means that code segments not in the initial boot/run medium (such as internal FLASH) are relocated by at run-time. Pre-initialized data segments are copied to volatile memory regions and the zero-initialized data is cleared. This happens before the call to main().

For IAR and Keil startup code, this is done after SystemInit(). For LPCXpresso, this is done *prior* to SystemInit().

main() is called

Finally, the main() function is called and the application is started.

Board layer vs Chip layer startup

All LPCopen applications that are routed via SystemInit() typically call Board_SystemInit() via the sysinit.c file. If a board layer isn't available, you can call Chip_SystemInit() instead. The chip layer system initialization file attempts to set the system up without making assumptions about the board. If you are using the SystemInit() function in sysinit.c, you can use the chip layer's startup code instead of the board layer's version by defining NO_BOARD_LIB somewhere in your project. Using Chip_SystemInit() may vary per device and may still have assumed requirements (ie, crystal rate) that may prevent it from always working correctly on every platform. See the API documentation for the Chip_SystemInit() function for any limitations on using the function.

/* Set up and initialize hardware prior to call to main */
void SystemInit(void)
{
#if defined(NO_BOARD_LIB)
    /* Chip specific SystemInit */
    Chip_SystemInit();
#else
    /* Board specific SystemInit */
    Board_SystemInit();
#endif
}

Overriding SystemInit in your application

If you don't want to use the standard SystemInit() that calls Chip_SystemInit() or Board_SystemInit() in sysinit.c, you can remove the file sysinit.c from the project and create a custom version of SystemInit() to use in your applicaiton.

The example below, located in the main.c file, uses the board layer's pin mux setup function, but the chip layer's PLL clock set function using the IRC.

/**
 * @brief    Override of SystemInit() to use IRC clocking
 * @return    Nothing
 */
void SystemInit(void)
{
    /* Setup system clocking and muxing */
    Board_SetupMuxing();
    Chip_SetupIrcClocking();
}

LPCOpen startup code requirements

Because of this approach, there are several requirements for LPCOpen software that must be met to work correctly.

Data use in SystemInit() code

Any data used in SystemInit() and functions that SystemInit() use must not be assumed to be correctly pre-initialized. This includes both 0 and non-0 values. SystemInit() is called prior to calling the data segment initialization functions, so data that is located in a data segment (non-stack data) is not yet initialized to it's starting values. All LPCOpen code used in early system bring up avoids the use on non-stack data for this reason.

This also applies to the use of any data that is located in external memory. External memory may not be correctly setup until the end of SystemInit(), so LPCopen functions used for SystemInit() should avoid the use of any memory except internal IRAM.

Relocation of code

Because code and data relocation occurs after SystemInit(), care must be used when relocating code to IRAM. If the code used for SystemInit() functions expects to be in IRAM (and is linked for IRAM addresses), SystemInit() will fail.

Startup code samples

The code samples below are from IAR, Keil, LPCXPresso startup code and provides a reference only to early board startup. Startup code in LPCopen can be found in the applications folder in the <chip>/startup area.

Startup code for IAR tool chain

The IAR startup code first calls SystemInit() and then calls __iar_program_start(). The _iar_program_start() functions performs code and data segment relocation and calls main().

Reset_Handler
        LDR     R0, =SystemInit
        BLX     R0
        LDR     R0, =__iar_program_start
        BX      R0

Startup code for Keil tool chain

The Keil startup code first calls SystemInit() and then calls __main(). The _main() functions performs code and data segment relocation and calls main().

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  SystemInit
                IMPORT  __main
                LDR     R0, =SystemInit
                BLX     R0
                LDR     R0, =__main
                BX      R0

Startup code for LPCXpresso tool chain

The LPCXpresso startup code works differently from the Keil and IAR startup code in that data segment initialization is performed first. If you need to change this behaviour, you can move the data segment initialization further down in the startup code sequence. After data segment initialization, SystemInit() is called and __main is called.

ResetISR(void) {

    //
    // Copy the data sections from flash to SRAM.
    //
    unsigned int LoadAddr, ExeAddr, SectionLen;
    unsigned int *SectionTableAddr;

    // Load base address of Global Section Table
    SectionTableAddr = &__data_section_table;

    // Copy the data sections from flash to SRAM.
    while (SectionTableAddr < &__data_section_table_end) {
        LoadAddr = *SectionTableAddr++;
        ExeAddr = *SectionTableAddr++;
        SectionLen = *SectionTableAddr++;
        data_init(LoadAddr, ExeAddr, SectionLen);
    }
    // At this point, SectionTableAddr = &__bss_section_table;
    // Zero fill the bss segment
    while (SectionTableAddr < &__bss_section_table_end) {
        ExeAddr = *SectionTableAddr++;
        SectionLen = *SectionTableAddr++;
        bss_init(ExeAddr, SectionLen);
    }
#if defined (__USE_CMSIS) || defined (__USE_LPCOPEN)
    SystemInit();
#endif

#if defined (__cplusplus)
    //
    // Call C++ library initialisation
    //
    __libc_init_array();
#endif

#if defined (__REDLIB__)
    // Call the Redlib library, which in turn calls main()
    __main() ;
#else
    main();
#endif
Labels (1)
Tags (1)
0 Replies