Click “Read the original text” to access more VxWorks resources
BSP is used to initialize hardware, boot the operating system, and provide the device driver interface between software and hardware. When developing a BSP for a specific architecture, it is necessary to understand its basic architecture and instruction system. Generally, the design and development of a BSP can be divided into several steps:
1. Establish the development environment, which goes without saying, is to install the integrated environment;
2. Choose a suitable BSP template that is as close as possible to the hardware platform;
3. Modify or add initialization code before activating the Wind kernel, such as initializing the CPU core, MMU, Cache disable/enable, etc.;
4. After kernel activation, connect system interrupts and system clocks, modify or add the required device drivers;
5. Testing and verification, the correctness and stability of the BSP play a crucial role in the stability of upper-layer software and the entire system, so the BSP must undergo testing and verification after completion.
The three main programs in the development of the BSP are:
1. The romInit() function in romInit.s, used to initialize the CPU and memory;
2. The sysHwInit() function in sysLib.c, used to initialize all hardware on the board to a quiescent state;
3. The sysHwInit2() function in sysLib.c, further initializing the hardware for use with vxWorks programs.
Below is a detailed analysis of the boot sequence of vxWorks based on the code:
1. Executing romInit():
When the system starts, the processor first jumps to the entry point _romInit() in ROM (located in romInit.s), the assembly code there is as follows:
_romInit:
Cold:
MOV r0, #BOOT_COLD
warm:
B start
.ascii "Copyright …"
.balign 4
start:
/* Adding a delay here can effectively solve the problem of some boards not booting after reset */
TEQ r0, #BOOT_COLD
MOVEQ r1, #INTEGRATOR_DELAY_VALUE
MOVNE r1, #1
delay_loop:
SUBS r1, r1, #1
BNE delay_loop
...Copy
This is the entry point of VxWorks in ROM. Before this, the function sysToMonitor in sysLib.c will check the first three lines of instructions to see if they have changed, thus determining whether it is a warm boot, the specific code is as follows:
if (p[0] == 0xE3A00002 && p[2] == 0x79706F43)
pRom = (FUNCPTR)(ROM_TEXT_ADRS + 4); /* warm boot address */
else
pRom = (FUNCPTR)ROM_TEXT_ADRS; /* start of ROM */
From the above code, it can be seen that for a warm boot, the processor will first add a small offset while executing the function romInit, and the code in romInit needs to be implemented in assembly language, mainly used for allocating memory, initializing the processor status word, and creating a temporary stack. Additionally, it will initialize the minimum necessary hardware, mask interrupts, and clear the cache. If the romInit function executes correctly, the memory space from LOCAL_MEM_LOCAL_ADRS to LOCAL_MEM_LOCAL_ADRS + LOCAL_MEM_SIZE will be readable and writable; otherwise, it indicates that this function has failed. After romInit executes successfully, it will determine the boot type and jump to the C function romStart() in bootInit.c, the jump code is as follows:
LDR r12, L$_rStrtInRom
ORR r12, r12, #1 /* in Thumb case */
BX r12
And L$_rStrtInRom is the address of the function romStart:
L$_HiPosn:
.long ROM_TEXT_ADRS + HiPosn - FUNC(romInit)
L$_rStrtInRom:
.long ROM_TEXT_ADRS + FUNC(romStart) - FUNC(romInit)
The parameter passed by romInit (i.e., the above-defined BOOT_COLD) determines whether to clear memory (cold boot) in romStart.
2. Executing romStart()
The main function of romStart is to copy the appropriate memory segments from ROM to RAM. If the code in ROM is compressed, the decompression process will also be added during the copying process. Then, if necessary, the processor will jump to the entry point of vxWorks in RAM, which is the _sysInit() function in sysALib.s; however, in general, it will jump to the usrInit() function in usrConfig.c. The distinction between these two jumps is related to the type of vxWorks image file. The following diagram will illustrate this. The function is relatively simple, mainly copying code and decompressing if necessary. The decompression code is as follows:
# ifdef UNCOMPRESSED
((FUNCPTR)ROM_OFFSET(copyLongs))(ROM_TEXT_ADRS,(UINT)K0_TO_K1(romInit),ROM_COPY_SIZE /sizeof (long));
# else
((FUNCPTR)ROM_OFFSET(copyLongs))(ROM_TEXT_ADRS,(UINT)K0_TO_K1(romInit),((UINT)wrs_kernel_data_end - (UINT)romInit) /sizeof (long));
# endif
The implementation of the copying code is done by the copylongs function, which is relatively simple and is omitted here. After completing these functions, the program jumps to the entry point sysInit function in RAM in sysALib.s.
3. Executing sysInit()
This is the first piece of code executed by the system after startup, mainly responsible for initializing processor registers and the system error table, while turning off interrupts, allowing tracing, and finally jumping to the usrInit function. The source code and explanation of the relevant operations are as follows:
MOV r1, #0
MCR CP_MMU, 0, r1, c13, c0, 0/* Initialize Processor registers */
/* Turn off CPU and external interrupts */
MRS r1, cpsr
BIC r1, r1, #MASK_MODE
ORR r1, r1, #MODE_SVC32 | I_BIT | F_BIT
MSR cpsr, r1
MOV r2, #IC_BASE /* R2->interrupt controller */
MVN r1, #0 /* &FFFFFFFF */
STR r1, [r2, #FIQ_DISABLE-IC_BASE] /* disable all FIQ sources */
STR r1, [r2, #IRQ_DISABLE-IC_BASE] /* disable all IRQ sources */
4. Executing usrInit()
This function is located in usrConfig.c and is the first C program started from the vxWorks image. The main task of this program is to fully initialize the CPU, turn off other hardware, and create conditions for the kernel’s self-start. Generally, no modifications are needed for this function, but the functions it calls, such as sysHwInit, do require changes. The usrInit function masks all hardware interrupts during execution, and its initialization work includes the following:
1. Initialize Cache: The beginning segment of the function initializes the cache (calling function cachelibInit), and at the end of the function, the instruction and data registers will be enabled by default;
2. Initialize the system BSS segment to 0: The code behavior is bzero(edata,end-edata), which is simple because the method of initializing variables in C is to set them to 0.
3. Initialize the interrupt vector: The exception vector must be initialized before enabling interrupts and starting the kernel. First, the intVecBaseSet function is called to determine the starting address of the vector table, then the excVecInit function initializes all exception vectors to default handlers, which can catch and handle exceptions caused by program errors;
4. Initialize system hardware to a quiet state: Initialize hardware by calling sysHwInit.
5. Call kernelInit, which initializes the multitasking environment and never returns. It requires many parameters, which are uniformly placed in a structure as follows:
typedef struct kernelInit_params
{
FUNCPTR rootRtn; /* function to run as tRootTask */
unsigned int rootMemSize; /* memory for TCB and root stack */
char *pMemPoolStart; /* beginning of memory pool */
char *pMemPoolEnd; /* end of memory pool */
unsigned int intStackSize; /* interrupt stack size */
int lockOutLevel; /* interrupt lock-out level (1-7) */
unsigned int vmPageSize; /* configuration's VM_PAGE_SIZE */
unsigned int intStackOverflowSize; /* stack overflow region size */
unsigned int intStackUnderflowSize; /* stack underflow region size */
unsigned int idleTaskExcepStkSize; /* SMP idle task exception stack size */
unsigned int idleTaskExcepStkOverflowSize; /*... overflow region size */
unsigned int idleTaskExcepStkUnderflowSize;/*... underflow region size */
} _KERNEL_INIT_PARAMS;
5. Executing kernelInit()
This function is encapsulated by the Wind River vxWorks library file, and its function is to extract the kernel and run it, allowing subsequent initialization to be completed by tasks running under the kernel. This task is called tRootTask, and its execution method is similar to usrRoot. kernelInit first calls the intLockLevelSet function to mask the round-robin scheduling mode (not sure how to translate this) and creates the interrupt stack. Then it generates the root stack and TCB (Task Control Block) from the top of the memory pool, generates the root thread usrRoot, and terminates the thread usrInit. At this point, interrupts are enabled.
6. Executing usrRoot as a task
usrRoot will complete the initialization of the kernel and all hardware (network, drivers, I/O, etc.), and then execute other program code. It will first call the memInit function (which can choose memShowInit or usrMmuInit), and once the system begins multi-task execution, the BSP will call the first function sysClkConnect, which will immediately call sysHwInit2, which completes all hardware initialization work that was not completed in sysHwInit, such as establishing connections between interrupt sources using intConnect.Then usrRoot continues with clock initialization, modifying SYS_CLK_RATE to set the default clock frequency (usually 60), and then calling sysClkEnable to enable the clock.
Next, a simplified introduction to the startup method through diagrams. Since vxWorks has multiple types of program images, they can mainly be divided into two categories: BootRom type and vxWorks type. Among them, the BootRom type image is a minimized, specialized vxWorks boot image that implements minimal system initialization, mainly used to boot load the vxWorks image, functioning similarly to the BIOS of a PC. The BootRom also establishes a multitasking environment during runtime, including usrRoot tasks, network tasks, TFFS tasks, and FTP tasks, etc. During the execution of the boot image, it may execute in ROM/Flash (e.g., ROM-resident boot image) or in RAM, and its corresponding compilation rules file in the system is rules.bsp. The startup flowchart is as follows:
And the vxWorks type image is the main image of the system, which is the image that the system ultimately runs. This image requires at least some part (such as the data segment and BSS segment) to run in RAM. Its corresponding compilation rules file in the system is rules.vxWorks, and its startup flowchart is as follows:
sysLib.c is the largest, and it is recommended to implement only the basic functions during the early stage of BSP development, including sysModel(), sysBspRev(), sysHwInit(), sysHwInit2(), and sysMemTop().