IP层之分片包的整合处理---BUG修复

发布于:2025-03-17 ⋅ 阅读:(18) ⋅ 点赞:(0)

在之前章节中,笔者就IP层之分片包的整合处理进行了概念介绍,以及代码编写和仿真,在整体代码调试环节,笔者发现了一个问题,在本文中,笔者将就这个BUG进行说明,以及进行修复,讲解代码实现思路,验证代码逻辑正确性
在这里插入图片描述
注意看图片中的框选文字,这里说明,如何确定这是一个单帧包,这里的确定单帧包逻辑是不存在问题,但是直接将UDP报文输出是存在问题的,因为即使确定了当前输入进来的数据报文不是分片包,但该报文的的上一帧报文,可能是分片包,而由于分片包要进行巨型帧组合后输出,其报文长度可能为9000字节以上(假设),那么当前帧报文传输来时,巨型帧报文可能尚未传输完成,此时的输出数据总线是被巨型帧报文占用的。而根据输出赋值的优先级,会出现,巨型帧输出不完全,被新一帧数据报文顶替输出,以及当前报文优先级低,无法获得总线使用权,报文丢失,所谓的优先级,即是在一个时序控制逻辑内,else if的上下级,当两个else if语句满足时,总是先执行最上面的else if语句,单纯的文字解释这个问题,可能会给读者的理解造成混乱,笔者接下来就流程示意框图、代码仿真波形两方面展现这类情况,使得读者更加清晰的理解这些逻辑

在这里插入图片描述
上图所示,便是一种情况,在RAM中整合了三包分片数据包之后,进行数据帧输出,此时接连来了两包单包
此时,这两个单包按照之前的逻辑是会顶替当前包输出的,造成数据混乱,代码仿真如下图所示
在这里插入图片描述
在这里插入图片描述
图片一模拟了流程图中的情况,而图片二中则显示了这样的数据流动出现的问题,接下来将就这种现象进行解决方案的思考。
显然,为了避免这种情况,需要判断此时的数据输出总线是否被占用,如果数据输出总线被占用,则应该缓存本帧数据,待到数据输出总线空闲,再进行数据的读取,同时,若是数据到来时,数据输出总线是空闲的,则不需要缓存数据,直接进行输出,减少不必要的时间浪费。
那么还有一个问题,数据输出总线被占用期间,可能有多帧数据到来,都需要被缓存,那么数据长度同样应该被缓存,在本处理模块中不涉及数据类型,因为其下一级在本设计中一定属于UDP报文,所以不考虑数据类型的缓存,当涉及TCP/UDP两种报文时,会就IP_RX模块中的输出信号进行添加,这在之后讲解的TCP协议栈实现中,会进行具体介绍,大家可以点个关注,后续会有更多文章分享。
对于这些单帧数据包,使用FIFO进行数据缓存,使得逻辑处理简单,同时还需要注意一个问题,每次读取指定长度后,还需要暂缓下一帧数据的输出,避免背靠背传输,对数据总线处理的压力。

代码的主要难点是以下几方面

  1. 缓存分片数据包,并在缓存完成后进行输出
  2. 在分片数据包输出过程中,多个单包数据包到来,缓存至FIFO
  3. 在单包数据包输出过程中,再次到来单包数据包(因为分片包导致的数据堆积)缓存至FIFO
  4. 两个单包从FIFO中读出时,输出间隔问题,保证输出间隔大于10个周期,减轻后续处理压力,以及背靠背传输导致数据包输出长度计算错误问题
  5. 评定各种情况优先级,合理规划缓存以及读取
  6. 针对以上难点,应该多进行代码仿真,找出时序存在问题中,进行对应修改,多仿真是写出好代码的关键

整体仿真测试时序:

  1. 首先发送3472字节的巨型帧,分三次发送1480–1480–512
  2. 之后发送512字节单包,多次128字节单包–在巨型帧尚未缓存完毕以及巨型帧尚未输出完成
  3. 之后发送128字节单包–在输出总线输出单包数据时
  4. 最后发送128字节单包-在输出总线空闲时候
initial begin
    #200
    /*ip frag*/
    @(posedge i_udp_clk)
    ip_frag_send(0,'d1480,1,0);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d1480,1,185);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d512,0,370);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d512,0,0);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d128,0,0);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d128,0,0);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d128,0,0);
    #400
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d128,0,0);
    #20000
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d128,0,0);
    #20000
    @(posedge i_udp_clk)
    ip_frag_send('d0,'d128,0,0);
end

情况一:对应仿真情况如下,可以看出分片包成功组合,数据长度3472字节,输出时序正确。

在这里插入图片描述
情况二:对应仿真情况如下,可以看出在巨型帧缓存输出完成后,被缓存的单帧数据包成功进行输出。

在这里插入图片描述

情况三:对应仿真情况如下,可以看出在输出单包数据完成后,在单包数据输出期间被缓存的单帧数据包成功进行输出。

在这里插入图片描述
情况四:对应仿真情况如下,可以看出在总线空闲期间到的数据包,被直接输出,不需经过缓存。

在这里插入图片描述
经过上述仿真模拟各种可能出现的情况,经验证后,输出时序正确,不会出现数据错误、覆盖、丢失等现象。
关于本次代码的实现、调试思路。大家有什么问题欢迎在评论区中进行讨论,有哪些考虑不到的地方,也请大家批评指正,可以关注下作者,后续会进行更多技术文章分享。
在下一章节中,笔者会将之前介绍的模块进行整合,组合成最终的UDP协议栈,当然,还不包括PHY层的处理,关于PHY层的处理,笔者也会在之后的章节进行介绍,以及UDP的上层及应用层协议,如IEEE1588,笔者也会进行介绍。