一,引言
前文链接: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通信的顶层设计。