赞
踩
FPGA实现UDP协议可难可易,具体根据项目需求而定,目前项目上的需求大概有如下几种:
1、使用Xilinx系列FPGA实现UDP通信,且传输的数据量大,速率快,带宽高,这类的UDP通信必然要用到Xilinx的三速网IP,用户接口的数据必然是AXIS流,此类UDP协议功能齐全,无疑是很好的方案,关于这类的UDP通信介绍以及工程源码请参考我之前写的文章点击查看:高端UDP通信,附带工程源码
此类UDP协议虽然很好,但占用FPGA资源较多,对于A7、Spatan6、Altera之类的低端FPGA来说资源消耗太大,不太可能被采用,再者,对于国产FPGA来说,没有三速网IP也无法实现;
2、不使用三速网IP,通信速率相对较低,实现过程几乎使用纯verilog代码实现的中等UDP通信方案,关于这类的UDP通信介绍以及工程源码请参考我之前写的文章点击查看:中等UDP通信,附带工程源码
此类方案没有了IP的限制,任何品牌的FPGA均可实现,但占用FPGA资源还是较多,低端FPGA依然很难实现;
3、精简版UDP通信方案
就是此文介绍的方案
优点:
纯verilog代码实现,没有任何IP;
代码量很少,所占逻辑资源很少;
通用性好,Xilinx、Altera、国产FPGA均可使用;
稳定性好,本人反复测试,很稳定;
总体实现方案如下:
RGMII-GMII模块实现网络PHY数据与FPGA接口的数据转换,由PHY的4位DDR双边沿采样数据到FPGA接口8位单边沿采样数据的转换;模块接口顶层如下:
module gmii_to_rgmii( input idelay_clk , //IDELAY时钟 //以太网GMII接口 output gmii_rx_clk , //GMII接收时钟 output gmii_rx_dv , //GMII接收数据有效信号 output [7:0] gmii_rxd , //GMII接收数据 output gmii_tx_clk , //GMII发送时钟 input gmii_tx_en , //GMII发送数据使能信号 input [7:0] gmii_txd , //GMII发送数据 //以太网RGMII接口 input rgmii_rxc , //RGMII接收时钟 input rgmii_rx_ctl, //RGMII接收数据控制信号 input [3:0] rgmii_rxd , //RGMII接收数据 output rgmii_txc , //RGMII发送时钟 output rgmii_tx_ctl, //RGMII发送数据控制信号 output [3:0] rgmii_txd //RGMII发送数据 );
ARP模块顾名思义就是实现了ARP协议,该模块在不同应用中可有可无,在于上位机通信时需要,在FPGA板间通信时则可以不需要,模块接口顶层如下:
module arp( input rst_n , //复位信号,低电平有效 //GMII接口 input gmii_rx_clk, //GMII接收数据时钟 input gmii_rx_dv , //GMII输入数据有效信号 input [7:0] gmii_rxd , //GMII输入数据 input gmii_tx_clk, //GMII发送数据时钟 output gmii_tx_en , //GMII输出数据有效信号 output [7:0] gmii_txd , //GMII输出数据 //用户接口 output arp_rx_done, //ARP接收完成信号 output arp_rx_type, //ARP接收类型 0:请求 1:应答 output [47:0] src_mac , //接收到目的MAC地址 output [31:0] src_ip , //接收到目的IP地址 input arp_tx_en , //ARP发送使能信号 input arp_tx_type, //ARP发送类型 0:请求 1:应答 input [47:0] des_mac , //发送的目标MAC地址 input [31:0] des_ip , //发送的目标IP地址 output tx_done //以太网发送完成信号 );
UDP模块顾名思义就是实现了UDP协议,该部分内部已经包含了IP协议的编解码,这部分时整个工程的核心,UDP协议其实真的很简单,接收部分就是根据协议进行数据解包,解出帧头部分并丢弃,解出有效数据并输出;发送部分就是根据协议进行数据组包,把发送数据加上帧头发出去就完事儿了,模块接口顶层如下:
module udp( input rst_n , //复位信号,低电平有效 //GMII接口 input gmii_rx_clk , //GMII接收数据时钟 input gmii_rx_dv , //GMII输入数据有效信号 input [7:0] gmii_rxd , //GMII输入数据 input gmii_tx_clk , //GMII发送数据时钟 output gmii_tx_en , //GMII输出数据有效信号 output [7:0] gmii_txd , //GMII输出数据 //用户接口 output rec_pkt_done, //以太网单包数据接收完成信号 output rec_en , //以太网接收的数据使能信号 output [31:0] rec_data , //以太网接收的数据 output [15:0] rec_byte_num, //以太网接收的有效字节数 单位:byte input tx_start_en , //以太网开始发送信号 input [31:0] tx_data , //以太网待发送数据 input [15:0] tx_byte_num , //以太网发送的有效字节数 单位:byte input [47:0] des_mac , //发送的目标MAC地址 input [31:0] des_ip , //发送的目标IP地址 output tx_done , //以太网发送完成信号 output tx_req //读数据请求信号 );
工程介绍:
简而言之就是实现UDP自发自收,以验证UDP协议的正确性;
工程1介绍;
开发板:Kintex7开发板;
网络PHY:B50610
输出:RJ45网口
接收:电脑上位机;
B50610不需要配置,使用默认的配置,工作在延时模式,PHY的发送时钟rgmii_txc 与FPGA发送时钟gmii_tx_clk一致;
代码部分如下:
assign rgmii_txc = gmii_tx_clk;
工程顶层代码如下:
module eth_udp_loop( input clk_50 , //系统时钟 //PL以太网RGMII接口 input phy1_rgmii_rx_clk , //RGMII接收数据时钟 input phy1_rgmii_rx_ctl, //RGMII输入数据有效信号 input [3:0] phy1_rgmii_rx_data , //RGMII输入数据 output phy1_rgmii_tx_clk , //RGMII发送数据时钟 output phy1_rgmii_tx_ctl, //RGMII输出数据有效信号 output [3:0] phy1_rgmii_tx_data , //RGMII输出数据 output phy_reset //以太网芯片复位信号,低电平有效 ); //parameter define parameter BOARD_MAC = 48'h00_11_22_33_44_55; //开发板MAC地址 parameter BOARD_IP = {8'd192,8'd168,8'd10,8'd1}; //开发板IP地址 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff; //目的MAC地址 parameter DES_IP = {8'd192,8'd168,8'd10,8'd2}; //目的IP地址 parameter IDELAY_VALUE = 0; //输入数据IO延时,此处为0,即不延时(如果为n,表示延时n*78ps) parameter BOARD_POART =16'd1234; parameter DES_POART =16'd1234; //wire define wire clk_200m ; //用于IO延时的时钟 wire gmii_rx_clk; //GMII接收时钟 wire gmii_rx_dv ; //GMII接收数据有效信号 wire [7:0] gmii_rxd ; //GMII接收数据 wire gmii_tx_clk; //GMII发送时钟 wire gmii_tx_en ; //GMII发送数据使能信号 wire [7:0] gmii_txd ; //GMII发送数据 wire arp_gmii_tx_en; //ARP GMII输出数据有效信号 wire [7:0] arp_gmii_txd ; //ARP GMII输出数据 wire arp_rx_done ; //ARP接收完成信号 wire arp_rx_type ; //ARP接收类型 0:请求 1:应答 wire [47:0] src_mac ; //接收到目的MAC地址 wire [31:0] src_ip ; //接收到目的IP地址 wire arp_tx_en ; //ARP发送使能信号 wire arp_tx_type ; //ARP发送类型 0:请求 1:应答 wire [47:0] des_mac ; //发送的目标MAC地址 wire [31:0] des_ip ; //发送的目标IP地址 wire arp_tx_done ; //ARP发送完成信号 wire udp_gmii_tx_en; //UDP GMII输出数据有效信号 wire [7:0] udp_gmii_txd ; //UDP GMII输出数据 wire rec_pkt_done ; //UDP单包数据接收完成信号 wire rec_en ; //UDP接收的数据使能信号 wire [31:0] rec_data ; //UDP接收的数据 wire [15:0] rec_byte_num ; //UDP接收的有效字节数 单位:byte wire [15:0] tx_byte_num ; //UDP发送的有效字节数 单位:byte wire udp_tx_done ; //UDP发送完成信号 wire tx_req ; //UDP读数据请求信号 wire [31:0] tx_data ; //UDP待发送数据 wire sys_rst_n; //***************************************************** //** main code //***************************************************** assign gmii_tx_clk = gmii_rx_clk ; assign tx_start_en = rec_pkt_done; assign tx_byte_num = rec_byte_num; assign des_mac = src_mac; assign des_ip = src_ip; assign phy_reset = sys_rst_n; clk_wiz_0 instance_name ( // Clock out ports .clk_200m(clk_200m), // output clk_200m .clk_125m(), // output clk_125m .clk_125m_90(), // output clk_125m_90 // Status and control signals .locked(sys_rst_n), // output locked // Clock in ports .clk_in1(clk_50)); // input clk_in1 //GMII接口转RGMII接口 gmii_to_rgmii #( .IDELAY_VALUE (IDELAY_VALUE) ) u_gmii_to_rgmii( .idelay_clk (clk_200m ), .gmii_rx_clk (gmii_rx_clk ), .gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .gmii_tx_clk (gmii_tx_clk ), .gmii_tx_en (gmii_tx_en ), .gmii_txd (gmii_txd ), .rgmii_rxc (phy1_rgmii_rx_clk ), .rgmii_rx_ctl (phy1_rgmii_rx_ctl ), .rgmii_rxd (phy1_rgmii_rx_data ), .rgmii_txc (phy1_rgmii_tx_clk ), .rgmii_tx_ctl (phy1_rgmii_tx_ctl ), .rgmii_txd (phy1_rgmii_tx_data ) ); //ARP通信 arp #( .BOARD_MAC (BOARD_MAC), //参数例化 .BOARD_IP (BOARD_IP ), .DES_MAC (DES_MAC ), .DES_IP (DES_IP ) ) u_arp( .rst_n (sys_rst_n ), .gmii_rx_clk (gmii_rx_clk), .gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .gmii_tx_clk (gmii_tx_clk), .gmii_tx_en (arp_gmii_tx_en ), .gmii_txd (arp_gmii_txd), .arp_rx_done (arp_rx_done), .arp_rx_type (arp_rx_type), .src_mac (src_mac ), .src_ip (src_ip ), .arp_tx_en (arp_tx_en ), .arp_tx_type (arp_tx_type), .des_mac (des_mac ), .des_ip (des_ip ), .tx_done (arp_tx_done) ); //UDP通信 udp #( .BOARD_MAC (BOARD_MAC), //参数例化 .BOARD_IP (BOARD_IP ), .DES_MAC (DES_MAC ), .DES_IP (DES_IP ), .BOARD_POART (BOARD_POART), .DES_POART (DES_POART ) ) u_udp( .rst_n (sys_rst_n ), .gmii_rx_clk (gmii_rx_clk ), .gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .gmii_tx_clk (gmii_tx_clk ), .gmii_tx_en (udp_gmii_tx_en), .gmii_txd (udp_gmii_txd), .rec_pkt_done (rec_pkt_done), .rec_en (rec_en ), .rec_data (rec_data ), .rec_byte_num (rec_byte_num), .tx_start_en (tx_start_en ), .tx_data (tx_data ), .tx_byte_num (tx_byte_num ), .des_mac (des_mac ), .des_ip (des_ip ), .tx_done (udp_tx_done ), .tx_req (tx_req ) ); //同步FIFO sync_fifo_2048x32b u_sync_fifo_2048x32b ( .clk (gmii_rx_clk), // input wire clk .rst (~sys_rst_n), // input wire rst .din (rec_data ), // input wire [31 : 0] din .wr_en (rec_en ), // input wire wr_en .rd_en (tx_req ), // input wire rd_en .dout (tx_data ), // output wire [31 : 0] dout .full (), // output wire full .empty () // output wire empty ); //以太网控制模块 eth_ctrl u_eth_ctrl( .clk (gmii_rx_clk), .rst_n (sys_rst_n), .arp_rx_done (arp_rx_done ), .arp_rx_type (arp_rx_type ), .arp_tx_en (arp_tx_en ), .arp_tx_type (arp_tx_type ), .arp_tx_done (arp_tx_done ), .arp_gmii_tx_en (arp_gmii_tx_en), .arp_gmii_txd (arp_gmii_txd ), .udp_gmii_tx_en (udp_gmii_tx_en), .udp_gmii_txd (udp_gmii_txd ), .gmii_tx_en (gmii_tx_en ), .gmii_txd (gmii_txd ) ); endmodule
K7上的资源消耗和性能表现如下:
由于工程中使用到了PLL和fifo,实际UDP部分所占资源更小;
工程2介绍;
开发板:Artix7开发板;
网络PHY:RTL8211
输出:RJ45网口
接收:电脑上位机;
RTL8211不需要配置,使用默认的配置,工作在延时模式,PHY的发送时钟rgmii_txc 与FPGA发送时钟gmii_tx_clk不一致;rgmii_txc与gmii_tx_clk相位偏差+90°;
代码部分如下:
module rgmii_tx( //GMII发送端口 input gmii_tx_clk , //GMII发送时钟 input gmii_tx_clk_90 , input gmii_tx_en , //GMII输出数据有效信号 input [7:0] gmii_txd , //GMII输出数据 //RGMII发送端口 output rgmii_txc , //RGMII发送数据时钟 output rgmii_tx_ctl, //RGMII输出数据有效信号 output [3:0] rgmii_txd //RGMII输出数据 ); assign rgmii_txc = gmii_tx_clk_90; //输出双沿采样寄存器 (rgmii_tx_ctl) ODDR #( .DDR_CLK_EDGE ("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT (1'b0), // Initial value of Q: 1'b0 or 1'b1 .SRTYPE ("SYNC") // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_inst ( .Q (rgmii_tx_ctl), // 1-bit DDR output .C (gmii_tx_clk), // 1-bit clock input .CE (1'b1), // 1-bit clock enable input .D1 (gmii_tx_en), // 1-bit data input (positive edge) .D2 (gmii_tx_en), // 1-bit data input (negative edge) .R (1'b0), // 1-bit reset .S (1'b0) // 1-bit set ); genvar i; generate for (i=0; i<4; i=i+1) begin : txdata_bus //输出双沿采样寄存器 (rgmii_txd) ODDR #( .DDR_CLK_EDGE ("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" .INIT (1'b0), // Initial value of Q: 1'b0 or 1'b1 .SRTYPE ("SYNC") // Set/Reset type: "SYNC" or "ASYNC" ) ODDR_inst ( .Q (rgmii_txd[i]), // 1-bit DDR output .C (gmii_tx_clk), // 1-bit clock input .CE (1'b1), // 1-bit clock enable input .D1 (gmii_txd[i]), // 1-bit data input (positive edge) .D2 (gmii_txd[4+i]),// 1-bit data input (negative edge) .R (1'b0), // 1-bit reset .S (1'b0) // 1-bit set ); end endgenerate endmodule
相应的工程顶层代码的时钟部分也有改变,如下:
module eth_udp_loop( input clk_50 , //系统时钟 //PL以太网RGMII接口 input phy1_rgmii_rx_clk , //RGMII接收数据时钟 input phy1_rgmii_rx_ctl, //RGMII输入数据有效信号 input [3:0] phy1_rgmii_rx_data , //RGMII输入数据 output phy1_rgmii_tx_clk , //RGMII发送数据时钟 output phy1_rgmii_tx_ctl, //RGMII输出数据有效信号 output [3:0] phy1_rgmii_tx_data , //RGMII输出数据 output phy_reset //以太网芯片复位信号,低电平有效 ); //parameter define parameter BOARD_MAC = 48'h00_11_22_33_44_55; //开发板MAC地址 parameter BOARD_IP = {8'd192,8'd168,8'd10,8'd1}; //开发板IP地址 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff; //目的MAC地址 parameter DES_IP = {8'd192,8'd168,8'd10,8'd2}; //目的IP地址 parameter IDELAY_VALUE = 0; //输入数据IO延时,此处为0,即不延时(如果为n,表示延时n*78ps) parameter BOARD_POART =16'd1234; parameter DES_POART =16'd1234; //wire define wire clk_200m ; //用于IO延时的时钟 wire gmii_rx_clk; //GMII接收时钟 wire gmii_rx_dv ; //GMII接收数据有效信号 wire [7:0] gmii_rxd ; //GMII接收数据 wire gmii_tx_clk; //GMII发送时钟 wire gmii_tx_en ; //GMII发送数据使能信号 wire [7:0] gmii_txd ; //GMII发送数据 wire arp_gmii_tx_en; //ARP GMII输出数据有效信号 wire [7:0] arp_gmii_txd ; //ARP GMII输出数据 wire arp_rx_done ; //ARP接收完成信号 wire arp_rx_type ; //ARP接收类型 0:请求 1:应答 wire [47:0] src_mac ; //接收到目的MAC地址 wire [31:0] src_ip ; //接收到目的IP地址 wire arp_tx_en ; //ARP发送使能信号 wire arp_tx_type ; //ARP发送类型 0:请求 1:应答 wire [47:0] des_mac ; //发送的目标MAC地址 wire [31:0] des_ip ; //发送的目标IP地址 wire arp_tx_done ; //ARP发送完成信号 wire udp_gmii_tx_en; //UDP GMII输出数据有效信号 wire [7:0] udp_gmii_txd ; //UDP GMII输出数据 wire rec_pkt_done ; //UDP单包数据接收完成信号 wire rec_en ; //UDP接收的数据使能信号 wire [31:0] rec_data ; //UDP接收的数据 wire [15:0] rec_byte_num ; //UDP接收的有效字节数 单位:byte wire [15:0] tx_byte_num ; //UDP发送的有效字节数 单位:byte wire udp_tx_done ; //UDP发送完成信号 wire tx_req ; //UDP读数据请求信号 wire [31:0] tx_data ; //UDP待发送数据 wire sys_rst_n; wire clk_125m; wire clk_125m_90; //***************************************************** //** main code //***************************************************** assign gmii_tx_clk = clk_125m ; assign tx_start_en = rec_pkt_done; assign tx_byte_num = rec_byte_num; assign des_mac = src_mac; assign des_ip = src_ip; assign phy_reset = sys_rst_n; clk_wiz_0 instance_name ( // Clock out ports .clk_200m(clk_200m), // output clk_200m .clk_125m(clk_125m), // output clk_125m .clk_125m_90(clk_125m_90), // output clk_125m_90 // Status and control signals .locked(sys_rst_n), // output locked // Clock in ports .clk_in1(clk_50)); // input clk_in1 //GMII接口转RGMII接口 gmii_to_rgmii #( .IDELAY_VALUE (IDELAY_VALUE) ) u_gmii_to_rgmii( .idelay_clk (clk_200m ), .gmii_rx_clk (gmii_rx_clk ), .gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .gmii_tx_clk (gmii_tx_clk ), .gmii_tx_clk_90(clk_125m_90), .gmii_tx_en (gmii_tx_en ), .gmii_txd (gmii_txd ), .rgmii_rxc (phy1_rgmii_rx_clk ), .rgmii_rx_ctl (phy1_rgmii_rx_ctl ), .rgmii_rxd (phy1_rgmii_rx_data ), .rgmii_txc (phy1_rgmii_tx_clk ), .rgmii_tx_ctl (phy1_rgmii_tx_ctl ), .rgmii_txd (phy1_rgmii_tx_data ) ); //ARP通信 arp #( .BOARD_MAC (BOARD_MAC), //参数例化 .BOARD_IP (BOARD_IP ), .DES_MAC (DES_MAC ), .DES_IP (DES_IP ) ) u_arp( .rst_n (sys_rst_n ), .gmii_rx_clk (gmii_rx_clk), .gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .gmii_tx_clk (gmii_tx_clk), .gmii_tx_en (arp_gmii_tx_en ), .gmii_txd (arp_gmii_txd), .arp_rx_done (arp_rx_done), .arp_rx_type (arp_rx_type), .src_mac (src_mac ), .src_ip (src_ip ), .arp_tx_en (arp_tx_en ), .arp_tx_type (arp_tx_type), .des_mac (des_mac ), .des_ip (des_ip ), .tx_done (arp_tx_done) ); //UDP通信 udp #( .BOARD_MAC (BOARD_MAC), //参数例化 .BOARD_IP (BOARD_IP ), .DES_MAC (DES_MAC ), .DES_IP (DES_IP ), .BOARD_POART (BOARD_POART), .DES_POART (DES_POART ) ) u_udp( .rst_n (sys_rst_n ), .gmii_rx_clk (gmii_rx_clk ), .gmii_rx_dv (gmii_rx_dv ), .gmii_rxd (gmii_rxd ), .gmii_tx_clk (gmii_tx_clk ), .gmii_tx_en (udp_gmii_tx_en), .gmii_txd (udp_gmii_txd), .rec_pkt_done (rec_pkt_done), .rec_en (rec_en ), .rec_data (rec_data ), .rec_byte_num (rec_byte_num), .tx_start_en (tx_start_en ), .tx_data (tx_data ), .tx_byte_num (tx_byte_num ), .des_mac (des_mac ), .des_ip (des_ip ), .tx_done (udp_tx_done ), .tx_req (tx_req ) ); //同步FIFO sync_fifo_2048x32b u_sync_fifo_2048x32b ( .clk (gmii_rx_clk), // input wire clk .rst (~sys_rst_n), // input wire rst .din (rec_data ), // input wire [31 : 0] din .wr_en (rec_en ), // input wire wr_en .rd_en (tx_req ), // input wire rd_en .dout (tx_data ), // output wire [31 : 0] dout .full (), // output wire full .empty () // output wire empty ); //以太网控制模块 eth_ctrl u_eth_ctrl( .clk (gmii_rx_clk), .rst_n (sys_rst_n), .arp_rx_done (arp_rx_done ), .arp_rx_type (arp_rx_type ), .arp_tx_en (arp_tx_en ), .arp_tx_type (arp_tx_type ), .arp_tx_done (arp_tx_done ), .arp_gmii_tx_en (arp_gmii_tx_en), .arp_gmii_txd (arp_gmii_txd ), .udp_gmii_tx_en (udp_gmii_tx_en), .udp_gmii_txd (udp_gmii_txd ), .gmii_tx_en (gmii_tx_en ), .gmii_txd (gmii_txd ) ); endmodule
A7上的资源消耗和性能表现如下:
由于工程中使用到了PLL和fifo,实际UDP部分所占资源更小;
代码架构如下:
工程1验证:
开发板连接
工程2验证:
开发板连接
上位机收发显示如下:
福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
获取方式:私,或者文章末尾的V名片
资料如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。