Overview of the UDP Protocol
The User Datagram Protocol (UDP) is a fundamental transport layer protocol in the OSI model, offering an unreliable, connectoinless communication service. Defined in IETF RFC 768, UDP operates over IP and uses a simple header format. Its protocol number in IP packets is 17 (0x11).
Packet Strucutre
In Ethernet communication, data flows through multiple layers:
- Ethernet Frame: Contains MAC addresses and payload.
- IP Packet: Encapsulates the UDP segment.
- UDP Segment: Carries application data along with port information.
- Application Data: The actual content, such as "Hello, welcome to FPGA!".
The ancapsulation sequence is: Ethernet frame → IP packet → UDP segment → Application data.
UDP Packet Format
Fields Description
-
Preamble:
- 8'h55 repeated seven times followed by 8'hd5.
-
Ethernet Header:
- Destination MAC address.
- Source MAC address.
- EtherType field set to 0x0800 for IPv4.
-
IP Header (20 bytes):
- Version (4 bits)
- Header Length (4 bits)
- Type of Service (8 bits)
- Total Length (16 bits)
- Identification (16 bits)
- Flags and Fragment Offset (16 bits)
- TTL (8 bits)
- Protocol (8 bits)
- Header Checksum (16 bits)
- Source IP Address (32 bits)
- Destination IP Address (32 bits)
The IP header checksum is calculated using one's complement arithmetic:
- Set the checksum field to zero.
- Split the header into 16-bit words.
- Sum all words using one's complement addition.
- Take the one's complement of the result.
-
UDP Header:
- Source Port (16 bits)
- Destination Port (16 bits)
- Length (16 bits)
- Checksum (16 bits) — can be zero if not required.
-
CRC:
- Calculated starting from the preamble until the end of the frame.
- Uses a standard CRC32 algorithm.
Implementation Details
// Time: 2020.04.11 21:22
// Description: UDP test module
module udp_test(
rst_n,
mii_tx_clk,
mii_tx_en,
mii_tx_er,
mii_tx_data,
phy_rst_n
);
input rst_n;
input mii_tx_clk;
output mii_tx_en;
output mii_tx_er;
output reg [3:0] mii_tx_data;
output phy_rst_n;
assign phy_rst_n = 1'b1;
parameter des_mac = 48'hc4_54_44_97_c5_d7;
parameter src_mac = 48'h00_0a_35_01_fe_c0;
parameter type_length = 16'h08_00;
parameter data_total_len = 16'd22;
parameter src_port = 16'd5000;
parameter des_port = 16'd6000;
parameter ver = 4'h4;
parameter hdr_len = 4'h5;
parameter tos = 8'h00;
parameter id = 16'h0000;
parameter offset = 16'h0000;
parameter ttl = 16'h40;
parameter protocol = 8'h11;
parameter src_ip = 32'hc0_a8_00_02;
parameter dst_ip = 32'hc0_a8_00_03;
wire [15:0] ip_total_len;
wire [15:0] udp_total_len;
assign ip_total_len = data_total_len + 16'd28;
assign udp_total_len = data_total_len + 16'd8;
wire [31:0] CRC_Result;
reg [7:0] lsm_cnt;
wire CRC_EN;
assign CRC_EN = (lsm_cnt >= 17 && lsm_cnt <= 144);
crc32_d4 u0 (
.Clk(mii_tx_clk),
.Rst_n(rst_n),
.Data(mii_tx_data),
.Enable(CRC_EN),
.Initialize(~mii_tx_en),
.Crc(),
.CrcError(),
.Crc_eth(CRC_Result)
);
wire [31:0] sum;
wire [15:0] ip_checksum;
assign sum = {ver,hdr_len,tos} + ip_total_len + id + offset + {ttl,protocol} + src_ip[31:16]+ src_ip[15:0] + dst_ip[31:16] + dst_ip[15:0];
assign ip_checksum = ~(sum[31:16] + sum[15:0]);
wire tx_go;
reg en_tx;
reg [28:0] cnt;
always @(posedge mii_tx_clk or negedge rst_n)
if (!rst_n)
cnt <= 28'd0;
else if (cnt == 28'd1000)
cnt <= 28'd0;
else
cnt <= cnt + 1'b1;
assign tx_go = (cnt == 28'd1000) ? 1'b1 : 1'b0;
always @(posedge mii_tx_clk or negedge rst_n)
if (!rst_n)
en_tx <= 1'd0;
else if (tx_go)
en_tx <= 1'd1;
else if (lsm_cnt >= 153)
en_tx <= 1'd0;
always @(posedge mii_tx_clk or negedge rst_n)
if (!rst_n)
lsm_cnt <= 8'd0;
else if (en_tx) begin
if (lsm_cnt == 8'd153)
lsm_cnt <= 8'd0;
else
lsm_cnt <= lsm_cnt + 1'b1;
end
else
lsm_cnt <= 8'd0;
always @(*) begin
case (lsm_cnt)
1: mii_tx_data <= 4'h5;
2: mii_tx_data <= 4'h5;
3: mii_tx_data <= 4'h5;
4: mii_tx_data <= 4'h5;
5: mii_tx_data <= 4'h5;
6: mii_tx_data <= 4'h5;
7: mii_tx_data <= 4'h5;
8: mii_tx_data <= 4'h5;
9: mii_tx_data <= 4'h5;
10: mii_tx_data <= 4'h5;
11: mii_tx_data <= 4'h5;
12: mii_tx_data <= 4'h5;
13: mii_tx_data <= 4'h5;
14: mii_tx_data <= 4'h5;
15: mii_tx_data <= 4'h5;
16: mii_tx_data <= 4'hd;
17: mii_tx_data <= des_mac[43:40];
18: mii_tx_data <= des_mac[47:44];
19: mii_tx_data <= des_mac[35:32];
20: mii_tx_data <= des_mac[39:36];
21: mii_tx_data <= des_mac[27:24];
22: mii_tx_data <= des_mac[31:28];
23: mii_tx_data <= des_mac[19:16];
24: mii_tx_data <= des_mac[23:20];
25: mii_tx_data <= des_mac[11:8];
26: mii_tx_data <= des_mac[15:12];
27: mii_tx_data <= des_mac[3:0];
28: mii_tx_data <= des_mac[7:4];
29: mii_tx_data <= src_mac[43:40];
30: mii_tx_data <= src_mac[47:44];
31: mii_tx_data <= src_mac[35:32];
32: mii_tx_data <= src_mac[39:36];
33: mii_tx_data <= src_mac[27:24];
34: mii_tx_data <= src_mac[31:28];
35: mii_tx_data <= src_mac[19:16];
36: mii_tx_data <= src_mac[23:20];
37: mii_tx_data <= src_mac[11:8];
38: mii_tx_data <= src_mac[15:12];
39: mii_tx_data <= src_mac[3:0];
40: mii_tx_data <= src_mac[7:4];
41: mii_tx_data <= type_length[11:8];
42: mii_tx_data <= type_length[15:12];
43: mii_tx_data <= type_length[3:0];
44: mii_tx_data <= type_length[7:4];
45: mii_tx_data <= 4'h5;
46: mii_tx_data <= 4'h4;
47: mii_tx_data <= 4'h0;
48: mii_tx_data <= 4'h0;
49: mii_tx_data <= ip_total_len[11:8];
50: mii_tx_data <= ip_total_len[15:12];
51: mii_tx_data <= ip_total_len[3:0];
52: mii_tx_data <= ip_total_len[7:4];
53: mii_tx_data <= 4'h0;
54: mii_tx_data <= 4'h0;
55: mii_tx_data <= 4'h0;
56: mii_tx_data <= 4'h0;
57: mii_tx_data <= 4'h0;
58: mii_tx_data <= 4'h0;
59: mii_tx_data <= 4'h0;
60: mii_tx_data <= 4'h0;
61: mii_tx_data <= 4'h0;
62: mii_tx_data <= 4'h4;
63: mii_tx_data <= 4'h1;
64: mii_tx_data <= 4'h1;
65: mii_tx_data <= ip_checksum[11:8];
66: mii_tx_data <= ip_checksum[15:12];
67: mii_tx_data <= ip_checksum[3:0];
68: mii_tx_data <= ip_checksum[7:4];
69: mii_tx_data <= 4'h0;
70: mii_tx_data <= 4'hc;
71: mii_tx_data <= 4'h8;
72: mii_tx_data <= 4'ha;
73: mii_tx_data <= 4'h0;
74: mii_tx_data <= 4'h0;
75: mii_tx_data <= 4'h2;
76: mii_tx_data <= 4'h0;
77: mii_tx_data <= 4'h0;
78: mii_tx_data <= 4'hc;
79: mii_tx_data <= 4'h8;
80: mii_tx_data <= 4'ha;
81: mii_tx_data <= 4'h0;
82: mii_tx_data <= 4'h0;
83: mii_tx_data <= 4'h3;
84: mii_tx_data <= 4'h0;
85: mii_tx_data <= src_port[11:8];
86: mii_tx_data <= src_port[15:12];
87: mii_tx_data <= src_port[3:0];
88: mii_tx_data <= src_port[7:4];
89: mii_tx_data <= des_port[11:8];
90: mii_tx_data <= des_port[15:12];
91: mii_tx_data <= des_port[3:0];
92: mii_tx_data <= des_port[7:4];
93: mii_tx_data <= udp_total_len[11:8];
94: mii_tx_data <= udp_total_len[15:12];
95: mii_tx_data <= udp_total_len[3:0];
96: mii_tx_data <= udp_total_len[7:4];
97: mii_tx_data <= 4'h0;
98: mii_tx_data <= 4'h0;
99: mii_tx_data <= 4'h0;
100: mii_tx_data <= 4'h0;
101: mii_tx_data <= 4'h8;
102: mii_tx_data <= 4'h4;
103: mii_tx_data <= 4'h5;
104: mii_tx_data <= 4'h6;
105: mii_tx_data <= 4'hc;
106: mii_tx_data <= 4'h6;
107: mii_tx_data <= 4'hc;
108: mii_tx_data <= 4'h6;
109: mii_tx_data <= 4'hf;
110: mii_tx_data <= 4'h6;
111: mii_tx_data <= 4'hc;
112: mii_tx_data <= 4'h2;
113: mii_tx_data <= 4'h7;
114: mii_tx_data <= 4'h7;
115: mii_tx_data <= 4'h5;
116: mii_tx_data <= 4'h6;
117: mii_tx_data <= 4'hc;
118: mii_tx_data <= 4'h6;
119: mii_tx_data <= 4'h3;
120: mii_tx_data <= 4'h6;
121: mii_tx_data <= 4'hf;
122: mii_tx_data <= 4'h6;
123: mii_tx_data <= 4'hd;
124: mii_tx_data <= 4'h6;
125: mii_tx_data <= 4'h5;
126: mii_tx_data <= 4'h6;
127: mii_tx_data <= 4'h0;
128: mii_tx_data <= 4'h2;
129: mii_tx_data <= 4'h4;
130: mii_tx_data <= 4'h7;
131: mii_tx_data <= 4'hf;
132: mii_tx_data <= 4'h6;
133: mii_tx_data <= 4'h0;
134: mii_tx_data <= 4'h2;
135: mii_tx_data <= 4'h6;
136: mii_tx_data <= 4'h4;
137: mii_tx_data <= 4'h0;
138: mii_tx_data <= 4'h5;
139: mii_tx_data <= 4'h7;
140: mii_tx_data <= 4'h4;
141: mii_tx_data <= 4'h1;
142: mii_tx_data <= 4'h4;
143: mii_tx_data <= 4'h1;
144: mii_tx_data <= 4'h2;
145: mii_tx_data <= CRC_Result[27:24];
146: mii_tx_data <= CRC_Result[31:28];
147: mii_tx_data <= CRC_Result[19:16];
148: mii_tx_data <= CRC_Result[23:20];
149: mii_tx_data <= CRC_Result[11:8];
150: mii_tx_data <= CRC_Result[15:12];
151: mii_tx_data <= CRC_Result[3:0];
152: mii_tx_data <= CRC_Result[7:4];
153: mii_tx_data <= 4'd0;
default: mii_tx_data <= 4'd0;
endcase
end
assign mii_tx_en = ((lsm_cnt >= 1) && (lsm_cnt <= 153)) ? 1'b1 : 1'b0;
endmodule
CRC Module Implementation
`timescale 1ns/1ns
module crc32_d4 (Clk, Rst_n, Data, Enable, Initialize, Crc, CrcError, Crc_eth);
parameter Tp = 1;
input Clk;
input Rst_n;
input [0:3] Data;
input Enable;
input Initialize;
output [31:0] Crc;
output [31:0] Crc_eth;
output CrcError;
reg [31:0] Crc;
wire [31:0] CrcNext;
assign CrcNext[0] = Enable & (Data[0] ^ Crc[28]);
assign CrcNext[1] = Enable & (Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29]);
assign CrcNext[2] = Enable & (Data[2] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[30]);
assign CrcNext[3] = Enable & (Data[3] ^ Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30] ^ Crc[31]);
assign CrcNext[4] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[0];
assign CrcNext[5] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[1];
assign CrcNext[6] = (Enable & (Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30])) ^ Crc[2];
assign CrcNext[7] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[3];
assign CrcNext[8] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[4];
assign CrcNext[9] = (Enable & (Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30])) ^ Crc[5];
assign CrcNext[10] = (Enable & (Data[3] ^ Data[2] ^ Data[0] ^ Crc[28] ^ Crc[30] ^ Crc[31])) ^ Crc[6];
assign CrcNext[11] = (Enable & (Data[3] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[31])) ^ Crc[7];
assign CrcNext[12] = (Enable & (Data[2] ^ Data[1] ^ Data[0] ^ Crc[28] ^ Crc[29] ^ Crc[30])) ^ Crc[8];
assign CrcNext[13] = (Enable & (Data[3] ^ Data[2] ^ Data[1] ^ Crc[29] ^ Crc[30] ^ Crc[31])) ^ Crc[9];
assign CrcNext[14] = (Enable & (Data[3] ^ Data[2] ^ Crc[30] ^ Crc[31])) ^ Crc[10];
assign CrcNext[15] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[11];
assign CrcNext[16] = (Enable & (Data[0] ^ Crc[28])) ^ Crc[12];
assign CrcNext[17] = (Enable & (Data[1] ^ Crc[29])) ^ Crc[13];
assign CrcNext[18] = (Enable & (Data[2] ^ Crc[30])) ^ Crc[14];
assign CrcNext[19] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[15];
assign CrcNext[20] = Crc[16];
assign CrcNext[21] = Crc[17];
assign CrcNext[22] = (Enable & (Data[0] ^ Crc[28])) ^ Crc[18];
assign CrcNext[23] = (Enable & (Data[1] ^ Data[0] ^ Crc[29] ^ Crc[28])) ^ Crc[19];
assign CrcNext[24] = (Enable & (Data[2] ^ Data[1] ^ Crc[30] ^ Crc[29])) ^ Crc[20];
assign CrcNext[25] = (Enable & (Data[3] ^ Data[2] ^ Crc[31] ^ Crc[30])) ^ Crc[21];
assign CrcNext[26] = (Enable & (Data[3] ^ Data[0] ^ Crc[31] ^ Crc[28])) ^ Crc[22];
assign CrcNext[27] = (Enable & (Data[1] ^ Crc[29])) ^ Crc[23];
assign CrcNext[28] = (Enable & (Data[2] ^ Crc[30])) ^ Crc[24];
assign CrcNext[29] = (Enable & (Data[3] ^ Crc[31])) ^ Crc[25];
assign CrcNext[30] = Crc[26];
assign CrcNext[31] = Crc[27];
always @(posedge Clk or negedge Rst_n)
begin
if (!Rst_n)
Crc <= #1 32'hffffffff;
else
if (Initialize)
Crc <= #Tp 32'hffffffff;
else if (Enable)
Crc <= #Tp CrcNext;
end
assign Crc_eth = ~{
CrcNext[28], CrcNext[29], CrcNext[30], CrcNext[31],
Crc[24], Crc[25], Crc[26], Crc[27],
Crc[20], Crc[21], Crc[22], Crc[23],
Crc[16], Crc[17], Crc[18], Crc[19],
Crc[12], Crc[13], Crc[14], Crc[15],
Crc[8], Crc[9], Crc[10], Crc[11],
Crc[4], Crc[5], Crc[6], Crc[7],
Crc[0], Crc[1], Crc[2], Crc[3]};
assign CrcError = Crc[31:0] != 32'hc704dd7b;
endmodule
Testbench
`timescale 1ns/1ps
module udp_test_tb;
reg rst_n;
reg mii_tx_clk;
wire mii_tx_en;
wire mii_tx_er;
wire [3:0] mii_tx_data;
wire phy_rst_n;
udp_test u0 (
.rst_n(rst_n),
.mii_tx_clk(mii_tx_clk),
.mii_tx_en(mii_tx_en),
.mii_tx_er(mii_tx_er),
.mii_tx_data(mii_tx_data),
.phy_rst_n(phy_rst_n)
);
initial
mii_tx_clk = 1'b0;
always #20 mii_tx_clk = ~mii_tx_clk;
initial
begin
rst_n = 1'b0;
#100;
rst_n = 1'b1;
#100000;
$stop;
end
endmodule