🔥点击查看精选 UCIe 系列文章🔥
🔥点击进入【芯片设计验证】社区,查看更多精彩内容🔥
📢 声明:
- 🥭 作者主页:【MangoPapa的CSDN主页】。
- ⚠️ 本文首发于CSDN,转载或引用请注明出处【https://mangopapa.blog.csdn.net/article/details/128673906】。
- ⚠️ 本文目的为 个人学习记录 及 知识分享。因个人能力受限,存在协议解读不正确的可能。若您参考本文进行产品设计或进行其他事项并造成了不良后果,本人不承担相关法律责任。
- ⚠️ 若本文所采用图片或相关引用侵犯了您的合法权益,请联系我进行删除。
- 😄 欢迎大家指出文章错误,欢迎同行与我交流 ~
- 📧 邮箱:mangopapa@yeah.net
文章目录
- 1. 介绍
- 2. Stall Message 复位 Timer
- 2.1 链路初始化中的 Stall
- 2.1.1 MBTRAIN.LINKSPEED 中的 Stall
- 2.1.2 Adapter 参数交换中的 Stall
- 2.2 链路管理状态转移中的 Stall
- 2.3 寄存器访问中的 Stall
- 3. pl_trdy 直接叫停 Flit 发送
- 4. pl_stallreq/lp_stallack 握手暂停 Flit 发送
- 4.1 Stallreq/Ack 机制
- 4.1.1 pl_stallreq/lp_stallack 握手的 4 个 Phase
- 4.1.2 补充说明
- 4.2 pl_stallreq/lp_stallack 握手的使用场景
- 4.3 pl_stallreq/lp_stallack Vs. pl_trdy
- 4.4 Flit Stall 会引发 CompletionTimeout 吗?
- 5. 退出 Stall
- 6. 参考
1. 介绍
UCIe 中有个 Stall 的概念,根据作用及载体的不同又可以细分为三类:
- Stall Message 复位 Timer。这类 Stall 的载体是 Sideband Message,其作用为复位相关 Timer,防止出现不必要的 Timeout。
- pl_trdy 直接叫停 Flit 发送。这类 Stall 可以由下层直接发起,上层立即响应。
- pl_stallreq/lp_stallack 握手暂停 Flit 发送。这类 Stall 是通过 FDI 或 RDI 接口上的 pl_stallreq/lp_stallack 信号握手实现的,非及时响应,通过握手来暂停上层到下层的 Flit 传输,称为 Stallreq/Ack 机制。
以上几种 Stall 中,pl_stallreq/lp_stallack 握手与 pl_trdy 有部分伴生关系,且均与 Stall Message 没有太大的关系。
2. Stall Message 复位 Timer
实际上并不存在一种专有的用以 Stall 的 Message。这里说的 Stall 是 MsgInfo 的一种,能够携带 Stall MsgInfo 的 Message 有几十种。
在 UCIe 中,对于需要 Response 的 Sideband 请求,要求请求发出后开始计时的 Timer 计时到 8ms 内收到 Response,否则就触发 Timeout。实际情况中,存在部分非异常的场景需要 8ms 以上的时间来回复 Response,比如链路传输时延较大,比如 Consumer 较忙来不及处理当前请求,此时不希望 Producer 报出 Timeout。
为了解决以上问题,UCIe 定义了携带有 Stall 信息的 Sideband Message 来复位相关 Timer,以避免不必要的 Timeout。这类 Stall 主要用于链路初始化及协商、链路管理状态转移、Sideband 寄存器访问过程中。
2.1 链路初始化中的 Stall
在 UCIe 链路初始化过程中用到 Stall 的地方有两处:MBTRAIN.LINKSPEED 及 Adapter 参数交换。
2.1.1 MBTRAIN.LINKSPEED 中的 Stall
在带有 Retimer 的 UCIe 链路中,链路初始化处于 MBTRAIN.LINKSPEED 时,UCIe Retimer 之间发送的 {MBTRAIN.LINKSPEED done req}
及 {MBTRAIN.LINKSPEED done resp}
需要携带 Stall 类型的 MsgInfo。不含有 Retimer 的不需要携带 Stall。
2.1.2 Adapter 参数交换中的 Stall
在 UCIe Adapter 初始化过程中,协议要求在 Timer 计时到 8ms 内完成参数交换与协商,否则便会认定为参数交换失败并触发 Timeout。对于带有 Retimer 的 UCIe 链路(尤其是 Off Package 场景),其链路参数协商时间较久,极有可能触发 8ms Timeout。对于这种预期中的、不必要的 Timeout,可以在发生 Timeout 之前定时、周期性地发送带有 Stall 信息的 Message(协议要求是每 4ms 发送一次),来复位对端的相关 Timer,从而避免触发 Timeout。
Adapter 参数交换中用到的 Message 有 {AdvCap.*}
及 {FinCap.*}
,其中 * 可以为 Adapter 或 CXL。这些 Message 中的 MsgInfo 为 0000h (Reserved) 表示未携带 Stall 信息;在需要复位对端 Adapter 参数协商 Timer 时,将 MsgInfo 置为 ffffh (Stall),即发送 {AdvCap.*.Stall}
或 {FinCap.*.Stall}
,表明当前 Message 携带了 Stall 请求,对端收到该 Message 后复位相关 Timer。这些 Stall 请求 Message 不需要对端反馈 Stall Ack。
2.2 链路管理状态转移中的 Stall
请求对端进行链路状态转移时,需要发送 {LinkMgmt.*} 相关 Message,其中部分 Message 可以通过 MsgInfo=Stall (Response) 来重置 Timer。目前支持 Stall 的链路管理类 Message 如下:
- {LinkMgmt.Adapter0.Req.Active},
For Retimer
- {LinkMgmt.Adapter1.Req.Active},
For Retimer
- {LinkMgmt.Adapter0.Rsp.Active}
- {LinkMgmt.Adapter0.Rsp.PMNAK}
- {LinkMgmt.Adapter0.Rsp.L1}
- {LinkMgmt.Adapter0.Rsp.L2}
- {LinkMgmt.Adapter0.Rsp.LinkReset}
- {LinkMgmt.Adapter0.Rsp.Disable}
- {LinkMgmt.Adapter1.Rsp.Active}
- {LinkMgmt.Adapter1.Rsp.PMNAK}
- {LinkMgmt.Adapter1.Rsp.L1}
- {LinkMgmt.Adapter1.Rsp.L2}
- {LinkMgmt.Adapter1.Rsp.LinkReset}
- {LinkMgmt.Adapter1.Rsp.Disable}
- {LinkMgmt.RDI.Rsp.Active}
- {LinkMgmt.RDI.Rsp.PMNAK}
- {LinkMgmt.RDI.Rsp.L1}
- {LinkMgmt.RDI.Rsp.L2}
- {LinkMgmt.RDI.Rsp.LinkReset}
- {LinkMgmt.RDI.Rsp.LinkError}
- {LinkMgmt.RDI.Rsp.Retrain}
- {LinkMgmt.RDI.Rsp.Disable}
以上 MsgInfo 支持 Stall 的 Message 中,Request 类型 Message 的只能由 Retimer 发出,Response 类型的 Message 无论是否 Retimer 都能发。
Parity 协商发生于 Retrain 期间,Retrain 期间 Parity 协商相关 Message 也可以携带 Stall 类型的 MsgInfo,用到的 Message 为 {ParityFeature.Ack}
及 {ParityFeature.Nak}
。
2.3 寄存器访问中的 Stall
在 Sideband 寄存器访问场景中,由于寄存器访问请求 Message 处理优先级低也好,由于 Completer Busy 或 Not Ready 也好,无论什么原因导致 Completer 无法在 8ms 内回复 Status=SC/UR/CA 的 Completion,其必须先行回复 Status=Stall 的 Completion,每 4ms 发送一次,以复位相关 Timer。
说到这里顺带提下 PCIe DMwr (《DMWr (Deferrable Memory Write) 详解》)的 Completion。PCIe 中,当 Completer 收到 DMWr 但无暇处理该 Memory Write 请求时,Completer 需要返回一笔完成状态为 RRS (Request Retry Status) 的 Completion 包来告知 Requester,Requester 可以(非必需)选择合适的时间重新发一次。同样都是无暇处理,PCIe 与 UCIe 采用的策略不同,PCIe 是 Requester 重传,“忙着呢,待会儿来找我”;UCIe 是 Requester 等一等,“好的,待会儿回复”。
3. pl_trdy 直接叫停 Flit 发送
pl_trdy 为低时上层不可以往下层发送 Flit,pl_trdy=1->0 需要发生在 Flit 边界。pl_trdy=0 时,无法继续通过 lp_data_* 这组信号继续发送 Flit,但是 Flit Mode 下 FDI 接口上仍然可以通过 lp_dllp* 继续发送 DLLP(参照 lp_dllp_valid 信号描述)。
这个较容易理解,不作过多解释。
4. pl_stallreq/lp_stallack 握手暂停 Flit 发送
如果说上述 Stall 是 Sideband 上的事,那么接下来将的 Stall 就是 Mainband 上的事。Sideband 上的 Stall 用于重置 Timer,Mainband 上的 Stall 用于暂停 Flit 发送。Mainband Stall 是通过 FDI 或 RDI 接口上的 pl_stallreq/lp_stallack 信号握手实现的,通过握手来暂停上层到下层的 Flit 传输,称为 Stallreq/Ack 机制。
4.1 Stallreq/Ack 机制
UCIe Stallreq/Ack 机制是一种 Mainband Flit Stall 请求应答机制,下层请求上层在 Flit 边界暂停发送 Flit。FDI 及 RDI 接口均需支持 Stallreq/Ack 机制。那么,上层可以请求下层进行 Stall 吗?显然不行,下层有数据发往上层时,上层必须接收,不能反压。
4.1.1 pl_stallreq/lp_stallack 握手的 4 个 Phase
Stallreq/Ack 机制的具体实现是基于 FDI 或 RDI 接口上的 pl_stallreq/lp_stallack 这对握手信号,由下层发起请求,上层进行应答。一次完整的 pl_stallreq/lp_stallack 握手分为 4 个 Phase(图 1),包括 req/ack 的拉高及拉低握手过程(而非仅有拉高握手),如下:
-
下层拉高 pl_stallreq
。下层将 pl_stallreq 拉高,发起 Stall 请求,请求上层 Flush 掉既有的 Flit,不要再生成新的 Flit 了。此时下层仍能够正常接收上层发来的数据。pl_stallreq 上升沿只能发生在 lp_stallack 为低的时候。若 lp_stallack 为高,表示此前进行的 pl_stallreq/lp_stallack 握手还在进行中,没必要重新发起握手请求。 -
上层拉高 lp_stallack
。上层发现下层将 pl_stallreq 拉高之后,适时在 Flit 边界暂 Flit 传输并拉高 lp_stallack,以对 pl_stallreq 进行响应。lp_stallack=1 期间,lp_data 停止传输新的数据,lp_valid=0 且 lp_irdy=0,防止下层误解。lp_stallack 上升沿只能发生于 pl_stallreq 拉高期间,若 pl_stallreq 为低,没必要对齐进行响应。 -
下层拉低 pl_stallreq
。下层收到上层发来的 lp_stallack=1 之后,得知上层已经确认暂停了 Flit 传输。然后,下层将 pl_stallreq 拉低,关闭之前的 Stall 请求。pl_stallreq 下降沿只能发生在 lp_stallack 为高或 reset 期间,若 pl_stallreq=1 时 lp_stallack 为低,表示上层还未对请求进行确认,或上层有故障再次取消了该确认信息。 -
上层拉低 lp_stallack
。上层收到下层发来的 pl_stallreq=0 之后,得知下层已经关闭了之前的 Stall 请求,上层将 lp_stallack 拉低,关闭响应。lp_stallack 下降沿只能发生在 pl_stallreq 拉低或 reset 期间。
在 UCIe Stallreq/Ack 机制的 pl_stallreq/lp_stallack 握手的 4 个 Phase 中,各个 Phase 之间的时间间隔并没有硬性要求。上层收到 pl_stallreq=1 后,并不要求立刻停止 Flit 传输,其不但能够继续发送当前 Flit 的剩余 Chunk,而且可以继续发送更多的 Flit,只需要找一个 Flit 边界暂停发送并响应以 lp_stallack=1 即可。同理,下层收到 lp_stallack=0->1 后不要求立刻将 pl_stallreq 拉低,上层收到 pl_stallreq=1->0 后也不要求立刻拉低 lp_stallack。但是出于性能考虑,在满足规则的前提下应尽快完成握手。当然也不能为了快就把 lp_stallack 到 pl_stallreq 的反馈环路做成纯组合逻辑,这种组合逻辑反馈环路容易有毛刺、震荡、实现违例等问题。为了避免组合逻辑反馈环路,在 pl_stallreq 与 lp_stallack 逻辑路径之间至少应包含一级 Flop。
这里考虑一个问题,为什么要求在 Flit 边界进行 Stall 呢?直接停止不行吗?对于 PCIe 而言,Flit 是最小传输单位,不能发到一半后暂停发送。在 UCIe 中,受限于 lp_data 接口位宽,一笔 Flit 可能会分多个 Clock Cycle 进行发送。如果收到 Stall 后立刻停止 Flit 在 lp_data 上的传送,有可能会导致当前 Flit 被 Stall 割裂,这是不符合 PCIe 协议的。在收到 Stall 请求后,至少应该把当前在传的 Flit 传输完毕,确保是在 Flit 边界 Stall 住。
4.1.2 补充说明
上文多次提到,上层可以自主选择一个 Flit 边界来暂停传输 Flit,这种说法是有争议的。
根据协议对 pl_stallreq 信号的描述,RDI 接口的 pl_stallreq 是状态转移过程中物理层请求 Adapter 发送端在 Flit 边界对齐并停止发送新的 Flit,FDI 接口的 pl_stallreq 是状态转移过程中 Adapter 请求协议层 Flush 掉所有未发出的 Flit 并停止生成新的 Flit。
区别在于,FDI 要求了 Flush 所有 Flit,而 RDI 并没有这个要求。这样一来就有疑问了:
- Adapter Tx Buffer 内还有未发出的 Flit 时可以暂停 Flit 发送吗?
- 协议层收到 pl_stallreq=0->1 请求后,在响应 lp_stallack=0->1 之前还能继续生成新的 Flit 吗?
- 协议层收到 pl_stallreq=0->1 请求后,可以不 Flush 完毕就暂停传输 Flit 并响应 lp_stallack=0->1 吗?
欢迎留言私信与笔者进行讨论。
4.2 pl_stallreq/lp_stallack 握手的使用场景
RDI 接口上,不论是 Raw Mode 还是 Flit Mode,在 RDI 状态由 Active 转为 Retrain、PM(L1/L2)、LinkReset 或 Disabled 之前,必须先进行 Stallreq/Ack 握手,确保 Adapter 发送完当前 Flit。
FDI 接口上,Raw Mode 与 Flit Mode 在 pl_stallreq/lp_stallack 握手使用场景上有所不同。对于 Raw Mode,在 FDI 状态由 Active 转为 Retrain、PM(L1/L2)、LinkReset 或 Disabled 之前必须先进行 Stallreq/Ack 握手(笔者理解 Raw Mode 的 64B 相当于一个 Flit,也应遵守 Flit 边界对齐的规则)。对于 Flit Mode,在 FDI 状态由 Active 转为 PM(L1/L2)之前,必须先进行 Stallreq/Ack 握手,确保协议层发送完当前 Flit;对于 Flit Mode 时 Active 到其他状态的跳转,不要求 Stallreq/Ack 握手,只需 Adapter 在状态转移之前在 Flit 边界拉低 pl_trdy 即可。
注意:Flit Mode 时 FDI 的 pl_trdy 不会 Stall 住 lp_dllp* 信号传输 DLLP,但通过 pl_stallreq/lp_stallack 握手在 FDI 上对 Flit 进行 Stall 后,不但不能发送 Flit,也不能发送 DLLP。
同样都是状态转移,为什么 RDI 与 FDI 对是否需要 pl_stallreq/lp_stallack 握手的要求不同?同样都是 FDI 接口上的状态转移,为什么 Raw Mode 跟 Flit Mode 时对是否需要 pl_stallreq/lp_stallack 握手的要求不同?协议中未明确指出。笔者思来想去,倒是想到几种说法,但总感觉有些牵强附会。笔者辗转反侧,觉得以下说辞尚能解释得通:
在对 pl_stallreq/lp_stallack 的握手需求上,RDI 跟 FDI Raw Mode 需求相同,FDI Flit Mode 自成一类。这几类的 Flit 有一大区别 —— Flit 中是否携带有效的 DLLP 信息。对于 FDI Raw Mode 及 RDI 上的 Flit 数据,有效的 DLLP (有的话)已经蕴含在了 Flit 中;而对于 FDI Flit Mode,其 DLLP 信息是与 Flit 解耦的(Flit 走 lp_data,DLLP 走 lp_dllp)。即便已经发起了 Active 到其他状态的状态转移请求,但在 FDI 或 RDI 状态由 Active 转为其他状态之前,Flit 是仍然在发送的,什么时候切换成功下层并不确定。既然 Flit 在发送,那么我们就需要 Credit 信息来决定是否能发 Flit。Credit 在哪获取?——在 DLLP 里。对于 RDI 及 FDI Raw Mode,如果采用 pl_trdy 来 Stall Flit,Flit 中的 DLLP 也一并被 Stall 住了,对端无法获的最新的 Credit 信息,这便存在一定的风险;对于 FDI Flit Mode,即便采用 pl_trdy Stall 住了 Flit,其 DLLP 仍能通过 lp_dllp* 到达 Adapter,Adapter 仍能获取到最新的 Credit 信息并发送给对端。当然 FDI Flit Mode 也能采用 pl_stallreq/lp_stallack 握手,但其不如 pl_trdy 来得痛快。
从上述描述能够看出,不论是 RDI 接口还是 FDI 接口,其从 Active 退出到 LinkError 状态之前不要求 Stallreq/Ack 握手,笔者理解是因为上层先于下层知晓 Link Error 发生,并直接通过 lp_linkerror 信号通知下层,而非通过 lp_state_req 来请求下层进行状态转换,以此换来更快的响应速度。lp_linkerror=1 更像是一种上层发给下层强制性的命令,要求下层由 Active 转换到 LinkError 状态,不需要协商也不需要握手。
从上述描述还能看出,协议中给出的 pl_stallreq/lp_stallack 握手全部发生于状态转换期间。那么,不涉及状态转换的时候是否可以进行 pl_stallreq/lp_stallack 握手来请求 Stall 呢?协议中未禁止亦未允许。笔者理解,若有 Stall 需求,直接用 pl_trdy 反压上层即可。
还有一点,pl_stallreq/lp_stallack 握手只能发生于 Active 到其他状态的转换过程中吗?从非 Active 到其他状态的转移过程中就不需要吗?是的,不需要,没那个必要。一来,lp_stallreq 的定义中规定了 pl_stallreq 请求是用于状态转移中的;二来在这些状态中,除了 Active,还有哪个状态是能够传输 Flit 的?没有!连 Flit 都不能发,还谈何 Stall。
4.3 pl_stallreq/lp_stallack Vs. pl_trdy
pl_stallreq/lp_stallack 握手与 pl_trdy 均能够暂停上层到下层的 Flit 传输,这两种有何区别呢?
采用 pl_trdy 的方式来暂停 Flit 发送,下层具有绝对的主动权。当下层出于任何原因无法继续接收上层发来的 Flit 时候,下层直接在 Flit 边界把 pl_trdy 拉低,上层一定不能继续发送新的 Flit,立竿见影,简单有效。pl_trdy 的作用域仅限于 Local Die 的上下层,管不住对端。pl_trdy 只挡 lp_data 不挡 lp_dllp。
采用 pl_stallreq/lp_stallack 握手方式来暂停 Flit 发送,上层具有较大的主动权。虽然 Stall 请求是由下层发起,但对 Stall 请求进行确认 Ack 权限在上层。上层可以不及时响应 Stall Req 继续发 Flit,笔者理解上层甚至都可以不理睬下层发来的 Stall 请求。如果上层长时间不对 pl_stallreq=0->1 进行反馈,请求的发起方应会有相关 Timeout 机制来应对该极端情况。pl_stallreq/lp_stallack 是伴随着状态转移的,状态转移是 UCIe 链路两端两个 Die 的事,状态转移完之前两端都会进行 Flit Stall。pl_stallreq/lp_stallack 既挡 lp_data 又挡 lp_dllp。
4.4 Flit Stall 会引发 CompletionTimeout 吗?
既然涉及到 Flit Stall,那就不得不考虑一个问题:如果一笔携带有 NPR (Non-Posted Request) 的 Flit 在收到 Completion 之前就发生了 Flit Stall 且迟迟无法退出 Stall,会触发 Completion Timeout 吗?
笔者理解,这取决于软硬件设计,不应该出现 Completion,但不排除这种可能。软件或硬件有责任在 Flit Stall 之前关闭 Completion Timeout 机制。如果关闭了,那就不会出现 Completion Timeout,否则是有可能出现的。如果非不关闭 Completion Timeout,大致有两种方案:① Timer 暂停;② 在 Timeout 之前恢复 Flit 传输。
5. 退出 Stall
Sideband 上的 Stall 仅用于复位 Retimer,不在讨论范围之类。
对于 pl_trdy=0 直接触发的 Flit Stall,在 pl_trdy=1 后即可继续发送 Flit。
对于 pl_stallreq/lp_stallack 直接触发的 Stall,则需要 FDI 及 RDI 状态转换为 Active 且 pl_trdy=1 后方能继续发送 Flit。
6. 参考
- UCIe Spec r1.0
|
🔥 精选往期 UCIe 协议系列文章,请查看【 Chiplet 专栏】🔥
⬆️ 返回顶部 ⬆️