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

FPGA网络通信实战:从MAC到PHY,打通以太网数据通路

呼终于写到网络通信这章了。从点灯、按键、串口一路走过来到这一章前面学的时序概念、状态机、FIFO、异步处理全都得拿出来用一遍。很多朋友到这一步会慌网络通信听起来太系统级了又是MAC又是PHY又是ARP又是CRC怕自己搞不定。实际上FPGA上的网络通信百分之八十的工作量在硬件初始化和数据通路打通上剩下的才是协议处理。对入门阶段来说你需要关心的核心就两个词MAC和PHY。搞清楚它们之间的接口长什么样再把数据从FPGA逻辑里搬到网口上本章的任务就完成一大半了。这篇是“从近似0基础开始FPGA开发”系列的第七篇默认你已经会开Vivado工程、会写简单Verilog、看得懂时序图。这篇不跟你扯太深的应用层协议目标是把FPGA和网线之间的这层窗户纸捅破让你能在一两天内从板子上电到用Wireshark看到自己FPGA发出来的以太网帧。1. 网络通信到底在干什么先把概念捋顺1.1 拆掉“网络通信”这个吓人的壳很多初学者一听到“FPGA网络通信”就脑补出一整套TCP/IP协议栈觉得要写一堆复杂逻辑。其实在FPGA入门阶段你要做的根本不是实现完整的网络协议栈而是先把“比特流从一个设备搬到另一个设备”这件事做通。打个比方。你要寄一封信其实分两层第一层是把信纸写出来第二层是把信纸装进信封、写上地址、丢进邮筒。对应到FPGA网络通信里你的逻辑代码是“写信的人”MAC层负责“装信封、写地址、贴条码”PHY芯片就是“邮筒和邮递员”把信封变成网线上的电信号送出去。所以FPGA上的网络通信核心就两部分一个是MAC控制器负责组帧、拆帧、校验一个是PHY芯片负责把MAC传来的并行数据和网线上的串行模拟信号互相转换。MAC层通常在FPGA内部实现可以用IP核也可以自己写PHY是板子上的独立芯片FPGA通过RGMII、GMII这类接口跟它相连。这篇文章我默认你用的是最常见方案FPGA 板载千兆PHY芯片 RJ45网口FPGA内部例化一个三速以太网MAC核外部通过RGMII接PHY。这套组合是市面上绝大多数开发板、核心板、通信板卡的标配也是你以后出去做项目最常遇到的架构。1.2 以太网帧长什么样你要处理的数据格式不管你后面想传图像、传AD数据、传自定义协议最终到线上都得包成以太网帧Ethernet Frame。这个帧的格式是IEEE 802.3定义的入门阶段你只要记住几个关键段就行。字段长度作用前导码 Preamble7字节同步时钟内容固定为0x55帧起始定界符 SFD1字节标志帧的开始固定为0xD5目的MAC地址6字节发给谁源MAC地址6字节我是谁长度/类型2字节小于1500表示长度大于1536表示协议类型如0x0800是IP包数据 Payload46~1500字节真正要传的内容不够46字节要填充帧校验 FCS4字节CRC32校验覆盖目的MAC到数据末尾这里有两个新手容易踩的坑。第一前导码和SFD不算在帧长度里但它们确实在线上传输。用Wireshark抓包时抓包软件通常把前导码和SFD直接隐藏了你看到的帧是从目的MAC开始的。第二以太网帧最小64字节最大1518字节不放VLAN标签的情况下。如果你要发一个只有10字节的数据必须在数据区补到46字节否则整个帧不够64字节很多PHY和交换机直接就不认了。反之数据超过1500字节就必须拆帧这就是IP层MTU的由来。FPGA如果做UDP大包发送经常要处理分包逻辑这一条后面会反复遇到。1.3 数据从FPGA到网线要经历哪几步我画一条完整通路给你看这样后面所有配置和调试你都有坐标参考。FPGA内部用户逻辑 — MAC IP核组帧/解帧/CRC — RGMII接口 — PHY芯片编码/并串转换 — 网络变压器 — RJ45 — 网线 — 电脑/交换机发送方向你写一个发送模块把用户数据通过AXI-Stream接口塞给MAC核MAC核自动添加前导码、目的MAC、源MAC、长度和CRC最后转成RGMII时序送给PHY。接收方向PHY从网线上收下模拟信号转成RGMII数字时序MAC核解析并对齐字节去掉前导码和SFD输出一帧完整的数据你再在逻辑里判断目的MAC是不是自己是就收下不是就丢掉。数据链路层的事基本就这些。至于ARP、ICMP、UDP、TCP这些属于网络层和传输层这章先不碰。你把MAC层跑通后续往上加协议就是模块叠加问题难度完全不同。2. 板载PHY芯片和RGMII接口硬件准备不能再想当然2.1 先到原理图里认识你的PHY芯片FPGA开发板上PHY芯片一般都在RJ45网口附近丝印上能直接看到型号最常见的有瑞昱RTL8211系列、Microchip的KSZ9031、裕太微的YT8531老一点的有Marvell的88E1111。不同PHY寄存器定义略有差异但功能结构大同小异。拿到一块新板子第一件事不是写代码而是去原理图里找这几个信号PHY的复位引脚接了FPGA的哪个GPIO还是直接拉了上电复位还是接到了别的复位芯片。PHY的MDIO/MDC引脚这是配置PHY的“管理总线”通常直接连到FPGA的普通IO。PHY的RGMII信号组TX_CLK、TX_CTL、TXD[3:0]、RX_CLK、RX_CTL、RXD[3:0]看看有没有中间加电平转换芯片。PHY的时钟来源有些板子PHY自带25MHz晶振有些是FPGA给时钟甚至有些设计用125MHz差分时钟这个直接决定你初始化的时序。为什么让你先看原理图因为PHY芯片的启动配置除了你后面用MDIO写的寄存器还涉及其硬件引脚上下拉。比如PHY地址通常通过引脚上的上下拉电阻设定常见的PHY地址是0x00、0x01、0x04个别板子拉到0x1F。如果后面MDIO读写没反应大概率就是PHY地址搞错了。提示所有PHY芯片的电源域和引脚电平都要确认清楚。现在很多板子FPGA是1.8V、2.5V、3.3V混合IOPHY的RGMII接口通常是2.5V或3.3V如果FPGA的Bank电压不一致轻则通信异常重则烧芯片。上电拿万用表量一下比写代码省心得多。2.2 RGMII信号怎么理解把它当成带方向的串口RGMII的全称是Reduced Gigabit Media Independent Interface相比老一代GMII数据线从8根减到4根速率提高一倍。你可以粗暴地理解为这是一组在时钟上下沿都能采数据的并行接口比UART复杂一点但本质还是“时钟控制数据”的同步接口。RGMII的信号就五组信号方向作用TX_CLKFPGA到PHY发送时钟1000M时为125MHzTX_CTLFPGA到PHY发送控制上升沿为TX_EN下降沿为TX_ERTXD[3:0]FPGA到PHY发送数据上升沿发低4位下降沿发高4位RX_CLKPHY到FPGA接收时钟由PHY恢复输出RX_CTLPHY到FPGA接收控制上升沿为RX_DV下降沿为RX_ERRXD[3:0]PHY到FPGA接收数据上升沿收低4位下降沿收高4位千兆模式下TX_CLK和RX_CLK都是125MHz每个时钟沿传4bit一个时钟周期传8bit正好是1Gbps。百兆模式下时钟变成25MHz十兆是2.5MHz。这里要注意的是发送方向时钟由FPGA输出给PHY接收方向时钟由PHY恢复出来送给FPGA。所以你在做时序约束时TX引脚要约束output delayRX引脚要约束input delay。不过用IP核的话IP内部已经做了大量处理你只需要在XDC里约束好RGMII引脚位置和时钟然后用set_input_delay/set_output_delay粗略约束一下就行。2.3 MDIO管理接口PHY芯片的“遥控器”MDIOManagement Data Input/Output是IEEE 802.3定义的管理总线作用就是让FPGA能读写PHY芯片内部的寄存器。你可以理解成PHY芯片背后有个控制面板但面板上的旋钮都在寄存器里你要通过MDIO去拧。MDIO总共就两根线MDC管理时钟和MDIO管理数据。MDC由FPGA提供频率不能太快一般建议2.5MHz稳妥一点1MHz都行很多PHY对太高的MDC时钟不敏感。MDIO的时序是标准帧格式写操作和读操作略有区别写操作32位前导码全1 起始码01 操作码01 PHY地址5bit 寄存器地址5bit 2bit的TA写为10 16bit数据读操作前面一样操作码换成10TA变成高阻PHY回数据手动用Verilog写MDIO控制器并不难但多数时候可以直接用Vivado的VIO虚拟IO配合ILA来在线读写甚至只在调试阶段用ILA发寄存器的值到MDIO模块。我自己习惯的做法是先写一个简单的MDIO驱动模块用状态机实现读写然后通过VIO给命令这样不需要反复编译。注意MDIO读操作在TA阶段MDIO线是双向的需要从FPGA输出切到高阻输入。如果写Verilog时把INOUT方向搞错读出来永远是0xFFFF或者0x0000。遇到这种情况先用逻辑分析仪抓MDIO线上的波形看TA阶段是不是真的拉了高阻。3. 用IP核搭数据通路三速以太网MAC使用要点3.1 为什么别一上来就手写MAC控制器我在很多群里看到新手问“能不能自己写一个MAC控制器”能当然能但前提是你对时序、CRC、状态机、跨时钟域都有足够的把控力。手写MAC控制器的工作量比写一个UART大一个数量级而且写完还要做大量仿真验证不是一个“从近似0基础开始”的系列应该走的路。用IP核不是丢人恰恰是工程上的正确选择。Xilinx Vivado自带一个三速以太网MAC核叫Tri-Mode Ethernet MAC简称TEMAC。它的作用是在你不需要了解RGMII和CRC细节的情况下把收发数据变成干净的AXI4-Stream接口。Intel/AMD平台的FPGA也有对应的三速以太网IP高云、安路这些国产FPGA基本都是类似思路。TEMAC内部已经包含了RGMII接口转换、收发FIFO、CRC校验/生成、流量控制、MDIO控制等模块。你要做的就三件事正确配置IP核、把收发接口连到自己的逻辑、约束好引脚和时序。这也是为什么工作三五年的人做FPGA网络通信第一步也都是例化IP只有特殊需求比如超高吞吐、超低延迟的定制协议才考虑完全手写。3.2 Vivado里例化TEMAC的关键配置项在Vivado的IP Catalog里搜索Tri-Mode Ethernet MAC双击打开配置界面。不同版本界面略有差异但核心配置项没变。我按自己的使用习惯给你过一遍Physical Interface选择RGMII。如果你板子PHY是GMII接口选GMII但绝大多数开发板是RGMII。Speed Default设置为1 Gbps。如果你的设计需要支持多种速率自动协商那就让PHY自协商之后通过寄存器读取当前速率再动态切换MAC侧的速率这个对新手稍复杂可以先固定千兆。Shared Logic选项如果只有一个MAC核直接选Include shared logic in core省事时钟和复位都由IP内部管理。如果是复杂系统多个以太网口再考虑Exclude shared logic自己统一管理。AXI4-Stream接口选项要勾上这样MAC对外的收发就是标准的tvalid/tready/tdata/tkeep/tlast握手信号方便你写自己的逻辑。收发FIFO深度一般默认即可默认通常是16KB至少能撑住几个大帧的突发。数据量大、突发密集的场景可以把发送FIFO调大到32KB。此外IP核里还有一个重要的寄存器接口。TEMAC有独立的配置寄存器需要通过AXI4-Lite口去写。这个AXI4-Lite口可以在配置时选择单独的接口也可以让IP核内部自动配置但多数情况下需要你通过一个AXI4-Lite接口模块来配置MAC的收发使能、速度、回环模式等。Vivado官方示例里提供了一个简单的主机接口你可以参考。如果不想写AXI4-Lite主机逻辑也可以用VIO去读写但这样不太优雅。我建议第一次跑通时直接用VIO控制功能验证通过后再用代码实现配置流程能省很多时间。3.3 收发逻辑怎么跟MAC核对接配置完TEMAC你在Block Design或直接例化Verilog时会发现MAC对外暴露了三个主要接口AXI4-Lite配置口、AXI4-Stream发送口、AXI4-Stream接收口。用户逻辑这边需要干三件事。接收方向MAC核会把收到的有效帧通过接收AXI4-Stream接口吐出来tvalid表示有数据tdata是数据tlast表示帧结束。你需要自己判断MAC地址是否匹配。不过TEMAC也提供地址过滤功能可以通过配置寄存器设置入学阶段更建议在用户逻辑里判断灵活便于加自己的过滤规则。发送方向你准备好一帧数据先把tvalid拉高数据按字节往tdata上放直到最后一拍拉tlast然后再看tready是否准备好。简单来说就是标准的AXI-Stream主从握手唯一要记得的是发送前必须组好整个帧至少64字节不够就填充0。讲一个我见过的典型错误有些新手直接用一个寄存器或开关控制发送把tvalid一直拉高然后数据一直往tdata送结果MAC反复发同一个帧。正确做法是先在缓存里把整个帧准备好再通过状态机启动一次发送tvalid只在数据有效期间拉高tlast在最后一拍拉高后立刻释放等tready一起握手才算发完一帧。接收方向还有个细节MAC核默认会检查CRC坏帧会直接丢弃这没问题。但如果你在调试阶段想看到原始帧可以在配置里关闭CRC检查收什么都往上送方便分析问题等后面跑协议时再打开。3.4 时序约束和引脚约束不要到最后才补很多FPGA例化IP一个顶俩但忘了写XDC综合能过上板后网口就是不通。RGMII这类高速接口引脚位置错一个、约束少一条根本起不来。引脚约束还好说照着原理图把TXD[3:0]、TX_CTL、TX_CLK、RXD[3:0]、RX_CTL、RX_CLK、MDC、MDIO这些信号的位置填进去就行。麻烦在时序约束。常规做法是对RX_CLK用create_clock创建输入时钟约束频率125MHz。对RXD、RX_CTL用set_input_delay约束大致范围在1ns到3ns之间。对TX_CLK如果是MAC输出给PHY的时钟通常由IP内部生成不需要额外约束太多但TXD和TX_CTL需要set_output_delay一般设2ns左右。具体数值要根据PHY手册的时序参数来算但入门阶段先用这些经验值就能跑通。如果后面上到非常高的频率或者遇到时序收敛问题再回头细调。提示RGB类RGB类先确认你在XDC里用的时钟名和IP核实际生成的时钟名是否一致否则约束不生效。用report_clock_interaction和report_timing_summary看是否有时钟没约束上这一步很多人忽略。4. 上板调试五步走从点灯到Wireshark看到包4.1 第一步先让PHY的link灯亮起来配置完和布局完上板第一件事不是写一堆收发逻辑而是看板子上的网口link灯亮不亮。link灯一般有两个一个表示链路通一个表示速率。插上网线到交换机或者电脑如果link灯不亮后面所有调试都是空谈。link不亮的排查顺序是固定的PHY有没有复位、PHY有没有时钟、PHY电源对不对、网络变压器和后级电路有没有焊错。没有时钟就查晶振复位被拉死就查复位芯片电源有误就查LDO大概率是硬件问题不是FPGA逻辑问题。如果link灯亮了且速率灯显示千兆说明PHY的物理层通路已经起来了FPGA即使不发一帧数据链路也是正常协商好的。这时候再进行下一步。4.2 第二步通过MDIO读PHY寄存器确认能通信PHY的link状态、速度模式、回环模式、中断状态全都可以通过寄存器读出来。用你写的MDIO驱动先读一下PHY的ID寄存器比如RTL8211的寄存器2和3会回读出芯片ID你必须能读到一个非0xFFFF的值才说明MDIO通路正常。读通之后再读PHY的Basic Status寄存器寄存器1看bit2是不是1表示链路已建立。如果不能读到寄存器检查MDIO时序有没有问题MDC是不是太快PHY地址对不对。这一步通过VIO加ILA来在线操作特别高效不用改代码重新编译。4.3 第三步MAC回环打通内部通路外环链路通了、MDIO也能读了接下来就是验证FPGA内部通路。TEMAC有寄存器可以配置MAC回环Loopback你可以把发送的数据直接从内部绕回接收侧逻辑里自己收自己发的帧。这个测试能快速验证MAC核配置、AXI-Stream接口、FIFO是否工作正常。具体操作是在IP核配置后通过AXI4-Lite写寄存器把内部回环使能打开然后用户逻辑开始发送数据接收侧理应能收到一模一样的帧。用ILA挂在接收AXI-Stream接口上看到tvalid/tdata/tlast正常变化就说明内部通路一点问题没有。4.4 第四步电脑直连并抓包分析内部通了把回环关掉网线直连电脑再用Wireshark抓包。这时候会看到两种结果。第一种电脑在发ARP广播包目的MAC是FF:FF:FF:FF:FF:FF源MAC是电脑自己。FPGA如果还没支持ARP协议通常不会回包但你能在Wireshark里看到请求帧说明接收方向是通的。第二种你让FPGA定期发送一个自定义广播帧目的MAC设成FF:FF:FF:FF:FF:FF电脑端Wireshark就能看到FPGA发出来的这个帧。能收到发送方向就通了。这时候再检查三个重要项目帧长度对不对、CRC是否正确、源目的MAC是不是填反了。Wireshark对坏CRC帧会直接标注“Malformed”或“bad FCS”一目了然。4.5 第五步用稳定性和收发统计验证项目做到“能收到包”只是第一步完整交付还要做稳定性和性能测试。比如写一个收发统计模块在逻辑里统计收了多少帧、丢了多少帧、CRC错帧多少、发送完成了多少。跑个10分钟、1小时看看数据是否稳定。电脑侧用ping测试更直观但ping需要IP和ARP协议这章先不展开。如果你只是想测试MAC层的收发完整性可以在FPGA内部生成已知模式的循环帧电脑端Wireshark过滤规则统计看有没有丢帧。到这一步一个FPGA的以太网通信通路就算真正打通了你可以在这个基础上加ARP协议、UDP协议、自定义数据协议都是后面的事了。5. 常见问题排查与避坑清单5.1 PHY link灯不亮但硬件看着都正常先查PHY的复位时序。PHY芯片上电后一般需要等待一个稳定时延再释放复位有些开发板把PHY复位接到FPGA的IO上但FPGA配置期间IO默认是什么状态、配置完成后是什么状态都不能拍脑袋。具体做法是上电后用信号量测一下PHY的复位引脚电平如果是低PHY一直处于复位肯定不工作。第二查PHY的时钟。很多PHY自带晶振但有些设计是从FPGA送时钟过来的。如果FPGA还没配置成功时钟没起来PHY就没法工作。第三查网线。这种反直觉的检查其实很常见尤其会议室网线牵错交叉线的案例数不胜数。现在的PHY基本都支持MDI-X自动翻转但有些老设备不行换一条直通线或换个交换机端口试试。5.2 MDIO读写无响应读ID出来全是0xFFFF这个问题的90%原因是PHY地址不对。开发板原理图里PHY地址一般有明确标注比如RTL8211引脚LED2_PHY_AD2、LED1_PHY_AD1、LED0_PHY_AD0上拉为1下拉为0组合起来就是PHY地址。没有原理图就挨个试0x00到0x1F总能读到。第二个原因是MDC时钟频率太高。有些PHY手册写最高25MHz但实际布局布线不好时走线噪声一大高频率下就是读写失败。把MDC降到2.5MHz甚至1MHz问题基本消失。第三个原因是你MDIO的TA阶段方向切换没做好写Verilog时inout信号在读操作时要先拉高阻抗不然PHY回的数据根本传不回来。用ILA抓波形一看就明白。5.3 能收到ARP广播但发出去的数据抓不到接收通了但发送不通排查重点在发送方向。先看发送AXI-Stream握手tvalid拉高后tready有没有拉高两拍相与才能完成一次数据传输。如果tready一直低可能是发送FIFO满了或者是MAC核没处于发送使能状态回去查MAC配置寄存器有没有使能发送路径。再看TX_CTL和TXD的时序。RGMII是双沿采样如果用错了IDDR/ODDR的沿就会出现数据错位。IP核内部一般没有这个问题但你自己扩展逻辑时要注意。最后确认发送帧有没有满足最小帧长。发一个只有几个字节的数据不填充PHY根本不会把它当成有效帧发出去Wireshark自然收不到。数据区补到46字节以上再试。5.4 收到的帧长度一直不对或者CRC错误帧长度不对十有八九是你对“帧长度”理解错了。以太网帧长度指的是从目的MAC开始到数据结束的长度不包含前导码和SFDFCS也是后加的。如果用Wireshark看它会自动把前导码隐藏显示长度一般等于目的MAC起始到FCS结束也就是64到1518字节。CRC错常发生在自己写MAC的场景此时往往是自己组RPGRAMCRC算法或初始值写错。用IP核时出现CRC错问题多半在PHY配置比如PHY工作在MII模式而MAC工作在RGMII模式速率不匹配就会产生错误帧。注意如果电脑网卡显示连接速率只有百兆而FPGA MAC配置的是千兆链路层速率不一致时收上来的帧就会出现大量CRC错误。先让两边速率模式对齐再排查逻辑问题。5.5 收发单包正常高负载就丢帧单个包跑得好好的大量数据进来就丢这是典型的FIFO深度和背压问题。MAC核内部FIFO如果不够大接收方向上上一帧还没被用户逻辑取走下一帧就来了丢帧。解决方法是调大FIFO深度或者在用户逻辑侧用DMA方式持续搬运而不是一帧一帧地停等。发送方向也有类似的坑。如果你的用户逻辑在写发送FIFO时等整个帧写完了才允许下一次发送那么连续快速发包时会导致发送FIFO没清空。正确做法是让发送状态机在上一次tlast发送完成后尽快启动下一帧中间只保留必要的帧间隔。我在实际项目中遇到过一个问题单独用testbench仿真空转什么都对一上板拉高速率就丢包最后查出来是时钟域没有处理好。接收方向MAC核输出的时钟和用户逻辑时钟不在同一个域直接用跨时钟域的办法搬运数据丢帧自然缓解。经验速查网络通信调试这张表足够现象排查方向link灯不亮PHY复位、时钟、电源、网线/网络变压器MDIO无响应PHY地址、MDC频率、inout方向能link但收不到帧RGMII引脚约束、RX_CTL极性、速率模式是否匹配收到坏CRC帧PHY速率配置、MAC回环配置、数据线位序单包正常高负载丢帧FIFO深度、发送背压、跨时钟域处理电脑网卡识别异常PHY自动协商配置、RJ45引脚极性最后再补充一个实战经验做FPGA网络通信不要把IP核和PHY绑定得太死。同一套RGMII接口逻辑换不同型号PHY芯片时寄存器和配置流程会变但MAC核和RGMII时序基本不变。我才开始学习时一直用一套板子和固定的PHY后来换了一个完全不同型号的PHY才发现真正值钱的是对RGMII和MAC层协议的理解那才是搬不走的东西。后面如果你想在自己设计的板子上跑网络通信先把这个基本功练扎实你会少走很多弯路。
分享:

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

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