通过之前的学习,我们已经开启了Endpoint端口(后面称EP)的LwIP对Switch外部网络的访问。用过Switch的小伙伴应该都知道,当出现网络风暴时,EP即使不开混杂模式,也会频繁进入中断,提高CPU的loading,降低网络的使用效率。如何解决这个问题呢,一般常见的MCU是没有抑制能力的,往往是通过关闭MAC,等待一段时间后重新尝试开启,通过这样的方式来规避。不过我们RT1180的NETC是非常强大的,自带了网络风暴抑制的功能。
今天我们介绍下网络风暴的抑制的功能,首先我们要了解下Rate Policer Table(速率监管表),主要用于控制 IEEE 802.1Qci 协议中规定的流量监管(Stream Policing)功能,他的功能包括:
提供流量控制配置:该表中包含了 ENETC(以太网控制器)实例的速率策略配置和相关操作信息。
实现令牌桶算法:通过配置该表,可以实现令牌桶算法(Token Bucket Algorithm)来控制和限制网络流量。例如,可以在表中设置C桶和E桶的承诺信息速率(CIR)、超额信息速率(EIR)以及各自的桶容量(CBS、EBS)。
与其他过滤表联动:一个 Rate Policer 实例可以在其他入站处理表中被关联或指定,这包括入口端口过滤表(Ingress Port Filter Table)、入口流表(Ingress Stream Table)入口流过滤表(Ingress Stream Filter Table)。
其中核心的令牌桶算法(Token Bucket Algorithm)是 IEEE 802.1Qci 标准中实现流量监管(Rate Policing)的核心机制,主要用于网络流量的速率测量与控制,在 RT1180 芯片的 NETC(以太网控制器)中,令牌桶算法通过配置 Rate Policer Table 来实现。它通过设定不同的“令牌桶”及其参数,对进入网络的数据帧进行颜色标记(流量测量),并根据标记结果执行相应动作(如放行或丢弃)。详细的算法机制和工作流程如下:
核心参数与令牌桶 该算法主要依赖两个桶:C桶(承诺令牌桶)和 E桶(超额令牌桶)。我们可以为这两个桶配置以下关键参数
数据帧的颜色标记流程 当数据帧进入控制器时,系统会根据帧的大小消耗相应的令牌,并根据桶内令牌的余量对其进行颜色标记。以默认的色盲模式(Color-blind mode)为例,具体的判别流程如下
执行动作 完成颜色标记后,系统会针对不同的颜色执行相应的流量控制策略。对于被标记为红色(Red)的违规数据帧,系统会根据 Rate Policer Table 中对应条目的配置动作来决定如何处理(例如,通常配置为直接丢弃该数据帧)

首先我们需要在SWT_Init的时候开启enBcastStormCtrl并设置RpEntryID,这里使用了相同的ID值=1,在初始化SWT_Init后调用enable_storm_control(),注意第二个参数就是RpEntryID,第三个参数是允许的网络带宽,对应上面提到的CIR,rate的单位是bit,下面代码对应的就是20M的带宽允许:

status_t enable_storm_control(swt_handle_t *handle, uint32_t rp_eid, uint64_t rate){ struct flow_meter_config fm_config; status_t ret; memset(&fm_config, 0U, sizeof(struct flow_meter_config)); fm_config.committed_information_rate = rate; // limited speed test with 200k fm_config.committed_burst_size = 2000; // burst frame size (set as the maximux frame size in the net) fm_config.excess_information_rate = 0; fm_config.excess_burst_size = 0; ret = netc_add_rate_policer_table_entry(handle, &fm_config, rp_eid); return ret;}enable_storm_control函数主要设置的4个参数fm_config.committed_information_rate,fm_config.committed_burst_size,fm_config.excess_information_rate,fm_config.excess_burst_size就对应之前我们提到的CIR, CBS, EIR, EBS。代码其实很简单,我们来对它进行测试验证。
测试之前我们需要搭建一个环境,本来打算找两台PC的网卡都接到Switch上,然后用一台PC给另一台发送,但是实际测试了几个发包软件,流量都上不去,很难达到风暴的效果。所以就用RT1170-EVK手搓了一个发包器,代码使用的是SDK中的enet_txrx_transfer_cm7示例,修改代码如下,首先要把EXAMPLE_USES_LOOPBACK_CABLE设置为1,因为默认情况下这个示例在MAC层环回了,数据是发不出来的。第二步就是修改while(1)里的内容,通过g_delayType来判断延时的大小,这样我们设置不同的值就对应了不同的流量。
#define EXAMPLE_USES_LOOPBACK_CABLE (1U) while (1) { ENET_SendFrame(EXAMPLE_ENET, &g_handle, &g_frame[0], ENET_DATA_LENGTH, 0, false, NULL); switch (g_delayType) { case 0: /* Delay 1ms */ //SDK_DelayAtLeastUs(1000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; case 1: /* Delay 5ms */ SDK_DelayAtLeastUs(100, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; case 2: /* Delay 10ms */ SDK_DelayAtLeastUs(200, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; case 3: /* Delay 50ms */ SDK_DelayAtLeastUs(300, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; case 4: /* Delay 100ms */ SDK_DelayAtLeastUs(400, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; case 5: /* Delay 500ms */ SDK_DelayAtLeastUs(500, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; default: /* Default delay 1ms */ SDK_DelayAtLeastUs(1000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); break; } }硬件接线如下图所示,PC和 RT1170通过网线接入RT1180的ENET0~3的任意口即可:

我们来看一段视频:
根据视频总结一下g_delayType的值与实际流量的对应关系:
设置CIR=20M,CBS=2000。从视频可以看出,当发包流量大于设定的20M后,都会被限制在20M
设置CIR=20M,CBS=CIR/5。从视频可以看出,当发包流量大于设定的20M后,会有比较大的过充,然后趋于稳定,由此可以看出CBS是用于设置延迟过充峰值的
设置CIR=0, CBS=0, EIR=20M, EBS = 2000。视频与测试1几乎一致,说明E桶和C桶是完全独立的,可以同时工作
设置CIR=20M, CBS=2000, EIR=10M, EBS = 2000。从视频可以看出,当C桶设置为20M,E桶设置为10M时,流量在20M~30M之前都是可以正常通过的。
i.MX RT1180 Reference Manual
AN13842