ARTICLE DETAIL

资讯详情

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

FPGA以太网接口设计:基于Vivado的RGMII时序约束与LwIP协议栈详解

FPGA以太网接口设计:基于Vivado的RGMII时序约束与LwIP协议栈详解 1. 写在前面为什么选Vivado 2019.1做以太网接口干FPGA这行的朋友应该都有体会以太网接口几乎是每个通信、工控、嵌入式项目的“必修课”。无论是做高速数据采集、图像传输还是给板卡加一个调试通道网口都是最常用的交互接口之一。我这次用的是Xilinx的Vivado 2019.1搭配常见的RGMII接口PHY芯片做了一套完整的以太网通路从IP配置到引脚约束再到SDK里的LwIP协议栈验证整体跑下来还是比较顺的。先说为什么挑2019.1这个版本。Vivado的版本迭代很快但2019.1算是一个“分水岭”级别的稳定版一方面它完整支持最新的Ultrascale系列另一方面对传统的7系列器件兼容性也做得很好不像后来的版本对老器件动辄报错。更关键的是2019.1的License机制相对宽松配合网上常见的license文件基本能解锁所有IP核这一点对学生党或者个人开发者来说非常友好。如果你手头正好有一块使用7系列或Ultrascale系列FPGA的开发板Vivado 2019.1是一个不会让你折腾太久的选择。这个项目适合谁我的看法是三类人一是刚接触FPGA通信接口的学生想用一块开发板把以太网“跑起来”二是做嵌入式系统集成的工程师需要在自己设计的板卡上快速验证网络通路三是想梳理RGMII时序约束和MAC层配置的开发者。我会尽量把整个过程拆细包括IP核参数怎么填、引脚怎么分、仿真怎么测、上板调试踩了什么坑都会讲清楚。2. 方案选型RGMII、GMII和SGMII到底怎么选2.1 三种接口的底层差异搭建以太网接口时FPGA和PHY芯片之间的连接方式是第一个要定下来的事。目前主流的连接方式就三种GMII、RGMII、SGMII。如果你用的是7系列或Ultrascale系列FPGA大多数开发板默认采用RGMII方案原因是它引脚数量少、PCB布线压力小同时速率完全够用。GMII是早期的并行接口标准发送和接收各8根数据线加上时钟和控制信号一共需要20多根引脚而且工作时钟高达125MHz信号完整性很难做。SGMII则是串行接口引脚极少但需要额外的GTP/GTX高速收发器资源开销明显变大。RGMII正好是中间路线数据线各4根时钟频率还是125MHz但通过双沿采样DDR的方式把带宽翻倍既保证了千兆速率又把引脚数量压到了十几根。这也是绝大多数开发板采用RGMII的最直接原因。2.2 MAC侧IP核的选择逻辑FPGA里的以太网MAC一般有两种实现方式直接用Vivado提供的Tri-Mode Ethernet MACTEMACIP核或者自己用Verilog/VHDL写一个简化版MAC。除非你有特殊需求比如实现自定义协议帧、硬件时间戳否则我强烈建议使用官方IP核。TEMAC支持10/100/1000M三速自适应内部集成了MAC层的CRC校验、帧间隔处理、流量控制等逻辑稳定性和时序性能远比自己写的代码可靠。在Vivado 2019.1中TEMAC IP核的配置界面很直观你只需要告诉它使用哪种PHY接口选择RGMII是否启用DMA配合AXI4-Stream接口使用时勾选、是否开启MDIO管理通道等关键选项。MDIO接口的核心价值在于你可以通过它去读写PHY芯片的寄存器完成自动协商、链路状态检测、速率配置等动作。如果你打算在SDK里跑LwIP协议栈MDIO更是必不可少因为LwIP初始化时需要通过MDIO读取PHY的状态寄存器来判断链路是否起来。2.3 数据通路架构的取舍以太网数据在FPGA内部的流转推荐采用“AXI4-StreamCMS-DMA”的方式。TEMAC IP核输出的是AXI4-Stream接口可以对接Xilinx官方的AXI Ethernet DMA IP核然后通过AXI-Lite或AXI4接口连到PS端的DDR或者MicroBlaze上。如果你用的是Zynq系列的SoC甚至可以直接把DMA中断接到PS端让ARM处理器运行LwIP协议栈实现一个真正意义上的嵌入式网络终端。这里有个选择要点Zynq平台优先推荐直接用PS端的GEMGigabit Ethernet MAC控制器外部PHY通过RGMII连接无需在PL侧做任何MAC逻辑这是最省资源的方案。但如果你用的是纯FPGA比如Artix-7就必须在PL侧例化TEMAC和DMA再通过AXI接口挂上MicroBlaze这是一个典型的软核SoC方案。两种方案的架构差别很大下文我会以纯FPGAMicroBlaze为主线展开因为这个方案更通用能帮你把以太网整个协议栈的链路给串起来。3. 环境准备Vivado 2019.1安装与工程创建避坑指南3.1 安装与License处理Vivado 2019.1的安装包非常大标准版大概20GB完整版接近50GB。个人开发者下载时建议选择“Vivado HL WebPACK”或“Vivado HL Design Edition”即可WebPACK免费的器件支持范围对大多数开发板已经够用。如果你用的是Artix-7 XC7A35T这类中低端器件WebPACK完全能覆盖。安装时需要注意一点Windows系统务必关闭杀毒软件和防火墙再运行安装程序否则经常出现某个DLL被拦截导致安装中断的情况。安装完成后最关键的一步是许可证配置。Vivado 2019.1支持加载本地license文件路径是“Help - License Manager - Load License”。常见的2035错误就是因为license文件不全或主机绑定信息不匹配导致的。我的建议是找到与你电脑MAC地址绑定的正版license或者使用网上合法的共享license统一版本加载后确认“Available Licenses”列表中能看到TEMAC、MicroBlaze等关键IP再开始下一步。3.2 创建工程的几个注意点用Vivado 2019.1新建工程时有几个细节直接影响后续流程。第一器件型号一定要选对很多人在综合时报“Cannot find BUFG for clock”之类错误本质就是没有为时钟资源做正确分配。第二RTL语言版本建议选Verilog方便维护。第三工程目录不要放在带中文的路径下这个已经是老生常谈了但总有人在这里栽跟头。工程建好之后第一件事不是写代码而是检查开发板的原理图确认PHY芯片型号、复位引脚连接方式、时钟源频率和物理地址PHY Address配置。比如常见的RTL8211、KSZ9031、AR8031等PHY它们的MDIO地址通常由硬件引脚决定可能为0x00、0x01或0x04你在SDK里读PHY寄存器时必须使用实际地址否则读回来的状态永远是0xFFFF。3.3 工程目录规划我个人的习惯是在工程根目录下新建src、constrs、sim、ip和sdk五个文件夹分别存放源代码、约束文件、仿真文件、IP核定制文件和SDK导出文件。这样做的最大好处是工程出问题时可以快速定位也方便后续版本管理或者复用。Vivado的IP核定制文件默认生成在原工程目录下如果你不想让.gen、.ip_user_files这类多余文件堆在根目录可以在Tools - Settings - General里把“IP Output Location”改到你指定的ip文件夹。4. 核心实现步骤RGMII接口从IP配置到引脚约束4.1 Tri-Mode Ethernet MAC IP核配置详解在Vivado的IP Catalog中搜索“Tri-Mode Ethernet MAC”并双击打开配置界面。我以RGMII为例把关键选项逐一带过。第一屏基本不用动直接默认。第二屏是接口模式这是整个配置里最关键的地方。在“Physical Interface”里选择RGMII速率模式勾选“10/100/1000Mb/s”这样能吃到完整的自适应特性。如果你确定板子上的PHY和FPGA直连且PCB走线没有跨电平转换芯片可以把“FPGA TX”和“FPGA RX”都设为“RGMII v1.3”注意不同PHY厂家对RGMII时序版本的定义略有差异这个问题后面我会单独说。第三屏涉及DMA和AXI接口这里我倾向于不启用TEMAC内部的“AXI-Lite Management”之外的额外选项因为管理接口和用户数据接口分离会简化后端的调试。数据通路采用AXI4-Stream接口用于对接DMA IP核。配置完点OK后Vivado会生成一个IP核你需要单独打开“IP Example Design”或直接生成综合产物。我个人建议先用“Open IP Example Design”跑一遍官方示例这个示例工程包含了完整的引脚约束和上板测试代码是学习最快的方式但要注意它默认的引脚约束文件可能不完全匹配你的开发板需要人工修改。4.2 时钟结构设计RGMII的时钟体系是整个接口能否正确收发的脉搏。PHY芯片的工作时钟一般由外部25MHz晶振提供PHY内部产生125MHz的GTXCLK回传给FPGAFPGA发送数据时需要提供一个125MHz发送时钟并保证数据在双沿上稳定输出。在Vivado里这个发送时钟可以通过MMCM/PLL把板载时钟倍频到125MHz也可以用PHY回传的GTXCLK经BUFG后使用。我的做法简单说就是把所有涉及RGMII数据总线的时钟统一约束到rgmii_rxc和rgmii_txc所在的时钟域并在约束文件里明确声明它们的周期为8ns。如果你用的是Zynq平台PS侧的GEM会自动处理时钟对齐但纯FPGA下必须手动保证不然会出现偶尔能通、偶尔完全不通的诡异问题。时钟部分的常见问题很容易造成误判。最典型的例子是ILA的采样时钟选的是125MHz的GTXCLK但设置的采样深度太小结果看到的波形只有零星几个跳变看起来就像PHY根本没工作其实问题可能只出在时钟约束或ILA触发条件上。这个问题我在第四节一开头踩过后来把ILA采样深度调到65536触发条件设为RGMII的RX_DV上升沿一抓一个准。4.3 引脚约束的核心逻辑以太网接口的引脚约束是整个工程的灵魂。RGMII一共涉及十几根引脚发送数据线eth_txd[3:0]、发送控制eth_tx_ctl、发送时钟eth_txc、接收数据线eth_rxd[3:0]、接收控制eth_rx_ctl、接收时钟eth_rxc以及MDIO接口的MDC和MDIO数据线。这些引脚必须在XDC文件里明确指定到FPGA的具体BANK引脚。编写XDC约束文件时要特别注意BANK电压。绝大多数开发板的PHY接口IO标准是LVCMOS33或LVCMOS25如果你把电平和IBUF/OBUF的属性写错了轻则功能异常重则烧毁PHY芯片。我建议在约束文件中一律显式声明IOSTANDARD不要依赖默认值。比如set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS33} [get_ports {eth_rxd[0]}]除了引脚分配时序约束也别漏。RGMII接收数据在接收时钟的双沿采样发送数据需要在发送时钟的双沿建立和保持。Vivado的时序引擎未必能自动推断出这种DDR接口的约束关系最省事的办法是使用create_clock明确声明RGMII时钟再配合input/output delay约束。因为延时参数的估计需要参考PHY芯片数据手册这里直接抄官方示例的约束值是不靠谱的必须根据自己的PHY型号调整。4.4 加入DMA和AXI接口数据要进到MicroBlaze或者DDR里TEMAC的AXI4-Stream接口还需要再接一个DMA。Xilinx提供的AXI Ethernet DMA IP核支持两种模式一种是带Scatter GatherSG模式适合跑Linux系统另一种是简单的寄存器模式适合裸机跑LwIP。我用的是SG模式因为SDK里Xilinx提供的LwIP库对SG模式的支持最成熟配置好BD之后基本零代码就能跑通TCP通信。DMA IP核配置时有一项“Width of Buffer Length Register”我建议保持默认的14位支持的包长上限是16384字节足够应对标准以太网帧最大1518字节。关于DMA的描述符缓冲区地址要放在DDR中并且按64字节对齐如果你的内存控制器还没有初始化好就启动DMA收到的包会直接被丢弃。这块调试起来比较坑我会在最后的问题汇总里专门讲。连接整个系统的最后一步是在Block DesignBD里把MicroBlaze、AXI Interconnect、DMA、TEMAC和UART调试串口连起来。连接关系很标准但要注意MicroBlaze的时钟频率和DMA的时钟频率必须分频正确否则AXI总线时序采样出错你会看到DMA寄存器读写的返回值时对时错。5. 上板调试从引脚约束到以太网通了的全过程记录5.1 第一步先验证PHY链路别急着跑协议栈直接跑TCP/IP是最容易翻车的我的方法论是“分步验证、逐层打通”。第一步是让PHY芯片的输出时钟正常翻转。用ILA或示波器量一下PHY回传的RX_CLK和GTX_CLK引脚的波形如果这两个时钟没有后面的数据通路都是空中楼阁。常见原因是PHY没被正确复位或MDIO初始化失败。检查PHY复位引脚的电平逻辑方向很多PHY芯片是低电平复位但开发板上的三极管反相之后FPGA给的GPIO电平可能恰好反了。这个细节我在项目调试时排查了将近一天。第二步是确认MDIO能够正确读写PHY寄存器。在SDK中写一段简单代码读取PHY的ID寄存器地址为0x02和0x03如果返回的不是该PHY型号对应的ID就要检查MDIO的地址线配置和PHY地址引脚是否一致。读取失败时大概率是MDIO本身时序有问题——时钟极性或者管理接口没有使能。如果MDIO通信正常接着检查自动协商状态。读PHY的0x01基本状态寄存器bit5是自动协商完成标志bit2是链路建立标志。如果这个寄存器一直不置位说明PHY和交换机之间的物理链路有问题——网线、连接器、PHY的变压器都可能造成这种状态。开发板上网口指示灯不亮是最直观的提示。5.2 数据环回测试链路起来后数据通路还未必就绪。我习惯先在TEMAC层面做一个内部环回测试把IP核的发送数据直接环回到接收端不经过PHY。这样能快速验证MAC侧的发包和收包逻辑。TEMAC在配置界面里有一个“Loopback”选项或者可以通过修改寄存器动态切换。内部环回通过后再关掉环回改为通过PHY芯片做外部环回——直接把PHY的发送引脚和接收引脚在软件层面短接或者通过外部测试治具连接看看数据能否从FPGA发到PHY再收回来。这一步能验证RGMII时序约束是否正确、IO延时设置是否符合PHY的要求。5.3 结合LwIP实现TCP通信当链路稳定、数据通路完整后最后一步是SDK中用LwIP实现TCP或UDP通信。Xilinx为你提供的LwIP库位于BSP的标准外设库中你需要在BSP设置界面启用LwIP并配置网卡的基地址和中断号。使用Xilinx XAxiEthernet驱动时最关键的初始化顺序是先调用XAxiEthernet_Reset完成MAC复位再通过XAxiEthernet_SetMacAddress设置MAC地址接着调用XEmacLwip的初始化函数绑定LwIP协议栈。代码方面更省事的方法是参照Xilinx官方提供的lwip echo server示例它几乎就是现成的跑通模板。你需要做的只是改一下PHY地址和MAC地址然后在main函数中初始化DMA再调用网络服务器的启动函数。如果TCP连接无法建立最常见的定位方法是用串口查看LwIP的打印日志如果逻辑上发起的ARP请求一直收不到响应说明PHY虽然通了但MAC和DMA之间的缓冲区配置可能有问题如果ARP能通但TCP握手不成功就要检查TCP的MSS窗口参数以及LwIP的内存池配置SDK中默认的MEMP_NUM_TCP_SEG可能偏小。6. 常见问题与排查技巧实录6.1 经典问题速查表我把做这个项目期间遇到的高频问题整理成一张表格现象可能原因排查思路PHY不产生125MHz时钟复位电平不对 / 晶振未起振量晶振引脚电压查复位逻辑MDIO读写全为0xFFFFPHY地址错误 / MDC时钟极性反根据原理图确认PHY ADDR引脚调整时序网口指示灯不亮自动协商失败 / 网线松检查PHY状态寄存器换线数据收发超时DMA描述符地址未对齐确保描述符64字节对齐偶尔丢包RX时钟约束不完整 / PHY上的RXC相位不对调整RGMII RX的IDELAY重建时序约束LwIP初始化卡死中断号配置错 / DMA未使能检查BSP中断配置读DMA状态寄存器6.2 RGMII时序问题的根源与处理所有RGMII调不通的问题九成出在时钟相位上。RGMII标准本身有v1.2和v1.3两个版本v1.3里接收数据相对于时钟有大概2ns的延时发送数据没有而v1.2则反过来。如果你的PHY芯片支持v1.3模式但FPGA侧的约束没有匹配这个2ns延时接收数据就会不稳定。处理方式有两个一是硬件层面在PCB上或通过FPGA内部的IDELAY模块调整RX_DATA的相位二是软件层面在SDK中对PHY芯片的寄存器进行配置把RX时钟的延时选项打开。对于RTL8211这类PHY寄存器里专门有控制组延时和时钟延时的位。调试时不要一次加太多以0.25ns为步进逐步调整发给对端一个持续的ping观察丢包率变化直到完全稳定。6.3 MicroBlaze和DMA联调的坑纯FPGA平台使用MicroBlaze时要特别注意AXI总线的跨时钟域问题。如果MicroBlaze运行在100MHzDMA和TEMAC运行在125MHz中间必须通过AXI Clock Converter做频率匹配。很多人忽略此细节导致DMA寄存器写进去的值读出来不对。我建议在Block Design里显式添加AXI Clock Converter和AXI Register Slice。Register Slice有“寄存器打拍”的功能在高频和长布线下可以提供更好的时序余量虽然会引入1拍延时但对于以太网场景完全可忽略。还有一个坑是MicroBlaze的本地存储器大小SDK里默认的local memory可能只有8KB跑LwIP时会频繁页错误务必在BD配置里加大到至少64KB。6.4 关于“vivado生成比特流失败”的补充很多人在综合和实现阶段就卡住了报错原因千奇百怪我总结下来主要是这几类第一IP核之间的接口不匹配导致端口悬空第二时序约束文件缺失导致综合工具无法实现时序收敛第三引脚约束冲突比如同一引脚被两个模块占用或者约束文件里写入了不存在的引脚名称。排查这类问题时我常用的做法是从Implementation的最终报告里看“Route”阶段是否有高扇出或者严重负slack的路径。如果出现负slack优先查看报告里高亮的路径是哪个模块产生的——绝大多数是跨时钟域的异步逻辑没用同步器或者RGMII数据线上的IO延时没有给够。这个信息往往比上网搜报错码更有效。7. 最后分享一点个人心得折腾完这套基于Vivado 2019.1的以太网接口工程最大的感受是以太网接口本身并不难难点往往在“看不见”的地方——时序约束的完整性、PHY芯片的细节差异、DMA内存管理的对齐规则以及不同IP核之间的时钟搭配。把这些细节逐一搞定之后整个系统就像流水线一样顺畅运转。如果你也是第一次亲手搭这类接口我的建议是先花半天时间把原理图完整看明白特别是PHY芯片的复位逻辑和地址配置这一步做扎实了后面能省出至少两天的排查时间。再用官方示例工程串一遍流程最后再逐步替换成自己的需求逻辑千万别一上来就指望从头到尾自己写所有代码。按照这个节奏走绝大多数开发板都能顺利把千兆网口跑起来这之后再去修改中断、增加自定义逻辑、扩展多路网口都是水到渠成的事情。
返回列表