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

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

一、数据的校验和筛选

  根据本次工程的情况,我们按照下表来进行设计:

1、包有效校验

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

//==========================================================================
//==    包有效校验(udp、port源、port目的)
//==========================================================================
always @(posedge clk) begin
    if(rst) begin
        pkg_value <= 40'd0;
    end
    else if(PHY_rx_cnt==31 || (PHY_rx_cnt>=42 && PHY_rx_cnt<=45)) begin
        pkg_value <= {pkg_value[31:0],PHY_rxd};
    end
end

always @(posedge clk) begin
    if(rst) begin
        pkg_vld <= 1'b0;
    end
    else if(PHY_rx_neg && pkg_value==40'h11_04d2_007b) begin
        pkg_vld <= 1'b1;
    end
    else begin
        pkg_vld <= 1'b0;
    end
end

2、CRC校验

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

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 begin
        crc32_value <= 32'hFFFFFFFF;
    end
end

3、数据的筛选

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

4、8bit转16bit

  以太网传过来的是双沿 4bit 数据,转换后是 8bit,而图像是 RGB565 格式,是 16bit 的,因此还需要进行一下 8bit 转 16bit 的设计。

//==========================================================================
//==    8bit转16bit
//==========================================================================
always @(posedge clk) begin
    if(rst)
        byte_cnt <= 0;
    else if(add_byte_rx_cnt) begin
        if(end_byte_rx_cnt)
            byte_cnt <= 0;
        else
            byte_cnt <= byte_cnt + 1;
    end
end

assign add_byte_rx_cnt = frx_vld;
assign end_byte_rx_cnt = add_byte_rx_cnt && byte_cnt== 2-1;

always @(posedge clk) begin
    if(rst) begin
        PHY_data <= 0;
    end
    else if(add_byte_rx_cnt) begin
        PHY_data <= {PHY_data[7:0],frx_data};
    end
end

always @(posedge clk) begin
    if (rst == 1'b1) begin
        PHY_vld <= 1'b0;
    end
    else if(end_byte_rx_cnt) begin
        PHY_vld <= 1'b1;
    end
    else begin
        PHY_vld <= 1'b0;
    end
end

 

二、代码设计

  上面已经贴了部分代码了,难道这里我要全贴代码吗?不,贴代码没有意义,重要的是懂内部的含义。我们以 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、HDMI结合,即可实现 以太网 + DDR3 + HDMI 显示了,尤其注意输出端口、时钟连线和引脚约束,工程结构如下所示:

  发送端在PC,利用 matlab 将一段影片转换为图像数据,图像数据格式为 RGB565,图像转换为一个个以太网的包,再利用 matlab 发送到网口,FPGA板卡接收以太网数据,经过 PHY_top 的转换,再在 DDR3_ctrl 中缓存,最后通过 HDMI_top 模块将视频输出到显示屏上。

  HDMI 的设计中没有考虑声音的输出,因此 matlab 那也没有转换声音,最终显示的视频是无声的,实验结果如下所示:

 

 

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

posted @ 2019-12-17 21:46  咸鱼IC  阅读(3036)  评论(0编辑  收藏  举报