ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于KU115 FPGA的高速数据采集预处理PCIe载板设计

基于KU115 FPGA的高速数据采集预处理PCIe载板设计 做高速数据采集和处理这行这么多年我有个很深的体会瓶颈从来不在ADC采样率也不在CPU主频而卡在数据从传感器到主机的这条路上。手里拿着十路二十路的宽带ADC单路采样率动辄几百MSPS一算原始吞吐马上就是好几GB/s。这种时候最合适的中间位置恰恰是一张以KU115FPGA为核心的高性能数据预处理载板——它本质是一张PCIe信号处理板也是一张FPGA载板把各种子卡的原始数据收进来实时处理然后再通过PCIe送进主机。这篇文章就把我在这类板卡项目里踩过的路、算过的账、埋过的雷都写出来适合正在做FPGA数据采集系统、或者打算用PCIe上主机做实时处理的朋友参考。1. 为什么是KU115FPGA项目需求与选型思路1.1 这块载板到底要解决什么问题先说清楚这块板卡的定位。它不是一个独立的数据采集设备而是一个“中间层”前端接各种子卡比如高速ADC子卡、多通道同步采集子卡后端接主机。前端数据进来之后不能直接丢给CPU一是速率太高CPU扛不住二是数据需要做实时处理用软件做延迟不可控。所以要用FPGA在中间做数据预处理把有效信息提取出来、把数据量减下来再通过PCIe送到主机内存里。我遇到过最典型的场景是宽带信号采集。客户那边要求16通道、每通道250MSPS采样率、16bit分辨率这算下来原始数据率是多少16乘以250M再乘以2字节正好8GB/s。这个数字放在PCIe Gen3 x8接口上理论带宽7.88GB/s链路就已经快满了。如果后端主机还要实时显示、存储、分析根本来不及。所以载板必须把预处理做在前面比如数字下变频、抽取、滤波、FFT把数据率从8GB/s压到2GB/s以内剩下的量PCIe才扛得动主机软件才有喘息空间。1.2 芯片选型为什么落在KU115上选芯片是个平衡题。中端器件逻辑单元少、DSP Slice少、高速串行收发器数量不够接不了那么多路JESD204B高速ADC顶端器件资源过剩功耗和成本又吃不消。KU115这个位置正好卡在黄金档位上约145万逻辑单元、超过5000个DSP Slice、几十路最高16.3Gbps的串行收发器还有接近10MB的片上BRAM。这些资源做一个中等规模的多通道预处理系统绰绰有余又不会浪费太多功耗。更关键的是这代器件对PCIe Gen3和DDR4的支持非常成熟。PCIe集成块和配套IP在厂商工具链里直接可用出个x8端点IP很快DDR4控制器也不用自己写PHY例化IP配好参数就能跑。对我们做载板的人来说生态成熟比纸面参数更重要——官方的例程、驱动参考、调试手段都齐全遇到问题能找到资料这能省下大量时间。1.3 为什么不用处理器方案有人会问这种数据预处理为什么不用带ARM的Zynq SoC或者直接在主机端加一块GPU先说Zynq它的FPGA资源和KU115不在一个量级上而且ARM处理高速数据流时一样要软件介入延迟和吞吐很难同时满足。GPU的优势是并行计算但数据要从PCIe进到GPU显存再算来回拷贝的开销很大对于流式数据来说并不划算。纯FPGA方案的优势在于全硬件流水线数据从子卡进来经过预处理、缓存、DMA一路到主机全程不需要CPU参与数据搬运延迟是确定性的带宽是可预测的。KU115所在的载板还承担了一个重要角色——通过FMC接口兼容多种子卡。换一张ADC子卡只要逻辑端的接口协议对上整块载板就能复用。这意味着做一次载板设计可以覆盖多个不同采样率、不同通道数的项目边际成本很低。这一点在做产品线规划时非常加分。2. 硬件设计的关键环节电源、时钟与信号完整性2.1 供电与上电时序最容易翻车的部分FPGA载板的供电复杂度往往被低估。KU115需要的电压轨很多核心供电VCCINT一般0.85V左右还有VCCBRAM、VCCAUX、各bank的VCCO以及高速收发器的AVCC和AVTT。电流方面VCCINT一路动辄几十安培这在板卡上不是随便一个LDO能搞定的需要多相Buck电源或者高集成度电源模块。我特别想强调上电时序。很多第一次做FPGA载板的朋友觉得时序无所谓“反正都通电就行”结果板子偶尔起不来、IO电流倒灌、芯片异常发热排查半天发现是时序问题。VCCINT、VCCBRAM、VCCAUX、VCCO的先后关系必须按芯片手册要求来一般建议用电源监控芯片加CPLD做顺序上电每一路电压都检测OK之后再释放下一路。压摆率也要控制斜坡太陡容易引起过冲太缓又可能让FPGA处于不确定状态。最后还有一个细节把FPGA的配置引脚和电源状态关联起来。等所有供电电压都稳定后再释放PROG_B引脚进入配置流程。我见过有人忽略这一步FPGA在电压还没稳定时就开始加载bitstream结果配置进一半失败系统看起来完全没反应。2.2 高速接口布局与参考时钟信号完整性不能靠运气这块板卡上跑着两类高速信号PCIe x8差分对和前端子卡的高速串行数据。PCIe Gen3的信号速率是8GT/sJESD204B的高速ADC链路通常也跑到10Gbps以上。这种速率下PCB走线已经不是普通连线了那就是传输线。差分阻抗要控制在100欧姆层叠设计要保证有完整的参考平面过孔要尽量少换层时最好有伴地过孔。层叠我建议至少12层TOP层放器件和高速信号中间要有专门的电源层和地层底层放低速信号和测试点。高速信号换层的地方过孔的stub效应会严重影响眼图必要时要考虑背钻。DDR4走线则要按组做等长数据线、地址线、控制线分别管理VTT端接电阻离末端颗粒越近越好。参考时钟是另一个重点。PCIe的100MHz参考时钟最好从金手指引入也可以通过板载晶振切换但要保证时钟源的质量抖动直接决定链路能不能训练到Gen3。JESD204B这边更讲究Device Clock必须和ADC的采样时钟同源SYSREF要满足建立保持时间否则确定性延迟就是一句空话。这部分我会在后面的逻辑设计中再展开。2.3 DDR4缓存为什么要有、怎么实现才算合格有朋友问纯流式数据通路为什么还要挂DDR4答案很简单缓冲。前端多路数据到达的瞬间是突发性的但PCIe DMA搬运是分时共享的如果没有一个蓄水池做平滑数据来了搬不走就直接丢包。DDR4在这里扮演的就是蓄水池同时还承担多通道数据汇聚、乒乓缓存、预处理中间结果暂存这些任务。常见配置是64bit位宽、总容量2GB到4GB带ECC。以DDR4-2400为例64bit位宽算下来理论带宽大约19.2GB/s远大于PCIe Gen3 x8的7.88GB/s所以缓存本身不会成为瓶颈。PCB上DDR4部分要重点关注数据线组内等长、地址命令线与数据线的间距、电源去耦。FPGA侧例化DDR4控制器IP时要仔细配时序参数特别是tCK、CL、CWL这些配错了内存初始化直接失败仿真和硬件表现都对不上。DDR4部分我有个习惯把控制器的工作频率和用户逻辑频率解耦中间用异步FIFO跨时钟域。这样后续调试时改逻辑频率不会牵扯到DDR4控制器重配省了很多事。3. FPGA逻辑该怎么搭数据通路的完整设计3.1 整体数据流从ADC到DDR再到PCIe逻辑设计的第一件事不是写代码而是画数据流图。拿我们做的那版方案来说完整链路是前端子卡的高速串行数据进FPGA经过JESD204B RX模块解包进入预处理流水线预处理结果写入DDR4缓存缓存侧的数据再被PCIe DMA引擎读出经PCIe链路送到主机内存。整个链路上每一级都要算带宽账。比如JESD204B RX进来的数据是8GB/s预处理模块做完下变频加抽取输出降到2GB/s写入DDR4的带宽需求就是2GB/sDMA读出的时候又是2GB/s这两个带宽和DDR4控制器总带宽19.2GB/s放在一起占用量很少不会互相打架。带宽账算平了系统设计才站得住脚。我见过不少方案逻辑写得挺炫带宽一算全是瓶颈板上跑起来数据就丢这种问题从架构上就错了。数据通路里的每个模块之间我统一用AXI4-Stream接口。这个接口标准、调试方便、官方IP全都支持自己写模块也容易对接。流接口的ready/valid握手必须处理好特别是ready信号不能随便拉高背压一旦失效FIFO溢出丢数据只是时间问题。3.2 数据预处理模块不只是搬数据“预处理”这三个字在不同项目里含义差别很大。我们这一版主要做了四件事多通道同步校正、直流偏置消除、数字下变频和抽取滤波。多通道同步校正很关键每路ADC的通道延迟都有细微差别不做对齐后续做波束成型或者相关分析时数据就是乱的。数字下变频和抽取滤波的目的很单纯——降数据率。宽带信号搬移到基带之后用CIC加FIR的级联结构抽取既省资源又能把带外噪声滤掉。在FPGA里做这些处理的好处是流水线式的数据进来多少就处理多少延迟只有几百纳秒到几微秒软件完全做不到这个量级。KU115的5000多个DSP Slice在这种场景下非常够用CIC滤波器几乎不占DSPFIR系数多了才用DSP切片一般工程做完资源占有率大概在60%到70%。预处理参数怎么配我建议通过PCIe映射寄存器来做在线配置比如抽取倍数、滤波系数、通道使能位主机端随时可调。这样同一块板卡在不同应用场景下只需要改软件参数就能适配不用重新综合bitstream现场调试效率会高很多。3.3 时钟域与复位处理逻辑设计里最阴间的部分多通道高速数据采集系统里时钟域多得让人头大ADC采样时钟域、JESD204B恢复出的时钟域、DDR4控制器时钟域、PCIe用户时钟域加上板载的全局用户时钟。跨时钟域处理不规范系统跑着跑着偶尔出一次错这种故障最难查。跨时钟域的标准做法是异步FIFO。写入侧用自己的时钟读出侧用对方的时钟格雷码指针保证安全。要注意异步FIFO的深度不能拍脑袋定得按最坏情况下的数据突发长度来算。举个例子DMA引擎发起读请求后DDR4的响应延迟可能是几百个周期这期间进来的数据如果FIFO装不下就只能是丢包。所以FIFO深度宁大勿小。复位设计也值得单独说。我见很多人图省事用全局复位按键直接异步复位所有模块结果复位释放时各模块状态不确定系统行为诡异。正确做法是每级模块做同步复位释放让所有触发器在同一个时钟沿退出复位状态。PCIe热复位、软件复位、看门狗复位要分清优先级软件复位不能把PCIe链路也带崩了。调试时在复位逻辑上加一个状态指示寄存器主机侧能读到每个模块的复位状态会省很多时间。4. 软件与驱动让载板在主机上真正跑起来4.1 Linux下PCIe驱动的落地路径硬件不出活软件都是空的。这块载板的PCIe驱动我的建议是不要从零写优先在官方DMA IP提供的驱动框架上改。框架里面已经处理好了BAR空间映射、MSI中断注册、DMA描述符队列这些基础功能我们要做的主要是两件事把厂商ID和设备ID改成自己板卡的值然后把DMA中断处理函数里跟具体业务相关的部分换成自己逻辑对应的事件。驱动改好之后怎么验证枚举插上板卡开机先用lspci看一下设备有没有被识别。如果设备出现但链路速率不对比如应该Gen3却显示Gen1优先查链路训练问题这个后面会说。设备枚举OK之后最简单的验证是读写BAR空间寄存器用devmem这种工具就可以# 查看当前PCIe设备信息 lspci -vvv -s 03:00.0 # 读BAR0映射后地址的第一个寄存器实际地址以lspci为准 devmem 0xfb000000 32能读到芯片版本号或板卡ID寄存器说明PCIe链路、BAR空间映射、寄存器读写通路全部正常这时候再往下做DMA中断和批量传输。4.2 主机内存与DMA缓冲管理PCIe DMA要把数据搬到主机内存这块内存必须满足两个条件物理地址连续、不会被内核换页。最省事的方案是模块加载时用dma_alloc_coherent分配一致内存但数量有限适合小缓冲。大数据量场景下我更喜欢用巨页内存。预留几GB的巨页空间再在驱动里把巨页的物理地址做成散列表DMA描述符可以指向这些地址。这里有个很容易踩的坑IOMMU。如果BIOS和内核开了IOMMUPCIe设备访问的地址是经过映射的IOVA不是物理地址。驱动里如果直接拿物理地址填DMA描述符数据就会跑到完全错误的地方表现为主机内存随机被破坏甚至直接崩溃。解决的办法是要么用内核DMA API做地址转换要么干脆在测试时先关掉IOMMU把通路先跑通。DMA中断也建议用MSI而不是传统INTx。MSI对每个设备有独立中断向量处理效率高而且PCIe Gen3设备支持多个MSI向量可以做到不同DMA队列用不同中断号。做多队列DMA时这个特性特别有用。4.3 联调流程与工具从板卡上电到整机联调我习惯按这个顺序来先测寄存器和中断再测小包DMA最后跑长时间压力。每步都要有明确通过标准不要跳步。寄存器自检阶段读写几个预定值寄存器确认INTx/MSI中断能产生并被驱动捕获。小包DMA阶段FPGA侧构造已知规律的数据块比如递增数主机收到后校验错一个字节都不放行。这阶段能排除大部分描述符地址错乱、长度错配问题。压力测试阶段要跑至少24小时数据率从低到高逐步加码。我还会写一个简单的统计工具同时读FPGA侧和主机侧的字节计数寄存器两边一对比就知道数据在哪个环节丢了。丢数不一定在PCIe上DDR4溢出、前端接口误码、FIFO背压都可能造成丢数能定位到具体模块就成功了一半。5. 性能实测、踩坑记录与调试心得5.1 实测性能数据与分析这版载板调试稳定后我们做了完整的性能摸底。PCIe Gen3 x8连续DMA读方向实测稳定在6.5GB/s左右写方向大概6.0GB/s主机CPU占用在单核范围内。这个数字离理论值7.88GB/s有差距但符合预期——协议开销、描述符更新、DDR4读写切换都有代价能跑到理论值的80%以上已经算健康。DDR4缓存侧的实测带宽更宽裕64bit DDR4-2400跑下来约16GB/s有效带宽在压缩后的数据流量面前完全够用。整机跑满16通道原始8GB/s输入的场景时前端JESD204B链路稳定无误码预处理后数据流2GB/s持续搬运24小时主机端校验零丢数。这个结果说明整个链路带宽账是平的每个环节都没有隐藏瓶颈。5.2 三大典型问题实录我必须把这个环节写细一点因为这类板卡项目里真正烧时间的永远是排查问题不是写逻辑。第一个经典问题上电后PCIe枚举不到或者只能训练到Gen1速率。排查顺序是先看参考时钟100MHz时钟有没有起来、幅值够不够再看复位PERST信号是否被正确释放接着检查电源每个电压轨是否都在正常范围内最后看金手指接触。我们遇到过一次最诡异的情况——板卡在台式机上跑Gen3正常换到某台服务器上只能跑Gen1查了一圈发现是服务器槽位的PCIe参考时钟走线质量不佳加了一个时钟缓冲器才解决。第二个经典问题DMA长时间跑数据后主机侧崩溃。这种问题十有八九是描述符越界或者内存地址错乱。我们的排查手段是在驱动里打开内核调试把DMA地址和长度打印出来和FPGA侧配置的描述符内容做比对。结果发现是某个多队列场景下描述符环形缓冲区指针没做回卷处理写到尾部就溢出。改了指针逻辑之后问题消失。第三个经典问题JESD204B多通道数据错位。现象是单路正确、多路同时采集时通道顺序乱序。原因基本都出在SYSREF的采样时刻不对确定性延迟没有建立起来。排查方法是在逻辑里加一个通道对齐状态机SYSREF来之后拉高SYNC等待各通道FIFO都到达预定位置再释放数据。这个问题表面看是逻辑问题实际和硬件上SYSREF布线等长有很大关系。5.3 常见问题速查表把调试中遇到的问题整理成一张速查表方便后来者对照现象可能原因排查手段PCIe枚举不到参考时钟未起、PERST时序不对、电源轨异常示波器查时钟和复位测量各电压轨链路速率只有Gen1参考时钟质量差、金手指接触不良、PCIe AC耦合电容问题检查100MHz时钟抖动重新插拔转接卡寄存器读写超时BAR空间映射错误、IOMMU地址转换错误lspci查看BAR地址核对驱动映射逻辑DMA传输数据乱码描述符地址错乱、长度字段配错打开调试打印比对FPGA侧与主机侧描述符长时间跑数据丢包FIFO溢出、DDR背压、DMA缓冲不足对比两侧字节计数定位丢包模块JESD204B多通道错位SYSREF时序未达标、通道延迟不一致检查SYSREF等长设计调整对齐状态机偶发性系统死机供电毛刺、复位信号干扰加宽电源余量检查复位监控电路调试工具方面FPGA工具自带的逻辑分析仪是查内部状态的第一利器。我会在关键模块预留调试探针信号比如FIFO水位、DMA计数、链路状态出问题时先把这些信号抓下来比瞎猜快得多。板卡上一定要留够测试点并且建议引导模式配成双备份——一片SPI Flash存放出厂bitstream一片用于日常调试万一调试配置写坏了还能自动回退。还有一个小技巧FPGA里的收发器链路质量可以用工具自带的IBERT测一下眼图和误码率高速链路不稳时先做这一步能直接判断问题在PCB走线还是逻辑配置省去大量绕弯时间。这个测试在调试前做一遍建立链路基线后续任何改动如果影响了链路一对比就知道。做这类FPGA载板项目越到后面我越觉得真正决定成败的往往不是某个大模块有多复杂而是这些细节电源时序、参考时钟、复位释放、描述符管理……每一项单看都不难串起来就容易翻车。前期多花点时间做接口梳理和带宽计算比急着焊板子有意义得多。至少对下一块板卡我们已经把这些经验变成了现成的设计规范和检查清单这就是项目结束之后最值得保留的资产。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进