2. 链路层协议基础

链路层,说白了就是UCIe协议栈里的“交通警察”。

它夹在物理层和事务层中间,干的全是脏活累活。我个人习惯把链路层看作是协议栈的“核心枢纽” —— 上层要发数据,得先到链路层报到;下层要收数据,也得链路层先验证一遍。你想想看,如果没有这一层,两个芯片之间根本没法好好聊天。

一句话总结:链路层负责把事务层的数据,安全可靠地交给物理层。反过来也一样。

2.1 链路层到底干些什么?

我刚开始接触UCIe时,觉得链路层不就是个中间人嘛,能有多复杂?

真正动手调协议栈之后才发现,这层干的活儿还真不少。咱们直接上干货,看它的核心功能:

  • FLIT打包与拆包:把事务层的报文,切成固定大小的FLIT(Flow Control Unit)。就像把大块头行李拆成标准行李箱,方便搬运。
  • CRC生成与校验:每个FLIT出门前,都给它附上一个「防伪标签」。收件方收到后先验标签,不对就扔。这叫循环冗余校验。
  • 链路层重传(LLR):CRC校验失败的FLIT,链路层会自动要求重传。我在项目中遇到过,有时候物理层环境差一点,重传率一下就上去了,这时候别慌,先查CRC校验逻辑。
  • 数据包仲裁与优先级管理:不同协议的报文(PCIe、CXL等)在链路层排队,谁先走谁后走,得按规矩来。
功能模块输入来源输出目标我踩过的坑
FLIT打包事务层物理层千万注意FLIT边界对齐,歪一位后面全乱
CRC生成FLIT数据FLIT尾部多项式选错了,怎么验都通不过,别问我怎么知道的
链路层重传物理层回传重传缓冲区缓冲深度设计不够,重传一来直接死锁,烦死
仲裁调度多协议VC调度后的FLIT优先级反转?设个老化计数器就解决了
注意:链路层重传和上层协议的重传是两码事。LLR只在链路层内部兜底,上层根本不知道下面发生了重传。我刚开始调试时总搞混,后来才明白这是分层设计的妙处。

2.2 与物理层的接口:怎么把FLIT送出去?

链路层和物理层之间,走的是原始数据接口(RDI)或高级数据接口(ADI)。

这俩接口有什么区别?你直接看这张表就明白了:

特性RDIADI
数据宽度固定位宽可配置
控制信号简单更丰富
时序要求宽松严格
应用场景简单Die-to-Die高性能、高可靠性

我个人习惯用ADI接口,因为它的控制信号更全,调试时能看到更多信息。但也别啥都用ADI,如果设计目标就是低功耗、低速率,RDI就够了,省电。

数据从链路层下到物理层时,有几个关键信号你得盯住了:

  • tx_data:要发送的FLIT数据。嗯,这个没问题。
  • tx_valid:数据有效信号。我遇到过明明是有效数据,这信号死活拉不高,最后发现是状态机跳转条件写错了。
  • tx_credits:物理层给链路层的信用量。说白了就是“我还能收几个FLIT”,这个要是配错了,直接导致链路堵塞。
  • rx_ready:接收端是否准备好。你不拉这个,发送端就一直干等。
我的经验:调试物理层接口时,先盯着tx_valid和rx_ready这对握手信号。94%的链路层问题,都出在这俩信号没对上。

2.3 与事务层的接口:数据从哪里来?

事务层就像是应用软件,只关心“发什么”,不关心“怎么发”。

链路层和事务层的接口,主要是通过一组服务原语来交互。我把它简化成三条关键线:

  • T-Hrd:事务层发请求给链路层。比如“帮我发送一个PCIe TLP”。
  • T-Prd:事务层收数据时,从链路层读回来的内容。
  • T-Err:传输出错。事务层收到这个信号,就知道链路层重传已经失败了,这时候该上层来决定了——是重试还是放弃。

你想想看,事务层其实是个“甩手掌柜”。它把数据包往链路层一扔,就不管了。所有的分割、校验、重传,全是链路层自己扛。

这里要特别注意一点:事务层发给链路层的数据,必须是一个完整的协议数据单元(PDU)。半拉子数据丢过来,链路层没法打包成FLIT。我曾经调试时发现链路层老报错,追了一周才知道是主机驱动在事务层把数据切成三份发送了,链路层完全懵圈。

好,咱们用一张图把三层的接口脉络理清楚。下面的SVG展示了UCIe协议栈中,链路层如何连接上下两层:

UCIe协议栈三层结构及接口 事务层 生成报文,通过服务原语下发 T-Hrd (请求下发) T-Prd / T-Err 链路层 FLIT打包、CRC生成与校验、重传管理 多协议仲裁调度、信用量管理 tx_data / tx_valid rx_data / credits 物理层 电气信号、时钟恢复、比特流传输 链路层是关键枢纽 上下接口都得盯

2.4 接口时序的一个典型场景

说个具体的例子。事务层发了一个32字节的数据包给链路层。

链路层收到后,把这个包拆成两个FLIT(每个FLIT 16字节)。接着给每个FLIT算CRC值,附在尾部。然后检查物理层的信用量——嗯,信用量够,链路层就把这两个FLIT通过tx_data和tx_valid拍下去。

物理层收到后,再一路送到对端芯片。对端的链路层收到后,先做CRC校验,没问题,就把FLIT重新组装成原始报文,通过T-Prd原语交给对端的事务层。

你看,整个流程看起来顺滑无比。但现实中呢?我在这中间见过至少10种不同的错误模式。比如CRC校验不过、信用量不足、T-Hrd握手超时……

调试建议:第一次看链路层运行情况,建议你把事务层和物理层的接口信号同时拉到逻辑分析仪里,一次看三层。别只看一层,那样根本发现不了跨层的问题。

2.5 链路层状态机简介

链路层内部有一套状态机,用来管理链路的连接状态。这套状态机并不复杂,但很关键:

  • Init状态:链路刚建立,等待训练。这时候链路层啥都不干,等着物理层给信号。
  • Active状态:能正常收发数据了。95%的调试时间都在这个状态里。
  • Error状态:链路出错了,可能是CRC错误超过阈值,也可能是重传超时。进入这个状态后,链路层会尝试恢复,恢复不了就通知上层。
  • LowPower状态:省电模式。链路上不走数据,但接口还活着,随时能唤醒。

我记得有一次调样机,链路隔几分钟就跳一次Error状态。追了好久才发现是物理层电源没稳,导致时不时丢一个比特。所以遇到链路莫名其妙掉线的,先查物理层,别一上来就怀疑链路层代码。

省流版:链路层把底下的事扛了大半,让上层专注于业务逻辑。调试时,盯住接口握手信号和状态机跳转,基本就能定位90%的问题。