在生产项目中使用 VxWorks 7 时,最关键的任务之一是 BSP (板级支持包) 开发。BSP 负责将内核适配到您的自定义 ARM 硬件上,确保引导启动、内存映射和外设初始化都能正确执行。
在本文中,我们提供了一个详尽的分步指南,用于将 VxWorks 移植到自定义 ARM 开发板。无论您使用的是 Cortex-A8、Cortex-A53 还是多核 SoC,本指南都将帮助您完成开发板的适配工作。
什么是 VxWorks BSP? #
板级支持包 (Board Support Package) 是操作系统与硬件之间的粘合剂。它提供:
- 引导和初始化代码
- MMU 设置和缓存策略
- 设备树和硬件描述
- 特定于开发板的驱动程序 (UART, Ethernet, I2C, SPI, GPIO)
- 系统库 (
sysLib.c
,sysHwInit.c
) - 内核配置参数
没有 BSP,VxWorks 就无法在您的开发板上运行。有了一个设计良好的 BSP,操作系统才能可靠地启动、运行,并充分利用所有硬件功能。
步骤 1:准备开发环境 #
-
安装工具链: 使用 Wind River Workbench 或 ARM 交叉编译器(ARMv7 使用
arm-wrs-linux-gnueabi-gcc
,ARMv8 使用aarch64-wrs-linux-gcc
)。 -
设置源码树: VxWorks BSP 位于以下路径:
$WIND_HOME/vxworks-7/bsps/arm/<board_name>/
-
收集硬件文档:
- SoC 参考手册
- 开发板原理图
- 现有的 U-Boot/Linux 设备树(供参考)
步骤 2:BSP 目录结构 #
一个典型的 ARM BSP 目录结构如下:
board/
├── Makefile # BSP 构建规则
├── config.h # 内核和开发板配置
├── sysHwInit.c # 硬件初始化
├── sysLib.c # 板级系统调用
├── sysClk.c # 时钟/定时器支持
├── sysSerial.c # UART/控制台驱动
├── fdt/ # 扁平设备树文件
├── linkscript.ld # 内存映射
└── drivers/ # 自定义设备驱动
📌 提示:从一个相似的参考 BSP(例如 ARMv8 Cortex-A53)开始,复制并根据您的硬件进行定制。
步骤 3:引导加载程序和启动代码 #
大多数 ARM 开发板使用 U-Boot。BSP 必须定义:
- 链接器脚本 (Linker Script):定义内存区域(DDR, SRAM, 外设)。
- 启动汇编代码:设置异常向量、堆栈、MMU 和缓存。
- sysHwInit():在内核启动前运行。
内存映射示例 (linkscript.ld
):
MEMORY
{
SRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 256K
DDR (rwx) : ORIGIN = 0x80000000, LENGTH = 1G
}
SECTIONS
{
.text : { *(.text*) } > DDR
.data : { *(.data*) } > DDR
.bss : { *(.bss*) } > DDR
}
硬件初始化示例 (sysHwInit.c
):
void sysHwInit(void)
{
/* 为 DDR 和外设设置 MMU */
armMmuInit();
/* 初始化系统时钟 */
sysClkInit();
/* 使能 UART 控制台 */
sysSerialHwInit();
/* 使能中断 */
intLibInit();
}
步骤 4:设备树 (FDT) #
VxWorks 使用扁平设备树 (Flattened Device Tree, FDT) 来描述硬件。
示例 (custom-board.dts
):
/ {
model = "vendor,custom-arm-board";
compatible = "vendor,custom";
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1GB DDR */
};
soc {
uart0: serial@101f1000 {
compatible = "arm,pl011";
reg = <0x101f1000 0x1000>;
clock-frequency = <24000000>;
};
eth0: ethernet@10100000 {
compatible = "snps,designware-eth";
reg = <0x10100000 0x2000>;
};
};
};
📌 专业提示:如果已有 Linux DTS 文件,可以从它开始,然后精简并适配给 VxWorks 使用。
步骤 5:编写自定义驱动程序 #
并非所有外设都有现成的支持。对自定义设备,请使用 VxBus 驱动模型。
UART 驱动示例 (sysSerial.c
):
STATUS sysSerialHwInit(void)
{
UART_REG->LCR = 0x3; /* 8 位数据位, 无校验, 1 位停止位 */
UART_REG->DLL = 13; /* 波特率除数 */
UART_REG->IER = 0x1; /* 使能接收中断 */
return OK;
}
📌 驱动集成:
- 使用 VxBus 注册设备。
- 通过内核 Shell 进行测试 (
-> printf("Hello from UART\n")
)。
步骤 6:MMU 和缓存设置 #
MMU (内存管理单元) 确保内存属性正确配置。属性示例:
- DDR → 可缓存 (Cacheable), 可缓冲 (Bufferable)
- 外设 I/O → 强序 (Strongly-Ordered), 不可缓存 (Non-Cacheable)
- 引导 ROM → 只读 (Read-Only)
在 sysLib.c
中配置:
VM_STATE sysPhysMemDesc[] = {
{ 0x80000000, 0x80000000, 0x40000000, VM_STATE_CACHEABLE },
{ 0x10100000, 0x10100000, 0x00100000, VM_STATE_IO }
};
步骤 7:测试与调试 #
-
引导测试 通过 U-Boot 中的 TFTP 加载 VxWorks 镜像:
=> tftpboot 0x80000000 vxWorks => go 0x80000000
-
控制台测试 验证 UART 输出:
VxWorks Bootrom 7.x >
-
外设测试
- UART:发送/接收测试
- 以太网:
ifconfig
,ping
- 定时器:检查系统时钟节拍
-
调试工具
- Workbench Debugger:加载符号表,设置断点
- JTAG:用于引导失败时的底层调试
- 内核 Shell:使用
peek
和poke
检查内存/寄存器
最佳实践 #
- 从最少功能开始(例如 UART + 定时器)。
- 使用参考 BSP(可以节省数周的开发时间)。
- 保持驱动程序的模块化和可重用性。
- 详细记录 MMU 属性和设备映射。
- 使用脚本自动化构建和测试流程。
常见问题 (FAQ) #
问:我可以在 VxWorks 中重用 Linux 设备树吗? 答:可以,但只能作为参考。您必须调整绑定 (bindings) 以适配 VxWorks 的驱动程序。
问:我需要一个自定义的引导加载程序吗? 答:通常不需要。U-Boot 得到广泛支持,但您可能需要打一些小补丁。
问:如何调试 BSP 在引导过程中挂起的问题?
答:使用 JTAG,或者在 sysHwInit()
中添加调试串口打印来跟踪执行流程。
BSP 架构概览 #
以下是 VxWorks 中 BSP 的一个简单分层视图:
+--------------------------+
| 应用程序 |
+--------------------------+
| VxWorks 内核 |
+--------------------------+
| BSP (驱动, 初始化) |
+--------------------------+
| 引导加载程序 (U-Boot) |
+--------------------------+
| 自定义 ARM 硬件 |
+--------------------------+
结论 #
为自定义 ARM 开发板移植 VxWorks 7 的 BSP 是一个多步骤的过程,需要对硬件、引导加载程序、内存管理和驱动程序有深入的理解。通过正确的方法——引导代码、设备树配置、驱动集成和系统性调试——您可以成功地启动您的开发板,并充分利用 VxWorks 的实时性能。
📌 在下一篇文章中,我们将重点介绍 VxBus 驱动程序开发,届时我们将从零开始构建一个完整的自定义驱动程序。