做FPGA开发这两年,我最大的感受是:很多初学者在写完UART、SPI、I2C这些低速接口之后,突然想上一个高速通信项目,结果卡在“千兆以太网”这里。不是不懂MAC层,也不是不会写RTL,而是被那一堆PHY芯片、MDIO、GTX Transceiver、SFP封装绕得晕头转向。尤其是用SFP光口跑千兆以太网,光模块和FPGA之间还要走SerDes,新手一看XAPP1082那篇应用笔记就头大,十几个模块的工程,不知道从哪下手。
这篇文章我想用最直接的方式,把“FPGA + SFP光模块 + 千兆以太网”这条路走一遍。我会以Xilinx官方XAPP1082为核心参考,讲讲它是怎么工作的,再手把手带你把工程搭起来、跑通回环测试。文章里会给出我实际调试时踩过的坑、改过的代码、抓到的波形,也会把GMII/RGMII、MDIO、GTX这几个关键概念讲明白。不管你是刚入门想做一个光口通信的毕设,还是工作中要做板级高速接口,这篇都能帮你省下不少翻手册的时间。
1. 先说清楚:SFP光口和千兆以太网到底是怎么接起来的
1.1 为什么是SFP而不是RJ45
很多人第一反应是:千兆以太网嘛,用个RJ45网口加PHY芯片不就行了,干嘛非要绕到光口。这话没错,电口方案确实更普及,RJ45加一个RTL8211或者88E1512,FPGA侧走RGMII,基本就能和电脑直连。但SFP光口的优势在于传输距离长、抗干扰强,而且SFP是一种可插拔的模块标准,光模块和电模块能互换。对于板级设计来说,SFP笼子占用面积也不算大,一个标准的SFP座子加外围电路,比一个带网络变压器的RJ45还要简洁。
更重要的一点是,SFP光口的物理层信号是高速串行的,FPGA想要驱动光模块,必须用内部的SerDes硬核,也就是Xilinx FPGA里的GTX/GTH(7系列叫GTX,UltraScale叫GTH/GTY)。这意味着学SFP千兆以太网,其实是在同时学“高速收发器”和“以太网协议栈”这两个硬骨头。一旦做通了,后面再做PCIe、JESD204B、SRIO这类高速接口,很多思路是相通的。
1.2 千兆以太网的协议栈拆分
在FPGA里实现千兆以太网,说白了就是把OSI模型里的数据链路层和物理层搬进芯片。数据链路层的核心是MAC(媒体访问控制),负责组帧、CRC校验、流量控制这些。物理层通常分为PCS(物理编码子层)、PMA(物理介质附加子层)、PMD(物理介质相关子层)。对于千兆光口,PMD就是SFP光模块本身,而PMD和PMA之间的电气接口,是SFP标准规定的差分对信号。
Xilinx的做法是:MAC层可以用FPGA逻辑实现,PCS/PMA层用GTX Transceiver的IP核实现,SFP直接接GTX的TX/RX差分引脚。中间还有一层叫GMII(千兆媒体独立接口),在FPGA内部,它连接MAC和PCS,是一个8bit、125MHz的并行接口。也就是说,数据路径是:FPGA逻辑(MAC) -> 8bit x 125MHz -> GTX IP(PCS/PMA) -> 串行差分对 -> SFP光模块 -> 光纤。
这里有个容易绕晕的点:很多学习板、开发板上的千兆网口是RGMII接口,那是MAC连接外部PHY芯片的接口。而XAPP1082针对的是SFP光口方案,它用GTX做PCS,不再需要外部PHY芯片。所以你在代码里看到的GMII其实是内部MAC和内部GTX之间的接口,不要再用“GMII就是接到外面PHY”的旧观念去理解。
1.3 XAPP1082是什么、解决了什么问题
XAPP1082的全称是“Using the Transceiver to Implement a Gigabit Ethernet Interface”(应用笔记编号1082),是Xilinx官方发布的参考设计,目标就是告诉我们:如何用Virtex-6/Kintex-7/Virtex-7这些带GTX的FPGA,通过SFP光模块实现1000BASE-X千兆以太网。它里面包含了从上到下的完整工程:MAC、地址解析、时钟模块、GTX的配置,以及和SFP对接的PCB注意事项。
严格说,XAPP1082的核心价值是两部分。一部分是一个可综合的GMII接口MAC,这个MAC是完整支持千兆以太网协议的;另一部分是对GTX的约束和配置,GTX工作在线速率1.25Gbps(千兆以太网经过8B/10B编码后,有效数据速率是1Gbps,但线速率是1.25Gbps),参考时钟125MHz。这个工程在Vivado和ISE里都能跑起来,配合它提供的例程,可以很方便地验证光模块和FPGA板的收发通路。
新手最容易犯的错是把XAPP1082当成“一个IP”去用,其实它是一堆源码和脚本的集合。我们需要做的,是理解它的模块划分,然后把需要的部分移植到自己的工程里。接下来我就按模块拆开讲怎么用它。
2. 硬件基础:SFP引脚定义与FPGA最小电路
2.1 SFP座子的引脚怎么看
做软件的人第一次画SFP板子时,最怕的就是看引脚定义。SFP的引脚一共20个,常用信号其实没几个。它的引脚排列和RJ45那种整齐的差分对还不一样,很多信号是交错的。为了省事,我直接把最关键的引脚列出来。
| 引脚号 | 名称 | 方向(相对FPGA) | 说明 |
|---|---|---|---|
| 1 | VEET | - | 发射部分地 |
| 2 | TX_FAULT | 输入 | 光模块发射故障指示,低有效?实际是高电平表示故障 |
| 3 | TX_DISABLE | 输出 | 高电平时关闭光模块发射,不用时务必拉低 |
| 4 | SDA | 双向 | I2C数据线,用于DDM信息读取 |
| 5 | SCL | 双向 | I2C时钟线 |
| 6 | MOD_ABS | 输入 | 模块在位检测,模块插入后接地 |
| 7 | RX_RATE_SELECT | 输出(对FPGA) | 速率选择,千兆模式下可悬空 |
| 8 | LOS | 输入 | 接收信号丢失指示,有光时低电平 |
| 9 | VEER | - | 接收部分地 |
| 10 | VEER | - | 接收部分地 |
| 11 | VEER | - | 接收部分地 |
| 12 | RD- | 输入 | 接收差分信号负极 |
| 13 | RD+ | 输入 | 接收差分信号正极 |
| 14 | VEER | - | 接收部分地 |
| 15 | VCCR | - | 接收电源3.3V |
| 16 | VCCT | - | 发射电源3.3V |
| 17 | VEFT | - | 发射部分地 |
| 18 | TD+ | 输出 | 发送差分信号正极 |
| 19 | TD- | 输出 | 发送差分信号负极 |
| 20 | VEFT | - | 发射部分地 |
注意,SFP的TX、RX是CML电平接口,AC耦合是标准做法,一般是在FPGA的GTX引脚和SFP引脚之间串一个0.1uF的电容。板上还需要给SFP供电,通常是3.3V。SFP的TX_DISABLE引脚如果不处理,默认可能是悬空,很多光模块内部有上拉,悬空会导致模块不发光,所以最好在FPGA里明确输出一个低电平。
2.2 FPGA怎么选、引脚怎么分配
不是所有FPGA都有GTX。Spartan-6的GTP也可以做千兆以太网,但XAPP1082主要是针对7系列GTX写的。如果手上只有Artix-7的A35T/A75T,照样可以跑,因为这些芯片里也有GTX(GTX个数少而已)。用Vivado创建一个工程时,一定要选带GTX的封装,A35T有4个GTX,做千兆以太网绰绰有余。
引脚分配时,GTX的TX和RX要接到SFP模块的TD/RD。另一个关键点是参考时钟。GTX需要一对参考时钟引脚,频率可以是125MHz,也可以是156.25MHz,因为GTX的PLL会做倍频/分频得到线速率。千兆以太网线速率1.25Gbps,参考时钟用125MHz是比较自然的。但是要注意,参考时钟引脚必须使用专用的GTREFCLK引脚,不能随便分配一个普通时钟引脚。
在XDC约束里,还需要给GTX的回环、SFP的LOS、TX_DISABLE等信号做引脚和电平约束。电平标准和普通LVCMOS不太一样,像LOS、TX_DISABLE这些引脚是LVCMOS33或LVCMOS18,要看板子的实际情况。最稳妥的做法是查你开发板的原理图,看SFP部分是怎么连接的,再照着写约束。
2.3 XAPP1082对PCB走线有什么要求
如果是在现成开发板上做,这部分不用管。但如果自己画板,就得知道:SFP差分对和GTX引脚之间的走线要按高速差分线处理,阻抗控制在100欧姆差分阻抗,尽量短,尽量同层。AC耦合电容要靠近SFP连接器放置。SFP的电源去耦要按手册要求做,通常是一个10uF和多个0.1uF组合。
高速信号对参考平面也很敏感,最好保证差分对下面是完整的地平面,不要跨分割。我当时画第一版板子时,因为SFP差分对跨了一个电源分割,结果眼图质量很差,经常丢包,后来重新改板才解决。这些经验写下来,就是提醒自己:高速接口的PCB设计容错率很低,前期布局必须留足心思。
3. 核心代码结构:XAPP1082的模块级拆解
3.1 顶层视角:数据从MAC到光纤走了谁
XAPP1082工程顶层模块名通常叫gig_ethernet,内部实例化了MAC、地址交换、时钟、GTX transceiver这些子模块。先看整体框图,它从上到下大概是:
| 模块 | 作用 |
|---|---|
| clock_gen | 产生125MHz用户时钟、GTX参考时钟 |
| gmac | 实现千兆MAC核心,包括发送状态机、接收状态机、CRC、FIFO |
| gtx_comma_alignment / sync | 处理SerDes的字对齐 |
| gtx_wrapper | 封装GTX IP,配置成1000BASE-X模式 |
| client_interface | 用户数据侧的FIFO接口 |
其实XAPP1082的工程结构不是固定的,Xilinx在不同版本里改过封装,但核心思想一致:用户数据通过一个类似FIFO的接口进MAC,MAC组帧后通过GMII交给GTX,GTX编码后由SFP发出去。接收方向反过来,SFP收到光信号,GTX解串、解码,把GMII数据交给MAC解帧,MAC剥离CRC、检查地址,然后用户就能在FIFO读到有效数据。
新手拿到工程第一件事,不要急着看RTL每行怎么写,先去找到顶层module的实例化列表。用Vivado或ISE打开工程后,在Hierarchy窗口里点开顶层,看看有哪些子模块、哪些端口连到了外部引脚。把顶层端口和原理图对照一遍,基本就知道整个数据流了。
3.2 GMII接口时序:125MHz上的8位数据流
理解GMII是理解这个工程的关键。GMII是一个同步并行接口,时钟125MHz,每个时钟周期传8bit数据。除此之外,还有两个控制信号:TX_EN(发送使能)和TX_ER(发送错误),接收方向对应RX_DV和RX_ER。在XAPP1082里,MAC和GTX之间的GMII接口实际上是由GTX的IP核模拟出来的。
为什么GTX能直接出GMII?因为GTX IP可以选择以太网模式,它在内部就把SerDes的8B/10B解码结果映射到GMII上了。所以顶层看到的就是一个“MAC + PHY”完整的接口。在GTX IP核配置界面里,选择“Ethernet”协议,线速率设1.25Gbps,参考时钟125MHz,编码方式8B/10B,GTX就能自动完成K字符对齐、接收恢复时钟等动作。
这里需要注意:GMII的125MHz时钟,在接收方向是CDR恢复出来的,不是板上直接给的。GTX IP输出rxclk,模块内部把rxclk和txclk处理成两个时钟域,MAC里面FIFO做跨时钟域。很多新手在调试时,直接拿板上的125MHz去采rx_data,结果数据乱跳,这就是没搞清接收时钟是恢复时钟。
3.3 MAC层的状态机:发送、接收、CRC
XAPP1082里的MAC核心并不复杂,是一个简化版的10/100/1000M MAC,只支持千兆模式。发送路径上,用户数据先写到tx_fifo,MAC状态机检测到FIFO非空且有完整帧就启动发送,先发前导码(7个0x55加1个0xD5),再发目的MAC、源MAC、长度/类型字段,然后是用户数据,最后是CRC校验值。以太网帧最小64字节,如果用户包小于46字节,MAC会自动填充。
接收路径上,MAC首先剥离前导码,然后检查目的MAC是否匹配自己的MAC地址(XAPP1082里有一个可配置的MAC地址寄存器,默认是0x5A5A5A5A5A5A,你可以改成自己的)。之后检查长度/类型字段是否合法、CRC是否正确,都通过了才把有效数据写入rx_fifo,并在用户接口上产生一个写脉冲。如果CRC错,整帧丢弃,不发给用户。
XAPP1082的MAC还支持流控帧处理,可以实现暂停帧,但大部分应用其实用不到。如果只是做数据收发测试,关注发送FIFO和接收FIFO的接口时序即可。
3.4 8B/10B编码和SFP光模块的关系
千兆以太网在物理层用的是8B/10B编码,把8bit数据变成10bit码字。这么做的目的是保证直流平衡、提供足够的跳变沿用于时钟恢复。GTX的PCS层已经自动完成了8B/10B编解码,所以FPGA逻辑里碰到的都是8bit数据,不用自己写编码逻辑。
另一个概念是Comma字符(逗号码),用于接收端的字节对齐(Word Alignment)。GTX IP可以配置对齐模式,在以太网模式下,它自动寻找K28.5(正码字1100000101,负码字0011111010)作为对齐标志。光纤重新插入或者对端重新上电时,GTX会自动重新对齐,这个特性对热插拔非常关键。我做回环测试时经常把光纤拔插,只要GTX复位正常,几微秒内就能重新同步,不需要重启FPGA。
4. 从零搭建工程:Vivado下跑通XAPP1082
4.1 准备工作:获取源码、确认开发板
去Xilinx官网搜索XAPP1082,进入文档页面,在“XAPP1082 - Using the Transceiver to Implement a Gigabit Ethernet Interface”下面,通常有“XAPP1082.zip”下载链接。下载后解压,里面有一堆文件夹,其中design目录下有HDL源码,在reference-design目录下有各个目标板的工程文件。
如果你是标准开发板,比如KC705,可以直接打开对应的Vivado工程。如果像我一样用的是自己的板子或者Artix-7板,最好新建工程,把design目录下的源码添加进来,再替换约束文件。注意XAPP1082的源码是基于Verilog的,Vivado对老版本工程会自动升级,但最好还是用自己熟悉的版本新建。
准备材料清单:
- Vivado开发环境(我用的是2018.3,新版本也可以用)
- SFP光模块(千兆模块就行,1.25G SFP,单模双纤或单纤都可以,但要注意收发波长匹配)
- 光纤跳线LC-LC
- 带有SFP座子的FPGA开发板
把Laser模块插进SFP笼子,听到咔哒声说明插到位。另一根光纤接到对端设备,如果只是自测,可以先把两根光纤的TX、RX交叉对接(或者模块直接插上就能自发自收,要看模块类型)。
4.2 新建工程并添加源码
用Vivado创建一个新的RTL工程,目标芯片选择你的FPGA型号。添加源文件时,建议按下面的分组添加,方便后期维护:
| 目录 | 内容 |
|---|---|
| source/hdl | XAPP1082的全部RTL源码 |
| source/sim | Testbench(XAPP1082自带Simulation Testbench) |
| source/ip | GTX IP核的xci文件,如果工程没有,需要自己生成 |
| constraints | XDC引脚约束和时钟约束 |
这里最容易出的问题就是GTX IP核。老一点的XAPP1082工程是使用ISE时代的Coregen生成的GTX,在Vivado里需要重新生成。你可以不用它自带的GTX IP,而是自己用Vivado的Transceiver Wizard生成一个符合需求的IP。配置向导的时候,重点设置:
- Transceiver Type选GTX
- Line Rate设为1.25 Gbps
- Reference Clock设为125MHz
- Protocol选择Ethernet(如果选项里没有,选择Basic,然后手动设置RX/TX的编码为8B/10B)
- 开启RX Word Alignment,对齐序列选择K28.5(选择Comma)
生成IP后,打开XAPP1082的gtx_wrapper,会发现它例化了一个叫gtx_ethernet的模块。我需要改例化名和参数,让它匹配自己生成的IP实例名。这一步是移植的核心。我的做法是:先把XAPP1082原版IP模块的端口列出来,再打开我生成的IP的例化模板,把端口逐一对应,然后把gtx_wrapper里的例化代码替换掉。
4.3 约束文件怎么写:时钟、引脚、GTX专用引脚
约束文件包含普通引脚和GTX专用引脚。普通引脚包括SFP的LOS、TX_DISABLE、MOD_ABS等。用set_property -name PACKAGE_PIN -value xx [get_ports ...]设置引脚,同时设置电平标准。GTX的引脚比较特殊,Vivado会自动识别,不需要在XDC里手动分配TXP、TXN、RXP、RXN,你只需要在约束里说明对应的GTX Reference Clock引脚即可。
举个例子,一个典型的XDC片段:
set_property PACKAGE_PIN AB0 [get_ports sfp_los] set_property IOSTANDARD LVCMOS33 [get_ports sfp_los] set_property PACKAGE_PIN AB1 [get_ports sfp_tx_disable] set_property IOSTANDARD LVCMOS33 [get_ports sfp_tx_disable] set_property PACKAGE_PIN AC2 [get_ports {gtrefclk_p}] set_property PACKAGE_PIN AC3 [get_ports {gtrefclk_n}] set_property IOSTANDARD LVDS [get_ports {gtrefclk_p}]注意GTX参考时钟引脚是专用引脚对,命名比如MRCC/SRCC,并且在XDC里需要选择GT_REF_CLK,如果只在引脚上写LVDS,Vivado可能还是会报错。更规范的做法是在约束里用get_pins找到GTX的参考时钟输入,然后用create_clock定义125MHz。
4.4 用ILA抓内部信号:回环测试和改MAC地址
工程跑起来后,怎么知道通没通?最直接的办法是看link up信号。XAPP1082工程一般会输出一个同步状态信号,比如sync_status或者link_timer,这个信号来自GTX的RX sync状态机。如果SFP模块插好、光纤接好、对端设备正常,这个信号应该拉高。我看到很多新手在这里卡住,一上来就发数据,但link都没up,以太网物理层都没同步,数据当然出不去。
用ILA抓信号的步骤:在Vivado里添加ILA IP,连接到GMII接口信号上,比如gmii_tx_en、gmii_txd、gmii_rx_dv、gmii_rxd,再连上复位和时钟。触发条件可以设gmii_tx_en上升沿。这样就能看到MAC是否在发送帧。如果发送状态机一直在发前导码但TX_EN不拉高,说明发送FIFO根本没有数据进来。
自测回环的常用方法有两种:一种是用SFP光模块自环,把TX和RX用光纤环回,这样可以验证FPGA内部的GTX和MAC通路;另一种是在目标板的对端接一个PC或交换机,用Wireshark抓包。回环测试需要特别注意:千兆以太网的自环不能直接接光模块,因为光的TX/RX是不同波长,必须用两根光纤将模块的TX口和RX口用交叉方式接起来(或者用一个双纤LC回环头模拟)。单纤模块不用交叉,但要注意波长。
5. MDIO接口的补充说明
5.1 MDIO在光口方案中的作用
MDIO是管理数据输入输出接口,通常用来读写外部PHY芯片的寄存器。但在SFP光模块的千兆以太网方案中,SFP本身并没有标准PHY寄存器,MDIO一般不会使用。XAPP1082的例子里,MDIO的用途往往是用来访问GTX内部的部分管理寄存器,或者在不使用GTX的自动协商时手动配置一些参数。大部分情况下,我们不需要去操作MDIO,直接用GTX的自动协商/同步机制即可。
5.2 自动协商还是强制模式
千兆光口以太网的自动协商在物理层是通过CGB(Configuration/Base page)来做的,GTX IP会发送/接收配置字。在XAPP1082里,如果想简单,可以关闭自动协商,直接设置成1000BASE-X强制模式。这样GTX发送端会一直发Idle字符,接收端只要同步Comma就可以link up,省去协商过程。但如果对端是普通交换机,开启自动协商更可靠。XAPP1082的顶层有一个参数叫AUTO_NEGOTIATION,可以在例化时设为1或0,开发板上默认是0(强制模式),我就喜欢在调试时打开自动协商,不然有些交换机的端口会一直起不来。
6. 实战问题排查实录:我踩过的坑和解决手法
6.1 SFP插上去 module 没有发光
这是最常见的硬件问题。先量SFP座子的TX_DISABLE引脚,如果悬空,模块内部上拉可能让它为高,那就关断了发射。解决办法是在FPGA里把TX_DISABLE拉低,或者用跳线帽接地。如果再不行,看SFP的电源,SFP VCC必须是3.3V,有些板子SFP T/R电源用两级LDO,纹波大会导致模块工作不稳定。
6.2 GTX复位时序不对导致link up不了
GTX复位信号通常需要满足一定的时序关系,特别是GTX的TX/RX的复位释放顺序有讲究。XAPP1082的工程里自带了一个reset_sync模块,它做多级时钟域同步复位释放。如果自己写代码,不要直接用按键复位信号去复位GTX IP,要加上同步器和延时释放。
实践下来,GTX参考时钟稳定之后至少等待100us,再释放GTX的复位,然后释放PCS的复位,这样link up的成功率最高。如果一上电就立刻拉高reset,GTX的PLL可能还没锁定,会导致之后持续报错。可以在ILA里观察gtx_txresetdone和gtx_rxresetdone信号,两个都拉高后才视为复位完成。
6.3 回环测试时收发不通,数据全是FF/00
这种情况多半是GTX的RX CDR没有锁定参考时钟。检查一下GTX参考时钟是否用的是正确的GTREFCLK引脚,而且频率是125MHz。如果没有给GTX提供差分参考时钟,GTX的CPLL根本无法工作,TX/RX都不会有输出。用Vivado里的Clock Report看一下GTX的时钟结构,确认gtrefclk和user clock都已经连上。
另外,SFP模块的los信号如果拉高,表示没收到光。自环时一定要确保模块的TX端和RX端都插上了光纤,并且光纤是交叉的。有的模块需要拉高RX_RATE_SELECT才能支持千兆,不过绝大多数千兆SFP模块默认就是1.25G工作,不需要额外配置。
6.4 ILA抓不到数据,但链路状态正常
链路up了,但ILA触发条件如果是gmii_tx_en,不管怎么都触发不了,原因可能是发送FIFO没接上,或者发送状态机一直处于等待。检查用户逻辑到tx_fifo的写信号,确认有没有产生写使能和写数据。还有一个容易被忽略的点:XAPP1082的MAC发送需要目的MAC地址、源MAC地址、长度类型这些字段都配好,如果用户侧数据没有正确对齐FIFO位宽(比如FIFO是32bit,但MAC接口是8bit),写入的数据会被截断,导致MAC不断发送错误帧,MAC可能会停在发送状态。调试时可以先用固定数据源,比如用一个计数器循环发0x55、0xAA,排除用户逻辑问题。
6.5 对端电脑/Wireshark抓不到包
如果FPGA连着交换机或PC的网口,交换机端口起不来,多半是自动协商没开或者SFP模块速率不匹配。方法:先确认光模块型号是不是千兆(1.25G),有的百兆模块插进去线速率就不对。然后确认FPGA侧GTX IP的配置是1000BASE-X兼容模式,如果你的GTX配置成了Basic模式,要检查有没有开启8B/10B和逗号对齐,否则发出的数据不是标准以太网码流,交换机没法识别。
还有一个小细节:SFP笼子的金属卡扣是否接地良好?如果接地不良,EMI干扰会让误码率上升,表现就是偶尔能抓到包,但大部分时间数据错乱。这一点在实验室桌面环境里尤其容易忽略。
7. 从XAPP1082到自己的项目:扩展与裁剪建议
7.1 用MAC自带FIFO还是自己写跨时钟域FIFO
XAPP1082的MAC自带发送/接收FIFO,位宽是8bit。如果你的用户逻辑时钟和GMII时钟不一致,得在外部再包一层异步FIFO。我建议直接使用Xilinx的FIFO Generator IP,将用户时钟域数据写入异步FIFO,再把FIFO的读时钟接MAC的tx_clk,读数据接到MAC的用户接口。这样最简单,也最稳。
接收方向同理,MAC输出rx_fifo到用户,接收时钟是rxclk,而用户逻辑常用自己的时钟,所以需要做跨时钟域。再次强调,不要用rx_dv去采rx_data,因为rx_dv和rx_data是同步于rxclk的,必须让rxclk做时序约束。尤其是用户时钟和rxclk频率相近但不精确同频,亚稳态风险很大。老老实实用异步FIFO。
7.2 多端口千兆以太网怎么扩展
一个GTX对应一个SFP,如果你想做4光口,就需要四个GTX。XAPP1082的每个MAC实例都相对独立,把顶层模块实例化四份,每个实例配一个GTX IP,地址和FIFO独立,就能实现4路光口。但这种做法占用的资源较多,逻辑也比较重复。如果对资源敏感,可以考虑用一个MAC配合一个GTX,四个MAC用时分复用,但设计复杂度会上升不少,不建议新手直接做。
做多端口时还有个管理问题:四个光口的link状态、MAC地址、统计信息都要独立,建议在顶层做一个寄存器映射模块,用AXI-Lite接口控制这些参数。这样后期接软核或者上位机调试都非常方便。
7.3 加入ARP和ICMP的简易方案
如果想让FPGA通过光口被PC ping通,光有MAC还不行,还需要实现一个简易的TCP/IP协议栈,至少要有ARP应答和ICMP echo。XAPP1082只做了MAC层,网络层需要自己写。一个常见的做法是用FPGA内部的RAM存一个ARP表,收到ARP请求时自动回复;收到ICMP请求时把数据包原样回显。这个流程网上有很多开源实现,但要注意兼容性和时序收敛,尤其是CRC计算在千兆速率下需要并行化,最好用LFSR展开成8位CRC,不要一个bit一个bit算。
我个人实际测试下来,一个简化版ARP+ICMP逻辑,资源大概占用不到500个LUT,完全能塞进Artix-7的最小芯片里。但这部分已经超出了XAPP1082的范畴,可以留作进阶项目。先跑通原始工程,再逐步加功能,这才是比较稳妥的节奏。
7.4 用SFP光口跑10G以太网还能用XAPP1082吗
不能直接用。XAPP1082针对的是1.25G线速率,10G以太网线速率是10.3125Gbps,需要用GTY/GTH的10G Ethernet MAC,也就是Xilinx另一个IP叫10G/25G High Speed Ethernet。不过学完XAPP1082,理解链路层和数据流之后,换到10G IP会更顺一些,因为很多概念是重叠的:GTX/GTH的复位、CDR、字节对齐、8B/10B(10G时会用64B/66B),只是数据位宽、时钟频率、编码方式变了。
8. 最后分享几个调试心得
以前我做接口类的实验,总喜欢直接上逻辑分析仪抓信号,后来发现高速串行信号逻辑分析仪根本抓不了,只能在GTX的并行侧看数据。所以调试的思路必须改:先是链路层,再看数据层。链路层就是GTX的sync状态、link状态、复位完成标志,这些信号用ILA能抓到。链路都通了,一切好说;链路不通,再查逻辑没有意义。
SFP光口千兆以太网的调试,很大程度是在跟物理层的“不确定性”做斗争。时钟稳定、复位时序、模块选型、光纤接法,这些环节只要错一个,链路就起不来。但反过来,只要你把链路层跑通,后面的MAC层问题都是可以慢慢推敲的。
从XAPP1082出发,我自己后来的几个高速接口项目都是这么起步的:先官方参考设计,把数据环回跑通,再逐步裁剪替换成自己的模块。这个思路不算聪明,但很可靠。如果你也是第一次碰FPGA光口,别急着在一周内通吃所有协议,先把SFP千兆以太网跑通,通了一次,你就会发现,原来高速接口也没有想象中那么遥远。