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 用户应用启动:根任务或启动脚本加载应用模块,创建用户任务,进入主循环。交付物是“用户应用开始执行”。

把这条链路画成一张图,能更直观地看到控制权、镜像形态和运行环境三者的同步变化:

VxWorks 启动链:控制权、镜像形态与运行环境的同步演进 阶段0 复位 时钟/PLL/DDR 异常向量表 交付:可用DDR 阶段1 Bootloader 串口/网口/Flash 引导参数与脚本 交付:加载环境 阶段2 加载校验 TFTP/Flash读取 头部/长度/CRC 交付:内存中镜像 阶段3 解压重定位 解压到运行地址 段搬运/BSS清零 交付:可执行映像 阶段4 内核初始化 MMU/中断/时钟 tUsrRoot/驱动 交付:可调度内核 阶段5 应用 加载模块 创建用户任务 交付:应用运行 控制权 CPU复位向量 Bootloader 镜像加载器 VxWorks入口 用户任务 镜像形态 bootrom.bin bootrom_uncmp vxWorks(压缩) vxWorks(解压) 应用.out 运行环境 仅SRAM/BootROM DDR可用+串口 DDR+网络/Flash MMU+中断+时钟 多任务+文件系统 关键交接条件(不满足则启动链断裂) ① DDR训练完成且读写校验通过 → ② 镜像头部magic与长度合法 → ③ CRC校验一致 → ④ 解压后入口地址与链接地址一致 → ⑤ 根任务成功创建并进入就绪态

三、各阶段之间的交接条件

启动链最容易出问题的地方不在阶段内部,而在阶段之间的交接点。以下五个条件是工程上必须逐项确认的:

  1. DDR可用性:阶段0结束时,必须通过一次写-读-比较测试确认DDR控制器配置正确。若DDR时序参数(如tRFC、tRCD、CL)与颗粒不匹配,后续镜像加载会表现为“加载成功但运行即挂”。
  2. 镜像头部合法性:VxWorks镜像头部包含magic、版本、入口偏移、段表等信息。Bootloader在加载后应先解析头部,确认magic匹配再继续,否则应报错并停留在命令行。
  3. 校验一致性:对镜像数据区计算CRC32并与头部记录值比较。这一步能拦截Flash位翻转、TFTP传输丢包等常见故障。
  4. 入口地址一致性:解压或重定位后的入口地址必须与镜像链接时的入口地址一致。若链接地址为0x00100000而实际加载到0x00200000,跳转后取指将失败。
  5. 根任务就绪:内核初始化完成后,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可测、镜像可校验、入口可检查、任务可观察,这为自动化启动测试提供了明确的检查点。

在项目初期就应确定每个阶段的串口打印标志和错误码,并写入板级支持包文档。这样即使换人维护,也能依据日志快速判断启动链断在哪一环。