4、PCIe协议基础:分层架构(事务层、数据链路层、物理层)
各位同学,今天我们来聊聊PCIe协议最核心的东西——分层架构。说实话,我刚接触PCIe那会儿,看到这三层结构也是一头雾水。但干久了你会发现,这个分层设计真的太巧妙了。每一层各司其职,就像我们团队里的不同角色,配合好了才能高效运转。
4.1 为什么需要分层?
先问大家一个问题:为什么PCIe要搞三层?直接一股脑把数据发过去不行吗?
嗯,其实不行。你想想看,芯片内部的数据传输和外部设备通信,完全是两码事。内部总线可以随便搞,但外部要兼容各种设备、各种速率、各种电气特性。分层的好处就是——每层只关心自己的事。
我在项目中遇到过这样一个坑:有个同事想绕过数据链路层直接操作物理层,结果导致链路训练死活过不去。后来我告诉他,你这样做相当于让快递员自己造飞机,专业的事交给专业的人干不好吗?
核心思想:PCIe分层架构遵循OSI模型思路,但做了精简优化。从上到下依次是:
- 事务层(Transaction Layer)——负责生成和解析TLP(事务层包)
- 数据链路层(Data Link Layer)——负责可靠传输和链路管理
- 物理层(Physical Layer)——负责电气特性、串行化/解串行化
4.2 事务层——协议的大脑
事务层是离软件最近的一层。说白了,它就是把软件发来的读写请求,打包成标准的TLP格式。我个人习惯把事务层比作「翻译官」——把CPU的意图翻译成PCIe设备能听懂的语言。
事务层主要干这几件事:
- TLP的组装与解析——包括Header、Data、CRC等字段
- 事务排序——不同优先级的事务怎么排队
- 流量控制——防止接收方被数据淹死
- 虚拟通道管理——VC0、VC1等优先级区分
这里有个关键点:TLP的Header格式。我记得刚做PCIe验证时,经常搞混Memory Read和Memory Write的Header格式。后来我总结了一个规律:看Fmt和Type字段,这两个字节决定了整个包的结构。
我的小技巧:验证TLP时,先抓一个正常的包,对照Spec把每个字段标出来。然后故意改错一个字段,看DUT怎么反应。这样一轮下来,你对事务层的理解会深很多。
事务层还负责一个容易被忽视的功能——完成超时。你发了一个读请求,对方一直没回应怎么办?事务层会启动一个定时器,超时了就报错。我曾经调试过一个bug,就是因为完成超时时间设得太短,导致正常的大延迟设备被误判为故障。
4.3 数据链路层——可靠的搬运工
数据链路层在中间,承上启下。它的核心任务就一个:保证事务层发出的TLP能完整、有序、无差错地到达对端。
怎么保证?靠两个机制:
- ACK/NAK协议——收到正确的包回ACK,错误的回NAK要求重传
- 序列号与CRC——每个TLP都带序列号,接收方检查连续性
你可能会问:事务层不是已经有CRC了吗?为什么数据链路层还要再加一个LCRC?
嗯,这个问题问得好。事务层的CRC只保护TLP内部数据,而数据链路层的LCRC保护的是整个DLLP(数据链路层包),包括序列号。说白了,就是多一层保险。我在项目中见过一个案例:物理层偶尔会翻转一个比特,事务层CRC没抓到,但数据链路层的LCRC抓到了。你说这层重不重要?
避坑指南:我曾经在验证数据链路层时,忽略了重传缓冲区的深度配置。结果在高负载下,重传缓冲区溢出,导致链路直接挂死。记住:重传缓冲区大小必须大于最大未完成TLP数量,这个参数一定要根据你的应用场景算清楚。
数据链路层还负责链路训练和状态管理。LTSSM(链路训练和状态机)就在这一层。LTSSM有11个状态,从Detect到L0、L0s、L1、L2等等。每个状态切换都有严格的时序要求。我建议你们用逻辑分析仪抓一下链路训练过程,亲眼看看状态跳转,比看Spec一百遍都管用。
4.4 物理层——信号的搬运工
物理层是最底层,也是最「硬」的一层。它负责把数据链路层传来的并行数据,转换成串行比特流,通过差分对发出去。反过来也一样,把收到的串行信号还原成并行数据。
物理层又分为两个子层:
| 子层 | 职责 | 关键组件 |
|---|---|---|
| 逻辑子层 | 8b/10b编码(Gen1/Gen2)或128b/130b编码(Gen3+) | 扰码器、解扰器、弹性缓冲区 |
| 电气子层 | 差分信号驱动、接收均衡、时钟恢复 | SerDes、PLL、CDR |
这里有个容易混淆的概念:Lane和通道。一个Lane就是一对差分发送+一对差分接收。x1、x2、x4、x8、x16指的就是Lane的数量。我见过有人把Lane和通道混为一谈,其实通道(Channel)是物理层之上的概念,一个通道可以包含多个Lane。
物理层还有一个重要功能——链路初始化与训练。上电后,物理层先做Detect,看看对端有没有设备。然后做Polling,交换训练序列。最后进入L0状态,开始正常传输。这个过程叫「链路训练」,是PCIe设备能否正常工作的第一步。
实战经验:我在调试一个Gen3设备时,发现链路只能跑到Gen2速率。查了半天,原来是物理层的接收均衡参数没调好。Gen3用的是128b/130b编码,对信号质量要求更高。后来我调整了CTLE(连续时间线性均衡器)的增益,问题就解决了。所以做物理层验证,信号完整性测试是逃不掉的。
4.5 三层之间的协作
说了这么多,这三层到底怎么配合?我画个简单的数据流:
软件层(驱动/应用)
↓ 发起读写请求
事务层:生成TLP(添加Header、CRC)
↓ 交给数据链路层
数据链路层:添加序列号、LCRC,生成DLLP
↓ 交给物理层
物理层:编码、串行化,通过差分对发送
↓ 经过PCIe链路
对端物理层:接收、解串、解码
↓ 交给数据链路层
数据链路层:检查序列号、LCRC,回ACK
↓ 交给事务层
事务层:解析TLP,提取数据
↓ 交给软件层
你看,每一层只和相邻层打交道。事务层不知道数据是怎么串行化的,物理层也不知道数据是读请求还是写请求。这就是分层的魅力——解耦。
但解耦也带来一个问题:调试困难。当链路出问题时,你得一层层排查。我一般先看物理层:信号有没有?时钟稳不稳?再看数据链路层:ACK/NAK正常吗?序列号连续吗?最后看事务层:TLP格式对不对?完成超时了吗?
我的调试口诀:「物理看信号,链路看重传,事务看格式」。记住这12个字,排查问题能省一半时间。
4.6 小结
PCIe的分层架构,说白了就是「各扫门前雪」。事务层管协议,数据链路层管可靠,物理层管信号。每一层都做好自己的事,整个系统才能稳定运行。
最后给大家一个建议:不要只盯着Spec看。找一块PCIe开发板,用协议分析仪抓几组实际波形,对照着Spec看。你会发现,很多Spec上写的「shall」和「must」,在实际波形里一目了然。我当年就是这么入门的,效果比死磕Spec好十倍。
下一章我们讲TLP的详细格式,包括各种事务类型和Header结构。到时候我会拿几个实际项目中的例子来分析,敬请期待。