第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出错,重传把顺序打乱了。从那以后,我每个项目必须三层分别做覆盖率分析。
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侧支持的粒度匹配。
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... // 成功
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锁定、再量差分电压摆幅、最后扫眼图。这三个指标正常,链路基本就跑得起来。