在官方提供的MES50HP_v3版本中,提供了“以太网传输实验例程”,根据文档实现了下述3个功能:
- arp协议
- udp协议
- icmp协议(ping正是基于此协议)
但是下载程序后,arp功能正常,能获取到开发板的MAC地址
但是wireshake无法抓取到定时发送的UDP数据包
更糟糕的是ping也失败了,显示请求超时
但是注意到在ping过程中,wireshake能抓到开发板返回的reply数据包,不过checksum校验是incorrect,如下图中黄色高亮显示
这是一个重要线索,于是度娘了一下icmp协议,了解一下它的checksum是如何计算的。
首先将校验和位置置0,然后将报头和数据按照16bit进行累加,全部进行完后,如果得到的数据超过16bit,则将超出部分与16bit在进行累加,最后对这16bit的数取反,得到最终checksum。
比如下图中的数据,0x0800,0x4cb0,0x0001,0x00ab,0x6162,0x6364,0x6566,0x6768,0x696a,0x6b6c,0x6d6e,0x6f70,0x7172,0x7374,0x7576,0x7761,0x6263,0x6465,0x6667,0x6869。
按照上述方法列出下式,得到的0x4cb0与校验位的值相等。
0x0800+0x0000+0x0001+0x00ab+
0x6162+0x6364+0x6566+0x6768+
0x696a+0x6b6c+0x6d6e+0x6f70+
0x7172+0x7374+0x7576+0x7761+
0x6263+0x6465+0x6667+0x6869
=0x6b349
0x6 + 0x6349 = 0xb34f
~0xb34f = 0x4cb0
了解完计算公式,那么现在需要定位,为什么reply的checksum会出错呢。打开例程中的icmp.v文件,搜索变量“icmp_tx_data”:
always @(posedge clk)
begin
if (~rstn)
icmp_tx_data <= 8'h00 ;
else if (state == SEND)
begin
case(icmp_rx_cnt)
16'd1 : icmp_tx_data <= ECHO_REPLY ;
16'd2 : icmp_tx_data <= icmp_code ;
16'd3 : icmp_tx_data <= reply_checksum[15:8];
16'd4 : icmp_tx_data <= reply_checksum[7:0] ;
16'd5 : icmp_tx_data <= icmp_id[15:8] ;
16'd6 : icmp_tx_data <= icmp_id[7:0] ;
16'd7 : icmp_tx_data <= icmp_seq[15:8] ;
16'd8 : icmp_tx_data <= icmp_seq[7:0] ;
default : icmp_tx_data <= icmp_rec_ram_rdata ;
endcase
end
else
icmp_tx_data <= 8'h00 ;
end
end
可以看到,只是单纯的将收到的icmp重新打包发出去。
再回到wireshake,对reply的包进行分析,发现抓取的“icmp_rec_ram_rdata”这一部分,和预想的数值偏移了一位。发送方是从0x61一直到0x69,而FPGA返回的是从0x62到0x69,并且少了一个Byte,并填入了0。
那么可以先下个结论,FPGA在计算checksum时读取的是完整的icmp_rec_ram_rdata,而在发送时却漏掉的一组数据,导致PC端校验失败,最终表现为Ping超时。
既然这样,问题就好解决了,将icmp_rec_ram_rdata延迟一拍。由于icmp_rec_ram_rdata来自RAM IP,那么我们可以直接在IP中把“Enable Ouput Register”勾选上就可以了。
重新编译并下载程序,果然ping正常响应了,wireshake也没有报错误。
至于没有UDP包,还没头绪,需要继续debug……