赞
踩
万兆以太网设计最终章节,巨型以太网数据帧传输设计。对于标准以太网而言,数据传输范围为46-1500字节,当大于1500字节后数据将无法传输。在IP层的报文描述当中,有一个分片字段,通过该字段即可实现将巨型数据帧拆分为多个小于1500字节的数据进行传输。
在UDP接收模块当中增加以下新设计:
//完整的一个包输入指示信号
//表示没有分片,那么last到来即一个包结束; 分片但是当前分片后没有更多片
assign w_recv_pkt_end = r_udp_pkt_valid && ((rs_axis_ip_last && r_udp_split == 0 && r_udp_offset == 0) ||
(rs_axis_ip_last && r_udp_split == 1 && r_udp_more_split == 0));
//检测是否为UDP数据包,通过user当中的type字段判断即可
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_udp_pkt_valid <= 'd0;
else if(s_axis_ip_valid && !rs_axis_ip_valid && s_axis_ip_user[36:29] == 8'd17 && w_ip_flags[2] == 0)//分片udp没有包头
r_udp_pkt_valid <= 'd1;
else if(s_axis_ip_valid && !rs_axis_ip_valid && ((s_axis_ip_user[36:29] != 8'd17) || (s_axis_ip_data[47:32] != ri_dymanic_src_port)))
r_udp_pkt_valid <= 'd0;
else if(s_axis_ip_valid && !rs_axis_ip_valid && s_axis_ip_user[36:29] == 8'd17 && s_axis_ip_data[47:32] == ri_dymanic_src_port)
r_udp_pkt_valid <= 'd1;
else
r_udp_pkt_valid <= r_udp_pkt_valid;
end
//偏移为0才表示当前udp数据包是第一个包,当分片时只有第一个udp包带包头信息
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_udp_header <= 'd0;
else if(rs_axis_ip_last)
r_udp_header <= 'd0;
else if(s_axis_ip_valid && !rs_axis_ip_valid && s_axis_ip_user[28:16] == 'd0)
r_udp_header <= 'd1;
else
r_udp_header <= r_udp_header;
end
该模块设计其实相对来说简单一点。
我的设计当中只用到了一个FIFO,没有其他FIFO和RAM资源的使用
FIFO_64X2048 FIFO_64X2048_UDP_TX (
.clk (i_clk ), // input wire clk
.srst (i_rst ), // input wire srst
.din (rs_axis_user_data ), // input wire [63 : 0] din
.wr_en (rs_axis_user_valid ), // input wire wr_en
.rd_en (r_fifo_data_rden ), // input wire rd_en
.dout (w_fifo_data_dout ), // output wire [63 : 0] dout
.full (w_fifo_data_full ), // output wire full
.empty (w_fifo_data_empty ) // output wire empty
);
注:说明一下发送端分片,当字节数大于1472即需要分片,因为还有20字节的IP头和8字节的UDP头,当传输完1472字节后,如果剩余字节大于1480,则继续分片,为什么不是1472了呢,因为后续的分片当中是不需要8字节的UDP头了。
//用户数据字节长度,每次发送结束后更新, //如果是第一个包,当其小于1472则不用分片 //如果不是第一个包,当其小于1480则不用继续分片 always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_byte_len <= 'd0; else if(s_axis_user_valid && !rs_axis_user_valid) r_byte_len <= s_axis_user_user[15:0]; else if(r_first_split && rm_axis_ip_last && r_split_flag) r_byte_len <= r_byte_len - 1472; else if(rm_axis_ip_last && r_split_flag) r_byte_len <= r_byte_len - 1480; else r_byte_len <= r_byte_len; end //根据r_byte_len数值判断是否继续分片 always @(posedge i_clk or posedge i_rst) begin if(i_rst) r_split_flag <= 'd0; else if(s_axis_user_valid && !rs_axis_user_valid && s_axis_user_user[15:0] <= 1472) r_split_flag <= 'd0; else if(s_axis_user_valid && !rs_axis_user_valid && s_axis_user_user[15:0] > 1472) r_split_flag <= 'd1; else if(r_split_flag && rm_axis_ip_last_1d && r_byte_len > 1480) r_split_flag <= 'd1; else if(r_split_flag && rm_axis_ip_last_1d && r_byte_len <= 1480) r_split_flag <= 'd0; else r_split_flag <= r_split_flag; end
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_offset <= 'd0;
else if(r_split_flag && rm_axis_ip_last)
r_offset <= r_offset + 16'd185;
else if(r_split_run && !r_split_flag && rm_axis_ip_last)
r_offset <= 'd0;
else
r_offset <= r_offset;
end
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
r_split_run <= 'd0;
else if(r_split_flag)
r_split_run <= 'd1;
else if(!r_split_flag && rm_axis_ip_last)
r_split_run <= 'd0;
else
r_split_run <= r_split_run;
end
always @(posedge i_clk or posedge i_rst) begin
if(i_rst)
rm_axis_ip_user <= 'd0;
else if(r_first_split && !r_split_flag)//只有包头需要+8
rm_axis_ip_user <= {r_byte_len + 16'd8, 3'b010, 8'd17, r_offset, 16'd0};
else if(r_split_flag)
rm_axis_ip_user <= {16'd1480, 3'b001, 8'd17, r_offset, 16'd0};
else
rm_axis_ip_user <= {r_byte_len, 3'b000, 8'd17, r_offset, 16'd0};
end
该模块的调试是最漫长的。。。。。。。。。。。
在实际上板后我发现,主机网卡在发送巨帧的时候,数据之间的间隔只有一个空闲位,也就是说俩帧数据头尾可能是直接挨着的,而之前的MAC接受模块存在一个隐性BUG,无法连续处理接收到的MAC数据,因此会丢失报文,而且CRC也会错乱,进而导致后续的CRC处理模块当中的RAM读取全部出错,大量数据累计在RAM当中无法被取出,在进行回环测试的时候,我上位机发送一个包,过了好久才能收到回环数据,并且还是错的。。
主要修改在于r_crc_run信号的判断。
之前的代码总是留有许多时钟余量,就是我一般都不会过于关注极限情况,觉得不影响后续数据的情况下晚几拍判断出结果更加稳妥,没想到这种思想导致后期BUG调试十分困难,
always @(posedge i_clk or posedge i_rst)begin if(i_rst) r_crc_run <= 'd0; else if(r_sof_location == 7 && w_eof && w_eof_location >= 2) r_crc_run <= 'd0; else if(r_sof_location == 7 && r_eof && r_eof_location < 2) r_crc_run <= 'd0; else if(r_sof_location == 3 && w_eof && w_eof_location >= 6) r_crc_run <= 'd0; else if(r_sof_location == 3 && r_eof && r_eof_location < 6) r_crc_run <= 'd0; // else if(r_crc_end) // r_crc_run <= 'd0; else if(r_sof && (r_sof_location == 7 || r_sof_location == 3)) r_crc_run <= 'd1; else r_crc_run <= r_crc_run; end
还有一些相关信号的修改,总之都是一些仿真难以暴露的问题。
以下分别是ILA波形、wireshark和网口调试助手所获取的信息,勘验后无误。
以下分别是ILA波形、wireshark和网口调试助手所获取的信息,勘验后无误。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。