3、EHCI寄存器架构:操作寄存器基地址、端口状态与控制寄存器、帧列表基地址寄存器
EHCI(Enhanced Host Controller Interface)寄存器的正确访问是批量传输配置的基础。本章聚焦三个核心寄存器组:操作寄存器基地址(CAPLENGTH + OPERATIONAL Base)、端口状态与控制寄存器(PORTSC)以及帧列表基地址寄存器(FLBASEADD)。理解它们的偏移、位域含义及配置顺序,是后续初始化与调度批量异步列表的前提。
关键概念: EHCI 寄存器空间分为两部分:能力寄存器(Capability Registers)和操作寄存器(Operational Registers)。能力寄存器固定从 MMIO 基地址偏移 0x00 开始,而操作寄存器的起始地址由能力寄存器中的 CAPLENGTH 字段决定。
3.1 寄存器空间布局总览
EHCI 的 MMIO 空间布局如下表所示。所有寄存器均为 32 位宽,小端序访问。
| 偏移地址 | 寄存器名称 | 所属区域 | 说明 |
|---|---|---|---|
| 0x00 | CAPLENGTH / HCIVERSION | 能力寄存器 | 低 8 位为 CAPLENGTH,高 16 位为 HCIVERSION |
| 0x04 | HCSPARAMS | 能力寄存器 | 结构参数,包含端口数量等信息 |
| 0x08 | HCCPARAMS | 能力寄存器 | 能力参数,包含帧列表大小等 |
| CAPLENGTH + 0x00 | USBCMD | 操作寄存器 | USB 命令寄存器,启动/停止主机控制器 |
| CAPLENGTH + 0x04 | USBSTS | 操作寄存器 | USB 状态寄存器 |
| CAPLENGTH + 0x08 | USBINTR | 操作寄存器 | 中断使能寄存器 |
| CAPLENGTH + 0x10 | FRINDEX | 操作寄存器 | 帧索引计数器 |
| CAPLENGTH + 0x14 | CTRLDSSEGMENT | 操作寄存器 | 控制数据结构段(64 位地址高 32 位) |
| CAPLENGTH + 0x18 | FLBASEADD | 操作寄存器 | 帧列表基地址 |
| CAPLENGTH + 0x44 + (n-1)*4 | PORTSCn | 操作寄存器 | 端口 n 状态与控制寄存器 |
编程要点: 在访问任何操作寄存器之前,必须先读取能力寄存器偏移 0x00 处的 CAPLENGTH 值。该值通常为 0x20(32 字节),但不同实现可能不同。切勿硬编码偏移。
3.2 操作寄存器基地址(CAPLENGTH)
操作寄存器基地址不是直接由一个寄存器存储,而是通过能力寄存器中的 CAPLENGTH 字段计算得出:
// 读取 CAPLENGTH(能力寄存器偏移 0x00,低 8 位)
uint8_t cap_length = *(volatile uint8_t*)(mmio_base + 0x00);
// 操作寄存器基地址
uint32_t op_base = mmio_base + cap_length;
// 示例:访问 USBCMD 寄存器
volatile uint32_t *usbcmd = (volatile uint32_t*)(op_base + 0x00);
常见错误: 部分开发者误以为操作寄存器基地址固定为 mmio_base + 0x10 或 mmio_base + 0x20。实际上,CAPLENGTH 的值由硬件设计决定,必须动态读取。在 x86 平台上,CAPLENGTH 通常为 0x20,但嵌入式 SoC 可能不同。
3.3 端口状态与控制寄存器(PORTSC)
每个 USB 端口对应一个 PORTSC 寄存器,偏移计算公式为:
// 端口 n 的 PORTSC 偏移(n 从 1 开始)
uint32_t portsc_offset = 0x44 + (n - 1) * 4;
volatile uint32_t *portsc = (volatile uint32_t*)(op_base + portsc_offset);
PORTSC 寄存器的关键位域如下:
| 位域 | 名称 | 类型 | 说明 |
|---|---|---|---|
| [31:24] | PTC | R/W | 端口测试控制,一般设为 0 |
| [23:22] | WKOC/E/F | R/W | 唤醒使能位 |
| [21] | PHY_LS | RO | 物理层线状态 |
| [20] | PP | R/W | 端口电源控制 |
| [19:16] | PSPD | RO | 端口速度:00=HS, 01=FS, 10=LS, 11=未用 |
| [13] | PIC | R/W | 端口指示灯控制 |
| [12] | PO | R/W | 端口过流检测 |
| [10] | PR | R/W | 端口复位(写 1 开始复位,硬件自动清零) |
| [9] | SUSP | R/W | 端口挂起 |
| [8] | FPR | R/W | 强制端口恢复 |
| [7] | OCC | R/WC | 过流变化(写 1 清除) |
| [6] | PEC | R/WC | 端口使能变化(写 1 清除) |
| [5] | CSC | R/WC | 连接状态变化(写 1 清除) |
| [4] | PE | R/W | 端口使能(1=使能,0=禁用) |
| [3] | OCA | RO | 过流状态 |
| [2:1] | LS | RO | 线状态:00=SE0, 01=K, 10=J, 11=未用 |
| [0] | CCS | RO | 当前连接状态(1=设备已连接) |
端口复位流程: 写 PORTSC.PR=1 → 等待至少 50ms → 轮询 PORTSC.PR 直到硬件自动清零 → 检查 PORTSC.PE 是否置位。复位成功后,端口进入使能状态,PSPD 字段指示设备速度。
3.4 帧列表基地址寄存器(FLBASEADD)
FLBASEADD 寄存器位于操作寄存器偏移 +0x18,用于存储帧列表(Frame List)的物理基地址。帧列表是 EHCI 调度异步和周期性传输的核心数据结构。
| 位域 | 名称 | 类型 | 说明 |
|---|---|---|---|
| [31:12] | BASE | R/W | 帧列表基地址的高 20 位(4KB 对齐) |
| [11:0] | Reserved | RO | 保留位,必须写 0 |
对齐要求: FLBASEADD 必须 4KB 对齐(即低 12 位为 0)。帧列表大小由 HCCPARAMS 寄存器的 [15:8] 位域(Frame List Size)决定:0=1024 帧,1=512 帧,2=256 帧。默认通常为 1024 帧。
帧列表基地址的配置示例:
// 假设帧列表已分配在 4KB 对齐的物理地址 0x10000000
uint32_t frame_list_base = 0x10000000;
// 写入 FLBASEADD 寄存器
volatile uint32_t *flbaseadd = (volatile uint32_t*)(op_base + 0x18);
*flbaseadd = frame_list_base; // 低 12 位必须为 0
// 验证写入
if (*flbaseadd != frame_list_base) {
// 错误处理:写入失败或对齐错误
}
3.5 配置顺序与注意事项
在 EHCI 初始化过程中,寄存器配置必须遵循严格的顺序:
- 读取 CAPLENGTH:从 MMIO 基地址偏移 0x00 获取操作寄存器基地址。
- 读取 HCSPARAMS:获取端口数量(N_PORTS 字段)。
- 读取 HCCPARAMS:获取帧列表大小(FLS 字段)及 64 位寻址能力。
- 分配帧列表:根据帧列表大小分配 4KB 对齐的物理内存。
- 配置 FLBASEADD:写入帧列表基地址。
- 配置 CTRLDSSEGMENT:如果使用 64 位寻址,写入高 32 位地址。
- 端口复位:对每个端口执行复位操作,等待使能。
- 设置 USBCMD:写入运行/停止位(RS=1)启动主机控制器。
重要: 在写入 FLBASEADD 之前,必须确保帧列表内存已分配且 4KB 对齐。如果使用 64 位 DMA