ARTICLE DETAIL

资讯详情

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

FPGA通过SFP光口实现千兆以太网传输:1000BASE-X协议与GTP收发器实战

FPGA通过SFP光口实现千兆以太网传输:1000BASE-X协议与GTP收发器实战 1. 项目背景与整体设计思路1.1 为什么选择SFP光口而不是RJ45电口做过以太网传输的朋友都知道短距离用RJ45电口最省事插上网线就能跑。但一旦传输距离超过100米或者现场电磁环境复杂——比如工厂车间里变频器、伺服电机扎堆——电口就开始丢包、误码率飙升。我最早做的一个项目是在一个大型制造车间里传视频流用普通网线跑千兆白天设备一开图像就花屏折腾了半个月换屏蔽线、加磁环都没根治。后来换成SFP光口加光纤问题直接消失。SFPSmall Form-factor Pluggable光模块的核心价值在于三点传输距离远多模光纤几百米、单模光纤可以到几十公里、抗电磁干扰光信号不受电磁场影响、电气隔离两端设备天然隔离不用担心地环路问题。在FPGA开发场景里SFP还带来一个额外好处——它本质上是高速串行差分信号直接对接FPGA的GTP/GTX收发器不需要额外的PHY芯片省掉了一颗物料和一堆布线麻烦。这个项目的目标很明确用FPGA通过SFP光口实现千兆以太网数据传输跑1000BASE-X协议最终能稳定收发数据包。适合有一定FPGA基础、想入门高速接口的开发者也适合做工业控制、视频传输、数据中心互联方向的朋友参考。1.2 1000BASE-X协议到底是怎么回事很多人一上来就急着写代码结果连协议都没搞清楚调不通就到处问。我建议先把1000BASE-X的框架理一遍。1000BASE-X是IEEE 802.3标准里定义的千兆以太网物理层协议跑在光纤或屏蔽双绞线上。它的结构分三层PCSPhysical Coding Sublayer负责8b/10b编解码。为什么是8b/10b而不是直接传8位因为要保证直流平衡和足够的跳变沿让接收端CDR时钟数据恢复能锁住。每8位数据编码成10位其中K码控制字符用于标识帧边界。PMAPhysical Medium Attachment负责串并转换和时钟恢复。FPGA里的GTP/GTX收发器就干这个活。PMDPhysical Medium Dependent光模块和光纤负责光电转换。数据流是这样的MAC层送来8位数据 → PCS做8b/10b编码变成10位 → PMA串化成高速串行流 → 光模块电光转换 → 光纤传输。接收端反过来走一遍。注意1000BASE-X的线速率是1.25Gbps因为8b/10b编码有25%的开销实际数据速率是1Gbps。这个1.25G就是你在Vivado里配置GTP收发器参考时钟时要算清楚的关键数字。1.3 整体方案选型与架构方案上我选的是Xilinx 7系列FPGAArtix-7或Kintex-7都行用片内的GTP收发器直接对接SFP。为什么不选外挂PHY芯片的方案因为外挂PHY意味着你要多调一颗芯片的MDIO接口、多布一组RGMII/SGMII走线调试复杂度翻倍而GTP本身就能干这个活何必多此一举。整体架构分四块GTP收发器配置成1000BASE-X模式负责串并转换、时钟恢复、8b/10b编解码。MAC层逻辑自己写一个简化的以太网MAC处理前导码、SFD、目的地址、源地址、长度/类型、数据和FCS。数据缓冲与仲裁用FIFO做跨时钟域处理AXI-Stream接口对接上层逻辑。用户逻辑可以是数据回环测试、视频流传输、或者自定义协议封装。选这个架构的理由很简单模块化。GTP配置好之后基本不用动MAC层可以独立仿真验证用户逻辑随便换。我见过有人把所有逻辑揉在一个模块里调起来简直是灾难改一行代码综合半小时出了问题根本不知道是哪一层的事。2. 核心细节解析与实操要点2.1 SFP接口引脚定义与硬件连接先解决硬件问题。SFP接口是一个20pin的笼子但实际用到的信号没那么多。关键引脚如下引脚编号名称功能是否必须1VEET发射端地是2TX_FAULT发射故障指示否3TX_DISABLE发射禁止低有效是4MOD_DEF(2)I2C数据线SDA是5MOD_DEF(1)I2C时钟线SCL是6MOD_DEF(0)模块存在指示是7Rate_Select速率选择否8LOS接收信号丢失否9VEER接收端地是10VEER接收端地是11VEER接收端地是12RD-接收差分负是13RD接收差分正是14VEER接收端地是15VCCR接收端电源3.3V是16VCCT发射端电源3.3V是17VEET发射端地是18TD发射差分正是19TD-发射差分负是20VEET发射端地是实际接线时TX_DISABLE必须拉低否则光模块不发光。MOD_DEF(0)一般接地表示模块已插入。I2C那两根线建议接上方便读取模块的DDM信息温度、光功率等调试时非常有用。实操心得我第一次画板子的时候把TX_DISABLE悬空了结果光模块死活不发光查了两天才发现这个引脚内部有上拉必须主动拉低。这个坑希望大家别踩。差分线走线要注意TD/TD-和RD/RD-必须严格等长差分阻抗100欧姆。走线尽量短避免过孔。如果板子空间允许在差分线旁边铺地并打过孔屏蔽。2.2 Vivado中GTP收发器的配置打开Vivado新建工程添加GTP IP核。以Artix-7 XC7A200T为例配置步骤如下选择收发器类型在IP Catalog里搜索GTPE2_CHANNEL双击打开。Line Rate设置填1.25 Gbps。这个数字是1000BASE-X的线速率不能填1.0。参考时钟SFP模块一般输出125MHz参考时钟有些板子用外部晶振。在PLL Selection里选PLL0参考时钟频率填125MHz。编码方式选8B/10B。Comma对齐使能Comma AlignmentComma值选K28.51000BASE-X的同步字符。均衡器短距离几百米以内选DFE或LPM都行长距离建议DFE。配置完成后IP核会生成一个example design里面包含收发器的复位逻辑和时钟模块。我建议直接参考example design的复位状态机不要自己从头写那个状态机涉及PLL锁定、复位时序自己写很容易出问题。时钟计算参考时钟125MHzGTP内部PLL倍频到1.25GHz。具体倍频系数由Vivado自动计算你只需要确认Line Rate和Reference Clock填对了就行。2.3 8b/10b编解码与K码的作用8b/10b编码是1000BASE-X的灵魂。它的核心思想是把8位数据映射成10位使得连续的0和1数量平衡直流平衡不会出现超过5个连续的相同位保证跳变沿K码是特殊的控制字符不用于数据传输只用于控制。1000BASE-X里用到的K码有K码编码值用途K28.50xBC同步/逗号字符K27.70xFB起始定界符SFDK28.10x3C配置字符K28.20x5C配置字符K29.70xFD空闲字符K30.70xFE错误传播字符发送端在帧与帧之间发送K28.5或K29.7作为空闲字符接收端通过检测K28.5来对齐字边界。这个机制叫Comma Detection。注意GTP IP核里已经集成了8b/10b编解码器你不需要自己写编码逻辑。但你必须理解K码的含义因为在MAC层你需要识别SFDK27.7来判断帧的起始。2.4 MAC层帧格式与FCS计算以太网帧格式如下字段长度说明前导码7字节0x55重复7次SFD1字节0xD5目的MAC6字节接收方MAC地址源MAC6字节发送方MAC地址长度/类型2字节小于1536表示长度大于表示类型数据46-1500字节有效载荷FCS4字节CRC32校验FCS用的是CRC32多项式为0x04C11DB7初始值0xFFFFFFFF结果取反。这个计算在FPGA里可以用LFSR实现每个时钟周期处理8位。Xilinx提供CRC IP核但我建议自己写一个因为逻辑很简单而且方便调试。// CRC32计算模块简化版 module crc32 ( input wire clk, input wire rst_n, input wire data_valid, input wire [7:0] data_in, output reg [31:0] crc_out ); wire [31:0] crc_next; // 8位并行CRC计算逻辑 assign crc_next {crc_out[23:0], 8h00} ^ crc_table(data_in, crc_out[31:24]); always (posedge clk or negedge rst_n) begin if (!rst_n) crc_out 32hFFFFFFFF; else if (data_valid) crc_out crc_next; end endmodule实际代码里crc_table是一个组合逻辑查找表根据输入字节和当前CRC高8位计算出新的CRC值。这个模块我仿真验证过和软件计算的CRC32结果一致。3. 实操过程与核心环节实现3.1 工程搭建与IP核集成新建Vivado工程器件选xc7a200tfbg484-2。添加以下IP核GTPE2_CHANNEL配置如2.2节所述。GTPE2_COMMONGTP的公共资源包含PLL和参考时钟缓冲。clk_wiz生成用户逻辑需要的时钟比如100MHz系统时钟。fifo_generator做跨时钟域缓冲收发各一个。GTP IP核的example design里有一个gtp_wrapper模块把CHANNEL和COMMON例化好了直接拿来用。你需要修改的是把参考时钟输入接到板子上的125MHz晶振把TX/RX差分对接到SFP的TD/RD引脚把用户接口TX_DATA、RX_DATA接到你的MAC层3.2 发送通路实现发送通路的数据流用户数据 → 添加前导码和SFD → 添加目的/源MAC → 添加长度/类型 → 添加数据 → 计算并附加FCS → 送入GTP发送接口。关键点在于时序控制。GTP的发送接口是16位宽因为8b/10b编码后是10位但GTP内部会处理用户侧一般是16位或32位。以16位为例每个时钟周期发送2个字节。你需要一个状态机来控制// 发送状态机简化 localparam IDLE 3d0; localparam PREAMBLE 3d1; localparam SFD 3d2; localparam HEADER 3d3; localparam PAYLOAD 3d4; localparam FCS 3d5; always (posedge clk) begin case (tx_state) IDLE: if (tx_start) tx_state PREAMBLE; PREAMBLE: if (preamble_cnt 7) tx_state SFD; SFD: tx_state HEADER; HEADER: if (header_cnt 7) tx_state PAYLOAD; PAYLOAD: if (payload_cnt payload_len) tx_state FCS; FCS: if (fcs_cnt 2) tx_state IDLE; endcase end前导码是7个0x55SFD是0xD5。注意在1000BASE-X里SFD对应的是K27.7控制字符GTP会自动把0xD5映射成K27.7如果你配置了正确的Comma值。实操心得发送状态机里一定要加超时保护。我有一次FIFO读空了但状态机还在PAYLOAD状态结果一直发无效数据接收端直接挂了。后来加了一个计数器超过最大帧长就强制回IDLE。3.3 接收通路实现接收通路比发送复杂因为要处理帧同步和错误检测。数据流GTP接收接口 → 检测SFD → 提取目的MAC → 判断是否发给本机 → 提取数据 → 校验FCS → 写入FIFO。接收状态机的关键是SFD检测。GTP接收接口会输出一个rx_char_is_k信号标识当前字符是否是K码。当收到K27.7SFD时启动帧接收。// 接收状态机简化 always (posedge clk) begin case (rx_state) IDLE: if (rx_char_is_k rx_data[7:0] 8hFB) rx_state HEADER; HEADER: begin // 提取目的MAC、源MAC、长度/类型 if (header_cnt 7) rx_state PAYLOAD; end PAYLOAD: begin // 写入FIFO if (payload_cnt payload_len) rx_state FCS; end FCS: begin // 校验FCS if (fcs_cnt 2) rx_state IDLE; end endcase endFCS校验失败时要丢弃整个帧并置一个错误标志。这个错误标志可以接到LED上方便调试。3.4 跨时钟域处理与FIFO配置GTP的收发时钟和用户逻辑时钟是异步的必须用FIFO做跨时钟域。发送FIFO的写时钟是用户时钟比如100MHz读时钟是GTP的发送时钟125MHz。接收FIFO反过来。FIFO深度怎么算假设用户数据速率是100MB/sGTP线速率是125MB/s差值25MB/s。如果FIFO深度是1024字节那么每1024/25M ≈ 40微秒就会溢出一次。所以要么提高用户时钟频率要么加大FIFO深度。我一般用2048深度留足余量。注意FIFO的读写位宽要匹配。如果用户侧是8位GTP侧是16位需要做位宽转换。Xilinx的FIFO IP核支持非对称位宽配置时选上就行。3.5 仿真验证与上板调试仿真分两步先做行为级仿真验证MAC层逻辑再做时序仿真验证GTP配置。行为级仿真用Vivado自带的Simulator就行。写一个testbench模拟发送一帧数据然后检查接收端是否收到相同的数据。重点检查前导码和SFD是否正确FCS计算是否正确接收状态机是否能正确同步时序仿真需要GTP的仿真模型Vivado会自动生成。仿真时间会比较长因为GTP的复位过程需要几百个时钟周期。上板调试时先用ILAIntegrated Logic Analyzer抓GTP的收发信号。重点看gtp_tx_ready和gtp_rx_ready是否拉高rx_char_is_k是否有跳变rx_data是否有有效数据如果gtp_rx_ready一直不拉高检查参考时钟是否正常、复位时序是否正确。如果rx_char_is_k一直为低检查光模块是否发光、光纤是否接好。4. 常见问题与排查技巧实录4.1 GTP复位不成功怎么办这是最常见的问题。GTP的复位状态机很复杂涉及PLL锁定、MMCM锁定、收发器复位等多个步骤。如果复位不成功gtp_tx_ready和gtp_rx_ready会一直为低。排查步骤检查参考时钟是否正常。用示波器量125MHz晶振输出或者用ILA抓时钟信号。检查复位信号极性。GTP的复位是高有效还是低有效看IP核配置。检查复位脉冲宽度。GTP要求复位信号至少保持一定时钟周期太短了状态机来不及响应。检查电源。GTP的供电电压1.0V、1.2V、1.8V是否正常。我遇到过一次复位不成功查了半天发现是参考时钟的IBUFDS原语没加时钟根本没进GTP。这个错误在综合时不会报只有上板才暴露。4.2 接收端收不到数据如果发送端正常但接收端没反应按以下顺序排查现象可能原因解决方法LOS信号为高光模块没收到光检查光纤是否接好、光模块是否发光rx_char_is_k一直为低Comma对齐失败检查Comma值配置、参考时钟频率收到数据但FCS错误数据错位检查SFD检测逻辑、状态机时序收到数据但MAC地址不对地址过滤逻辑错误检查目的MAC比较逻辑实操心得我习惯在接收通路上加一个计数器统计收到的帧数、错误帧数、FCS错误数。上板后通过UART打印出来一眼就能看出问题在哪。4.3 Vivado实现报错DRC RTSTAT-2这个错误通常出现在布局布线阶段提示RTSTAT-2: GTP收发器未正确连接。原因是GTP的某些引脚比如TX_DISABLE、LOS没有约束。解决方法在XDC约束文件里把这些引脚约束到具体的管脚或者设置为常量。比如set_property PACKAGE_PIN Y4 [get_ports tx_disable] set_property IOSTANDARD LVCMOS33 [get_ports tx_disable]如果某个引脚不用可以设置为set_property IS_LOC_FIXED FALSE让工具自动处理。4.4 仿真速度太慢怎么优化GTP的时序仿真非常慢因为要模拟高速串行信号。优化方法只仿真关键路径不要全芯片仿真。用行为级模型代替时序模型。Vivado的GTP IP核支持生成行为级仿真模型速度快很多。减少仿真时间。GTP复位需要几百个时钟周期但数据传输只需要几十个周期仿真时可以直接跳过复位阶段。我一般先用行为级仿真验证逻辑确认没问题后再做一次短时间的时序仿真确认时序收敛就行。4.5 光模块DDM信息读取失败如果I2C读不到光模块的DDM信息检查SDA和SCL是否接上拉电阻一般4.7kΩI2C时钟频率是否过高光模块一般支持100kHz模块地址是否正确一般是0xA0DDM信息里最有用的两个值温度地址0x60和光功率地址0x68。温度过高说明模块散热不好光功率过低说明光纤损耗太大。5. 性能优化与扩展思路5.1 提高吞吐量的几个方向千兆以太网的理论吞吐量是1Gbps但实际能跑满的很少。瓶颈通常在MAC层和FIFO。优化方向加大FIFO深度减少溢出概率。提高用户时钟频率从100MHz提到125MHz和GTP时钟同频省掉跨时钟域。用AXI-Stream接口Xilinx的DMA IP核支持AXI-Stream可以直接对接省掉自己写FIFO。我实测下来用AXI-Stream加DMA的方案吞吐量能到900Mbps以上基本跑满千兆。5.2 扩展到多光口方案如果需要多个光口比如4个SFP可以用GTP Quad。一个Quad包含4个GTP通道共享一个COMMON。配置时注意每个通道的参考时钟可以独立也可以共享。复位逻辑要分开一个通道出问题不影响其他通道。电源要留足余量4个GTP同时工作功耗不小。5.3 从1000BASE-X升级到10GBASE-R10G以太网用的是64b/66b编码不是8b/10b。GTP换成GTX或GTH参考时钟变成156.25MHz。MAC层的帧格式不变但FCS还是CRC32。升级时主要改GTP配置和时钟计算MAC层逻辑基本不用动。这个项目后续还可以扩展成带时间戳的精确同步方案或者加一个简单的ARP协议实现让设备能响应ping。我在实际项目里就是这么一步步加功能上去的先跑通点对点传输再加协议栈最后做成一个完整的产品。最后分享一个小技巧调试光口的时候准备一个光功率计和一个光纤跳线随时量一下发送和接收光功率。正常范围是-3dBm到-20dBm低于-20dBm就说明链路损耗太大了。这个工具不贵但能帮你省下大量排查时间。
返回列表