收udp 会校验mac地址吗_UDP/IP硬件协议栈设计(七):UDP

b241e2068944be9d1a0bde1e7003313a.png

前文完成了ICMP的回显请求处理,已经实现可以ping通板子的功能了,接下来就是传递数据了,无非就是采用UDP协议将一段数据从一台主机传输到另一台主机,也就是“搬砖”。

6072a771915ab0d589bf0f0bb7923950.png
搬砖

前文的缘起和分类两章中简单介绍了UDP,所以本文只对其设计进行介绍。

因为本协议栈只要是为了UDP的数据传输,所以分别就其的接收端和发送端进行介绍,为方便起见,笔者用Visio画了张简图加以描述:

9c1b2b45de511b758d92f4ce8f939f94.png
UDP发送端和接收端的处理结构

接收端

前文已经获得了UDP数据部分和校验信息,接收端的功能就是将正确的UDP数据传给用户使用,所以笔者在设计中如下:

根据前文分类时得到的UDP数据和状态信息(FCS、Checksum等校验)进行处理,如果校验均正确,并且也是UDP数据包,则根据设计方案加上自定义的帧头(源IP地址、源端口号、目的端口号、帧长)存入到FIFO当中进行缓存,其中该缓冲区可以进行配置深度,然后传输到用户侧使用。

当然如果校验等信息有误,则当场将数据丢掉处理。

RTL实现可如下所示:

    //移位寄存
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            udp_data_valid_r            <=  1'h0;
            udp_data_shift              <=  96'h0;
            udp_data_valid_edge_shift   <=  16'h0;
            udp_data_valid_shift        <=  16'h0;
        end
        else begin
            udp_data_valid_r            <=  udp_data_valid;
            udp_data_shift              <=  {udp_data_shift[87:0],udp_data};
            udp_data_valid_edge_shift   <=  {udp_data_valid_edge_shift[14:0],udp_data_valid&(~udp_data_valid_r)};
            udp_data_valid_shift        <=  {udp_data_valid_shift[14:0],udp_data_valid};
        end
    end

    //---current src_ip/src_port/dst_port/udp_length
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            udp_custom_header_shift     <=  80'h0;
        end
        else if (udp_data_valid && (!udp_data_valid_r)) begin
            udp_custom_header_shift     <=  {current_frame_src_ip,current_frame_src_port,current_frame_dst_port,current_frame_length};
        end
        else begin
            udp_custom_header_shift     <=  {udp_custom_header_shift[71:0],8'h0};
        end
    end
    //加上自定义帧头{源IP、源端口、目的端口、帧长}
    assign udp_custom_header_valid  =   |udp_data_valid_edge_shift[9:0];
    assign udp_custom_header        =   udp_custom_header_valid?{1'b0,udp_custom_header_shift[79:72]}:9'h0;

    //----UDP 纯数据
    assign udp_pure_data_valid      =   udp_data_valid_shift[10];
    assign udp_pure_data            =   udp_pure_data_valid?{udp_pure_data_last,udp_data_shift[87:80]}:9'h0;
    assign udp_pure_data_last       =   (~udp_data_valid_shift[9]) & udp_data_valid_shift[10];

    //----UDP 自定义数据
    assign udp_custom_data_valid    =   udp_custom_header_valid | udp_pure_data_valid;
    assign udp_custom_data          =   udp_custom_header | udp_pure_data;

发送端

发送端处理与接收端处理机制相反,是用户想将自己的应用层数据(比如采集的数据等)通过该协议栈发送出去给其他网络设备,而前面已经订好了自定义的帧头,即发送端获得的数据已经知道该数据是发给哪个IP、发给哪个端口、发多长的了。

那发送端要做的事情,就是:发!

那问题又来了,又不能直接将纯UDP数据发出去,还得加上UDP头、IP头、MAC头才行,那似乎这个发送端逻辑不用写也行,直接写入缓存FIFO当中,然后送给封包模块封装成以太网帧发送不就好了。

但是,UDP头里有个checksum字段,如果直接丢给封包模块,那还的重新计算一遍,为降低延迟,所以笔者决定在收到用户侧需要发送的数据的时候就进行累加计算,这样封包的时候,将这个累加值加上其他几个UDP头的字段并取反,就可以作为UDP头的Checksum了。

这里UDP纯数据部分的累加计算RTL如下:

    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            udp_data_valid_r    <=  1'b0;
            udp_data_r          <=  8'h0;
        end
        else begin
            udp_data_valid_r    <=  udp_axis_tx_valid_i && udp_axis_tx_ready_o;
            udp_data_r          <=  udp_axis_tx_data_i;  //寄存
        end
    end

    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            udp_data_calc_en    <=  1'b0;
        end
        else if (udp_axis_tx_valid_i && udp_axis_tx_ready_o) begin
            udp_data_calc_en    <=  ~udp_data_calc_en;  //高8bit计算使能
        end
        else if (udp_tx_status_fifo_wr_en) begin
            udp_data_calc_en    <=  1'b0;
        end
    end

    assign udp_data_calc_en_n   =   ~udp_data_calc_en;  //低8bit计算使能

    //计算得到32bit的UDP纯数据的累加值
    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            udp_data_checksum_32    <=  32'h0;
        end
        else if (udp_data_calc_valid && udp_data_calc_en && udp_data_valid_r) begin
            udp_data_checksum_32    <=  udp_data_checksum_32 + {udp_data_r,8'h0};
        end
        else if (udp_data_calc_valid && udp_data_calc_en_n && udp_data_valid_r) begin
            udp_data_checksum_32    <=  udp_data_checksum_32 + {8'h0,udp_data_r};
        end
        else if (udp_tx_status_fifo_wr_en) begin
            udp_data_checksum_32    <=  32'h0;
        end
    end

然后用户需要发送的数据存入缓冲FIFO当中,此外将以上计算得到的累加值、目的IP、目的端口、源端口等信息存入状态信息FIFO当中,供后级封包发送使用。

AXI-Stream接口协议

UDP处理模块的对用户侧的接口采用AXIS接口协议,这里不再赘述AXIS协议的传输了,可以参考下面知乎文章:

ljgibbs:深入 AXI4总线(C1)旧版存档:AXI4 的兄弟协议​zhuanlan.zhihu.com
ad48cdc9c3eed46f42eab52eaa167e3b.png

笔者只利用FIFO的接口简单转成AXI-Stream接口,即UDP接收缓冲FIFO的输出接口转为对用户侧的AXIS_Rx接口,若用户侧处理不过来可暂存于FIFO当中,同样,即AXI_Tx接口转为UDP的发送缓冲区FIFO的输入接口,当UDP发送阻塞时,用户待发送的数据也可暂存于FIFO中,避免数据丢失。


以上即UDP处理模块所有内容,当然实现的比较简单,并未做特定端口号的过滤提取,反正接收到的UDP数据采用自定义帧格式,帧头包含了源IP、源端口、目的端口,直接供用户侧处理就好了。

如有不对之处,还望批评指正~


十二点过九分:UDP/IP硬件协议栈设计(八):封包​zhuanlan.zhihu.com
e6256aabae133e7182da0e220a7b7e68.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值