前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >千兆以太网(2):接收——包校验和数据筛选

千兆以太网(2):接收——包校验和数据筛选

作者头像
FPGA开源工作室
发布2020-04-16 17:32:36
1.1K0
发布2020-04-16 17:32:36
举报
文章被收录于专栏:FPGA开源工作室

前面我们实现了FPGA板卡接收以太网的数据,但是里面的数据比较乱,而且可能出现无效帧,即便是有效帧,也不是所有数据都是我们要的,必须对数据进行筛选。本篇博客详细记录一下以太网数据的校验和筛选。

一、数据发送

  我们用 Matlab 软件实现电脑向以太网发送数据,FPGA板卡接收数据。具体程序网上很多,就不贴了。

二、数据的校验和筛选

1、UDP以太网结构

  以太网的发送以包为单位,每个包的结构如下图所示。图中有帧首部、MAC首部、IP首部、UDP首部、用户数据、帧尾部等。后面我们将利用这些数据进行校验和筛选出我们需要的值。

2、包有效校验

  如上图蓝色部分即是我们的包有效校验区,包发送数据过来,而刚好蓝色位置的 5byte(40bit)数据和标准值一样,那么就可以认定该包为有效包。

代码语言:javascript
复制
always @(posedge sclk) begin
    if(rst) begin
        pkg_vld_value <= 40'd0;
    end
    else if(rx_en_cnt==31 || (rx_en_cnt>=42 && rx_en_cnt<=45)) begin //udp、port源、port目的
        pkg_vld_value <= {pkg_vld_value[31:0],rx_data};
    end
end

always @(posedge sclk) begin
    if(rst) begin
        pkg_vld <= 1'b0;
    end
    else if(rx_en_fall && pkg_vld_value==40'h11_04d2_007b) begin
        pkg_vld <= 1'b1;
    end
    else begin
        pkg_vld <= 1'b0;
    end
end

3、CRC校验

  CRC校验是为了证明一个数据包是否出错,多数为 8 位或 32 位,本次采用 32 位的CRC校验,但是记住,只能用于验证数据是否出错,而不能对错误进行纠正。此外 CRC 校验是去掉了帧首部的 8 byte 数据后的校验。如下所示是32位的CRC校验模块,注意校验和解校验的电路默认初始状态都是 32‘hffffffff,即全1状态。千兆以太网的解校验结果为 32’hc704dd7b。可以用例化的方式对该模块进行使用,而实际 CRC 校验的科学原理则略微高深,此处不做讲解。

代码语言:javascript
复制
代码语言:javascript
复制
always @(posedge sclk) begin
    if(rst) begin
        crc32_value <= 32'hFFFFFFFF;
    end
    else if(crc_en) begin
        crc32_value[ 0] <= c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[ 1] <= c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[ 2] <= c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[ 3] <= c[27]^d[ 4]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];
        crc32_value[ 4] <= c[28]^d[ 3]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[ 5] <= c[29]^d[ 2]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^c[31]^d[ 0]^d[ 6]^c[24]^c[30]^d[1]^d[7];
        crc32_value[ 6] <= c[30]^d[ 1]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];
        crc32_value[ 7] <= c[31]^d[ 0]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^d[ 7];
        crc32_value[ 8] <= c[ 0]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^d[ 6]^c[24]^d[ 7];
        crc32_value[ 9] <= c[ 1]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^d[ 6];
        crc32_value[10] <= c[ 2]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[24]^d[ 7];
        crc32_value[11] <= c[ 3]^c[28]^d[ 3]^c[27]^d[ 4]^c[25]^d[ 6]^c[24]^d[ 7];
        crc32_value[12] <= c[ 4]^c[29]^d[ 2]^c[28]^d[ 3]^c[26]^d[ 5]^c[25]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[13] <= c[ 5]^c[30]^d[ 1]^c[29]^d[ 2]^c[27]^d[ 4]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];
        crc32_value[14] <= c[ 6]^c[31]^d[ 0]^c[30]^d[ 1]^c[28]^d[ 3]^c[27]^d[ 4]^c[26]^d[5];
        crc32_value[15] <= c[ 7]^c[31]^d[ 0]^c[29]^d[ 2]^c[28]^d[ 3]^c[27]^d[ 4];
        crc32_value[16] <= c[ 8]^c[29]^d[ 2]^c[28]^d[ 3]^c[24]^d[ 7];
        crc32_value[17] <= c[ 9]^c[30]^d[ 1]^c[29]^d[ 2]^c[25]^d[ 6];
        crc32_value[18] <= c[10]^c[31]^d[ 0]^c[30]^d[ 1]^c[26]^d[ 5];
        crc32_value[19] <= c[11]^c[31]^d[ 0]^c[27]^d[ 4];
        crc32_value[20] <= c[12]^c[28]^d[ 3];
        crc32_value[21] <= c[13]^c[29]^d[ 2];
        crc32_value[22] <= c[14]^c[24]^d[ 7];
        crc32_value[23] <= c[15]^c[25]^d[ 6]^c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[24] <= c[16]^c[26]^d[ 5]^c[25]^c[31]^d[ 0]^d[ 6];
        crc32_value[25] <= c[17]^c[27]^d[ 4]^c[26]^d[ 5];
        crc32_value[26] <= c[18]^c[28]^d[ 3]^c[27]^d[ 4]^c[24]^c[30]^d[ 1]^d[ 7];
        crc32_value[27] <= c[19]^c[29]^d[ 2]^c[28]^d[ 3]^c[25]^c[31]^d[ 0]^d[ 6];
        crc32_value[28] <= c[20]^c[30]^d[ 1]^c[29]^d[ 2]^c[26]^d[ 5];
        crc32_value[29] <= c[21]^c[31]^d[ 0]^c[30]^d[ 1]^c[27]^d[ 4];
        crc32_value[30] <= c[22]^c[31]^d[ 0]^c[28]^d[ 3];
        crc32_value[31] <= c[23]^c[29]^d[ 2];
    end
    else if(!crc_en) begin
        crc32_value <= 32'hFFFFFFFF;
    end
end
代码语言:javascript
复制

4、数据的筛选

  由上图可知,只有中间红色部分的 “用户数据”才是我们真正需要的,因此最终输出结果还需要进行一番筛选,去头去尾即可,这也没什么难的。

三、代码设计

  上面已经贴了部分代码了,难道这里我要全贴代码吗?不,贴代码没有意义,重要的是懂内部的含义。我们以 rx_filter 来命名此模块,rx_filter 的输入是网口数据经过前一讲中转换后的 8bit 数据值和对应使能,输出则是经过校验和筛选后的数据和使能。

  上面说的 包有效校验 CRC校验,代码本身都不难,用个计数器数进来的数据使能,然后对那些关键节点进行比较即可。但是那头数据边来这头又要边校验,很可能时序出错,因此有必要建立一个 data_fifo 先缓存住过来的数据,然后进行包有效校验和 CRC 校验。data_fifo 的深度可以深一点,例如8192,这样就能容纳多帧了。校验完了后是需要判断是否丢弃该包的,因此还需要另一个 status_fifo 对校验信息进行存储,同时只要 status_fifo 由空变成不空了,说明该包校验信息写入了,即该包校验结束,那就直接设计 status_fifo 的读使能让信息数据出来,并用一个寄存器锁存住后面用。status_fifo 的读使能有了后,data_fifo 的读使能也要立马设计出来,免得新来的各种包不断进入 data_fifo,那不得撑爆了。注意 data_fifo 的读使能持续时间应该和进来时的数据使能一样长,所以前面计数时不能光计数了,还得把一个完整包的长度存起来。怎么存?直接将长度寄存住后 和 包有效校验、CRC校验拼接一起写给 status_fifo 就行了,后面 status_fifo 读出来这些数据就能为我们所用了。现在 data_fifo 不断的写数据读数据,status_fifo 也不断的写信息数据读信息数据,下一步我们就能利用读出来的信息数据,判断里面的的信息(即包有效校验和CRC校验)是不是真的OK,如果是真的OK,那就将 data_fifo 的读数据和读使能进行数据剔除,留下中间“用户数据”部分再传出去,如果不OK就不管了,让那个 data_fifo 的读使能和读数据继续工作,但我们不使用它,相当于丢弃了。示意图如下所示:

波形图如下所示,结合上面所说,理解了下面的波形图那本模块就没问题了。

四、波形

  申请一个 ila 观察数据,可以让 Matab 发送一段 0-255 的数据,查看我们的包有无问题,波形图如下所示:

五、以太网 + DDR3 + HDMI 显示

  将千兆以太网的上一讲和本讲结合,替代掉之前 DDR3 工程中的串口发送模块,即可实现 以太网 + DDR3 + HDMI 显示了,尤其注意输出端口、时钟连线和引脚约束。

  OK,到此为止,本项目的一半已经完成啦!

参考资料:威三学院FPGA教程

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FPGA开源工作室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档