2. UCIe协议栈概述
做多 Die 集成,绕不开 UCIe 协议栈。它就像 Die 之间的「沟通语言」,规定了数据怎么打包、怎么传、怎么纠错。如果你只关心物理连接,不把协议栈吃透,调试时很容易一头雾水。
我个人习惯把协议栈拆成四层来看:物理层、链路层、传输层、协议适配层。每一层各司其职,层与层之间接口清晰。你想想看,如果物理层换了工艺,链路层代码基本不用动——这就是分层设计的好处。
2.1 物理层(PHY Layer)
物理层是协议栈的基石。它管的是最底层的电气连接——信号怎么驱动、时钟怎么恢复、通道怎么训练。说白了,如果物理层没搞定,上面三层再好也是白搭。
我在项目中遇到过好几次这样的情况:链路层报 CRC 错误,翻来覆去查代码逻辑,最后发现是物理层的信号完整性出了问题。眼图都闭上了,链路层再怎么重传也没用。
- 电气 PHY:驱动强度、接收灵敏度、阻抗匹配。这些参数如果配不对,高速信号反射会很严重。
- 时钟与复位:Die 之间的时钟同源还是异步?复位时序有没有满足?我建议在芯片 bring-up 阶段先用低速模式把时钟路径打通。
- 通道训练:包括均衡器系数自适应、延时校准。训练失败是物理层最常见的挂死原因之一。
2.2 链路层(Link Layer)
链路层负责把物理层传上来的原始比特流变成可靠的传输通道。它做三件事:CRC 校验、重传、流控。
CRC 校验是为了检测传输中的错误。如果发现错误,链路层会启动重传。嗯,这里要注意,重传次数是有限制的——超过阈值就直接报链路故障了。
我曾经踩过一个坑:流控的 Credit 初始化顺序搞反了,导致发送端以为自己有 Credit,接收端却根本没准备好。结果就是链路层死锁,数据发不出去,也收不回来。折腾了两天才定位到是固件里 Credit 初始化的时序问题。
- CRC 校验:链路层会在每个 Flit 尾部附加 CRC 字段。接收端计算校验值,不匹配就请求重传。调试时可以用 CRC 错误计数器判断链路质量。
- 重传机制(ARQ):UCIe 用的是 NAK 触发重传。收到 NAK 后发送端重传对应的 Flit。重传缓冲区深度设置很关键——设得太小会丢包,设得太大浪费面积。
- Credit 流控:每个虚拟通道独立管理 Credit。发送端有 Credit 才能发数据。流控死锁是链路层调试的常见难题。
2.3 传输层(Transport Layer)
传输层是协议栈的「路由器」。它负责把 Flit 送到正确的目标 Die。UCIe 协议用 Die ID 来标识每个 Die,传输层根据 Die ID 做路由决策。
我记得有次调试一个 4-Die 堆叠的系统,有一个 Die 总是收不到数据。排查了半天,发现是 Die ID 的配置跳线焊错了。嗯,这种问题在硬件调试时特别容易忽略——你总觉得是协议栈有 bug,结果是最不起眼的配置出错。
- Flit 格式:UCIe 定义了多种 Flit 类型(数据 Flit、控制 Flit、空闲 Flit)。每种 Flit 的头部字段规定了路由信息。
- Die 寻址:每个 Die 有唯一的 ID。传输层根据目标 ID 查路由表,决定 Flit 走哪个方向。路由表配置错误会导致 Flit「迷路」。
- 多播与广播:UCIe 支持一对多传输。这对缓存一致性协议很重要。但多播的调试比单播复杂得多——你要同时检查多个 Die 的接收状态。
2.4 协议适配层(Protocol Adapter)
协议适配层是协议栈的最上层。它把 UCIe 的传输能力映射成具体的协议接口——比如 PCIe、CXL、或者自定义的 Streaming 协议。
说白了,协议适配层决定了你的 Die 对外「长什么样」。如果你是做 CXL 内存扩展,这一层就要实现 CXL.io 和 CXL.mem 的语义;如果你是做 PCIe 交换机,这一层就要处理 TLP 包的封装和解封。
- PCIe 适配:把 UCIe Flit 转换成 PCIe TLP。需要处理事务层包头、完成超时、流量分类等。
- CXL 适配:除了 PCIe 基础,还要支持 CXL.cache 和 CXL.mem 协议。多 Die 场景下,CXL 的缓存一致性协议对传输延时非常敏感。
- Streaming 适配:用于自定义数据流场景。没有复杂的协议头,但需要自己定义数据边界和同步机制。
2.5 协议栈各层核心对比
调试时你需要在各层之间快速切换视角。下面这个表格总结了我常用的各层核心参数和调试入口:
| 协议层 | 核心功能 | 关键参数 / 指标 | 调试入口 |
|---|---|---|---|
| 物理层 | 电气连接、时钟、训练 | 眼图高度 / 宽度、抖动 RMS | PHY 状态寄存器、训练 FSM |
| 链路层 | CRC、重传、流控 | CRC 错误计数、重传次数、Credit 余量 | 链路层状态机、Credit 计数器 |
| 传输层 | Flit 路由、Die 寻址 | 路由错误计数、Flit 丢弃率 | 路由表、Flit 跟踪器 |
| 协议适配层 | 协议映射、事务转换 | 事务超时、协议错误码 | 协议层跟踪日志、驱动回显 |
2.6 协议栈与多 Die 调试的关联
理解了协议栈的每层分工,调试时就能更有条理。我个人的排查习惯是:
- 先看物理层:锁相环是否锁定?通道训练是否完成?眼图是否张开?——如果物理层不健康,先修物理层。
- 再看链路层:CRC 有没有持续报错?链路状态机是否稳定?流控有没有死锁?——链路层稳定了,数据传输才有保障。
- 接着看传输层:Flit 有没有丢?路由是否正确?Die ID 是否匹配?——如果数据到了错误的 Die,一定是传输层配置有问题。
- 最后看协议适配层:事务有没有超时?协议握手是否完成?——这一层的 bug 往往和上层软件配合有关,调试时需要驱动和固件的协助。
多 Die 集成调试的难点在于——问题可能跨层传播。物理层的一点点信号抖动,到了链路层可能变成大量重传,再到传输层可能表现为 Flit 丢失,最后在协议适配层触发事务超时。如果你不熟悉协议栈的分层结构,很容易被表象迷惑,在错误的方向上浪费大量时间。
所以我的建议是:先把协议栈的每层功能刻在脑子里,调试时才能快速定位根因。