前面说到TCP/IP是一个协议簇,其中包含有IP协议、TCP协议、UDP协议、ARP协议、DNS协议、FTP协议等。设备之间要想完成通信,就必须通过这些网络通信协议。

物理层的主要作用就是传输比特流(将1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,即数模转换与模数转换)。
数据链路层接收来自物理层的位流形式的数据,并封装成帧,传送到上一层;也将来自上层的数据帧,拆装为位流形式的数据转发到物理层。MAC数据包位于数据链路层,当MAC数据包经过数据链路层到达网络层时,前导码、帧起始界定符、目的 MAC 地址、源 MAC 地址、类型/长度以及校验字节均被去除,只有有效数据传入了网络层。
网路层通过路由选择算法,为报文(该层的数据单位,由上一层数据打包而来)通过通信子网选择最适当的路径。这一层定义的是IP地址,通过IP地址寻址,所以产生了IP协议。传入网络层的数据包并不完全是需要传输的有效数据,他的前面还包含着 20 字节的IP协议首部。网络层在接收到数据包后,取下数据包的IP首部,将剩余有效数据包发送到传输层。
而传输层提供了主机应用程序进程之间的端到端的服务,基本功能是:分割与重组数据、按端口号寻址、连接管理、差错控制和流量控制、纠错功能。若传输层使用UDP协议,那么传入传输层的数据包为UDP数据包。
应用层是计算机用户,以及各种应用程序和网络之间的接口。

以太网数据包就是对各层协议的逐层封装来实现数据传输,MAC帧中的数据段为IP数据报文,IP报文中的数据段位UDP报文,UDP报文中的数据段为传输数据。
IP协议规定了数据传输时的基本单元和格式,位于以太网MAC格式的数据段,由IP首部和数据字段组成。

IP首部校验和计算步骤:

IP首部校验和校验
对IP首部中每个16bit进行二进制反码求和,将计算结果再取反码,若结果为0,通过检验,否则不通过检验。
例:验证IP首部 45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d
(1)对 IP 首部进行反码求和:4500+0030+804c+4000+8006+b52e+d343+117b+cb51+153d=3fffc
0300+3fffc=ffff
(2)对和结果取反码:~ffff=0,校验正确。
UDP (User Datagram Protocol),即用户数据报协议,是一种面向无连接的传输层协议。无连接是指在传输数据时,数据的发送端和接收端不建立逻辑连接。即发送端只管发送,不会管接收端到底有没有接收到数据,而接收端也不会向发送端反馈反馈是否收到数据。
UDP首部共8个字节,同IP首部一样,也是一行以32位(4个字节)为单位。
UDP校验和的计算需要三部分数据:UDP 伪首部、UDP首部和有效数据。伪首部包含IP首部一些字段,其目的是让UDP两次检查数据是否已经正确到达目的地,只是单纯做校验使用。

UDP校验和计算步骤:校验字节强制置0,将三部分数据按2字节, 即16比特,分开分别相加,若如果大于 FFFF 那么把高16位与低16位相加,直到最终结果为16比特数据。将计算结果取反作为 UDP 校验和字节。
例:

(1) 将校验和字段00 92置为00 00:
a9 fe bf lf a9 fe 01 17 00 11 00 28 04 d2 04 d2 00 28 00 00 68 74 74 70 3a 2f 2f 77 7777 2e 63 6d 73 6f 66 74 2e 63 6e 20 51 51 3a 31 30 38 36 35 36 30 30
(2) 以2字节为单位,数据反码求和:
a9fe + bflf + a9fe + 0117 + 0011 + 0028 + 04d2 + 04d2 + 0028 + 0000 + 6874 + 7470+ 3a2f + 2f77 + 7777 + 2e63 + 6d73 + 6f66 + 742e + 636e + 2051 + 513a + 3130 +3836 + 3536 + 3030 = 6 ff67
(3) 将进位(6)加到低 16 位(f67)上:6 + ff67 = ff6d
(4)将 ff6d 取反得: checksum = 0092
上位机通过网口调试助手发送数据给FPGA,FPGA通过以太网接口接收数据并将收到的数据发送给上位机,完成以太网UDP数据的回环。

GMII TO RGMII模块负责将双沿 (DDR) 数据和单沿 (SDR) 数据之间的转换,GMII接收侧的引脚同时连接至 ARP顶层模块和UDP顶层模块,这个两个模块会分别根据ARP协议和UDP协议解析数据。而GMII发送侧引脚只能和ARP顶层模块和UDP顶层模块的其中一个连接,因此以太网控制模块会根据当前接收到的协议类型,选择切换GMII发送侧引脚和ARP顶层模块或者UDP顶层模块连接,并且根据输入的ARP接收的类型,控制ARP顶层模块返回ARP应答信号。ARP顶层模块解析ARP 请求命令,并返回开发板的MAC地址。UDP顶层模块实现了以太网 UDP 数据包的接收、发送以及 CRC 校验的功能。以太网单次会接收到大量数据,因此需要FIFO模块用来缓存数据,由于所使用的GMII接收时钟和GMII发送时钟实际上为同一个时钟,因此这里使用同步FIFO。

UDP模块实现了以太网帧格式和UDP协议功能,由UDP接收模块、UDP发送模块和CRC校验模块组成。

UDP接收模块:接收模块较为简单只需要判断目的MAC地址与开发板MAC地址、目的IP地址与开发板IP地址是否一致。接收模块的解析顺序是:前导码+顺起始界定符→以太网头→IP首部→UDP首部→UDP数据(有效数据)→接收结束。IP数据报一般以32bit为单位,为了和IP数据报格式保持一致,所以要把8位数据转成32位数据,因此接收模块实际上是完成了8位数据转32位数据的功能。
UDP发送模块:发送模块多了IP首部校验和和CRC循环冗余校验的计算,CRC的校验在CRC校验模块里完成。发送模块的发送顺序是前导码+起始界定符→以太网头→IP首部→UDP首部→UDP数据(有效数据)→CRC校验。输入的有效数据为32位数据,GMII接口为8位数据接口,因此发送模块实际上完成的是32位数据转8位数据的功能。
CRC校验模块:CRC校验模块是对UDP发送模块的数据(不包括前导码和起始界定符)做校验,把校验结果值拼在以太网倾格式的FCS字段,如果CRC校验值计算错误或者没有的话,那么电脑网卡会直接丢弃该顿导致收不到数据。CRC32 校验在FPGA实现的原理是LFSR (Linear Feedback Shif Register,线性反馈移位寄存器),其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值。
UDP接收模块按照UDP的数据格式解析数据,并实现将8位用户数据转成32位数据的功能,通过三段式状态机来解析以太网包。

- module udp_rx(
- input clk , //时钟信号
- input rst_n , //复位信号,低电平有效
-
- input gmii_rx_dv , //GMII输入数据有效信号
- input [7:0] gmii_rxd , //GMII输入数据
- output reg rec_pkt_done, //以太网单包数据接收完成信号
- output reg rec_en , //以太网接收的数据使能信号
- output reg [31:0] rec_data , //以太网接收的数据
- output reg [15:0] rec_byte_num //以太网接收的有效字节数 单位:byte
- );
-
- //parameter define
- //开发板MAC地址 00-11-22-33-44-55
- parameter BOARD_MAC = 48'h00_11_22_33_44_55;
- //开发板IP地址 192.168.1.10
- parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
-
- localparam st_idle = 7'b000_0001; //初始状态,等待接收前导码
- localparam st_preamble = 7'b000_0010; //接收前导码状态
- localparam st_eth_head = 7'b000_0100; //接收以太网帧头
- localparam st_ip_head = 7'b000_1000; //接收IP首部
- localparam st_udp_head = 7'b001_0000; //接收UDP首部
- localparam st_rx_data = 7'b010_0000; //接收有效数据
- localparam st_rx_end = 7'b100_0000; //接收结束
-
- localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
- localparam UDP_TYPE = 8'd17 ; //UDP协议类型
-
- //reg define
- reg [6:0] cur_state ;
- reg [6:0] next_state ;
-
- reg skip_en ; //控制状态跳转使能信号
- reg error_en ; //解析错误使能信号
- reg [4:0] cnt ; //解析数据计数器
- reg [47:0] des_mac ; //目的MAC地址
- reg [15:0] eth_type ; //以太网类型
- reg [31:0] des_ip ; //目的IP地址
- reg [5:0] ip_head_byte_num; //IP首部长度
- reg [15:0] udp_byte_num ; //UDP长度
- reg [15:0] data_byte_num ; //数据长度
- reg [15:0] data_cnt ; //有效数据计数
- reg [1:0] rec_en_cnt ; //8bit转32bit计数器
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- //(三段式状态机)同步时序描述状态转移
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n)
- cur_state <= st_idle;
- else
- cur_state <= next_state;
- end
-
- //组合逻辑判断状态转移条件
- always @(*) begin
- next_state = st_idle;
- case(cur_state)
- st_idle : begin //等待接收前导码
- if(skip_en)
- next_state = st_preamble;
- else
- next_state = st_idle;
- end
- st_preamble : begin //接收前导码
- if(skip_en)
- next_state = st_eth_head;
- else if(error_en)
- next_state = st_rx_end;
- else
- next_state = st_preamble;
- end
- st_eth_head : begin //接收以太网帧头
- if(skip_en)
- next_state = st_ip_head;
- else if(error_en)
- next_state = st_rx_end;
- else
- next_state = st_eth_head;
- end
- st_ip_head : begin //接收IP首部
- if(skip_en)
- next_state = st_udp_head;
- else if(error_en)
- next_state = st_rx_end;
- else
- next_state = st_ip_head;
- end
- st_udp_head : begin //接收UDP首部
- if(skip_en)
- next_state = st_rx_data;
- else
- next_state = st_udp_head;
- end
- st_rx_data : begin //接收有效数据
- if(skip_en)
- next_state = st_rx_end;
- else
- next_state = st_rx_data;
- end
- st_rx_end : begin //接收结束
- if(skip_en)
- next_state = st_idle;
- else
- next_state = st_rx_end;
- end
- default : next_state = st_idle;
- endcase
- end
-
- //时序电路描述状态输出,解析以太网数据
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- skip_en <= 1'b0;
- error_en <= 1'b0;
- cnt <= 5'd0;
- des_mac <= 48'd0;
- eth_type <= 16'd0;
- des_ip <= 32'd0;
- ip_head_byte_num <= 6'd0;
- udp_byte_num <= 16'd0;
- data_byte_num <= 16'd0;
- data_cnt <= 16'd0;
- rec_en_cnt <= 2'd0;
- rec_en <= 1'b0;
- rec_data <= 32'd0;
- rec_pkt_done <= 1'b0;
- rec_byte_num <= 16'd0;
- end
- else begin
- skip_en <= 1'b0;
- error_en <= 1'b0;
- rec_en <= 1'b0;
- rec_pkt_done <= 1'b0;
- case(next_state)
- st_idle : begin
- if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))
- skip_en <= 1'b1;
- end
- st_preamble : begin
- if(gmii_rx_dv) begin //解析前导码
- cnt <= cnt + 5'd1;
- if((cnt < 5'd6) && (gmii_rxd != 8'h55)) //7个8'h55
- error_en <= 1'b1;
- else if(cnt==5'd6) begin
- cnt <= 5'd0;
- if(gmii_rxd==8'hd5) //1个8'hd5
- skip_en <= 1'b1;
- else
- error_en <= 1'b1;
- end
- end
- end
- st_eth_head : begin
- if(gmii_rx_dv) begin
- cnt <= cnt + 5'b1;
- if(cnt < 5'd6)
- des_mac <= {des_mac[39:0],gmii_rxd}; //目的MAC地址
- else if(cnt == 5'd12)
- eth_type[15:8] <= gmii_rxd; //以太网协议类型
- else if(cnt == 5'd13) begin
- eth_type[7:0] <= gmii_rxd;
- cnt <= 5'd0;
- //判断MAC地址是否为开发板MAC地址或者公共地址
- if(((des_mac == BOARD_MAC) ||(des_mac == 48'hff_ff_ff_ff_ff_ff))
- && eth_type[15:8] == ETH_TYPE[15:8] && gmii_rxd == ETH_TYPE[7:0])
- skip_en <= 1'b1;
- else
- error_en <= 1'b1;
- end
- end
- end
- st_ip_head : begin
- if(gmii_rx_dv) begin
- cnt <= cnt + 5'd1;
- if(cnt == 5'd0)
- ip_head_byte_num <= {gmii_rxd[3:0],2'd0}; //寄存IP首部长度
- else if(cnt == 5'd9) begin
- if(gmii_rxd != UDP_TYPE) begin
- //如果当前接收的数据不是UDP协议,停止解析数据
- error_en <= 1'b1;
- cnt <= 5'd0;
- end
- end
- else if((cnt >= 5'd16) && (cnt <= 5'd18))
- des_ip <= {des_ip[23:0],gmii_rxd}; //寄存目的IP地址
- else if(cnt == 5'd19) begin
- des_ip <= {des_ip[23:0],gmii_rxd};
- //判断IP地址是否为开发板IP地址
- if((des_ip[23:0] == BOARD_IP[31:8])
- && (gmii_rxd == BOARD_IP[7:0])) begin
- if(cnt == ip_head_byte_num - 1'b1) begin
- skip_en <=1'b1;
- cnt <= 5'd0;
- end
- end
- else begin
- //IP错误,停止解析数据
- error_en <= 1'b1;
- cnt <= 5'd0;
- end
- end
- else if(cnt == ip_head_byte_num - 1'b1) begin
- skip_en <=1'b1; //IP首部解析完成
- cnt <= 5'd0;
- end
- end
- end
- st_udp_head : begin
- if(gmii_rx_dv) begin
- cnt <= cnt + 5'd1;
- if(cnt == 5'd4)
- udp_byte_num[15:8] <= gmii_rxd; //解析UDP字节长度
- else if(cnt == 5'd5)
- udp_byte_num[7:0] <= gmii_rxd;
- else if(cnt == 5'd7) begin
- //有效数据字节长度,(UDP首部8个字节,所以减去8)
- data_byte_num <= udp_byte_num - 16'd8;
- skip_en <= 1'b1;
- cnt <= 5'd0;
- end
- end
- end
- st_rx_data : begin
- //接收数据,转换成32bit
- if(gmii_rx_dv) begin
- data_cnt <= data_cnt + 16'd1;
- rec_en_cnt <= rec_en_cnt + 2'd1;
- if(data_cnt == data_byte_num - 16'd1) begin
- skip_en <= 1'b1; //有效数据接收完成
- data_cnt <= 16'd0;
- rec_en_cnt <= 2'd0;
- rec_pkt_done <= 1'b1;
- rec_en <= 1'b1;
- rec_byte_num <= data_byte_num;
- end
- //先收到的数据放在了rec_data的高位,所以当数据不是4的倍数时,
- //低位数据为无效数据,可根据有效字节数来判断(rec_byte_num)
- if(rec_en_cnt == 2'd0)
- rec_data[31:24] <= gmii_rxd;
- else if(rec_en_cnt == 2'd1)
- rec_data[23:16] <= gmii_rxd;
- else if(rec_en_cnt == 2'd2)
- rec_data[15:8] <= gmii_rxd;
- else if(rec_en_cnt==2'd3) begin
- rec_en <= 1'b1;
- rec_data[7:0] <= gmii_rxd;
- end
- end
- end
- st_rx_end : begin //单包数据接收完成
- if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
- skip_en <= 1'b1;
- end
- default : ;
- endcase
- end
- end
-
- endmodule

- module udp_tx(
- input clk , //时钟信号
- input rst_n , //复位信号,低电平有效
-
- input tx_start_en, //以太网开始发送信号
- input [31:0] tx_data , //以太网待发送数据
- input [15:0] tx_byte_num, //以太网发送的有效字节数
- input [47:0] des_mac , //发送的目标MAC地址
- input [31:0] des_ip , //发送的目标IP地址
- input [31:0] crc_data , //CRC校验数据
- input [7:0] crc_next , //CRC下次校验完成数据
- output reg tx_done , //以太网发送完成信号
- output reg tx_req , //读数据请求信号
- output reg gmii_tx_en , //GMII输出数据有效信号
- output reg [7:0] gmii_txd , //GMII输出数据
- output reg crc_en , //CRC开始校验使能
- output reg crc_clr //CRC数据复位信号
- );
-
- //parameter define
- //开发板MAC地址 00-11-22-33-44-55
- parameter BOARD_MAC = 48'h00_11_22_33_44_55;
- //开发板IP地址 192.168.1.10
- parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
- //目的MAC地址 ff_ff_ff_ff_ff_ff
- parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
- //目的IP地址 192.168.1.102
- parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
-
- localparam st_idle = 7'b000_0001; //初始状态,等待开始发送信号
- localparam st_check_sum = 7'b000_0010; //IP首部校验和
- localparam st_preamble = 7'b000_0100; //发送前导码+帧起始界定符
- localparam st_eth_head = 7'b000_1000; //发送以太网帧头
- localparam st_ip_head = 7'b001_0000; //发送IP首部+UDP首部
- localparam st_tx_data = 7'b010_0000; //发送数据
- localparam st_crc = 7'b100_0000; //发送CRC校验值
-
- localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
- //以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
- //所以数据至少46-20-8=18个字节
- localparam MIN_DATA_NUM = 16'd18 ;
- localparam UDP_TYPE = 8'd17 ; //UDP协议类型
-
- //reg define
- reg [6:0] cur_state ;
- reg [6:0] next_state ;
-
- reg [7:0] preamble[7:0] ; //前导码
- reg [7:0] eth_head[13:0] ; //以太网首部
- reg [31:0] ip_head[6:0] ; //IP首部 + UDP首部
-
- reg start_en_d0 ;
- reg start_en_d1 ;
- reg [15:0] tx_data_num ; //发送的有效数据字节个数
- reg [15:0] total_num ; //总字节数
- reg trig_tx_en ;
- reg [15:0] udp_num ; //UDP字节数
- reg skip_en ; //控制状态跳转使能信号
- reg [4:0] cnt ;
- reg [31:0] check_buffer ; //首部校验和
- reg [1:0] tx_byte_sel ; //32位数据转8位数据计数器
- reg [15:0] data_cnt ; //发送数据个数计数器
- reg tx_done_t ;
- reg [4:0] real_add_cnt ; //以太网数据实际多发的字节数
-
- //wire define
- wire pos_start_en ;//开始发送数据上升沿
- wire [15:0] real_tx_data_num;//实际发送的字节数(以太网最少字节要求)
- //*****************************************************
- //** main code
- //*****************************************************
-
- assign pos_start_en = (~start_en_d1) & start_en_d0;
- assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM)
- ? tx_data_num : MIN_DATA_NUM;
-
- //采tx_start_en的上升沿
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- start_en_d0 <= 1'b0;
- start_en_d1 <= 1'b0;
- end
- else begin
- start_en_d0 <= tx_start_en;
- start_en_d1 <= start_en_d0;
- end
- end
-
- //寄存数据有效字节
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- tx_data_num <= 16'd0;
- total_num <= 16'd0;
- udp_num <= 16'd0;
- end
- else begin
- if(pos_start_en && cur_state==st_idle) begin
- //数据长度
- tx_data_num <= tx_byte_num;
- //UDP长度:UDP首部长度 + 有效数据
- udp_num <= tx_byte_num + 16'd8;
- //IP长度:IP首部长度 + UDP首部 + 有效数据
- total_num <= tx_byte_num + 16'd20 + 16'd8;
- end
- end
- end
-
- //触发发送信号
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n)
- trig_tx_en <= 1'b0;
- else
- trig_tx_en <= pos_start_en;
-
- end
-
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n)
- cur_state <= st_idle;
- else
- cur_state <= next_state;
- end
-
- always @(*) begin
- next_state = st_idle;
- case(cur_state)
- st_idle : begin //等待发送数据
- if(skip_en)
- next_state = st_check_sum;
- else
- next_state = st_idle;
- end
- st_check_sum: begin //IP首部校验
- if(skip_en)
- next_state = st_preamble;
- else
- next_state = st_check_sum;
- end
- st_preamble : begin //发送前导码+帧起始界定符
- if(skip_en)
- next_state = st_eth_head;
- else
- next_state = st_preamble;
- end
- st_eth_head : begin //发送以太网首部
- if(skip_en)
- next_state = st_ip_head;
- else
- next_state = st_eth_head;
- end
- st_ip_head : begin //发送IP首部+UDP首部
- if(skip_en)
- next_state = st_tx_data;
- else
- next_state = st_ip_head;
- end
- st_tx_data : begin //发送数据
- if(skip_en)
- next_state = st_crc;
- else
- next_state = st_tx_data;
- end
- st_crc: begin //发送CRC校验值
- if(skip_en)
- next_state = st_idle;
- else
- next_state = st_crc;
- end
- default : next_state = st_idle;
- endcase
- end
-
- //发送数据
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- skip_en <= 1'b0;
- cnt <= 5'd0;
- check_buffer <= 32'd0;
- ip_head[1][31:16] <= 16'd0;
- tx_byte_sel <= 2'b0;
- crc_en <= 1'b0;
- gmii_tx_en <= 1'b0;
- gmii_txd <= 8'd0;
- tx_req <= 1'b0;
- tx_done_t <= 1'b0;
- data_cnt <= 16'd0;
- real_add_cnt <= 5'd0;
- //初始化数组
- //前导码 7个8'h55 + 1个8'hd5
- preamble[0] <= 8'h55;
- preamble[1] <= 8'h55;
- preamble[2] <= 8'h55;
- preamble[3] <= 8'h55;
- preamble[4] <= 8'h55;
- preamble[5] <= 8'h55;
- preamble[6] <= 8'h55;
- preamble[7] <= 8'hd5;
- //目的MAC地址
- eth_head[0] <= DES_MAC[47:40];
- eth_head[1] <= DES_MAC[39:32];
- eth_head[2] <= DES_MAC[31:24];
- eth_head[3] <= DES_MAC[23:16];
- eth_head[4] <= DES_MAC[15:8];
- eth_head[5] <= DES_MAC[7:0];
- //源MAC地址
- eth_head[6] <= BOARD_MAC[47:40];
- eth_head[7] <= BOARD_MAC[39:32];
- eth_head[8] <= BOARD_MAC[31:24];
- eth_head[9] <= BOARD_MAC[23:16];
- eth_head[10] <= BOARD_MAC[15:8];
- eth_head[11] <= BOARD_MAC[7:0];
- //以太网类型
- eth_head[12] <= ETH_TYPE[15:8];
- eth_head[13] <= ETH_TYPE[7:0];
- end
- else begin
- skip_en <= 1'b0;
- tx_req <= 1'b0;
- crc_en <= 1'b0;
- gmii_tx_en <= 1'b0;
- tx_done_t <= 1'b0;
- case(next_state)
- st_idle : begin
- if(trig_tx_en) begin
- skip_en <= 1'b1;
- //版本号:4 首部长度:5(单位:32bit,20byte/4=5)
- ip_head[0] <= {8'h45,8'h00,total_num};
- //16位标识,每次发送累加1
- ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
- //bit[15:13]: 010表示不分片
- ip_head[1][15:0] <= 16'h4000;
- //协议:17(udp)
- ip_head[2] <= {8'h40,UDP_TYPE,16'h0};
- //源IP地址
- ip_head[3] <= BOARD_IP;
- //目的IP地址
- if(des_ip != 32'd0)
- ip_head[4] <= des_ip;
- else
- ip_head[4] <= DES_IP;
- //16位源端口号:1234 16位目的端口号:1234
- ip_head[5] <= {16'd1234,16'd1234};
- //16位udp长度,16位udp校验和
- ip_head[6] <= {udp_num,16'h0000};
- //更新MAC地址
- if(des_mac != 48'b0) begin
- //目的MAC地址
- eth_head[0] <= des_mac[47:40];
- eth_head[1] <= des_mac[39:32];
- eth_head[2] <= des_mac[31:24];
- eth_head[3] <= des_mac[23:16];
- eth_head[4] <= des_mac[15:8];
- eth_head[5] <= des_mac[7:0];
- end
- end
- end
- st_check_sum: begin //IP首部校验
- cnt <= cnt + 5'd1;
- if(cnt == 5'd0) begin
- check_buffer <= ip_head[0][31:16] + ip_head[0][15:0]
- + ip_head[1][31:16] + ip_head[1][15:0]
- + ip_head[2][31:16] + ip_head[2][15:0]
- + ip_head[3][31:16] + ip_head[3][15:0]
- + ip_head[4][31:16] + ip_head[4][15:0];
- end
- else if(cnt == 5'd1) //可能出现进位,累加一次
- check_buffer <= check_buffer[31:16] + check_buffer[15:0];
- else if(cnt == 5'd2) begin //可能再次出现进位,累加一次
- check_buffer <= check_buffer[31:16] + check_buffer[15:0];
- end
- else if(cnt == 5'd3) begin //按位取反
- skip_en <= 1'b1;
- cnt <= 5'd0;
- ip_head[2][15:0] <= ~check_buffer[15:0];
- end
- end
- st_preamble : begin //发送前导码+帧起始界定符
- gmii_tx_en <= 1'b1;
- gmii_txd <= preamble[cnt];
- if(cnt == 5'd7) begin
- skip_en <= 1'b1;
- cnt <= 5'd0;
- end
- else
- cnt <= cnt + 5'd1;
- end
- st_eth_head : begin //发送以太网首部
- gmii_tx_en <= 1'b1;
- crc_en <= 1'b1;
- gmii_txd <= eth_head[cnt];
- if (cnt == 5'd13) begin
- skip_en <= 1'b1;
- cnt <= 5'd0;
- end
- else
- cnt <= cnt + 5'd1;
- end
- st_ip_head : begin //发送IP首部 + UDP首部
- crc_en <= 1'b1;
- gmii_tx_en <= 1'b1;
- tx_byte_sel <= tx_byte_sel + 2'd1;
- if(tx_byte_sel == 2'd0)
- gmii_txd <= ip_head[cnt][31:24];
- else if(tx_byte_sel == 2'd1)
- gmii_txd <= ip_head[cnt][23:16];
- else if(tx_byte_sel == 2'd2) begin
- gmii_txd <= ip_head[cnt][15:8];
- if(cnt == 5'd6) begin
- //提前读请求数据,等待数据有效时发送
- tx_req <= 1'b1;
- end
- end
- else if(tx_byte_sel == 2'd3) begin
- gmii_txd <= ip_head[cnt][7:0];
- if(cnt == 5'd6) begin
- skip_en <= 1'b1;
- cnt <= 5'd0;
- end
- else
- cnt <= cnt + 5'd1;
- end
- end
- st_tx_data : begin //发送数据
- crc_en <= 1'b1;
- gmii_tx_en <= 1'b1;
- tx_byte_sel <= tx_byte_sel + 2'd1;
- if(tx_byte_sel == 1'b0)
- gmii_txd <= tx_data[31:24];
- else if(tx_byte_sel == 2'd1)
- gmii_txd <= tx_data[23:16];
- else if(tx_byte_sel == 2'd2) begin
- gmii_txd <= tx_data[15:8];
- if(data_cnt != tx_data_num - 16'd2)
- tx_req <= 1'b1;
- end
- else if(tx_byte_sel == 2'd3)
- gmii_txd <= tx_data[7:0];
- if(data_cnt < tx_data_num - 16'd1)
- data_cnt <= data_cnt + 16'd1;
- else if(data_cnt == tx_data_num - 16'd1)begin
- //如果发送的有效数据少于18个字节,在后面填补充位
- //填充的值为0
- tx_req <= 1'b0;
- if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)
- real_add_cnt <= real_add_cnt + 5'd1;
- else begin
- skip_en <= 1'b1;
- data_cnt <= 16'd0;
- real_add_cnt <= 5'd0;
- tx_byte_sel <= 2'd0;
- end
- if(real_add_cnt > 0) begin
- gmii_txd <= 8'd0;
- end
- end
- end
- st_crc : begin //发送CRC校验值
- gmii_tx_en <= 1'b1;
- tx_byte_sel <= tx_byte_sel + 2'd1;
- if(tx_byte_sel == 2'd0)
- gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
- ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
- else if(tx_byte_sel == 2'd1)
- gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],
- ~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};
- else if(tx_byte_sel == 2'd2) begin
- gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],
- ~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};
- end
- else if(tx_byte_sel == 2'd3) begin
- gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
- ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
- tx_done_t <= 1'b1;
- skip_en <= 1'b1;
- end
- end
- default :;
- endcase
- end
- end
-
- //发送完成信号及crc值复位信号
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- tx_done <= 1'b0;
- crc_clr <= 1'b0;
- end
- else begin
- tx_done <= tx_done_t;
- crc_clr <= tx_done_t;
- end
- end
-
- endmodule
- module eth_ctrl(
- input clk , //系统时钟
- input rst_n , //系统复位信号,低电平有效
- //ARP相关端口信号
- input arp_rx_done, //ARP接收完成信号
- input arp_rx_type, //ARP接收类型 0:请求 1:应答
- output reg arp_tx_en, //ARP发送使能信号
- output arp_tx_type, //ARP发送类型 0:请求 1:应答
- input arp_tx_done, //ARP发送完成信号
- input arp_gmii_tx_en, //ARP GMII输出数据有效信号
- input [7:0] arp_gmii_txd, //ARP GMII输出数据
- //UDP相关端口信号
- input udp_tx_start_en,//UDP开始发送信号
- input udp_tx_done, //UDP发送完成信号
- input udp_gmii_tx_en, //UDP GMII输出数据有效信号
- input [7:0] udp_gmii_txd, //UDP GMII输出数据
- //GMII发送引脚
- output gmii_tx_en, //GMII输出数据有效信号
- output [7:0] gmii_txd //UDP GMII输出数据
- );
-
- //reg define
- reg protocol_sw; //协议切换信号
- reg udp_tx_busy; //UDP正在发送数据标志信号
- reg arp_rx_flag; //接收到ARP请求信号的标志
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- assign arp_tx_type = 1'b1; //ARP发送类型固定为ARP应答
- assign gmii_tx_en = protocol_sw ? udp_gmii_tx_en : arp_gmii_tx_en;
- assign gmii_txd = protocol_sw ? udp_gmii_txd : arp_gmii_txd;
-
- //控制UDP发送忙信号
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n)
- udp_tx_busy <= 1'b0;
- else if(udp_tx_start_en)
- udp_tx_busy <= 1'b1;
- else if(udp_tx_done)
- udp_tx_busy <= 1'b0;
- end
-
- //控制接收到ARP请求信号的标志
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n)
- arp_rx_flag <= 1'b0;
- else if(arp_rx_done && (arp_rx_type == 1'b0))
- arp_rx_flag <= 1'b1;
- else if(protocol_sw == 1'b0)
- arp_rx_flag <= 1'b0;
- end
-
- //控制protocol_sw和arp_tx_en信号
- always @(posedge clk or negedge rst_n) begin
- if(!rst_n) begin
- protocol_sw <= 1'b0;
- arp_tx_en <= 1'b0;
- end
- else begin
- arp_tx_en <= 1'b0;
- if(udp_tx_start_en)
- protocol_sw <= 1'b1;
- else if(arp_rx_flag && (udp_tx_busy == 1'b0)) begin
- protocol_sw <= 1'b0;
- arp_tx_en <= 1'b1;
- end
- end
- end
-
-
- endmodule
其他模块和以太网ARP测试实验一样