拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于AXI Ethernet Subsystem的2.5G网络加速:从Vivado配置到Linux调优实战

我最早的 2.5G 网络加速项目是在一块 Zynq UltraScale 板上做的接了颗 2.5G PHY逻辑里放了 AXI Ethernet Subsystem想当然配完以后发现线缆两端协商出来的速度只有 1G就算手动强制 2.5Giperf3 实测也才 900M 多一点。后来把 Block Design 里每个参数抠了一遍又把 Linux 设备树和 DMA 描述符环调了一轮才真正跑到接近线速。这篇东西就是那段时间踩坑换来的配置心得围绕“用 AXI Ethernet Subsystem 实现 2.5G 吞吐量”这件事把选型、Vivado 配置、Linux 驱动配合、测试方法和常见翻车点一次讲清楚。1. 2.5G 先算账IP 边界、线路速率与数据通路选型1.1 2.5Gbps 到底意味着多少数据量很多人一看“2.5G 以太网”第一反应是“比千兆快 2.5 倍”这没错但落到 DMA、DDR、中断这些子系统上必须先把它换成一个工程上能核算的数字。2.5Gbps 的原始比特率是每秒 2.5×10^9 比特折算下来大约 312.5MB/s。这个数字决定了后面所有总线、缓存、DMA 描述符的预算。举个例子一条 64bit 100MHz 的 AXI 总线理论带宽是 800MB/s看起来比 312.5MB/s 大不少但实际情况下会有读写混合、地址切换、响应延迟再加上 DDR 的刷新和仲裁开销留的余量并没有想象中那么宽敞。还有一层的账必须算明白以太网线速和有效吞吐不是一回事。每个以太网帧在物理线路上占用的时间除了帧本身以外还包含 7 字节前导码、1 字节帧起始符、12 字节帧间隙。以 1518 字节的标准帧为例线路上实际传输一个 MAC 帧相当于占 1538 字节而里面我们能拿来跑业务的 IP payload 只有 1500 字节。所以理论最大有效吞吐是1500MTU、UDP约 2.438Gbps1500MTU、TCP去掉 40 字节的 IPTCP 头约 2.373Gbps9000 字节巨型帧、UDP约 2.489Gbps9000 字节巨型帧、TCP约 2.478Gbps这就是为什么要做 2.5G 网络加速时几乎所有人都会把 MTU 调到 9000。不调巨型帧你费半天劲配 DMA、配中断最高上限也就在 2.44G 左右调了巨型帧能把有效吞吐顶到接近 2.49G多出来的这 50Mbps 在长期跑业务时非常可观。1.2 为什么必须用 AXI Ethernet Subsystem 而不是 PS 的 GEMZynq UltraScale 的 PS 部分自带多个 GEMGigabit Ethernet MAC但绝大多数 PS GEM 的对外接口是 RGMII 或 SGMII速率上限就是 1Gbps。想往 2.5Gbps 走最干净的办法就是放弃 PS GEM改到 PL 里挂一个 AXI Ethernet Subsystem。AXI Ethernet Subsystem 是 Xilinx/AMD 在 Vivado 里提供的集成以太网 MAC IP它继承了老的 Tri-Mode Ethernet MACTEMAC的位置核心能力是提供 10/100/1000Mbps并且在支持 SGMII/2500BASE-X 路径的版本里可以跑到 2.5Gbps。具体到 Vivado 里配置时不能选 RGMIIRGMII 是肯定上不了 2.5G 的要选 SGMII 或 2500BASE-X 这类经过 GT 收发器的接口再接一颗支持 2.5G 速率的外部 PHY。这句话我加粗一下不要指望 PS 侧的硬核以太网解决 2.5G2.5G 要求 MAC 和 PHY 之间的 SerDes 速率直接翻倍Xilinx 把它落在 PL 的 GT 上。换句话说跑 2.5G 的方案本质上是“PL MAC GT 外部 PHY”必须确认板子上的 GT 参考时钟、PHY 型号、以及 PCB 走线全部支持 2.5G否则 IP 配置再多也是白搭。1.3 数据通路选型DMA、FIFO 直通还是自定义 StreamAXI Ethernet Subsystem 的数据接口是 AXI4-Stream但下游怎么接决定了这个方案的吞吐天花板和 CPU 负载。我见过三种典型接法数据通路适用场景优点缺点AXI DMAScatter GatherCPU 收包、组包、跑协议栈与 Linux 网络栈天然配合缓冲管理灵活有描述符和中断开销AXIS FIFO / AXIS Switch 直通FPGA 内做纯转发、过滤、重定向延迟低、不占 DDR 带宽无法直接进协议栈需要逻辑处理PL 内自定义 AXIS 处理模块需要硬件卸载、多端口汇聚吞吐可控线性扩展开发量大调试难我在这类项目里默认选 AXI DMA Scatter Gather。原因很简单网络加速的绝大部分实际需求最终还是要让 CPU 能看到数据包或者要经过 Linux 协议栈做分发。AXI DMA 的 SG 模式可以不连续物理内存组织多个缓冲描述符由硬件自动搬运CPU 只需要在中断里处理收尾。对 2.5G 这个量级单核 CPU 配合 NAPI 和合理的中断合并是完全扛得住的没必要一开始就上复杂的硬件卸载引擎。如果确认你的业务就是 FPGA 内部两个端口之间做线速转发不想让 CPU 参与那可以直接 AXI Ethernet Subsystem 的 RX → AXIS Switch → FIFO → TX省掉 DMA 和 DDR延迟低到微秒级。但这个方案不具备“可编程”后续想加过滤、加统计就只能在逻辑里写。总之先把需求想清楚再选通路数据通路选错是整个项目最大的返工来源。2. Block Design 内逐项参数精调AXI Ethernet 和 DMA 的配合细节2.1 按这个顺序把七个部分组装起来用 Vivado Block Design 做 2.5G 网络加速新手最容易犯的错是一上来就拖 IP哪个缺了再补。我的习惯是严格按以下几个层次往上搭先加 Zynq UltraScale PS把 PS-PL 接口、PL 时钟、中断口都打开。加 AXI Ethernet Subsystem先把 MAC 和 GT 部分定下来。加 AXI DMA选择 SG 模式并指定数据宽度。加 AXI Interconnect连接 PL 侧 DMA 的 Master/Slave 口到 PS 的 DDR 通路。加 Clocking Wizard 和 Processor System Reset把所有时钟域和复位域理清爽。加 Concat把 DMA 的 TX/RX 中断和 MAC 的中断汇总到 GIC。有必要的话加 AXI Performance Monitor把 DDR 侧带宽和 DMA 侧带宽同时监控起来。这个顺序能保证你每次新增 IP 时连接关系都是清晰的不会出现某个口没接、时钟域乱套的问题。尤其是复位PL 里的 AXI 外设复位如果没接对经常会出现“看起来配置没问题一跑 DMA 就超时”这种玄学故障。2.2 AXI Ethernet Subsystem 参数速查表不同 Vivado 版本的 IP 弹窗细节会有差别但核心参数就那么几个我列一个实战向的清单Speed/接口类型必须选 SGMII 或者 2500BASE-X 这一类可以走 GT 的接口。如果选 RGMII硬上限就是 1Gbps直接否掉。Speed 范围有 10/100/1000/2500Mbps 可选的话选全速率方便调试时先降速排查。AXI Data Width建议设成 64bit。32bit 在 2.5G 线速下虽然理论也够但在中断和描述符处理时会放大 CPU 压力64bit 更稳妥。FIFO 深度AXI Ethernet Subsystem 内部有收发 FIFO影响的是 MAC 和 DMA 之间吸收突发的能力。跑 9000 字节巨型帧的时候FIFO 深度建议尽量开大否则大帧容易背压导致丢包。支持巨型帧MAC 侧必须把最大以太网帧长放开到 9600 或更高前缀我记得是按“帧长”参数配置的默认 1518 字节。不开这个选项哪怕 Linux 侧配了 MTU 9000硬件在入口处就把大帧丢掉了。MDIO 接口打开。调试 PHY 时 ethtool 和 mii-tool 都需要通过 MDIO 访问 PHY 寄存器。参考时钟GT 一侧的 refclk 频率要按 PHY 的 2.5G SerDes 要求来。常见的是 125MHz 或 156.25MHz也有 312.5MHz 的配置具体得查你所选 PHY 的手册和收发器向导的要求。这个时钟必须干净建议走专用参考时钟引脚不要图省事从 PS 的普通 PL 时钟拉。2.3 AXI DMA 与 Interconnect 的参数配合AXI DMA 是整个方案的“搬运工”参数直接决定吞吐。我常用的配置如下Scatter Gather 模式必须选 SG而不是 Micro DMA。Micro DMA 适合单缓冲场景做网卡这种多包任务会非常痛苦。数据宽度MM 侧和 Stream 侧都设成 64bit这样和 AXI Ethernet Subsystem 的 Stream 口对齐避免跨宽度转换带来的额外开销和不必要的地址对齐问题。描述符数量AXI DMA 的 SG 引擎会用一段内存做描述符环常见选项是 8、16、32、64 条。2.5Gbps 下我建议至少 64 条以上128 条更好。描述符少了在突发大流量时描述符会被耗尽DMA 只能暂停等待软件补充缓冲区吞吐自然掉。Buffer Length Register 配置DMA 的缓冲长度必须覆盖最大帧长。如果开了 9000 字节巨型帧软件侧分配的 RX buffer 也要大于等于 9000否则大帧只能被截断或丢弃。Max Burst如果没有特殊限制突发长度按 16 或 32 是合理的。太大会增加总线占用和缓存压力太小会把一次大帧拆成很多笔小事务拉低带宽利用率。AXI Interconnect 的参数也别忘了动数据宽度要和 DMA 保持一致别出现 DMA 是 64bit、Interconnect 内部硬生生切成 32bit 的情况。另外如果 DDR 访问要走 SMMU/IOMMUInterconnect 的地址位宽要覆盖完整的物理地址空间否则 DMA 只能访问低 32bit 地址内存一多就出错。2.4 时钟、复位和中断的接线要点接时钟时我建议分三路去理AXI-Lite 配置时钟、AXI4-Stream 数据时钟、GT 参考时钟。AXI-Lite 通常用 100MHz走 PS 的 PL 时钟即可数据通路建议和以太网字节时钟同步用 125MHz 或更高GT 参考时钟必须由专用引脚进收发器不能和普通逻辑混在一起。复位这块务必用 Processor System Reset IP 统一生成不要在 Block Design 里手动拉 high 再拉 low。AXI 外设对复位时序很敏感多个外设复位不同步就会出现 DMA 描述符更新到一半被重置的奇怪问题。我踩过一次MAC 已经起来PHY 也协商到 2.5G但 DMA 一直报 busy查了半天是 DMA 的复位被外部逻辑直接按住没释放。中断接法也有一套标准动作把 AXI DMA 的 TX 和 RX 中断、以及 AXI Ethernet Subsystem 的如果有独立中断都用 Concat 汇总连到 Zynq UltraScale PS 的 pl_ps_irq0/pl_ps_irq1 上。Linux 设备树里对应的 interrupt 编号必须和 Vivado 里实际接的 GIC 中断号一致这个编号对不上驱动 probe 都会失败。3. Linux 侧数据通路铺垫设备树、Cache 一致性与中断合并3.1 设备树节点让 Linux 认出这个 2.5G 口Block Design 生成硬件之后下一步是设备树。Xilinx 自带设备树生成插件能出一版基础节点但很多参数需要手动确认。一个典型的 AXI Ethernet Subsystem 节点长这样axi_ethernet_0: etherneta0000000 { compatible xlnx,axi-ethernet-1.00.a; reg 0x0 0xa0000000 0x0 0x40000; interrupt-parent gic; interrupts 0 45 4, 0 46 4; local-mac-address [00 0a 35 00 00 00]; xlnx,has-mdio 0x1; phy-mode sgmii; phy-handle phy0; mdio { #address-cells 1; #size-cells 0; phy0: ethernet-phy0 { reg 0; }; }; };这里几个点必须和生产环境对上phy-mode如果 Block Design 里配的是 2500BASE-X 模式设备树里很可能要写phy-mode 2500base-x或类似值而不是sgmii。具体以内核支持的 PHY interface 定义为准。有些内核版本还需要加max-speed 2500告诉 MAC/PHY 不要自动协商到 1G 以下。中断号interrupts 0 45 4中的 45 是 GIC SPI 编号对应 Vivado 里 pl_ps_irq 实际接到的那一路。每个板子不一样一定要对着生成的 xsa 或者设备树自动生成结果核对。reg 地址a0000000只是示例必须和你 Block Design 里给 AXI Ethernet Subsystem 分配的地址一致。这个地址错了驱动读写寄存器全落到空地上自然起不来。3.2 描述符数量和缓存一致性是吞吐的隐形天花板Linux 跑 AXI DMA 时描述符环和 packet buffer 都分配在内存里。这里有两个坑一个是描述符数量太少另一个是缓存一致性问题。先讲描述符。AXI DMA 的 SG 模式里描述符环在软件和硬件之间共享。要么把描述符深度调大、通过多包中断合并来减少 CPU 参与要么把 ring 设得很小、频繁唤醒 CPU 处理。实测下来2.5G 线速场景描述符少于 32 条UDP 小包很容易丢。我一般从 128 条开始调如果不做深度业务还可以考虑用 veth 类似方式把包处理批量抬到 NAPI 的 poll 里。再说缓存一致性。Zynq UltraScale 上 DMA 访问内存默认走的路径可能涉及 SMMU/IOMMU而 CPU 和 DMA 对 cache 的理解不一致就会出现 DMA 看到的数据和 CPU 写下去的数据不一样。解决办法是设备树里给 DMA 通道或以太网节点加上dma-coherent;并且驱动里用dma_alloc_coherent或dma_pool来分配 buffer让硬件和软件访问同一份一致性内存。如果代码里图省事用普通kmalloc再手动 flush cache吞吐一大就会随机出坏包排查起来非常痛苦。一句话总结描述符管的是“够不够快”cache 一致性管的是“对不对”。先保证对再谈快。3.3 中断合并与 NAPI 轮询的取舍2.5Gbps 跑满时如果全是 1500 字节小包每秒大约 20 万个包。这么多包每个都触发一次中断CPU 基本就别干别的了。这时候中断合并和 NAPI 就是必选项。Xilinx AXI Ethernet 的 Linux 驱动走的是 NAPI 框架中断触发后不是马上逐包收而是关中断、进入 poll 循环批量收包直到收完再开中断。这个机制在千兆时代都很有必要2.5G 下更是救命稻草。正常情况下驱动会自动跑 NAPI但你要确认设备树里没有错误地把interrupt模式搞成强制轮询或强制中断。还可以用ethtool -C eth0 rx-usecs 32 rx-frames 8这类命令微调合并阈值。合并太激进会增大延迟合并太松会导致中断风暴。如果项目对延迟和吞吐都有要求我的调法是从rx-usecs 16开始逐步往上加观察吞吐和 CPU 占用率的变化找一个平衡点。另外别忘了用taskset把网卡中断绑定到一个专用 CPU 核上避免中断在两个核之间来回迁移迁移一次要冲刷 TLB 和 cache对高 pps 场景影响非常大。4. 把 2.5G 真实测出来回环设计、iperf3 与带宽拆解4.1 测试拓扑先证明数据路径再证明物理链路很多人一上来就拿两台设备隔着交换机对打一旦丢包根本分不清是 FPGA 逻辑问题、PHY 问题还是网线问题。正确顺序是内部回环在 AXI Ethernet Subsystem 寄存器层面开 MAC 环回让 TX 的数据直接回到 RX。这时候不经过外部 PHY也不经过网线只要软件发送、接收逻辑没问题吞吐应该立刻跑到接近线速。这一步能验证 Block Design 和驱动的全链路。PHY 回环把环回点从 MAC 挪到 PHY通过 MDIO 写 PHY 寄存器开 loopback。此时验证的是 PL 到 PHY 的 SerDes 链路。线缆直连关掉所有环回两台设备通过 2.5G 网线直连验证物理层协商和实际传输。这个顺序看起来慢实际是最快的。内部回环一旦跑出 2.4Gbps基本可以排除逻辑设计问题再往下出问题就是 PHY、时钟或线缆的事排查范围一下子就收窄了。如果板子上没有第二个 2.5G 口也可以用 FPGA 内部 AXIS 直接把 TX 侧数据捕回来灌到 RX 侧相当于做一个硬件环回。ILI 或者 ChipScope 能在这个环回链路上看到实际在跑的 stream。4.2 iperf3 命令与预期值判断Linux 里最常用的验证工具是 iperf3但也要注意它的单线程瓶颈。跑 2.5G 时建议多开几个流# 服务端 iperf3 -s # 客户端多线程 TCP iperf3 -c 192.168.100.2 -t 60 -P 4 # 客户端UDP 收发包注意 -b 要大于线速才能测出上限 iperf3 -c 192.168.100.2 -t 60 -u -b 2.6G -l 1400 # 调成巨型帧之后MTU 先改 ip link set dev eth0 mtu 9000判断结果时不要把“2.5G”当作及格线。按前面 1.1 节的账1518 帧下 UDP 上限是 2.44G 左右TCP 还要更低。所以场景理论最大有效吞吐实测达到值建议1500MTUUDP2.438 Gbps4 个流以上能稳定 2.35G 以上就算合格1500MTUTCP2.373 Gbps2.3G 以上属正常9000MTUUDP2.489 Gbps2.45G 以上再谈优化9000MTUTCP2.478 Gbps2.4G 以上符合预期如果 UDP 掉包率高于 0.01%就要去查 DMA 描述符是否耗尽、CPU 是否跑满、FIFO 是否背压。如果是 TCP 掉速先看单核 CPU 是不是跑到 100%再看重传率。4.3 数据链路分层拆解MAC、DMA、内存各自的状态怎么看吞吐不达标时按以下顺序逐层确认MAC/PHY 层用ethtool eth0看 link speed 是不是 2500Mb/sethtool -S eth0看 rx_crc_errors、rx_frame_errors、tx_bytes、rx_bytes。CRC 错误多大概率是 SerDes 或时钟问题只有速率协商成 1000Mb/s就去查 phy-mode、PHY 的 2.5G 能力和网线。DMA 层读 DMA 寄存器里的 completed descriptor 计数或直接看/proc/interrupts里对应中断的触发次数。如果中断次数成百万级/秒说明中断处理占用过高如果 0 个包也中断很多那更要查驱动状态机。内存层用 AXI Performance Monitor 挂在 DMA 到 DDR 的通路上看实际带宽。很多板子 DDR 带宽看着很高但仲裁策略、page miss 一多实际可用带宽只有理论值的一半。把 APM 的读/写带宽数值和 iperf3 的结果对齐能很清楚地看出瓶颈在哪一层。5. 调试现场最常踩的六个坑及处理手册5.1 协商永远停在 1G最典型的现象PHY 明明支持 2.5G但 ethtool 显示 1000Mb/s。优先排查顺序是phy-mode是否正确2500BASE-X 模式下用sgmii描述协商结果大概率不对。PHY 是否真的支持 2.5G好多“千兆 PHY”是不支持 2.5G 的光看百兆千兆标称没用。GT 参考时钟频率是否和 PHY 期望一致SerDes 跑 2.5G 的速率和跑 1G 的速率对时钟要求完全不同。网线是否达标2.5G 对线缆质量有要求旧线缆导致降速很常见。5.2 中断风暴CPU 被打满症状是吞吐不高但 CPU 占用接近 100%。先cat /proc/interrupts看中断次数如果每秒几十万次基本就是中断合并没生效。检查设备树驱动是否触发 NAPI、是否有人在应用层把中断 CPU 亲和性设乱。必要时在 FPGA 里把 DMA 中断合并阈值调高或在 Linux 里用ethtool -C调合并参数。5.3 RX 丢包DMA 描述符被耗光用ethtool -S看到 rx_dropped 飚升多半是软件来不及回收 buffer或者描述符环太浅。解决办法是把描述符数量从 32 提到 128同时确保应用层及时读socket不要让协议栈背压。注意如果 DMA 缓冲长度小于 9000也会出现“大帧进不来”的丢包这个最隐蔽。5.4 时序问题GT 通道锁不住或者偶尔 link down2.5G SerDes 对电源纹波、时钟 jitter、PCB 走线长度差都很敏感。如果 link 能上来但跑几秒钟就掉优先抓 GT RX 的误码率。软件上能做的有限确认参考时钟没有用普通 PL 时钟硬顶确认 PCB 上 GT 走线没有跨分割确认 PHY 的电源去耦电容足够。Vivado 里看 transceiver 的 CDR 锁定状态是很好的判断手段。5.5 驱动 probe 成功但收不到包这类问题最常见的原因是地址映射错误。AXI Ethernet Subsystem 的寄存器和 DMA 的寄存器基地址如果在设备树里写错驱动固然能 probe因为寄存器里有些字段是通配的但真正收包时 DMA 描述符指针飞到奇怪地址直接 hypervisor 或 IOMMU fault。应对方法是把 xsa 导入 Linux 设备树生成工具拿生成的 dts 和手工节点逐字段对比重点看 reg、interrupts、dma-coherent。5.6 Checksum Offload 没开CPU 都在算校验和Linux 默认对 xilinx 驱动开 checksum offload 的时机不完全一致。如果 TSO/GSO 没打开大量大包分段会让 CPU 消耗在计算校验和和分段上。可以执行ethtool -K eth0 tx-checksum-ipv4 on ethtool -K eth0 rx-checksum-ipv4 on ethtool -K eth0 tso on ethtool -K eth0 gso on如果驱动不支持再去查 AXI DMA 是否接了支持 checksum offload 的接口或者干脆接受 CPU 开销把更多核让给协议栈。这个优化看起来不起眼但在 2.5G 场景下往往就是压死 CPU 的最后一根稻草。最后再分享一个调试顺序上的心得说实话这种方案真正难的不是某一个参数配置而是整个链路的“木桶效应”。我后来每次新板子调 2.5G都会先固化一套流程内部回环验证数据通路 → PHY 环回验证 SerDes → iperf3 拉 UDP 看丢包 → 再切 TCP 看 CPU → 最后才开 checksum offload 和中断合并。只要某一步卡住就把该层所有寄存器状态拉出来看而不是盲目去调别的环节。这套方法帮我排除过 PHY 型号看错、GT 参考时钟接错、DMA 描述符深度不够、Linux 设备树中断号错位等一堆问题。希望这篇配置经验也能帮你少走几周弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门