在上一篇中,通过iddr_ctrl模块将DDR信号转换为SDR信号,并输出rx_data[7:0]信号和对应的rx_en数据有效信号。本篇将在此基础上对信息进行CRC校验和有效包检测,以达到包过滤的目的。
目录
一、为什么要包过滤?
在一个系统的发送端(这里指上位机)可能有不同的应用程序,接收端也可能有不同的接收设备。那么在数据接收中就需要考虑两个问题:1.当一包数据到来时(rx_en==1),这一包数据是不是发给我的?2.传来的数据有没有错误?
例如,我只想接收上位机中的A应用发来的数据,而A应用也只想让我自己收到数据。那么我在接收到数据的时候就要进行判断,如果是发给我的,并且数据在传输过程中没有发生错误,就留下这包数据;如果不是发给我的,或者发生了错误,就将这包数据过滤掉。
二、有效包检测
从以太网的包结构中可以看到,前50字节是以太网包头,其中具体包括了MAC首部、IP首部和UDP首部。最后还有四个字节的数据校验位,传输的信息存放在data中。我们进行有效包检测就是利用以太网包头中的信息进行判断。已知UDP协议字段为8'h11,我们设置PC端应用端口号为1234,目的端口号为123,我们可以通过判断这三个信息来证明此包为来自于我们需要的应用。
![](https://img-blog.csdnimg.cn/direct/d28b1e7ec8f64788b48fd91b829718db.png)
具体实现上,定义两个计数器rx_cnt和vld_cnt,rx_cnt根据rx_en进行计数,当rx_cnt==31时判断UDP协议字段,当rx_cnt==42-43时判断源端口,当rx_cnt==44-45时判断目的端口,共要进行5次判断。每满足一个判断条件就给vld_cnt+1,当vld_cnt==5时,将vld_pkg信号拉高,代表包有效。
reg [13:0] rx_cnt;
reg [3:0] filter_cnt;
reg vld_pkg;
always @(posedge sclk) begin
if (rst == 1'b1) begin
filter_cnt <= 'd0;
end
else if (rx_en == 1'b1 && rx_cnt == 'd31 && rx_data == 8'h11) begin //udp
filter_cnt <= filter_cnt + 1'b1;
end
else if (rx_en == 1'b1 && rx_cnt == 'd42 && rx_data == 8'h04) begin //source port
filter_cnt <= filter_cnt + 1'b1;
end
else if (rx_en == 1'b1 && rx_cnt == 'd43 && rx_data == 8'hD2) begin
filter_cnt <= filter_cnt + 1'b1;
end
else if (rx_en == 1'b1 && rx_cnt == 'd44 && rx_data == 8'h00) begin//dest port
filter_cnt <= filter_cnt + 1'b1;
end
else if (rx_en == 1'b1 && rx_cnt == 'd45 && rx_data == 8'h7B) begin
filter_cnt <= filter_cnt + 1'b1;
end
else if(rx_en == 1'b0) begin
filter_cnt <= 'd0;
end
end
always @(posedge sclk) begin
if (rst == 1'b1) begin
vld_pkg <= 1'b0;
end
else if (rx_en == 1'b0 && rx_en_t == 1'b1 && filter_cnt == 'd5) begin
vld_pkg <= 1'b1;
end
else if(rx_en == 1'b0 && rx_en_t == 1'b1 && filter_cnt != 'd5) begin
vld_pkg <= 1'b0;
end
end
三、错误检测(CRC校验)
原理
CRC 是一种基于二进制除法的错误检测机制。它通过在数据末尾附加一个校验码(称为 CRC 码或 CRC 字符)来实现。在数据接收端,使用相同的算法对接收到的数据进行校验,如果计算结果与接收到的 CRC 码一致,则数据被认为是完整的;否则,数据被认为是损坏的。CRC是数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定。
在通信时,收、发双方需要约定一个生成多项式,这个生成多项式可以理解为CRC的计算规则。发送方在发送数据时,需要将数据与生成多项式进行二进制除法运算,得到的余数就是CRC校验码,附加在数据的末尾进行传输。接收端在接收到数据后,使用相同的生成多项式进行除法运算,如果余数为0,则数据没有发生错误。
常用的CRC校验有CRC8、CRC16和CRC32,所谓CRC8就可以理解为生成多项式和最后得到的CRC检验码长度为8bit。
CRC计算方法
根据CRC的原理可知,CRC计算实际上就是二进制除法运算,但是这种计算方法并不适用于FPGA硬件实现。下面介绍一种移位寄存器计算法,适合FPGA实现。
首先要根据生成多项式的相关性画出移位寄存器的流程图,假设生成多项式的相关性为1_0000_0111(二进制),每遇到1 的地方就要有一个异或门, 得到数据流向图:
![](https://img-blog.csdnimg.cn/direct/c4384ef9d5c1440f9c2a7178e53c268f.png)
假设数据信息为1101、8个寄存器初始值都为零,那么数据信息从高位到低位依次进入到寄存器数据流中,经过4次数据变化后,寄存器中的值为00100011(二进制),该值即为CRC校验码。CRC16、CRC32同理。
假设数据信息为D3 D2 D1 D0,移位寄存器的初始值为R1 R2 R3 R4 R5 R6 R7 R8,则Verilog实现示例如下:
Reg[7:0] crc;
Reg R1,r2….;
Always @(….)
If(…)
Crc <= 00;
Else
Begin
Crc[0] <= D0^R5;
Crc[1] <= D0^R5^ D1^R6;
Crc[2] <= D0^R5^ D1^R6^ D2^R7;
Crc[3] <= D1^R6^ D2^R7^ D3^R8;
Crc[4] <= D2^R7^ D3^R8^R1;
Crc[5] <= D3^R8^R2;
Crc[6] <= R3 ;
Crc[7] <= R4;
end
四、模块整体描述
模块整体框图如下:
![](https://img-blog.csdnimg.cn/direct/eed5e87806cf49b4844ae461233e0a17.png)
进来的数据分为两路,上一路存放到RX_BUFFER(fifo)中,下一路进行有效包检测和CRC校验。得到的数据有效信号vld_pkg信号、CRC检验结果信号crc_ok和数据长度信号pkg_len[13:0]拼接成一个16bit的status信号写入RX_STA_BUFFER(fifo)中,一旦RX_STA_BUFFER非空,就将里面的数据读出,判断vld_pkg、crc_ok是否==1,若满足就从RX_BUFFER中读出pkg_len个数据。
五、上板验证
连接板卡,在网络调试助手中设置①协议类型为UDP;②本地主机端口为1234;③远程主机IP为255.255.255.255(广播地址);④远程端口为123。
![](https://img-blog.csdnimg.cn/direct/b9673fa562e846b09e649c3aa1ce693e.png)
连接成功后,使用调试助手向板卡发送0~255共256个数据,在vivado中使用ila抓取rx_filter_cnt==5,说明此包数据时有效数据。
![](https://img-blog.csdnimg.cn/direct/df65bd23400f45e3a97d0d3610a21f3e.png)
观察过滤后的数据frx_data[7:0]以及数据有效标志位frx_en,可以看到数据为0~255。
![](https://img-blog.csdnimg.cn/direct/893616db404340d4bf8b8edbb44c45ed.png)
此外,在调试助手中修改远程端口号不是123时,ila将不会抓取到数据,说明数据已经被过滤掉。