3. 初始化阶段总览:UHCI初始化流程、关键寄存器配置、DMA准备步骤

UHCI 控制器的初始化是 USB 子系统正常工作的前提,也是后续 DMA 传输能够正确执行的基石。本节从整体流程出发,梳理初始化阶段的关键步骤、寄存器配置要点以及 DMA 引擎的准备条件,帮助你在实战中建立清晰的初始化全景图。

3.1 UHCI 初始化完整流程

UHCI 初始化遵循“主机控制器 → 根集线器 → 帧列表 → 传输描述符”的递进顺序。以下流程图展示了从系统上电到 DMA 就绪的完整路径:

UHCI 初始化完整流程 UHCI 初始化完整流程 系统上电 / 复位 配置 USBCMD 复位位 设置 FLBASEADD 配置 SOF 帧长度 使能根集线器端口 DMA 引擎就绪 注意:每一步都必须检查寄存器状态位,确保前一步完成后再进入下一步

3.2 关键寄存器配置详解

UHCI 初始化阶段涉及的核心寄存器集中在 I/O 空间基址偏移处。下表列出必须配置的寄存器及其功能:

偏移地址 寄存器名称 位宽 初始化值 关键位说明
0x00 USBCMD 16-bit 0x0000 Bit0=1 运行/停止;Bit1=1 全局复位;Bit2=1 全局挂起
0x02 USBSTS 16-bit 0x0020 Bit5 HCHalted 只读,指示控制器是否停止
0x04 USBINTR 16-bit 0x0000 中断使能位,初始化阶段通常全部清零
0x06 FRNUM 16-bit 0x0000 当前帧号,由硬件自动递增
0x08 FLBASEADD 32-bit 对齐到 4KB 帧列表基址,必须指向 4KB 对齐的物理内存
0x0C SOFMOD 8-bit 0x40 SOF 帧长度调整,典型值 0x40(64 个 bit time)
0x10 PORTSC1/2 16-bit 0x0080 Bit7 端口使能;Bit0 端口连接状态检测

初始化顺序要点:

  1. 先通过 USBCMD 的 Bit1 发起全局复位,等待 USBSTS 的 Bit5 变为 1(HCHalted)。
  2. 清除复位位后,设置 FLBASEADD 为帧列表的物理基址。
  3. 配置 SOFMOD 为 0x40,设定 1ms 帧周期。
  4. 最后设置 USBCMD 的 Bit0 为 1,启动控制器运行。

3.3 DMA 准备步骤

DMA 传输的准备工作在 UHCI 控制器进入运行状态后进行。核心任务包括帧列表初始化、传输描述符(TD)队列构建以及链表指针的正确链接。

3.3.1 帧列表初始化

帧列表是 1024 个 32 位指针的数组,每个指针指向一个 TD 或 QH。初始化时将所有帧列表项指向一个空的同步 TD(SKIP 位=1),表示该帧无传输任务。

// 帧列表初始化示例(C 伪代码)
#define FRAME_LIST_SIZE 1024
uint32_t *frame_list = (uint32_t *)FRAME_LIST_BASE;

for (int i = 0; i < FRAME_LIST_SIZE; i++) {
    // 设置 Terminate 位(Bit0=1),表示链表结束
    frame_list[i] = 0x00000001;
}

3.3.2 传输描述符(TD)结构准备

每个 TD 占用 32 字节,包含链接指针、控制状态、令牌和缓冲区指针。DMA 引擎通过 TD 链表遍历传输任务。以下表格列出 TD 各字段的偏移和用途:

偏移 字段 位宽 描述
0x00 Link Pointer 32-bit 指向下一个 TD/QH,Bit0=0 表示 TD,Bit0=1 表示 QH
0x04 Control/Status 32-bit 包含完成状态、错误计数、SPD、IOC 等控制位
0x08 Token 32-bit 包含端点号、设备地址、PID(IN/OUT/SETUP)和数据长度
0x0C Buffer Pointer 32-bit 数据缓冲区物理地址,必须 4 字节对齐

3.3.3 链表构建与 DMA 启动

DMA 传输依赖链表结构。帧列表项指向第一个 TD,TD 的 Link Pointer 指向下一个 TD,形成单向链表。最后一个 TD 的 Link Pointer 必须设置 Terminate 位(Bit0=1)。

UHCI DMA 链表结构 UHCI DMA 链表结构 帧列表 [0] → TD1 [1] → TD4 [2] → SKIP ... [1023]→ SKIP TD1 Link → TD2 Buffer → Data1 TD2 Link → TD3 Buffer → Data2 TD3 Link → Terminate Buffer → Data3 DMA 引擎从帧列表当前帧号索引处读取指针,沿链表依次处理每个 TD 直到遇到 Terminate 位或到达帧末尾,等待下一帧 SOF 重新开始

实战建议:在初始化阶段,建议预先分配 3~5 个 TD 作为测试链表,验证 DMA 引擎能否正确遍历。使用逻辑分析仪抓取 PCI 总线上的内存读取事务,确认帧列表和 TD 的物理地址是否正确。

3.4 初始化阶段常见问题与避坑

  • 帧列表未 4KB 对齐:FLBASEADD 的低 12 位必须为 0,否则 DMA 引擎会读取错误的帧列表项。
  • TD 缓冲区未对齐:Buffer Pointer 必须 4 字节对齐,否则 UHCI 会忽略低 2 位,导致数据错位。
  • 遗漏 Terminate 位:链表末尾的 TD 必须设置 Bit0=1,否则 DMA 引擎会无限遍历非法内存地址。
  • 未等待 HCHalted 确认:复位后立即配置寄存器可能导致写入无效,必须轮询 USBSTS 的 Bit5 确认控制器已停止。

关键警告:不要在 UHCI 运行状态下修改 FLBASEADD 或帧列表内容。必须先通过 USBCMD 停止控制器(Bit0=0),等待 HCHalted 置位后再修改,否则会导致 DMA 引擎读取不一致的帧列表,引发系统崩溃。

3.5 初始化状态检查清单

以下清单可用于初始化完成后的快速验证:

检查项 预期值 验证方法
USBCMD 运行位 Bit0=1 读取 USBCMD 寄存器
USBSTS HCHalted Bit5=0 读取 USBSTS 寄存器
FLBASEADD 对齐 低 12 位为 0 检查物理地址
帧列表项 Terminate Bit0=1 读取帧列表内存
TD 链表完整性 末尾 Terminate 遍历 TD 链表
端口使能状态 PORTSC Bit7=1 读取 PORTSC 寄存器