简介:《USB3.0协议规范中文版》以单份PDF形式呈现,共1个文件,压缩包约5.61MB,面向硬件开发者、驱动/固件工程师及需要理解USB设备内部机制的技术人员。内容围绕SuperSpeed分层通信体系展开,系统梳理协议层端到端规则、主机导向传输、连续突发与块传输流控,并讲解ITP时间戳同步、ERDY/NRDY异步通知及链路低功耗管理等关键机制。文中还覆盖USB3.0双总线HUB与主机结构、管道与端点属性、默认控制管道、Stream流式传输,并通过与USB2.0在事务组成、双工方式、轮询模式和电源管理上的对比,帮助读者建立规范级理解。已有3511人学习,适合用于产品开发、协议查阅与问题排查时对照参考。
1. 一块板子插上去,枚举正常,bulk 传输只跑到三百多 MB/s
翻手边这份 USB3.0 协议规范中文版,对照第 4 章数据流模型和第 8 章协议层,才在 SuperSpeed Endpoint Companion Descriptor 里逮到那个低级错误:bMaxBurst 填了 0,等于把超速链路当高速用,一个包一个包地等 ACK,往返延迟把带宽全吃了。这个坑我第一次做超速设备时踩得结结实实,查了三天寄存器,最后靠规范里一句“最大数据突发大小是每个端点各自的能力”才醒过来。
这篇不铺概念,就干两件事:把 USB3.0 和 USB2.0 在必须做决策的地方逐条摆开,说清哪些差异会直接影响你的固件、驱动和板子;再讲这份中文规范该按什么路径读,才不至于在 700 多页里迷路。做硬件、写驱动、选主控方案的人,看完能直接拿去对照自己的描述符和带宽预算。
2. 超速与高速怎么选:五个维度逐项对比后给结论
2.1 双总线结构决定了 HUB 和主控不能只做一半
USB3.0 不是把 USB2.0 推倒重来,而是在原有总线旁边平行长出一条超速总线。规范里写得很直白:USB3.0 HUB 在逻辑上是两个 HUB 的组合,一个 USB2.0 HUB 加一个 USB3.0 HUB,上游端口的电源和地线共享。主控侧同理,一个超速主控必须同时包含 USB3.0 部分和 USB2.0 部分,两边各自管理自己总线上的控制、状态与信息交换。
这个结构对选型的第一层影响是器件成本。你要做的是一个双总线 HUB,下行口在物理上是一对超速差分线加一对高速差分线,线缆、连接器、ESD 保护都要按两套来。很多人算 BOM 时只盯着超速那一半,结果样机做出来发现连接器选错了,只能返工。血泪经验就一句:选连接器之前先把规范的互连章节翻一遍,确认它同时支持两套触点。
第二层影响在软件。系统软件层沿袭了 USB2.0 的那套结构——设备枚举和配置、周期性和异步传输的规划、设备与功能电源管理、总线和设备管理信息,全部继承。这意味着已有的 USB2.0 驱动框架可以复用,你在枚举流程上不需要另起炉灶,真正要重写的是传输调度和链路电源管理。我做方案评估的时候,会把“能复用多少既有代码”当成一个硬指标,而不是只看峰值速率。
2.2 事务模型:从主机轮询到异步通知,改的是整个调度逻辑
这是两种协议分歧最大、也最容易在移植时翻车的地方。USB2.0 的事务固定三段式:令牌包、数据包、握手包,主机必须等这一轮走完才能发下一轮。所有包广播到总线上,每个设备自己解析地址、端点、方向,决定要不要响应。
超速把这三段的用法整个改了。OUT 事务里,原来令牌包携带的设备地址和端点信息被合进了数据包头,不需要额外令牌;IN 事务里,令牌被握手包代替——主机发一个 ACK TP 过去,既是“我要收数据”,也隐含确认了之前收到的包。更关键的是轮询没了:设备端点忙不过来时返回 NRDY(没准备好),等它缓过来主动发 ERDY 通知主机,主机再重新排事务。
我一般会这么比:如果你的应用是周期性小数据采集,比如传感器每 1ms 上报一次,USB2.0 的轮询模型其实更省心,主机按固定节奏来,时序可预测。反过来,如果是突发性大块数据,设备自己有缓存、能攒一批再发,超速的异步通知明显更划算,省掉了大量空轮询。规范里提到超速包带路由信息,HUB 决定包送到哪个设备,只有等时时间戳包(ITP)是广播的——这一点直接决定了非参与链路可以待在低功耗状态,是省电的根基。
2.3 物理层与带宽账:为什么实测永远对不上理论值
USB2.0 是半双工广播总线,一对差分线,收发不能同时。超速是 dual-simplex,收发各一对差分线,全双工、非广播,能同时跑 IN 和 OUT 事务。这个改动带来的直接好处是握手不用串行等待:设备连续发多个数据包,不必每个包都停下来等主机确认,因为应答走的是反方向那条独立通路。
下面的表是我做方案对比时固定会填的一张,字段全部来自规范正文,可以直接抄:
| 维度 | USB2.0 高速 | USB3.0 超速 |
|---|---|---|
| 物理层 | 半双工,单对差分线广播 | 双差分对,全双工,非广播 |
| 线速率 | 480 Mbps | 5 Gbps |
| 事务发起 | 主机轮询 | 主机发起 + 设备 ERDY 异步通知 |
| 令牌包 | 独立 token 包 | OUT 合入 DPH,IN 由 ACK TP 代替 |
| 突发能力 | 不支持 | 每端点 1~16 个包连续突发 |
| 流能力 | 无 | bulk 端点支持 Stream 多路复用 |
| 链路电源管理 | 主机单方面导向 | 链路两端均可独立控制 |
| 时序基准 | 固定 1 ms / 125 μs 帧 | ITP 时间戳 + 间隔调整消息 |
| 错误恢复 | 每事务端到端处理 | 端到端(数据重试)+ 链路级(头包重试) |
| 最大包长 | bulk 512 B | bulk 1024 B,控制 512 B |
带宽这笔账必须自己算一遍,不然很容易被“5Gbps”这个数字带偏。满载一个 1024 字节的数据包,线上实际要传的东西包括:头包 20 个符号(4 个 K-symbol 的起始帧、14 字节包头含 12 字节头信息加 2 字节 CRC-16、2 字节链路控制字),数据包帧 8 个 K-symbol,1024 字节数据,加 4 字节 CRC-32,合计 1056 个符号。算上线路编码开销,有效载荷占比大约 77%,5 Gbps 折下来理论载荷上限在 480 MB/s 上下。再扣掉链路层有序集、空闲符号和事务间隙,实测落在 400 MB/s 附近是正常水平。
所以当你看到某方案号称“跑到 500MB/s”,先别急着鼓掌,回头看看它的包长和突发配置是不是踩在理论边界之外。反过来,如果实测只有三百多,第一件事就是查 bMaxBurst 和 bMaxPacketSize 这两个字段,而不是怀疑 PCB 走线。
3. 协议层拆包:四类包怎么读、路由怎么走
3.1 头包 20 个符号里到底放了什么
所有头包结构一致:一个头包帧、一个包头、一个链路控制字。头包帧是由 K-symbol 组成的有序集,三个连续起始标志加一个结束标志,设计上能容忍一个符号的错误。包头 14 字节,其中 12 字节是头信息,2 字节 CRC-16 保护这 12 字节的完整性。链路控制字 2 字节收尾,总共 20 个符号长度。
头信息开头是包类型字段,接收方靠它决定后续怎么处理。大部分包还带路由字符串和三参数的设备地址(设备地址、端点号、方向)。路由字符串是给主机用的,指明这个包该走哪条拓扑路径;设备发出去的包则由集线器默认路由,集线器永远把来自下游端口的数据往上游端口送,这个过程不需要路由信息。
多字节域一律小端传输,最低有效字节先上线,最高有效字节最后。这个细节在做协议分析仪解码脚本时很要命,字节序搞反,整个字段表都是错的。
3.2 四类包的分工,别混着用
规范把包分成四类,各自的穿透范围完全不同,这是排查问题时第一件要分清楚的事:
- Link Management Packet(LMP):只穿过一对直接连接的端口,也就是链路两端,用来管链路本身,不跨设备。
- Transaction Packet(TP):穿过所有直连主机与设备的链路,用来控制流式数据包、配置设备和集线器。注意 TP 本身不带数据,本质就是一个包头。
- Data Packet(DP):同样端到端,由数据包头 DPH 加数据负载包 DPP 组成,DPP 里是数据块加 4 字节 CRC-32。
- Isochronous Timestamp Packet(ITP):主机广播到所有活动链路,用来做时间戳同步,是唯一走广播路径的包。
DPP 的帧结构也有讲究。起始帧是三个连续 SDP 标志加一个结束标志;正常结束用 DPPEND(三个 END 加一个结束标志),半途终止用 DPPABORT(三个 EDB 加一个结束标志)。规范要求 DPP 必须快速无缝地跟着对应的 DPH,中间不允许有间隙——这一条在写 FPGA 接收状态机时是硬约束,中间插一个空闲周期就会导致解析错位。
3.3 突发与流控:NRDY/ERDY 的时序怎么理解
超速事务本质上是分离式事务。同一时刻总线上可以有多个 OUT 事务在活动(不同设备),但最多只有一个 IN 事务在活动(主机只有一个)。设备对每个收到的 DP 都要回一个 ACK TP,但发送方不需要等应答就能把整个突发发完。
这个机制在固件里的实现要点是:维护一个发送顺序号,ACK TP 里记录的是出错包的顺序号,主机从这个号开始重发。所以端点必须支持 0~31 的顺序值循环,收端要能识别序号跳跃。
设备侧如果缓存不够或暂时没数据,回 NRDY;等能服务了,主动发 ERDY。主机收到 ERDY 后按传输类型的时机限制重新安排事务。常见的思路是把 NRDY/ERDY 当成一次轻量级的状态机跳转,而不是错误——它不触发重试计数,也不该被记进错误日志,否则日志会被刷爆。
下面这段脚本用来估算给定包长和突发配置下的理论载荷率,改两个参数就能对比不同配置:
# 估算超速链路在给定配置下的理论载荷率 # payload: 单个数据包的有效字节数,bulk 端点最大 1024 # burst: 每个突发包含的包数,取值范围 1~16 def ss_payload_rate(payload=1024, burst=16, line_rate_gbps=5.0): # 头包 20 符号 + DPP 帧 8 符号 + CRC-32 4 符号 overhead_per_packet = 20 + 8 + 4 total_symbols = burst * (payload + overhead_per_packet) useful_bits = burst * payload * 8 # 8b/10b 线路编码,每个符号占 10 bit 时间 line_bits = total_symbols * 10 efficiency = useful_bits / line_bits return efficiency, line_rate_gbps * 1000 / 8 * efficiency # 单位 MB/s for b in (1, 4, 8, 16): eff, mbps = ss_payload_rate(burst=b) print(f"burst={b:>2} 载荷率={eff:.3%} 理论上限={mbps:.1f} MB/s")这段代码把三个开销项单独列出来,方便你按自己的包长重新算。payload 改成 512 会看到载荷率明显下滑——包越小,固定开销占比越高,这也是为什么 bulk 端点应该尽量跑满 1024 字节。line_rate_gbps 默认 5.0,如果你的方案跑的是别的速率,改这一个数就行。burst 那一列能直观看出:从 1 调到 16,理论收益接近翻倍,这就是 bMaxBurst 不能填 0 的原因。
4. 四种传输类型选型:参数表比经验更靠谱
4.1 最大包长与突发大小的硬约束
四种传输类型——控制、块、中断、同步,用途和 USB2.0 一致,但参数边界全部重画了。这张表建议直接贴在设计文档里:
| 传输类型 | 最大包长 | 最大突发 | 每服务周期事务数 | 失败是否重传 |
|---|---|---|---|---|
| 控制 control | 512 B(固定) | 1,不支持突发 | 按调度 | 是 |
| 块 bulk | 1024 B | 1~16 | 按可用带宽 | 是 |
| 中断 interrupt | 突发=1 时 1~1024 B;突发>1 时仅 1024 B | 最大 3 | 1 次尝试 | 下一服务周期重试 |
| 同步 isochronous | 突发=1 时 0~1024 B;突发>1 时仅 1024 B | 最大 16 | 最多 3 次突发事务 | 不重传 |
控制端点的规则最死:最大包长固定 512 字节,最大突发为 1,也就是不支持突发。设备描述符里的 bMaxPacketSize 必须填 09H,解码出来就是 2 的 9 次方。默认控制管道必须支持 0~31 的顺序值。
块端点的最大包长 1024 字节,突发 1~16,必须支持 0~31 顺序值。主机有义务支持任何块端点和所有突发大小组合,同时要保证发出去的包不超过描述符里声明的最大包长和最大突发。
中断端点有个容易看漏的细节:突发次数大于 1 时,最大包长只能是 1024 字节,不能是别的值;突发次数等于 1 时才能取 1~1024 的任意值。而且中断端点允许的最大突发是 3,不是 16。同步端点反过来,最大突发 16,但同一服务周期内可以请求 3 次突发事务,所以单周期上限是 3×16×1024 字节。
4.2 带宽分配:80% 这条线怎么用
周期端点(中断和同步)最多能拿走可用超速总线带宽的 80%,剩下的留给块传输和控制传输去竞争。这个 80% 是规划带宽预算时的第一道闸门。
服务周期由端点描述符里的 bInterval 决定,公式是 2 的 (bInterval-1) 次方乘以 125 微秒,bInterval 取值范围 1~16。系统软件在配置阶段用这个信息判断端点能不能被安排,实际给到的周期可能比设备想要的更短,最短就是总线轮询周期 125 微秒。
块传输没有指定带宽的途径,只能在总线上有可用带宽时发生。控制传输对总线操作的优先级高于块传输。当多个端点的块传输挂起时,主机按公平访问原则分配机会,而且会在 1 到最大突发之间动态调整每次事务的突发大小——挂起的块流越多、可用带宽越少,主机就越倾向于用更小的突发,把时间切碎了分给所有人。
这里有个实际决策点:如果你的设备既要周期上报又要传大文件,别指望块传输能拿到稳定速率。规范明确写了端点和客户软件不能为块传输假定一个指定的服务率,总线时间会因为其他设备插拔、其他功能端点需求而变化,甚至在个别情形下块传输能跑到控制传输前面。我在方案评审时会把这条念给产品经理听,避免他们把“最高 400MB/s”写进规格书。
4.3 Stream 什么时候值得用
块传输在超速里扩出了一个叫 Stream 的能力:协议级支持在同一个标准块传输管道里多路复用多个独立逻辑数据流。这是 USB2.0 完全没有的东西。
什么时候值得开?当你有多个逻辑上独立的数据通道,但不想为每个通道各占一个端点的时候。比如一个采集设备同时传图像帧和元数据,传统做法是两个 bulk 端点,各占一份带宽预算和描述符资源;用 Stream 可以合到一个端点里,靠流 ID 区分。
代价是复杂度和兼容性。Stream 需要额外的描述符配置,主机侧驱动要处理流 ID 的分配和回收,还得考虑对端不支持时的降级路径。我的判断标准很简单:通道数少于 3 个、且各通道带宽需求差异不大时,老老实实用多个端点,省事;通道数多、或者通道是动态创建销毁的,再考虑 Stream。
4.4 中断与同步的时序约束差异
中断传输的定位是“带有轮询服务间隔需要高可靠性传输小数量数据的设备”。每个服务周期会尝试一次传输,成功之后本周期内不再尝试;如果端点回 NRDY 或者说不能接收,主机本次服务就不再试,等到收到 ERDY 通知后,必须在 2 个服务周期内把端点服务掉。周期内发生错误,主机不被要求在同一周期重试,只在下一个周期重试。
同步传输的定位是“能容忍错误、周期性轮询的传输流”。它保证带宽和延迟范围,但规范写得很清楚:同步事务中的包没有应答,主机没有方法知道包没成功传输,因此不会重试。同步端点总是在轮询周期里从顺序号 0 开始发包,之后依次递增,到 31 后回绕到 0。它不支持重试,也不能应答流控。
这两种时序约束直接决定固件怎么写。中断端点要能响应 ERDY 的时机要求,同步端点则必须自己扛住丢包——应用层得有一套丢帧补偿逻辑,别指望链路层给你兜底。
5. 避坑清单:五个让超速跑不出超速的配置错误
5.1 bMaxBurst 填 0,超速当高速用
现象:枚举正常,驱动加载正常,块传输吞吐稳定卡在三百多 MB/s,怎么优化 DMA 都没用。
原因:SuperSpeed Endpoint Companion Descriptor 里的 bMaxBurst 字段填了 0,等于告诉主机“我这个端点不支持突发”。主机于是退回一次一个包的保守调度,每个包之间都要等应答,全双工的优势完全没发挥出来。
解决:按端点的实际缓存能力填 1~16。填完之后重新算一遍带宽预算,确认主控那边的调度器能撑住这个突发量。填大不是免费的,突发越大对端点的缓冲和主控的仲裁要求越高。
5.2 把 USB2.0 的“等 ACK 再发下一个”照搬过来
现象:移植完 USB2.0 的传输逻辑,功能都对,吞吐只有预期的一半左右。
原因:USB2.0 事务必须走完令牌、数据、握手三段,主机在开始下一次事务前要完成这一次总线处理。超速是分离式事务,发送方不用等应答就能把整个突发发完,应答走独立的接收通路。照搬旧逻辑等于主动放弃了流水线。
解决:发送侧维护发送顺序号并把整个突发一次性推出去,接收侧的 ACK TP 里带的是出错包序号,从那个号开始重发就行。顺序值必须覆盖 0~31,收端要能识别跳跃。
5.3 同步端点按块传输的方式做重传
现象:同步传输偶尔出现画面撕裂或音频爆音,加了重传逻辑后反而更乱,顺序号对不上。
原因:同步端点不支持重试,不应答流控,主机也无从知道包丢了。在端点侧自作主张重发,会打乱 0~31 的顺序循环,接收端按序号重组时直接错位。
解决:链路层不做任何补偿,把丢包当成正常事件往上抛,由应用层按时间戳做补偿或者丢弃。同时利用好 PING / PING_RESPONSE 机制——同步传输要穿过非活动链路时,电源管理会拖慢它,主机可以先发 PING,设备回 PING_RESPONSE 确认所有链路都在活动状态,再开始传输。
5.4 给控制端点配了突发
现象:设备在某个主机上能枚举,换一个主机就失败,或者描述符读取阶段直接超时。
原因:控制端点的最大突发固定为 1,不支持突发。描述符里给它写了大于 1 的值,属于非法配置,不同主控的校验严格程度不一样,表现就不一致。
解决:控制端点老老实实 512 字节包长、突发 1。默认控制管道必须支持 0~31 顺序值,这一点不能省。
5.5 链路电源管理没考虑异步通知
现象:空闲一段时间后第一次传输延迟特别大,偶发超时;持续传输反而一切正常。
原因:超速的链路两端都能独立进入低功耗状态,不参与数据通讯的链路会待机。包送到处于低功耗状态的端口时,端口要切到退出状态并上报切换事件,这个切换是有代价的。如果驱动没处理这个延迟,第一次访问就会撞上超时。
解决:把链路状态机(U0 到 U1/U2/U3 那一套)的进入和退出路径单独测一遍,特别是空闲后首次传输的场景。利用好 Latency Tolerance 消息告诉主机设备能容忍多长的延迟,以及间隔调整消息微调 125 微秒的服务节拍,别让主机按最保守的参数调度。
6. 把中文规范当工具书:三步定位法加一张速查表
这份规范 700 多页,从头读到尾是最低效的用法。我现在的习惯是把它当字典,按问题类型分三条路径进去。
第一条路径查时序和传输行为,直接翻第 4 章数据流模型。端点属性、管道类型、四种传输的参数边界、NRDY/ERDY 的交互,全在这一章里,而且写的是行为描述不是寄存器定义,读起来最顺。遇到“这个场景主机应该怎么反应”这类问题,八成能在这里找到答案。
第二条路径查包格式和字段,翻第 8 章协议层和第 7 章链路层。头包的 20 个符号怎么排、DPP 的三种帧序列集、CRC 覆盖范围、字节序,都在这两章。做协议分析仪解码或者写 FPGA 接收状态机时,这两章要摊在桌上。
第三条路径查描述符和标准命令,翻第 9 章。设备描述符、配置描述符、端点描述符、SuperSpeed Endpoint Companion Descriptor 的字段定义和取值约束都在这里。我一般会先看字段表,再回头看正文里的约束说明,因为表格里不会写“为什么不能这么填”。
| 你遇到的问题 | 先翻哪章 | 关键看什么 |
|---|---|---|
| 吞吐上不去 | 第 4 章 | 突发大小、包长、流控交互 |
| 枚举失败 | 第 9 章 | 描述符字段取值约束 |
| 包解析错位 | 第 7、8 章 | 帧序列集、字节序、CRC 范围 |
| 空闲后首次传输超时 | 第 4 章 | 链路电源状态切换、延迟容忍 |
| 同步传输丢包 | 第 4 章 | 无应答机制、PING 流程 |
| 多路数据复用 | 第 4 章 | Stream 配置与端点属性 |
一个具体技巧:中文版的价值不在翻译本身,而在术语对照。英文规范里 endpoint、pipe、transfer、transaction 这四个词用得非常严格,中文版对应成端点、管道、传输、事务,读的时候盯着这四个词,能快速判断一句话讲的是哪一层。很多理解偏差就出在把 transaction 当成 transfer,把管道当成端点。
回过头看,我最早的错误是把规范当教材读,从头啃了三周,记住的全是零散概念,真上手还是懵。从那以后我每次遇到新协议文档,都强制走一遍“先按问题定位章节、再回头补上下文”的路径,先解决手头这个具体问题,再顺着引用往外扩。这份 USB3.0 协议规范中文版尤其适合这么用,章节编号和英文原版对得上,查到中文之后想深挖还能对照原文。希望帮到你。
本文还有配套的精品资源,点击获取