3. UHCI 主机控制器架构:寄存器组、端口状态机、帧列表与队列头

UHCI(Universal Host Controller Interface)是 Intel 定义的 USB 1.x 全速/低速主机控制器标准。其核心架构围绕四个关键模块构建:主机控制器寄存器组、端口状态机、帧列表(Frame List)与队列头(Queue Head)。理解这些模块的协同机制,是实现嵌入式调试端口与 DMA 传输的基础。

架构要点:UHCI 将大部分调度逻辑交给软件(驱动),硬件只负责解析帧列表、执行队列头中的传输描述符(TD),并通过寄存器暴露状态。这种设计降低了硬件复杂度,但要求软件严格遵循时序。

3.1 主机控制器寄存器组

UHCI 主机控制器通过一组 PCI 配置空间寄存器与 I/O 映射寄存器暴露控制与状态信息。软件通过读写这些寄存器完成初始化、调度启停、中断管理及端口控制。

寄存器名称偏移地址宽度功能描述
USBCMD0x0016-bit运行/停止(RS)、全局复位(GR)、帧列表大小(FLSIZE)等控制位
USBSTS0x0216-bit主机控制器错误(HCHalted)、中断挂起(INT)、DMA 错误等状态位
USBINTR0x0416-bit中断使能:传输完成、短包、错误等中断源屏蔽
FRNUM0x0616-bit当前帧号(Frame Number),每 1 ms 递增,用于帧列表索引
FLBASEADD0x0832-bit帧列表基地址(4 KB 对齐),指向物理内存中的帧列表数组
SOF0x0C16-bit帧起始(Start of Frame)调节值,控制 SOF 包发送时序
PORTSC[1:2]0x10, 0x1216-bit端口状态与控制寄存器,每个端口一个
调试端口视角:在嵌入式环境中,USBSTS 的 HCHalted 位用于判断主机控制器是否进入停止状态;FLBASEADD 必须指向 DMA 可访问的内存区域,否则帧列表无法被硬件正确读取。

3.2 端口状态机

每个 UHCI 端口对应一个 PORTSC 寄存器,其内部状态机管理 USB 设备的连接、断开、复位、挂起与唤醒。状态机由硬件自动迁移,软件通过写控制位触发状态转换。

UHCI 端口状态机迁移图 DISCONNECTED CONNECTED RESET ENABLED SUSPEND 连接检测 端口复位 复位完成 使能 挂起 唤醒 断开 → 回到 DISCONNECTED 说明:状态迁移由硬件自动完成,但复位和挂起需要软件通过 PORTSC 寄存器触发。 调试端口通常使用 ENABLED 状态进行数据传输,SUSPEND 用于低功耗调试场景。
调试陷阱:在端口状态机处于 RESET 或 SUSPEND 时,DMA 传输请求会被主机控制器忽略。软件必须在启动 DMA 前确认 PORTSC 的 Port Enabled/Disabled 位为 1。

3.3 帧列表(Frame List)

帧列表是 UHCI 调度的核心数据结构,位于系统内存中,由 FLBASEADD 寄存器指向。它是一个包含 1024 个表项的数组(帧列表大小可通过 USBCMD.FLSIZE 调整为 256 或 512),每个表项是一个 32 位的帧指针。

  • 帧指针格式:位 [31:4] 指向一个队列头或传输描述符的物理地址(16 字节对齐),位 [0] 为终止位(Terminate),位 [1] 为 QH/TD 选择位。
  • 调度周期:主机控制器每 1 ms 递增 FRNUM,以 FRNUM 的低 10 位(或 8/9 位)索引帧列表,取出帧指针开始执行。
  • DMA 协同:帧列表本身必须位于 DMA 可访问的内存区域,且不能被缓存(Cache)——嵌入式系统中需使用非缓存内存区域或刷新 Cache。
帧列表与帧指针结构 Frame[0] Frame[1] Frame[2] ... Frame[1023] 32-bit 帧指针格式 位 [31:4] : 物理地址 (16B 对齐) 位 [3:2] : 保留 (必须为 0) 位 [1] : 0=QH, 1=TD 位 [0] : 终止位 (1=列表结束) 示例: 0x12345002 → 地址 0x12345000, QH, 非终止 关键约束:帧列表必须位于物理内存中 4 KB 对齐的地址;每个帧指针指向的 QH/TD 也必须位于 DMA 可访问区域。嵌入式系统中建议使用 非缓存(Non-cacheable) 内存区域存放帧列表。

3.4 队列头(Queue Head)

队列头是 UHCI 传输调度的基本单元,每个 QH 对应一个 USB 端点(Endpoint)。QH 位于系统内存中,由帧指针或上一个 QH 的链接指针指向。QH 的主要字段包括:

字段偏移宽度描述
Horizontal Link Pointer0x0032-bit指向下一个 QH 或 TD,格式与帧指针相同
Endpoint Capabilities0x0432-bit设备地址、端点号、数据速率(FS/LS)、最大包长等
Current Link Pointer0x0832-bit指向当前正在处理的 TD(硬件维护,软件初始化时置为 QH 首 TD)
Reserved / Overlay0x0C32-bit保留或用于 TD 覆盖(短包检测)
DMA 传输协同:QH 中的 Horizontal Link Pointer 形成单向链表,主机控制器通过 DMA 读取 QH 和 TD。当 QH 的 Current Link Pointer 指向一个 TD 时,硬件自动发起 DMA 传输,将数据从内存搬运到 USB 总线(或反向)。传输完成后,硬件更新 TD 的状态字段,并移动到下一个 TD。
队列头链表与 TD 执行流程 QH (端点 1) HLP → QH2 Curr → TD1 DevAddr=0x02 MaxPkt=64 QH (端点 2) HLP → QH3 Curr → TD3 DevAddr=0x03 MaxPkt=32 QH (端点 3) HLP → Terminate Curr → TD5 DevAddr=0x04 MaxPkt=16 TD1 (IN) TD2 (OUT) TD3 (IN) TD4 (IN) TD5 (OUT) 执行顺序:主机控制器从帧列表取出 QH1,处理其 TD 链表(TD1→TD2),然后通过 HLP 跳转到 QH2, 处理 TD3→TD4,最后到 QH3 处理 TD5。每个 TD 的 DMA 传输完成后,硬件更新状态并自动推进。

3.5 关键协同机制:帧列表 ↔ QH ↔ TD ↔ DMA

UHCI 的整个传输调度是一个从帧列表到 QH 再到 TD 的逐级解析过程,DMA 贯穿始终:

  1. 帧列表索引:每 1 ms 硬件以 FRNUM 为索引,从帧列表中读取一个帧指针。
  2. QH 遍历:帧指针指向一个 QH,硬件通过 QH 的 Horizontal Link Pointer 遍历该帧的所有 QH。
  3. TD 执行:在每个 QH 内部,硬件通过 Current Link Pointer 找到第一个 TD,发起 DMA 读/写操作。
  4. DMA 完成:TD 中的缓冲区地址指向系统内存,硬件通过 DMA 引擎直接传输数据,完成后更新 TD 状态。
  5. 中断触发:如果 TD 设置了中断使能位,传输完成后 USBINTR 寄存器会触发中断,通知软件处理。
嵌入式调试端口优化:将调试端口的 QH 放在帧列表的前几个表项中,可以降低调试数据的传输延迟。同时,使用 DMA 批量传输(多个 TD 链接)可以减少 CPU 干预,适合调试日志的高吞吐场景。