《FPGA实现IEEE 802.1Qav信用整形模块》

4、信用整形算法详解:信用值计算、发送/暂停条件、空闲斜率(idleSlope)与发送斜率(sendSlope)的数学定义

IEEE 802.1Qav(Forwarding and Queuing Enhancements for Time-Sensitive Streams,FQTSS)是时间敏感网络(TSN)标准族中面向音视频桥接(AVB)的核心协议之一。它通过信用整形(Credit-Based Shaper,CBS)机制,为两类实时流(Class A 和 Class B)提供有界延迟和低抖动保障。与严格优先级(SP)或加权轮询(WRR)不同,CBS 并不依赖队列深度或权重轮转,而是通过一个连续变化的“信用值(credit)”来动态控制帧的发送与暂停。本章将深入剖析信用值的数学定义、发送/暂停判定条件,以及 idleSlope 与 sendSlope 两个关键斜率的物理含义与工程实现要点,为后续 FPGA 模块设计奠定算法基础。

CBS 信用整形核心算法模型 Class A/B 队列 帧到达/排队 队列非空判定 信用值累加器 (credit) 初始值 0,范围 [loCredit, hiCredit] 按斜率线性增减 发送判决 credit ≥ 0 且队列非空 → 发送 credit < 0 或队列空 → 暂停 队列状态 credit 值 帧发送 斜率控制 发送帧时:credit -= sendSlope 空闲/等待时:credit += idleSlope sendSlope = idleSlope - portTransmitRate 发送/暂停事件 更新 credit 关键参数定义 idleSlope:带宽预留值(如 Class A = 20 Mbps),由 SRP 协商 sendSlope = idleSlope - portTransmitRate(通常为负值) hiCredit = maxFrameSize × sendSlope / portTransmitRate loCredit = maxFrameSize × idleSlope / portTransmitRate

4.1 信用值(Credit)的核心原理与数学定义

信用整形器的本质是一个带上下限的积分器。每个 AVB 队列(Class A 或 Class B)都维护一个独立的信用值 credit,该值随时间连续变化,但只在帧发送开始和结束时刻被采样用于判决。信用值的物理含义可以理解为“该队列在当前时刻相对于其预留带宽的透支或盈余程度”。当 credit 为 0 或正数时,表示队列有资格发送;当 credit 为负数时,表示队列正在“偿还”之前超额发送的带宽。

数学上,credit 的更新遵循以下分段线性规则:

  • 发送状态(Transmission Active):当队列正在发送一帧时,credit 以 sendSlope 的速率递减。即 credit(t) = credit(t₀) + sendSlope × (t - t₀),其中 sendSlope 为负值。
  • 阻塞状态(Blocked):当队列有帧等待发送但 credit < 0 时,credit 以 idleSlope 的速率递增,直到达到 0 或队列变为空。
  • 空闲状态(Idle):当队列为空时,credit 被重置为 0(而非继续累积),这是为了防止队列在长时间空闲后积累大量“信用”而突发占用链路。

credit 的上下限 hiCredit 和 loCredit 并非随意设定,而是由最大帧长和斜率参数推导而来。hiCredit 表示在发送一帧期间 credit 可能达到的最大值,loCredit 表示在等待期间 credit 可能跌至的最小值。这两个边界保证了 credit 不会无限增长或减少,从而将突发大小限制在可控范围内。

工程要点:在 FPGA 实现中,credit 通常使用定点数表示(如 Q16.16 格式),以避免浮点运算带来的资源开销和不确定延迟。斜率参数 idleSlope 和 sendSlope 需要根据端口速率和预留带宽预先计算为固定点常量,并在模块初始化时加载。

4.2 发送/暂停条件:判决逻辑与状态机

CBS 的发送判决逻辑可以归纳为一个简单的二输入条件:队列非空 且 credit ≥ 0。当两个条件同时满足时,队列被允许发送帧;否则,队列必须暂停发送,直到 credit 回升至非负值。值得注意的是,credit 的更新与帧的发送过程是同步的:一旦帧开始发送,credit 立即开始以 sendSlope 递减,即使此时 credit 变为负值,当前帧也会继续发送完毕,不会中途中断。

从状态机角度看,每个 AVB 队列的发送过程可以划分为四个状态:

  1. IDLE:队列为空,credit 保持为 0。当帧到达时,若 credit ≥ 0,则直接进入 TRANSMIT 状态;否则进入 BLOCKED 状态。
  2. TRANSMIT:正在发送帧,credit 以 sendSlope 递减。帧发送完毕后,若队列中仍有帧且 credit ≥ 0,则继续发送下一帧;若 credit < 0,则进入 BLOCKED 状态;若队列为空,则回到 IDLE 状态。
  3. BLOCKED:队列有帧等待,但 credit < 0。credit 以 idleSlope 递增,直到 credit 达到 0,立即切换到 TRANSMIT 状态(注意:不是等到 credit 变为正值,而是达到 0 即可)。
  4. WAITING(可选):在某些实现中,当队列为空但 credit 尚未归零时,会进入等待状态,将 credit 快速归零。但 802.1Qav 标准规定,队列为空时 credit 直接重置为 0,因此该状态通常不单独建模。

这种判决逻辑的关键优势在于:它天然实现了带宽的平滑分配。当多个 AVB 队列竞争同一出口端口时,每个队列的 credit 独立演化,不会出现严格优先级中低优先级队列被完全饿死的情况,也不会像 WRR 那样产生明显的突发。

状态 触发条件 credit 变化 动作
IDLE 队列空 保持 0 等待帧到达
TRANSMIT 队列非空 & credit ≥ 0 以 sendSlope 递减 发送帧
BLOCKED 队列非空 & credit < 0 以 idleSlope 递增 暂停发送

4.3 idleSlope 与 sendSlope 的数学定义及参数推导

idleSlope 和 sendSlope 是 CBS 算法中最重要的两个参数,它们直接决定了带宽分配和延迟特性。根据 IEEE 802.1Qav 标准,这两个斜率的定义如下:

  • idleSlope(空闲斜率):表示当队列处于阻塞状态时,credit 恢复的速率。其数值等于该 AVB 类预留的带宽值。例如,若 Class A 预留 20 Mbps 的带宽,则 idleSlope = 20 Mbps。idleSlope 必须小于端口发送速率(portTransmitRate),否则算法无法收敛。
  • sendSlope(发送斜率):表示当队列正在发送帧时,credit 消耗的速率。其定义为 sendSlope = idleSlope - portTransmitRate。由于 portTransmitRate 通常远大于 idleSlope,sendSlope 一般为负值。例如,端口速率为 100 Mbps,idleSlope 为 20 Mbps,则 sendSlope = 20 - 100 = -80 Mbps。

这两个斜率的物理意义可以通过一个简单的带宽守恒模型来理解。假设一个 AVB 队列在长时间内的平均发送速率为 R_avg,那么 credit 在一个完整周期内(发送时间 + 阻塞时间)的净变化必须为零。设发送时间为 T_tx,阻塞时间为 T_blk,则有:

sendSlope × T_tx + idleSlope × T_blk = 0
=> T_tx / (T_tx + T_blk) = idleSlope / (idleSlope - sendSlope)
=> R_avg = portTransmitRate × T_tx / (T_tx + T_blk) = idleSlope

由此可见,CBS 算法保证了每个 AVB 队列的长期平均发送速率恰好等于其预留的 idleSlope 值,实现了精确的带宽隔离。

hiCredit 和 loCredit 的计算公式如下:

hiCredit = maxFrameSize × sendSlope / portTransmitRate
loCredit = maxFrameSize × idleSlope / portTransmitRate

其中 maxFrameSize 是该队列允许的最大帧长(以字节为单位,需转换为比特)。hiCredit 的绝对值表示在发送一帧期间 credit 的最大消耗量,loCredit 的绝对值表示在等待一帧期间 credit 的最大积累量。这两个值用于在 FPGA 中设置 credit 累加器的饱和边界,防止溢出。

参数计算示例:假设端口速率为 1 Gbps,Class A 预留带宽 50 Mbps,最大帧长 1522 字节(含以太网帧头、VLAN 标签和 FCS)。则 sendSlope = 50 - 1000 = -950 Mbps。hiCredit = 1522 × 8 × (-950) / 1000 = -11567.2 bits ≈ -1446 bytes。loCredit = 1522 × 8 × 50 / 1000 = 608.8 bits ≈ 76 bytes。注意 hiCredit 为负值,表示发送一帧后 credit 最多下降约 1446 字节的信用量。

4.4 案例拆解:100Mbps 端口下 Class A 与 Class B 的共存场景

为了更直观地理解 CBS 算法的行为,我们设计一个双 AVB 队列的仿真场景。假设端口速率为 100 Mbps,Class A 预留 20 Mbps(idleSlope_A = 20 Mbps),Class B 预留 30 Mbps(idleSlope_B = 30 Mbps)。两个队列的 maxFrameSize 均为 1522 字节。则 sendSlope_A = 20 - 100 = -80 Mbps,sendSlope_B = 30 - 100 = -70 Mbps。

初始状态:两个队列均为空,credit_A = 0,credit_B = 0。此时两个队列同时到达大量帧。由于 credit 均为 0,两个队列都有资格发送。假设先发送 Class A 的一帧(1522 字节),发送时间为 1522×8/100Mbps ≈ 121.76 μs。在这段时间内,credit_A 从 0 降至 -80 Mbps × 121.76 μs = -9.74 Kbits ≈ -1218 字节。而 credit_B 在等待期间以 idleSlope_B = 30 Mbps 的速率上升,经过 121.76 μs 后,credit_B = 30 × 121.76 μs = 3.65 Kbits ≈ 456 字节。

Class A 帧发送完毕后,credit_A = -1218 字节 < 0,因此 Class A 进入 BLOCKED 状态。此时 Class B 的 credit_B = 456 字节 ≥ 0,且队列非空,因此 Class B 开始发送。在 Class B 发送一帧期间(同样约 121.76 μs),credit_B 从 456 字节降至 456 - 70 Mbps × 121.76 μs = 456 - 1065 = -609 字节。同时 credit_A 以 idleSlope_A = 20 Mbps 的速率回升,经过 121.76 μs 后,credit_A = -1218 + 20 × 121.76 μs = -1218 + 304 = -914 字节。

如此交替进行,两个队列的 credit 呈现锯齿波状波动。经过多个周期后,系统达到稳态:Class A 的实际发送速率趋近于 20 Mbps,Class B 趋近于 30 Mbps,剩余 50 Mbps 带宽留给非 AVB 流量(如尽力而为队列)。

从这个案例中可以看出:

  • credit 的初始值为 0,允许队列在启动时立即发送一帧,这有助于降低初始延迟。
  • 发送一帧后,credit 变为负值,强制队列暂停,从而限制了突发长度。
  • 两个队列的 credit 独立演化,互不干扰,实现了带宽的硬隔离。
常见误区:有些工程师误以为 credit 必须大于 0 才能发送,实际上标准规定 credit ≥ 0 即可。另外,credit 的更新是连续的,但在 FPGA 中通常以时钟周期为步进进行离散化更新,步进大小会影响精度。建议使用高精度定点数(如 32 位宽,其中 16 位小数位)来保证斜率计算的精度,避免长期运行后产生累积误差。

4.5 常见误区与工程复盘

在 FPGA 实现 CBS 模块时,以下几个问题最容易导致功能异常或性能不达标:

  1. 斜率单位混淆:idleSlope 和 sendSlope 的单位是 bits/s,但在 FPGA 中通常需要转换为“每时钟周期的信用变化量”。例如,若时钟频率为 125 MHz,则每个时钟周期内 credit 的变化量为 idleSlope / 125e6。转换时务必注意单位一致性,避免数量级错误。
  2. credit 饱和处理不当:当 credit 达到 hiCredit 或 loCredit 时,必须进行饱和(clamp)处理,而不是简单地截断。否则,credit 可能溢出到错误的方向,导致发送判决逻辑混乱。
  3. 帧发送期间的 credit 冻结:有些实现错误地在帧发送期间冻结 credit,直到帧发送完毕后才一次性扣除。这会导致 credit 变化不连续,影响带宽分配的准确性。正确做法是在帧发送的每个时钟周期内持续递减 credit。
  4. 队列空时 credit 未归零:标准规定队列为空时 credit 必须重置为 0。如果保留之前的负值,会导致队列在下一轮帧到达时经历不必要的阻塞延迟。

复盘一个实际项目中的排障案例:某 AVB 交换机在 1Gbps 端口上配置 Class A 带宽 100 Mbps,实测吞吐量仅为 85 Mbps。通过逻辑分析仪抓取 credit 波形,发现 credit 在发送帧期间递减速率正确,但在阻塞期间递增速率只有理论值的 80%。进一步检查发现,FPGA 代码中使用了 16 位定点数表示 idleSlope,而 100 Mbps 对应的每周期增量需要 18 位才能精确表示,导致量化误差累积。将定点数位宽扩展至 24 位后,吞吐量恢复至 99.8 Mbps,误差在可接受范围内。

总结而言,CBS 算法的 FPGA 实现核心在于:精确的斜率参数计算、合理的定点数位宽选择、严格的 credit 饱和处理,以及状态机中发送/暂停条件的无毛刺切换。掌握这些要点,即可为后续的队列管理、帧调度和 AVB 端到端延迟保障打下坚实基础。