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

FPGA网络通信从零到通:UDP收发与RGMII接口实战

在FPGA开发这条路上到了“网络通信”这个环节不少人是心里发怵的。因为一提到网络潜意识里想到的是TCP、TCP/IP协议栈、HTTP这些偏软件的东西似乎离硬件很远。但真实情况是FPGA做网络通信并不需要你去把TCP协议栈用Verilog写一遍甚至不需要真正搞懂三次握手。你要做的是把数据在正确的时间、用正确的格式从FPGA的引脚送出去或者从引脚收进来。我见过不少工程师学了大半年FPGA会把LED灯点得飞起UART收发也写了一到以太网就卡住其实就是被“网络”这两个字唬住了。这一篇作为系列的第7篇先给你吃一颗定心丸网络通信在FPGA里的本质是“按照规定好的字节格式做并转串和串转并”。FPGA做网络通信核心价值主要体现在三个方向一是直接用专用硬件逻辑把高速数据流打包进以太网帧带宽能做到线速不受CPU和操作系统干扰二是从网络上收到的数据可以在硬件层直接做处理过滤、解析、拼接而不用经过CPU三是延迟极低适合做工业控制、仪器测量和软件无线电这类对时间敏感的场景。这篇文章我会从零开始把一套最简单的UDP收发链路拆开讲清楚——以太网帧和RGMII接口、PHY芯片选型与硬件连接、用Vivado的三速以太网MAC核快速打通MAC层、自己写UDP组包逻辑、处理ARP应答最后附上完整的调试经验。1. 先想清楚FPGA在网络通信链路里的位置在动手敲代码之前有几件事必须想明白。很多初学者一上来就去找TCP/IP协议栈的Verilog实现或者纠结要不要在FPGA里跑一个软核CPU去处理网络数据这都是没搞清楚定位的表现。1.1 为什么不是“用FPGA写一个网卡驱动”FPGA里的网络通信和PC上的网卡驱动完全是两码事。PC网卡驱动面对的是操作系统有内存管理、中断、协议栈调度这些复杂的软件体系在FPGA里既不可能也没必要完整复现。FPGA面对的是一个很窄的问题把硬件产生或接收的数据以特定的字节序、速率和时序送进以太网物理层芯片PHY或者反过来。打个比方PC的网卡驱动像一个大型物流公司的调度中心要处理成千上万个包裹的进进出出而FPGA里的网络逻辑更像一条专用的自动分拣线它不考虑“这个包裹是哪个客户寄的”只关心“这个包裹按什么顺序放到传送带上”。这不是功能残缺而是物理世界的规则决定的——FPGA的逻辑资源、时序约束和时钟频率决定了它最适合做这种确定性很强的数据搬移任务。理解这一点之后你就会明白为什么很多FPGA网络方案都聚焦在UDP而不是TCP上UDP是无连接、无状态、无重传的天然适合硬件实现TCP有流控、窗口、重传、有序性这些有状态的内容纯逻辑做起来非常吃力。不过那是后话初学阶段完全可以从UDP走通。1.2 FPGA网络通信的典型应用场景想清楚“FPGA在链路里的位置”再看看实际应用中FPGA网络通信到底解决什么问题高速数据采集ADC采样数据经过FPGA处理后组包成UDP帧发到PC上位机只用Wireshark或一个简单的UDP接收脚本就能拿到数据带宽轻松跑到几百兆甚至千兆。图像传输工业相机通过FPGA读取的帧数据经过简单压缩或直接打包走网口传给显示端或存储端。板间互联两块FPGA板卡之间通过网线直连传输控制命令和高速数据流。接口转换与协议转换把串口、CAN、SPI等低速接口的汇聚数据封装成网络报文送到远端服务器。这些场景的共性是数据高速产生的且流式处理不需要复杂的操作系统介入。凡是你发现“用STM32或者ZYNQ里的ARM核来做网络太慢了CPU占用太高了”FPGA网络通信就派上用场了。1.3 这篇的目标边界在开始之前我们把目标限定在一个可完成的范围。读完这一篇你应该能做到理解以太网帧、RGMII接口的基本概念知道PHY芯片怎么选、怎么接到FPGA上用Vivado的TEMAC核生成一个可用的以太网MAC层自己写出UDP组包和ARP应答的Verilog代码用PC对FPGA做ping通、UDP收发成功。如果你已经能完成以上任务那么“FPGA网络通信设计”这扇门就算打开了后面是想上光纤WAN口、还是换SGMII、还是和DDR、PCIE配合做高速采集板那就都是下一步的事了。2. 动手前必须吃透的两个基础以太网帧与RGMII接口网络通信看似庞杂但作为FPGA工程师信息密度最高的两块就是以太网帧格式和RGMII接口时序。这两个搞明白你就已经打败了至少一半的入门者。2.1 以太网帧从7个0x55开始的字节流从发送方向看FPGA要把数据丢进网络最终呈现在PHY芯片MDI引脚上的是一串高速差分信号。但在MAC层看来它就是一帧一帧的字节流。Ethernet II帧结构如下从目的MAC到FCS这段是MAC层负责的核心区域。细节拆开看前导码Preamble7字节固定0x55接收方用它做时钟同步和位同步。帧起始定界符SFD1字节0xD5表示“后面的内容是正经帧内容”。目的MAC6字节例如PC网卡的MAC地址。如果是广播或多播分别有特殊值。源MAC6字节对PC来说这就是FPGA的MAC地址。类型字段2字节0x0800表示IPv40x0806表示ARP0x86DD表示IPv6。数据载荷从类型字段之后到FCS之前最少46字节。如果IP包加UDP头之后不足46字节需要填充。FCS4字节CRC32覆盖从目的MAC到数据末尾由发送方计算、接收方验证。这里必须强调一个初学者极易踩的坑前导码和SFD通常由MAC控制器比如TEMAC核在发送时自动生成。如果你自己用状态机在MAC客户侧组帧你只需要从目的MAC开始给数据不要自己往发送字节流前面加0x55否则会“前导码重复”对端的协议栈很可能直接丢包。另外一个基础常识是“线速”概念。千兆以太网物理层速率为1Gbps对应的是8位并行数据在125MHz时钟下逐字节发送。你可以简单推导125MHz × 8bit 1000Mbps正好是千兆。这也是后面理解RGMII接口为什么是125MHz时钟的关键。2.2 RGMII接口四组信号双向各两组RGMII的全称是Reduced Gigabit Media Independent Interface精简千兆媒体独立接口。它存在的意义很直接标准GMII接口需要8位数据线加若干控制信号引脚太多于是把数据线砍到4根代价是这4根线要在时钟的上升沿和下降沿各采一次数据即DDR方式。发送方向里FPGA到PHYTXD[3:0]4位数据线。TXC上升沿时给低4位下降沿时给高4位拼起来就是一个完整的8位数据。TX_CTL上升沿时表达TX_EN发送使能下降沿时表达TX_ERR发送错误标志平时TX_ERR拉低即可。TX_CLK由FPGA送给PHY的125MHz时钟千兆模式。接收方向里PHY到FPGARXD[3:0]PHY送给FPGA的4位数据线同样上下沿各4位。RX_CTL上升沿是RX_DV数据有效信号下降沿是RX_ERR错误标志。RX_CLK由PHY送给FPGA的125MHz时钟。最简单的理解方式RGMII就是一个DDR接口4bit位宽在双沿采样后等效于8bit位宽在单个时钟沿采样。你后续看Xilinx官方例程里的RGMII转GMII逻辑本质就是在做“DDR采样”和“单沿数据”之间的拼接转换。2.3 千兆、百兆与自适应锁定一种速率再谈其他RGMII接口物理上支持10/100/1000Mbps三种速率差异就体现在时钟频率上千兆125MHz百兆25MHz十兆2.5MHz。10/100M模式下实际上可以只用上升沿采样等效于SDR但RGMII的标准引脚定义没变。对初学者我的建议非常干脆锁定千兆别碰自适应。理由有三第一千兆模式下RGMII的时序约束逻辑清晰、资料最多学习曲线最平滑 第二你手里的开发板上的PHY芯片大概率默认千兆或者可通过strap引脚配成千兆 第三百兆意味着25MHz时钟、意味着你的FIFO深度、状态机速率预期全部要重新调一遍等千兆调通了再切百兆就是一次降速而已没必要反着来。实际项目中如果FPGA只是传输控制命令这种低频小流量数据选百兆模式能省一点功耗和温度但核心数据通路该千兆就千兆别在主链路上搞“低速省电”的小聪明。3. 硬件底子PHY芯片选型与原理图阶段的几个决定性问题网络通信不是纯软件活硬件选型和连接在起点上就决定了后面调试验证的难易。如果你用的是现成开发板请重点关注下面这些配置在板子上是怎么拉的如果你自己画板子这一节内容能帮你省下好几周时间。3.1 常见PHY芯片与开发板选型市面上FPGA开发板里出现频率最高的几款千兆PHY如下PHY芯片接口支持特点常见应用场景瑞昱RTL8211ERGMII经典老将价格低资料多国产板卡最爱大多入门级FPGA开发板微芯KSZ9031RNXRGMII/SGMIIMDIO寄存器设计清晰带增强的延迟控制各种核心板、工业级模块美满88E1512RGMII/SGMII可配成SGMII稳定性好中高端开发板和网卡模块博通BCM54210RGMII/SGMII性能强但寄存器复杂且配置锁紧原厂高速评估板选型原则我总结成一句话找“网上能搜到FPGA例程、开发板厂商有参考原理图、数据手册能正常免费下载”的芯片。不要为了追求某一项指标去选冷门PHY后面连个参考都没得抄。手里没有硬件的话可以先拿黑金AX7、正点原子达芬奇系列开发板的原理图做参考这些板子都有完整的网络例程。3.2 strap引脚、工作模式与MDIO原理图阶段就要确认的三件事PHY芯片不是接上电源和信号线就能跑它有好几个关键配置必须在硬件设计阶段定死。首先是接口模式。RGMII模式一般通过strap引脚上拉或下拉电阻来决定有的PHY在复位信号释放瞬间采样这些引脚的电平从而决定“我是工作在RGMII还是GMII还是SGMII模式”。如果你配置错了后面的数据哪怕逻辑完全正确依然不通。这个要仔细查数据手册找到“PHYCRS_CTRL”或类似名称的pin function table。其次是PHY地址。MDIO总线上可以挂多个PHY每个PHY有一个5位地址。很多芯片把地址引脚也做成了strap引脚默认可能是0x00或者0x04。你在FPGA里通过MDIO读写PHY寄存器之前必须知道地址是多少否则数据写不进去。然后是时钟延迟模式这是RGMII最容易出玄学问题的一点。RGMII规范对数据相对时钟的延迟定义得比较宽松不同PHY对此处理不同有的PHY内部自带延迟单元有的需要外部电阻设置有的需要MDIO寄存器配置。如果你忽略这个问题常见现象是链路偶尔通偶尔不通、或者热复位后失效。我见过最玄学的一个案子板子A通信稳定程序原封不动烧到板子B上就不稳定了。最后定位到是两批PHY芯片的版本不同默认的延迟模式不一致。所以原理图阶段就确认好clock delay的strap配置并把最终固定的用法写进设计说明里能避免后面很多“明明一模一样的板子为什么表现不同”的困惑。3.3 不需要过分担心的高速信号设计如果你的开发板只要跑100Mbps以下那LVCMOS信号随便连问题不大。但跑千兆时RGMII信号频率到达125MHzDDR双沿采样后等同一个250MHz的采样速率概念这时候布局布线就需要注意了RGMII数据线尽量等长组内误差控制在100mil以内TX_CLK和TX_CTL、TXD组尽量一起走避免过大的时钟偏斜PHY芯片下方铺地减少回流噪声串阻尽量靠近发送端放置阻值22Ω、33Ω这类是常见选择。大部分成熟开发板已经把这些处理好了你拿到的现成板子不用操心但如果你要自己画一个小模块那就一定别在RGMII上“走线随意化”否则调试阶段你很可能会对着示波器怀疑人生。4. 用Vivado TEMAC核快速打通MAC层生成、配置与时序约束基础知识储备够了正式开始动手。我自己的习惯是初学阶段不要一股脑全手写MAC控制器先用Xilinx官方IP核把链路打通建立“正确链路行为的基线”然后再研究内部逻辑。这就好比你先用别人封装好的库函数把程序跑通再去看底层源码学习效率最高。有人一听“IP核”就抵触觉得用了就是不懂原理。这个心态可以理解但不推荐。TEMAC核生成时会附带example design里面包含完整的约束和测试逻辑这是你学习RGMII时序和MAC层行为的绝佳教材。你完全可以一边用核一边读example design的源码两三个月后你会发现你已经有能力自己写一个简化的MAC了。4.1 生成TEMAC核关键选项按这样选在Vivado里例化Tri-Mode Ethernet MAC这个IP时几个关键配置项直接影响后续使用接口选择RGMII这是你物理层PHY所采用的接口。速率选1Gbps千兆去掉自适应的勾选保持固定千兆。管理接口勾选MDIO后续调试时可以通过MDIO读PHY寄存器来确认链路状态。用户侧接口建议选NativeFIFO/简单并行接口不要一开始就选AXI4-Stream。Native接口暴露给用户的是gmii_tx_en、gmii_txd[7:0]这类原始信号组包逻辑写起来清晰简单。内存缓冲如果不是特殊需求选择不添加额外缓冲跑通后再按需添加。这个核比较贴心的一点是你选RGMII它内部会自动帮你例化一个“GMII转RGMII”的转换模块。所以你在用户侧看到的时序是GMII风格的8位并行数据、tx_en、tx_er这些物理引脚上才跑DDR的RGMII信号。这对新手来说大幅度降低了心智负担——你只管按GMII时序填数据的节奏上沿下沿的数据拼接交给核完成。4.2 RGMII时序约束为什么例程能过你却不能RGMII是DDR接口Vivado不会自动帮你分析“上下沿都采数据”的时序必须手工加约束。如果你用的是TEMAC核的example design里面已经带了一套参考约束如果是自己新建工程需要手动仿照添加。千兆RGMII的约束核心思路给TXC创建时钟约束例如在引脚上创建125MHz时钟设置TXD/TX_CTL相对TXC的output delayXilinx官方推荐的范围和PHY数据手册有关常见是min约1.0nsmax约2.0nsRX方向根据PHY是否内置延迟来决定是否加input delay约束。很多同学编译能过就忽略时序报告这是大忌。编译通过不代表时序收敛RGMII路径如果出现时序违规板子上表现出来就是“偶尔能通、偶尔不通”或者跑大流量时随机丢包。我的建议是花十分钟把约束写对然后打开时序报告专门看RGMII那几条路径的setup和hold margin是否为正。一切都是正向的再去碰逻辑。5. 自己动手一个最简单的UDP发送模块到了这步MAC层已经通了剩下的核心工作是组包。这个阶段的成就感非常解压数据会在你的眼前变成实实在在的网络帧。组包过程我用写信来类比特别形象你的用户数据是信纸内容UDP头是信封上的“收件人地址”端口号IP头是外层更大的信封IP地址保证能在互联网上路由以太网帧头是最外层的快递面单MAC地址保证数据在同一局域网内传输。每一层的“信封”都有固定的格式也都有自己的校验方式。我们要做的就是按顺序把这些头拼上去。5.1 UDP发送模块的整体框架数据流向应用数据例如一个计数器的值 - UDP头8字节 - IP头20字节 - 以太网帧头14字节 - 用户侧接口 - TEMAC核 - RGMII - PHY - 网线。在FPGA里这个过程就是一个大状态机。我通常把它拆成以下几段状态IDLE等待发送触发信号一触发就跳到MAC_HDR。MAC_HDR输出目的MAC6字节、源MAC6字节、类型0x08002字节。IP_HDR输出20字节IP头。UDP_HDR输出8字节UDP头。DATA按字节输出用户数据。PAD如果数据长度小于18字节在这里填充0保证总帧长达到64字节的最小值。结束回到IDLE等待下一包。看到没在这个框架里前导码、FCS、IFG帧间隙全部由TEMAC核替你完成你只需要关心从目的MAC开始的字节流。如果你将来自己写GMACFCS和IFG就要单独处理那是进阶内容了。5.2 CRC和校验和哪些能偷懒哪些必须自己算这个点特别容易让新手纠结CRC32到底怎么算坦白说如果你正确用了TEMAC核FCSCRC32是它自动生成的你完全不用管。但如果将来要做纯手写MACCRC32就是你绕不开的坎。以太网FCS的CRC32算法标准是多项式0x04C11DB7初值0xFFFFFFFF反射输入输出结果异或0xFFFFFFFF。所谓“反射输入输出”指的是数据按位反序处理这常常让零基础玩家算出来的值和大厂给的参考CRC对不上。我的建议是手写时直接用Xilinx的CRC Generator IP核参数里选择CRC32、反射模式、数据位宽8比自己拿位运算磨半天靠谱得多。IP头校验和则必须自己算。算法很简单把IP头按16位一组做反码求和结果取反。IP头20字节相当于10次16位加法。注意这里有一个“反码求和”和“补码求和”的区别很多人在这里算错。用Verilog连续累加时每次加完如果产生进位要把进位回卷加到最低位这就是反码进位的本质。UDP校验和IPv4下是可选的初学阶段建议填0。Wireshark可能会显示checksum 0x0000并标注一下但链路能通。跑通之后如果你想填真实校验和就要算上伪头部源IP、目的IP、协议号、UDP长度和整个UDP段工作量会上来但那样才是一个“能过专业抓包工具扫描”的完整UDP包。5.3 一段极简的Verilog示例IP头生成逻辑以发送状态机中的IP头部分为例代码可以这么组织// 发送状态机的IP头输出部分 // 固定20字节IP头 localparam [7:0] IP_VERSION_IHL 8h45; // IPv4, 5个32位字头 localparam [7:0] IP_TOS 8h00; // 服务类型 localparam [7:0] IP_TTL 8h40; // TTL 64 localparam [7:0] IP_PROTOCOL 8h11; // UDP always (posedge clk) begin if (tx_state IP_HDR) begin case (ip_hdr_cnt) 0: tx_data IP_VERSION_IHL; // version/ihl 1: tx_data IP_TOS; 2: tx_data ip_total_len[15:8]; // total length高8位 3: tx_data ip_total_len[7:0]; // total length低8位 4: tx_data 8h00; // identification高8位 5: tx_data 8h00; // identification低8位 6: tx_data 8h00; // flags/fragment offset高8位 7: tx_data 8h00; // flags/fragment offset低8位 8: tx_data IP_TTL; 9: tx_data IP_PROTOCOL; 10: tx_data ip_checksum[15:8]; // 校验和高8位 11: tx_data ip_checksum[7:0]; // 校验和低8位 12: tx_data my_ip_addr[31:24]; // 源IP 13: tx_data my_ip_addr[23:16]; 14: tx_data my_ip_addr[15:8]; 15: tx_data my_ip_addr[7:0]; 16: tx_data dst_ip_addr[31:24]; // 目的IP 17: tx_data dst_ip_addr[23:16]; 18: tx_data dst_ip_addr[15:8]; 19: tx_data dst_ip_addr[7:0]; endcase end end这里有一个经验IP头校验和不要在发送的同时实时累加否则会在关键路径上引入一串加法器对时序很不友好。应该在组帧前由另一个组合逻辑或小状态机预先算好存入寄存器发送时直接查表输出。对于固定字段的IP头校验和甚至可以和长度一起提前算好只需要在长度变化时重算一次。这是标准做法能省掉很多不必要的时序风险。6. 收发全打通ARP应答与UDP接收发送通了之后你一定会发现一个新问题PC往FPGA发UDP包FPGA收不到。这几乎是必然的因为PC在往一个陌生IP发包之前必须做ARP解析搞清楚目的MAC地址。如果你不处理ARPPC会把UDP包留在本地缓存里等ARP超时后直接丢弃。6.1 为什么必须先处理ARP考虑这样一个场景PC的IP是192.168.1.20FPGA的IP是192.168.1.10。PC要往FPGA发UDP第一步不是直接组UDP包而是查自己的ARP缓存表。如果表里没有192.168.1.10对应的MAC地址PC会向局域网发一个广播ARP请求“谁是192.168.1.10请把你的MAC地址告诉我。”如果你的FPGA不回答PC永远等不到回应UDP包也就永远不会发给FPGA。所以哪怕你只做UDP接收也至少要做个ARP应答模块。ARP应答逻辑非常简单收到目的MAC为全FF的广播帧类型字段为0x0806解析ARP请求内容检查操作字段是否为1请求检查目标IP是否等于自己的IP如果匹配把目标MAC替换成PC的MAC把自己的MAC填到发送端MAC把操作字段改成2应答然后原路发出去。这里特别容易出错的点是字节序。网络上传输遵循“大端”习惯MAC地址和IP地址都是高字节在前、低字节在后。可很多FPGA开发板的代码里寄存器里存的IP地址却是低字节在前小端习惯导致你在Wireshark里看到源IP变成了“1.0.168.192”这种怪东西。所以写代码时先把IP地址的字节序固定下来比如用my_ip {8d192, 8d168, 8d1, 8d10}这种方式定义就不会晕了。6.2 UDP接收状态机的设计思路接收方向就是从MAC接收接口的字节流里把用户数据提取出来。TEMAC核给出的接收信号是GMII风格的8位数据加rx_dv有效信号我们用一个状态机按偏移解析即可等待rx_dv拉高拉高后的第一个字节为目的MAC的第一个字节数到第13个字节时判断类型字段0x0806进入ARP解析状态0x0800继续往下走IP解析IP头中判断协议字段是否为0x11UDP是则继续解析UDP源端口、目的端口、长度把数据载荷写入FIFO或触发后续处理逻辑。这个状态机本身不复杂复杂的是要处理各种非理想情况。比如PC发了VLAN标签帧类型字段是0x8100比如IP头长度不是20字节带了IP选项比如UDP包发生了IP分片。初学阶段建议统一策略只要特征不匹配直接丢包。这是最稳妥的“白名单”方式不用去处理那些边角情况。还有一个关键提醒RGMII接收方向的RX_CLK是由PHY产生的它和FPGA内部的本地时钟是异步的。TEMAC核内部已经做了一部分时钟域处理但如果你的用户逻辑要在本地时钟域操作收到的数据强烈建议在用户侧再接一个异步FIFO把数据从RX时钟域搬到用户逻辑时钟域。否则你会看到极偶发的随机单字节错误很难排查。6.3 用ICMP回显来验证链路通畅在UDP收发这套逻辑接通之前有个很棒的工具可以用来判断IP层是否正常——那就是用PC的ping命令。Ping使用ICMP协议协议号是1如果你的FPGA逻辑能正确识别ICMP回显请求type8并按格式回复回显应答type0那么PC就能成功ping通FPGA。这一步通了你就可以放心地去写UDP的应用层处理了。如果ping不通排查思路是先确认ARP是否已经解析成功在PC上用arp -a能看到对应IP的MAC再看FPGA是否收到了ICMP请求ILA抓信号看有没有进入IP解析状态最后确认回显应答的IP头校验和与ICMP校验和是否正确用Wireshark看回包是否报checksum错误。这套链路走通之后你对FPGA网络通信的“整体直觉”基本上就建立起来了。7. 调试实录从“灯都不亮”到“Wireshark出包”的全链路排查思路写到这里我把自己多年下来最常用的一套调试流程完整贴出来。这套流程帮我自己解决过无数次问题也给好几位刚入门的同事排过雷。核心思想就一句话从物理层往协议层一层一层验证绝不跨层瞎猜。7.1 分层验证法每一步都有一个明确的“可观测结果”第一步验证PHY链路是否建立。用MDIO去读PHY的寄存器一般是寄存器1Basic Mode Status看bit1Link Status是否为1。如果链路没建立先查网线、查对端设备、查PHY是否被复位。链路灯不亮后面所有抓包都没有意义。第二步发“裸MAC帧”——不带IP只发目的MAC全FF的广播帧。用Wireshark看能不能抓到如果抓不到说明MAC层发送逻辑或者RGMII时序有问题。测试时不要拿复杂的组包状态机来试直接写一个极简的循环发送逻辑固定字节流即可。第三步加IP/UDP头看Wireshark能否正确解析出“UDP”协议。如果裸帧能抓到加了IP头后抓不到多半是IP头长度或校验和算错。Wireshark对UDP的校验和填0是比较宽容的不会因为UDP校验和错误就丢包但IP头校验和错了Wireshark会显示“bad checksum”很多协议栈会直接丢。第四步加ARP应答用PC ping FPGA的IP。能ping通说明ARP、IP层、ICMP回显都正常。第五步跑UDP收发应用。此时你的所有底层问题都已经解决了剩下的就是应用逻辑的调试。7.2 几个典型问题与完整排查链路下面列几个我亲眼见过的问题每一个都尽量还原排查过程这种“链条式”的思路比答案本身更值钱。问题一Wireshark能抓到FPGA发出的包但全都标红“incorrect checksum”。排查链路先用简单的CRC自测工具检查CRC模块的输入输出。常见原因有两个一是CRC计算模块的数据位宽与GMII数据宽度不匹配导致FCS错位二是CRC输出时序比数据晚了一个周期FCS插入位置错位。这个问题的解决办法是仔细阅读Xilinx CRC Generator IP核的时序图确认valid信号之后多少拍输出CRC结果再在状态机里安排FCS发送。别嫌这一步繁琐你将来换成SGMII或者光口同样要处理CRC时序。问题二小包能通大包不通。排查链路这种情况90%是帧长限制或者FIFO深度不够。以太网标准帧最大1518字节如果IP/UDP头占了28字节那应用数据的最大值就是1472字节。如果你发的UDP数据超过1472PC端要么看到分片要么直接丢弃。处理办法是先在FPGA端限制UDP负载不超过1472字节再抓包确认。如果是接收方向大包丢那就是接收FIFO深度或读侧背压没有做好增大FIFO容量是最快的方案。问题三上电第一次能通复位几次后就不通。这是一个典型的“RGMII时序玄学问题”。排查链路先打开Vivado时序报告看RGMII相关路径的setup/hold slack再检查PHY的延迟模式确认它是否处于随机状态。很多PHY的时钟延迟是靠strap引脚或MDIO寄存器配置的如果这些配置没有固定住复位后PHY可能进入一个不稳定的延迟模式。解决办法是用MDIO在初始化时强制写一次延迟配置寄存器把延时值固定下来。这个问题的最佳解决时机就是原理图设计阶段把strap引脚配好代码里再做一次MDIO显式配置双保险。问题四PC端发大流量UDPFPGA疯狂丢包。排查链路先确认流量速率是否已经超过了你接收FIFO的写入速率。千兆以太网理论峰值是线速148809帧每秒64字节最小帧如果你每秒只能处理10万帧必然会丢。最简单的办法是先降速率测试比如PC端用工具限制发包速率到200Mbps如果这时候不丢了说明是吞吐瓶颈如果还丢再查FIFO的读写指针、跨时钟域逻辑、以及MAC核的背压信号是否有效。7.3 我现在的经验总结关于FPGA网络通信做了几个项目之后我最大的体会是分层调试的效率远远高于一次调到位的直觉。FPGA网络通信看起来东西多核心其实就两块——MAC层行为和UDP/IP封装格式。你不需要背RFC文档但需要清楚每一层在硬件上消耗的资源与时间。我自己的开发习惯是在新板子上跑网络逻辑之前永远先花半小时做一次“链路自检”——读PHY寄存器、抓MAC层广播帧、看时序报告、确认RX时钟延迟。这个自检流程跑通之后后面再复杂的UDP/TCP应用都只是组包逻辑的问题。如果你按照本文的顺序把TEMACARP简单UDP收发跑通了下一步就可以自然分成几个方向去扩展把UDP发送模块改成带FIFO的连续数据流让数据能实时流式上传把接收数据接到算法模块比如图像处理、FFT、滤波做成一个完整的硬件数据处理链路或者换用SGMII/光纤接口去突破千兆的带宽限制做到10G甚至更高。这个最小系统吃透了FPGA网络通信这个方向就再也不是黑盒了。
分享:

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

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