1. 引言 #
板级支持包(BSP) 是 VxWorks 的硬件抽象层,负责在系统启动时初始化处理器、内存、时钟、中断控制器和外设,然后将控制权移交至操作系统内核。如果没有合适的 BSP,VxWorks 无法在您的硬件上运行。
BSP 通常高度依赖特定硬件,但共享一些通用组件,例如:
- 早期处理器初始化
- 内存管理(内存管理单元设置)
- 设备初始化和配置
- 启动中断控制器
- 通过 VxBus 支持设备驱动程序
由于 BSP 开发涉及使用 C 语言和汇编语言的低级编程,理解处理器手册和电路板原理图至关重要。
2. BSP 开发工作流程 #
2.1 硬件文档 #
首先收集以下资料:
- 处理器/片上系统参考手册
- 内存映射和引脚复用的电路板原理图
- 外设数据手册,包含寄存器和中断信息
2.2 选择起始 BSP #
通常可以找到适用于相似处理器的 VxWorks BSP。例如,可以通过更新内存和设备树条目,将基于 Cortex-A9 的 BSP 适配到新的 A9 板上。
2.3 移植 BSP #
- 内存映射:调整物理和虚拟内存范围,确保内存管理单元设置正确。
- 时钟和锁相环设置:根据外设需求初始化板上时钟。
- 串口/控制台设置:用于早期调试输出。
- 中断控制器:初始化中断控制器以处理设备中断请求(IRQ)。
- 设备树:描述所有硬件外设。
2.4 构建与测试 #
- 使用 Wind River 的
make
或vxprj
工具构建 BSP。 - 使用 JTAG 或串口控制台调试启动过程。
- 使用 VxWorks shell 命令(
devs
、i
、ld
)进行运行时诊断。
3. BSP 目录结构 #
示例布局:
your_bsp/
├── Makefile # 构建规则和编译器标志
├── config.h # BSP 全局定义(处理器频率、内存大小)
├── sysLib.c # 系统初始化函数
├── sysClk.c # 系统时钟(定时器)支持
├── sysSerial.c # 串口驱动接口
├── hwconf.c # 硬件资源配置表
├── device-tree/ # 设备树源文件 (*.dts)
│ ├── myboard.dts
│ └── ...
└── README.md
3.1 sysLib.c #
包含处理器设置、内存初始化和系统启动入口点,例如:
void sysHwInit(void)
{
/* 禁用中断 */
vxCpuIntDisable();
/* 初始化内存控制器 */
sysMemInit();
/* 初始化串口以进行早期调试 */
sysSerialHwInit();
/* 设置中断控制器 */
sysIntInit();
/* 设置系统定时器 */
sysClkInit();
/* 启用中断 */
vxCpuIntEnable();
}
3.2 config.h #
定义板卡参数,如时钟频率、内存大小和 BSP 配置宏。示例:
#define SYS_CLK_RATE 100 /* 100 ticks per second */
#define LOCAL_MEM_LOCAL_ADRS 0x80000000
#define LOCAL_MEM_SIZE 0x10000000 /* 256MB */
#define UART_BASE_ADDR 0x10000000
#define UART_BAUD_RATE 115200
4. BSP 核心组件 #
4.1 引导加载程序概述 #
引导加载程序的任务是执行非常早期的系统初始化并将 VxWorks 镜像加载到内存中。通常,这是一个小型程序或 U-Boot 变体,负责:
- 配置处理器模式(缓存、内存管理单元开启或关闭)
- 设置初始动态随机存储器(DRAM)控制器
- 初始化串口以进行调试输出
- 从闪存、网络或 SD 卡加载内核镜像
- 跳转到 VxWorks 内核的
sysStart()
入口
4.2 早期初始化代码示例 #
void sysHwInit0(void)
{
/* 全局禁用中断 */
vxCpuIntDisable();
/* 初始化时钟 */
clkInit();
/* 初始化串口以进行早期控制台输出 */
uartInit(UART_BASE_ADDR, UART_BAUD_RATE);
/* 打印启动信息 */
printf("BSP early initialization complete.\n");
}
4.3 引导加载程序早期初始化 #
void sysHwInit0(void)
{
vxCpuIntDisable();
*(volatile uint32_t *)0xF8006000 = 0x12345678; /* DDR 时序 */
*(volatile uint32_t *)0xF8006004 = 0x0000AABB;
pllConfig(0x1F, 0x2A);
uartInit(0x9000000, 115200);
printf("Board early init done, UART ready.\n");
}
4.4 sysStart() 入口点 #
sysStart()
函数是引导加载程序加载操作系统镜像后调用的内核启动例程。通常它执行进一步的硬件初始化,然后调用用户应用程序入口 usrRoot()
。
5. 设备树配置 #
5.1 什么是设备树? #
设备树(DT)是一种以平台无关方式描述系统硬件组件的数据结构。设备树避免在 BSP 代码中硬编码硬件参数,允许操作系统和驱动程序在启动时动态发现和配置设备。
VxWorks 使用从 .dts
源文件编译的设备树二进制文件(.dtb
)来初始化硬件资源并通过 VxBus 绑定驱动程序。
5.2 设备树语法示例 #
/ {
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <5>;
clock-frequency = <24000000>;
};
timer0: timer@10002000 {
compatible = "arm,armv7-timer";
reg = <0x10002000 0x1000>;
interrupts = <30>;
};
};
compatible
:标识设备类型或绑定的驱动程序reg
:设备寄存器的基地址和大小interrupts
:中断请求号(IRQ)- 其他属性(例如时钟频率)用于配置设备
5.3 在 BSP 中集成设备树 #
- 将
.dts
文件放置在bsp/your_bsp/device-tree/
目录 - 在 BSP Makefile 中添加设备树编译,使用
dtc
(设备树编译器) - 在 BSP 初始化代码中加载并解析设备树二进制文件,然后探测设备
hwconf.c
示例(伪代码):
void hwconfInit(void)
{
dtb = loadDeviceTreeBlob("/boot/myboard.dtb");
vxBusInit(dtb);
}
5.4 复杂设备树片段 #
i2c1: i2c@40800000 {
compatible = "arm,my-i2c";
reg = <0x40800000 0x1000>;
interrupts = <23>;
clock-frequency = <100000>;
temp_sensor@48 {
compatible = "ti,tmp102";
reg = <0x48>;
};
eeprom@50 {
compatible = "at,24c256";
reg = <0x50>;
};
};
6. 内存管理单元与缓存设置 #
6.1 内存管理单元设置的重要性 #
内存管理单元(MMU)控制虚拟内存转换、访问权限和缓存属性。正确的内存管理单元配置对于以下方面至关重要:
- 保护内存区域
- 启用缓存以提高性能
- 将外设映射为非缓存区域
6.2 定义物理内存区域 #
使用 VM_REGION
结构在 sysPhysMemDesc[]
中描述内存布局。示例:
VM_REGION sysPhysMemDesc[] = {
{
(VIRT_ADDR) LOCAL_MEM_LOCAL_ADRS,
(PHYS_ADDR) LOCAL_MEM_LOCAL_ADRS,
LOCAL_MEM_SIZE,
VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE,
VM_STATE_VALID | VM_STATE_WRITABLE
},
{
(VIRT_ADDR) PERIPH_BASE_ADDR,
(PHYS_ADDR) PERIPH_BASE_ADDR,
PERIPH_SIZE,
VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE | VM_STATE_MASK_CACHEABLE,
VM_STATE_VALID | VM_STATE_WRITABLE /* 无缓存 */
}
};
6.3 启用缓存 #
在启动代码中尽早启用指令和数据缓存:
void cacheEnable(void)
{
cacheEnable(INSTRUCTION_CACHE);
cacheEnable(DATA_CACHE);
}
6.4 直接内存访问(DMA)缓冲区的缓存管理 #
对于处理器和 DMA 设备共享的缓冲区,刷新或使缓存失效以确保一致性:
void prepareDmaBuffer(void *buffer, size_t size)
{
cacheFlush(DATA_CACHE, buffer, size);
}
7. 中断与定时器初始化 #
7.1 中断控制器设置 #
BSP 必须初始化中断控制器(例如 ARM 上的 GIC),以:
- 路由设备中断请求
- 启用/禁用中断
- 设置中断优先级
sysIntInit()
示例:
void sysIntInit(void)
{
gicInit();
gicEnableDistributor();
}
7.2 连接中断服务例程(ISR) #
使用 intConnect()
将中断向量与中断服务例程关联:
STATUS sysSerialIntConnect(void)
{
return intConnect(INUM_TO_IVEC(UART_INT_VEC), (VOIDFUNCPTR)sysSerialIntHandler, 0);
}
7.3 系统时钟初始化 #
系统时钟驱动操作系统滴答和定时。典型的 BSP 设置:
STATUS sysClkConnect(FUNCPTR routine, int arg)
{
sysClkRoutine = routine;
sysClkArg = arg;
return OK;
}
void sysClkInt(void)
{
if (sysClkRoutine)
sysClkRoutine(sysClkArg);
}
STATUS sysClkEnable(void)
{
timerEnable(TIMER0, SYS_CLK_RATE);
intConnect(INUM_TO_IVEC(TIMER0_INT_VEC), sysClkInt, 0);
intEnable(TIMER0_INT_VEC);
return OK;
}
7.4 ARM GIC 中断设置 #
void sysIntInit(void)
{
gicDistInit();
gicCpuInit();
gicDistEnable();
gicSetPriority(UART_INT_VEC, 0x80);
gicEnableInterrupt(UART_INT_VEC);
intConnect(INUM_TO_IVEC(UART_INT_VEC), (VOIDFUNCPTR)uartIsr, 0);
intEnable(UART_INT_VEC);
}
8. 添加设备驱动程序 #
8.1 VxBus 框架 #
VxWorks 驱动程序由 VxBus 管理,VxBus 使用设备树信息动态探测和绑定驱动程序。
8.2 示例:串口驱动程序初始化 #
#include <vxBusLib.h>
#include <hwif/vxbus/vxBus.h>
LOCAL VXB_DEV_ID uartDev;
STATUS sysSerialHwInit(void)
{
uartDev = vxbInstByNameFind("ns16550", 0);
if (!uartDev)
return ERROR;
return OK;
}
8.3 驱动程序探测与绑定方法 #
驱动程序定义探测和绑定回调函数,供 VxBus 在设备枚举时调用:
LOCAL STATUS myUartProbe(VXB_DEV_ID pDev)
{
/* 验证硬件存在 */
return OK;
}
LOCAL STATUS myUartAttach(VXB_DEV_ID pDev)
{
/* 映射寄存器,初始化设备 */
return OK;
}
LOCAL VXB_DRV_METHOD myUartMethods[] = {
{ VXB_DEVMETHOD_CALL(vxbDevProbe), (FUNCPTR)myUartProbe },
{ VXB_DEVMETHOD_CALL(vxbDevAttach), (FUNCPTR)myUartAttach },
VXB_DEVMETHOD_END
};
VXB_DRV_DEF(myUartDrv, myUartMethods, "My UART Driver");
8.4 串口驱动程序探测与绑定 #
LOCAL STATUS uartProbe(VXB_DEV_ID pDev)
{
volatile uint32_t *reg = (volatile uint32_t *)vxbRegBaseAddr(pDev);
if ((*reg & 0xFF) != EXPECTED_UART_ID)
return ERROR;
return OK;
}
LOCAL STATUS uartAttach(VXB_DEV_ID pDev)
{
volatile uint32_t *base = (volatile uint32_t *)vxbRegBaseAddr(pDev);
base[UART_BAUD_REG] = UART_BAUD_115200;
base[UART_CTRL_REG] = UART_ENABLE | UART_RX_INT_ENABLE;
return OK;
}
9. BSP 调试与测试 #
9.1 使用 VxWorks Shell #
VxWorks shell 提供命令用于检查和调试 BSP 及驱动程序:
devs
— 列出 VxBus 识别的设备i
— 显示活动中断ld
— 列出已加载的模块/驱动程序sp
— 启动任务以测试驱动程序
示例:
-> devs
ns16550@10000000 (serial)
timer@10002000
9.2 WindView 事件追踪 #
WindView 允许追踪中断和上下文切换等事件,以分析 BSP 性能。在关键 BSP 代码段中集成 WindView 宏以进行细粒度分析。
9.3 硬件调试器 #
使用 JTAG 或 BDI 进行:
- 逐步执行启动代码
- 检查寄存器和内存
- 在 BSP 代码(如
sysHwInit
)中设置断点
10. 最佳实践 #
- 从相近的 BSP 开始:节省时间并减少错误
- 增量测试:分别测试每个阶段 — 内存、控制台、中断、定时器
- 使用设备树:避免硬编码硬件描述,便于维护
- 记录一切:内存映射、中断请求、时钟设置
- 隔离 BSP 和应用程序代码:保持模块化设计
11. 高级 BSP 主题 #
11.1 多核(SMP)启动 #
在 VxWorks 中启用 SMP #
定义处理器数量并启用 SMP 配置宏:
#define _WRS_CONFIG_SMP 1
#define VX_SMP_NUM_CPUS 4
启动次级处理器 #
BSP 必须启动次级核心并初始化其内核数据:
void sysSecondaryCpuStart(void)
{
sysSecondaryCpuInit();
kernelCpuInit();
}
每个次级处理器运行此代码以加入 SMP 内核。
11.2 添加自定义外设驱动程序 #
设备树示例 #
spi1: spi@40013000 {
compatible = "myvendor,myspi";
reg = <0x40013000 0x1000>;
interrupts = <12>;
bus-frequency = <48000000>;
};
驱动程序框架 #
LOCAL STATUS myspiProbe(VXB_DEV_ID pDev) { return OK; }
LOCAL STATUS myspiAttach(VXB_DEV_ID pDev) { return OK; }
LOCAL VXB_DRV_METHOD myspiMethods[] = {
{ VXB_DEVMETHOD_CALL(vxbDevProbe), (FUNCPTR)myspiProbe },
{ VXB_DEVMETHOD_CALL(vxbDevAttach), (FUNCPTR)myspiAttach },
VXB_DEVMETHOD_END
};
VXB_DRV_DEF(myspiDrv, myspiMethods, "MyVendor SPI Driver");
11.3 性能优化 #
- 启用缓存并明确管理 DMA 缓冲区的一致性
- 使用快速中断连接(
intConnect
)并仅启用必要的中断请求 - 将设备映射为非缓存内存区域以避免数据陈旧
- 通过保持中断服务例程简短并推迟工作来最小化中断延迟
11.4 启动次级处理器(ARM Cortex-A9) #
void sysSecondaryCpuStart(void)
{
*(volatile uint32_t *)CPU_RELEASE_ADDR = SECONDARY_CPU_START_ADDR;
while(!secondaryCpuReady());
kernelCpuInit();
}
12. 调试高级 BSP 问题 #
- 多核调试:使用支持多核调试的 JTAG 工具;注意同步问题
- 驱动程序调试:使用 VxBus 命令如
vxbDevShow()
并检查驱动程序探测/绑定返回值 - 内存问题:仔细检查内存管理单元映射;使用 VxWorks 内存工具监控堆/栈
13. 高级最佳实践 #
- 增量启动 SMP,逐个测试每个处理器
- 使用设备树叠加层测试新硬件而无需重新编译 BSP
- 设计确定性的中断服务例程并使用内核安全的 API
- 为 BSP 驱动程序接口维护详细的 API 文档
14. 结论 #
掌握 VxWorks 的 BSP 开发可以完全控制嵌入式系统硬件。从基本启动初始化到高级 SMP 支持和自定义驱动程序集成,精心设计的 BSP 是可靠、高性能实时应用的基础。
通过模块化设计、完善的文档和彻底的测试,您的 BSP 可以支持复杂的硬件平台,并使 VxWorks 系统满足苛刻的实时要求。