4. BSP初始化流程:从sysHwInit到usrRoot的中断与定时器初始化链路
VxWorks 的 BSP 初始化流程是一条从底层硬件抽象到上层系统服务的清晰链路。中断控制器与时钟定时器的初始化贯穿其中,是系统能够响应外部事件、实现任务调度与时间管理的基石。本章节将详细拆解从 sysHwInit 到 usrRoot 的完整初始化路径,聚焦中断与定时器相关的关键节点。
核心链路概览:
sysHwInit() → sysIntInit() → sysTimerInit() → usrKernelInit() → usrRoot() → sysClkConnect() / sysAuxClkConnect() → sysClkEnable() / sysAuxClkEnable()
4.1 第一阶段:sysHwInit — 硬件抽象层入口
sysHwInit() 是 BSP 初始化流程的起点,由 usrInit() 在系统启动早期调用。此阶段运行在单任务、无缓存、无 MMU 的原始环境中,主要职责是完成 CPU 核心之外的基础外设初始化。
| 初始化项 | 典型实现 | 与中断/定时器的关系 |
|---|---|---|
| CPU 寄存器与模式 | 设置异常向量表基址、CPU 工作模式 | 为中断向量表重定位做准备 |
| 内存控制器 | 配置 SDRAM/DDR 时序与地址映射 | 确保中断向量表与栈空间可访问 |
| 中断控制器基础复位 | 屏蔽所有中断源、设置默认优先级 | 防止未初始化的外设产生误中断 |
| 定时器外设时钟使能 | 使能定时器模块的 IP 时钟与总线时钟 | 为后续定时器寄存器访问提供时钟 |
实战要点: 在
sysHwInit() 中,中断控制器应处于“全部屏蔽”状态。任何未连接 ISR 的中断源在此时触发,都会导致系统崩溃。建议在函数入口处执行一次全局中断屏蔽操作。
4.2 第二阶段:sysIntInit — 中断控制器初始化
sysIntInit() 是中断子系统的核心初始化函数,通常在 sysHwInit() 内部被调用。其目标是将中断控制器配置为已知的、可工作的初始状态。
4.2.1 标准初始化步骤
- 复位中断控制器: 将控制器恢复到硬件默认状态,清除所有挂起的中断。
- 配置中断分发模式: 对于 GIC(通用中断控制器),需设置 CPU 接口、中断分组(Group 0/1)以及安全状态。
- 设置中断优先级与掩码: 为所有中断源分配默认优先级,并确保所有中断处于屏蔽状态。
- 配置中断向量表基址: 将 CPU 的异常向量表指向 VxWorks 的中断向量表(通常位于 RAM 中)。
- 使能 CPU 中断接口: 最后使能 CPU 与中断控制器之间的接口信号。
4.2.2 关键数据结构
/* VxWorks 中断描述符表结构示例 */
typedef struct {
VOIDFUNCPTR isr; /* 中断服务函数指针 */
int parameter; /* 传递给 ISR 的参数 */
UINT8 priority; /* 中断优先级 */
UINT8 vector; /* 中断向量号 */
} INT_DESC;
/* 全局中断描述符表,由 sysIntInit 初始化 */
LOCAL INT_DESC intDescTable[NUM_INT_VECTORS];
4.3 第三阶段:sysTimerInit — 定时器硬件初始化
sysTimerInit() 负责初始化板级上的定时器硬件资源,包括系统时钟定时器(System Clock Timer)和辅助时钟定时器(Auxiliary Clock Timer)。此函数通常在 sysHwInit() 中被调用。
4.3.1 定时器初始化清单
| 定时器类型 | 硬件资源 | 初始化动作 | 后续用途 |
|---|---|---|---|
| 系统时钟定时器 | SoC 内部通用定时器 / PIT | 设置初始计数值、分频系数、工作模式(周期/单次) | 生成系统 Tick 中断,驱动任务调度 |
| 辅助时钟定时器 | 独立的定时器通道 / 看门狗定时器 | 配置时钟源、预分频器、比较值 | 提供高精度时间测量或辅助 Tick |
| 时间戳定时器 | CPU 内部 Cycle Counter / 全局定时器 | 使能计数器、设置初始值 | 提供 timestamp() 高精度时间戳 |
常见陷阱: 定时器的时钟源选择至关重要。如果使用了错误的时钟源(如选择了门控时钟或低频时钟),会导致系统 Tick 频率异常,进而引发任务调度超时或时间片轮转失效。务必查阅芯片参考手册,确认定时器模块的输入时钟频率。
4.4 第四阶段:usrKernelInit — 内核基础服务初始化
在 sysHwInit() 返回后,VxWorks 内核启动流程进入 usrKernelInit()。此阶段初始化内核对象(如任务控制块、信号量、消息队列)所需的内存池和数据结构。虽然不直接操作中断控制器寄存器,但会建立内核中断管理框架。
- 中断栈初始化: 为中断服务例程分配独立的栈空间,防止中断嵌套导致栈溢出。
- 中断锁机制建立: 初始化
intLock()/intUnlock()所需的全局状态变量。 - 异常处理挂钩注册: 注册默认的异常处理函数,用于捕获未连接 ISR 的中断。
4.5 第五阶段:usrRoot — 系统时钟与辅助时钟的最终连接与使能
usrRoot() 是 VxWorks 启动后的第一个根任务。在此任务中,中断与定时器初始化进入最终阶段:将定时器硬件中断连接到具体的 ISR,并启动定时器计数。
4.5.1 系统时钟连接与使能
/* usrRoot 中系统时钟初始化典型代码片段 */
STATUS sysClkConnect(
FUNCPTR routine, /* 系统时钟中断服务函数,通常为 tickAnnounce */
int arg /* 传递给 ISR 的参数 */
)
{
/* 1. 通过 intConnect() 将定时器中断向量连接到 routine */
if (intConnect(INUM_TO_IVEC(SYS_CLK_INT_VEC), routine, arg) == ERROR)
return ERROR;
/* 2. 保存 ISR 信息到全局变量 */
sysClkRoutine = routine;
sysClkArg = arg;
return OK;
}
STATUS sysClkEnable(void)
{
/* 1. 设置定时器重装载值,计算方式:时钟频率 / 系统 Tick 频率 */
UINT32 reloadVal = sysClkRateGet() / sysClkFreqGet();
/* 2. 使能定时器中断 */
sysIntEnablePIC(SYS_CLK_INT_VEC);
/* 3. 启动定时器计数器 */
TIMER_REG_WRITE(SYS_TIMER_LOAD_REG, reloadVal);
TIMER_REG_WRITE(SYS_TIMER_CTRL_REG, TIMER_ENABLE | TIMER_PERIODIC);
return OK;
}
4.5.2 辅助时钟连接与使能
辅助时钟的初始化流程与系统时钟类似,但通常用于不同的目的,如性能分析或高精度延时。
/* 辅助时钟初始化示例 */
STATUS sysAuxClkConnect(FUNCPTR routine, int arg)
{
/* 连接到不同的中断向量 */
return intConnect(INUM_TO_IVEC(SYS_AUX_CLK_INT_VEC), routine, arg);
}
STATUS sysAuxClkEnable(void)
{
/* 配置为单次模式,用于精确延时 */
TIMER_REG_WRITE(AUX_TIMER_CTRL_REG, TIMER_ENABLE | TIMER_ONE_SHOT);
return OK;
}
4.6 初始化链路总结
| 阶段 | 函数 | 中断控制器状态 | 定时器状态 | 执行环境 |
|---|---|---|---|---|
| 1 | sysHwInit | 复位、全部屏蔽 | 时钟使能、寄存器未配置 | 单任务、无缓存 |
| 2 | sysIntInit | 配置完成、接口使能 | 未启动 | 单任务、无缓存 |
| 3 | sysTimerInit | 已就绪 | 参数配置完成、停止状态 | 单任务、无缓存 |
| 4 | usrKernelInit | 内核框架建立 | 未启动 | 内核初始化 |
| 5 | usrRoot | ISR 连接完成 | 启动计数、产生中断 | 多任务环境 |
链路关键原则:
- 先屏蔽,后配置: 在中断控制器初始化完成前,确保所有中断源处于屏蔽状态。
- 先配置,后使能: 定时器必须在所有参数(时钟源、分频、模式)配置完成后,最后才使能启动。
- 连接在使能之前:
sysClkConnect()必须在sysClkEnable()之前调用,否则定时器中断触发时找不到 ISR。