做FPGA的工程师,很少有人没跟SerDes打过交道。从ADC采集板往CPU传数据、两个FPGA之间搬大容量数据、或者把视频流通过光纤拉出去,这些都绕不开高速串行接口。前几年我第一次在项目里要把一路GTX从1.25Gbps提到5Gbps,以为自己配置好IP核就能跑,结果板子回来后链路完全锁不住,眼图几乎闭合,排查了整整两周才定位到是参考时钟选错了。从那以后我明白,SerDes不像GPIO或者普通LVDS那样点两下就能用,它是一条从PCB到PLL再到编码逻辑的完整链路,任何一环没做对,最终都会体现在误码率上。这篇文章就把FPGA实现SerDes高速接口设计时最关键的知识点、实施步骤和我踩过的坑系统整理出来。无论你是刚接触高速接口的新手,还是已经会跑IBERT、但还没系统梳理过整条链路的工程师,这篇应该都能给你一些参考。
1. 为什么并行总线会在高速场景退出历史舞台
1.1 并行总线的三个硬伤
先看一个最常见的问题:为什么现在高速数据不用32位或者64位并行总线,而偏偏要用SerDes这样一根差分线串行传?
早期板级互连大量使用并行总线,比如FPGA和DSP之间挂EMIF、FPGA和ADC之间走并行CMOS接口。低速的时候没问题,但速率一旦往上提,三个问题就会暴露出来。
第一个是时钟偏斜(Clock Skew)。并行总线需要同时保证数据和时钟的时序关系,数据总线的每一根线延迟必须基本一致。PCB走线经过过孔、连接器、走线换层之后,各根线之间的延迟差很难控制在皮秒量级。速率越高,一个比特窗口越短,偏斜带来的时序裕量就消耗得越快。
第二个是串扰(Cross-talk)。并行总线位宽动辄16位、32位,相邻信号线之间的耦合电容和互感会把一根线上跳变耦合到另一根线上,形成噪声。速率越高,信号边沿越陡,串扰越明显。我做过一块板子,DDR2接口速率提到400MT/s时,数据总线内部串扰导致偶发错误,最后只能靠降低速率和加大间距解决,代价非常大。
第三个是功耗和引脚数。并行总线的每一位都需要一对驱动器和接收器,32位数据总线意味着几十个IO同时以高速翻转,动态功耗高得吓人。而且高速并行接口通常需要大量引脚,这对FPGA的封装尺寸、BGA布线难度、PCB层数都是很大的压力。相比之下,SerDes用一对差分线就能完成同样的吞吐量,引脚省下来一大半。
1.2 SerDes的核心架构拆解
SerDes是SERializer/DESerializer的缩写,说白了就是发送端把并行数据变成一串高速比特流,接收端再从比特流中把数据恢复出来。不要被"高速"两个字吓到,它的基本功只有三块:并串转换、传输、串并转换。
发送端的流程是这样:用户逻辑把并行数据送到发送端的PMA(物理介质附加层)模块,先经过编码(比如8B/10B),再经并串转换变成高速串行比特流,通过差分对输出。接收端则相反,先由接收端的模拟前端对差分信号做均衡,再由时钟数据恢复电路(CDR)从比特流里提取出时钟和数据,串并转换之后经过解码还原成并行数据给用户逻辑。
这里有一个初学者容易误解的点:SerDes传输不需要单独传时钟。数据里的跳变沿本身就是时钟信息,接收端通过CDR从数据流中恢复出采样时钟。这和并行总线最大的区别,也是它能跑高速的根本原因之一——没有时钟偏斜问题了。
1.3 四大核心技术机制及其作用
为了保证高速串行传输的可靠性,SerDes规范里出现了一系列配套机制。我挑四个最关键的讲,它们也是后面调板、查错时会反复遇到的概念。
8B/10B编码:每8位数据被编码成10位符号,多出来的2位用来保证直流平衡——即传输码流中0和1的数量尽量接近。这样做有两个目的:一是避免长时间没有跳变导致CDR失锁;二是提供足够的跳变密度帮助CDR提取时钟。除此之外,10位码空间里有一部分是控制字符(K码),比如K28.5,接收端可以拿它来做字节对齐。代价是带宽利用率只有80%,协议传输效率相对较低,但换来的是鲁棒性。
CDR:接收端从数据流中恢复时钟。它内部有个本地振荡器,通过锁相环跟踪数据流的相位变化,从而找到最佳采样点。CDR的锁定需要数据里保持一定的跳变密度,所以"长期不发数据"对SerDes来说反而是灾难——上游逻辑必须在空闲时发送K码或者置位,维持链路活跃。
均衡(Equalization):高速信号在PCB上传输时,高频分量衰减比低频分量严重得多,导致接收端看到的是一个"闭合"的眼图。均衡就是发送端对高频分量做预加重(Pre-emphasis),接收端对高频分量做增益补偿(CTLE/DFE),把传输通道的频响拉平。这也是为什么越长的高速链路越依赖均衡参数——不是PCB铺出来就能直接用的。
加扰(Scrambling):如果数据流出现长串连续的0或1,会造成频谱能量集中在低频,干扰CDR。加扰器通过伪随机序列对数据做异或处理,让频谱能量分散开来。像PCIe、万兆以太网这类协议会用64B/66B或128B/130B编码配合加扰来提升效率,而不是用8B/10B。
1.4 哪些场景必须上SerDes
开门见山说结论:只要单通道速率超过1Gbps,或者并行总线宽度大到你已经养不起引脚和功耗,就轮到SerDes上场了。常见场景包括FPGA与FPGA之间的高速互连(Aurora协议)、FPGA和ADC/DAC之间的JESD204B接口、PCIe、万兆以太网、光纤通信、视频传输(SDI)等等。
我见过有人为了省事,想把1.6Gbps的数据用LVDS在FPGA之间传,结果PCB布线难度、功耗、时序收敛全在拖后腿。实际上FPGA内部GTX/GTH等硬核SerDes通道容量非常充足,直接用半分力气就能解决问题。
2. FPGA内建SerDes的硬件架构与选型思路
2.1 从PMA到PCS:SerDes内部的分层
FPGA里的SerDes并不是一个黑盒,它内部大致分两层:PMA层和PCS层,理解这两层的分工对于调试非常重要。
PMA层是模拟部分,负责物理信号的处理:发送端的并串转换、差分驱动器、TX均衡;接收端的均衡器、CDR、串并转换。这一层的参数通常通过IP核配置,比如TX摆动幅度、预加重强度、接收端CTLE增益等,一旦配置错误,眼图直接闭合。
PCS层是数字部分,负责编解码和通道绑定逻辑,常见的8B/10B编解码器就在这一层。8B/10B编码器把16位(或32位)数据转换成20位(或40位)码流,再交给PMA层串行化。接收方向则反过来,CDR恢复出来的码流先做字节对齐,然后经8B/10B解码还原成并行数据。还有弹性缓冲(RX Elastic Buffer)也在PCS层,用来补偿收发两端时钟频率差,避免数据溢出或不足。
2.2 主流FPGA厂商的SerDes资源对比
不同厂商、不同型号的FPGA,SerDes的能力和配置方式差异很大。我在选型时一般看四个指标:线速率范围、通道数、支持的协议、配置工具成熟度。
从Xilinx(现AMD)阵营说起,7系列之后基本靠GTX、GTH、GTY这几个系列。Artix-7里的GTX支持到6.6Gbps,Kintex-7的GTX到12.5Gbps,GTH到13.1Gbps,Virtex-7里GTX/GTX+更高。到了UltraScale家族,GTH能到16.3Gbps,GTY到32.75Gbps。配置方面用IP Catalog里的Transceiver Wizard,非常成熟。
Intel(Altera)那边,Cyclone 10 GX的Transceiver支持到6.6Gbps,Arria 10能到17.4Gbps,Stratix 10通过不同速率等级的Transceiver支持到更高的速率。配置用的是Qsys/Platform Designer里的Altera Transceiver IP,流程相对复杂,但工具链也能生成完整的例化代码。
国产FPGA近几年的进展也很快。高云(Gowin)的GW2A系列和GW2AR系列内置了支持2.5Gbps的SerDes资源;紫光同创的Logos系列、安路的高性能FPGA也都陆续支持高速串行接口。选国产芯片的时候要特别注意两点:一是支持的线速率范围是否覆盖你的应用,尤其是上限值;二是必须仔细看参考手册,不同型号SerDes的参考时钟结构、复位时序差异不小,网上能搜到的资料也比国际厂商少很多。
2.3 选型前必须确定的五个参数
在动手生成IP之前,把下面五个参数先定死,不然会来回返工。
线速率(Line Rate):这是SerDes传输的物理速率,直接决定数据吞吐量。用户侧数据位宽、参考时钟频率都由它推导出来。
参考时钟频率:SerDes的PLL需要一个低频参考时钟,通常是线速率的1/40或1/50,比如GTX 5Gbps时常配125MHz参考时钟。也有100MHz或156.25MHz的,具体看IP向导里的可选范围和你的晶振方案。
编码方式:8B/10B、64B/66B还是透明传输?编码方式决定PCS层的最低位宽和CDR的抖动容限设计,同时直接影响有效数据带宽。8B/10B有效带宽只有80%,对带宽敏感的场合建议考虑64B/66B或原语协议。
用户侧数据位宽:一般选2字节(16bit)、4字节(32bit)或8字节(64bit)。位宽越大,用户逻辑时钟越低,时序越容易收敛,但总线交错和字节对齐处理起来也更复杂。
协议层选择:如果只是FPGA之间互传数据,选Aurora这个轻量协议最省心,它自带流控和对齐;如果要接PCIe、JESD204B这些标准外设,建议直接用厂商提供的协议IP,不要自己从头搭物理层+链路层。
3. 项目落地:从IP配置到收发通道跑通
3.1 用向导生成收发器IP:关键配置项逐条拆解
拿Xilinx的Transceiver Wizard举个例子,配置界面里有一堆选项,新手很容易被吓到。我实际项目里常改的其实就那几项。
进入页面选择"GTX Transceiver",第一个要填的是线速率。比如我要跑5Gbps,直接把Line Rate填成5.0。注意,不同参考时钟下,线速率能选的档位范围不一样,如果参考时钟给的125MHz,5Gbps通常没问题。
然后是参考时钟,界面里会让你选参考时钟频率和是哪个参考时钟引脚。很多板子的GTX参考时钟是专用引脚,比如MGTREFCLK0和MGTREFCLK1,必须让IP知道你的时钟来源。有个容易踩的坑:不是所有板子都能把所有REFCLK频率都支持,一定要对着板卡原理图确认。
编码方式那里选择8B/10B。选完后界面会同步展示用户侧数据位宽和内部时钟频率。比如5Gbps经过8B/10B编码后有效带宽是4Gbps,如果数据位宽选32bit,用户侧时钟就是100MHz。看到这个频率,你就可以反推自己的逻辑能不能在这个时钟下收敛。
还有一组很关键的选项是TX/RX均衡参数。向导默认会给一个值,但实际板子上能不能跑稳,得靠IBERT扫描眼图来确认。我的习惯是先把IP里的TX Diff Swing、Pre-emphasis这些参数设为大到中等强度,等眼图出来了再细化。
3.2 收发逻辑的用户侧设计
IP生成之后,它的用户侧接口是一组并行数据总线和一些控制信号。多数初学者在这里会犯一个错误:以为把数据放到TX_DATA引脚上就能发出去,完全不管K码和字节对齐。
以8B/10B编码为例,IP分成两个字节通道:一个数据字节通道和一个控制字节通道。每条用户数据的边上都有一个TXCHARISK信号,用来标识对应字节是数据还是K码。发送时,如果你想发对齐用的逗号字符,就要在那个字节位置把TXCHARISK拉高,并在数据线上放K28.5的编码值0xBC。
接收方向正好反过来。IP输出RX_DATA、RXCHARISK和RXDATAVALID,在调试阶段先把它收到的数据原样存下来,检查K码是否落在了正确的位置。如果K码位置不对,说明字节边界没对齐,需要检查RXSLIDE控制信号或者检查上层的对齐状态机。
下面这个Verilog片段展示了我常用的简洁发送逻辑,它把用户数据包在0110和0xBC之间,方便接收端搜索帧头。
// 发送逻辑:每个周期发2字节,首字节为K28.5(对齐标识) // 通过 tx_charisk[0]=1 告知PCS层该字节为控制字符 always @(posedge user_clk) begin if (tx_ready) begin if (send_idle) begin tx_data <= 16'hBC00; // 低字节为K28.5 tx_charisk <= 2'b01; end else begin tx_data <= {data_hi, data_lo}; tx_charisk <= 2'b00; end end end接收端要做的第一件事不是解析数据,而是确认链路状态。IP一般会输出多个状态信号,比如TX_RESET_DONE和RX_RESET_DONE,它们拉高才代表收发器完成复位。还有一些锁存状态如RX_STATUS里的CDR锁定标志、RXBUF_STATUS里的弹性缓冲溢出/不足标志。调试阶段把这些信号引到LED上,是最快速的物理检查手段。
3.3 整体例化结构:参考时钟、复位和异步FIFO
完整的用户工程不止一个IP核,还包含参考时钟管理、全局复位逻辑和跨时钟域处理。
参考时钟进来之后,通常不能直接给GTX的REFCLK引脚,而要先经过BUFG或者MMCM/PLL,再通过专用BUFG_GTX之类的资源连到GTX IP。这样做的目的是给每个QUAD提供干净、低抖动的时钟树。
复位逻辑是很多问题的源头。GTX上电复位有着严格的顺序:先确保QPLL或者CPLL锁定,再给TX、RX子系统复位,之后等待TX_RESET_DONE/RX_RESET_DONE。如果直接把所有复位信号接到全局复位按键上,大概率第一次上电能跑、复位一次就失锁。
还有一个新手容易忽略的地方:RX恢复出来的RXUSRCLK和用户逻辑主时钟是不同频同相的,直接从RX_DATA采样数据会出问题。规范做法是在用户侧加一个异步FIFO,读时钟用RXUSRCLK,写时钟用用户系统时钟,或者反过来,保证数据平滑过跨时钟域。
4. 板级设计:SerDes性能的另一半决定权在PCB上
4.1 电源是SerDes稳定工作的第一前提
很多人调试SerDes不稳定的第一反应是改IP参数、改逻辑,但实际上我遇到过的失锁和误码问题,有三分之一是先查电源就能解决的。
FPGA的SerDes通常有独立的电源引脚,Xilinx的GTX有AVCC和OVCC,分别给PMA模拟电路和PCS数字电路供电。这两路电源对纹波非常敏感,尤其AVCC,纹波直接耦合进模拟前端,影响CDR的抖动容限。我用的排查方法是高带宽示波器测纹波,带宽至少500MHz,探头尽量短,在FPGA电源引脚附近直接测。
如果发现纹波超标,先从电源模块本身查起:是否使用了低噪声LDO、开关电源的频率是否太高、输出电容的ESR是否过大。高速板卡上我习惯给SerDes电源独立分区,用磁珠隔离,避免数字电路的开关噪声串进来。
4.2 参考时钟是高速链路的心跳
参考时钟的抖动要求远高于普通时钟。SerDes的PLL虽然能对参考时钟的绝对频率误差做跟踪,但对短周期抖动(Jitter)几乎是原样传递到高速输出上,最终形成输出数据流的相位噪声。
选晶振时,我的选型标准是随机抖动RMS小于0.5ps,对10Gbps以上的链路更得严格到0.2~0.3ps。普通有源晶振不一定够,许多高端板卡直接上VCXO或者OCXO。另外,参考时钟引脚的走线要尽可能地短、尽量少打过孔,避免换层时引起阻抗不连续。如果参考时钟走线要从FPGA一侧拉到另一侧,宁可走内层、加地孔栅栏保护,也不要贴着高速信号线并行走。
4.3 差分走线、连接器与AC耦合电容
高速差分线的PCB设计有它自己的规矩,这里列几条我几乎每次画板子都会核对的要求。
第一,差分阻抗控制到100Ω。SerDes的差分对标准阻抗通常是100Ω,有些协议会指定85Ω,但FPGA内部驱动器是按100Ω优化的。阻抗不匹配会在链路上形成反射,反映到眼图上就是眼皮变厚、噪声加大。
第二,差分对内等长要控制得极其严格。5Gbps时信号周期200ps,PCB上每英寸走线约160ps延迟,对内长度差超过几十mil就会产生明显的时间偏移,从而劣化差分信号质量。我的习惯是差分对内等长误差控制在2mil以内,对间等长做成5~10mil。
第三,AC耦合电容的位置和选型有讲究。绝大多数SerDes链路要求收发端之间存在直流隔离,所以差分走线上要串两个AC耦合电容。电容容值常见0.1uF,封装越小寄生电感越小,0402甚至0201都可以用。放置位置尽量靠近发送端,而且两个电容的焊盘要做好阻抗补偿,避免在焊盘处产生阻抗突变。
第四,连接器选型不能只看机械尺寸,传输特性同样重要。SMA、SMP这类RF连接器性能好,但连接两个FPGA板卡时,更多会选QSFP/SFP笼子或者高速板对板连接器。连接器的带宽要高于你的信号速率,最好留有20%以上裕量,否则连接器本身就成了瓶颈。
5. 上板调试:从眼图到误码率的实测流程
5.1 先跑IBERT,把所有变量归一
拿到新板子,我最推荐做的第一件事不是跑自己的用户逻辑,而是先跑厂商自带的IBERT。Xilinx和Intel的FPGA都集成了这个功能,它不需要用户写逻辑,通过JTAG就能配置SerDes通道,并内置PRBS发生器、误码校验器和眼图扫描模块。
用IBERT能一次性确认这板子的硬件链路是否健康。连接好板子和JTAG后,把PRBS模式设成PRBS-7或PRBS-31,跑上几分钟,看误码率。然后打开眼图扫描,软件能画出每个通道的二维眼图。正常高速通道眼图应该像一只完全睁开的眼睛,开口至少有UI的50%以上,交叉点清晰,内外环分离。如果眼图高度低、宽度窄,或者有个别采样点颜色异常深,说明链路有反射或者均衡设置不对。
IBERT还有一项好处是能测不同参数组合。TX预加重、RX均衡、输出电压摆幅这些都是在IBERT里调出来的,调到最优再固化到IP核里。注意你实际用户逻辑使用的数据模式不一定是PRBS,如果链路对PRBS-31低误码,很大概率对真实业务也能跑。
5.2 链路失锁的排查顺序
如果上板后RX_RESET_DONE拉不上去,或者拉上去之后链路又掉,千万不要上来就改IP参数。我总结了一个排查顺序,按这个顺序节省了不少时间。
先看电源。用万用表量AVCC和OVCC电压是否在规格内,再用示波器看纹波,重点看FPGA启动瞬间和高速翻转时有没有跌落。我遇到过AVCC在FPGA配置过程中跌落300mV,导致PHY状态机无法正常初始化的案例。
然后看时钟。用示波器测参考时钟引脚的波形,确认频率、电平、幅度都正常。注意有些FPGA的REFCLK是专用引脚,你不能随意用普通IO输出一个时钟来接。
接着看复位。检查IP的中复位信号有没有被错误地持续拉低,复位释放时序是否符合手册要求——尤其要等PLL锁定信号之后再释放接收端复位。
最后看配置。打开Protocol Analyzer或者读IP的状态寄存器,看看有没有报出CDR失锁、弹性缓冲溢出/不足等错误标志。不要光看用户逻辑侧的状态,有些链路问题是IP内部状态机自己都没缓过来的。
5.3 数据错位和误码的定位方法
链路锁住了,但数据不对,这是第二个常见麻烦。数据错位的症状通常是:接收端能收到数据,流速率看起来也对,但数据的字节顺序错乱,或者偶尔出现一个字节的错位。
这时先判断是字节对齐问题还是数据内容问题。用IBERT跑PRBS,如果PRBS校验全过,说明链路物理层没问题,问题在PCS层或者用户逻辑。如果是字节对齐问题,接收方向需检查RXSLIDE(对齐调整信号)的时序是否正确,对齐状态机有没有在K码出现的时候完成搜索和锁定。
如果物理层没问题、字节对齐也对,那就要怀疑用户侧的跨时钟域处理了。我之前就遇到过把RX_DATA直接用另一个时钟域的FIFO采样,导致偶发数据错位的情况。这种问题不是每次都复现,可能需要加压测试才能抓出来。解决办法是把RX侧的接口用一个独立的异步FIFO隔离,确保读写时钟都是本地化的。
6. 避坑指南:我在高速接口上踩过的几个大坑
6.1 复位时序处理导致偶发失锁
第一次用GTX的时候,我把所有复位信号接到系统复位按键上,板子刚上电一切正常,但只要按一下全局复位,链路就再也恢复不了,必须重新配置FPGA才能跑。排查了很久才意识到,复位信号必须严格遵守时序:要先等QPLL/CPLL锁定,再释放TX和RX复位,两个子系统的复位释放还有先后顺序。全局复位按键直接灌到所有复位引脚,等于让链路在PLL没锁定的情况下强行复位,自然起不来。
现在的项目中,我干脆用状态机实现一个复位控制器:上电先复位PLL,等PLL_LOCKED拉高,延迟若干个时钟周期,再分别复位TX和RX,每个子系统复位后等对应的RESET_DONE信号拉高,再进入正常工作状态。这套逻辑写一次可以复用在所有带SerDes的板子上。
6.2 参考时钟引脚接错引发的疑难杂症
有一块板子,GTX跑1.25Gbps时非常稳定,改成5Gbps后眼图始终不好,误码率在1e-6到1e-8之间波动,重做了好几次板子都找不到原因。后来无意间发现设计者把GTX参考时钟接到了普通IO引脚上,而不是专用的MGTREFCLK引脚,等于高速PLL一直在一个没有干净时钟树支持的参考源上工作,性能上不去是必然的。
Xilinx FPGA上每个GTX Quad有专用的参考时钟引脚,标为MGTREFCLK0/MGTREFCLK1,这些引脚内部有专用的时钟布线通道,可以直达对应Quad的PLL。你把参考时钟放到普通IO,即使能通过全局时钟网络进去,走线路径长、抖动增大、无法满足高速PLL的需求。画原理图时一定要把REFCLK引脚接到指定的专用脚上,这一点比任何IP配置都重要。
6.3 跨时钟域处理不当造成数据偶发错位
另一个印象深刻的坑是数据错位问题。用户逻辑用100MHz系统时钟,GTX的RXUSRCLK也是100MHz,两个频率完全一样,想着直接采数据没问题。结果高压测试时发现,每传输几百万个字节就会错一个字节——频率一样但相位不同,长期累积的相位差会让寄存器偶尔满足不了建立保持时间,产生亚稳态,再恢复过来后数据就错位了。这个坑最坑人的地方在于,它不常复现,普通调试根本发现不了。
教训就是:即使两个时钟频率相同,只要它们不是同一个源,就必须做异步处理。最简单的做法是加一个异步FIFO,深度不用太深,16或32深度就够了,关键是能平滑跨时钟域的数据。
6.4 电源毛刺导致的链路不稳定
还有一次调试,链路单独跑IBERT的时候完全正常,一旦加载了复杂的用户逻辑(尤其是大量逻辑同时翻转时),链路就会出现误码。百思不得其解,后来用示波器挂在AVCC上才发现端倪:用户逻辑大规模翻转时,数字部分的开关电流瞬间增大,AVCC在高速开关器件的耦合下出现了幅值大约80mV的毛刺。
这类问题在高速板上特别常见。从根本上解决,要把SerDes电源的滤波做好,磁珠、电容阵列、独立电源层都不可省。我后来在项目规范里加了一条:凡是带SerDes的板卡,模拟电源域和数字电源域必须物理隔离,模拟电源走独立的LDO,严禁直接从一个电源轨拉出来共用。
说句实在话,SerDes高速接口设计最劝退研究者的地方不是逻辑代码,而是这种"看不见摸不着"的模拟链路——信号能不能稳定恢复,跟你PCB上的一根走线、一个电容、一个复位时序都有关系。我的习惯是每条新板卡回来,先跑48小时循环误码测试,确认无误再去做协议层和应用层。如果你正在调板子,建议也从干净的眼图开始,一步步把各环节排查清楚,相关工具和方法上面已经写得很详细了,照着做能少走很多弯路。