最近在调一个10G以太网的UDP卸载引擎,接收端经常出现乱序,丢包率直接飙到30%,抓包看是IP分片和不同流之间的交织导致的。我目前只是简单按序号存到BRAM里,但重排序逻辑写得太粗暴,资源也吃紧。想请教各位大佬:在FPGA里做UDP包的乱序重排,一般用什么数据结构?是哈希表还是CAM?重排序窗口设多大比较合理?有没有工程上验证过的方案能兼顾吞吐和资源?
2026年了,FPGA工程师做以太网UDP卸载引擎,收到乱序包丢包率飙到30%,怎么用RTL解决重排序问题?
3 个回答

先别急着上CAM,10G口上CAM资源太奢侈了。我做过类似的东西,核心就一句话:按流ID分桶,桶内乱序深度一般不会超过4~8个包,分片重组的乱序深度另算。你用流ID哈希到8~16个桶,每个桶里放一个小的滑动窗口,窗口大小按最大乱序深度加2个余量设,比如16。窗口用有效位图做,每个slot有valid和seq,来了包先查窗口内有没有空洞,填上之后从窗口头开始连续吐出。BRAM不够就拆成分布式RAM,每个桶一小块,吞吐反而比一个大BRAM高,因为并行度上去了。你现在的30%丢包,大概率不是窗口不够,是回压没做好,窗口满的时候直接把新包丢了,应该在入口就按流做背压。你用的FPGA型号是什么?UltraScale+的话URAM其实也能当存储用。

这个问题我调过整整一个月,说点实在的。你抓包看到IP分片和不同流交织,其实要拆成两层看:第一层是流间交织,第二层是单流内的乱序。30%丢包如果发生在重排序逻辑之后,那问题多半出在你把不同流混在一个重排队列里了——A流和B流的包序号互相干扰,窗口永远在挪,结果谁都排不出来。正确做法是两级结构:第一级按流ID哈希分桶,每个桶独立维护重排状态机;第二级才是桶内的滑动窗口。这里有个关键细节,哈希碰撞要处理,否则两个流挤一个桶,乱序深度直接翻倍。我建议桶数取流数的1.5到2倍,碰撞时用链式或者直接丢给软件处理,别在硬件里做复杂冲突解决。窗口大小我一般设16,够覆盖绝大多数IP分片场景,TCP乱序另说。存储用分布式RAM,每个桶独立地址,BRAM留给大块缓存。资源吃紧的另一个坑是无效数据清理——窗口吐完一个包后,对应slot要立刻清valid,不然下次写进来要额外比较。你提到用BRAM简单按序号存,我猜你是把所有流的包放在同一个大数组里,只按全局序号排,那必然炸。还有个工程细节,回压要打在入口,而不是重排出口,否则窗口满了新包直接丢,丢包率降不下来。你现在的乱序深度实测过吗?抓包统计一下最大偏移量,窗口设成那个值加4就够,别盲目设大。

换个角度说,如果资源实在紧张,可以考虑不做完整重排,而是做「部分有序」交付。很多UDP应用其实只关心分片重组,不要求全局按序——比如视频流,帧内分片必须有序,但不同帧之间乱序无所谓。那你可以只对同一分片ID的包做重排,其他包直接透传,这样重排窗口只需要覆盖一个分片的最大长度,比如8KB/1500B约等于6个包,窗口设8就够了,资源省一半以上。代价是上层软件要自己处理帧间乱序,但大多数协议栈本来就有这个能力。另一个思路是软件辅助,FPGA只做哈希分桶和标记乱序深度,把重排工作丢给CPU跑DPDK,10G口上单核能处理几百万包每秒,重排这种状态机操作CPU做起来比RTL灵活得多。你如果是在校学生或者刚接手这个项目,我建议先确认一下需求文档里到底有没有「必须硬件全序」这条,很多时候只是验收标准写得太严,实际应用根本不需要。你现在的丢包率30%,是重排逻辑没生效还是入口就丢?如果入口有随机丢包,那重排做得再好也白搭,先查PHY和MAC的FIFO深度和反压信号。最后说一句,别迷信CAM,这东西在FPGA里要么用LUT实现占资源爆炸,要么用Xilinx的TCAM IP核,但那玩意儿价格和授权都够呛,分布式RAM加哈希桶是主流做法。你方便说下现在的数据通路架构吗?包进来是先存再排还是边存边排,这个决定重排逻辑能不能流水化。
发表回答
登录后可在本页底部提交回答