ARTICLE DETAIL

资讯详情

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

FPGA实现1000BASE-X光口以太网传输:从协议栈到上板调试

FPGA实现1000BASE-X光口以太网传输:从协议栈到上板调试 1. 项目缘起与整体设计思路1.1 为什么选择SFP光口而不是RJ45电口做过工业现场数据采集或者多板卡互联的朋友应该都有体会当传输距离超过30米、电磁环境又比较恶劣的时候RJ45电口就开始力不从心了。我之前做一个多通道同步采集的项目八块板卡分布在两个机柜里最远的两块板子之间走线大概40米用电口跑千兆的时候丢包率肉眼可见地往上飙示波器一挂全是共模噪声。后来换成SFP光口方案同样的拓扑、同样的数据量误码率直接降到可以忽略不计的水平。SFP光口以太网传输的核心价值在于三点电气隔离、长距离低损耗、抗电磁干扰。光纤本身是介质两端天然隔离不存在地环路问题多模光纤在千兆速率下可以稳定跑550米单模更是能到10公里以上而且光信号不受电机、变频器、继电器这些工业现场常见干扰源的影响。对于FPGA开发者来说SFP接口的另一个好处是它本质上就是一个高速串行收发器通道和FPGA内部的GTP/GTX/GTH收发器直接对接不需要额外的PHY芯片做并串转换链路简洁、延迟确定。这个项目要做的就是在一块带有SFP笼子的FPGA板卡上实现完整的1000BASE-X光口以太网传输链路包括物理层编码、MAC层收发、以及上层的数据打包与校验。整个设计在Vivado环境下完成目标器件可以是Xilinx 7系列及以上的FPGA。1.2 1000BASE-X协议栈的层次拆解很多刚接触光口以太网的朋友容易把1000BASE-X和1000BASE-T搞混。简单说1000BASE-T是跑在双绞线上的需要复杂的回声消除和均衡1000BASE-X是跑在光纤上的物理层干净得多。它的协议栈从下往上大致是这样的PMD层物理介质相关负责光电转换SFP模块干的就是这个活。FPGA这边输出的是高速差分电信号SFP模块把它转成光信号发出去接收方向反过来。PCS层物理编码子层这是FPGA内部逻辑要处理的核心部分。1000BASE-X使用8B/10B编码把8位数据映射成10位码字保证直流平衡和足够的跳变沿供时钟恢复使用。同时PCS层还负责 comma字符检测、码组同步、通道绑定等。PMA层物理介质附加串并转换、时钟恢复、预加重和均衡。这部分通常由FPGA内部的硬核收发器完成。MAC层以太网帧的组装与解析包括前导码、SFD、目的地址、源地址、长度/类型、数据和FCS校验。在Vivado里Xilinx提供了两种主要路径来实现这个链路一种是用1G/2.5G Ethernet PCS/PMA or SGMII IP核它把PCS和PMA都封装好了你只需要接上SFP的差分对和时钟配置成1000BASE-X模式即可另一种是直接用GTP/GTX收发器 自己写8B/10B编解码和MAC逻辑灵活性更高但工作量也大得多。对于绝大多数项目我建议走IP核路线稳定可靠时序收敛也容易。1.3 整体数据流与模块划分这个设计的顶层数据流是这样的上位机通过某种接口比如UART或者PCIe把待发送的数据交给FPGAFPGA的MAC层把数据封装成以太网帧加上前导码和FCS然后送给PCS/PMA IP核做8B/10B编码和串化最后通过SFP光模块发出去。接收方向完全对称SFP收到光信号转成差分电信号送给FPGA收发器IP核完成解串和10B/8B解码恢复出以太网帧MAC层做FCS校验和地址过滤有效数据存入FIFO供后续处理。模块划分上我习惯分成四个部分光口物理层模块IP核时钟约束、MAC收发模块帧组装/解析FCS、数据缓冲模块异步FIFO做时钟域 crossing、用户接口模块对接实际业务逻辑。这样划分的好处是每个模块职责单一仿真验证的时候可以单独拎出来测出了问题也容易定位。2. 硬件设计与引脚约束的关键细节2.1 SFP接口引脚定义与板级连接SFP笼子有20个引脚但真正和FPGA打交道的核心信号就那么几组。先看发送方向TD和TD-是一对高速差分信号从FPGA收发器的TX端出来经过AC耦合电容通常0.1uF接到SFP的发送引脚。接收方向RD和RD-从SFP的接收引脚出来同样经过AC耦合电容接到FPGA收发器的RX端。注意这里的AC耦合电容位置有讲究一般放在靠近发送端的地方接收端是否需要电容要看具体收发器要求7系列GTP的RX端内部有耦合外部通常不需要再加。除了高速差分对还有几个低速控制信号必须接TX_DISABLE用来关闭发送TX_FAULT指示发送故障RX_LOS指示接收光信号丢失MOD_ABS指示模块是否插入。这几个信号我一般直接接到FPGA的普通IO上内部做上拉通过逻辑读取状态。另外I2C接口SCL/SDA用来读取SFP模块内部的EEPROM信息包括厂商、波长、传输距离等调试阶段很有用但产品里不一定需要。注意SFP的TD/RD差分对在PCB上必须做100欧姆差分阻抗控制走线尽量短且等长过孔数量控制在最少。我见过因为差分对走线不对称导致眼图闭合的案例重新布线后问题消失。2.2 收发器参考时钟与约束写法7系列FPGA的GTP收发器需要外部提供参考时钟频率取决于你选的线速率和内部PLL配置。对于1000BASE-X线速率是1.25Gbps8B/10B编码后GTP的参考时钟通常选125MHz。这个时钟必须从专用的时钟引脚输入不能随便找个普通IO。板上一般会放一个125MHz的差分晶振专门给收发器用。在Vivado里参考时钟的约束要写在XDC文件里基本格式是create_clock -period 8.000 -name gt_refclk_p [get_ports gt_refclk_p] set_property LOC GTPE2_CHANNEL_X0Y1 [get_cells gt_wrapper_i/gtp_i] set_property LOC IBUFDS_GTE2_X0Y1 [get_cells gt_wrapper_i/ibufds_gt]如果你用的是IP核Vivado会自动生成一部分约束但参考时钟的周期约束和引脚位置约束还是得自己确认。我踩过的坑是IP核生成的示例设计里参考时钟约束是200MHz但我的板子实际是125MHz综合不报错实现也不报错但上板就是不通。后来用ILA抓收发器的状态寄存器才发现PLL根本没锁。所以上板前一定核对参考时钟频率和约束是否一致。2.3 时钟域分析与跨时钟处理这个设计里至少有三个时钟域收发器恢复时钟从接收数据里恢复出来的125MHz、用户时钟IP核输出的125MHz或62.5MHz、系统时钟板上晶振提供的100MHz或200MHz。数据从MAC层到用户逻辑必然要跨时钟域。我的做法是在MAC层和用户接口之间放一个异步FIFO用IP核生成的独立时钟FIFO写端用MAC时钟读端用系统时钟。FIFO深度选512或1024足够缓冲突发数据。这里有个细节FIFO的写使能要结合MAC层的有效信号和FIFO满信号做反压否则数据丢了都不知道。读端同理空信号要参与用户逻辑的流控。实操心得跨时钟域的信号除了数据总线还有valid、ready、last这些控制信号。我的习惯是控制信号也走FIFO或者用双触发器同步加握手绝对不要直接拿一个时钟域的组合逻辑输出去驱动另一个时钟域的触发器亚稳态会让你调到怀疑人生。3. Vivado工程搭建与IP核配置实战3.1 工程创建与器件选型打开Vivado新建工程选RTL Project器件根据你手头的板子来。我用的是xc7a200tfbg484-2属于Artix-7系列带GTP收发器。选器件的时候注意看两点有没有GTP/GTX收发器收发器的参考时钟引脚是否引到了板子上的晶振。有些低端器件比如Spartan-6的某些型号只有GTP速率上限低一些但跑1.25Gbps没问题。工程建好后先把板级的XDC约束文件加进去包括时钟、复位、SFP控制信号、差分对引脚位置。这些约束最好从板卡厂商的示例工程里抄自己写容易漏。特别是差分对的LOC约束写错了综合能过但实现会报错。3.2 1G Ethernet PCS/PMA IP核配置要点在IP Catalog里搜“1G Ethernet PCS/PMA”双击打开配置界面。几个关键选项Standard选1000BASE-X不要选SGMII除非你接的是PHY芯片而不是光模块。Physical Interface选GTP或GTX取决于你的器件。Line Rate1000BASE-X固定1.25Gbps不用改。Reference Clock选125MHz和板上晶振一致。Include Shared Logic in core如果这是你工程里唯一用到的收发器选这个IP核会把QPLL/CPLL和复位逻辑都包进来省事。如果多个通道共享就选另一个选项把共享逻辑放外面。配置完成后IP核会生成一个示例设计Example Design强烈建议先生成示例设计跑一遍确认硬件链路没问题再往自己的工程里集成。示例设计里包含了完整的MAC层用AXI Ethernet IP或者简单的FIFO接口你可以直接用它来测试光口回环。3.3 收发器复位与初始化流程GTP收发器的复位不是简单拉一下就行它有一套严格的时序要求。IP核会输出几个状态信号tx_resetdone、rx_resetdone、pll_lock。正确的初始化顺序是等待参考时钟稳定至少100us。释放PLL复位等待pll_lock拉高。释放TX复位等待tx_resetdone拉高。释放RX复位等待rx_resetdone拉高。此时链路才真正建立可以开始收发数据。我一般写一个简单的状态机来管理这个过程每个状态加一个超时计数器如果某个信号在规定时间内没拉高就报错。实测下来从复位释放到链路建立大概需要几十毫秒取决于参考时钟的稳定时间。常见问题如果rx_resetdone一直不拉高先检查SFP模块有没有插好、光模块的TX_DISABLE有没有被误拉高、对端有没有在发送数据。1000BASE-X的接收方向需要收到对端的comma字符才能完成码组同步如果对端没发数据rx_resetdone是不会拉高的。4. MAC层收发逻辑与FCS校验实现4.1 以太网帧格式与发送状态机以太网帧的格式是固定的7字节前导码0x55 1字节SFD0xD5 6字节目的MAC 6字节源MAC 2字节长度/类型 46到1500字节数据 4字节FCS。发送的时候前导码和SFD由PCS层自动加MAC层只需要从目的MAC开始组装。我的发送状态机是这样的IDLE状态下等待用户数据有效收到后先发目的MAC和源MAC这两个可以从寄存器配置然后发长度/类型接着把用户数据流式发出去同时把数据送给FCS计算模块。数据发完后把FCS计算结果取反以太网FCS是CRC32的补码发出去最后回到IDLE。这里有个细节如果用户数据不足46字节需要补零到46字节否则接收端会当成runt帧丢弃。我一般会在发送状态机里加一个计数器数据发完后如果计数小于46就继续发零直到满足最小帧长。4.2 CRC32 FCS计算模块的写法FCS用的是CRC32多项式是0x04C11DB7初始值全1结果取反字节序是LSB first。网上有很多现成的Verilog代码但要注意字节序和位序的对应关系。我习惯用查表法或者逐位异或法逐位法资源占用少但速度慢查表法速度快但需要ROM。对于千兆速率125MHz时钟下每个时钟周期处理8位数据逐位法肯定来不及。我的做法是用8位并行CRC每个时钟周期算8位具体实现是把CRC寄存器的高8位和输入数据异或然后查表得到新的CRC值。这个表可以预先算好用case语句或者ROM实现。// 8位并行CRC32核心逻辑简化示意 always (posedge clk) begin if (crc_en) begin crc_reg next_crc; end end assign next_crc[31:24] crc_reg[23:16] ^ crc_table[data_in ^ crc_reg[31:24]]; assign next_crc[23:16] crc_reg[15:8] ^ crc_table[data_in ^ crc_reg[31:24]]; // ... 其余字节类似实测下来8位并行CRC在125MHz下时序余量很足Artix-7上跑200MHz都没问题。4.3 接收方向帧解析与地址过滤接收方向比发送复杂一些因为要处理各种异常情况。我的接收状态机流程是等待SFD0xD5收到后开始接收目的MAC和本机MAC寄存器比较如果不匹配且不是广播地址就丢弃整帧匹配的话继续接收源MAC、长度/类型然后根据长度字段接收数据同时算FCS。数据收完后把算出来的FCS和帧尾的4字节比较一致才把数据写入FIFO否则丢弃并置错误标志。这里有个坑长度/类型字段的值如果大于1500表示这是EtherType而不是长度比如0x0800表示IPv40x0806表示ARP。我的做法是判断这个字段如果大于1536就当成EtherType处理数据长度由帧尾的FCS位置决定而不是由这个字段决定。这个逻辑在纯自定义协议里可以简化但如果你要和标准以太网设备互通就必须处理。避坑技巧接收状态机一定要有超时机制。如果链路中途断开或者对端发了畸形帧状态机可能卡在某个状态出不来。我一般给每个状态加一个计数器超过一定周期没进展就强制回到IDLE同时置一个错误标志供上层查询。5. 上板调试与常见问题排查实录5.1 ILA抓波形链路建立过程可视化Vivado的ILAIntegrated Logic Analyzer是调试光口链路的利器。我一般会在以下几个信号上挂ILApll_lock、tx_resetdone、rx_resetdone、rx_los、tx_fault、以及MAC层的rx_valid和rx_data。触发条件设成rx_resetdone的上升沿这样能抓到链路建立的完整过程。实测下来正常的链路建立波形是这样的pll_lock先拉高大约几十个时钟周期后tx_resetdone拉高再过一段时间rx_resetdone拉高。如果rx_resetdone一直不拉高看rx_los是不是高如果是高说明没收到光信号检查光纤有没有插好、对端有没有发数据。如果rx_los是低但rx_resetdone不拉高可能是comma字符检测有问题检查参考时钟频率和IP核配置。5.2 常见报错与解决方法速查表报错/现象可能原因排查方法解决方法实现阶段DRC RTSTAT-2收发器位置约束冲突检查XDC中GTP LOC是否重复确保每个GTP通道唯一LOC比特流生成失败引脚约束缺失或冲突看Implementation的Critical Warning补全XDC或修正冲突引脚上板后链路不通参考时钟频率不对ILA抓pll_lock核对晶振频率和约束接收数据错乱8B/10B解码错误看rx_resetdone和rx_los检查光纤和SFP模块FCS校验一直失败CRC字节序或初值错误用已知数据测试CRC模块核对多项式和位序跨时钟域数据丢失FIFO满/空处理不当看FIFO的满空标志加反压或加深FIFO5.3 光模块兼容性与眼图测试不是所有SFP模块都兼容所有FPGA板卡。我遇到过某品牌的SFP模块在A板上能用在B板上就不行后来发现是B板的AC耦合电容值不对。选SFP模块的时候优先选和板卡厂商推荐一致的型号或者至少确认模块的速率、波长、传输距离和板卡匹配。眼图测试需要用到示波器加光探头一般开发者没有这个条件。替代方案是用FPGA内部的收发器眼图扫描功能7系列GTP支持内部眼图扫描通过DRP接口读取可以大致评估信号质量。如果眼图张开度很小检查PCB走线、连接器焊接、SFP模块金手指是否干净。实操心得调试光口的时候我习惯先做近端回环用一根光纤把同一个模块的TX和RX连起来确认FPGA内部逻辑和SFP模块本身没问题再做远端回环两块板子对接这样能把问题范围缩小一半。近端回环通了但远端不通问题大概率在对端或者光纤上。6. 性能优化与进阶扩展方向6.1 时序收敛技巧与资源优化千兆速率下125MHz时钟对7系列FPGA来说不算高时序收敛一般不难。但如果你在同一个工程里还跑了其他高速逻辑比如DDR控制器或者图像处理流水线就可能出现时序违例。我的经验是把光口相关的逻辑放在独立的pblock里给它分配专用的时钟区域和IO资源减少和其他逻辑的竞争。资源优化方面MAC层的FIFO用Block RAM实现不要用分布式RAM否则LUT占用会很高。CRC模块用查表法的话ROM也会占Block RAM如果资源紧张可以改成逐位法但提高时钟频率用面积换速度。6.2 从千兆到万兆的升级路径如果项目需求升级到万兆1000BASE-X的方案就不能直接用了。万兆以太网10GBASE-R的线速率是10.3125Gbps需要GTH/GTY收发器8B/10B编码也换成了64B/66B。Xilinx提供了10G Ethernet PCS/PMA IP核配置流程和千兆类似但时钟要求更高PCB走线也更严格。我的建议是如果当前项目只是千兆需求先把千兆做稳万兆的升级路径留好接口比如把MAC层和物理层之间的接口做成标准化的AXI-Stream将来换物理层的时候MAC层不用动。6.3 多板卡同步与时间戳应用在多板卡采集系统里光口以太网不仅仅是数据传输通道还可以用来做时间同步。我的做法是在以太网帧里嵌入一个64位的时间戳发送端在帧离开MAC的瞬间锁存本地计数器接收端收到帧后用自己的计数器减去时间戳得到链路延迟。如果多块板卡都这样做就可以实现亚微秒级的同步精度。这个方案的关键是时间戳的锁存点要尽量靠近物理层越靠近PHY延迟越确定。我一般把锁存点放在PCS层的发送使能信号上这样软件层面的抖动就被隔离掉了。实测下来两块板卡之间的同步误差可以做到200纳秒以内对于大多数工业采集场景足够了。最后分享一个我在实际项目中总结的小技巧光口调试的时候先不要急着上协议分析仪用FPGA内部的ILA把收发器状态和MAC层信号抓出来90%的问题都能定位。协议分析仪虽然强大但价格贵、操作复杂而且很多时候你只需要知道“链路通没通”“数据对不对”ILA完全够用。等ILA确认物理层和MAC层都没问题了再上分析仪看上层协议效率会高很多。
返回列表