UDP 协议提供了一种低延迟的网络通信方式,通信效率高,但不保证接收的可靠性,常用在以太网图像传输等高速通信场合。
目录
1 UDP 协议
用户数据报协议(User Datagram Protocol, UDP)是一种面向无连接的传输协议,采用这种协议的通讯,无需事先建立连接。
UDP 数据段由 UDP 首部和数据两部分组成,其中首部的长度固定为 8 个字节,首部包括源端口、目的端口、UDP 长度和 16 位校验和。
2 校验过程
UDP 协议校验时需要添加 12 字节的伪首部,并且和 IP 协议不同,UDP 协议还需要校验数据。发送数据时,UDP 协议的校验步骤为:
(1)在 UDP 首部前添加 12 字节的伪首部;
(2)将首部校验和部分置零为 0x0000;
(3)以 16bit 为字长进行反码求和(数据不足的需补齐 16bit);
(4)将校验结果填充到 UDP 首部。
接收数据时,UDP 协议的校验步骤为:
(1)在 UDP 首部前添加 12 字节的伪首部;
(2)以 16bit 为字长进行反码求和;
(3)判断校验结果是否为 0xFFFF;
(4)若检验结果为 0xFFFF,校验通过;否则检验失败,丢弃该数据段。
3 HDL 描述
根据反码求和的特性,可以把 UDP 协议的校验分两部分处理,伪首部 + 首部的校验为一部分,UDP 用户数据的校验为另一部分。用户数据的校验需要根据实际的位宽,转换为 16bit。
VHDL 版本
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity validate_udp is
port(
-- System level
sys_rst : in std_logic;
sys_clk : in std_logic;
-- IP and UDP information
local_ip_addr : in std_logic_vector(4*8-1 downto 0);
remote_ip_addr : in std_logic_vector(4*8-1 downto 0);
local_udp_port : in std_logic_vector(2*8-1 downto 0);
remote_udp_port : in std_logic_vector(2*8-1 downto 0);
udp_packet_len : in std_logic_vector(2*8-1 downto 0);
-- UDP user data input
udp_usr_data : in std_logic_vector(7 downto 0);
udp_usr_wren : in std_logic;
-- UDP checksum output
udp_checksum : out std_logic_vector(2*8-1 downto 0)
);
end entity;
architecture behav of validate_udp is
-- internal signal declarations
subtype word is std_logic_vector(15 downto 0);
type check_info_t is array(0 to 9) of word;
signal udp_check_info: check_info_t;
signal usr_data_buf: std_logic_vector(15 downto 0);
signal usr_wren_toggle: std_logic;
signal usr_wren_toggle_d1: std_logic;
signal checksum_head: std_logic_vector(15 downto 0);
signal checksum_data: std_logic_vector(15 downto 0);
function checksum_adder(
ina: std_logic_vector(15 downto 0);
inb: std_logic_vector(15 downto 0)
) return std_logic_vector is
variable tmp1,tmp2,tmp: std_logic_vector(31 downto 0);
begin
tmp1 := X"0000" & ina;
tmp2 := X"0000" & inb;
tmp := tmp1 + tmp2;
return tmp(15 downto 0) + tmp(31 downto 16);
end function;
function checksum_out(
ina: std_logic_vector(15 downto 0);
inb: std_logic_vector(15 downto 0)
) return std_logic_vector is
variable tmp1,tmp2,tmp: std_logic_vector(31 downto 0);
begin
tmp1 := X"0000" & ina;
tmp2 := X"0000" & inb;
tmp := tmp1 + tmp2;
return not(tmp(15 downto 0) + tmp(31 downto 16));
end function;
begin
-- todo
process(sys_rst,sys_clk)
begin
if sys_rst = '1' then
udp_check_info <= (others => (others => '0'));
elsif rising_edge(sys_clk) then
udp_check_info(0) <= local_ip_addr(4*8-1 downto 2*8);
udp_check_info(1) <= local_ip_addr(2*8-1 downto 0*8);
udp_check_info(2) <= remote_ip_addr(4*8-1 downto 2*8);
udp_check_info(3) <= remote_ip_addr(2*8-1 downto 0*8);
udp_check_info(4) <= X"0011"; -- UDP protocol
udp_check_info(5) <= udp_packet_len;
udp_check_info(6) <= local_udp_port;
udp_check_info(7) <= remote_udp_port;
udp_check_info(8) <= udp_packet_len;
udp_check_info(9) <= X"0000"; -- UDP Checksum padding
end if;
end process;
process(udp_check_info)
variable tmp: std_logic_vector(31 downto 0);
begin
tmp := (others => '0');
for i in 0 to 9 loop
tmp := tmp + udp_check_info(i);
end loop;
checksum_head <= tmp(15 downto 0) + tmp(31 downto 16);
end process;
process(sys_rst,sys_clk)
begin
if sys_rst = '1' then
usr_data_buf <= (others => '0');
usr_wren_toggle <= '0';
usr_wren_toggle_d1 <= '0';
elsif rising_edge(sys_clk) then
if udp_usr_wren = '1' then
usr_data_buf <= usr_data_buf(7 downto 0) & udp_usr_data;
else
usr_data_buf <= (others => '0');
end if;
if udp_usr_wren = '1' then
usr_wren_toggle <= not usr_wren_toggle;
else
usr_wren_toggle <= '0';
end if;
usr_wren_toggle_d1 <= usr_wren_toggle;
end if;
end process;
process(sys_rst,sys_clk)
begin
if sys_rst = '1' then
checksum_data <= (others => '0');
elsif rising_edge(sys_clk) then
if usr_wren_toggle_d1 = '1' then
checksum_data <= checksum_adder(checksum_data, usr_data_buf);
else
checksum_data <= (others => '0');
end if;
end if;
end process;
process(sys_rst,sys_clk)
begin
if sys_rst = '1' then
udp_checksum <= (others => '0');
elsif rising_edge(sys_clk) then
udp_checksum <= checksum_out(checksum_head, checksum_data);
end if;
end process;
end architecture;
Verilog 版本
`timescale 1ns / 1ps
module validate_udp(
// System level
sys_rst,
sys_clk,
// IP and UDP information
local_ip_addr,
remote_ip_addr,
local_udp_port,
remote_udp_port,
udp_packet_len,
// UDP user data input
udp_usr_data,
udp_usr_wren,
// UDP checksum output
udp_checksum
);
// IO ports declarations
input sys_rst;
input sys_clk;
input [4*8-1:0] local_ip_addr;
input [4*8-1:0] remote_ip_addr;
input [2*8-1:0] local_udp_port;
input [2*8-1:0] remote_udp_port;
input [2*8-1:0] udp_packet_len;
input [7:0] udp_usr_data;
input udp_usr_wren;
output [2*8-1:0] udp_checksum;
reg [2*8-1:0] udp_checksum;
// internal function declarations
function [15:0] checksum_adder;
input [15:0] ina;
input [15:0] inb;
integer tmp,tmp1,tmp2;
begin
tmp1 = {16'd0, ina};
tmp2 = {16'd0, inb};
tmp = tmp1 + tmp2;
checksum_adder = tmp[15:0] + tmp[31:16];
end
endfunction
function [15:0] checksum_output;
input [15:0] ina;
input [15:0] inb;
integer tmp,tmp1,tmp2;
begin
tmp1 = {16'd0, ina};
tmp2 = {16'd0, inb};
tmp = tmp1 + tmp2;
checksum_output = ~(tmp[15:0] + tmp[31:16]);
end
endfunction
// internal signal declarations
reg [2*8-1:0] udp_check_info[9:0];
reg [2*8-1:0] checksum_head,checksum_data;
reg [4*8-1:0] tmp1,tmp2;
reg [2*8-1:0] usr_data_buf;
reg usr_wren_toggle,usr_wren_toggle_d1;
integer i;
always@(posedge sys_rst or posedge sys_clk) begin
if(sys_rst)
for(i = 0; i < 10; i=i+1)
udp_check_info[i] <= 16'h0000;
else begin
udp_check_info[0] <= local_ip_addr[4*8-1-:16];
udp_check_info[1] <= local_ip_addr[2*8-1-:16];
udp_check_info[2] <= remote_ip_addr[4*8-1-:16];
udp_check_info[3] <= remote_ip_addr[2*8-1-:16];
udp_check_info[4] <= 16'h0011; // UDP protocol
udp_check_info[5] <= udp_packet_len;
udp_check_info[6] <= local_udp_port;
udp_check_info[7] <= remote_udp_port;
udp_check_info[8] <= udp_packet_len;
udp_check_info[9] <= 16'h0000; // UDP checksum bytes padding
end
end
always@(*) begin
tmp1 = 32'd0;
for(i = 0; i < 10; i=i+1) begin
tmp1 = tmp1 + udp_check_info[i];
end
checksum_head = tmp1[15:0] + tmp1[31:16];
end
always@(posedge sys_rst or posedge sys_clk) begin
if(sys_rst) begin
usr_data_buf <= 16'd0;
usr_wren_toggle <= 1'd0;
usr_wren_toggle_d1 <= 1'd0;
end
else begin
if(udp_usr_wren)
usr_data_buf <= {usr_data_buf[7:0], udp_usr_data};
else
usr_data_buf <= 16'd0;
if(udp_usr_wren)
usr_wren_toggle <= ~usr_wren_toggle;
else
usr_wren_toggle <= 1'd0;
usr_wren_toggle_d1 <= usr_wren_toggle;
end
end
always@(posedge sys_rst or posedge sys_clk) begin
if(sys_rst)
checksum_data <= 16'd0;
else
if(usr_wren_toggle_d1)
checksum_data <= checksum_adder(checksum_data, usr_data_buf);
else
checksum_data <= 16'd0;
end
always@(posedge sys_rst or posedge sys_clk) begin
if(sys_rst)
udp_checksum <= 16'd0;
else
udp_checksum <= checksum_output(checksum_head, checksum_data);
end
endmodule
综合得到的 RTL 电路如下: