第2章 CXL协议栈详解:事务层、链路层、物理层

做CXL调试,第一件事就是搞清楚这协议到底分几层。很多人一上来就对着波形看,结果事务层和链路层的东西混在一起,越查越乱。我自己的习惯是,先把三层边界划清楚,再逐层下钻。今天咱们就把事务层、链路层、物理层挨个拆开聊。

2.1 三层架构整体认知

CXL协议栈从顶到底分为三层:事务层、链路层、物理层。说白了,顶层管“做什么”,中间层管“怎么传”,底层管“怎么发”。

举个例子:主机想读一块加速器上的数据。事务层生成读请求报文,链路层把它切成FLIT(流控制单元)并加校验,物理层再把FLIT转成高速串行信号打出去。

平时调试遇到最多的问题,就是报文丢在链路层重传里,但分析工具把错误定位到了事务层。嗯,这里要注意——三层之间是有明确接口的,链路层对事务层是透明的,但链路层的重传行为会影响事务层的时延和顺序。

层次核心职责主要数据单元调试关注点
事务层生成/解析CXl.io、CXL.cache、CXL.mem协议事务事务层报文(TLP)报文类型、地址、Completion状态
链路层FLIT封装、CRC校验、重传、链路流控FLIT序列号、ACK/NACK、重传次数
物理层串行化/解串、PCS编码、链路训练逻辑子块(PMA/PCS)BER、眼图、LTSSM状态

我之前带过一个项目,加速器老是丢读响应。我花了三天三夜追事务层报文,全对。最后物理层眼图一测,信号质量太差导致链路层CRC出错,重传把顺序打乱了。从那以后,我每个项目必须三层分别做覆盖率分析。

三层调试黄金法则:先物理层确认链路稳定,再链路层检查重传情况,最后事务层验证功能正确。跳过任意一层都可能误判根因。
CXL协议栈三层架构 事务层 (Transaction Layer) CXL.io | CXL.cache | CXL.mem TLP封装:Request / Response / Data 典型问题:地址越界、Field误置、DVM消息乱序 链路层 (Link Layer) FLIT生成:Sequence ID + CRC + 重传控制 流控:Credit管理 / 链路状态协商 典型问题:FLIT CRC错导致重传风暴、Credit死锁 物理层 (Physical Layer) PMA:SerDes / 时钟恢复 / 均衡 PCS:128b/130b编码 / 扰码 / 块同步 典型问题:LTSSM卡在Recovery、误码率超标 每一层对上层透明,但出错时下层症状会向上层暴露

2.2 事务层详解

事务层是CXL协议栈中最“像PCIe”的一层。它负责三种协议:CXL.io(配置和I/O)、CXL.cache(缓存一致性)、CXL.mem(内存访问)。

2.2.1 CXL.io 沿用PCIe的TLP格式,主要用于设备枚举、MMIO、MSI中断等。调试时我一般先看有没有合规的Configuration Request,如果设备没响应,多半是RC侧的地址解码不对。

2.2.2 CXL.cache 是CXL的亮点——主机和设备共享缓存行。事务层定义了Snoop、Non-Snoop、Clean、Dirty等状态。我记得第一次调试缓存一致性,设备发了个Clean Eviction,结果主机这边没收,两边数据对不上了。后来发现是事务层的CacheID配错了。

2.2.3 CXL.mem 让设备访问主机内存,分内存读、内存写、原子操作。原子操作尤其容易踩坑,因为TLP里的AtomicOp参数必须和RC侧支持的粒度匹配。

调试小技巧: 在事务层抓TLP时,用Vendor Defined Message做时间戳标记,可以精确测量端到端延迟。我在两个项目里都用这个方法定位到了软件竞争条件。

2.3 链路层详解

链路层是CXL特有的,和PCIe差别很大。它把事务层报文打包成固定大小的FLIT(128字节或256字节),加序列号和CRC,然后靠ACK/NACK机制保证可靠传输。

2.3.1 FLIT结构 包含:FLIT头 + 净荷 + 尾字段。FLIT头里有序列号(Sequence ID),尾字段有16位CRC。我见过一个坑:某些工具只显示TLP内容,不显示FLIT头,结果重传序列号完全看不到,排查链路层问题就像蒙眼走路。

2.3.2 重传机制 链路层发一个FLIT后,必须等到对方回ACK才能发下一个(或滑动窗口内)。如果收到NACK或超时,就重传。我遇到最头疼的是“重传风暴”——链路质量变差,CRC错误激增,重传队列溢出,最后链路直接Down掉。

2.3.3 流控 基于Credit。发送方跟踪Credit余额,接收方通过更新Credit来告诉对方“还能收多少个FLIT”。为什么会死锁?很可能Credit更新报文被重传堵住了。我曾经见过一个案例:Credit更新报文和普通FLIT共用一个CRC校验,结果普通FLIT错导致Credit更新也重传,来回死等。

// 链路层调试日志摘录(简化)
[ 0.000123] FLIT_TX: Seq=42, Ack=0, Nack=0, CRC=0xA3F1
[ 0.000135] FLIT_RX: Seq=42, Ack=0, Nack=1, CRC=0xE4C2  // CRC错误
[ 0.000147] FLIT_TX: Seq=42, Ack=0, Nack=0, CRC=0xA3F1  // 重传
[ 0.000159] FLIT_RX: Seq=42, Ack=1, Nack=0, CRC=0x...    // 成功
警告: 链路层重传次数过多会导致事务层超时。实际项目中建议将链路层重传最大次数限制在3~5次,超过后强制下电复位,而不是无限重传。否则事务层会永久挂起。

2.4 物理层详解

物理层负责把FLIT转成高速串行信号。CXL 2.0和3.0都支持32GT/s甚至更高的速率,物理层架构分为PMA和PCS两块。

2.4.1 PMA(物理介质适配层) 包含SerDes、时钟恢复电路、发送均衡、接收均衡等。眼图测试是这层最直观的诊断手段。我记得第一次调32GT/s链路,眼图中间几乎闭合,折腾了两周才发现在PCB换层处阻抗不连续。

2.4.2 PCS(物理编码子层) 负责128b/130b编码(CXL 3.0),以及块同步、扰码、去加重。PCS最常见的调试场景是“块锁定失败”,表现为LTSSM反复在Polling和Recovery之间跳转。这时候要看PCS同步头的误码率。

2.4.3 LTSSM(链路训练和状态机) 物理层最重要的状态机。从Detect开始,经过Polling、Configuration、L0,再到L0s/L1节能态。我接触过的项目中,LTSSM卡在Recovery是占比最高的物理层问题。原因可能是对端的Tx/Rx极性错配,或者链路协商速率不一致。

LTSSM状态含义常见调试手段
Detect检测对端是否在线检查TX共模电压、接收检测电路
Polling发送TS1/TS2序列逻辑分析仪看训练序列数据完整度
Configuration交换链路参数(宽度、速率)确认两端能力寄存器一致
L0正常工作数据流量监控、CRC统计
Recovery检测到错误后重新训练查看错误计数器、眼图裕量

物理层调试一定离不开示波器。我建议手边常备一份“物理层快速检查清单”:先看PLL锁定、再量差分电压摆幅、最后扫眼图。这三个指标正常,链路基本就跑得起来。

物理层避坑指南: 我曾经遇到过加速器间歇性断开,查了三天,最后发现是SKP OS(跳频有序集)插入导致时钟抖动过大。物理层测试时一定要覆盖SKP间隙场景,不要只在理想时钟下测。