恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
ZYNQ 7020 FPGA驱动UDP实现千兆以太网:从MAC到RGMII时序调试
首页
资讯中心
/
ZYNQ 7020 FPGA驱动UDP实现千兆以太网:从MAC到RGMII时序调试
ZYNQ 7020 FPGA驱动UDP实现千兆以太网:从MAC到RGMII时序调试
发布时间:2026/9/16 14:57:57
简介面向ZYNQ 7020嵌入式开发者的以太网UDP通信FPGA驱动资源完整演示了PL端实现UDP引擎、RGMII/GMII接口与DMA传输并配合ARM端驱动完成ARP、IP、UDP协议栈交互。资源包共610个文件、约49.5MB核心包括Verilog/VHDL设计代码、XDC约束与Vivado工程文件另含Tcl/Shell编译脚本、do仿真脚本、txt说明文档、IP核配置及综合报告覆盖从RTL设计、仿真验证到生成比特流的完整流程。已有996人学习适合正在学习FPGA网络通信或需要快速搭建ZYNQ UDP收发链路的读者。通过分析项目代码可掌握UDP帧解析与校验、PHY芯片连接、DMA内存管理及中断处理等关键环节同时示例应用与驱动源码帮助理解软硬件协同工作方式整体是一套兼顾原理讲解与工程实践的完整参考。压缩包内目录结构清晰RTL设计与软件驱动分置配合顶层工程文件和比特流文件读者可直接加载运行观察效果。1. 拿到 ZYNQ 7020 管以太网为什么偏要让 FPGA 自己驱动 UDP把以太网接到 ZYNQ 7020 上最容易想到的方案是启动 Linux、打开 socket把数据交给 ARM 内核。但“FPGA驱动”这几个字的潜台词是完全不同的路径MAC、RGMII 接口、IP/UDP 封装都在 PL 侧完成PS 只负责寄存器配置和报文搬运包收发过程不打断 CPU。好处非常直接千兆端口能跑到接近线速单包延迟从毫秒级压到微秒级还能在 FPGA 里给每个 UDP 包盖上硬件时间戳。这个方案尤其适合图像采集、ADC 采样回传、以及不想被操作系统的调度抖动干扰的数据通路。对于已经玩过 FPGA 最小系统、想往高速接口方向走的人来说这条路比在 PS 上调协议栈更能理解以太网本身。2. 三速以太网 MAC 与数据通路先选型再做 UDP2.1 三条路线对比PS 侧协议栈、PSLinux 还是 PL 驱动在 ZYNQ 7020 上做 UDP 通信一共有三种常见做法。初学者往往选第一条但标题既然强调 FPGA 驱动就值得把三条路放在一起对比。实现方案位置PS 资源占用延迟与吞吐特点适合场景裸机 LwIPPS 侧 GEM MACCPU 参与中断和拷贝收包延迟几十微秒到毫秒级控制指令、小数据量Linux socketPS 侧 GEM MAC依赖内核协议栈调度吞吐高但延迟不定功能复杂、多进程协作PL 驱动 TEMAC 自研 UDPFPGA 内实现 MAC 和协议PS 基本不参与数据面延迟微秒级、接近线速、可加硬件时间戳我一般会把资源账先算清楚ZYNQ 7020 有 53,200 个 LUT 和 140 个 BRAM一个三速以太网 MAC 加上收发 FIFO 大约吃掉 3000 到 5000 个 LUT如果只做千兆 UDP 而不做 TCP协议栈在 FPGA 里的开销很小。剩下的逻辑资源足够给应用留出空间。所以只要你的 UDP 包不是每毫秒一个的小控制包而是希望持续传图像的码流第三条路是更可靠的选择。2.2 RGMII 的时钟与 IDELAY千兆 UDP 的第一道坎选择第三条路之后第一个要面对的现实问题是 RGMII 接口。千兆模式下TXC 时钟是 125MHz数据在上下沿同时采样DDR 模式下每个 RX 时钟沿都要采到 4 bit两个沿拼成一个字节。数据与时钟之间的相位关系由 PCB 走线决定同一组信号在到达 FPGA 引脚时偏差动不动就超过 0.5ns。所以 TEMAC IP 核接收到 RGMII 信号后会用 IDELAY 在数据引脚上做延时调整。典型调试流程是这样先用 ILA 抓 RX_DV 和 RX_DATA观察数据变化沿是不是多次跨越了时钟采样沿再通过 AXI-Lite 接口改写 IDELAY_VALUE每加 1 表示约 78ps直到眼图稳定。如果你在 Vitis 里做 PSPL 联合调试也可以把 IDELAY 调成从 0 扫到 31看哪个区间链路不出 CRC 错误。这个参数不要迷信 PHY 芯片的参考设计必须拿实测数据来定。2.3 数据通路AXI Stream 进、AXI Stream 出PL 驱动 UDP 的结构不复杂核心是一条从 TEMAC 到用户逻辑的 AXI-Stream 通路。接收方向TEMAC 解析完以太网帧头、校验 FCS 后把有效负载以 AXI-Stream 包的形式推送出来发送方向用户逻辑填好 UDP/IP/MAC 头把整个帧交给 TEMAC 生成 FCS 发出去。中间要插入 FIFO 做跨时钟域和速率匹配。// AXI-Stream 接口收发 FIFO 的核心握手 assign s_axis_tready fifo_almost_full ? 1b0 : 1b1; always (posedge clk) begin if (s_axis_tvalid s_axis_tready) begin fifo_wr_data s_axis_tdata; fifo_wr_en 1b1; end end // tlast 表示一个 UDP 包的结束进 FIFO 时同样需要保存这里的参数理解很简单tvalid 由发送端拉高表示数据有效tready 由接收端给出表示可以接收只有当两者同时为高时数据才算真正传走tlast 标志一个包的末尾。很多人在调 FIFO 深度时只看 tdata忘了一包数据里可能有多个 tkeep 非全 1 的尾拍。如果要让 PS 侧感知一包数据是否完整到达常见做法是把 FIFO 写指针存到 AXI-Lite 寄存器里让软件判断当前包长度是否等于预期值这个思路可以顺便解决“FPGA IP 核缓存索引重组”的需求。3. 在 FPGA 里实现 UDP帧格式、CRC32 与校验和3.1 以太网帧格式与 UDP 包字段对照在 PL 里写 UDP本质是在拼一串字节流。以太网帧结构固定为目的 MAC 6 字节、源 MAC 6 字节、EtherType 2 字节IPv4 是 0x0800、IP 头 20 字节、UDP 头 8 字节、负载长度不定最后跟 4 字节 FCS由硬件自动生成。把整个帧按偏移拆开就能对应到 RGMII 总线上的每一个字节。偏移字段长度内容与注意点0x00目的 MAC6对端 MAC未知时先发 ARP0x06源 MAC6本机 MAC要和 TEMAC 配置一致0x0CEtherType20x0800 表示 IPv40x0EIP 头20含源/目的 IP、协议字段 170x22UDP 头8源端口、目的端口、长度、校验和0x2A应用负载不定MTU 1500 下最大 1472 字节帧尾FCS4CRC32通常由 MAC 硬件计算实际发送时不需要软件填 FCSTEMAC 会在发完数据后自动追加。最容易看错的是 UDP 长度字段它包含 UDP 头本身所以值等于 8 加负载长度而 IP 总长度是 20 加 UDP 长度。这两个长度差一字节在 Wireshark 里会直接显示校验不通过。3.2 CRC32 查表法FCS 计算与结果取反如果不用 TEMAC 自带的 FCS 生成功能而想在逻辑里自己算 CRC32一定要按 IEEE 802.3 的标准来初值 0xFFFFFFFF、多项式 0x04C11DB7、结果按位取反后低字节在前发送。逐位计算在千兆下会消耗大量 LUT常见做法是查表法每个时钟处理一个字节。// CRC32 查表法核心逻辑多项式 0x04C11DB7 // 每个时钟周期处理 8 bit在包结束后对结果取反 reg [31:0] crc; wire [31:0] crc_next; assign crc_next (crc 8) ^ crc_table[data_in ^ crc[7:0]]; always (posedge clk or posedge rst) begin if (rst) crc 32hFFFFFFFF; else if (crc_en) crc crc_next; end // 取反输出FCS ~crc代码里最关键的是查表索引取法用当前输入字节异或 crc 的低 8 位作为表下标而不是直接用输入字节。CRC 表需要用软件生成一次并固化成 ROM 数据256 个 32bit 项正好占一个 BRAM。注意 CRC 模块使能信号 crc_en 要覆盖从帧头第一个字节到负载最后一个字节的全部有效数据不能把空闲周期算进去否则对端一收包就报 FCS 错误。3.3 IP 校验和与 UDP 校验和两个容易算错的地方IP 头校验和覆盖 IP 头 20 字节算法是把每 16 bit 当成一个数累加溢出回卷最后取反。UDP 校验和则要额外包含伪首部源 IP、目的 IP、协议号 17、UDP 长度再连 UDP 头和数据一起参与累加。很多 FPGA 例程在传输模式下直接忽略 UDP 校验和把该字段填 0这在纯内网实验里通常能通但一旦经过三层交换机或某些防火墙UDP 包会被直接丢弃排查起来非常费劲。// UDP 校验和的惰性累加在发送最后两个字节前把结果填入 reg [31:0] checksum_acc; always (posedge clk) begin if (frame_active byte_index[0]) // 每两个字节一累加 checksum_acc checksum_acc {payload[i], payload[i1]}; end // 累加完成后需将高 16bit 回卷加到低 16bit再取反这里要理解“惰性计算”的含义不是边收边算而是先把校验和字段所在位置留成 0x0000等负载全部发送完毕、累加器停下时再在时钟间隙把最终结果计算出来插入到 UDP 头的校验和位置。这样做的好处是不需要等数据收完再重发一遍也避免了乒乓缓存两帧数据。要注意的是 IP 头校验和只算 IP 头UDP 校验和则连伪首部一起算两者不能共用一个累加器。3.4 ARP 不处理UDP 就发不出去纯 PL 方案里最容易被忽略的是 ARP。PC 第一次向 FPGA 发 UDP 前会发现自己的 ARP 表里没有 FPGA 的 MAC先发一个广播 ARP 请求如果 FPGA 不回PC 根本不会把 UDP 包送出来。界面上看到的现象是网络调试助手显示“发送失败”或一直停留在 ARP 阶段。常见的处理有两种一种是在 PL 里实现一个极简 ARP 应答模块收到请求时把自己的 IP/MAC 填进 ARP 响应返回另一种图省事在 PC 上先 ping 一次 FPGA把 ARP 缓存填满再发 UDP。除了 ARP 应答还要记得处理对端主动发来的 ARP 请求这样在重启板卡之后不需要 PC 手动清缓存。为了节省逻辑ARP 模块只回请求、不发探测即可绝大多数场景够用。4. Vivado 实操从 IP 配置到 XDC 约束4.1 添加三速以太网 MAC 与时钟方案在 Vivado 里建 Block Design最麻烦的不是连线而是 IP 配置。添加“Tri-Mode Ethernet MAC”IP 后先把它配置为 1000Mbps RGMIIPHY 接口选择 RGMII管理接口选择 MDIO。注意 IP 内部会有几个时钟域时钟信号频率来源与作用gt_txclk125MHz由 MMCM 生成送给 RGMII TXrx_clk125MHz由 PHY 提供作为 RX 侧参考user_clk125MHzAXI-Stream 接口的主时钟生成 IP 后需要在顶层例化 mmcm 和 idelayctrl。IDELAYCTRL 的参考时钟通常用 200MHz如果漏掉它ILA 里会看到 rx_data 完全错乱或全部为 0。更隐蔽的坑是 TEMAC 的 MDIO 接口MDIO 时钟由 AXI-Lite 时钟分频产生但 MDC 频率不能超过 2.5MHz在 PS 侧驱动时需要先配好分频寄存器否则软件读不回 PHY 的状态寄存器链路协商状态全都不对。4.2 XDC 约束模板RGMII 时钟分组与 input delayRGMII 接口的时序约束是整个工程里最关键也最容易出错的地方。千兆模式下 RX 数据和 RX_CLK 都是 DDR 信号时钟在板上会有一段几十到几百 ps 的错位必须用 set_input_delay 约束这个错位范围# RGMII 接收时序约束模板具体延时由 PCB 与 PHY 手册决定 set_property PACKAGE_PIN AK15 [get_ports eth_rx_clk] set_property IOSTANDARD LVCMOS33 [get_ports eth_rx_clk] set_input_delay -clock [get_clocks eth_rx_c] -max 1.800 [get_ports {eth_rxd[*] eth_rx_ctl}] set_input_delay -clock [get_clocks eth_rx_c] -min 0.400 [get_ports {eth_rxd[*] eth_rx_ctl}]这里的输入延迟是相对于 RX 时钟实现对数据的偏移具体数值由 PHY 芯片 Tco、PCB 走线和温度共同决定。我一般会把 max 和 min 留足余量然后靠 IDELAY 来精确对准。约束写完先跑实现查看时序报告中 RGMII 接收路径是否有负裕量。如果负裕量非常大优先检查约束里时钟对象是不是选对了很多人直接把 PHY 提供的时钟和内部 user_clk 放同一组结果时序永远做不开。应把 eth_rx_clk 设为独立时钟RX 数据用 set_input_delay用户逻辑和 TEMAC 之间的内部时钟则走默认路径。4.3 用 ILA 抓 RGMII确认数据对齐烧录之后先用 ILA 抓到 RX 引脚上的波形看数据沿是否在采样窗口内。具体做法是把 eth_rx_clk、eth_rxd、eth_rx_ctl 加进 ILA 探针采样深度设 4096触发条件设为 rx_ctl 上升沿。抓回来的波形里如果数据跳变沿集中在时钟沿附近说明 IDELAY 没调够这时通过 AXI-Lite 改写 IDELAY_VALUE观察数据保持时间变宽直到 FCS 错误消失。ILA 调试完成后建议把这些探针信号从综合属性里去掉避免给布局布线增加额外负担也不要让 ILA 逻辑残留到最终版本里。5. 仅靠这三招就能把 UDP 收发的坑排完5.1 第一招网络调试助手加回环验证数据通路板卡烧录后先用直连网线或交换机把 FPGA 和 PC 接起来PC 侧配置静态 IP比如 192.168.1.10FPGA 侧定成 192.168.1.20。打开网络调试助手选择 UDP 协议先发一段递增序列。如果 PC 发到 FPGA 后 FPGA 不回先查两个点一是 TEMAC 是否 link up读 PHY 的 BMSR 寄存器看千兆协商结果二是回环发送模块有没有把 tvalid 和 tlast 正确拼装。在这个阶段不要追求速度和效率只要确保 PC 能收到一个完整回包数据通路就算通了。5.2 第二招Wireshark 盯这三个字段Wireshark 抓到的以太网数据包能直接暴露 FPGA 侧的问题。重点看三个地方现象原因处理IP checksum incorrect软件/硬件计算错误或 PC 网卡校验卸载干扰关闭网卡 IPv4 Checksum Offload 再抓包UDP length 与 IP total length 不符FPGA 头组装时长度字段填错核对帧偏移表IP 长度必须等于 208负载FCS 错误或 Bad CRCRGMII 时序不对或 IDELAY 没调好用 ILA 看信号眼图逐级调整 IDELAY有个反直觉的点Wireshark 显示 IP checksum incorrect 不一定是 FPGA 算错。Windows 网卡默认开启校验和卸载发出的包本身就带错值抓包软件看到的反而是 FPGA 回包有问题。遇到这个问题先把 PC 网卡的高级属性里 IPv4 校验和卸载全部禁用再重新抓包。5.3 第三招包间隔计数器判断实时性是否达标调试时常需要知道 FPGA 收到的 UDP 包是连续背靠背的还是中间有间隙。写一个小计数器统计相邻两个包之间 tvalid 为低的时钟周期数存到 AXI-Lite 寄存器里软件每隔一秒读一次。如果平均值波动很大说明上游时序不稳定如果连续传输时几乎为零则说明数据通路没有瓶颈。// 统计相邻 UDP 包之间的空闲时钟周期数 reg [15:0] gap_count; always (posedge clk) begin if (axis_tvalid) begin gap_observed axis_tvalid ? gap_count : gap_observed; gap_count 0; end else begin gap_count gap_count 1; end end这个计数器成本极低但能直观地反映链路行为。比如图像采集场景下帧间间隙通常稳定在几百个时钟周期如果出现几万个周期的抖动那问题多半在源端而不在 UDP 通路里。顺着这三个排查点走一遍绝大多数 ZYNQ 7020 上的 UDP 通信问题都能定位到具体信号层面。本文还有配套的精品资源点击获取