2. 理解VxWorks启动流程与BSP初始化顺序
任务目标
在VxWorks BSP开发中,启动流程的初始化顺序直接决定硬件环境是否就绪、内核能否正常引导。本案例通过阅读BSP源码并在关键阶段插入打印函数,梳理从复位向量到usrInit、usrKernelInit等阶段的执行顺序,建立对启动链路的清晰认知。
前置条件
- 已安装Wind River Workbench或VxWorks开发环境,目标板为x86或PowerPC架构(本案例以x86为例)。
- 已创建或获取一个可编译的VxWorks BSP工程(如
mv4100或pcPentium)。 - 具备串口终端或调试器输出通道,用于观察打印信息。
启动阶段与关键源码位置
VxWorks启动流程分为两个阶段:Boot ROM阶段与VxWorks内核阶段。Boot ROM负责最小硬件初始化和加载VxWorks镜像;内核阶段从sysInit开始,依次经过usrInit、usrKernelInit、usrRoot等函数完成系统初始化。以下为x86 BSP中典型的调用顺序:
- 复位向量:CPU上电后跳转到
sysInit(位于sysAlib.s),完成关中断、设置栈指针等基础操作。 - usrInit(
usrConfig.c):关闭中断、初始化Cache、清零BSS段,然后调用usrKernelInit。 - usrKernelInit(
usrKernel.c):创建内核对象、初始化任务调度器,随后启动usrRoot任务。 - usrRoot(
usrConfig.c):完成设备驱动挂载、网络协议栈初始化、启动应用任务。
实现步骤
在BSP源码的上述关键函数入口处添加打印语句,编译后通过串口观察输出顺序。以下代码展示了在usrInit和usrKernelInit中添加打印的实现:
// 文件:usrConfig.c(部分)
#include <vxWorks.h>
#include <stdio.h>
#include <usrLib.h>
// 在usrInit函数入口处添加打印
void usrInit(int memStart, int memEnd)
{
printf("\n[BOOT] Enter usrInit: memStart=0x%x, memEnd=0x%x\n", memStart, memEnd);
/* 原有初始化代码:关闭中断、初始化Cache等 */
cacheLibInit();
usrKernelInit();
}
// 文件:usrKernel.c(部分)
void usrKernelInit(void)
{
printf("[BOOT] Enter usrKernelInit\n");
/* 原有内核初始化代码 */
kernelInit(usrRoot, ...);
}
// 在usrRoot入口处添加打印
void usrRoot(void)
{
printf("[BOOT] Enter usrRoot\n");
/* 原有系统初始化代码 */
usrIosLibInit();
usrNetInit();
}
对于Boot ROM阶段的sysInit(汇编文件),可在C语言可调用处添加打印,或通过调试器设置断点观察。若需在汇编阶段输出,可调用sysSerialHwInit后的串口发送函数,但更简单的方式是使用调试器在sysInit处打断点,单步跟踪执行流。
编译与运行
- 在Workbench中重新编译BSP工程,生成
bootrom.bin和vxWorks镜像。 - 将Boot ROM烧录到目标板(或通过仿真器加载),连接串口终端(波特率通常为9600或115200,视BSP配置而定)。
- 上电或复位目标板,观察串口输出。
验证方法
串口输出应依次出现以下打印信息(顺序严格匹配):
[BOOT] Enter usrInit: memStart=0x..., memEnd=0x...
[BOOT] Enter usrKernelInit
[BOOT] Enter usrRoot
若打印顺序与上述一致,且系统最终进入VxWorks shell提示符(如->),则说明启动流程梳理正确。若缺少某条打印或顺序错乱,则需检查对应函数是否被调用、链接脚本中入口地址是否正确。
常见问题
- 无串口输出:检查串口驱动是否在
sysHwInit中完成初始化,或Boot ROM阶段是否已配置串口参数。 - 打印顺序与预期不符:确认修改的源码文件确实被编译进当前镜像(检查Makefile中是否包含该文件),并清除旧镜像重新编译。
- 系统启动崩溃:若在
usrInit前添加打印导致崩溃,说明串口尚未就绪,此时应改用调试器观察,而非直接打印。