文章目录

前言:以太网MAC层报文解析一、MAC发送模块1.1、组帧FIFO1.1、发送端CRC校验码

二、MAC接收模块2.1、接收端CRC校验2.1、接收端CRC处理模块1、设计思路2、考虑问题

总结

前言:以太网MAC层报文解析

前导码:7个0h55和一个起始界定符SFD,0hD5,不过大部分地方好像是7个0hAA,就是bit顺序相反,不过我用FPGA接收到的上位机的网口数据是55,估计是大小端传输问题吧。注:有时候有可能是六个55一个D5 类型:IP:0X0800。ARP:0X0806

一、MAC发送模块

发送模块按部就班按照协议格式进行组帧即可,木有难度,只有俩点注意一下。

1.1、组帧FIFO

用户进入的数据需要先存入FIFO,因为用户数据不能立马填入MAC帧当中,需要先填充前面的前导码,MAC地址等内容

1.1、发送端CRC校验码

直接通过网址http://crctool.easics.be/产生相应的CRC校验模块即可。 简单介绍一下代码如何使用: 原始文件:下载下来是这样的

module CRC32_D8;

// polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1

// data width: 8

// convention: the first serial bit is D[7]

function [31:0] nextCRC32_D8;

input [7:0] Data;

input [31:0] crc;

reg [7:0] d;

reg [31:0] c;

reg [31:0] newcrc;

begin

d = Data;

c = crc;

newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];

newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];

newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31];

newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];

newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];

newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31];

newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31];

newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31];

newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28];

newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29];

newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29];

newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];

newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30];

newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31];

newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31];

newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31];

newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29];

newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30];

newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31];

newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31];

newcrc[20] = d[4] ^ c[12] ^ c[28];

newcrc[21] = d[5] ^ c[13] ^ c[29];

newcrc[22] = d[0] ^ c[14] ^ c[24];

newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30];

newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31];

newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];

newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30];

newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31];

newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30];

newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31];

newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31];

newcrc[31] = d[5] ^ c[23] ^ c[29];

nextCRC32_D8 = newcrc;

end

endfunction

endmodule

具体实现可以看看其他文章介绍,每一个8bit数据的校验结果都用来和下一个8bit进行计算。前导码不参与校验 原始文件是一个function函数的形式,我们一般使用module的形式进行调用。记得进行大小端翻转。

module CRC32_D8(

input i_clk ,

input i_rst ,

input i_en ,

input [7 :0] i_data ,

output [31:0] o_crc

);

reg [31:0] crc;

wire [7 :0] d;

wire [31:0] c;

wire [31:0] newcrc;

assign o_crc = ~{crc[0] ,crc[1] ,crc[2] ,crc[3] ,crc[4] ,crc[5] ,crc[6] ,crc[7] ,

crc[8] ,crc[9] ,crc[10],crc[11],crc[12],crc[13],crc[14],crc[15],

crc[16],crc[17],crc[18],crc[19],crc[20],crc[21],crc[22],crc[23],

crc[24],crc[25],crc[26],crc[27],crc[28],crc[29],crc[30],crc[31]};

assign d = {i_data[0],i_data[1],i_data[2],i_data[3],i_data[4],i_data[5],i_data[6],i_data[7]};

assign c = crc;

assign newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];

assign newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];

assign newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31];

assign newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];

assign newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];

assign newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31];

assign newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31];

assign newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31];

assign newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28];

assign newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29];

assign newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29];

assign newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];

assign newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30];

assign newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31];

assign newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31];

assign newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31];

assign newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29];

assign newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30];

assign newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31];

assign newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31];

assign newcrc[20] = d[4] ^ c[12] ^ c[28];

assign newcrc[21] = d[5] ^ c[13] ^ c[29];

assign newcrc[22] = d[0] ^ c[14] ^ c[24];

assign newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30];

assign newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31];

assign newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];

assign newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30];

assign newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31];

assign newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30];

assign newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31];

assign newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31];

assign newcrc[31] = d[5] ^ c[23] ^ c[29];

always @(posedge i_clk or posedge i_rst) begin

if(i_rst)

crc <= 32'hffffffff;

else if(i_en)

crc <= newcrc;

else

crc <= crc;

end

endmodule

二、MAC接收模块

2.1、接收端CRC校验

接收模块对数据逐字节按照协议进行解析即可,并且要比对CRC校验结果,把前面的前导码去掉很简单,那么要怎么去除后面四个byte的校验码呢,此时需要对数据进行打拍处理,比如把数据打5拍,第一拍数据结束的时候,第五拍数据刚好就是在有效数据刚刚结束的地方,如黄线部分。 接收到的CRC校验可以通过第一拍数据进行不断移位操作便可以得到;本地则对第五拍数据进行校验,即可同步得到接收报文里面的CRC和本地检查结果的CRC。进行对比,得出结论。

2.1、接收端CRC处理模块

该模块需要实现以下功能:将MAC_RX接收到的数据同步存储到本地RAM,然后根据MAC_RX模块得到的校验结果决定该数据需要继续向上一层传输(CRC比对正确)还是直接丢弃(CRC比对错误)。

1、设计思路

具体实现过程是通过一个环形RAM,随着接收数据的进入,会一个个存入RAM当中,并且地址进行累加,如果CRC正确,那么地址则停留在此,不进行回退,如果数据错误,那么RAM地址则直接回退到上一次写数据结束的地方,因此就可以通过覆盖此数据以实现将数据丢弃的功能。

2、考虑问题

在此过程中还需要记录每一次数据的类型和长度,同步存储到相应的FIFO当中。这是为什么呢? 首先我们考虑设计的RAM最大长度,由于MAC数据最大长度为1500,所以RAM最小为1500,当到达一个1500的数据后,它会先存入RAM,随后等待校验结果,正确则开始输出,倘若随后来到的数据包很小,比如连续到达俩个46的数据包,而此时第一个1500的数据包依旧还在发送,所以我们必须要将每一个到达的数据的类型和长度都存入FIFO,才能够在后续发送他们的时候知道他们的长度和类型,如果不知道数据长度,就没办法正确从RAM中将他们读出。

总结

除了CRC相关模块以外,并没有其它难度,完整代码可以参考本人GitHub:https://github.com/shun6-6/Tri_Eth_UDP_pro_stack

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: