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 MAC(TEMAC)IP核,或者自己用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-Stream+CMS-DMA”的方式。TEMAC IP核输出的是AXI4-Stream接口,可以对接Xilinx官方的AXI Ethernet DMA IP核,然后通过AXI-Lite或AXI4接口连到PS端的DDR或者MicroBlaze上。如果你用的是Zynq系列的SoC,甚至可以直接把DMA中断接到PS端,让ARM处理器运行LwIP协议栈,实现一个真正意义上的嵌入式网络终端。
这里有个选择要点:Zynq平台优先推荐直接用PS端的GEM(Gigabit Ethernet MAC)控制器,外部PHY通过RGMII连接,无需在PL侧做任何MAC逻辑,这是最省资源的方案。但如果你用的是纯FPGA(比如Artix-7),就必须在PL侧例化TEMAC和DMA,再通过AXI接口挂上MicroBlaze,这是一个典型的软核SoC方案。两种方案的架构差别很大,下文我会以纯FPGA+MicroBlaze为主线展开,因为这个方案更通用,能帮你把以太网整个协议栈的链路给串起来。
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回传给FPGA;FPGA发送数据时,需要提供一个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 Gather(SG)模式,适合跑Linux系统;另一种是简单的寄存器模式,适合裸机跑LwIP。我用的是SG模式,因为SDK里Xilinx提供的LwIP库对SG模式的支持最成熟,配置好BD之后基本零代码就能跑通TCP通信。
DMA IP核配置时,有一项“Width of Buffer Length Register”我建议保持默认的14位,支持的包长上限是16384字节,足够应对标准以太网帧(最大1518字节)。关于DMA的描述符缓冲区,地址要放在DDR中并且按64字节对齐,如果你的内存控制器还没有初始化好就启动DMA,收到的包会直接被丢弃。这块调试起来比较坑,我会在最后的问题汇总里专门讲。
连接整个系统的最后一步是在Block Design(BD)里把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读写全为0xFFFF | PHY地址错误 / 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运行在100MHz,DMA和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芯片的复位逻辑和地址配置,这一步做扎实了,后面能省出至少两天的排查时间。再用官方示例工程串一遍流程,最后再逐步替换成自己的需求逻辑,千万别一上来就指望从头到尾自己写所有代码。按照这个节奏走,绝大多数开发板都能顺利把千兆网口跑起来,这之后再去修改中断、增加自定义逻辑、扩展多路网口,都是水到渠成的事情。