1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html
UDP(udp)模块是对以太网接收模块(ip_receive)、以太网发送模块(ip_send)、CRC
校验模块(crc32_d4)的例化,其中crc32_d4模块是对发送模块做CRC校验。下面着重介绍各
个子模块代码的实现。
我们在前面介绍过,以太网接收模块实现的是4位转32位的功能以及解析数据的顺序,可
以发现,解析数据的顺序很适合使用状态机来实现,下图为以太网接收模块的状态跳转图。
图 43.4.5 ip_receive模块的状态跳转图
接收模块使用三段式状态机来解析以太网包,从上图可以比较直观的看到每个状态实现的
功能以及跳转到下一个状态的条件。这里需要注意的一点是,在中间状态如前导码错误、MAC
地址错误以及IP地址错误时跳转到st_rx_end状态而不是跳转到st_idle转态。因为中间状态在
解析到数据错误时,单包数据的接收还没有结束,如果此时跳转到st_idle状态会误把有效数
据当成前导码来解析,所以状态跳转到st_rx_end。而eth_rxdv信号为0时,单包数据才算接收
结束,所以st_rx_end跳转到st_idle的条件是eth_rxdv=0,准备接收下一包数据。因为代码较
长,只粘贴了第三段状态机的接收数据状态和接收结束状态源代码,代码如下:
265 st_rx_data : begin
266 //接收数据,转换成32bit
267 if(rx_byte_val) begin
268 data_cnt <= data_cnt + 16'd1;
269 rec_en_cnt <= rec_en_cnt + 2'd1;
270 if(data_cnt == data_byte_num - 16'd1) begin
271 skip_en <= 1'b1; //有效数据接收完成
272 data_cnt <= 16'd0;
273 rec_en_cnt <= 2'd0;
274 rec_pkt_done <= 1'b1;
275 rec_en <= 1'b1;
276 rec_byte_num <= data_byte_num;
277 end
278 //先收到的数据放在了rec_data的高位,所以当数据不是4的倍数时,
279 //低位数据为无效数据,可根据有效字节数来判断(rec_byte_num)
280 if(rec_en_cnt == 2'd0)
281 rec_data[31:24] <= rx_data;
282 else if(rec_en_cnt == 2'd1)
283 rec_data[23:16] <= rx_data;
284 else if(rec_en_cnt == 2'd2)
285 rec_data[15:8] <= rx_data;
286 else if(rec_en_cnt==2'd3) begin
287 rec_en <= 1'b1;
288 rec_data[7:0] <= rx_data;
289 end
290 end
291 end
292 st_rx_end : begin //单包数据接收完成
293 if(eth_rxdv == 1'b0 && skip_en == 1'b0)
294 skip_en <= 1'b1;
295 end
图 43.4.6为接收过程中SignalTap抓取的波形图,上位机通过网口调试助手发送
http://www.alientek.com/(十六进制为:68 74 74 70 3A 2F 2F 77 77 77 2E 61 6C 69 65
6E 74 65 6B 2E 63 6F 6D 2F),图中eth_rxdv和eth_rx_data为MII接口的接收信号,skip_en
为状态机的跳转信号。每次单包数据接收完成都会产生rec_pkt_done信号,rec_en和rec_data
为收到的数据有效信号和数据。
图 43.4.6 接收过程SignalTap波形图
以太网发送模块实际上完成的是32位数据转4位数据的功能,也就是接收模块的逆过程,
同样也非常适合使用状态机来完成发送数据的功能,状态跳转图如下图所示:
图 43.4.7 ip_send模块的状态跳转图
发送模块和接收模块有很多相似之处,同样使用三段式状态机来发送以太网包,从上图可
以比较直观的看到每个状态实现的功能以及跳转到下一个状态的条件。
发送模块的代码中定义了数组来存储以太网的帧头、IP首部以及UDP的首部,在复位时初
始化数组的值,部分源代码如下。
68 reg [7:0] preamble[7:0] ; //前导码
69 reg [7:0] eth_head[13:0] ; //以太网首部
70 reg [31:0] ip_head[6:0] ; //IP首部 + UDP首部
省略部分代码……
198 //初始化数组
199 //前导码 7个8'h55 + 1个8'hd5
200 preamble[0] <= 8'h55;
201 preamble[1] <= 8'h55;
202 preamble[2] <= 8'h55;
203 preamble[3] <= 8'h55;
204 preamble[4] <= 8'h55;
205 preamble[5] <= 8'h55;
206 preamble[6] <= 8'h55;
207 preamble[7] <= 8'hd5;
208 //目的MAC地址
209 eth_head[0] <= DES_MAC[47:40];
210 eth_head[1] <= DES_MAC[39:32];
211 eth_head[2] <= DES_MAC[31:24];
212 eth_head[3] <= DES_MAC[23:16];
213 eth_head[4] <= DES_MAC[15:8];
214 eth_head[5] <= DES_MAC[7:0];
215 //源MAC地址
216 eth_head[6] <= BOARD_MAC[47:40];
217 eth_head[7] <= BOARD_MAC[39:32];
218 eth_head[8] <= BOARD_MAC[31:24];
219 eth_head[9] <= BOARD_MAC[23:16];
220 eth_head[10] <= BOARD_MAC[15:8];
221 eth