FPGA - 以太网UDP通信(五)

一,引言

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

FPGA - 以太网UDP通信(二)

FPGA - 以太网UDP通信(三)

FPGA - 以太网UDP通信(四)​​​​​​

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

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

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

三,以太网UDP通信顶层设计

在之前文章中,我们已经完成了物理层(RGMII接口-GMII接口转换逻辑),数据链路层(MAC层接受发送逻辑),网络层(IP层接受发送逻辑),传输层(UDP层接受发送逻辑)。接下来将各层连接起来完成以太网UDP通信的顶层设计。

首先,我们将mac_layer,ip_layer,udp_layer例化udp_protocol_stack:

udp协议--udp_protocol_stack:

`timescale 1ns / 1ps

module udp_protocol_stack #(
	parameter	    LOCAL_MAC_ADDR  = 48'hffffff_ffffff ,
	parameter       TARGET_MAC_ADDR = 48'hffffff_ffffff	,	
	parameter		LOCAL_IP_ADDR   = 32'h0             ,
	parameter       TARGET_IP_ADDR  = 32'h0	            ,
	parameter		LOCAL_PORT      = 16'h0             ,
	parameter		TARGET_PORT     = 16'h0 	     

)(
	input				         		phy_tx_clk   ,
	input                        		phy_rx_clk   ,
	input                        		reset        ,

   /* ------------GMII接口信号--------------------------*/
	input                               gmii_rx_data_vld ,
	input       [7:0]                   gmii_rx_data     ,
	output                              gmii_tx_data_vld ,
	output      [7:0]                   gmii_tx_data     ,	

   /* ------------用户接送端信号--------------------------*/
    input                               app_rx_clk       ,
	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_clk       ,
	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_ready   	

    );
 

   	wire 		 app_tx_req        ;

	wire         mac_rx_data_vld   ;
	wire         mac_rx_data_last  ;
	wire   [7:0] mac_rx_data       ;
	wire  [15:0] mac_rx_frame_type ;
	wire         mac_tx_data_vld   ;
	wire         mac_tx_data_last  ;
	wire   [7:0] mac_tx_data       ;
	wire  [15:0] mac_tx_frame_type ;
	wire  [15:0] mac_tx_length     ;

	wire         ip_rx_data_vld    ;
	wire         ip_rx_data_last   ;
	wire   [7:0] ip_rx_data        ;
	wire         ip_tx_data_vld    ;
	wire         ip_tx_data_last   ;
	wire  [15:0] ip_tx_length      ;
	wire  [7:0 ] ip_tx_data        ;

	wire         udp_rx_data_vld   ;
	wire         udp_rx_data_last  ;
	wire   [7:0] udp_rx_data       ;
	wire  [15:0] udp_rx_length     ;
	wire         udp_tx_data_vld   ;
	wire         udp_tx_data_last  ;
	wire   [7:0] udp_tx_data       ;
	wire  [15:0] udp_tx_length     ;

	wire         icmp_rx_data_vld  ;
	wire         icmp_rx_data_last ;
	wire   [7:0] icmp_rx_data      ;
	wire  [15:0] icmp_rx_length    ;

	wire         arp_rx_data_vld   ;
	wire         arp_rx_data_last  ;
	wire   [7:0] arp_rx_data       ;

/*---------------------------------------------------*\
                  复位信号处理
\*---------------------------------------------------*/
	reg  [19:0] phy_rx_reset_timer;
	reg         phy_rx_reset_d0;
	reg         phy_rx_reset_d1;
	reg         phy_rx_reset   ;

	always @(posedge phy_rx_clk or posedge reset) begin
	    if (reset) 
	        phy_rx_reset_timer <= 0;
	    else if (phy_rx_reset_timer <= 20'h000ff) 
	    	phy_rx_reset_timer <= phy_rx_reset_timer + 1; 
	    else 
	        phy_rx_reset_timer <= phy_rx_reset_timer;
	end
	
	always @(posedge phy_rx_clk or posedge reset) begin
	    if (reset) begin
	        phy_rx_reset_d0 <= 1'b1;
	        phy_rx_reset_d1 <= 1'b1;
	        phy_rx_reset    <= 1'b1;
	    end
	    else begin
	    	phy_rx_reset_d0 <= phy_rx_reset_timer <= 20'h000ff;
	    	phy_rx_reset_d1 <= phy_rx_reset_d0;
	    	phy_rx_reset    <= phy_rx_reset_d1;
	    end   
	end
	
//---------------------------------------------------------------------------
	reg  [19:0] phy_tx_reset_timer;
	reg         phy_tx_reset_d0;
	reg         phy_tx_reset_d1;
	reg         phy_tx_reset   ;

	always @(posedge phy_tx_clk or posedge reset) begin
	    if (reset) 
	        phy_tx_reset_timer <= 0;
	    else if (phy_tx_reset_timer <= 20'h000ff) 
	    	phy_tx_reset_timer <= phy_tx_reset_timer + 1; 
	    else 
	        phy_tx_reset_timer <= phy_tx_reset_timer;
	end
	
	always @(posedge phy_tx_clk or posedge reset) begin
	    if (reset) begin
	        phy_tx_reset_d0 <= 1'b1;
	        phy_tx_reset_d1 <= 1'b1;
	        phy_tx_reset    <= 1'b1;
	    end
	    else begin
	    	phy_tx_reset_d0 <= phy_tx_reset_timer <= 20'h000ff;
	    	phy_tx_reset_d1 <= phy_tx_reset_d0;
	    	phy_tx_reset    <= phy_tx_reset_d1;
	    end   
	end

//---------------------------------------------------------------------------
	reg  [19:0] app_tx_reset_timer;
	reg         app_tx_reset_d0;
	reg         app_tx_reset_d1;
	reg         app_tx_reset   ;

	always @(posedge app_tx_clk or posedge reset) begin
	    if (reset) 
	        app_tx_reset_timer <= 0;
	    else if (app_tx_reset_timer <= 20'h000ff) 
	    	app_tx_reset_timer <= app_tx_reset_timer + 1; 
	    else 
	        app_tx_reset_timer <= app_tx_reset_timer;
	end
	
	always @(posedge app_tx_clk or posedge reset) begin
	    if (reset) begin
	        app_tx_reset_d0 <= 1'b1;
	        app_tx_reset_d1 <= 1'b1;
	        app_tx_reset    <= 1'b1;
	    end
	    else begin
	    	app_tx_reset_d0 <= app_tx_reset_timer <= 20'h000ff;
	    	app_tx_reset_d1 <= app_tx_reset_d0;
	    	app_tx_reset    <= app_tx_reset_d1;
	    end   
	end

//---------------------------------------------------------------------------
	reg  [19:0] app_rx_reset_timer;
	reg         app_rx_reset_d0;
	reg         app_rx_reset_d1;
	reg         app_rx_reset   ;

	always @(posedge app_rx_clk or posedge reset) begin
	    if (reset) 
	        app_rx_reset_timer <= 0;
	    else if (app_rx_reset_timer <= 20'h000ff) 
	    	app_rx_reset_timer <= app_rx_reset_timer + 1; 
	    else 
	        app_rx_reset_timer <= app_rx_reset_timer;
	end
	
	always @(posedge app_rx_clk or posedge reset) begin
	    if (reset) begin
	        app_rx_reset_d0 <= 1'b1;
	        app_rx_reset_d1 <= 1'b1;
	        app_rx_reset    <= 1'b1;
	    end
	    else begin
	    	app_rx_reset_d0 <= app_rx_reset_timer <= 20'h000ff;
	    	app_rx_reset_d1 <= app_rx_reset_d0;
	    	app_rx_reset    <= app_rx_reset_d1;
	    end   
	end

/*---------------------------------------------------*\
                    模块例化
\*---------------------------------------------------*/
	mac_layer #(
			.LOCAL_MAC_ADDR(LOCAL_MAC_ADDR),
			.TARGET_MAC_ADDR(TARGET_MAC_ADDR),
			.CRC_CHACK_EN(1)
		) mac_layer (
			.app_tx_clk        (app_tx_clk),
			.app_rx_clk        (app_rx_clk),
			.phy_tx_clk        (phy_tx_clk),
			.phy_rx_clk        (phy_rx_clk),

			.app_tx_reset      (app_tx_reset),
			.app_rx_reset      (app_rx_reset),
			.phy_tx_reset      (phy_tx_reset),
			.phy_rx_reset      (phy_rx_reset),

			.gmii_rx_data_vld  (gmii_rx_data_vld),
			.gmii_rx_data      (gmii_rx_data),
			.gmii_tx_data_vld  (gmii_tx_data_vld),
			.gmii_tx_data      (gmii_tx_data),

			.mac_rx_data_vld   (mac_rx_data_vld),
			.mac_rx_data_last  (mac_rx_data_last),
			.mac_rx_data       (mac_rx_data),
			.mac_rx_frame_type (mac_rx_frame_type),

			.mac_tx_data_vld   (ip_tx_data_vld),
			.mac_tx_data_last  (ip_tx_data_last),
			.mac_tx_data       (ip_tx_data),
			.mac_tx_frame_type (16'h0800    ),  //ip层
			.mac_tx_length     (ip_tx_length)
		);

	mac_to_ip_arp mac_to_ip_arp
		(
			.clk               (app_rx_clk),
			.reset             (app_rx_reset),

			.mac_rx_data_vld   (mac_rx_data_vld),
			.mac_rx_data_last  (mac_rx_data_last),
			.mac_rx_data       (mac_rx_data),
			.mac_rx_frame_type (mac_rx_frame_type),

			.ip_rx_data_vld    (ip_rx_data_vld),
			.ip_rx_data_last   (ip_rx_data_last),
			.ip_rx_data        (ip_rx_data),

			.arp_rx_data_vld   (arp_rx_data_vld),
			.arp_rx_data_last  (arp_rx_data_last),
			.arp_rx_data       (arp_rx_data)
		);


	ip_layer #(
			.LOCAL_IP_ADDR(LOCAL_IP_ADDR),
			.TARGET_IP_ADDR(TARGET_IP_ADDR)
		) ip_layer (
			.app_tx_clk        (app_tx_clk),
			.app_rx_clk        (app_rx_clk),

			.app_rx_reset      (app_rx_reset),
			.app_tx_reset      (app_tx_reset),

			.ip_rx_data_vld    (ip_rx_data_vld),
			.ip_rx_data_last   (ip_rx_data_last),
			.ip_rx_data        (ip_rx_data),
			.ip_tx_data_vld    (ip_tx_data_vld),
			.ip_tx_data_last   (ip_tx_data_last),
			.ip_tx_length      (ip_tx_length),
			.ip_tx_data        (ip_tx_data),

			.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),
			.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),

			.icmp_rx_data_vld  (icmp_rx_data_vld),
			.icmp_rx_data_last (icmp_rx_data_last),
			.icmp_rx_data      (icmp_rx_data),
			.icmp_rx_length    (icmp_rx_length)
		);

	udp_layer #(
			.LOCAL_PORT(LOCAL_PORT),
			.TARGET_PORT(TARGET_PORT)
		) udp_layer (
			.app_tx_clk       (app_tx_clk),
			.app_rx_clk       (app_rx_clk),

			.app_tx_reset     (app_tx_reset),
			.app_rx_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),
			.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_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),
			.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

在上述代码中为什么要对复位信号处理呢,因为输入的复位并不能保证在这四个时钟(app_tx_clk,app_rx_clk,phy_tx_clk,phy_rx_clk)下的。所以复位信号在分别在这4个时钟域下进行打拍,就可以分别得到这4个时钟域下复位信号。

顶层设计

根据udp_protocol_stack和rgmii_interface(FPGA - 以太网UDP通信(一)中)·将两部分例化封装顶层top

`timescale 1ns / 1ps

module top(
	input	wire		clkin_50m         ,

	input               phy_rgmii_rx_clk_i  ,
	input               phy_rgmii_rx_ctl ,
	input [3:0]         phy_rgmii_rx_data ,

	output              phy_rgmii_tx_clk  ,
	output              phy_rgmii_tx_ctl  ,
	output [3:0]        phy_rgmii_tx_data ,

	output              phy_reset
    );

	parameter  LOCAL_MAC_ADDR = {8'd0,8'd1,8'd2,8'd3,8'd4,8'd5};
	parameter TARGET_MAC_ADDR = {8'd0,8'd1,8'd2,8'd3,8'd4,8'd5};
	parameter   LOCAL_IP_ADDR = {8'd0,8'd1,8'd2,8'd3};
	parameter  TARGET_IP_ADDR = {8'd0,8'd1,8'd2,8'd3};
	parameter      LOCAL_PORT = 16'h8060;
	parameter     TARGET_PORT = 16'h8060;

	wire			reset;
    wire			clkout_125m;
    wire			clkout_200m;
    wire			delay_refclk;
    wire			phy_tx_clk;
    wire			phy_rx_clk;
    wire			app_rx_clk;
    wire			app_tx_clk;

	wire			gmii_rx_vld;
	wire			gmii_rx_error;
	wire	[7:0]	gmii_rx_data;
	wire			gmii_tx_vld;
	wire	[7:0]	gmii_tx_data;

	wire            app_rx_data_vld;
	wire            app_rx_data_last;
	wire  [7:0]     app_rx_data;
	wire [15:0]     app_rx_length;
	wire            app_tx_data_vld;
	wire            app_tx_data_last;
	wire  [7:0]     app_tx_data;
	wire [15:0]     app_tx_length;
	wire            app_tx_ready;
	
    wire            phy_rgmii_rx_clk;
    wire            pll_locked;
/*------------------------------------------*\
              clk and reset
\*------------------------------------------*/	

  clk_wiz_1 instance_name
   (
    // Clock out ports
    .clk_out1(phy_rgmii_rx_clk),     // output clk_out1
    // Status and control signals
    .locked(pll_locked),       // output locked
   // Clock in ports
    .clk_in1(phy_rgmii_rx_clk_i));      // input clk_in1


assign delay_refclk = clkout_200m;
assign phy_tx_clk   = clkout_125m;
assign app_rx_clk   = clkout_200m;
assign app_tx_clk   = clkout_200m;
assign phy_reset    = ~reset;

/*------------------------------------------*\
                 模块例化
\*------------------------------------------*/
	clock_and_reset clock_and_reset (
			.clkin_50m   (clkin_50m),
			.clkout_125m (clkout_125m),
			.clkout_200m (clkout_200m),
			.reset       (reset)
		);

	rgmii_interface rgmii_interface(
			.reset             (reset),

			.delay_refclk      (delay_refclk),

			.phy_rgmii_rx_clk  (phy_rgmii_rx_clk),
			.phy_rgmii_rx_ctl  (phy_rgmii_rx_ctl),
			.phy_rgmii_rx_data (phy_rgmii_rx_data),

			.phy_rgmii_tx_clk  (phy_rgmii_tx_clk),
			.phy_rgmii_tx_ctl  (phy_rgmii_tx_ctl),
			.phy_rgmii_tx_data (phy_rgmii_tx_data),

			.gmii_rx_clk       (phy_rx_clk), //rgmii_interface输出的gmii_rx_clk
			.gmii_rx_vld       (gmii_rx_vld),
			.gmii_rx_error     (gmii_rx_error),
			.gmii_rx_data      (gmii_rx_data),

			.gmii_tx_clk       (phy_tx_clk),
			.gmii_tx_vld       (gmii_tx_vld),
			.gmii_tx_data      (gmii_tx_data)
		);

	udp_protocol_stack #(
			.LOCAL_MAC_ADDR   (LOCAL_MAC_ADDR),
			.TARGET_MAC_ADDR  (TARGET_MAC_ADDR),
			.LOCAL_IP_ADDR    (LOCAL_IP_ADDR),
			.TARGET_IP_ADDR   (TARGET_IP_ADDR),
			.LOCAL_PORT       (LOCAL_PORT),
			.TARGET_PORT      (TARGET_PORT)
		) udp_protocol_stack (
			.phy_tx_clk       (phy_tx_clk),
			.phy_rx_clk       (phy_rx_clk),
			.reset            (reset),

			.gmii_rx_data_vld (gmii_rx_vld),
			.gmii_rx_data     (gmii_rx_data),
			.gmii_tx_data_vld (gmii_tx_vld),
			.gmii_tx_data     (gmii_tx_data),

			.app_rx_clk       (app_rx_clk),
			.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),

			.app_tx_clk       (app_tx_clk),
			.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_ready     (app_tx_ready)
		);

/*------------------------------------------*\
                 上板回环
\*------------------------------------------*/
c_shift_ram_0 top_delay_1 (
  .A(60),      // input wire [5 : 0] A
  .D({app_rx_data_vld,app_rx_data_last,6'd0}),      // input wire [7 : 0] D
  .CLK(clkout_200m),  // input wire CLK
  .Q({app_tx_data_vld,app_tx_data_last,6'd0})      // output wire [7 : 0] Q
);
c_shift_ram_0 top_delay_2 (
  .A(60),      // input wire [5 : 0] A
  .D(app_rx_data),      // input wire [7 : 0] D
  .CLK(clkout_200m),  // input wire CLK
  .Q(app_tx_data)      // output wire [7 : 0] Q
);

c_shift_ram_1 top_delay_3 (
  .A(60),      // input wire [5 : 0] A
  .D(app_rx_length),      // input wire [15 : 0] D
  .CLK(clkout_200m),  // input wire CLK
  .Q(app_tx_length)      // output wire [15 : 0] Q
);


endmodule

注意 (我在这里也调试了好久) :PHY芯片的时钟(phy_rgmii_rx_clk_i)是外部时钟 ,将外部时钟信号不加处理直接引入FPGA芯片 使用,有时候会导致意想不到的BUG发生,而且这种BUG是不可重复的。

将外部时钟直接连接到FPGA芯片的普通I/O管脚 ,而非专用时钟输入管脚 ,将会导致下面问题

1.由于该时钟信号是通过各种长短布线资源,甚至经过LUT连接才能到达其驱动的各个寄存器,因此该时钟信号从进入FPGA管脚,到传递到各个寄存器的时钟输入端,其时间 是很难保持相同的,距离的远近直接决定了该时钟信号的传输延迟 (时钟延迟)。而这个传输延迟的差值,可能达到几纳秒甚至十几纳秒。这个差值,将直接影响数据的建立保持时间 ,造成时序无法收敛,从而导致设计失败。

2.使用非全局布线资源 ,时钟信号在布线的过程中更容易受到周围信号的干扰 。导致时钟质量变差。什么意思呢?打个比方,一只小鸟和一只兔子共同穿越一个满是灰尘的工地。工地上到处都是灰尘。小鸟从空中飞过,不直接与灰尘接触,因此基本不会沾到灰尘,因为它有自己独立的路线和空间。而兔子因为不会飞,因此只能跑着从工地中穿过,那么,不可避免的,兔子的脚上会沾上灰尘。导致当兔子穿过这个工地的时候,早已由小白兔变成了小灰兔。时钟信号也是如此,全局时钟资源有专门的时钟路径,在自己的空间走线,不穿过或很少穿过各种高速翻转的逻辑区域,因此很少受到污染。而非全局时钟资源没有专门的时钟路径,只能使用通用布线资源,而这些布线不可避免的会穿过很多高速翻转的逻辑区域。从而受到这些逻辑的翻转噪声的污染。最终时钟信号变的很差。例如边沿上升和下降更慢,占空比发生变化,时钟抖动增大等。

所以,在这里我把PHY芯片的时钟 (phy_rgmii_rx_clk_i)引入到PLL 中,输入(phy_rgmii_rx_clk_i)125Mhz 输出(phy_rgmii_rx_clk)125Mhz,并将其相位 设置为90,延时一下,使其更稳定,这样就保证了外部输入时钟的稳定性

四,以太网UDP通信仿真

仿真设计

对以太网UDP通信设计进行仿真,仿真代码如下:

`timescale 1ns / 1ps
module tb_top();

	parameter  LOCAL_MAC_ADDR = {8'd0,8'd1,8'd2,8'd3,8'd4,8'd5};
	parameter TARGET_MAC_ADDR = {8'd0,8'd1,8'd2,8'd3,8'd4,8'd5};
	parameter   LOCAL_IP_ADDR = {8'd0,8'd1,8'd2,8'd3};
	parameter  TARGET_IP_ADDR = {8'd0,8'd1,8'd2,8'd3};
	parameter      LOCAL_PORT = 16'h8060;
	parameter     TARGET_PORT = 16'h8060;

	reg 		phy_clk 			;
	reg 		clk 				;
	reg 		reset 				;

	wire        gmii_rx_data_vld 	;
	wire  [7:0] gmii_rx_data 		;
	wire        gmii_tx_data_vld 	;
	wire  [7:0] gmii_tx_data 		;

	wire        app_rx_data_vld 	;
	wire        app_rx_data_last 	;
	wire [7 :0] app_rx_data 		;
	wire [15:0] app_rx_length 		; 

	reg         app_tx_data_vld 	;
	reg         app_tx_data_last 	;
	reg  [7 :0] app_tx_data 		;
	reg  [15:0] app_tx_length 		;
	wire        app_tx_ready 		;

	initial begin
		phy_clk = 0;
		forever # (4)
		phy_clk = ~ phy_clk;
	end

	initial begin
		clk = 0;
		forever # (10) //20ns
 		clk = ~ clk;
	end

	initial begin
		reset = 1;
		#2000
		reset = 0;
	end
	/*----------------------------------------------------*\
	                  	  发起APP请求
	\*----------------------------------------------------*/
	initial begin
		#300000
		upd_rw_test(18);

		upd_rw_test(1024);
		#3000;
		$stop;
	end


	/*----------------------------------------------------*\
	                  	   封装task任务
	\*----------------------------------------------------*/
	task upd_rw_test
		(
		input  	[15:0]	length 
		);begin
			#3000;
			wait(app_tx_ready);
			app_tx_data_vld  <= 0;
			app_tx_data_last <= 0;
			app_tx_length    <= length;
			#400;

			repeat(length)@(posedge clk)begin
				app_tx_data_vld <= 1;
			end
			app_tx_data_vld  <= 1;
			app_tx_data_last <= 1;
			#20;
			app_tx_data_vld  <= 0;
			app_tx_data_last <= 0;
		end
	endtask 


	always @(posedge clk ) begin
		if (reset) begin
			app_tx_data <= 0;
		end
		else if (app_tx_data_vld) begin
			app_tx_data <= app_tx_data + 1;
		end
	end


	assign gmii_rx_data_vld  = gmii_tx_data_vld	;
	assign gmii_rx_data      = gmii_tx_data 	;

	udp_protocol_stack #(
			.LOCAL_MAC_ADDR(LOCAL_MAC_ADDR),
			.TARGET_MAC_ADDR(TARGET_MAC_ADDR),
			.LOCAL_IP_ADDR(LOCAL_IP_ADDR),
			.TARGET_IP_ADDR(TARGET_IP_ADDR),
			.LOCAL_PORT(LOCAL_PORT),
			.TARGET_PORT(TARGET_PORT)
		) udp_protocol_stack (
			.phy_tx_clk       (phy_clk),
			.phy_rx_clk       (phy_clk),
			.reset            (reset),

			.gmii_rx_data_vld (gmii_rx_data_vld),
			.gmii_rx_data     (gmii_rx_data),
			.gmii_tx_data_vld (gmii_tx_data_vld),
			.gmii_tx_data     (gmii_tx_data),

			.app_rx_clk       (clk),
			.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),

			.app_tx_clk       (clk),
			.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_ready     (app_tx_ready)
		);

endmodule

仿真波形

udp_send:

ip_send:

mac_send:

mac_receive:

mac_to_ip_arp:

ip_receive:

udp_receive:

top:

五,以太网UDP通信上板验证

修改源mac地址和ip地址,以及目标mac地址和ip地址。

	parameter  LOCAL_MAC_ADDR = {8'h48,8'h2c,8'ha0,8'hdf,8'h00,8'hff};
	parameter TARGET_MAC_ADDR = {8'h88,8'ha4,8'hc2,8'hc8,8'h19,8'h11};
	parameter   LOCAL_IP_ADDR = {8'd169,8'd254,8'd189,8'd137};
	parameter  TARGET_IP_ADDR = {8'd169,8'd254,8'd189,8'd136};
	parameter      LOCAL_PORT = 16'd1234;
	parameter     TARGET_PORT = 16'd1234;

逻辑综合,布局布线,生成Bitstream:

打开网络调试助手:

打开后点击发送:

可以看到可以正常接收。

六,总结

我们完成了以太网udp通信的发送与接受。同时,也已经了解了MAC协议首部,IP协议首部,UDP协议首部的具体内容,并完成了各层的接受与发送。

但是本次设计的以太网udp通信,只做了udp协议回环 ,不带arpicmp 的功能,是简化版本的 udp工程,开发板和PC通信,需要手动在电脑端添加板卡的ip地址mac地址

如下图,完整的以太网udp通信是包括arpicmp功能:

但是,在我们的代码设计中,我们按每一层去编写设计代码,因此,想要完成完整的udp设计,只需要mac层 后提添加ICMP功能 ,在ip层后 添加ARP功能 。添加这2个功能后要涉及一个很大的问题:仲裁 ,设计好仲裁后,就可以完成完整版本的udp工程。

至此,简化版的以太网udp通信的发送与接受设计完成。

相关推荐
szxinmai主板定制专家36 分钟前
【国产NI替代】基于国产FPGA+兆易创新GD32F450的全国产16振动+2转速(24bits)高精度终端采集板卡
fpga开发
打鱼又晒网1 小时前
linux网络套接字 | 深度解析守护进程 | 实现tcp服务守护进程化
linux·网络协议·计算机网络·tcp
szxinmai主板定制专家2 小时前
【国产NI替代】基于FPGA的32通道(24bits)高精度终端采集核心板卡
大数据·人工智能·fpga开发
HIZYUAN8 小时前
AGM FPGA如何配置上拉或者下拉电阻
fpga开发
∑狸猫不是猫8 小时前
(13)CT137A- 简易音乐盒设计
fpga开发
njnu@liyong13 小时前
图解HTTP-HTTP报文
网络协议·计算机网络·http
ThreeYear_s14 小时前
基于FPGA 的4位密码锁 矩阵键盘 数码管显示 报警仿真
fpga开发·矩阵·计算机外设
kaixin_learn_qt_ing15 小时前
了解RPC
网络·网络协议·rpc
爱吃水果蝙蝠汤17 小时前
DATACOM-IP单播路由(BGP)-复习-实验
网络·网络协议·tcp/ip
嵌入式大圣17 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp