3. PCIe数据链路层:DLLP包格式、ACK/NAK流控机制、数据完整性校验(LCRC)、重传机制
好,我们进入数据链路层。这一层在PCIe协议栈里,说实话,是承上启下的关键角色。它负责把事务层包(TLP)安全可靠地送到对端。我经常跟团队里的小朋友说,数据链路层就像快递公司的中转站——你包裹写得再好,中转站给你弄丢了,一切白搭。
3.1 DLLP包格式:链路层的“管理报文”
数据链路层包,简称DLLP。它不是用来传用户数据的,而是用来管理链路本身的。你可以把它理解成链路层的“握手信号”或“控制指令”。
DLLP的格式非常精简,总共只有6个字节:
| 字段 | 长度 | 说明 |
|---|---|---|
| 起始(Start) | 1字节 | 固定值,标识DLLP开始 |
| DLLP类型 | 1字节 | 指示DLLP的种类(ACK、NAK、PM等) |
| 数据载荷 | 2字节 | 根据类型不同,内容不同 |
| CRC | 2字节 | 16位CRC校验,保护整个DLLP |
常见的DLLP类型有这么几种:
- ACK DLLP:告诉发送方“我收到了,没问题”。
- NAK DLLP:告诉发送方“出错了,重发吧”。
- PM DLLP:电源管理相关的握手。
- InitFC1/InitFC2 DLLP:初始化流控参数。
我个人习惯把DLLP比作“快递回执单”。你寄了个包裹(TLP),对方收到后给你撕一张回执(ACK)。如果包裹破损,对方就给你一张“拒收单”(NAK)。
3.2 ACK/NAK流控机制:可靠传输的基石
PCIe的数据链路层使用一种“带重传的肯定确认”机制。说白了,就是发送方每发一个TLP,都等着对方回ACK。如果收到NAK,或者超时没收到ACK,就重传。
具体流程是这样的:
- 发送方给每个TLP分配一个序列号(Sequence Number)。
- 发送方把TLP存到重传缓冲区(Replay Buffer)里。
- 接收方收到TLP后,检查LCRC。如果正确,发ACK DLLP,并带上期望的下一个序列号。
- 如果LCRC错误,发NAK DLLP,要求重传。
- 发送方收到ACK后,从重传缓冲区里删除对应的TLP。
- 发送方收到NAK,或者超时未收到ACK,就从重传缓冲区里取出TLP重新发送。
嗯,这里要注意:ACK/NAK机制只保证链路层点到点的可靠传输。它不保证端到端的可靠——那是软件协议栈的事。
我曾经在一个项目中遇到过这样的问题:某个FPGA实现的PCIe端点,在压力测试下频繁丢包。查了三天,最后发现是重传缓冲区太小,导致TLP还没等到ACK就被覆盖了。嗯,从那以后,我设计重传缓冲区时都会留至少两倍的余量。
3.3 数据完整性校验(LCRC):32位CRC的威力
LCRC,全称是Link CRC,32位。它保护的是TLP从序列号到结束符的全部内容。接收方收到TLP后,会重新计算LCRC,跟收到的LCRC比对。不一致?那就说明数据在传输过程中被破坏了。
LCRC的生成多项式是固定的,PCIe规范里写得很清楚。不过,你不需要自己实现它——大多数PCIe硬核IP都帮你做好了。但作为架构师,你得知道它的计算范围:
LCRC覆盖范围:
[序列号(2字节)] + [TLP头(12或16字节)] + [数据载荷(可选)] + [ECRC(可选,4字节)]
注意,LCRC不覆盖DLLP本身。DLLP有自己的16位CRC。为什么这么设计?因为DLLP和TLP是分开处理的,它们的校验逻辑也独立。这样设计的好处是,DLLP出错不会影响TLP的重传机制,反之亦然。
3.4 重传机制:丢了就重来
重传机制是ACK/NAK的配套动作。发送方维护一个重传缓冲区,里面存着所有已发送但未确认的TLP。一旦触发重传条件,就把缓冲区里的TLP全部重新发送一遍。
重传条件有三个:
- 收到NAK DLLP:接收方明确告诉你“数据坏了”。
- ACK超时:发送方等ACK等得太久,默认对方没收到。
- 重复ACK:收到两次相同的ACK序列号,说明中间有TLP丢了。
重传的时候,发送方会把重传缓冲区里的所有TLP都发一遍。注意,不是只发一个,而是从出错的序列号开始,全部重发。为什么?因为接收方可能只收到了部分TLP,但顺序乱了。全部重发,接收方可以重新排序。
我建议你在设计重传缓冲区时,考虑以下几点:
- 缓冲区大小:至少能容纳链路延迟时间内发送的所有TLP。链路延迟包括:发送时间、传播时间、接收处理时间、ACK返回时间。
- 序列号管理:序列号是12位的,范围0-4095。注意处理回绕(wrap-around)的情况。
- 重传触发逻辑:不要只依赖NAK,超时机制也很重要。有些错误场景下,NAK可能根本发不出来。
好了,数据链路层的内容就这些。总结一下:DLLP是管理报文,ACK/NAK是可靠传输的基石,LCRC是数据完整性的守护神,重传机制是最后的兜底方案。这四个东西配合起来,才保证了PCIe链路层的高可靠性。
下一章,我们进入事务层,看看TLP到底长什么样,以及怎么用它来读写数据。