1. VxWorks启动链全景
理解VxWorks的启动过程,不能只记住“上电后先跑bootrom再跑VxWorks”这样一句概括。真正决定系统能否在几百毫秒内进入用户应用、以及出问题时该从哪里下手的,是启动链上每一个阶段各自承担的任务、交付物和交接条件。本章把整条链路拆成六个阶段,从CPU复位向量一直走到用户应用入口,并说明每个阶段结束时“什么东西已经就绪、什么东西还没有就绪”。
一、为什么启动链必须分阶段
VxWorks是运行在目标硬件上的实时操作系统,它面对的第一个约束是:CPU复位后,内存控制器尚未配置,DDR不可用,此时能使用的只有芯片内部SRAM、Boot ROM或片上RAM。而VxWorks内核镜像通常有几百KB到数MB,必须被搬到DDR中才能运行。这就天然形成了“小体积引导代码先跑、大体积系统镜像后跑”的两级甚至三级结构。
第二个约束是硬件初始化具有严格的先后顺序。时钟、PLL、DDR控制器、串口、网络控制器、Flash控制器之间存在依赖关系:没有稳定时钟就无法配置DDR时序,没有DDR就无法把镜像解压到内存,没有串口就无法输出启动日志。启动链的阶段划分,本质上就是把这些依赖关系排成一个可执行的偏序序列。
第三个约束是可维护性。工程上需要把“与板级硬件强相关”的代码和“与操作系统相关”的代码分开,前者放在bootrom中随板卡固化,后者放在VxWorks镜像中可独立升级。这种分离直接决定了启动链的形态。
二、六个阶段与各自的交付物
下面按执行顺序给出各阶段的核心任务与交付物。需要说明的是,具体项目可能把某些阶段合并(例如U-Boot直接引导VxWorks),也可能拆得更细(例如增加二级引导),但职责边界基本一致。
- 阶段0 复位与初级硬件初始化:CPU从复位向量取指,执行汇编级初始化——关闭看门狗、设置异常向量表基址、初始化时钟与PLL、配置DDR控制器、建立临时栈。交付物是“可用的DDR和可运行的C环境”。
- 阶段1 Bootloader主体运行:完成串口、网口、Flash等外设初始化,提供命令行或自动引导脚本。交付物是“可加载镜像的运行环境”和“引导参数”。
- 阶段2 镜像加载与校验:从Flash、TFTP、磁盘或USB读取VxWorks镜像到DDR指定地址,校验头部、长度与CRC。交付物是“内存中完整且校验通过的镜像”。
- 阶段3 镜像解压与重定位:若镜像是压缩格式,先解压;随后按链接地址把各段搬到运行位置,清零BSS段。交付物是“位于最终运行地址、段布局正确的内核映像”。
- 阶段4 内核初始化:跳入VxWorks入口,建立MMU页表、初始化中断控制器与系统时钟、创建tUsrRoot根任务、挂载文件系统、初始化网络协议栈。交付物是“多任务调度已运行、驱动已就绪的内核”。
- 阶段5 用户应用启动:根任务或启动脚本加载应用模块,创建用户任务,进入主循环。交付物是“用户应用开始执行”。
把这条链路画成一张图,能更直观地看到控制权、镜像形态和运行环境三者的同步变化:
三、各阶段之间的交接条件
启动链最容易出问题的地方不在阶段内部,而在阶段之间的交接点。以下五个条件是工程上必须逐项确认的:
- DDR可用性:阶段0结束时,必须通过一次写-读-比较测试确认DDR控制器配置正确。若DDR时序参数(如tRFC、tRCD、CL)与颗粒不匹配,后续镜像加载会表现为“加载成功但运行即挂”。
- 镜像头部合法性:VxWorks镜像头部包含magic、版本、入口偏移、段表等信息。Bootloader在加载后应先解析头部,确认magic匹配再继续,否则应报错并停留在命令行。
- 校验一致性:对镜像数据区计算CRC32并与头部记录值比较。这一步能拦截Flash位翻转、TFTP传输丢包等常见故障。
- 入口地址一致性:解压或重定位后的入口地址必须与镜像链接时的入口地址一致。若链接地址为0x00100000而实际加载到0x00200000,跳转后取指将失败。
- 根任务就绪:内核初始化完成后,tUsrRoot任务应进入就绪态并被调度。若串口只打印到“usrRoot”之前就停止,通常说明驱动初始化中发生了阻塞或异常。
四、一个可观察的启动日志示例
下面是一段典型的串口启动输出(示例数据,用于说明各阶段的可见标志,非实测记录):
VxWorks Bootloader 2.0
CPU: ARM Cortex-A9 @ 800MHz
DDR: 512MB @ 400MHz ... OK
Flash: 16MB SPI NOR ... OK
Loading vxWorks from 0x00080000, size 0x2A0000
Header magic: 0x56584B53 ... OK
CRC32: 0x8F3A21C7 ... OK
Decompressing to 0x00100000 ... done
Jump to entry 0x00100000
VxWorks 6.9
Kernel: WIND kernel version 3.0
usrInit: MMU enabled, page table at 0x00300000
intCtlr: GIC initialized, 96 IRQs
sysClk: 1000 Hz tick
usrRoot: creating tUsrRoot
usrRoot: mounting /tffs0 ... OK
usrRoot: starting network ... OK
usrRoot: loading app.out ... done
Application started.
从日志中可以清楚看到阶段边界:Bootloader打印结束后跳转,VxWorks打印版本号表示阶段4开始,usrRoot相关行表示根任务正在执行初始化,最后“Application started”表示阶段5完成。排障时,日志停在哪个阶段,问题就大概率落在该阶段或其前一个交接点。
五、阶段划分对工程实践的意义
把启动链分成六个阶段,直接带来三个工程收益。第一,定位效率:现场设备启动失败时,通过串口日志可以快速把问题范围缩小到一个阶段,而不必从头审查全部代码。第二,升级策略:bootrom与VxWorks镜像分离,使得修改应用逻辑时只需重新烧写VxWorks镜像,bootrom保持不动,降低变砖风险。第三,可测试性:每个阶段的交付物都是可验证的——DDR可测、镜像可校验、入口可检查、任务可观察,这为自动化启动测试提供了明确的检查点。