IP头校验和
IP头校验和是一种错误检测机制,用于在互联网协议(IP)中保证IP头的数据完整性。
当一个IP数据包从源主机发送到目的主机时,它经过许多路由器和交换机,校验和可以帮助这些中间设备检查数据包在传输过程中是否出现错误。
IP头校验和的计算方式如下:
- 分割:将IP头部分为16位(2字节)的段。
- 求和:将所有16位的段相加(以二进制求和,也就是每个段直接按位相加)。
- 进位处理:如果求和的结果产生了溢出,则将溢出的部分(即进位)加到求和的结果的最低位上。这可能需要重复进行,直到不再有溢出发生。
- 取反:将求和结果取反(按位取反,1变0,0变1)得到校验和。
这个校验和随后被填充在IP头中专门为校验和预留的字段内。在接收端,同样的计算会被执行在收到的IP头上,包括校验和字段本身。如果数据包在传输过程中没有错误,所有段的总和加上接收到的校验和的结果应该是一个全1的二进制数(因为原始计算中结果取反了)。如果计算结果不是全1,那么就说明数据在传输过程中可能出现了错误。
重要的是,IP头校验和只检查头部的完整性,并不保护整个IP数据包的内容。传输层(如TCP或UDP)有自己的校验和,负责整个数据段(包括数据)的完整性检查。
在实际的网络设备中,这个计算通常是由硬件来执行,以提高数据包处理的效率。当数据包通过每个路由器传输时,因为TTL字段会改变,所以需要重新计算校验和。
module emcode_checksum_ip #(
parameter Length_HeartBeat_all = 8'd114
)(
input wire sys_clk ,
input wire sys_rst_n ,
input wire i_vaild ,
input wire [7:0] i_data ,
output wire o_vaild ,
output wire [7:0] o_data
);
reg [15:0] r_i_vaild ;
reg [07:0] r1_i_data [15:0] ;
reg [07:0] checksum_cnt ;
reg [01:0] concat_cnt ;
wire [15:0] concat_data ;
reg [31:0] r1_sum ;
wire [31:0] r1_checksum_value ;
wire [15:0] r2_checksum_value ;
reg [15:0] r3_checksum_value ;
reg [07:0] vaild_cnt ;
reg [07:0] r_o_data ;
always @(posedge sys_clk) begin
r_i_vaild <= {r_i_vaild[14:0],i_vaild};
end
reg [7:0] reg_i1;
reg [7:0] reg_i2;
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
for (reg_i1 = 0; reg_i1 < 'd15; reg_i1 = reg_i1 + 1) begin
r1_i_data[reg_i1] <= 'd0;
end
end else begin
for (reg_i2 = 0; reg_i2 < 'd15; reg_i2 = reg_i2 + 1) begin
r1_i_data[reg_i2+1] <= r1_i_data[reg_i2];
end
r1_i_data[0] <= i_data;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
checksum_cnt <= 8'h0;
end else if(i_vaild == 1'b0 && r_i_vaild[0] == 1'b1) begin
checksum_cnt <= 8'h0;
end else if(i_vaild == 1'b1)begin
checksum_cnt <= checksum_cnt + 1'b1;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
vaild_cnt <= 8'h0;
end else if(r_i_vaild[10] == 1'b0 && r_i_vaild[11] == 1'b1) begin
vaild_cnt <= 8'h0;
end else if(r_i_vaild[10] == 1'b1)begin
vaild_cnt <= vaild_cnt + 1'b1;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
concat_cnt <= 2'd0;
end else if(i_vaild == 1'b0 && r_i_vaild[0] == 1'b1) begin
concat_cnt <= 2'd0;
end else if(i_vaild == 1'b1 && checksum_cnt >= 'd22 && checksum_cnt <= 41) begin
if(concat_cnt == 1'b1) begin
concat_cnt <= 2'd0;
end else begin
concat_cnt <= concat_cnt + 1'b1;
end
end else begin
concat_cnt <= concat_cnt;
end
end
assign concat_data = {r1_i_data[0],i_data};
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r1_sum <= 32'd0;
end else if(i_vaild == 1'b0 && r_i_vaild[0] == 1'b1) begin
r1_sum <= 32'd0;
end else if(i_vaild == 1'b1 && checksum_cnt >= 'd22 && checksum_cnt <= 'd41 && concat_cnt == 1'b1) begin
r1_sum <= r1_sum + concat_data;
end else begin
r1_sum <= r1_sum;
end
end
assign r1_checksum_value = r1_sum[15:0] + r1_sum[31:16];
assign r2_checksum_value = ~(r1_checksum_value[15:0]);
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r3_checksum_value <= 'd0;
end else if(r_i_vaild[10] == 1'b0 && r_i_vaild[11] == 1'b1) begin
r3_checksum_value <= 'd0;
end else if(i_vaild == 1'b1 && checksum_cnt == 'd42) begin
r3_checksum_value <= r2_checksum_value;
end
end
assign o_vaild = r_i_vaild[11];
assign o_data = r_o_data ;
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r_o_data <= 8'd0;
end else if(r_i_vaild[10] == 1'b1 && vaild_cnt == 'd32) begin
r_o_data <= r3_checksum_value[15:08];
end else if(r_i_vaild[10] == 1'b1 && vaild_cnt == 'd33) begin
r_o_data <= r3_checksum_value[07:00];
end else begin
r_o_data <= r1_i_data[10];
end
end
endmodule
UDP校验和
UDP校验和是一种错误检测机制,与IP校验和类似,但它覆盖了更多的数据:UDP校验和不仅检查UDP头部,还检查数据部分。这为UDP数据包提供了一个简单的形式的数据完整性验证。
计算UDP校验和的步骤如下:
-
伪头部:首先,创建一个所谓的“伪头部”,它包括源IP地址、目的IP地址、一个8位全零字段、协议字段(对于UDP,这个值是17,8‘h11)和UDP长度字段。这个伪头部不是UDP数据包的一部分,而是仅用于校验和计算过程。
-
合并:将伪头部、UDP头部和数据部分合并起来。如果数据的总字节长度不是偶数,会在最后添加一个零字节来填充。
-
分割:将合并后的数据分割成16位的段。
-
求和:以二进制求和这些16位的段,包括溢出。
-
进位处理:与IP校验和相同,如果求和的结果产生溢出,将溢出的部分加回到求和结果的最低位。
-
取反:将最终的求和结果取反。
得到的校验和随后放入UDP头部中的校验和字段。在接收端,同样的计算会被执行在整个数据包上(包括伪头部)。如果数据包传输正确,所有的16位段相加(包括校验和字段)应该结果为全1的二进制数(0xFFFF)。如果不是,那么就说明数据包在传输过程中出现了错误。
值得注意的是,UDP校验和是可选的,在IPv4中可以设置为零表示不计算校验和,但在IPv6中,UDP校验和是必需的。这是因为IPv6不包括一个独立的校验和,因此依赖于UDP校验和提供错误检测。
module checksum_ip_buffer #(
parameter UDP_CODE = 8'h11 ,
parameter TX_TARGET_PORT = 16'd808 ,
parameter TX_SOURCE_PORT = 16'd8080 ,
parameter RX_Sync_Frame = 32'hF05AA50F ,
parameter TX_SOURCE_Addr = {8'd192,8'd168,8'd0,8'd1} ,
parameter TX_TARGET_Addr = {8'hFF,8'hFF,8'hFF,8'hFF} ,
//Length
parameter TX_PREAMBLE = {8'h55,8'h55,8'h55,8'h55,8'h55,8'h55,8'h55,8'hD5},
parameter TX_TARGET_MAC = {8'hFF,8'hFF,8'hFF,8'hFF,8'hFF,8'hFF} ,// broadcast packet
parameter TX_SOURECE_MAC = {8'hA8,8'hB2,8'h3C,8'h7D,8'h97,8'hD7} ,
parameter Length_HeartBeat = 16'd92 ,
parameter Length_UDP = 16'd72 ,
parameter Length_HeartBeat_all = 8'd114 ,
parameter Length_EnCODE_Packet = 8'd114
)(
input wire sys_clk ,
input wire sys_rst_n ,
input wire i_vaild ,
input wire [7:0] i_data ,
output wire o_vaild ,
output wire [7:0] o_data
);
reg [15:0] i_vaild_cnt ;
reg [15:0] r_i_vaild ;
reg [07:0] r1_i_data ;
wire [15:0] concat_data ;
//IP check sum
reg [01:0] ip_concat_cnt ;
reg [01:0] udp_concat_cnt ;
reg [31:0] r1_IPsum ;
wire [31:0] r1_IPchecksum_value ;
reg [15:0] r2_IPchecksum_value ;
//UDP check sum
reg udp_reg ;
reg [31:0] r1_UDPsum ;
wire [31:0] r1_UDPchecksum_value ;
wire [15:0] r2_UDPchecksum_value ;
reg [15:0] r3_UDPchecksum_value ;
//rd
reg rd_cycle ;
reg [15:0] rd_cnt ;
reg [15:0] r_rd_cnt ;
reg r1_o_vaild ;
reg r2_o_vaild ;
reg [07:0] r1_o_data ;
wire [07:0] rd_data ;
wire [31:00] Faker_header ;
wire [15:00] length_check ;
wire length_check_flag ;
assign Faker_header = TX_SOURCE_Addr[31:16] + TX_SOURCE_Addr[15:00]
+ TX_TARGET_Addr[31:16] + TX_TARGET_Addr[15:00]
+ {8'h00,UDP_CODE} + Length_UDP;
assign length_check = Length_UDP - 'd8;
assign length_check_flag = (length_check[0] == 0)?(1'b0):(1'b1);
always @(posedge sys_clk) begin
r_i_vaild <= {r_i_vaild[14:0],i_vaild};
r1_o_vaild <= rd_cycle;
r2_o_vaild <= r1_o_vaild;
r1_i_data <= i_data;
r_rd_cnt <= rd_cnt;
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
i_vaild_cnt <= 8'h0;
end else if(i_vaild == 1'b1) begin
if(i_vaild_cnt == Length_EnCODE_Packet - 1'b1) begin
i_vaild_cnt <= 8'h0;
end else begin
i_vaild_cnt <= i_vaild_cnt + 1'b1;
end
end
end
assign concat_data = {r1_i_data,i_data};
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
ip_concat_cnt <= 2'd0;
end else if(i_vaild == 1'b0 && r_i_vaild[0] == 1'b1) begin
ip_concat_cnt <= 2'd0;
end else if(i_vaild == 1'b1 && i_vaild_cnt >= 'd22 && i_vaild_cnt <= 41) begin
if(ip_concat_cnt == 1'b1) begin
ip_concat_cnt <= 2'd0;
end else begin
ip_concat_cnt <= ip_concat_cnt + 1'b1;
end
end else begin
ip_concat_cnt <= ip_concat_cnt;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r1_IPsum <= 32'd0;
end else if(i_vaild == 1'b0 && r_i_vaild[0] == 1'b1) begin
r1_IPsum <= 32'd0;
end else if(i_vaild == 1'b1 && i_vaild_cnt >= 'd22 && i_vaild_cnt <= 'd41 && ip_concat_cnt == 1'b1) begin
r1_IPsum <= r1_IPsum + concat_data;
end else begin
r1_IPsum <= r1_IPsum;
end
end
assign r1_IPchecksum_value = r1_IPsum[15:0] + r1_IPsum[31:16];
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r2_IPchecksum_value <= 'd0;
end else if(rd_cycle == 1'b1 && rd_cnt == Length_EnCODE_Packet - 1'b1) begin
r2_IPchecksum_value <= 'd0;
end else if(i_vaild == 1'b1 && i_vaild_cnt == 'd42) begin
r2_IPchecksum_value <= ~(r1_IPchecksum_value[15:0]);
end
end
// UDP
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
udp_concat_cnt <= 2'd0;
end else if(i_vaild == 1'b0 && r_i_vaild[0] == 1'b1) begin
udp_concat_cnt <= 2'd0;
end else if(i_vaild == 1'b1 && ((i_vaild_cnt >= 42 && i_vaild_cnt <= 49)
|| (i_vaild_cnt >= 'd50 && i_vaild_cnt <= Length_EnCODE_Packet - 1'b1))) begin
if(udp_concat_cnt == 1'b1) begin
udp_concat_cnt <= 2'd0;
end else begin
udp_concat_cnt <= udp_concat_cnt + 1'b1;
end
end else begin
udp_concat_cnt <= udp_concat_cnt;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
udp_reg <= 1'b0;
end else if(i_vaild == 1'b1 && i_vaild_cnt == Length_EnCODE_Packet - 1'b1) begin
udp_reg <= 1'b1;
end else begin
udp_reg <= 1'b0;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r1_UDPsum <= 'd0;
end else if(rd_cycle == 1'b1 && rd_cnt == Length_EnCODE_Packet - 1'b1) begin
r1_UDPsum <= 'd0;
end else if(i_vaild == 1'b1 && r_i_vaild == 1'b0) begin
r1_UDPsum <= Faker_header;
end else if(i_vaild == 1'b1 && udp_concat_cnt == 1'b1
&& ((i_vaild_cnt >= 'd42 && i_vaild_cnt <= 'd49)
|| (i_vaild_cnt >= 'd50 && i_vaild_cnt <= Length_EnCODE_Packet - 1'b1))) begin
r1_UDPsum <= r1_UDPsum + concat_data;
end else begin
r1_UDPsum <= r1_UDPsum;
end
end
assign r1_UDPchecksum_value = r1_UDPsum[15:0] + r1_UDPsum[31:16];
assign r2_UDPchecksum_value = ~(r1_UDPchecksum_value[15:0]);
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r3_UDPchecksum_value <= 'd0;
end else if(rd_cycle == 1'b1 && rd_cnt == Length_EnCODE_Packet - 1'b1) begin
r3_UDPchecksum_value <= 'd0;
end else if(udp_reg == 1'b1) begin
r3_UDPchecksum_value <= r2_UDPchecksum_value;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
rd_cycle <= 1'b0;
end else if(rd_cycle == 1'b1 && rd_cnt == Length_EnCODE_Packet - 1'b1) begin
rd_cycle <= 1'b0;
end else if(rd_cycle == 1'b0 && i_vaild == 1'b1 && i_vaild_cnt == Length_EnCODE_Packet - 1'b1) begin
rd_cycle <= 1'b1;
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
rd_cnt <= 8'h0;
end else if(rd_cycle == 1'b1) begin
if(rd_cnt == Length_EnCODE_Packet - 1'b1) begin
rd_cnt <= 8'h0;
end else begin
rd_cnt <= rd_cnt + 1'b1;
end
end
end
always @(posedge sys_clk) begin
if(sys_rst_n == 1'b0) begin
r1_o_data <= 8'h0;
end else if(r1_o_vaild == 1'b1 && r_rd_cnt == 'd32) begin
r1_o_data <= r2_IPchecksum_value[15:08];
end else if(r1_o_vaild == 1'b1 && r_rd_cnt == 'd33) begin
r1_o_data <= r2_IPchecksum_value[07:00];
end else if(r1_o_vaild == 1'b1 && r_rd_cnt == 'd48) begin
r1_o_data <= r3_UDPchecksum_value[15:08];
end else if(r1_o_vaild == 1'b1 && r_rd_cnt == 'd49) begin
r1_o_data <= r3_UDPchecksum_value[07:00];
end else if(r1_o_vaild == 1'b1) begin
r1_o_data <= rd_data;
end else begin
r1_o_data <= 8'hff;
end
end
assign o_vaild = r2_o_vaild;
assign o_data = r1_o_data;
buffer_encode buffer_encode_inst0 (
.clka ( sys_clk ), // input wire clka
.wea ( i_vaild ), // input wire [0 : 0] wea
.addra( i_vaild_cnt ), // input wire [6 : 0] addra
.dina ( i_data ), // input wire [7 : 0] dina
.clkb ( sys_clk ), // input wire clkb
.addrb( rd_cnt ), // input wire [6 : 0] addrb
.doutb( rd_data ) // output wire [7 : 0] doutb
);
endmodule