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 字节),但不同实现可能不同。切勿硬编码偏移。
EHCI 寄存器空间布局 能力寄存器区域 (固定偏移 0x00 开始) 0x00: CAPLENGTH / HCIVERSION 0x04: HCSPARAMS 0x08: HCCPARAMS CAPLENGTH 操作寄存器区域 (基地址 = MMIO_Base + CAPLENGTH) +0x00: USBCMD +0x04: USBSTS +0x08: USBINTR +0x10: FRINDEX +0x14: CTRLDSSEGMENT +0x18: FLBASEADD ★ ... +0x44 + (n-1)*4: PORTSCn ★ ★ 本章重点寄存器 FLBASEADD: 帧列表基地址 PORTSCn: 端口状态与控制

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 字段指示设备速度。
PORTSC 寄存器位域结构(32 位) PTC [31:24] WK [23:22] LS [21] PP [20] PSPD [19:16] ... [15:14] PIC [13] PO [12] PR [10] SUSP [9] FPR [8] OCC [7] PEC [6] CSC [5] PE [4] OCA [3] LS [2:1] CCS [0] 关键位域说明 ■ PR[10]: 端口复位 — 写 1 启动复位,硬件自动清零 ■ PE[4]: 端口使能 — 复位成功后自动置 1 ■ PSPD[19:16]: 端口速度 — 00=HS, 01=FS, 10=LS ■ CCS[0]: 连接状态 — 1=设备已连接 ■ CSC[5]: 连接状态变化 — 写 1 清除 ■ PEC[6]: 端口使能变化 — 写 1 清除 ■ OCC[7]: 过流变化 — 写 1 清除 ■ SUSP[9]: 挂起 — 写 1 进入挂起状态

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) {
    // 错误处理:写入失败或对齐错误
}
帧列表基地址与帧列表结构 FLBASEADD 偏移 +0x18 指向 帧列表(Frame List) 4KB 对齐,1024/512/256 帧 Frame[0]: 指向异步列表或周期性列表 Frame[1]: 指向异步列表或周期性列表 Frame[2]: 指向异步列表或周期性列表 ... 每个帧条目 32 位 位 [31:5] = 指针 位 [4] = T 位(终止) 位 [3:0] = 保留

3.5 配置顺序与注意事项

在 EHCI 初始化过程中,寄存器配置必须遵循严格的顺序:

  1. 读取 CAPLENGTH:从 MMIO 基地址偏移 0x00 获取操作寄存器基地址。
  2. 读取 HCSPARAMS:获取端口数量(N_PORTS 字段)。
  3. 读取 HCCPARAMS:获取帧列表大小(FLS 字段)及 64 位寻址能力。
  4. 分配帧列表:根据帧列表大小分配 4KB 对齐的物理内存。
  5. 配置 FLBASEADD:写入帧列表基地址。
  6. 配置 CTRLDSSEGMENT:如果使用 64 位寻址,写入高 32 位地址。
  7. 端口复位:对每个端口执行复位操作,等待使能。
  8. 设置 USBCMD:写入运行/停止位(RS=1)启动主机控制器。
重要: 在写入 FLBASEADD 之前,必须确保帧列表内存已分配且 4KB 对齐。如果使用 64 位 DMA