FPGA - 以太网UDP通信(四)

发布于:2024-04-18 ⋅ 阅读:(30) ⋅ 点赞:(0)

 一,引言


前文链接:FPGA - 以太网UDP通信(一)

                  FPGA - 以太网UDP通信(二)

                  FPGA - 以太网UDP通信(三)

在以上文章中介绍了以太网简介,以太网UDP通信硬件结构,以及PHY芯片RGMII接口-GMII接口转换逻辑,以及数据链路层(MAC层)接受发送逻辑,IP层接受发送逻辑。接下来介绍UDP层接收发送逻辑。

二,以太网UDP通信结构框图

在之前文章中已经介绍了UDP通信结构框图,如下:

三,UDP层数据帧

UDP(User Datagram Protocol),用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP提供了无连接通信,且不对传送数据包进行可靠性保证,适合于一次传输少量数据,UDP传输的可靠性由应用层负责。

UDP报文没有可靠性保证顺序保证流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。 UDP在IP报文中的位置如下图所示:

UDP的报文格式

UDP首部有8个字节,由4个字段构成,每个字段都是两个字节。

------------------------------------------------------------UDP首部---------------------------------------------------------

源端口       源端口号,需要对方回信时选用,不需要时全部置0。

目的端口  目的端口号,在终点交付报文的时候需要用到。

长度         UDP的数据报的长度(包括首部和数据)其最小值为8字节(只有首部),最大值为65535字节。

校验和      检测UDP数据报在传输中是否有错,有错则丢弃。
该字段是可选的,当源主机不想计算校验和,则直接令该字段全为0。

---------------------------------------------------------------------------------------------------------------------------------

四,UDP层代码设计

UDP层的接受和发送逻辑和mac层以及IP层的接受发送逻辑类似,和之前一样只需要在udp_receive中要完成数据解包校验,在udp_send中要完成数据组包,就完成了整个udp_layer的设计。

udp_receive模块模块

`timescale 1ns / 1ps
module udp_receive#(
	parameter 				LOCAL_PORT = 16'h0
	)(
	input 							clk					,
	input 							reset 				,

	/*-------------ip_receive交互信号----------------*/
	input     						udp_rx_data_vld 	,
	input    						udp_rx_data_last 	,
	input     [7 :0]				udp_rx_data 		,
	input     [15:0]				udp_rx_length 		,

	/*-----------------用户端接受信号------------------*/	
	output reg  					app_rx_data_vld 	,
	output reg 						app_rx_data_last 	,
	output reg  [7 :0]				app_rx_data 		,
	output reg  [15:0]				app_rx_length 		
    );  

    reg [10:0] 					rx_cnt 			;
    reg [15:0] 					rx_target_port 	;


    /*------------------------------------------*\
                 cnt
	\*------------------------------------------*/
	always @(posedge clk ) begin 
		if(reset) 
			rx_cnt <= 0;
		else if(udp_rx_data_vld)
			rx_cnt <= rx_cnt + 1 ;
		else
			rx_cnt <= 0;
	end

	always @(posedge clk ) begin
		if (rx_cnt == 2 ||  rx_cnt == 3) 
			rx_target_port <= {rx_target_port[7:0],udp_rx_data};
		else 
			rx_target_port <= rx_target_port;
	end

	always @(posedge clk ) begin
		if (rx_target_port == LOCAL_PORT && rx_cnt >= 8) begin
			app_rx_data_vld <= udp_rx_data_vld;
			app_rx_data_last<= udp_rx_data_last;
			app_rx_data 	<= udp_rx_data;
			app_rx_length 	<= udp_rx_length - 8;
		end
		else begin
			app_rx_data_vld <= 0;
			app_rx_data_last<= 0;
			app_rx_data 	<= 0;
			app_rx_length 	<= 0;
		end
	end

endmodule

udp_send模块

`timescale 1ns / 1ps

module udp_send #(
	parameter		LOCAL_PORT  = 16'h0 ,
	parameter		TARGET_PORT = 16'h0 	

)(
	input					       clk,
	input                          reset,

	/*-------ip_send块交互的信号--------------------*/	
	output  reg                    udp_tx_data_vld  ,
	output  reg                    udp_tx_data_last ,
	output  reg     [7:0]          udp_tx_data      ,	
	output  reg     [15:0]         udp_tx_length    ,

    /*----------------用户发送端信号 ----------------*/	
	input                          app_tx_data_vld  ,
	input                          app_tx_data_last ,
	input       [7:0]              app_tx_data      ,
	input       [15:0]             app_tx_length    ,
    output                         app_tx_req       ,
	output reg                     app_tx_ready         
    );


localparam  READY_CNT_MAX =  35;
reg  [7:0]  ready_cnt          ;
reg  		app_tx_data_vld_r  ;
reg  		app_tx_data_last_r ;
reg  [7:0]  app_tx_data_r      ;
reg  [15:0] app_tx_length_r    ;
reg  [15:0] app_length         ;
reg  [10:0] tx_cnt             ;
wire [7:0]  app_tx_data_delay  ;

assign app_tx_req = app_tx_data_vld;
/*------------------------------------------*\
                  打拍
\*------------------------------------------*/
always @(posedge clk) begin
	app_tx_data_vld_r  <= app_tx_data_vld;
	app_tx_data_last_r <= app_tx_data_last;
	app_tx_length_r    <= app_tx_length;
	app_tx_data_r      <= app_tx_data;
end

always @(posedge clk) begin
    if (app_tx_data_vld_r) 
        app_length <= app_tx_length_r;
    else 
        app_length <= app_length;
end

/*------------------------------------------*\
                  tx_cnt
\*------------------------------------------*/
always @(posedge clk) begin
    if (reset) 
        tx_cnt <= 0;
    else if (app_length < 18 && tx_cnt == 25)  //数据最小帧长为18byte
        tx_cnt <= 0;
    else if (app_length >= 18 && tx_cnt == app_length + 7)
        tx_cnt <= 0;
    else if (app_tx_data_vld_r || tx_cnt != 0)
    	tx_cnt <= tx_cnt + 1;
    else 
    	tx_cnt <= tx_cnt;
end

/*------------------------------------------*\
                   组包
\*------------------------------------------*/

always @(posedge clk) begin
    if (reset) 
        udp_tx_data <= 0;
    else begin
    	case(tx_cnt)
    		0 : udp_tx_data <= LOCAL_PORT[15:8];
    		1 : udp_tx_data <= LOCAL_PORT[7:0];

    		2 : udp_tx_data <= TARGET_PORT[15:8];
     		3 : udp_tx_data <= TARGET_PORT[7:0];
     		
     		4 : udp_tx_data <= udp_tx_length[15:8];
     		5 : udp_tx_data <= udp_tx_length[7:0];

     		6 : udp_tx_data <= 0;
     		7 : udp_tx_data <= 0;

     		default : udp_tx_data <= app_tx_data_delay;
    	endcase
    end 
end

always @(posedge clk) begin
    if (app_length <= 18) 
        udp_tx_length <= 26;
    else if (app_length > 18) 
        udp_tx_length <= app_length + 8;
    else 
        udp_tx_length <= udp_tx_length;
end

always @(posedge clk) begin
    if (reset) 
        udp_tx_data_vld <= 0;
    else if (udp_tx_data_last) 
        udp_tx_data_vld <= 0;
    else if (app_tx_data_vld_r)
    	udp_tx_data_vld <= 1;
    else 
    	udp_tx_data_vld <= udp_tx_data_vld;  
end

always @(posedge clk) begin
    if (reset) 
        udp_tx_data_last <= 0;
    else if (app_length < 18 && tx_cnt == 25) 
        udp_tx_data_last <= 1'b1;
    else if (app_length >= 18 && tx_cnt == app_length + 7)
    	udp_tx_data_last <= 1'b1;
    else 
    	udp_tx_data_last <= 0;
        
end

/*------------------------------------------*\
                  ready
\*------------------------------------------*/
always @(posedge clk) begin
    if (reset) 
        ready_cnt <= 0;
    else if (ready_cnt == READY_CNT_MAX) 
        ready_cnt <= 0;
    else if (app_tx_data_last_r || ready_cnt != 0)
        ready_cnt <= ready_cnt + 1;
    else 
    	ready_cnt <= ready_cnt;
end

always @(posedge clk) begin
    if (reset) 
        app_tx_ready <= 1'b1;
    else if (ready_cnt != 0) 
        app_tx_ready <= 0;
    else 
        app_tx_ready <= 1'b1;
end

/*------------------------------------------*\
                  打拍
\*------------------------------------------*/
//注意 : 如果A的值为7,udp_tx_data_delay打拍为8拍
c_shift_ram_0 ip_delay (
  .A(7),      // input wire [5 : 0] A
  .D(app_tx_data_r),      // input wire [7 : 0] D
  .CLK(clk),  // input wire CLK
  .Q(app_tx_data_delay)      // output wire [7 : 0] Q
);

endmodule

 

顶层设计

udp层分别实现了接收和发送两部分,将两部分例化封装顶层udp_layer

`timescale 1ns / 1ps

module udp_layer #(
	parameter		LOCAL_PORT  = 16'h0 ,
	parameter		TARGET_PORT = 16'h0 	
)(
	input						   app_tx_clk       ,
	input                          app_rx_clk       ,

	input                          app_tx_reset     ,
	input                          app_rx_reset     ,

	input                          udp_rx_data_vld  ,
	input                          udp_rx_data_last ,
	input       [7:0]              udp_rx_data      ,	
	input       [15:0]             udp_rx_length    ,

	output                         udp_tx_data_vld  ,
	output                         udp_tx_data_last ,
	output          [7:0]          udp_tx_data      ,	
	output          [15:0]         udp_tx_length    ,

	output                         app_rx_data_vld  ,
	output                         app_rx_data_last ,
	output      [7:0]              app_rx_data      ,
	output      [15:0]             app_rx_length    ,

	input                          app_tx_data_vld  ,
	input                          app_tx_data_last ,
	input       [7:0]              app_tx_data      ,
	input       [15:0]             app_tx_length    ,
    output                         app_tx_req       ,
	output                         app_tx_ready   	

    );

	udp_receive #(
			.LOCAL_PORT(LOCAL_PORT)
		) udp_receive (
			.clk              (app_rx_clk),
			.reset            (app_rx_reset),

			.udp_rx_data_vld  (udp_rx_data_vld),
			.udp_rx_data_last (udp_rx_data_last),
			.udp_rx_data      (udp_rx_data),
			.udp_rx_length    (udp_rx_length),

			.app_rx_data_vld  (app_rx_data_vld),
			.app_rx_data_last (app_rx_data_last),
			.app_rx_data      (app_rx_data),
			.app_rx_length    (app_rx_length)
		);


	udp_send #(
			.LOCAL_PORT(LOCAL_PORT),
			.TARGET_PORT(TARGET_PORT)
		) udp_send (
			.clk              (app_tx_clk),
			.reset            (app_tx_reset),

			.udp_tx_data_vld  (udp_tx_data_vld),
			.udp_tx_data_last (udp_tx_data_last),
			.udp_tx_data      (udp_tx_data),
			.udp_tx_length    (udp_tx_length),

			.app_tx_data_vld  (app_tx_data_vld),
			.app_tx_data_last (app_tx_data_last),
			.app_tx_data      (app_tx_data),
			.app_tx_length    (app_tx_length),
			.app_tx_req       (app_tx_req),
			.app_tx_ready     (app_tx_ready)
		);


endmodule

五,总结

至此,我们完成了UDP层的发送与接受。同时,也已经了解了UDP协议首部的具体内容。

接下来,在下一篇博客中将会实现以太网UDP通信的顶层设计。