Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Overview

At the highest level, the NuttX initialization sequence can be represented in three phases:

Phase A - The hardware-specific power-on reset initialization,

Phase B - NuttX RTOS initialization, and

Phase C - Application Initialization.

This initialization sequence is really quite simple because the system runs in single-thread mode up until the point the that is starts the application. That means that the initialization sequence is just a simple, straight-line of function calls. Just It is until just before starting the application , the that system goes to multi-threaded mode and things can get more complex.Each of these will be discussed in more detail in the following sections.

At the highest level, the NuttX initialization sequence can be represented in three phases:

Phase A - The hardware-specific power-on reset initialization,

Phase B - NuttX RTOS initialization, and

Phase C - Application Initialization.

Each of these will be discussed in more detail in the following sections.

Case example: STM32 F4

In this discussion, we'll use the STM32 F4 MCU and its popular evaluation board STM32F4Discovery as example but the explanation can be applied to any supported architecture.

Here is the map of initialization function calls

Code Block
titleFunction Map
collapsetrue
__start()-arch/arm/src/stm32/stm32_start.c
    |
    +--*Set stack limit
    +--stm32_clockconfig()
    +--stm32_fpuconfig()
    +--stm32_lowsetup()
    +--stm32_gpioinit()
    +--showprogress('A')
    +--
    +--
    +--stm32_boardinitialize()-boards/arm/stm32/stm32f4discovery/src/stm32_boot.c
    |    |
    |    +--stm32_spidev_initialize()-stm32_spi.c:ONLY CHIP SELECTS
    |    +--stm32_usbinitialize()-
    |    +--stm32_netinitialize()-
    |    +--board_autoled_initialize()-
    |                  
nx_start()-sched/init/nx_start.c
    |                  
    +--*Initialize global data structures
    +--*Initialize OS facilities
    +--net_initialize()-net/net_initialize.c
    |    |   
    |    +--net_lockinitialize()
    |    +--mld_initialize()
    |    +--can_initialize()
    |    +--netlink_initialize()
    |    +--tcp_initialize()
    |    +--udp_initialize()
    |    +--usrsock_initialize()
    |
    +--up_initialize()-arch/arm/src/common/up_initialize.c
    |    |                  
    |    +--arm_dmainitialize()
    |    +--Config basic /dev nodes
    |    +--arm_serialinit()
    |    +--Console Init
    |    +--Crypto Config
    |    +--arm_netinitialize()
    |    |    |
    |    |    +--stm32_spibus_initialize()
    |    |
    |    |
    |    +--arm_usbinitialize()
    |    +--L2 Cache Init
    |
    +--board_early_initialize()
    +--g_nx_initstate = OSINIT_HARDWARE
    +--shm_initialize()
    +--lib_initialize()
    +--binfmt_initialize()
    +--Start SMP
    +--syslog_initialize()
    +--g_nx_initstate = OSINIT_OSREADY
    +--DEBUGVERIFY(nx_bringup())-sched/init/nx_bringup.c
    |    |
    |    +--nx_pgworker()
    |    +--nx_workqueues()
    |    +--nx_create_initthread()-sched/init/nx_bringup.c
    |         |              
    |         +----+different thread
    |         :    :
    |         :  nx_start_task()-sched/init/nx_bringup.c
    |         :    :        
    |     same+----+--nx_start_application()-sched/init/nx_bringup.c
    |     thread        |
    |                   +--board_late_initialize()-stm32_boot.c:BOARD_DEPENDANT
    |                   |    |
    |                   |    +--stm32_bringup()
    |                   |         |
    |                   |         +--stm32_i2ctool()
    |                   |         +--board_bmp180_initialize()
    |                   |         +--stm32_sdio_initialize()
    |                   |         +--stm32_usbhost_initialize()
    |                   |         +--stm32_pwm_setup()
    |                   |         +--stm32_can_setup()
    |                   |         +--etc
    |                   |
    |                   +--
    |
    |
    |
    |
    |
    +--kmm_givesemaphore()
    +--up_idle()

Phase A - Power-On Reset Initialization.

...

The system begins execution when the processor is reset. This usually at power-on, but all resets are basically the same whether they occur because of power-on, pressing the reset button, or on a watchdog timer expiration. The code that executes when the processor is reset is unique to the particular CPU architecture and is not a common part of NuttX. The kind of things that must be done by the architecture-specific reset handling includes:

...

In C implementations, there are two general classes of variable storage. First there are the initialized variables. For example, consider the global variable x:

Code Block

  int x = 5;

The C code must be assured that after reset, the variable x has the value 5. Initialized variable of this kind are retained in a special memory section called data (or .data).

Other variables are not initialized. Like the global variable y:

Code Block

  int y;

But the C code will still expect y to have an initial value. That initial value will be zero. All uninitialized variables of this this type have have the value zero. These uninitialized variables are retained in a section called bss (or .bss).

...

  1. It provides the (initial) values of the initialized variables by copying the values from FLASH into the .data section, and
  2. It resets all of the uninitialized variables to zero. It clears the .bss section.

Case example: STM32 F4

...

  1. all of the uninitialized variables to zero. It clears the .bss section.

Lets walk through reset sequence. This reset logic can be found in two files:

...

This file provides all of the STM32 exception vectors and power-on reset is simply another exception vector. Some important things to note about this file:

.section .vectors, "ax". This pseudo operation will place all of the vectors into a special section call .vectors. On of the STM32 F4 linker scripts is located at nuttx/boards/arm/stm32/stm32f4discovery/scripts/ld.script. In that file, you can see that section .vectors is forced to lie at the very beginning of FLASH memory. The STM32 F4 can be configured to boot in different ways via strapping. If it is strapped to boot from FLASH, then the STM32 FLASH memory will be aliased to address 0x0000 0000 when the reset occurs. That is the address of the power-up reset interrupt vector.

The first two 32-bit entries in the vector table represent the power-up exception vector (which we know will be positioned at address 0x0000 0000 when the reset occurs). Those two entries are:

Code Block

.word IDLE_STACK /* Vector  0: Reset stack pointer */
.word __start    /* Vector  1: Reset vector */

The Cortex-M family is unique in the way that is handles the reset vector. Notice that there are two values: the stack pointer for the start-up thread (the IDLE thread), and the entry point in the IDLE thread. When the reset occurs, the the stack pointer is automatically set to the first value and then the processor jumps to reset entry point

...

__start

...

 specified specified in the second entry. This means that the reset exception handling code can be implemented in C rather than assembly language.

nuttx/arch/arm/src/stm32_start.c

The reset vector

...

__start

...

lies in the file nuttx/arch/arm/src/stm32/stm32_start.c and does the real, low-level architecture-specific initialization. This initialization includes:

...

arm_pminitialize() - If CONFIG_PM is defined, the function must initialize the power management subsystem. This MCU-specific function must be called very early in the intialization sequence before any other device drivers are initialized (since they may attempt to register with the power management subsystem). There is no implementation of up_pminitialize() for any STM32 platform.

arm_dmainitialize() ;- Initialize the DMA subsystem. For the STM32 F4, this DMA initialization can be found in nuttx/arch/arm/src/stm32/stm32_dma.c (which includes nuttx/arch/arm/src/stm32f4xxx_dma.c).

devnull_register() ;- Registers the standard /dev/null.

devrandom_register() ;- Registers the standard /dev/random.

devurandom_register() ;- Registers the standard /dev/urandom.

updevzero_irqinitializeregister() ; This function initialize the interrupt subsystem. For the STM32 F4, up_irqinitialize() is implemented in nuttx/arch/arm/src/stm32/stm32_irq.c.

up_timerinit(); Initialize the system timer interrupt. For the STM32 F4, this function initializes the ARM Cortex-M SYSTICK timer and can be found at nuttx/arch/arm/src/stm32/stm32_timerisr.c.

- Registers the standard /dev/zero.

loop_register() - Registers the standard /dev/loop.

note_register() - Registers the standard /dev/note.

arm_serialinit() - Initialize Then this function initializes the console device (if any). This means calling one of (1) up_serialinit(); for the standard serial driver (found at nuttx/arch/arm/src/stm32/stm32_serial.c for the STM32 F4), (2) lowconsole_init(); for the low-level, write-only serial console (found at nuttx/drivers/serial/lowconsole_init.c), or (2) ramlog_sysloginit() for the RAM console (found at nuttx/drivers/ramlog.c).

uparm_netinitialize(); Initialize the network. For the STM32 F4, this function is in nuttx/arch/arm/src/stm32/stm32_eth.c.

uparm_usbinitialize(); Initialize USB (host or device). For the STM32 F4, this function is in nuttx/arch/arm/src/stm32/stm32_otgfsdev.c.

arm_l2ccinitialize() - Initialize the L2 cache if present and selected .

up_ledon(LED_IRQSENABLED); Finally, up_initialize() illuminates board-specific LEDs to indicate the IRQs are now enabled.

...

23- nx_bringup() - Create the initial tasks. This will be described in more detail below.

nx_bringup()

This function is called at the very end of the initialization sequence in nx_start(), just before entering the IDLE loop. It is located in nuttx/sched/init/nx_bringup.c and it starts all of the required threads and tasks needed to bring up the system. This function performed the following specific operations:

...

  1. If the worker was not started (see nx_bringup() below), then the IDLE thread will perform memory clean-up. Memory clean is required to handle deferred memory deallocation. Memory allocations must be deferred when the memory is freed in a context where the software does not have access to the heap and, hence, cannot truly free the memory (such as in an interrupt handler). In this case, the memory is simply put into a list of freed memory and, eventually, cleaned up by the IDLE thread. NOTE: The worker thread's primary function is as the “bottom half” for extended device driver processing. If the worker thread was started, then it will run at a higher priority than the IDLE thread. In this case, the worker thread will take over responsibility for cleaning up these deferred allocations.
  2. up_idle(); Then the loop calls up_idle(). The operations performed by up_idle() are architecture- and board-specific. In general, this is the location where CPU-specific reduced power operations may be performed.

...

STM32 F4 IDLE thread

The default STM32 F4 IDLE thread is located at nuttx/arch/arm/src/stm32_idle.c. This default version does very little:

...

  1. If you have C++ static initializers, it will call your implementation of up_cxxinitialize() which will, in turn, call those static initializers. For the case of the STM3240G-EVAL board, the implementation of up_cxxinitialize() can be found at nuttx/boards/arm/stm32/stm3240g-eval/src/up_cxxinitialize.c.
  2. This function then calls nsh_initialize() which initializes the NSH library. nsh_initialize() is described in more detail below.
  3. If the Telnet console is enabled, it calls nsh_telnetstart() which resides in the NSH library. nsh_telnetstart() will start the Telnet daemon that will listen for Telnet connections and start remote NSH sessions.
  4. If a local console is enabled (probably on a serial port), then nsh_consolemain() is called. nsh_consolemain() also resides in the NSH library. nsh_consolemain() does not return so that finished the entire NSH initialization sequence.

...

The NSH initialization function, nsh_initialize(), be found in apps/nshlib/nsh_init.c. It does only three things:

nsh_romfsetc(); If so configured, it executes an NSH start-up script that can be found at /etc/init.d/rcS in the target file system. /etc is the location where a read-only, ROMFS file system is mounted by nsh_romfsetc(). The ROMFS image is, itself, just built into the firmware. By default, this rcS startup script contains the following logic:

Code Block

# Create a RAMDISK and mount it at XXXRDMOUNTPOUNTXXX

mkrd -m XXXMKRDMINORXXX -s XXMKRDSECTORSIZEXXX XXMKRDBLOCKSXXX
mkfatfs /dev/ramXXXMKRDMINORXXX
mount -t vfat /dev/ramXXXMKRDMINORXXX XXXRDMOUNTPOUNTXXX

Where the XXXX*XXXX strings get replaced in the template when the ROMFS image is created:

...

  • board_app_initialize(): For the STM3240G-EVALSTM32F4Discovery, this architecture specific initialization can be found at boards/arm/stm32/stm3240g-evalstm32f4discovery/src/stmstm32_nshappinit.c. This it does things like: (1) Initialize SPI devices, (2) Initialize SDIO, and (3) mount any SD cards that may be inserted.

...