ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Zynq UltraScale+ 2.5G以太网:AXI Ethernet Subsystem与DMA吞吐量调优

Zynq UltraScale+ 2.5G以太网:AXI Ethernet Subsystem与DMA吞吐量调优 干FPGA开发这些年最折腾的一件事就是网络吞吐量上不去。尤其是遇到Zynq UltraScale这种多核ARM和可编程逻辑在一颗芯片里的平台很多人习惯直接用PS侧自带的GEMGigabit Ethernet MAC但它的上限就是1G想要2.5G只能另找出路。这个题目里的AXI Ethernet Subsystem就是Xilinx官方给出的解决方案在PL侧例化一个支持多速率以太网的IP配合DMA和DDR把整条网络路径打通。这篇文章就围绕我实际做过的2.5G吞吐量调优整理一下把关键的配置思路、参数选择的理由和踩过的坑都摊开讲希望能帮到正在和吞吐量较劲的人。1. 开工之前的盘算先看懂AXI Ethernet Subsystem的底细1.1 为什么不用PS自带的GEM非要自讨苦吃Zynq UltraScale这颗芯片的PS端集成有多个GEM看起来很方便——直接调驱动就能跑网络。但GEM有一个绕不过去的短板每个通道的线速上限就是10/100/1000M严格意义上说是一个千兆控制器。你要2.5G这个瓶颈是硬性的不管软件怎么优化都不可能突破。我最早也尝试过把GEM配置成RGMII 2.5G看寄存器配置手册上似乎有个1000Mbps以上的速率选项实际上RGMII的电平标准在2.5G下根本不稳定尤其是走长走线或者接普通千兆PHY时要么直接断链要么误码率暴涨。这个方向别去碰浪费时间。所以在PL侧例化AXI Ethernet Subsystem本质上不是炫技而是平台能力边界决定的必然选择。它通过硬件MAC、PCS/PMA和DMA的组合把2.5G的串行数据转换成AXI总线上的并行数据体这样PS侧CPU只需要管理描述符和业务逻辑数据面完全交给硬件。1.2 AXI Ethernet Subsystem的总体架构这个IP从功能上可以拆成三部分首先是MAC层负责以太网帧的封装和解封装包含FCS校验、前导码处理、流控帧识别这些基础功能。它输出的接口可以是RGMII、GMII也可以是SGMII或1000BASE-X。对2.5G来说基本只能选SGMII或者1000BASE-X因为RGMII到不了2.5GGMII更是为百兆和千兆设计的。其次是PCS/PMA层负责8b/10b编码、时钟恢复、auto-negotiation。这部分可以直接集成在IP内部也可以挂在外部PHY上。如果直接用SFP光模块或者2.5G铜缆PHY一般建议把PCS/PMA集成在IP内减少对外部器件的依赖。第三是DMA和通用AXI接口其实这个IP本身不带DMA需要搭配AXI DMA或者AXI CDMA一起用。Xilinx习惯把它当成一对搭档来使用。数据从网络进来之后通过MAC解帧再经过AXI4-Stream接口送到DMA由DMA直接搬运到DDR指定的缓冲区里整个过程CPU不参与拷贝。1.3 2.5G吞吐量的物理限制和实际目标很多人在定性能目标之前没有把单位搞清楚。2.5Gbps是线速也就是说物理链路上每秒传输2.5G个比特。但以太网帧有前导码、帧间隙、FCS校验这些开销另外8b/10b编码本身有20%的损耗——2.5G的串行速率实际对应的是等效2.0Gbps的MAC层有效数据率也就是大约250MB/s。所以当我说实现2.5G吞吐量的时候严谨一点是物理链路跑在2.5Gbps帧接收速率达到接近于线速的水平。DMA搬运能力、DDR带宽、中断处理效率、缓存一致性维护这些方面都会影响最终的数字。你别看250MB/s对DDR4来说连个零头都不到但数据从PHY进FPGA再到DDR中间经过的每一级缓冲、每一个握手信号都可能是绊脚石。我用Iperf实测时发现在配置合理的情况下TCP吞吐量能做到大约2.3GbpsUDP单向能做到2.38Gbps左右这个数字基本已经接近MAC层开销压完之后的极限。如果还想再往上挤就得从帧长度、中断聚合、DDR通道分配这些细节去抠。2. 配置要点Vivado里把这些选项勾对2.1 创建IP时的关键选项在Vivado IP Catalog里搜AXI Ethernet Subsystem双击进入配置界面这个界面其实决定了很多后续硬件的走向。第一个要注意的是Component Name这个无所谓但建议起一个有意义的名字比如axi_eth_2g5方便后期在约束和软件里识别。PHY Interface这个选项是重头戏。如果你的板载PHY是SGMII接口比如Marvell的88E1512这类选SGMII。如果是要直接接SFP光模块通常选1000BASE-X。为什么这两个选项会直接影响2.5G因为SGMII在物理层支持速率协商它可以通过寄存器把速率拉高到2.5G而1000BASE-X是光纤以太网的一种标准速率但是PCS层允许更高的速率运行。实际做下来1000BASE-X配合外部SFP在2.5G下工作得反而比SGMII更稳定原因是SGMII的AN机制在某些PHY上看到2.5G不一定协商得出来。顶层选择FIFO还是DMA这是一个分水岭。早期我图省事选了Internal FIFO结果吞吐量卡在1.2G上不去。FIFO模式适合测试或者低速场合数据从MAC进入FIFO之后还需要ARM核通过AXI总线一个字节一个字节地读这种设计在2.5G的高速率下完全是瓶颈。做2.5G一定要选DMA模式外部接AXI DMA。还需要留意Enable PCS/PMA这个选项。如果你的2.5G PHY是纯物理层芯片和FPGA之间只走SGMII信号那么这个选项必须勾上因为IP内部要承担PCS编码工作。如果PHY芯片已经包含了PCS比如某些自适应的PHY可以关掉PCS/PMA让PHY输出GMII/RGMII给FPGA。但GMII接口在IO数量上不划算所以主流方案还是SGMII加上内部的PCS/PMA。2.2 2.5G的时钟树规划AXI Ethernet Subsystem的时钟配置并不复杂但极易出错。你打开Address Editor和Clock Configuration会看到需要分配至少三组时钟。第一组是MAC时钟通常叫gtx_clk。对SGMII 1G来说这个时钟是125MHz但跑2.5G的时候串行速率变成了2.5Gbps而SGMII是DDR采样所以接口时钟需要变成312.5MHz。很多人习惯性地沿用千兆的125MHz结果链路死活不亮或者吞吐量只有1G。第二组是AXI4-Lite接口时钟一般叫s_axi_lite_clk用来配置IP内部的寄存器。这个时钟频率不需要太高100MHz就够。它和以太网的数据通道没有直接关系但是建议和PS的FCLK_CLK0保持一致省得跨时钟域出幺蛾子。第三组是AXI4-Stream时钟也就是和DMA对接的时钟。这组时钟基本可以跟着GTX参考时钟走我通常直接给它配成250MHz或者200MHz。DMA搬数据的速率上限取决于这个时钟频率乘以位宽。规划时钟的时候一定要打开Vivado的Clocking Wizard单独生成一个不要用PS的默认时钟去乱接。2.5G的GTX参考时钟一般需要一个高品质的差分时钟源频率常见的是125MHz因为SGMII 2.5G的参考时钟可以倍频到312.5M。如果你选择的板卡上有可编程时钟芯片记得先通过I2C初始化到正确频率。这一步漏了后面怎么调都不通。2.3 地址空间与中断的分配AXI Ethernet Subsystem和AXI DMA是独立的中断源。需要在Vivado里分别把axi_eth_2g5的interrupt和axi_dma_0的interrupt连到PS的PL-PS中断端口上。Zynq UltraScale上的IRQ编号是从121开始的接上去之后在设备树里要写对差一个数字中断就进不来。另外不要忘了MDIO接口。AXI Ethernet Subsystem支持MDIO管理外部PHY但如果你的PHY是通过I2C或者SPI管理可以把这个接口悬空。我遇到过一个奇葩问题MDIO引脚没约束导致IO电平冲突整个IP的寄存器读出来全是0xFF。后来检查约束才发现MDIO漏了。2.4 AXI DMA的关键配置AXI DMA配置界面里有几个参数和吞吐量强相关Address Width一般选64配合64位AXI数据总线。如果选32数据带宽减半2.5G吞吐量大概率上不去。Burst Size选16。Xilinx文档里说AXI DMA最高支持256拍的burst但实测中过大的burst会挤占DDR带宽影响其他master的访问。16拍是一个平衡点。Stream IP选AXI Ethernet相关选项这样DMA会按照以太网包的格式解析。如果选Generic那么DMA只会傻傻地按长度搬运以太网帧的实际长度需要用户自己去分析编程会麻烦很多。Enable Micro DMA这个选项别勾。Micro DMA面向小包优化2.5G下的以太网包普遍超过512字节它反而会成为负担。3. 软件侧的配合驱动、描述符与缓存一致性3.1 内核驱动与设备树怎么写Vivado导出硬件之后需要有对应的设备树描述。如果你用的是PetaLinux可以跑petalinux-config里边的auto硬件配置但建议还是手动检查一段设备树关键内容axi_eth_2g5: axi_etha0040000 { compatible xlnx,axi-ethernet-1.00.a; reg 0x0 0xa0040000 0x0 0x40000; xlnx,phy-type 0x5; /* 对应SGMII 2.5G */ xlnx,has-mtr 0x1; xlnx,rxcsum 0x2; xlnx,txcsum 0x2; local-mac-address [00 0a 35 00 11 22]; phy-handle phy0; }; axi_dma_0: dmaa0030000 { compatible xlnx,axi-dma-1.00.a; reg 0x0 0xa0030000 0x0 0x10000; dma-channel ...; interrupt-parent intc; interrupts 0 56 4 0 57 4; };xlnx,phy-type是定义PHY接口类型的不同版本的内核里值不一样一定要和Vivado生成的xparameters.h对应上。驱动加载的时候主要靠这一句来判断接口是SGMII还是RGMII。3.2 DMA描述符的分配与缓存一致性这一部分是吞吐量能不能稳住的命门。AXI DMA使用环形描述符表来管理数据搬运。每个描述符对应一个缓冲区缓冲区地址、字节数、状态都由描述符标识。在Linux里DMA缓冲区最好是使用dma_alloc_coherent分配保证物理地址连续并且缓存属性是non-cached。我最早偷懒用了kmalloc分配结果发现每次收包之后CPU读到的数据总是旧数据或者数据是乱的。原因就是DMA把数据写进DDR之后CPU的Cache里还留着之前的内容形成了cache aliasing。折腾了很久加了dma_map_single和dma_sync_single_for_cpu才解决。如果直接用dma_alloc_coherent这些同步麻烦从一开始就不存在。描述符的数量对吞吐量也有影响。描述符太少DMA来不及提交新缓冲网络包只能被MAC丢弃。描述符太多内存占用大而且Cache Miss严重。我实测下来128个描述符是比较合适的值对应每描述符缓冲区2KB总共256KB的内存足够应付2.5G的线速。3.3 中断处理与NAPI调优Zynq UltraScale运行Linux系统时如果频繁中断CPU会大量消耗在上下文的切换上吞吐量会骤降。某个版本的驱动默认在每次完成一次中断后立即调度softirq小包环境下能跑到1G但大包2.5G情况下CPU占用率直接爆表。解决办法是启用NAPI把收包过程改成轮询模式。具体来说在设备树里给axi_eth_2g5增加一个属性让驱动注册NAPI回调而不是纯粹的IRQ处理。NAPI的weight参数我通常设成64。收包时中断触发后驱动关闭中断然后NAPI开始轮询接收队列直到队列为空或者达到预算才重新开启中断。这样一次中断就能处理多包CPU占用率从原来的80%多降到30%左右。还有个隐蔽的坑是gro和lro的处理。在设备树里如果启用了gro大包收下来之后会被内核聚合后再交给上层协议栈TCP窗口利用率提高了吞吐量大概能再涨个3%-5%。但要注意有的老内核里gro和DMA的csum offload有兼容问题可能导致校验和错误。打开之前先看一下ethtool -k eth0的输出确认rx-checksumming已经打开。3.4 速率协商与autonegSGMII 2.5G模式在没有对端2.5G设备的时候需要关闭auto-negotiation强制设为2.5G。这个操作既可以在PHY侧做也可以借助ethtool在Linux下操作。我一般在驱动程序里通过MDIO写PHY寄存器把ANEN位置0然后在Phy Control Register里设速度位。不同PHY芯片的寄存器布局不完全一样需要查datasheet。如果你用的是SFP光模块很多时候没有标准的PHY寄存器可以直接控制就得写一个简单的mdio-bus驱动或者直接在IP内部的PCS寄存器里操作。这个环节遇到最多的问题是链路状态显示up但是流量只有1G。原因在于对端交换机或者网卡是1G/10G自适应的而AXI Ethernet Subsystem的PCS没有正确把2.5G能力广播出来。解决办法是如果对端是固定2.5G的网卡干脆就不要AN直接强制模式。4. 调通只是第一步常见问题与性能排查实录4.1 速率为啥只有1G2.5G的PCS为何不工作这是我在实际项目中碰到的第一个拦路虎。系统启动之后ethtool eth0看到的速度是1000Mb/s怎么都上不到2500Mb/s。排查思路是这样先确认PL侧的GTX收发器是不是跑在2.5G频率。可以通过ILA抓取IP输出的tx_disable、reset_done信号看看状态如果reset_done一直不拉高多半是GT参考时钟没有起来或者是GT Quad没有正确配置。另一个原因是PHY芯片在boot时通过strap引脚配置了默认速率。有些PHY的默认是千兆模式需要通过MDIO写寄存器才能进入2.5G模式。我遇到过一款PHY需要额外配置一个速率模式寄存器而该寄存器的地址不在标准IEEE寄存器集合里走了很多弯路才从datasheet里翻出来。再一个高频坑是SGMII固定速率的问题。很多PHY芯片把SGMII理解成只有1G速率即使物理层已经能跑2.5G但是PHY的SGMII侧还不支持312.5MHz的DDR时钟。解决办法是检查PHY是否支持2.5G SGMII如果支持通常有一个专用的2.5G base-T mode寄存器需要设置。如果PHY不支持只能换用1000BASE-X界面或者换PHY。4.2 DMA reset不过来描述符状态异常Linux驱动加载之后DMA初始化阶段偶尔会卡在reset状态出不来。最常见的原因是DMA的S2MM通道和MM2S通道的复位时序不对。AXI DMA有一个内部状态机需要先把DMA的复位引脚拉低等待至少8个时钟周期再拉高。在驱动的初始化函数里不能简单地写一个寄存器就继续往下走要加入等待机制轮询DMA的DMACSR寄存器的reset bit直到硬件完成复位。如果没有这个等待后面配置描述符的时候很容易遇到状态错乱。另外描述符的状态位是DMA硬件写、软件清。软件在初始化描述符的时候必须把状态清零。如果软件之前没有正确释放描述符DMA硬件会认为描述符还在使用中永远不搬下一包数据。我在中断处理完一个描述符之后会显式地把状态字清零并加dma_wmb()再更新tail指针。漏掉写屏障在高频率下会出现偶发的丢包。4.3 吞吐量卡在1.8G到2.1G瓶颈究竟在哪如果你发现链路已经协商到2.5G但是实际吞吐量总在2G上下晃悠不够接近理论线速那就要分环节排查了。首先看CPU占用率。如果收包时CPU占用接近100%说明中断或者软件处理跟不上我把这类问题叫CPU-bound。措施是启用NAPI增加描述符数量检查是否有其他高优先级中断抢占以太网中断。如果CPU占用不高但是吞吐量依然上不去再看DDR带宽。Zynq UltraScale的DDR控制器虽然带宽很大但是如果DMA的burst过小、或者DMA通道和其他高带宽外设抢DDR通道效率会明显降低。可以在Vivado里打开DDRC的QoS配置给AXI DMA分配较高的优先级。还有一种情况是缓存一致性问题造成的吞吐量假低。如果接收缓冲区使用Cacheable属性但没有正确执行Cache清理操作DMA看到的数据和CPU看到的数据永远不一致这时驱动会反复重传或者丢包。用perf看LLC miss比例如果异常高基本可以确定是缓存问题。最后是帧长度的影响。2.5G下如果测试用的小包64字节帧间隙和前导码的开销占比太高无论怎么调都到不了940Mbps级别的效率。对于2.5G建议用MTU 9000的巨型帧来测吞吐上限。在Linux里设ip link set eth0 mtu 9000同时确保对端也支持巨型帧否则大包会被分片性能反而下降。4.4 高负载丢包的排查方向当流量达到一定速率后开始出现RX丢包这通常和描述符耗尽有关。通过ethtool -S eth0能看到rx_dropped计数。如果rx_dropped急剧增加有两个方向去查。一个是DMA的缓冲区不够。每个描述符对应的缓冲长度如果小于实际收到的帧长度DMA会截断或者丢弃。要在驱动初始化时配置描述符对应的缓冲大小至少为MTU14以太网头4 CRC4 VLAN标签我一般按MTU64字节来分配留够余量。另一个是DMA发生FIFO溢出。AXI Ethernet Subsystem内部的RX FIFO深度是可以通过Vivado配置的如果FIFO深度太小而DMA处理速度跟不上数据就会在这里丢掉。把RX FIFO深度加大同时在Vivado的Address Editor里确认FIFO没有和别的外设映射冲突再重新综合。还有一类丢包比较隐蔽是在Linux协议栈层面。net.core.rmem_max和net.core.netdev_max_backlog默认值对高速网络来说偏小。在/etc/sysctl.conf里把backlog增大到10000以上把rmem_max调整到16MB以上通常能缓解一些软件丢包问题。5. 小结之外的一些体感整个项目做下来最大的感受是Zynq UltraScale的2.5G方案并不是配置完就完事而是一个软硬件协同的工程。Vivado里把IP勾对只是走出了第一步真正决定最终性能的是时钟、DMA、缓存、中断这四者的配合。尤其是缓存一致性和NAPI这两块几乎决定了你能跑出1.8G还是2.4G。如果你也想在这个方向上动手我给的建议是先别急着上Linux可以先在裸机环境里把DMA的环回测试跑通确认DMA能够在2.5G线速下稳定搬运数据。裸机测试稳定了再引入Linux把问题分层隔离调试起来会快得多。遇到吞吐量上不去的时候不要先从硬件开始怀疑先看看是不是中断抛得太频繁或者Cache属性没设对——这两处出问题的概率比硬件本身大得多。最后再分享一个调参的小技巧调试2.5G时多在/proc/interrupts里观察中断分布如果发现中断集中在某个CPU核上用smp_affinity把以太网中断绑定到其他空闲核有时能凭空多出300Mbps的吞吐量。这个操作简单但非常有效尤其在双核或者四核处理器上值得一试。
返回列表