
板子拿回来第一天我就想直接把网口调通结果在VIVADO 2019.1里折腾了好几天才从零把一个以太网接口跑起来。回想整个过程最熬人的不是写逻辑而是那些“看起来没问题但就是不工作”的硬件细节。这篇文章把我在VIVADO 2019.1上搭建以太网接口的完整过程、踩坑记录和排查思路写出来给正准备做同样事情的你一个参考。VIVADO 2019.1目前是很多中低端FPGA开发板的主力版本像Artix-7系列的板子官方教程大多用这个版本。以太网接口在FPGA项目里属于“看着简单、做起来全是坑”的部分。它涉及的链路很长PHY芯片、MAC逻辑、DMA数据通路、CPU协议栈每一层都可能成为瓶颈。1. 方案选型在VIVADO里搭以太网接口你到底需要什么1.1 先分清自己是哪条路Zynq硬核还是纯FPGA软核方案开始之前先问自己一个最关键的问题你的芯片上有没有以太网硬核MAC。如果你用的是Zynq-7000事情会简单很多。Zynq的PS侧自带千兆以太网MAC也就是常说的GEMGigabit Ethernet MAC。你要做的只是把PS的MIO或EMIO引脚连到外部的PHY芯片上然后在SDK里配置BSP根本不需要在PL侧搭建以太网IP核。很多网友把Zynq和纯FPGA的方案混在一起看到别人的AXI Ethernet教程就去抄结果自己的工程里根本不需要这个东西。如果你是纯FPGA比如Artix-7或者Kintex-7情况完全不同。芯片内部没有硬核MAC必须用PL侧逻辑实现。VIVADO里有两个现成IP可以用AXI Ethernet和AXI Ethernet Lite。对于绝大多数项目选择AXI Ethernet这就是你的MAC层。在做技术选型时还要看一下板子的PHY芯片型号和接口。比如常见的88E1512、RTL8211、YT8531这些PHY都支持RGMII。如果你的板子用的是GMII接口的老PHY或者SGMII接口的PHY就要相应的选择对应的PHY Interface配置项。还有一点需要提前确认VIVADO 2019.1的IP核支持情况。同一个IP核在不同版本里的默认配置和Bug修复都不一样如果你用的板子需要在2019.1上开发建议先查一下Xilinx官方文档中AXI Ethernet IP核在这个版本的支持状态。我个人用的2019.1版本跑Artix-7的RGMII是完全没有问题的但如果是UltraScale的部分新器件建议换到更新的版本来做。1.2 AXI Ethernet和AXI Ethernet Lite怎么选打开VIVADO 2019.1的IP Catalog搜索“ethernet”你会看到两个关键词AXI Ethernet和AXI Ethernet Lite。AXI Ethernet Lite是一个简化版实现特点是资源占用低逻辑结构简单但数据通路是AXI4-LiteCPU需要逐个寄存器读写效率非常低。它适合什么场景呢比如你只是需要一个远程调试口跑一点点控制数据或者做教学演示那它够用。但你只要想跑TCP/IP协议栈、收发视频流、传UART透传数据Lite版本马上变成瓶颈。AXI Ethernet才是完整版本。它支持AXI4-Stream数据接口还能在IP核内部集成DMA控制器也就是Scatter Gather模式。数据可以自动从内存搬运到MAC不需要CPU干预性能高得多。我在选型时的判断标准很简单只要板子上PHY能跑千兆就直接用AXI Ethernet。不要考虑Lite因为后续一旦性能不够迁移完整版的工作量比一开始直接用大得多。这个道理跟写代码一样先用最简单的方式跑通再考虑优化的思路在这里并不成立。1.3 接口MAC/PHY分工GMII、RGMII和SGMII的区别AXI Ethernet配置界面里会要求你选择PHY Interface。这里不是让你选板载PHY芯片的品牌而是选MAC和PHY之间的物理接口类型。GMII是经典的并行接口。发送和接收都用8位数据线加上控制信号和时钟需要20多根线。它每个时钟周期采8位125MHz时就是千兆。缺点是引脚太多布线麻烦现在新板子已经很少用GMII了。RGMII是GMII的简化版数据线减半4根发送、4根接收利用时钟的双沿采样来保住同样的带宽。RGMII最大的好处是引脚少很多PHY芯片都支持是当前最主流的MAC-PHY连接方式。对应的时钟在千兆时是125MHz。SGMII是串行接口数据通过一对差分线跑引脚最少常用在背板、交换机这种需要高速走线的场景。SGMII通常要借助FPGA的高速收发器比如Artix-7上的GTP配置复杂度比RGMII高一个级别功耗和资源占用也更大。如果你只是做一块普通的开发板或者嵌入式控制板RGMII是性价比最高的选择。2019.1版本的AXI Ethernet核在配置页面对RGMII支持得很好下面我会把具体的参数解释一下。2. 工程准备与IP核配置从空工程到AXI Ethernet跑起来的几个关键动作2.1 新建工程时的版本选择、安装路径和许可证问题VIVADO 2019.1这个版本有个特点它对7系列的支持非常稳定但安装时不能图省事装精简版否则很多IP核用不了。安装时要勾选对应的器件族比如Artix-7就勾Artix-7Zynq就勾Zynq-7000。否则你创建工程时找不到目标器件就算找到了IP Catalog里也没有AXI Ethernet核。安装路径不能有中文和空格这个问题老生常谈了但还是有人踩。装好之后许可证要单独处理Xilinx的license就是那个.lic文件在Help - License Manager里导入。如果你的license不包含对应器件或IP的授权生成比特流或仿真时会报2035类似的错误。很多网友遇到的“vivado注册 2035”其实就是License验证失败的典型报错卡住的人非常多。解决办法要么换一个完整的license要么把不需要的IP从工程里禁用掉。新建工程时器件型号一定要选对。以Arty A7为例型号是xc7a35tcsg324-1。这里面csg324是封装-1是速度等级差一个字母后期约束全是问题。选错了器件管脚名和时序模型全对不上工程等于白建。2.2 添加AXI Ethernet IP核时哪些参数必须核对双击IP Catalog里的AXI Ethernet进入配置界面我建议按下面这几项逐一看DMA Type这个选项决定是否在IP核内部集成DMA。建议选Scatter Gather这样CPU下发数据时只需要维护描述符IP核自动搬运数据到内存效率高很多。如果选None后面就得自己在Block Design里接一个AXI DMA多一层连线就多一层出错的可能。PHY Interface按板子的实际接口选。我的是RGMII就选RGMII。Reference Clock Frequency填125。这里指的是IP核内部逻辑使用的参考时钟不是PHY芯片的晶振注意别混淆。Enable MDIO勾上。MDIO是用来读写PHY寄存器的链路状态、速率协商都要靠它。Number of Channels一般选1个就够用。多通道是给特殊场景的比如网口备份和聚合。配置完这些点击OK生成IP核。你会在Block Design里看到一堆端口这个时候先别急着连先想清楚你的数据通路是怎么走的。2.3 时钟的来龙去脉125MHz为什么这么关键以太网最核心的时钟就是125MHz。千兆RGMII需要125MHz的TX时钟百兆是25MHz十兆是2.5MHz。AXI Ethernet核会根据当前协商速率自动调整内部逻辑但外部必须给它提供正确的基准时钟。最可靠的方案是板载晶振比如50MHz或100MHz进Clock Wizard通过MMCM/PLL生成125MHz和25MHz。125MHz提供给AXI Ethernet的GTX_CLK25MHz可以接到PHY的时钟输入或者给MAC做百兆参考时钟。有一个坑我印象特别深单独给AXI Ethernet的gtx_clk连了时钟却忘了给ref_clk连时钟。综合能过实现也能过但上板之后网口完全不通。后来用VIVADO的report_clocks查了下发现ref_clk根本没有时钟源IP核内部的逻辑全在空跑。在XDC里外部输入时钟要显式约束。比如板子上的系统时钟是100MHz那么至少要写create_clock -period 10.000 -name system_clk [get_ports clk_100m]如果不创建VIVADO会默认给你推导但推导出来的时钟网络经常不是你想要的那个后面的时序报告也会误导你。而像rx_clk这样的PHY输出时钟也要记得约束否则ILA采集时会出现奇怪的采样结果。3. 连线、地址与约束Block Design里最容易翻车的地带3.1 带内置DMA的AXI Ethernet如何和MicroBlaze互联我推荐把DMA Type选成Scatter Gather所以Block Design里不需要单独加AXI DMA。这时AXI Ethernet对外暴露的主要接口包括S_AXI管理寄存器接口供MicroBlaze配置MAC和DMA寄存器。AXI_MM2S和AXI_S2MM这是内部DMA读写内存的接口连到axi_interconnect最后接DDR控制器。AXI_Stream接口内部已经和MAC连好外部不可见。TX_INTR和RX_INTRDMA发送和接收的中断输出。PHY接口RGMII引脚、MDIO引脚。MicroBlaze这边M_AXI_DC和M_AXI_IC接axi_interconnect的S_AXI侧即可。然后axi_interconnect的M_AXI分别连到AXI Ethernet的S_AXI和它的MM2S/S2MM接口。如果你用的是DDR3/DDR4内存别忘了在Block Design里加一个MIGMemory Interface Generator或者直接加一个AXI DDR控制器把DDR的地址空间映射出来。否则MicroBlaze能运行程序但没内存lwIP一分配报文缓冲区就崩。一个常见的地址映射错误给AXI Ethernet的寄存器地址分配了和DDR重叠的地址段结果软件访问寄存器时实际访问到了DDR读出来的数据自然不对。Block Design的Address Editor里要仔细检查所有外设的地址段必须唯一不能重叠。3.2 中断信号怎么连优先级怎么处理只要用了Scatter Gather模式以太网的收发就依赖中断。TX_INTR和RX_INTR这两个中断信号要连接到中断控制器axi_intc。连接步骤添加axi_intc IP核配置为单通道或双通道均可。把TX_INTR和RX_INTR分别连到axi_intc的intr端口注意intr是多bit的INTC输入每个bit对应一个中断源。把axi_intc的irq连到MicroBlaze的INTERRUPT输入。在SDK中BSP会自动生成xintc的驱动。应用里需要给每个中断源注册回调函数。优先级分配一般不用太纠结但要注意如果多个中断源共用一个axi_intc默认情况下同一个intc产生的中断优先级是可以通过寄存器配置的但在小系统里只要保证不丢中断就行。我的经验是接收中断RX_INTR的优先级给高一点发送中断可以略低这样接收侧不容易丢包。3.3 XDC管脚约束里那些“少写一行就废了”的约束Block Design连线完成后需要Create HDL Wrapper生成顶层然后新建XDC文件做管脚约束。RGMII接口的引脚约束比较直观比如set_property -dict {PACKAGE_PIN R2 IOSTANDARD LVCMOS33} [get_ports rgmii_txd[0]] set_property -dict {PACKAGE_PIN T3 IOSTANDARD LVCMOS33} [get_ports rgmii_tx_ctl]但要额外注意以下几点rx_clk是PHY输出给FPGA的必须声明为时钟create_clock -period 8.000 -name rgmii_rxc [get_ports rgmii_rxclk]PHY复位引脚大部分PHY复位是低有效所以FPGA侧要把它拉低才是正常。有些人写成拉高结果PHY一直处于复位状态网口灯不亮数据完全出不去。MDIO的MDC和MDIO引脚要加合适的IO标准MDIO的信号质量要求没那么苛刻但MDC是时钟最好也加上create_clock约束让VIVADO知道它是时钟。如果板子上PHY芯片有独立的LED引脚也要约束否则PHY的link状态灯不亮你根本不知道线的状态。约束写完直接跑综合和实现。生成比特流失败是最常见的现象之一绝大多数失败原因是时序收敛不过去。我都建议先点开Report Timing Summary看是哪个时钟域timing violation90%的情况都是某个外部时钟没有约束或者约束的周期不对。4. 软件侧SDK、BSP和lwIP的配置才是真正的分水岭4.1 导出硬件到SDK时容易忽略的选项硬件工程跑了Generate Bitstream后下一步是File - Export Hardware注意勾选Include bitstream。如果不勾选SDK里没有比特流下载程序时会提示器件没有被配置。打开SDK后新建Application Project。Board Support Package的创建向导会让你选择OS和库这里务必在库列表里勾选lwip和xilffs如果需要文件系统。BSP生成后右键BSP目录选Board Support Package Settings可以修改lwIP的配置。在SDK版本匹配方面2019.1自带的SDK对应lwIP有v1.4.1和v2.0.x两个选项。我建议选v2.0.x。v1.4.1虽然老资料多但API有些地方和新的lwIP不兼容尤其是RAW API的注册方式。这里插一句有些朋友在PC上遇到过“续订接口以太网时出错无法联系DHCP服务器请求超时”那个问题多半是Windows网络栈或者路由器的问题跟FPGA这边自己搭的DHCP客户端完全不是一回事。在板子上跑DHCP时你更该关心的是PC端抓包能不能看到DHCP Discover报文。4.2 lwIP的缓冲区配置直接影响性能和稳定性BSP里的lwIP配置项很多这里只挑三个我觉得最关键的说MEM_SIZElwIP堆内存总大小。默认值可能只有几十KB如果跑TCP大数据很容易内存不足。建议调到150KB以上纯FPGA的MicroBlaze一般有足够BRAM。PBUF_POOL_SIZE报文缓冲区数量。这个直接决定你在高负载下能缓存多少包。收包速度大于CPU处理速度时池子小了就会丢包。建议调到32以上。PBUF_POOL_BUFSIZE单个缓冲区大小。至少要能装下一个最大以太网帧也就是1518字节最好留点余量设成1600。除了这三个还有MEMP_NUM_TCP_PCB、MEMP_NUM_UDP_PCB这些协议控制块数量如果你要同时开15个TCP连接默认的数量不够需要调大。改完这些参数后重新生成BSP再编译应用工程。这里有一个坑BSP配置改动后应用工程的库文件路径和宏定义会自动更新但有时候编译不会触发重新编译你会看到旧版本的配置还在生效。解决办法是Clean一下工程Project - Clean再重新Build。4.3 最小可跑的lwIP代码从PHY初始化到UDP echo在SDK里BSP会自动生成lwIP和以太网驱动的基础框架。你只需要在main函数里完成几件事初始化以太网MAC调用对应驱动设置PHY地址。PHY地址一般在硬件原理图上标注比如很多PHY的地址是0x00或0x04。设置MAC地址、IP地址、子网掩码。启动网络接口进入lwIP并存。注册UDP echo回调。以RAW API为例伪代码大概是#include lwip/init.h #include lwip/udp.h #include netif/xadapter.h static struct netif netif; static void udp_echo_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { udp_sendto(pcb, p, addr, port); pbuf_free(p); } int main(void) { lwip_init(); IP4_ADDR(ipaddr, 192, 168, 1, 10); IP4_ADDR(netmask, 255, 255, 255, 0); IP4_ADDR(gw, 192, 168, 1, 1); netif_add(netif, ipaddr, netmask, gw, NULL, ethernetif_init, ethernet_input); netif_set_default(netif); netif_set_up(netif); struct udp_pcb *pcb udp_new(); udp_bind(pcb, IP_ADDR_ANY, 5001); udp_recv(pcb, udp_echo_callback, NULL); while (1) { ethernetif_poll(); } return 0; }这段代码里ethernetif_poll的调用频率是关键。如果你的BSP配了中断驱动那么不需要轮询如果是轮询模式主循环要尽可能多的调用否则吞吐上不去。我最开始照抄网上Zynq例程结果那个例程里用的是emacps驱动和纯FPGA的AXI Ethernet驱动xemac完全不一样编译报错一堆。这里提醒一下纯FPGA项目千万不要照抄Zynq的PS侧例程要找AXI Ethernet对应的例程。5. 实测阶段从“网口灯亮”到“ping通”、再到“大包不丢”5.1 ILA抓信号看不到波形之前别乱猜在没有逻辑分析仪的情况下ILA是你最好的朋友。在Vivado 2019.1里ILA核可以直接插入到Block Design里也可以插入到RTL的任意信号处。我的做法是在AXI Ethernet的PHY接口上插一组观察点重点观察rgmii_tx_ctl / rgmii_txd[3:0]rgmii_rx_ctl / rgmii_rxd[3:0]gtxx_clk触发条件可以设置为rgmii_tx_ctl1这样只要FPGA发出数据ILA就会抓到。采样深度建议1024或2048因为一个以太网帧最短也有64字节512深度的ILA可能只抓到半个帧分析起来很被动。如果你发现ILA触发不了说明MAC根本没尝试发送数据问题多半在DMA或者软件侧。如果触发了但波形里全是0或者不变那就要往时钟和复位的方向查。ILA的采样频率不是越高越好。对于125MHz的RGMII时钟ILA用250MHz采样能看清双沿但如果你用500MHz数据可能会因为采样对齐问题出现毛刺。尽量让ILA的采样时钟和被测时钟同源这样抓出来的波形才真实。5.2 ping不通的排查链路从底层往上层一层层排除ping不通的时候我一般按这个顺序排查能省很多时间第一步看PHY的link灯。灯不亮那就是物理层问题。先查复位信号、时钟、电源。 第二步用串口或SDK读PHY寄存器。读寄存器0和1bit状态能看出速率和link状态。如果读出来全是0xFF或全0说明MDIO根本没通查MDC/MDIO引脚连接和PHY地址。 第三步PC端Wireshark抓包。如果PC发ARP请求板子没有回复说明问题在MAC或DMA。如果板子回复了ARP那二层是通的继续查IP层和ICMP。 第四步确认板子的IP、MAC没有被重复配置。如果板子和PC在同一个网段但板子MAC地址和虚拟机的某个虚拟网卡冲突ARP就会出问题导致ping不通。遇到“ping第一次通第二次不通”多数是lwIP的ARP表老化或者DMA描述符没释放。把PBUF_POOL_SIZE和描述符数量调大之后这种问题会明显缓解。5.3 大包传输失败和吞吐不达标的调优记录我有一次调UDP传输PC发1500字节的包板子收不全总是在后半段丢数据。排查了半天最后发现是PBUF_POOL_BUFSIZE设置太小只有800字节大于这个值的包分片后直接被lwIP丢弃。解决办法就是前面说的把缓冲区至少调到1600字节。另外如果PHY支持Jumbo Frame而板子需要传超大帧那还要同步调大IP核的FIFO深度和DMA描述符的大小。吞吐不达标的问题更常见。我用裸机lwIP跑UDP刚开始吞吐只有几Mbps后来做了几个优化把MicroBlaze的主频从100MHz提高到150MHz。开启MicroBlaze的指令缓存和数据缓存注意数据缓存要和DDR地址空间打通。把DMA描述符数量从默认的16增加到64。把lwIP的TCP窗口调大TCP_SND_BUF和TCP_WND都加大。优化后UDP单向吞吐能跑到700Mbps左右接近千兆线速TCP也有明显提升。这个数字在Zynq或者带硬核MAC的平台上可能不稀奇但在纯FPGA软核方案里已经是不错的成绩了。最后说一点我在实际项目里的个人体会。VIVADO 2019.1搭建以太网接口难的不是某个IP核配置而是整条链路太长每个环节都有可能是坑。遇到问题一定要用ILA和串口日志把问题边界缩小不要上来就猜代码。XDC里所有外部输入时钟都必须主动约束这个习惯能让你在后面少走非常多的弯路。这些经验是我在Artix-7平台上踩出来的换一块板子、换一颗PHY芯片步骤基本类似但引脚、PHY地址、时钟频率这些细节还是要以你手上的板子为准。祝大家一次点亮网口。