从零构建FPGA以太网通信系统:vivado2018.3实战全解析
你有没有遇到过这样的场景?
项目急着要传数据,Wi-Fi不稳定、串口太慢,而手头那块Zybo Z7板子上的千兆网口却一直“躺平”——明明硬件支持,软件也跑起来了,可就是ping不通、发不出包。调试几天下来,时序违例满天飞,PHY link灯死活不亮。
别慌。这正是我们今天要彻底解决的问题。
本文将带你从零开始,在vivado2018.3环境下完整实现一个基于Tri-Mode Ethernet MAC IP核的以太网通信工程。不跳步骤、不省略约束、不依赖黑盒脚本,每一步都讲清楚“为什么这么做”,让你真正掌握FPGA以太网设计的核心逻辑。
为什么选择vivado2018.3?
虽然现在Vivado已经更新到2023.x版本,但vivado2018.3依然是高校实验室和工业现场最常见的稳定版本之一。它的IP库成熟、bug少、兼容性强,尤其适合教学与产品定型前的验证阶段。
更重要的是,Xilinx官方为7系列器件(Artix-7/Kintex-7/Zynq-7000)提供的Tri-Mode Ethernet MAC IP在该版本中已趋于完善,配合Zynq SoC的PS+PL架构,完全可以胜任大多数嵌入式网络应用需求。
我们的目标是:
👉让FPGA通过RGMII接口与外部PHY芯片通信,实现千兆以太网帧的可靠收发,并能与PC或路由器建立物理连接。
核心武器:Tri-Mode Ethernet MAC IP到底是什么?
这个IP核不是简单的“MAC层搬运工”,它是一个功能完整的媒体访问控制器,名字里的“三模”指的是它可以支持三种速率和接口组合:
| 模式 | 速率 | 接口类型 |
|---|---|---|
| Mode 0 | 10/100/1000 Mbps | GMII |
| Mode 1 | 10/100 Mbps | RGMII |
| Mode 2 | 1000 Mbps | RGMII |
也就是说,只要你外接合适的PHY芯片(比如KSZ9031RN for RGMII千兆),就能自动协商速率并建立链路。
它能做什么?
- 自动生成前导码(Preamble)、SFD(Start of Frame Delimiter)
- 自动添加CRC校验码(发送) / 验证CRC(接收)
- 支持全双工流控(IEEE 802.3x)
- 提供AXI4-Stream用户接口,便于对接DMA或自定义逻辑
- 内建FIFO缓冲,缓解跨时钟域压力
⚠️ 注意:它不包含PHY功能!你需要一块真实的PHY芯片来完成电平转换和差分信号驱动。
工程搭建全流程(Tcl脚本驱动)
与其手动点鼠标拖IP,不如用一段可复用的Tcl脚本一次性生成整个Block Design。这样不仅效率高,还能避免配置错误。
下面是你可以在vivado2018.3 Tcl Console中直接运行的完整脚本:
# 创建工程 create_project eth_demo ./eth_demo -part xc7z020clg400-1 set_property board_part digilentinc.com:zybo-z7:part0:1.0 [current_project] # 创建Block Design create_bd_design "eth_system" # 添加ZYNQ PS create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7 processing_system7_0 apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 \ -config {make_external "FIXED_IO, DDR" apply_board_preset "1"} \ [get_bd_cells processing_system7_0] # 启用EMIO MIO扩展以输出中断等信号 set_property -dict [list CONFIG.PCW_USE_S_AXI_HP0 {1} \ CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {1} \ CONFIG.PCW_ENET0_ENET0_IO {rgmii} \ CONFIG.PCW_ENET0_RESET_ENABLE {1} \ CONFIG.PCW_ENET0_RESET_POLARITY {1}] \ [get_bd_cells processing_system7_0]等等——这里有个关键点很多人忽略:即使你要用PL端实现MAC,PS的EMAC0仍然需要启用RGMII IO配置,否则MIO引脚不会映射出去!
继续添加MAC IP:
# 添加Tri-Mode Ethernet MAC IP create_bd_cell -type ip -vlnv xilinx.com:ip:tri_mode_ethernet_mac tri_mode_ethernet_mac_0 # 配置为RGMII千兆模式 set_property -dict [list \ CONFIG.TEMAC_IF_TYPE {RGMII} \ CONFIG.RGMII_TXCLK_PHASE_SELECTION {Phase_90} \ CONFIG.RGMII_RXCLK_PHASE_SELECTION {Phase_0} \ CONFIG.C_EXTERNAL_CLOCKING {false} \ CONFIG.GMII_FIFO_DEPTH {16} \ CONFIG.USE_BOARD_FLOW {true}] \ [get_bd_cells tri_mode_ethernet_mac_0]重点解释几个关键参数:
RGMII_TXCLK_PHASE_SELECTION = Phase_90:告诉IP内部使用90度相移时钟驱动TXD,补偿PCB延迟;USE_BOARD_FLOW = true:启用开发板流程,自动匹配时序模型;GMII_FIFO_DEPTH:建议设为16以上,防止突发流量溢出。
接下来连接时钟:
# 外部提供125MHz参考时钟(来自PHY或晶振) create_bd_port -dir I -type clk eth_ref_clk create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:diff_clock_rtl:1.0 ref_clkin # 使用IBUFDS将差分时钟转为单端 create_bd_cell -type ip -vlnv xilinx.com:ip:util_ds_buf util_ds_buf_0 set_property -dict [list CONFIG.C_BUF_TYPE {IBUFDS}] [get_bd_cells util_ds_buf_0] connect_bd_net [get_bd_pins util_ds_buf_0/CLK_IN_D] [get_bd_intf_pins ref_clkin] connect_bd_net [get_bd_pins util_ds_buf_0/CLK_OUT] [get_bd_pins tri_mode_ethernet_mac_0/gtx_clk]注意:gtx_clk必须接稳定低抖动的125MHz时钟源,不能由FPGA内部PLL随便分频而来。
最后导出RGMII物理引脚:
# 导出RGMII信号 make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/rgmii_txd] make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/rgmii_tx_ctl] make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/rgmii_txc] make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/rgmii_rxd] make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/rgmii_rx_ctl] make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/rgmii_rxc] # PHY复位引脚(可选GPIO控制) make_bd_pins_external [get_bd_pins tri_mode_ethernet_mac_0/phy_rst_n] # 最终保存并生成顶层包装 save_bd_design make_wrapper -files [get_files ./eth_demo.srcs/sources_1/bd/eth_system/eth_system.bd] -top add_files -norecurse ./eth_demo.srcs/sources_1/bd/eth_system/hdl/eth_system_wrapper.v执行完这些命令后,你的Block Design就已经具备基本通信能力了。
RGMII接口的关键:时序约束才是成败所在
很多开发者卡住的地方不在逻辑,而在时序。
RGMII采用DDR(双沿采样)机制,即每个时钟周期传输两个bit。例如125MHz时钟下,有效速率相当于250Mbps × 4线 = 1Gbps。
这就带来一个问题:FPGA输出的数据和时钟之间必须满足严格的延迟关系。
发送路径(FPGA → PHY)
标准要求:
- TXC相对于TXD延迟1.0ns ~ 1.6ns
- 对应于125MHz(8ns周期)下的~90°相位偏移
Xilinx推荐的做法是:使用ODDR原语 + 90度相移时钟驱动TXD
ODDR #( .DDR_CLK_EDGE("SAME_EDGE") ) u_oddr_txd [ .Q(rgmii_txd[i]), .C(gtx_clk_90), // 90度相移时钟 .CE(1'b1), .D1(txd_out[2*i]), .D2(txd_out[2*i+1]) ];幸运的是,Tri-Mode MAC IP内部已经集成了这些原语,我们只需正确配置RGMII_TXCLK_PHASE_SELECTION即可。
接收路径(PHY → FPGA)
接收更复杂,因为时钟由PHY提供(RXC),属于源同步输入。我们必须告诉Vivado:“这不是普通异步信号,它是跟着时钟走的。”
所以必须加XDC约束:
# 主时钟定义(假设REF_CLK为125MHz差分输入) create_clock -name clk_125m -period 8.000 [get_ports eth_ref_clk_p] # RGMII接收时钟(来自PHY) create_clock -name rxc_clk -period 8.000 [get_ports rgmii_rxc] # 输入延迟约束(基于KSZ9031数据手册) set_input_delay -clock rxc_clk -max 1.8 [get_ports rgmii_rxd[*]] set_input_delay -clock rxc_clk -max 1.8 [get_ports rgmii_rx_ctl] set_input_delay -clock rxc_clk -min 0.2 [get_ports rgmii_rxd[*]] -clock_fall -add_delay set_input_delay -clock rxc_clk -min 0.2 [get_ports rgmii_rx_ctl] -clock_fall -add_delay📌 这些数值来源于PHY芯片手册中的“Output Delay from RXC”参数。如果你用的是Microchip LAN8720,则需调整为对应值。
没有这些约束?恭喜你,综合工具会默认按最坏情况处理,大概率出现上百条时序违例,最终导致接收乱码甚至无法建链。
常见问题排查清单
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| PHY link灯不亮 | MDIO未配置 | 检查PS是否初始化PHY寄存器(如BMCR、ANAR) |
| 能ping通但丢包严重 | FIFO深度不足 | 增大GMII FIFO至32或启用DMA |
| 收到乱码 | 输入延迟未约束 | 补充set_input_delay |
| 发送失败 | GTX_CLK不稳定 | 检查外部时钟源质量 |
| 板子发热严重 | IO标准错误 | 确保RGMII使用LVCMOS18而非LVTTL |
💡 小技巧:可以先在IP核中启用Internal Loopback Mode,让发送的数据直接回环到接收端。如果此时能收到自己发的包,说明PL侧逻辑正常,问题出在外围电路或PHY配置上。
实际应用场景举例
这套设计已在多个项目中落地:
场景一:实时图像采集传输系统
摄像头 → FPGA图像预处理(边缘检测)→ Tri-Mode MAC → 千兆网 → PC显示
✅ 利用FPGA并行处理优势,实现<5ms端到端延迟
场景二:工业PLC远程监控终端
传感器数据 → FPGA采集 → 打包为UDP → 上位机接收分析
✅ 替代传统RS485,提升带宽与抗干扰能力
场景三:PTP时间同步设备基础平台
利用以太网帧携带时间戳,实现微秒级同步精度
✅ 为后续做主从时钟打下硬件基础
如何进一步提升性能?
当前方案基于纯轮询方式读写AXI Stream接口,CPU占用较高。下一步优化方向包括:
- 集成AXI DMA:实现零拷贝传输,解放ARM核心;
- 绑定lwIP协议栈:在SDK中启用LWIP,支持TCP/IP通信;
- 加入中断机制:每收到一帧触发IRQ,降低查询开销;
- 升级至Zynq Ultrascale+ MPSoC:使用GigE Vision等专业协议。
但请记住:先把最基础的物理层打通,再谈上层优化。
结语:掌握底层,才能驾驭高速
以太网看似只是“插上网线就能通”,但在FPGA世界里,每一个bit的跳变都需要精心设计。
通过本次实践,你应该已经明白:
- Tri-Mode MAC IP不是魔法盒子,它依赖正确的时钟和约束;
- RGMII节省的不只是引脚,更是对PCB布局的友好;
- XDC约束不是可选项,而是确保高速接口稳定的必要条件;
- Tcl脚本自动化是团队协作和工程复现的生命线。
如果你正在做毕业设计、课程项目或产品原型开发,不妨就从这个最小可行系统开始。当你第一次看到Wireshark抓到自己FPGA发出的ARP包时,那种成就感,绝对值得。
如果你在实现过程中遇到具体问题(比如某个约束报错、PHY无法link),欢迎留言交流。我们可以一起看log、调时序、改XDC。毕竟,搞FPGA的人,都是靠debug活着的。