3. TSN核心机制(下):流量调度与带宽预留

上一章我们聊了时钟同步和时间同步,那是TSN的"心跳"。这一章我们来看看TSN真正的"肌肉"——流量调度和带宽预留。说白了,就是怎么让数据在正确的时间、以正确的顺序、到达正确的地方。

我在做车载项目时,最头疼的不是功能实现,而是多个ECU同时发数据时,总线上的"交通堵塞"。你想想看,摄像头要传视频,雷达要传点云,座舱要传音频,这些数据挤在一起,谁先谁后?TSN给出了答案。

3.1 时间感知整形(Qbv)——给数据排个时间表

Qbv的全称是Time-Aware Shaper,时间感知整形器。它的核心思想很简单:把时间切成一个个小窗口,每个窗口只允许特定类型的数据通过。

核心概念:Qbv通过门控列表(Gate Control List,GCL)控制每个队列的开关状态。时间到了,门打开,数据出去;时间过了,门关上,数据等着。

我记得第一次看Qbv的配置时,感觉就像在看一个复杂的交通信号灯系统。每个队列对应一个"车道",GCL就是"红绿灯"的时序表。

3.1.1 Qbv的工作原理

每个端口有8个优先级队列(0-7)。每个队列都有一个门(gate),门的状态只有两种:开(open)或关(close)。GCL定义了每个时间点,哪些门开,哪些门关。

// GCL配置示例(伪代码)
GCL Entry 0: 时间偏移 0ms,  队列7开, 其他关
GCL Entry 1: 时间偏移 1ms,  队列6开, 其他关
GCL Entry 2: 时间偏移 2ms,  队列5开, 其他关
GCL Entry 3: 时间偏移 3ms,  队列4开, 其他关
GCL Entry 4: 时间偏移 4ms,  队列3开, 其他关
GCL Entry 5: 时间偏移 5ms,  队列2开, 其他关
GCL Entry 6: 时间偏移 6ms,  队列1开, 其他关
GCL Entry 7: 时间偏移 7ms,  队列0开, 其他关

这个配置意味着:每1ms切换一个队列。时间敏感的数据(比如控制指令)放在高优先级队列,就能在固定的时间窗口内被发送出去。

我的经验:实际项目中,GCL的粒度通常是纳秒级的。我曾经在一个ADAS项目中,把GCL的周期设为125μs,每个窗口只有15.625μs。这个精度对时钟同步的要求非常高,稍有偏差就会导致数据错位。

3.1.2 Qbv的避坑指南

我曾经踩过一个坑:GCL的周期和PTP的同步周期没有对齐。结果就是,时钟同步的瞬间,GCL刚好在切换窗口,导致一个关键的控制帧被延迟了整整一个窗口周期。嗯,从那以后,我养成了一个习惯——GCL的周期必须是PTP同步周期的整数倍。

注意事项:

  • GCL的基时间必须与PTP的全局时间同步
  • 保护带(Guard Band)要留够,防止前一个窗口的数据侵占下一个窗口的时间
  • 窗口切换时,正在发送的帧允许完成,但新帧不能开始发送

3.2 帧抢占(Qbu)——让紧急数据插个队

Qbv虽然好,但有个问题:如果一个长帧(比如1500字节的IP包)正在发送,突然来了一个紧急的控制帧,怎么办?等长帧发完?那延迟就不可控了。

Qbu(Frame Preemption)就是解决这个问题的。它允许一个正在发送的帧被"打断",先发送紧急帧,然后再恢复发送原来的帧。

3.2.1 Qbu的工作机制

帧抢占的核心是"切分"。发送端把一个长帧切成多个小片段(fragment),每个片段后面留一个"可打断点"。当紧急帧到达时,发送端在当前片段发送完成后,插入紧急帧。

特性 说明
可抢占帧 长度大于124字节的非时间敏感帧
不可抢占帧 长度小于等于124字节的帧,或标记为"express"的帧
片段大小 最小64字节(含前导码和帧间距)
恢复机制 接收端重组片段,恢复原始帧

我个人觉得,Qbu就像高速公路上的"应急车道"。平时不用,但关键时刻能救命。在车载网络中,紧急制动指令的延迟要求是微秒级的,没有Qbu,光靠Qbv很难满足。

关键点:Qbu需要发送端和接收端都支持。如果接收端不支持,发送端就不能启用抢占。我在项目中遇到过兼容性问题——有些老款ECU的MAC层不支持帧抢占,导致整个网络无法启用Qbu。

3.3 入口管制(Qci)——把"坏孩子"挡在门外

Qci(Per-Stream Filtering and Policing)是TSN的"门卫"。它负责检查每个数据流是否遵守约定——带宽有没有超标?帧间隔对不对?如果违规,就采取行动:丢弃、标记或降级。

3.3.1 Qci的过滤规则

Qci的过滤是基于流的。每个流由目的MAC地址、VLAN ID和优先级唯一标识。对于每个流,可以配置以下规则:

  • 带宽上限:每秒最多发送多少字节
  • 突发大小:一次最多连续发送多少字节
  • 帧间隔:两帧之间的最小时间间隔
  • 帧大小:最小和最大帧长

我曾经在一个项目中,发现某个ECU的软件bug导致它每隔1ms就发送一个广播帧。这个广播帧占用了大量带宽,导致其他关键数据被延迟。后来用Qci限制了该ECU的发送速率,问题就解决了。

实用技巧:Qci的配置要"宽进严出"。入口处可以稍微宽松一点,避免误杀正常数据;出口处要严格,确保网络质量。我一般把入口的带宽上限设为预留带宽的110%,出口设为100%。

3.4 带宽预留(Qav)——给关键数据"划地盘"

Qav(Credit-Based Shaper)是TSN的"交通警察"。它不直接控制时间窗口,而是通过"信用值"来调节每个队列的发送速率。

3.4.1 Qav的工作原理

每个队列有一个信用值(credit)。信用值随时间线性增加,发送数据时线性减少。当信用值为正时,队列可以发送数据;为负时,必须等待。

// Qav信用值计算(简化版)
// 每发送一个字节,信用值减少 1/发送速率
// 每空闲一个时间单位,信用值增加 1/空闲速率

if (队列非空 && 信用值 >= 0) {
    发送数据();
    信用值 -= 发送字节数 / 发送速率;
} else {
    信用值 += 空闲时间 / 空闲速率;
}

这个机制的好处是:带宽分配是"软"的。如果某个队列没有数据,它的信用值会一直增加,等有数据时就能"爆发"发送。这比Qbv的"硬"时间窗口更灵活。

3.4.2 Qav的带宽计算

假设总带宽为100Mbps,我们要给三个流分配带宽:

流 预留带宽 发送速率 空闲速率
控制流 20 Mbps 100 Mbps 20 Mbps
视频流 50 Mbps 100 Mbps 50 Mbps
普通数据 30 Mbps 100 Mbps 30 Mbps

注意看,发送速率都是100Mbps(线速),但空闲速率不同。这意味着:当队列有数据时,它可以以线速发送;当队列空闲时,它的信用值增长速度不同。这样,控制流虽然只预留了20Mbps,但需要时能"抢"到全部带宽。

重要提醒:Qav不能单独保证确定性延迟。它只能保证平均带宽。要保证最坏情况下的延迟,必须和Qbv配合使用。我一般把Qav用于非时间敏感但需要带宽保障的流(比如视频),把Qbv用于时间敏感的流(比如控制指令)。

3.5 四种机制的协同工作

好了,四种机制都讲完了。它们不是孤立的,而是协同工作的。我画了一张图来说明它们的关系:

TSN流量调度与带宽预留协同架构 数据流入口 Qci 入口管制 8个优先级队列(每个队列有独立的Qav信用值) Q7 Q6 Q5 Q4 Q3 Q2 Qbv 门控列表 Qbu 帧抢占 数据流先经过Qci过滤 再进入Qav管理的队列 最后由Qbv调度发送 Qbu在发送时 允许紧急帧抢占

从图中可以看出,数据流的处理流程是:

  1. Qci先做入口检查,把违规的流量挡在外面
  2. 通过检查的数据进入Qav管理的优先级队列,每个队列有自己的信用值
  3. Qbv根据GCL控制每个队列的发送窗口
  4. 在发送过程中,Qbu允许紧急帧抢占正在发送的长帧

这四种机制配合起来,就能实现车载网络最核心的需求:确定性延迟和带宽保障。

实战建议:我在项目中一般这样分配:

  • 控制指令(如制动、转向):Qbv + 最高优先级队列,周期125μs
  • 传感器数据(如摄像头、雷达):Qav + 高优先级,预留带宽
  • 诊断和OTA:Qav + 低优先级,尽力而为
  • 所有流都经过Qci过滤,防止异常流量