基于FPGA与JESD204B的250MSPS双通道ADC网络传输采集系统设计与实现
1. 项目缘起与整体架构拆解1.1 这个项目到底在做什么ADDA网络传输采集项目说白了就是一套把模拟信号变成数字信号、再通过网络送到电脑上显示和分析的完整链路。核心硬件是双通道ADC采样率250MSPS分辨率14BIT。上位机负责接收数据、画波形、做触发判断、算频谱和信噪比。这套东西在雷达、超声、通信基带、高速数据采集卡里都是标配架构只不过我这次把它做成了一个可以独立跑通的完整项目。先把这个项目的几个硬指标拆开说。250M采样率意味着每秒采2.5亿个点14BIT意味着每个采样点用14位二进制表示双通道就是两路模拟输入同时采。这三个参数放一起数据率是相当可观的250M × 14bit × 2通道 7Gbps的原始数据率。这个量级的数据不可能直接塞给普通单片机必须走高速接口加FPGA做缓存和预处理再通过网络往上位机送。我选这个架构的原因很直接FPGA负责硬实时任务比如ADC接口时序、数据缓存、触发判断网络负责把数据搬到PC上位机负责软实时任务比如波形显示、频谱计算、信噪比分析。三层各干各擅长的事不互相拖累。1.2 为什么这么选方案先说ADC选型。250MSPS、14BIT、双通道市面上能选的芯片不少比如AD9250、ADS42LB69、LTC2145这些。我最终选的是AD9250原因是它的JESD204B接口在高采样率下布线比LVDS简单而且双通道集成度高省板子面积。14BIT而不是12BIT或16BIT是因为12BIT动态范围不够做信噪比分析时底噪压不下去16BIT在250M这个速率下功耗和价格都上去了14BIT是性价比拐点。FPGA这边我用的Xilinx Artix-7系列具体型号是XC7A200T。选它不是因为性能过剩而是因为JESD204B的IP核需要足够的逻辑资源和高速收发器。Artix-7的GTP收发器能跑到6.6Gbps对付JESD204B的lane速率绰绰有余。如果你用Zynq当然更好PS端可以直接跑Linux做网络协议栈但我这次想把网络部分也放在FPGA里做所以选了纯FPGA方案。网络传输这块我纠结过很久。千兆以太网理论带宽1Gbps实际UDP吞吐大概能到900Mbps左右。但我的原始数据率是7Gbps差了一个数量级。所以必须做降速处理要么降采样要么只传触发后的片段要么做数字下变频后传基带。我最终选了“触发后片段传输可选降采样”的方案因为实际做信号分析时你真正关心的往往只是触发点附近的一小段波形没必要把7Gbps全传上去。上位机用C#写的基于.NET Framework 4.8加WinForm。选C#不是因为它是性能最好的而是因为开发效率高、图表控件成熟、网络编程简单。做频谱计算时我调用了MathNet.Numerics库做FFT信噪比计算是自己写的。如果你更在意跨平台Qt确实是个好选择但Windows下C#加WinForm的开发速度是Qt比不了的。1.3 整体数据流长什么样从模拟信号进来开始完整的数据流是这样的模拟前端经过变压器耦合或运放缓冲进入ADC的差分输入ADC在250M时钟下采样输出JESD204B串行数据到FPGA的GTP收发器。FPGA里的JESD204B IP核把串行数据解成并行采样点然后写入FIFO做跨时钟域处理。触发模块实时监测采样值满足触发条件时把前后一段数据打上标记存入DDR3。网络模块从DDR3读出标记过的数据打包成UDP包通过千兆PHY发出去。上位机收到UDP包后解析、拼帧、画波形、算FFT、算SNR。这个链路里最容易出问题的地方是JESD204B的同步和DDR3的带宽分配。JESD204B需要正确的SYSREF和同步信号否则链路起不来。DDR3这边ADC持续写入和网络读取会抢带宽必须做优先级仲裁否则要么丢数要么网络卡顿。注意JESD204B的链路建立对时钟质量极其敏感SYSREF的抖动要控制在几个ps以内否则会出现间歇性失锁。我在第一版板子上就吃了这个亏后来换了低抖动的时钟分配芯片才稳定。2. 硬件设计与ADC接口核心细节2.1 ADC前端电路的关键参数计算ADC前端是整个信号链的第一道关设计不好后面全白搭。AD9250的模拟输入是差分形式满量程输入范围典型值是1.75Vpp差分。这意味着单端信号最大幅度是0.875Vpp超过这个值就会削顶。我在前端用了Mini-Circuits的TCM1-63AX变压器做单端转差分带宽覆盖到几百MHz没问题。输入阻抗匹配这块要注意。AD9250的差分输入阻抗在250M采样率下大约是几百欧姆量级具体值要看数据手册的等效输入模型。我用了50欧姆端接加变压器的方式在变压器次级并联一个100欧姆电阻做阻抗匹配。实测在100MHz输入时回波损耗优于-15dB够用了。抗混叠滤波器是必须的。250M采样率下奈奎斯特频率是125MHz超过125MHz的信号会混叠回来。我在变压器后面加了一个五阶巴特沃斯低通滤波器截止频率设在110MHz这样在125MHz处已经有足够的衰减。滤波器的电感电容值是用ADI的Filter Wizard算出来的实际调试时用网络分析仪扫了一遍和仿真结果基本吻合。时钟输入这块AD9250需要一个250MHz的差分时钟。我用的是TI的LMK04828时钟芯片它能同时产生ADC采样时钟、FPGA的JESD204B参考时钟和SYSREF。LMK04828的抖动指标是100fs RMS左右对14BIT ADC来说完全够用。时钟走线必须做差分阻抗控制100欧姆差分阻抗等长误差控制在5mil以内。2.2 JESD204B链路配置要点JESD204B是这个项目里最复杂的部分配置不对链路根本起不来。AD9250的JESD204B参数是这样的双通道每个通道14BIT所以总数据位是28BIT。我配置成两个lane每个lane跑14BIT线速率是250M × 14 × 10/8 4.375Gbps。这个速率下Artix-7的GTP完全能胜任。JESD204B的链路参数包括M、N、N、S、L、F、K这些。M是转换器数量这里是2N是ADC分辨率14N是每个样本传输的位数16S是每帧的样本数我设成1L是lane数2F是每帧的字节数算下来是2K是每多帧的帧数设成32。这些参数在FPGA的JESD204B IP核和ADC的寄存器里必须完全一致错一个链路就起不来。链路建立的过程分几个阶段代码组同步、初始通道对齐、用户数据阶段。代码组同步时ADC发K28.5字符FPGA的接收端检测到足够数量的K28.5后拉高SYNC信号。初始通道对齐阶段发ILA序列FPGA用这个序列对齐多个lane的延迟。最后进入用户数据阶段开始传真正的采样数据。提示调试JESD204B时一定要用示波器看SYNC信号和SYSREF的时序关系。SYSREF必须在SYNC拉高之前稳定否则链路会反复重启。我建议在FPGA里加一个链路状态寄存器实时监测当前处于哪个阶段出问题时能快速定位。2.3 电源与PCB布局的坑ADC的电源噪声直接影响信噪比。AD9250需要1.8V模拟电源、1.8V数字电源和3.3V接口电源。我用的是LT3045和LT3094这对超低噪声LDO模拟电源的噪声密度在10Hz到100kHz范围内小于1nV/√Hz。数字电源用开关电源加LC滤波就够了但模拟电源千万别用开关电源直接供底噪会明显恶化。PCB布局上ADC的模拟输入走线要尽量短差分对严格等长两边包地。时钟走线远离数字信号最好走在内层上下都有地平面屏蔽。JESD204B的差分线要做100欧姆差分阻抗过孔数量尽量少换层时旁边要加回流地过孔。地平面处理是个容易犯错的地方。我第一版把模拟地和数字地分开用磁珠连接结果发现JESD204B的误码率很高。后来改成统一地平面只在ADC下方做局部隔离问题就解决了。原因是JESD204B的高速信号回流路径被分割地平面打断了导致信号完整性恶化。3. FPGA逻辑设计与触发实现3.1 数据接收与缓存架构FPGA里的数据接收从JESD204B IP核开始。IP核输出的是并行采样数据位宽是32BIT两个通道各16BIT高14位有效。这个数据率是250M × 32BIT 8Gbps在FPGA内部200MHz时钟下是40BIT宽时序压力不小。我在IP核输出后加了一级FIFO做跨时钟域从JESD204B的恢复时钟域转到系统时钟域。DDR3缓存是必须的。Artix-7内部BRAM只有几Mbit存不了多少数据。我外挂了一颗512MB的DDR3用MIG IP核控制。ADC数据持续写入DDR3的一个环形缓冲区触发发生时把触发点前后的数据打上标记。网络模块从DDR3读数据时优先读标记过的区域。DDR3的带宽分配是个关键问题。ADC写入是持续不断的网络读取是突发的。如果仲裁做得不好网络读的时候ADC写不进去就会丢数。我的做法是给ADC写入最高优先级网络读取用剩余带宽。实测下来千兆网络最多用到800Mbps左右DDR3的带宽是足够的。触发模块我做了三种模式边沿触发、电平触发和窗口触发。边沿触发是信号超过阈值时触发电平触发是信号在阈值以上时持续触发窗口触发是信号进入某个范围时触发。触发阈值可以通过上位机配置精度是14BIT。触发位置可以设置触发点前后各存多少数据我设的是触发前50%触发后50%实际用的时候可以根据信号特点调整。3.2 网络传输协议设计UDP协议是我选的因为TCP的重传机制在高速数据流下会引入不可控的延迟。UDP虽然会丢包但在局域网内丢包率极低而且我加了应用层的序号和重传请求机制关键数据不会丢。每个UDP包我设成1400字节的有效载荷加上UDP头和IP头总长度控制在1500字节以内避免IP分片。包结构是这样的包头4字节0xAA55AA55包序号4字节数据长度2字节通道号1字节保留1字节然后是有效载荷。上位机收到包后先检查包头然后按序号拼帧。网络发送的速率控制很重要。如果FPGA不管上位机能不能处理就全速发上位机的缓冲区会溢出。我在FPGA里加了一个简单的流控机制上位机每隔一段时间发一个心跳包FPGA根据心跳包的间隔调整发送速率。如果上位机处理不过来心跳包间隔会变长FPGA就自动降速。注意UDP包的大小不要超过MTU否则会被IP层分片增加丢包概率。1500字节是标准以太网的MTU减去IP头和UDP头有效载荷最多1472字节。我设1400字节是留了余量防止某些网络设备对MTU处理不一致。3.3 触发精度的实测与优化触发精度直接影响你抓到的波形是不是你想要的。我实测下来边沿触发的精度可以做到一个采样点以内也就是4ns。这个精度对于大多数应用足够了但如果你要做更精细的时序分析可以用FPGA内部的高速比较器做亚采样点触发。触发抖动主要来自两个方面ADC的孔径抖动和FPGA比较器的时钟抖动。AD9250的孔径抖动典型值是50fs RMS对应250M采样率下的时间误差是0.0125ns可以忽略。FPGA比较器的时钟抖动取决于系统时钟的质量我用的是LMK04828产生的200MHz系统时钟抖动在100fs量级对应的时间误差也很小。实际调试时我发现一个问题触发阈值附近如果有噪声会导致反复触发。解决办法是加迟滞也就是触发阈值和释放阈值分开设置。比如触发阈值设成1000释放阈值设成900这样信号在1000附近抖动时不会反复触发。迟滞窗口的大小可以根据信号噪声水平调整。4. 上位机软件实现与信号分析4.1 波形显示与触发控制界面上位机用C# WinForm写的主界面分三个区域波形显示区、控制参数区和频谱分析区。波形显示用的是ScottPlot控件它的渲染速度比Chart控件快很多250M采样率下每帧显示几千个点毫无压力。波形显示我做了降采样处理。原始数据是250M采样率屏幕宽度最多1920像素不可能每个点都画出来。我的做法是每N个点取一个最大值和一个最小值画成包络线。这样既能看清信号的整体形状又不会因为降采样丢失细节。N的值根据当前显示的时基自动计算。触发控制这块上位机可以设置触发模式、触发阈值、触发位置和触发次数。触发阈值用滑块加数值框的方式滑块调大概数值框调精确。触发位置用百分比表示0%表示触发点在最左边100%表示触发点在最右边。触发次数设成1就是单次触发设成0就是连续触发。提示波形显示刷新率不要设太高30fps足够了。刷新率太高会占用大量CPU导致网络接收线程被抢占反而容易丢包。我一般设成20fps既流畅又不影响数据接收。4.2 频谱计算与信噪比算法频谱计算用的是FFT。上位机收到触发后的数据段后先做加窗处理我默认用Blackman-Harris窗它的旁瓣抑制比Hanning窗好很多适合做信噪比分析。窗函数的选择会影响频谱泄漏和幅度精度Blackman-Harris的幅度误差在1%以内够用了。FFT点数我设成8192点这是2的整数次幂计算效率最高。如果数据段长度不是8192就补零到8192。补零不会提高频率分辨率但能让频谱曲线更平滑。频率分辨率是采样率除以FFT点数250M/8192 30.5kHz。如果你需要更高的频率分辨率可以增加FFT点数但计算量会相应增加。信噪比的计算是这样的先找到信号主瓣把主瓣内的功率加起来作为信号功率。然后把除了主瓣和直流分量之外的功率加起来作为噪声功率。信噪比就是信号功率除以噪声功率取10log10。主瓣的宽度取决于窗函数Blackman-Harris窗的主瓣宽度是8个bin所以我从峰值bin左右各取4个bin作为主瓣。实测下来这套算法在输入100MHz正弦波、幅度-1dBFS时信噪比能测到70dB左右和AD9250数据手册标称的72dBFS很接近。差距主要来自前端电路的噪声和时钟抖动。如果你要测更高的信噪比前端和时钟都要用更好的器件。4.3 网络接收与数据拼帧的稳定性优化网络接收这块我踩了不少坑。最开始用同步阻塞的方式收数据结果界面卡死。后来改成异步接收用BeginReceive和回调函数界面就不卡了。但异步接收有个问题回调函数在IO线程上执行不能直接操作UI控件必须用Invoke转到UI线程。数据拼帧的逻辑是这样的每个UDP包带序号接收线程把包按序号放进一个字典。如果收到连续的包就按顺序拼成完整的数据段。如果中间有丢包就等待重传或者跳过。我设了一个超时时间超过100ms还没收到缺失的包就放弃等待把已有的数据拼起来显示。缓冲区管理也很重要。网络接收线程和UI显示线程共享一个环形缓冲区必须加锁保护。我用的是ReaderWriterLockSlim读的时候允许多个线程同时读写的时候独占。这样UI线程读数据时不会阻塞网络线程写数据反之亦然。注意C#的UDP接收缓冲区默认是8KB对于高速数据流来说太小了。我把它设成了1MB通过Socket.ReceiveBufferSize属性设置。这个值不能设太大否则会占用过多非分页内存一般1MB到4MB之间比较合适。5. 联调实测与常见问题排查5.1 从零到跑通的完整调试顺序调试这种项目最忌讳东一榔头西一棒子必须有明确的顺序。我的调试顺序是这样的先调电源再调时钟然后调ADC接口接着调FPGA内部逻辑再调网络传输最后调上位机。电源调试时用万用表和示波器测每一路电源的电压和纹波。模拟电源的纹波要小于1mVpp数字电源小于10mVpp。如果纹波超标先检查LDO的输入输出电容是否合适再检查负载电流是否超过LDO的最大输出。时钟调试时用频谱分析仪测时钟的相位噪声用示波器测时钟的峰峰值和上升时间。250MHz时钟的峰峰值应该在800mVpp左右上升时间小于200ps。如果时钟质量不好先检查时钟芯片的配置寄存器再检查时钟走线的阻抗匹配。ADC接口调试时先看JESD204B的SYNC信号能不能拉高。如果SYNC一直不拉高说明代码组同步没成功检查ADC的JESD204B配置和FPGA的IP核配置是否一致。SYNC拉高后再看ILA序列能不能对齐对齐了才能进入用户数据阶段。5.2 典型问题速查表现象可能原因排查方法解决办法JESD204B链路起不来SYSREF时序不对示波器看SYSREF和SYNC的时序调整SYSREF延迟确保在SYNC之前稳定采集波形有周期性尖峰时钟串扰频谱分析仪看时钟频谱时钟走线远离数字信号加屏蔽信噪比低于预期前端噪声大短路ADC输入看底噪换低噪声LDO优化前端布局网络丢包严重UDP缓冲区太小查看Socket统计信息增大ReceiveBufferSize触发位置不稳定触发阈值附近有噪声观察触发点附近的波形加迟滞窗口调整阈值波形显示卡顿刷新率太高查看CPU占用率降低刷新率优化降采样算法DDR3读写冲突仲裁优先级不合理用ChipScope看DDR3接口给ADC写入最高优先级频谱泄漏严重窗函数选择不当对比不同窗函数的频谱改用Blackman-Harris窗5.3 几个只有踩过才知道的坑第一个坑是JESD204B的SYSREF必须比SYNC早到至少一个时钟周期。我第一版把SYSREF和SYNC同时给结果链路时好时坏。后来在FPGA里把SYSREF延迟了两个时钟周期就稳定了。这个延迟值不是固定的跟你的时钟频率和走线长度有关需要实际调试确定。第二个坑是ADC的模拟输入不能悬空。悬空时输入端的共模电压会漂移导致ADC输出乱码。我一开始没注意调试时发现采集到的全是噪声查了半天才发现是输入端没接。后来在输入端加了50欧姆端接电阻到地问题就解决了。第三个坑是网络传输的字节序。FPGA是大端x86是小端如果上位机解析时没做字节序转换数据全是错的。我在协议里统一用大端上位机收到后手动转小端。这个坑很隐蔽因为数据看起来像是随机噪声不容易想到是字节序问题。第四个坑是FFT的频谱搬移。ADC采样后的频谱是周期延拓的0到250M的频谱里0到125M是正频率125M到250M是负频率。做频谱分析时要把负频率部分搬移到正频率旁边才能得到正确的双边谱。我一开始没做搬移看到的频谱是折叠的后来加了fftshift才正确。提示调试时一定要用已知信号做参考。我一般用信号发生器产生一个100MHz、-1dBFS的正弦波先确认采集到的波形频率和幅度都对再做后续的频谱和信噪比分析。如果连已知信号都测不对说明链路有问题不要急着调算法。6. 性能边界与扩展方向6.1 当前方案的性能天花板这套方案在当前配置下的性能边界是这样的采样率250MSPS分辨率14BIT双通道模拟带宽110MHz信噪比约70dB无杂散动态范围约85dBc。网络传输在千兆以太网下触发片段传输的等效吞吐约800Mbps连续传输的等效吞吐约200Mbps因为要做降采样。如果你需要更高的采样率比如500MSPS那ADC要换成AD9680之类的FPGA要换成Kintex-7或Zynq UltraScale因为JESD204B的lane速率会翻倍。网络也要升级到万兆以太网否则传输会成为瓶颈。如果你需要更高的分辨率比如16BIT那ADC要换成ADS54J60之类的前端电路的噪声要压得更低时钟抖动要更小。16BIT下信噪比的理论极限是98dB实际能做到85dB就很不错了。6.2 可以怎么扩展第一个扩展方向是加数字下变频。在FPGA里做DDC把高频信号搬到基带然后降采样再传给上位机。这样网络传输的数据率可以降低一个数量级同时保留信号的全部信息。DDC的实现可以用Xilinx的DDS Compiler加乘法器加CIC滤波器资源占用不大。第二个扩展方向是加自动增益控制。在前端加一个可变增益放大器FPGA根据信号幅度反馈控制增益让ADC始终工作在满量程附近。这样既能保证小信号的信噪比又能防止大信号削顶。AGC的环路带宽要设得合适太快会振荡太慢跟不上信号变化。第三个扩展方向是加数据存储。在FPGA里挂一块大容量NAND Flash或者SD卡把触发后的数据存下来事后再分析。这样就不受网络带宽限制了可以存原始250M采样率的数据。存储速度要够快NAND Flash的写入速度一般在100MB/s量级对应250M×14BIT×2通道的数据率是875MB/s需要多片并行才能跟上。第四个扩展方向是加实时频谱分析。在FPGA里做FFT把频谱结果传给上位机而不是传原始波形。这样网络传输的数据量可以降低几个数量级上位机也能做更长时间的频谱监测。FPGA里做FFT可以用Xilinx的FFT IP核8192点FFT在200MHz时钟下大概需要几百个时钟周期完全能满足实时性要求。6.3 一些个人经验这个项目我从画板子到跑通花了大概三个月其中调试JESD204B就花了将近一个月。最大的体会是高速ADC项目里硬件设计和FPGA逻辑是强耦合的不能分开调。硬件有问题FPGA逻辑再对也跑不起来FPGA逻辑有问题硬件再好也白搭。另一个体会是测试点一定要留够。我在板子上留了ADC时钟、SYSREF、SYNC、DDR3时钟的测试点调试时用示波器一测就知道问题在哪。如果没留测试点就只能盲猜效率极低。最后说一个关于上位机的小技巧频谱分析时不要每次都重新算FFT可以把上次的结果缓存起来只更新变化的部分。这样在连续触发模式下频谱显示的刷新率能提高不少。我用的是增量FFT的思路只对新的数据段做FFT然后和旧结果做滑动平均既平滑了频谱又降低了计算量。