一,引言
前文链接: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协议回环 ,不带arp 和icmp 的功能,是简化版本的 udp工程,开发板和PC通信,需要手动在电脑端添加板卡的ip地址 和mac地址。
如下图,完整的以太网udp通信是包括arp 和icmp功能:
但是,在我们的代码设计中,我们按每一层去编写设计代码,因此,想要完成完整的udp设计,只需要mac层 后提添加ICMP功能 ,在ip层后 添加ARP功能 。添加这2个功能后要涉及一个很大的问题:仲裁 ,设计好仲裁后,就可以完成完整版本的udp工程。
至此,简化版的以太网udp通信的发送与接受设计完成。