前面分别实现了ARP协议和ICMP协议,但这俩协议都不能进行数据的传输,如果想要用以太网传输数据的话还需要实现UDP协议或者TCP协议,关于二者的差别主要包括以下几点:
1.连接性
TCP是面向连接的协议,它在传输数据之前需要建立连接(三次握手),并在数据传输完成后关闭连接(四次挥手)。这种连接是全双工的,即数据可以在两个方向上同时传输。
UDP则是无连接的协议,它不需要建立或关闭连接,发送方可以直接发送数据报,接收方也可以随时接收数据报。
TCP提供可靠的传输服务。它使用确认和重传机制来确保数据正确、完整、有序地到达目的地。TCP还通过流量控制和拥塞控制机制来防止网络过载。
UDP则提供不可靠的传输服务。它不检查数据包的顺序、错误或重传。如果数据包在传输过程中丢失或损坏,UDP不会采取任何补救措施。因此,UDP通常用于对实时性要求较高、但对数据可靠性要求不高的应用(如视频流、音频流、实时游戏等)
TCP的头部开销相对较大,包含更多的控制信息,如序列号、确认号、窗口大小等。
UDP的头部开销较小,只包含必要的字段,如源端口、目的端口、长度和校验和。
由于TCP需要建立连接、确认数据、处理重传等,所以其传输效率相对较低。但这也使得TCP在需要可靠传输的场景下表现更好。
UDP则不需要这些额外的步骤,因为其传输效率更高。但这也意味着UDP在传输过程中可能会丢失数据。
TCP通常用于需要可靠传输的场景,如文件存储、电子邮件、远程登录等。
UDP则常用于对实时性要求较高、但对数据可靠性要求不高的场景,如视频流、音频流、DNS查询、VoIP(网络电话)等。
TCP具有流量控制和拥塞控制机制,可以根据网络状况动态调整发送速率,以防止网络拥塞和丢包。
UDP则没有这些机制,它只负责将数据报从源端发送到目的端,则不关心网络状况和数据传输质量。
TCP通常用于一对一的通信,即一个TCP连接只能有一个发送方和一个接收方。
UDP则支持一对多、多对一和多对多的通信模式,可以实现广播和组播功能。
总而言之,UDP的优点是协议简单,传输速度快,不用进行握手,一般用于进行视频数据的传输。
UDP协议结构图如下所示:
UDP首部组成如下:
UDP首部主要包括源端口号、目的端口号、UDP总长度和校验和。
源端口号:2个字节的发送端端口号,用于区分不同的发送端口。
目的端口号:2个字节的接收端端口号。
UDP长度:UDP首部和数据段的长度,单位字节,对于接收方来说该长度其实作用不大,因为UDP数据段的长度可以通过IP首部的总长度和IP首部长度计算出来。
UDP校验和:计算方式与IP首部校验和一致,需要对UDP伪首部、UDP首部、UDP数据进行校验。伪首部包括源IP地址、目的IP地址、协议类型、UDP长度。
UDP数据组成如下图所示:
代码实现如下所示:
发送部分:
`timescale 1ns / 1psmodule udp_tx(input clk , //系统时钟input rst_n , //系统复位//inputinput tx_start_en , //发送使能信号input [15:0] tx_byte_num , //发送有效字节数input [7:0] tx_data , //发送数据input [47:0] des_mac , //源MAC地址input [31:0] des_ip , //源ip地址input [31:0] crc_data , //CRC校验数据input [7:0] crc_next , //CRC下次校验数据//ooutputoutput 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清零信号
);//开发板MAC地址 00-11-22-33-44-55parameter BOARD_MAC = 48'h00_11_22_33_44_55;//开发板IP地址 192.168.1.123 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd123}; //目的MAC地址 ff_ff_ff_ff_ff_ffparameter 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 IDLE = 7'b000_0001 ; //初始化状态localparam CHECK_SUM = 7'b000_0010 ; //校验和localparam PREAMBLE = 7'b000_0100 ; //发送前导码+SFDlocalparam ETH_HEAD = 7'b000_1000 ; //发送以太网帧头localparam IP_HEAD = 7'b001_0000 ; //发送ip首部localparam TX_DATA = 7'b010_0000 ; //发送数据localparam CRC = 7'b100_0000 ; //CRC校验localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议localparam MIN_DATA_NUM= 16'd18 ; //以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节,所以数据至少46-20-8=18个字节reg [6:0] cur_state ; //现态reg [6:0] next_state ; //次态reg state_en ; //状态跳转使能reg [7:0] preamble[7:0] ; //前导码+帧起始界定符reg [7:0] eth_head[13:0] ; //以太网帧头reg [31:0] ip_head[6:0] ; //ip首部reg [4:0] cnt ; //数据发送计数器reg start_en_r0 ; //暂存发送使能信号reg start_en_r1 ;reg start_en_r2 ;reg [15:0] tx_data_num ; //发送有效数据字节个数reg [15:0] total_num ; //总字节个数reg trig_tx_en ; //发送使能触发信号reg [15:0] udp_num ; //udp字节数reg [31:0] check_buffer ; //校验和reg [1:0] tx_bit_sel ; //reg [15:0] data_cnt ; //ip首部发送数据计数器reg tx_done_t ; //发送结束信号寄存器reg [4:0] real_add_cnt ; //实际多发字节数wire posedge_start ; //开始发送信号上升沿wire [15:0] real_tx_data_num; //实际发送字节数据 assign posedge_start = (~start_en_r2) && start_en_r1;assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM) ? tx_data_num : MIN_DATA_NUM; always @(posedge clk or negedge rst_n) beginif(!rst_n)beginstart_en_r0 <= 1'b0;start_en_r1 <= 1'b0;start_en_r2 <= 1'b0;endelse beginstart_en_r0 <= tx_start_en;start_en_r1 <= start_en_r0;start_en_r2 <= start_en_r1;endend//寄存数据有效字节always @(posedge clk or negedge rst_n) beginif(!rst_n)begintx_data_num <= 16'd0;total_num <= 16'd0;udp_num <= 16'd0;endelse beginif(posedge_start && (cur_state == IDLE))begintx_data_num <= tx_byte_num;total_num <= tx_byte_num + 16'd28;udp_num <= tx_byte_num + 16'd8;endelse;endend//发送触发信号always @(posedge clk or negedge rst_n) beginif(!rst_n)trig_tx_en <= 1'b0;elsetrig_tx_en <= posedge_start;end//三段式状态机第一段always @(posedge clk or negedge rst_n) beginif(!rst_n)cur_state <= IDLE;elsecur_state <= next_state;end//三段式状态第二段always @(*) beginnext_state = IDLE;case (cur_state)IDLE:beginif(state_en)next_state = CHECK_SUM;elsenext_state = IDLE; end CHECK_SUM:beginif(state_en)next_state = PREAMBLE;elsenext_state = CHECK_SUM;endPREAMBLE:beginif(state_en)next_state = ETH_HEAD;elsenext_state = PREAMBLE; endETH_HEAD:beginif(state_en)next_state = IP_HEAD;elsenext_state = ETH_HEAD;endIP_HEAD:beginif(state_en)next_state = TX_DATA;elsenext_state = IP_HEAD;endTX_DATA:beginif(state_en)next_state = CRC;elsenext_state = TX_DATA;endCRC:beginif(state_en)next_state = IDLE;elsenext_state = CRC;enddefault: next_state = IDLE;endcaseend//三段式状态机第三段always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt <= 5'd0;check_buffer <= 32'd0;state_en <= 1'b0;ip_head[1][31:16] <= 16'd0;tx_bit_sel <= 2'd0;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;//初始化数组//前导码+帧起始界定符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];endelse begincrc_en <= 1'b0;gmii_tx_en <= 1'b0;tx_done_t <= 1'b0;state_en <= 1'b0;case (next_state)IDLE:beginif(trig_tx_en)beginstate_en <= 1'b1;//版本号:4 首部长度:5ip_head[0] <= {8'h45,8'h00,total_num};//16位标识,每次发送累加一ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;//ip_head[1][15:0] <= 16'h4000;//表示udp协议ip_head[2] <= {8'h40,8'd17,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;//udp端口号ip_head[5] <= {16'd1234,16'd1234};//udp校验长度与udp校验和ip_head[6] <= {udp_num,16'h0000};if(des_mac != 48'd0)begineth_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];endelse;endelse;end CHECK_SUM:begincnt <= cnt + 1'b1;if(cnt == 5'd0)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];else if(cnt == 5'd1)check_buffer <= check_buffer[31:16] + check_buffer[15:0];else if(cnt == 5'd2)check_buffer <= check_buffer[31:16] + check_buffer[15:0];else if(cnt == 5'd3)begincnt <= 5'd0;ip_head[2][15:0] <= ~check_buffer[15:0];state_en <= 1'b1;endelse;endPREAMBLE:begingmii_tx_en <= 1'b1;gmii_txd <= preamble[cnt];if(cnt == 5'd7)begincnt <= 5'd0;state_en <= 1'b1;endelsecnt <= cnt + 1'b1;endETH_HEAD:begingmii_tx_en <= 1'b1;gmii_txd <= eth_head[cnt];crc_en <= 1'b1;if(cnt == 5'd13)begincnt <= 5'd0;state_en <= 1'b1;endelsecnt <= cnt + 1'b1;endIP_HEAD:begintx_bit_sel <= tx_bit_sel + 1'b1;gmii_tx_en <= 1'b1;crc_en <= 1'b1;if(tx_bit_sel == 2'd0)gmii_txd <= ip_head[cnt][31:24];else if(tx_bit_sel == 2'd1)gmii_txd <= ip_head[cnt][23:16];else if(tx_bit_sel == 2'd2)begingmii_txd <= ip_head[cnt][15:8];if(cnt == 5'd6)tx_req <= 1'b1;//提前读请求数据else;endelse if(tx_bit_sel == 2'd3)begingmii_txd <= ip_head[cnt][7:0];if(cnt == 5'd6)begincnt <= 5'd0;state_en <= 1'b1;endelsecnt <= cnt + 1'b1;endendTX_DATA:begincrc_en <= 1'b1;gmii_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 1'b1;gmii_txd <= tx_data;if(data_cnt < tx_data_num - 1'b1)data_cnt <= data_cnt + 1'b1;else if(data_cnt == tx_data_num - 1'b1)if(data_cnt + real_add_cnt < real_tx_data_num - 1'b1)real_add_cnt <= real_add_cnt + 1'b1;//发送有效数据字节少于18位则在后面补充最后一次发送的有效数据else beginstate_en <= 1'b1;real_add_cnt <= 16'd0;data_cnt <= 16'd0;tx_bit_sel <= 2'd0;endelse;if(data_cnt == tx_data_num - 16'd2)tx_req <= 1'b0;else;endCRC:begingmii_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 1'b1;tx_req <= 1'b0;if(tx_bit_sel == 3'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_bit_sel == 3'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_bit_sel == 3'd2) begingmii_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]}; endelse if(tx_bit_sel == 3'd3) begingmii_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;state_en <= 1'b1;end else ;enddefault:;endcaseendend//发送完成信号与CRC清零信号always @(posedge clk or negedge rst_n) beginif(!rst_n)begintx_done <= 1'b0;crc_clr <= 1'b0;endelse begintx_done <= tx_done_t;crc_clr <= tx_done_t;endendendmodule
仿真结果:
检测到发送开始信号上升沿,开始发送前导码和帧起始界定符。
数据发送部分。
接收部分:
`timescale 1ns / 1psmodule udp_rx(input clk , //系统时钟input rst_n , //系统复位//inputinput gmii_rx_dv , //接收数据有效input [7:0] gmii_rxd , //接收数据output reg rec_pkt_done, //以太网接收单包数据完成output reg rec_en , //接收使能信号output reg [7:0] rec_data , output reg [15:0] rec_byte_num //以太网接收有效字节数
);parameter BOARD_MAC = 48'h00_11_22_33_44_55 ; //开发板MAC地址 00-11-22-33-44-55parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10} ; //开发板IP地址 192.168.1.10 localparam IDLE = 7'b000_0001 ; //空闲状态localparam PREAMBLE = 7'b000_0010 ; //前导码+帧起始界定符localparam ETH_HEAD = 7'b000_0100 ; //接收以太网帧头localparam IP_HEAD = 7'b000_1000 ; //接收ip首部localparam UDP_HEAD = 7'b001_0000 ; //接收udp首部localparam RX_DATA = 7'b010_0000 ; //接收数据localparam RX_DONE = 7'b100_0000 ; //接收结束localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议localparam UDP_TYPE = 8'd17 ; //UDP协议reg [6:0] cur_state ; //现态reg [6:0] next_state ; //次态reg [4:0] cnt ; //接收数据计数器reg state_en ; //状态跳转使能reg error_flag ; //错误指示信号reg [47:0] des_mac ; //目的MAC地址reg [31:0] des_ip ; //目的ip地址reg [15:0] eth_type ; //以太网类型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 ; //ip首部发送计数//三段式状态机第一段always @(posedge clk or negedge rst_n) beginif(!rst_n)cur_state <= IDLE;elsecur_state <= next_state;end//三段式状态机第二段always @(*) beginnext_state = IDLE;case (cur_state)IDLE:beginif(state_en)next_state = PREAMBLE;elsenext_state = IDLE; end PREAMBLE:beginif(state_en)next_state = ETH_HEAD;else if(error_flag)next_state = RX_DONE;elsenext_state = PREAMBLE; end ETH_HEAD:beginif(state_en)next_state = IP_HEAD;else if(error_flag)next_state = RX_DONE;elsenext_state = ETH_HEAD; end IP_HEAD:beginif(state_en)next_state = UDP_HEAD;else if(error_flag)next_state = RX_DONE;elsenext_state = IP_HEAD; end UDP_HEAD:beginif(state_en)next_state = RX_DATA;elsenext_state = UDP_HEAD; endRX_DATA:beginif(state_en)next_state = RX_DONE;elsenext_state = RX_DATA; end RX_DONE:beginif(state_en)next_state = IDLE;elsenext_state = RX_DONE; end default: next_state = IDLE;endcaseend//三段式状态机第三段always @(posedge clk or negedge rst_n) beginif(!rst_n)beginstate_en <= 1'b0;error_flag <= 1'b0;cnt <= 5'd0;des_mac <= 48'd0;des_ip <= 32'd0; eth_type <= 16'd0;ip_head_byte_num <= 6'd0;udp_byte_num <= 16'd0;data_byte_num <= 16'd0;data_cnt <= 16'd0;rec_data <= 32'd0;rec_pkt_done <= 1'b0;rec_en <= 1'b0;rec_byte_num <= 16'd0;endelse beginstate_en <= 1'b0;rec_pkt_done <= 1'b0;error_flag <= 1'b0;case (next_state)IDLE:begin if(gmii_rx_dv && (gmii_rxd == 8'h55))state_en <= 1'b1;else;endPREAMBLE:beginif(gmii_rx_dv)begincnt <= cnt + 1'b1;if((cnt < 5'd6) && (gmii_rxd != 8'h55))error_flag <= 1'b1;else if(cnt == 5'd6)begincnt <= 5'd0;if(gmii_rxd != 8'hd5)error_flag <= 1'b1;elsestate_en <= 1'b1;endelse;endendETH_HEAD:beginif(gmii_rx_dv)begincnt <= cnt + 1'b1;if(cnt < 5'd6)des_mac <= {des_mac[39:0],gmii_rxd};else if(cnt == 5'd12)eth_type[15:8] <= gmii_rxd;else if(cnt == 5'd13)begineth_type[7:0] <= gmii_rxd;cnt <= 5'd0;//判断目的ip地址与开发板的ip地址是否相同或者为广播地址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])) state_en <= 1'b1; elseerror_flag <= 1'b1;endelse;endelse;endIP_HEAD:beginif(gmii_rx_dv)begincnt <= cnt + 1'b1;if(cnt == 5'd0)ip_head_byte_num <= {gmii_rxd[3:0],2'b00};else if(cnt == 5'd9)if(gmii_rxd != UDP_TYPE)beginerror_flag <= 1'b1;cnt <= 5'd0;endelse;else if((cnt >= 5'd16) && (cnt <= 5'd18))des_ip <= {des_ip[23:0],gmii_rxd};else if(cnt == 5'd19)begindes_ip <= {des_ip[23:0],gmii_rxd};if((des_ip[23:0] == BOARD_IP[31:8]) && (gmii_rxd == BOARD_IP[7:0]))begincnt <= 5'd0;state_en <= 1'b1;endelse begincnt <= 5'd0;error_flag <= 1'b1;endendelse;endelse;endUDP_HEAD:beginif(gmii_rx_dv)begincnt <= cnt + 1'b1;if(cnt == 5'd4)udp_byte_num[15:8] <= gmii_rxd;else if(cnt == 5'd5)udp_byte_num[7:0] <= gmii_rxd;//解析UDP字节长度else if(cnt == 5'd7)begindata_byte_num <= udp_byte_num - 16'd8;//有效数据字节长度,UDP首部长度为8所以减去8state_en <= 1'b1;cnt <= 5'd0;endelse;endelse;endRX_DATA:beginif(gmii_rx_dv)begindata_cnt <= data_cnt + 1'b1;rec_data <= gmii_rxd;rec_en <= 1'b1;if(data_cnt == data_byte_num - 16'd1)begin//有效字节接收完成state_en <= 1'b1;rec_pkt_done <= 1'b1;data_cnt <= 16'd0;rec_byte_num <= data_byte_num;endelse;endelse;endRX_DONE:beginrec_en <= 1'b0;if((gmii_rx_dv == 1'b0) && (state_en == 1'b0))state_en <= 1'b1;else;enddefault:;endcaseendend
endmodule
仿真结果:
上板测试:
基于FPGA的UDP实现(包含源工程文件)_fpga udp-CSDN博客
TCP是什么、UDP是什么,它们有什么区别-CSDN博客