8.3   VxWorks Initialization Timeline

This section covers the initialization sequence for VxWorks in a typical development configuration. The steps are described in sequence of execution. This is not the only way VxWorks can be bootstrapped on a particular processor. There are often more efficient or robust techniques unique to a particular processor or hardware; consult your hardware's documentation.

For final production, the sequence can be revisited to include diagnostics or to remove some of the generic operations that are required for booting a development environment, but that are unnecessary for production. This description can provide only an approximate guide to the processor initialization sequence and does not document every exception to this time-line.

The early steps of the initialization sequence are slightly different for ROM-based versions of VxWorks; for information, see 8.6.3 Initialization Sequence for ROM-Based VxWorks.

For a summary of the initialization time-line, see Table 8-1. The following sections describe the initialization in detail by routine name. For clarity, the sequence is divided into a number of main steps or function calls. The key routines are listed in the headings and are described in chronological order.

The VxWorks Entry Point: sysInit( )

The first step in starting a VxWorks system is to load a system image into main memory. This usually occurs as a download from the development host, under the control of the VxWorks boot ROM. Next, the boot ROM transfers control to the VxWorks startup entry point, sysInit( ). This entry point is configured by RAM_LOW_ADRS in the makefile and in config.h. The VxWorks memory layout is different for each architecture; for details, see the appendix that describes your architecture.

The entry point, sysInit( ), is in the system-dependent assembly language module, sysALib.s. It locks out all interrupts, invalidates caches if applicable, and initializes processor registers (including the C stack pointer) to default values. It also disables tracing, clears all pending interrupts, and invokes usrInit( ), a C subroutine in the usrConfig.c module. For some targets, sysInit( ) also performs some minimal system-dependent hardware initialization, enough to execute the remaining initialization in usrInit( ). The initial stack pointer, which is used only by usrInit( ), is set to occupy an area below the system image but above the vector table (if any).

The Initial Routine: usrInit( )

The usrInit( ) routine (in usrConfig.c) saves information about the boot type, handles all the initialization that must be performed before the kernel is actually started, and then starts the kernel execution. It is the first C code to run in VxWorks. It is invoked in supervisor mode with all hardware interrupts locked out.

Many VxWorks facilities cannot be invoked from this routine. Because there is no task context as yet (no TCB and no task stack), facilities that require a task context cannot be invoked. This includes any facility that can cause the caller to be preempted, such as semaphores, or any facility that uses such facilities, such as printf( ). Instead, the usrInit( ) routine does only what is necessary to create an initial task, usrRoot( ). This task then completes the startup.

The initialization in usrInit( ) includes the following:

Cache Initialization

The code at the beginning of usrInit( ) initializes the caches, sets the mode of the caches and puts the caches in a safe state. At the end of usrInit( ), the instruction and data caches are enabled by default.

Zeroing Out the System bss Segment

The C and C++ languages specify that all uninitialized variables must have initial values of 0. These uninitialized variables are put together in a segment called bss. This segment is not actually loaded during the bootstrap, because it is known to be zeroed out. Because usrInit( ) is the first C code to execute, it clears the section of memory containing bss as its very first action. While the VxWorks boot ROMs clear all memory, VxWorks does not assume that the boot ROMs are used.

Initializing Interrupt Vectors

The exception vectors must be set up before enabling interrupts and starting the kernel. First, intVecBaseSet( ) is called to establish the vector table base address.


*

NOTE: There are exceptions to this in some architectures; see the appendix that describes your architecture for details.

After intVecBaseSet( ) is called, the routine excVecInit( ) initializes all exception vectors to default handlers that safely trap and report exceptions caused by program errors or unexpected hardware interrupts.

Initializing System Hardware to a Quiescent State

System hardware is initialized by calling the system-dependent routine sysHwInit( ). This mainly consists of resetting and disabling hardware devices that can cause interrupts after interrupts are enabled (when the kernel is started). This is important because the VxWorks ISRs (for I/O devices, system clocks, and so on), are not connected to their interrupt vectors until the system initialization is completed in the usrRoot( ) task. However, do not attempt to connect an interrupt handler to an interrupt during the sysHwInit( ) call, because the memory pool is not yet initialized.

Initializing the Kernel

The usrInit( ) routine ends with calls to two kernel initialization routines:

usrKernelInit( ) (defined in usrKernel.c)
calls the appropriate initialization routines for each of the specified optional kernel facilities (see Table 8-1 for a list).

kernelInit( ) (part of kernelLib.c)
initiates the multitasking environment and never returns. It takes the following parameters:

  • The application to be spawned as the "root" task, typically usrRoot( ).

  • The stack size.

  • The start of usable memory; that is, the memory after the main text, data, and bss of the VxWorks image. All memory after this area is added to the system memory pool, which is managed by memPartLib. Allocation for dynamic module loading, task control blocks, stacks, and so on, all come out of this region. See Initializing the Memory Pool.

  • The top of memory as indicated by sysMemTop( ). If a contiguous block of memory is to be preserved from normal memory allocation, pass sysMemTop( ) less the reserved memory.

  • The interrupt stack size. The interrupt stack corresponds to the largest amount of stack space any interrupt-level routine uses, plus a safe margin for the nesting of interrupts.

  • The interrupt lock-out level. For architectures that have a level concept, it is the maximum level. For architectures that do not have a level concept, it is the mask to disable interrupts. See the appendix that describes your architecture for details.

kernelInit( ) calls intLockLevelSet( ), disables round-robin mode, and creates an interrupt stack if supported by the architecture. It then creates a root stack and TCB from the top of the memory pool, spawns the root task, usrRoot( ), and terminates the usrInit( ) thread of execution. At this time, interrupts are enabled; it is critical that all interrupt sources are disabled and pending interrupts cleared.

Initializing the Memory Pool

VxWorks includes a memory allocation facility, in the module memPartLib, that manages a pool of available memory. The malloc( ) routine allows callers to obtain variable-size blocks of memory from the pool. Internally, VxWorks uses malloc( ) for dynamic allocation of memory. In particular, many VxWorks facilities allocate data structures during initialization. Therefore, the memory pool must be initialized before any other VxWorks facilities are initialized.

Note that the Tornado target server manages a portion of target memory to support downloading of object modules and other development functions. VxWorks makes heavy use of malloc( ), including allocation of space for loaded modules, allocation of stacks for spawned tasks, and allocation of data structures on initialization. You are also encouraged to use malloc( ) to allocate any memory your application requires. Therefore, it is recommended that you assign to the VxWorks memory pool all unused memory, unless you must reserve some fixed absolute memory area for a particular application use.

The memory pool is initialized by kernelInit( ). The parameters to kernelInit( ) specify the start and end address of the initial memory pool. In the default usrInit( ) distributed with VxWorks, the pool is set to start immediately following the end of the booted system, and to contain all the rest of available memory.

The extent of available memory is determined by sysMemTop( ), which is a system-dependent routine that determines the size of available memory. If your system has other noncontiguous memory areas, you can make them available in the general memory pool by later calling memAddToPool( ) in the usrRoot( ) task.

The Initial Task: usrRoot( )

When the multitasking kernel starts executing, all VxWorks multitasking facilities are available. Control is transferred to the usrRoot( ) task and the initialization of the system can be completed. For example, usrRoot( ) performs the following:

To review the complete initialization sequence within usrRoot( ), see installDir/target/config/all/ usrConfig.c.

Modify these initializations to suit your configuration. The meaning of each step and the significance of the various parameters are explained in the following sections.

Initialization of the System Clock

The first action in the usrRoot( ) task is to initialize the VxWorks clock. The system clock interrupt vector is connected to the routine usrClock( ) (described in The System Clock Routine: usrClock( )) by calling sysClkConnect( ). Then, the system clock rate (usually 60Hz) is set by sysClkRateSet( ). Most boards allow clock rates as low as 30Hz (some even as low as 1Hz), and as high as several thousand Hz. High clock rates (>1000Hz) are not desirable, because they can cause system thrashing.1

The timer drivers supplied by WRS include a call to sysHwInit2( ) as part of the sysClkConnect( )routine. Wind River BSPs use sysHwInit2( ) to perform further board initialization that is not completed in sysHwInit( ). For example, an intConnect( ) of ISRs can take place here, because memory can be allocated now that the system is multitasking.

Initialization of the I/O System

If INCLUDE_IO_SYSTEM is defined in configAll.h, the VxWorks I/O system is initialized by calling the routine iosInit( ). The arguments specify the maximum number of drivers that can be subsequently installed, the maximum number of files that can be open in the system simultaneously, and the desired name of the "null" device that is included in the VxWorks I/O system. This null device is a "bit-bucket" on output and always returns end-of-file for input.

The inclusion or exclusion of INCLUDE_IO_SYSTEM also affects whether the console devices are created, and whether standard in, standard out, and standard error are set; see the next two sections for more information.

Creation of the Console Devices

If the driver for the on-board serial ports is included (INCLUDE_TTY_DEV), it is installed in the I/O system by calling the driver's initialization routine, typically ttyDrv( ). The actual devices are then created and named by calling the driver's device-creation routine, typically ttyDevCreate( ). The arguments to this routine includes the device name, a serial I/O channel descriptor (from the BSP), and input and output buffer sizes.

The macro NUM_TTY specifies the number of tty ports (default is 2), CONSOLE_TTY specifies which port is the console (default is 0), and CONSOLE_BAUD_RATE specifies the bps rate (default is 9600). These macros are specified in configAll.h, but can be overridden in config.h for boards with a nonstandard number of ports.

PCs can use an alternative console with keyboard input and VGA output; see your PC workstation documentation for details.

Setting of Standard In, Standard Out, and Standard Error

The system-wide standard in, standard out, and standard error assignments are established by opening the console device and calling ioGlobalStdSet( ). These assignments are used throughout VxWorks as the default devices for communicating with the application developer. To make the console device an interactive terminal, call ioctl( ) to set the device options to OPT_TERMINAL.

Installation of Exception Handling and Logging

Initialization of the VxWorks exception handling facilities (supplied by the module excLib) and logging facilities (supplied by logLib) takes place early in the execution of the root task. This facilitates detection of program errors in the root task itself or in the initialization of the various facilities.

The exception handling facilities are initialized by calling excInit( ) when INCLUDE_EXC_HANDLING and INCLUDE_EXC_TASK are defined. The excInit( ) routine spawns the exception support task, excTask( ). Following this initialization, program errors causing hardware exceptions are safely trapped and reported, and hardware interrupts to uninitialized vectors are reported and dismissed. The VxWorks signal facility, used for task-specific exception handling, is initialized by calling sigInit( ) when INCLUDE_SIGNALS is defined.

The logging facilities are initialized by calling logInit( ) when INCLUDE_LOGGING is defined. The arguments specify the file descriptor of the device to which logging messages are to be written, and the number of log message buffers to allocate. The logging initialization also includes spawning the logging task, logTask( ).

Initialization of the Pipe Driver

If named pipes are desired, define INCLUDE_PIPE in configAll.h so that pipeDrv( ) is called automatically to initialize the pipe driver. Tasks can then use pipes to communicate with each other through the standard I/O interface. Pipes must be created with pipeDevCreate( ).

Initialization of Standard I/O

VxWorks includes an optional standard I/O package when INCLUDE_STDIO is defined.

Creation of File System Devices and Initialization of Device Drivers

Many VxWorks configurations include at least one disk device or RAM disk with a dosFs, rt11Fs, or rawFs file system. First, a disk driver is installed by calling the driver's initialization routine. Next, the driver's device-creation routine defines a device. This call returns a pointer to a BLK_DEV structure that describes the device.

The new device can then be initialized and named by calling the file system's device-initialization routine--dosFsDevInit( ), rt11FsDevInit( ), or rawFsDevInit( )--when the respective constants INCLUDE_DOSFS, INCLUDE_RT11FS, and INCLUDE_RAWFS are defined. (Before a device can be initialized, the file system module must already be initialized with dosFsInit( ), rt11FsInit( ), or rawFsInit( ).) The arguments to the file system device-initialization routines depend on the particular file system, but typically include the device name, a pointer to the BLK_DEV structure created by the driver's device-creation routine, and possibly some file-system-specific configuration parameters.

Initialization of Floating-Point Support

Support for floating-point I/O is initialized by calling the routine floatInit( ) when INCLUDE_FLOATING_POINT is defined in configAll.h. Support for floating-point coprocessors is initialized by calling mathHardInit( ) when INCLUDE_HW_FP is defined. Support for software floating-point emulation is initialized by calling mathSoftInit( ) when INCLUDE_SW_FP is defined. See the appropriate architecture appendix for details on your processor's floating-point support.

Inclusion of Performance Monitoring Tools

VxWorks has two built-in performance monitoring tools. A task activity summary is provided by spyLib, and a subroutine execution timer is provided by timexLib. These facilities are included by defining the macros INCLUDE_SPY and INCLUDE_TIMEX, respectively, in configAll.h.

Initialization of the Network

Before the network can be used, it must be initialized with the routine usrNetInit( ), which is called by usrRoot( ) when the constant INCLUDE_NET_INIT is defined in one of the configuration header files. (The source for usrNetInit( ) is in installDir/target/src/config/usrNetwork.c.) The routine usrNetInit( ) takes a configuration string as an argument. This configuration string is usually the "boot line" that is specified to the VxWorks boot ROMs to boot the system (see Tornado Getting Started). Based on this string, usrNetInit( ) performs the following:

  • Initializes network subsystem by calling the routine netLibInit( ).

  • Attaches and configures appropriate network drivers.

  • Adds gateway routes.

  • Initializes the remote file access driver netDrv, and adds a remote file access device.

  • Initializes the remote login facilities.

  • Optionally initializes the Remote Procedure Calls (RPC) facility.

  • Optionally initializes the Network File System (NFS) facility.

As noted previously, the inclusion of some of these network facilities is controlled by definitions in configAll.h; see Table 8-6 for a list of these constants. The network initialization steps are described in the VxWorks Network Programmer's Guide.

Initialization of Optional Products and Other Facilities

Shared memory objects are provided with the optional product VxMP. Before shared memory objects can be used, they must be initialized with the routine usrSmObjInit( ) (in installDir/target/src/config/usrSmObj.c), which is called from usrRoot( ) if INCLUDE_SM_OBJ is defined.


*

CAUTION: The shared memory objects library requires information from fields in the VxWorks boot line. The functions are contained in the usrNetwork.c file. If no network services are included, usrNetwork.c is not included and the shared memory initialization fails. The project facility calculates all dependencies but if you are using manual configuration, either add INCLUDE_NETWORK to configAll.h or extract the bootline cracking routines from usrNetwork.c and include them elsewhere.

Basic MMU support is provided if INCLUDE_MMU_BASIC is defined. Text protection, vector table protection, and a virtual memory interface are provided with the optional product VxVMI, if INCLUDE_MMU_FULL is defined. The MMU is initialized by the routine usrMmuInit( ), located in installDir/target/src/config/usrMmuInit.c. If the macros INCLUDE_PROTECT_TEXT and INCLUDE_PROTECT_VEC_TABLE are also defined, text protection and vector table protection are initialized.

The GNU C++ compiler is shipped with Tornado. To initialize C++ support for the GNU compiler, define either INCLUDE_CPLUS or INCLUDE_CPLUS_MIN. To include one or more of the Wind Foundation Class libraries, define the appropriate INCLUDE_CPLUS_library macros (listed in Table 8-6).2

Initialization of WindView

Kernel instrumentation is provided with the optional product WindView. It is initialized in usrRoot( ) when INCLUDE_WINDVIEW is defined in configAll.h. Other WindView configuration constants control particular initialization steps; see the WindView User's Guide: Configuring WindView.

Initialization of the Target Agent

If INCLUDE_WDB is defined, wdbConfig( ) in installDir/target/src/config/usrWdb.c is called. This routine initializes the agent's communication interface, then starts the agent. For information on configuring the agent and the agent's initialization sequence, see Tornado Getting Started.

Execution of a Startup Script

The usrRoot( ) routine executes a user-supplied startup script if the target-resident shell is configured into VxWorks, INCLUDE_STARTUP_SCRIPT is defined, and the script's file name is specified at boot time with the startup script parameter (see Tornado Getting Started). If the parameter is missing, no startup script is executed.

The System Clock Routine: usrClock( )

Finally, the system clock ISR usrClock( ) is attached to the system clock timer interrupt by the usrRoot( ) task described The Initial Task: usrRoot( ). The usrClock( ) routine calls the kernel clock tick routine tickAnnounce( ), which performs OS bookkeeping. You can add application-specific processing to this routine.

Initialization Summary

Table 8-1 shows a summary of the entire VxWorks initialization sequence for typical configurations. For a similar summary applicable to ROM-based VxWorks systems, see Overall Initialization for ROM-Based VxWorks.

Table 8-1:  VxWorks Run-time System Initialization Sequence 


Routine
Activity
File

sysInit( )  
(a) lock out interrupts 
sysALib.s  
 
(b) invalidate caches, if any 
 
 
(c) initialize system interrupt tables with default
      stubs (i960 only) 
 
 
(d) initialize system fault tables with default stubs
      (i960 only)  
 
 
(e) initialize processor registers to known default
      values 
 
 
(f) disable tracing 
 
 
(g) clear all pending interrupts 
 
 
(h) invoke usrInit( ) specifying boot type 
 
usrInit( )  
(a) zero bss (uninitialized data) 
usrConfig.c  
 
(b) save bootType in sysStartType  
 
 
(c) invoke excVecInit( ) to initialize all system and
      default interrupt vectors 
 
 
(d) invoke sysHwInit( ) 
 
 
(e) invoke usrKernelInit( ) 
 
 
(f) invoke kernelInit( ) 
 
usrKernelInit( )  
The following routines are invoked if their configuration constants are defined. 
usrKernel.c  
 
(a) classLibInit( ) 
 
 
(b) taskLibInit( ) 
 
 
(c) taskHookInit( ) 
 
 
(d) semBLibInit( ) 
 
 
(e) semMLibInit( ) 
 
 
(f) semCLibInit( ) 
 
 
(g) semOLibInit( ) 
 
 
(h) wdLibInit( ) 
 
 
(i) msgQLibInit( ) 
 
 
(j) qInit( ) for all system queues 
 
 
(k) workQInit( ) 
 
kernelInit( )  
Initialize and start the kernel. 
kernelLib.c  
 
(a) invoke intLockLevelSet( ) 
 
 
(b) create root stack and TCB from top of memory
      pool 
 
 
(c) invoke taskInit( ) for usrRoot( ) 
 
 
(d) invoke taskActivate( )for usrRoot( ) 
 
 
(e) usrRoot( ) 
 
usrRoot( )  
Initialize I/O system, install drivers, and create devices as specified in configAll.h and config.h
usrConfig.c  
 
 
(a) sysClkConnect( ) 
 
 
(b) sysClkRateSet( ) 
 
 
(c) iosInit( ) 
 
 
(d) if (INCLUDE_TTY_DEV and NUM_TTY)
         ttyDrv( ),
      then establish console port, STD_IN,
         STD_OUT, STD_ERR 
 
 
(e) initialize exception handling with excInit( ),
      logInit( ), sigInit( )  
 
 
(f) initialize the pipe driver with pipeDrv( ) 
 
 
(g) stdioInit( )  
 
 
(h) mathSoftInit( ) or mathHardInit( )  
 
 
(i) wdbConfig( ): configure and initialize target agent 
 
 
(j) run startup script if target-resident shell is
     configured 
 


1:  Thrashing occurs when clock interrupts are so frequent that the processor spends too much time servicing the interrupts, and no application code can run.

2:  For information on using the GNU C++ compiler and the optional Wind Foundation Classes, see 5. C++ Development and 8.4.2 Compiling Application Modules.