以太网UDP协议栈实现(支持ARP、ICMP、UDP)--FPGA学习笔记26

纯verilog实现,仅使用锁相环IP、FIFO IP,方便跨平台移植。支持ping指令。

以太网系列文章:

以太网ICMP协议(ping指令)——FPGA学习笔记25-CSDN博客

以太网ARP协议——FPGA学习笔记23-CSDN博客 

以太网PHY_MDIO通信(基于RTL8211)--FPGA学习笔记22_mdio前导码-CSDN博客

FPGA千兆网口数据传输MDIO接口——FPGA学习笔记3_yt8531sh原理图-CSDN博客 

 

一、UDP简介

        UDP(User Datagram Protocol),即用户数据报协议, 是一种面向无连接的传输层协议。无连接是指在传输数据时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用 UDP 协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输(如视频会议等)都会采用 UDP 协议进行传输,这种情况即使偶尔丢失一两个数据包,也不会对接收结果
产生太大影响。

 

二、UDP协议

UDP 首部共 8 个字节,同 IP 首部一样,也是一行以 32 位(4 个字节)为单位。
源端口号: 16 位发送端端口号,用于区分不同服务的端口,端口号的范围从 0 到 65535。
目的端口号: 16 位接收端端口号。
UDP 长度: 16 位 UDP 长度,包含 UDP 首部长度+数据长度,单位是字节(byte)。
UDP 校验和: 16 位 UDP 校验和。 UDP 计算校验和的方法和计算 IP 数据报首部校验和的方法相似,但不同的是 IP 数据报的校验和只检验 IP 数据报的首部,而 UDP 校验和包含三个部分: UDP 伪首部, UDP 首部和 UDP 的数据部分伪首部的数据是从 IP 数据报头和 UDP 数据报头获取的,包括源 IP 地址,目的 IP地址,协议类型和 UDP 长度,其目的是让 UDP 两次检查数据是否已经正确到达目的地,只是单纯为了做校验用的。在大多数使用场景中接收端并不检测 UDP 校验和,因此这里不做过多介绍。

用户数据打包在 UDP 协议中, UDP 协议又是基于 IP 协议之上的, IP 协议又是走 MAC 层发送的,即从包含关系来说: MAC 帧中的数据段为 IP 数据报, IP 报文中的数据段为 UDP 报文, UDP 报文中的数据段为用户希望传输的数据内容。


三、目标实现UDP协议(含ICMP协议)

四、代码编写

1、UDP框图

(1)udp_rx

 

module udp_rx(input                clk         ,    //时钟信号input                rst_n       ,    //复位信号,低电平有效input                gmii_rx_dv  ,    //GMII输入数据有效信号input        [7:0]   gmii_rxd    ,    //GMII输入数据output  reg          rec_pkt_done,    //以太网单包数据接收完成信号output  reg          rec_en      ,    //以太网接收的数据使能信号output  reg  [7 :0]  rec_data    ,output  reg  [15:0]  rec_byte_num     //以太网接收的有效字数 单位:byte     
);//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55; 
//开发板IP地址 192.168.1.10 
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};localparam  st_idle     = 7'b000_0001; //初始状态,等待接收前导码
localparam  st_preamble = 7'b000_0010; //接收前导码状态 
localparam  st_eth_head = 7'b000_0100; //接收以太网帧头
localparam  st_ip_head  = 7'b000_1000; //接收IP首部
localparam  st_udp_head = 7'b001_0000; //接收UDP首部
localparam  st_rx_data  = 7'b010_0000; //接收有效数据
localparam  st_rx_end   = 7'b100_0000; //接收结束localparam  ETH_TYPE    = 16'h0800   ; //以太网协议类型 IP协议
localparam  UDP_TYPE    = 8'd17      ; //UDP协议类型reg  [6:0]   cur_state       ;
reg  [6:0]   next_state      ;reg          skip_en         ; //控制状态跳转使能信号
reg          error_en        ; //解析错误使能信号
reg  [4:0]   cnt             ; //解析数据计数器
reg  [47:0]  des_mac         ; //目的MAC地址
reg  [15:0]  eth_type        ; //以太网类型
reg  [31:0]  des_ip          ; //目的IP地址
reg  [5:0]   ip_head_byte_num; //IP首部长度
reg  [15:0]  udp_byte_num    ; //UDP长度
reg  [15:0]  data_byte_num   ; //数据长度
reg  [15:0]  data_cnt        ; //有效数据计数    //*****************************************************
//**                    main code
//*****************************************************//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;  elsecur_state <= next_state;
end//组合逻辑判断状态转移条件
always @(*) beginnext_state = st_idle;case(cur_state)st_idle : begin                                     //等待接收前导码if(skip_en) next_state = st_preamble;elsenext_state = st_idle;    endst_preamble : begin                                 //接收前导码if(skip_en) next_state = st_eth_head;else if(error_en) next_state = st_rx_end;    elsenext_state = st_preamble;    endst_eth_head : begin                                 //接收以太网帧头if(skip_en) next_state = st_ip_head;else if(error_en) next_state = st_rx_end;elsenext_state = st_eth_head;           end  st_ip_head : begin                                  //接收IP首部if(skip_en)next_state = st_udp_head;else if(error_en)next_state = st_rx_end;elsenext_state = st_ip_head;       end st_udp_head : begin                                 //接收UDP首部if(skip_en)next_state = st_rx_data;elsenext_state = st_udp_head;    end                st_rx_data : begin                                  //接收有效数据if(skip_en)next_state = st_rx_end;elsenext_state = st_rx_data;    end                           st_rx_end : begin                                   //接收结束if(skip_en)next_state = st_idle;elsenext_state = st_rx_end;          enddefault : next_state = st_idle;endcase                                          
end    //时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginend else beginskip_en         <=  1'b0;error_en        <=  1'b0;rec_pkt_done    <=  1'b0;case (next_state)st_idle    :beginif ((gmii_rx_dv == 1'b1)&&(gmii_rxd == 8'h55)) beginskip_en     <=  1'b1;end endst_preamble:beginif (gmii_rx_dv) begincnt <= cnt + 1'b1;if((cnt < 5'd6) && (gmii_rxd != 8'h55))  //7个8'h55  error_en <= 1'b1;else if(cnt==5'd6) begincnt <= 5'd0;if(gmii_rxd==8'hd5)                  //1个8'hd5skip_en <= 1'b1;elseerror_en <= 1'b1;    endend endst_eth_head:beginif(gmii_rx_dv) begincnt <= cnt + 1'b1;if(cnt < 5'd6) des_mac <= {des_mac[39:0],gmii_rxd}; //目的MAC地址else if(cnt == 5'd12) eth_type[15:8] <= gmii_rxd;          //以太网协议类型else if(cnt == 5'd13) begineth_type[7:0] <= gmii_rxd;cnt <= 5'd0;//判断MAC地址是否为开发板MAC地址或者公共地址if(((des_mac == BOARD_MAC) ||(des_mac == 48'hff_ff_ff_ff_ff_ff))&& eth_type[15:8] == ETH_TYPE[15:8] && gmii_rxd == ETH_TYPE[7:0])            skip_en <= 1'b1;elseerror_en <= 1'b1;end        end  endst_ip_head :beginif (gmii_rx_dv) begincnt <= cnt + 1'b1;if (cnt == 5'd0) beginip_head_byte_num <= {gmii_rxd[3:0],2'd0};endelse if (cnt == 5'd9) beginif (gmii_rxd != UDP_TYPE) beginerror_en    <= 1'b1;cnt         <= 5'd0;endend else if ((cnt >= 5'd16)&&(cnt <= 5'd18))begin       //目的IP地址des_ip  <=  {des_ip[23:0],gmii_rxd};endelse if (cnt == 5'd19) begindes_ip  <=  {des_ip[23:0],gmii_rxd};        if ({des_ip[23:0],gmii_rxd} == BOARD_IP) begin  //判断目的IP是否为开发板skip_en     <= 1'b1;cnt         <= 5'd0;endelse begin                                      //IP错误error_en    <=  1'b0;cnt         <=  5'd0;end end end endst_udp_head:beginif (gmii_rx_dv) begincnt <= cnt + 1'b1;if ((cnt >= 5'd4)&&(cnt <= 5'd5)) begin             //解析UDP字节长度udp_byte_num    <=  {udp_byte_num[7:0],gmii_rxd};end else if(cnt == 5'd7) begin                          //有效长度=字节长度-首部长度data_byte_num   <=  udp_byte_num - 16'd8;skip_en         <=  1'b1;cnt             <=  5'd0;endend endst_rx_data :beginif (gmii_rx_dv) begindata_cnt    <=  data_cnt + 1'b1 ;                   //接收数据计数器rec_data    <=  gmii_rxd        ;                   //以太网接收数据rec_en      <=  1'b1            ;                   //接收数据使能信号if (data_cnt == data_byte_num - 1'b1) beginskip_en         <=  1'b1;data_cnt        <=  16'd0;rec_pkt_done    <=  1'b1;rec_byte_num    <=  data_byte_num;              //以太网接收数据有效数量end end endst_rx_end  :beginrec_en  <=  1'b0;                                       //单包数据传输完成if ((gmii_rx_dv == 1'b0) && (skip_en == 1'b0 ) ) beginskip_en <=  1'b1;endend default: ;endcaseend
end
endmodule

仿真结果: 

 

(2)udp_tx

 

module udp_tx(    input                clk        , //时钟信号input                rst_n      , //复位信号,低电平有效input                tx_start_en, //以太网开始发送信号input        [ 7:0]  tx_data    , //以太网待发送数据 input        [15:0]  tx_byte_num, //以太网发送的有效字节数input        [47:0]  des_mac    , //发送的目标MAC地址input        [31:0]  des_ip     , //发送的目标IP地址    input        [31:0]  crc_data   , //CRC校验数据input        [ 7:0]  crc_next   , //CRC下次校验完成数据output  reg          tx_done    , //以太网发送完成信号output  reg          tx_req     , //读数据请求信号output  reg          gmii_tx_en , //GMII输出数据有效信号output  reg  [7:0]   gmii_txd   , //GMII输出数据output  reg          crc_en     , //CRC开始校验使能output  reg          crc_clr      //CRC数据复位信号 
);//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.123     
parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd123}; 
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102     
parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102};localparam  st_idle      = 7'b000_0001; //初始状态,等待开始发送信号
localparam  st_check_sum = 7'b000_0010; //IP首部校验和
localparam  st_preamble  = 7'b000_0100; //发送前导码+帧起始界定符
localparam  st_eth_head  = 7'b000_1000; //发送以太网帧头
localparam  st_ip_head   = 7'b001_0000; //发送IP首部+UDP首部
localparam  st_tx_data   = 7'b010_0000; //发送数据
localparam  st_crc       = 7'b100_0000; //发送CRC校验值localparam  ETH_TYPE     = 16'h0800  ;  //以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam  MIN_DATA_NUM = 16'd18    ;  //reg define
reg  [6:0]   cur_state          ;
reg  [6:0]   next_state         ;        
reg  [7:0]   preamble   [7:0]   ; //前导码
reg  [7:0]   eth_head   [13:0]  ; //以太网首部
reg  [31:0]  ip_head    [6:0]   ; //IP首部 + UDP首部                      
reg          start_en_d0        ;
reg          start_en_d1        ;
reg          start_en_d2        ;
reg  [15:0]  tx_data_num        ; //发送的有效数据字节个数
reg  [15:0]  total_num          ; //总字节数
reg          trig_tx_en         ;
reg  [15:0]  udp_num            ; //UDP字节数
reg          skip_en            ; //控制状态跳转使能信号
reg  [4:0]   cnt                ;
reg  [31:0]  check_buffer       ; //首部校验和
reg  [1:0]   tx_bit_sel         ;
reg  [15:0]  data_cnt           ; //发送数据个数计数器
reg          tx_done_t          ;
reg  [4:0]   real_add_cnt       ; //以太网数据实际多发的字节数//wire define                       
wire         pos_start_en       ;//开始发送数据上升沿
wire [15:0]  real_tx_data_num   ;//实际发送的字节数(以太网最少字节要求)//采tx_start_en上升沿
assign  pos_start_en        = (!start_en_d2) & start_en_d1 ;
//判断实际发送数据
assign  real_tx_data_num    = (tx_data_num >= MIN_DATA_NUM) ? tx_data_num : MIN_DATA_NUM ;//采tx_start_en上升沿
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginstart_en_d0 <=  1'd0;start_en_d1 <=  1'd0;start_en_d2 <=  1'd0;end else beginstart_en_d0 <=  tx_start_en;start_en_d1 <=  start_en_d0;start_en_d2 <=  start_en_d1;end
end//寄存数据有效长度
always @(posedge clk or negedge rst_n) beginif (!rst_n) begintx_data_num <=  16'd0;      //发送的有效数据字节个数total_num   <=  16'd0;      //总字节数udp_num     <=  16'd0;      //UDP字节数end else beginif (pos_start_en && cur_state == st_idle) begin//有效数据长度tx_data_num     <=  tx_byte_num ;//IP长度:有效数据长度 + IP首部长度total_num       <=  tx_data_num + 16'd28;//UDP长度:有效数据 + UDP首部长度udp_num         <=  tx_byte_num + 16'd8;end end
end//寄存触发发送信号
always @(posedge clk or negedge rst_n) beginif(!rst_n) trig_tx_en <= 1'b0;elsetrig_tx_en <= pos_start_en;end//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;  elsecur_state <= next_state;
end//组合逻辑判断状态转移条件
always @(*) beginnext_state = st_idle;case(cur_state)st_idle     : begin                               //等待发送数据if(skip_en)                next_state = st_check_sum;elsenext_state = st_idle;end  st_check_sum: begin                               //IP首部校验if(skip_en)next_state = st_preamble;elsenext_state = st_check_sum;    end                             st_preamble : begin                               //发送前导码+帧起始界定符if(skip_en)next_state = st_eth_head;elsenext_state = st_preamble;      endst_eth_head : begin                               //发送以太网首部if(skip_en)next_state = st_ip_head;elsenext_state = st_eth_head;      end              st_ip_head : begin                                //发送IP首部+UDP首部               if(skip_en)next_state = st_tx_data;elsenext_state = st_ip_head;      endst_tx_data : begin                                //发送数据                  if(skip_en)next_state = st_crc;elsenext_state = st_tx_data;      endst_crc: begin                                     //发送CRC校验值if(skip_en)next_state = st_idle;elsenext_state = st_crc;      enddefault : next_state = st_idle;   endcase
end                      //时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginskip_en             <= 1'b0 ; cnt                 <= 5'd0 ;check_buffer        <= 32'd0;ip_head[1][31:16]   <= 16'd0;       //IP首部表示tx_bit_sel          <= 2'b0 ;crc_en              <= 1'b0 ;gmii_tx_en          <= 1'b0 ;gmii_txd            <= 8'd0 ;tx_req              <= 1'b0 ;tx_done_t           <= 1'b0 ; data_cnt            <= 16'd0;real_add_cnt        <= 5'd0 ;//初始化数组    //前导码 7个8'h55 + 1个8'hd5preamble[0]     <=  8'h55   ;                 preamble[1]     <=  8'h55   ;preamble[2]     <=  8'h55   ;preamble[3]     <=  8'h55   ;preamble[4]     <=  8'h55   ;preamble[5]     <=  8'h55   ;preamble[6]     <=  8'h55   ;preamble[7]     <=  8'hd5   ;//目的MAC地址eth_head[0]     <=  DES_MAC[47:40]  ;eth_head[1]     <=  DES_MAC[39:32]  ;eth_head[2]     <=  DES_MAC[31:24]  ;eth_head[3]     <=  DES_MAC[23:16]  ;eth_head[4]     <=  DES_MAC[15:8]   ;  eth_head[5]     <=  DES_MAC[7:0]    ;//源MAC地址eth_head[6]     <=  BOARD_MAC[47:40];eth_head[7]     <=  BOARD_MAC[39:32];eth_head[8]     <=  BOARD_MAC[31:24];eth_head[9]     <=  BOARD_MAC[23:16];eth_head[10]    <=  BOARD_MAC[15:8] ;eth_head[11]    <=  BOARD_MAC[7:0]  ;//以太网类型eth_head[12]    <=  ETH_TYPE[15:8]  ;eth_head[13]    <=  ETH_TYPE[7:0]   ;  end else beginskip_en     <= 1'b0;crc_en      <= 1'b0;gmii_tx_en  <= 1'b0;tx_done_t   <= 1'b0;case (next_state)st_idle      :begin                                         //等待发送数据if(trig_tx_en) beginskip_en     <= 1'b1; //版本号:4 首部长度:5(单位:32bit,20byte/4=5)ip_head[0]  <= {8'h45,8'h00,total_num};   //16位标识,每次发送累加1      ip_head[1][31:16]   <= ip_head[1][31:16] + 1'b1; //bit[15:13]: 010表示不分片ip_head[1][15:0]    <= 16'h4000;    //协议:17(udp)                  ip_head[2]  <= {8'h40,8'd17,16'h0};   //源IP地址               ip_head[3]  <= BOARD_IP;//目的IP地址    if(des_ip != 32'd0)ip_head[4]  <= des_ip;elseip_head[4]  <= DES_IP;       //16位源端口号:1234  16位目的端口号:1234                      ip_head[5]  <= {16'd1234,16'd1234};  //16位udp长度,16位udp校验和              ip_head[6]  <= {udp_num,16'h0000};  //更新MAC地址if(des_mac != 48'b0) begin//目的MAC地址eth_head[0] <= des_mac[47:40];eth_head[1] <= des_mac[39:32];eth_head[2] <= des_mac[31:24];eth_head[3] <= des_mac[23:16];eth_head[4] <= des_mac[15:8];eth_head[5] <= des_mac[7:0];endend   endst_check_sum :begin                                         //IP首部校验和cnt <= cnt + 1'b1;if (cnt == 5'd0) begin                                             check_buffer <=   ip_head[0][31:16] + ip_head[0][15:0]+ ip_head[1][31:16] + ip_head[1][15:0]+ ip_head[2][31:16] + ip_head[2][15:0]+ ip_head[3][31:16] + ip_head[3][15:0]+ ip_head[4][31:16] + ip_head[4][15:0];endelse if(cnt == 5'd1)                      //可能出现进位,累加一次check_buffer <= check_buffer[31:16] + check_buffer[15:0];else if(cnt == 5'd2) begin                //可能再次出现进位,累加一次check_buffer <= check_buffer[31:16] + check_buffer[15:0];end                             else if(cnt == 5'd3) begin                //按位取反 skip_en <= 1'b1;cnt     <= 5'd0;            ip_head[2][15:0] <= ~check_buffer[15:0];end endst_preamble  :begin                                         //发送前导码+帧起始界定符gmii_tx_en  <=  1'b1;gmii_txd    <=  preamble[cnt];if (cnt == 5'd7) beginskip_en <=  1'b1;cnt     <=  5'd0;endelse begincnt <= cnt + 1'b1;endendst_eth_head  :begin                                         //发送以太网首部gmii_tx_en  <=  1'b1;crc_en      <=  1'b1;gmii_txd    <=  eth_head[cnt];if (cnt == 5'd13) beginskip_en <=  1'b1;cnt     <=  5'd0;endelse begincnt <= cnt + 1'b1;endendst_ip_head   :begin                                         //发送IP首部 + UDP首部gmii_tx_en  <=  1'b1;crc_en      <=  1'b1;tx_bit_sel  <=  tx_bit_sel + 1'b1;if (tx_bit_sel == 2'd0) begingmii_txd    <=  ip_head[cnt][31:24];endelse if (tx_bit_sel == 2'd1) begingmii_txd    <=  ip_head[cnt][23:16];end else if (tx_bit_sel == 2'd2) begingmii_txd    <=  ip_head[cnt][15:8];if (cnt == 5'd6) begin//提前读请求数据,等待数据有效时发送tx_req <= 1'b1;     end else begintx_req <= 1'b0;   endendelse if (tx_bit_sel == 2'd3) begin      //tx_bit_sel自动溢出gmii_txd    <=  ip_head[cnt][7:0];if (cnt == 5'd6) beginskip_en <=  1'b1;cnt     <=  5'd0;end else begincnt     <=  cnt + 1'b1;    end    end endst_tx_data   :begin                                         //发送数据gmii_tx_en  <=  1'b1;crc_en      <=  1'b1;gmii_txd    <=  tx_data;tx_bit_sel  <=  tx_bit_sel + 1'b1;if (data_cnt < tx_data_num - 16'd1) begindata_cnt <= data_cnt + 1'b1;endelse if (data_cnt == tx_data_num - 16'd1) begin//如果发送的有效数据少于18个字节,在后面填补充位//补充的值为最后一次发送的有效数据if (data_cnt + real_add_cnt < real_tx_data_num - 16'd1) beginreal_add_cnt <= real_add_cnt + 1'b1;end else beginskip_en         <=  1'b1;data_cnt        <=  16'd0;real_add_cnt    <=  5'd0;tx_bit_sel      <=  3'd0;    endend if (data_cnt == tx_byte_num - 16'd2) begintx_req <= 1'b0;endendst_crc      : begin                                         //发送CRC校验值gmii_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 3'd1;tx_req <= 1'b0;  if(tx_bit_sel == 3'd0)gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};else if(tx_bit_sel == 3'd1)gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};else if(tx_bit_sel == 3'd2) begingmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};                              endelse if(tx_bit_sel == 3'd3) begingmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};  tx_done_t <= 1'b1;skip_en <= 1'b1;end else ;end  default: ;endcaseend
end//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintx_done <= 1'b0;crc_clr <= 1'b0;endelse begintx_done <= tx_done_t;crc_clr <= tx_done_t;end
endendmodule

仿真结果:

2、协议栈顶层

(1)框图及顶层

module eth_udp_loop(input                   sys_rst_n           ,   //系统复位信号,低电平有效 //PL以太网RGMII接口                     input                   eth_rxc             ,   //RGMII接收数据时钟input                   eth_rx_ctl          ,   //RGMII输入数据有效信号input       [3:0]       eth_rxd             ,   //RGMII输入数据output                  eth_txc             ,   //RGMII发送数据时钟    output                  eth_tx_ctl          ,   //RGMII输出数据有效信号output      [3:0]       eth_txd                 //RGMII输出数据          
);//parameter define
parameter  BOARD_MAC = 48'h00_11_22_33_44_55;       //开发板MAC地址 00-11-22-33-44-55
parameter  BOARD_IP  = {8'd192,8'd168,8'd1,8'd10};  //开发板IP地址 192.168.1.10
parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;       //目的MAC地址 ff_ff_ff_ff_ff_ff
parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102}; //目的IP地址 192.168.1.102     
parameter  IDELAY_VALUE = 15;                       //输入数据IO延时,此处为0,即不延时(如果为n,表示延时n*78ps) //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                    icmp_gmii_tx_en	        ;   //ICMP GMII输出数据有效信号 
wire          [7:0]     icmp_gmii_txd  	        ;   //ICMP GMII输出数据
wire                    icmp_rec_pkt_done       ;   //ICMP单包数据接收完成信号
wire                    icmp_rec_en             ;   //ICMP接收的数据使能信号
wire          [ 7:0]    icmp_rec_data           ;   //ICMP接收的数据
wire          [15:0]    icmp_rec_byte_num       ;   //ICMP接收的有效字节数 单位:byte 
wire          [15:0]    icmp_tx_byte_num        ;   //ICMP发送的有效字节数 单位:byte 
wire                    icmp_tx_done   	        ;   //ICMP发送完成信号
wire                    icmp_tx_req             ;   //ICMP读数据请求信号
wire          [ 7:0]    icmp_tx_data            ;   //ICMP待发送数据
wire                    icmp_tx_start_en        ;   //ICMP发送开始使能信号wire                    udp_gmii_tx_en	        ;   //UDP GMII输出数据有效信号 
wire          [7:0]     udp_gmii_txd  	        ;   //UDP GMII输出数据
wire                    rec_pkt_done  	        ;   //UDP单包数据接收完成信号
wire                    udp_rec_en    	        ;   //UDP接收的数据使能信号
wire          [ 7:0]    udp_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                    udp_tx_req    	        ;   //UDP读数据请求信号
wire          [ 7:0]    udp_tx_data   	        ;   //UDP待发送数据
wire                    tx_start_en   	        ;   //UDP发送开始使能信号wire          [7:0]	    rec_data			    ;   //FIFO写入数据
wire        		    rec_en			        ;   //FIFO写使能
wire        		    tx_req			        ;   //FIFO读使能
wire          [7:0]	    tx_data	    	        ;   //FIFO读出数据assign icmp_tx_start_en =   icmp_rec_pkt_done   ;   //ICMP 接收端结束标志,作为 ICMP发送端开始标志
assign icmp_tx_byte_num =   icmp_rec_byte_num   ;   //ICMP 接收端数据个数,作为 ICMP发送端发送数据 assign tx_start_en      =   rec_pkt_done        ;   //UDP 接收端结束标志,作为 UDP发送开始使能信号
assign tx_byte_num      =   rec_byte_num        ;   //UDP 接收端数据个数,作为 UDP发送端发送数据个数assign des_mac          =   src_mac             ;   //ARP 接收到的 源MAC,作为 ICMP\UDP 目的MAC,实际为电脑端MAC
assign des_ip           =   src_ip              ;   //ARP 接收到的 源IP ,作为 ICMP\UDP 目的IP ,实际为电脑端IP//数据位于异步FIFO之中,如需单独使用一侧功能,可以修改FIFO数据//需要注意写入有效数据数量要与此处相同,一定要注意 数据个数与FIFO读出数据对齐!!!!!!! //MMCM/PLL 产生200Mhz时钟--> gmii2rgmii
clk_wiz_0 u_clk_wiz_0
(.clk_out1           (clk_200m           ),      // output clk_out1.reset              (~sys_rst_n         ),      // input reset.locked             (locked             ),      // output locked.clk_in1            (eth_rxc            )       // PHY侧提供eth_rxc时钟125Mhz
);  //GMII接口转RGMII接口
gmii_to_rgmii 
#(.IDELAY_VALUE       (IDELAY_VALUE       )
)      
u_gmii_to_rgmii(        .idelay_clk         (clk_200m           ),      //IDELAY时钟//以太网GMII接口    .gmii_rx_clk        (gmii_rx_clk        ),      //GMII接收时钟.gmii_rx_dv         (gmii_rx_dv         ),      //GMII接收数据有效信号.gmii_rxd           (gmii_rxd           ),      //GMII接收数据.gmii_tx_clk        (gmii_tx_clk        ),      //GMII发送时钟.gmii_tx_en         (gmii_tx_en         ),      //GMII发送数据使能信号.gmii_txd           (gmii_txd           ),      //GMII发送数据   //以太网RGMII接口   .rgmii_rxc          (eth_rxc            ),      //RGMII接收时钟.rgmii_rx_ctl       (eth_rx_ctl         ),      //RGMII接收数据控制信号.rgmii_rxd          (eth_rxd            ),      //RGMII接收数据.rgmii_txc          (eth_txc            ),      //RGMII发送时钟    .rgmii_tx_ctl       (eth_tx_ctl         ),      //RGMII发送数据控制信号.rgmii_txd          (eth_txd            )       //RGMII发送数据   
);//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接口  //input.gmii_rx_clk        (gmii_rx_clk	    ),      //GMII接收数据时钟.gmii_rx_dv         (gmii_rx_dv 	    ),      //GMII输入数据有效信号.gmii_rxd           (gmii_rxd   	    ),      //GMII输入数据.gmii_tx_clk        (gmii_tx_clk	    ),      //GMII发送数据时钟//output.gmii_tx_en         (arp_gmii_tx_en     ),      //GMII输出数据有效信号.gmii_txd           (arp_gmii_txd       ),      //GMII输出数据         //用户接口                           //output.arp_rx_done        (arp_rx_done	    ),      //ARP接收完成信号.arp_rx_type        (arp_rx_type	    ),      //ARP接收类型 0:请求  1:应答.src_mac            (src_mac    	    ),      //接收到目的MAC地址.src_ip             (src_ip     	    ),      //接收到目的IP地址 //input   .arp_tx_en          (arp_tx_en  	    ),      //ARP发送使能信号.arp_tx_type        (arp_tx_type	    ),      //ARP发送类型 0:请求  1:应答.des_mac            (des_mac    	    ),      //发送的目标MAC地址.des_ip             (des_ip     	    ),      //发送的目标IP地址//output.tx_done            (arp_tx_done	    )       //以太网发送完成信号    
);  //ICMP通信  
icmp                                             
#(.BOARD_MAC          (BOARD_MAC          ),      //参数例化.BOARD_IP           (BOARD_IP           ),.DES_MAC            (DES_MAC            ),.DES_IP             (DES_IP             )
)
u_icmp(.rst_n              (sys_rst_n   	    ),      //复位信号,低电平有效//GMII接口//input.gmii_rx_clk        (gmii_rx_clk 	    ),      //GMII接收数据时钟         .gmii_rx_dv         (gmii_rx_dv  	    ),      //GMII输入数据有效信号       .gmii_rxd           (gmii_rxd    	    ),      //GMII输入数据                 .gmii_tx_clk        (gmii_tx_clk 	    ),      //GMII发送数据时钟//output.gmii_tx_en         (icmp_gmii_tx_en	),      //GMII输出数据有效信号       .gmii_txd           (icmp_gmii_txd	    ),      //GMII输出数据//用户接口//output.rec_pkt_done       (icmp_rec_pkt_done  ),      //以太网单包数据接收完成信号  .rec_en             (icmp_rec_en        ), 	    //以太网接收的数据使能信号				  .rec_data           (icmp_rec_data      ),      //以太网接收的数据				 	    .rec_byte_num       (icmp_rec_byte_num  ),      //以太网接收的有效字节数 单位:byte   //input.tx_start_en        (icmp_tx_start_en   ),      //以太网开始发送信号      .tx_data            (icmp_tx_data       ),      //以太网待发送数据					     .tx_byte_num        (icmp_tx_byte_num   ),      //以太网发送的有效字节数 单位:byte.des_mac            (des_mac     	    ),      //发送的目标MAC地址.des_ip             (des_ip      	    ),      //发送的目标IP地址  //output.tx_done            (icmp_tx_done	    ),      //以太网发送完成信号      .tx_req             (icmp_tx_req        )       //读数据请求信号					     
); //UDP通信
udp                                             
#(.BOARD_MAC          (BOARD_MAC          ),      //参数例化.BOARD_IP           (BOARD_IP           ),.DES_MAC            (DES_MAC            ),.DES_IP             (DES_IP             )
)
u_udp(.rst_n              (sys_rst_n          ),      //复位信号,低电平有效//GMII接口//input.gmii_rx_clk        (gmii_rx_clk        ),      //GMII接收数据时钟 .gmii_rx_dv         (gmii_rx_dv         ),      //GMII输入数据有效信号.gmii_rxd           (gmii_rxd           ),      //GMII输入数据.gmii_tx_clk        (gmii_tx_clk        ),      //GMII发送数据时钟   //output .gmii_tx_en         (udp_gmii_tx_en     ),      //GMII输出数据有效信号.gmii_txd           (udp_gmii_txd       ),      //GMII输出数据 //用户接口//outpur.rec_pkt_done       (rec_pkt_done       ),      //以太网单包数据接收完成信号 .rec_en             (udp_rec_en         ),      //以太网接收的数据使能信号.rec_data           (udp_rec_data       ),      //以太网接收的数据      .rec_byte_num       (rec_byte_num       ),      //以太网接收的有效字节数 单位:byte      //input.tx_start_en        (tx_start_en        ),      //以太网开始发送信号      .tx_data            (udp_tx_data        ),      //以太网待发送数据        .tx_byte_num        (tx_byte_num        ),      //以太网发送的有效字节数 单位:byte      .des_mac            (des_mac            ),      //发送的目标MAC地址      .des_ip             (des_ip             ),      //发送的目标IP地址    //output .tx_done            (udp_tx_done        ),      //以太网发送完成信号 .tx_req             (udp_tx_req         )       //读数据请求信号   
); //异步FIFO,实际做同步FIFO使用
async_fifo_2048x8b u_async_fifo_2048x8b (//input.rst                (~sys_rst_n	        ),      //input wire rst.wr_clk             (gmii_rx_clk        ),  	//input wire wr_clk.rd_clk             (gmii_rx_clk        ),  	//input wire rd_clk.din                (rec_data	        ),      //input wire [7 : 0] din.wr_en              (rec_en		        ),    	//input wire wr_en.rd_en              (tx_req		        ),    	//input wire rd_en//output.dout               (tx_data	        ),      //output wire [7 : 0] dout.full               (                   ),      //output wire full.empty              (                   )    	//output wire empty
);//以太网控制模块
eth_ctrl u_eth_ctrl(//input.clk            	(gmii_rx_clk	    ),      //时钟.rst_n          	(sys_rst_n		    ),      //系统复位信号,低电平有效 //ARP相关端口信号 //input.arp_rx_done    	(arp_rx_done   	    ),      //ARP接收完成信号.arp_rx_type    	(arp_rx_type   	    ),      //ARP接收类型 0:请求  1:应答.arp_tx_done    	(arp_tx_done   	    ),      //ARP发送完成信号.arp_gmii_tx_en 	(arp_gmii_tx_en	    ),      //ARP GMII输出数据有效信号 .arp_gmii_txd   	(arp_gmii_txd  	    ),      //ARP GMII输出数据//output.arp_tx_en      	(arp_tx_en     	    ),      //ARP发送使能信号.arp_tx_type    	(arp_tx_type   	    ),      //ARP发送类型 0:请求  1:应答//ICMP相关端口信号//input.icmp_tx_start_en	(icmp_tx_start_en   ),      //ICMP开始发送信号.icmp_tx_done		(icmp_tx_done	    ),      //ICMP发送完成信号.icmp_gmii_tx_en	(icmp_gmii_tx_en    ),      //ICMP GMII输出数据有效信号  .icmp_gmii_txd		(icmp_gmii_txd	    ),      //ICMP GMII输出数据 //ICMP fifo接口信号//input.icmp_rec_en       	(icmp_rec_en        ),      //ICMP接收的数据使能信号.icmp_rec_data     	(icmp_rec_data      ),      //ICMP接收的数据.icmp_tx_req       	(icmp_tx_req        ),      //ICMP读数据请求信号//output.icmp_tx_data      	(icmp_tx_data       ),      //ICMP待发送数据//UDP相关端口信号//input.udp_tx_start_en	(tx_start_en   	    ),      //UDP开始发送信号.udp_tx_done    	(udp_tx_done   	    ),      //UDP发送完成信号    .udp_gmii_tx_en 	(udp_gmii_tx_en	    ),      //UDP GMII输出数据有效信号  .udp_gmii_txd   	(udp_gmii_txd  	    ),      //UDP GMII输出数据   //UDP fifo接口信号//input.udp_rec_data		(udp_rec_data	    ),      //UDP接收的数据		.udp_rec_en			(udp_rec_en		    ),      //UDP接收的数据使能信号     .udp_tx_req			(udp_tx_req		    ),      //UDP读数据请求信号   //output .udp_tx_data		(udp_tx_data	    ),      //UDP待发送数据		//fifo接口信号//output.rec_data			(rec_data	        ),      //待发送的数据	.rec_en	        	(rec_en	            ),      //读数据请求信号 .tx_req	        	(tx_req	            ),      //接收的数据使能信号//input.tx_data	    	(tx_data	        ),      //接收的数据//GMII发送引脚  //output.gmii_tx_en     	(gmii_tx_en    	    ),      //GMII输出数据有效信号 .gmii_txd       	(gmii_txd      	    )       //GMII输出数据 
);      endmodule

(2)代码框架

3、资源消耗情况

五、下载验证

ping测试:

网口环回测试:

 

六、福利获取

后台私信   UDP协议分析表,即可获得 UDP协议分析表原件!!!!

七、工程移植、源码获取请后台私信

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/502555.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

edeg插件/扩展推荐:助力生活工作

WeTab 此插件在我看来有2个作用 1.改变edeg的主页布局和样式,使其更加精简,无广告 2.提供付费webtab Ai(底层是chatGpt) 沉浸式翻译 此插件可翻译网页的内容 假设我们浏览github 翻译前 翻译后 Better Ruler 可以对网页的距离进行测量 适合写前端的小伙伴 用法示例:

java项目之校园管理系统的设计与实现(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的校园管理系统的设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; springboot校园…

设计模式 结构型 适配器模式(Adapter Pattern)与 常见技术框架应用 解析

适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户端所期望的另一个接口&#xff0c;从而使原本因接口不兼容而无法一起工作的类能够协同工作。这种设计模式在软件开发中非常有用&#xff0c;尤其是在需要集成…

打造三甲医院人工智能矩阵新引擎(一):文本大模型篇--基于GPT-4o的探索

一、引言 当今时代,人工智能技术正以前所未有的速度蓬勃发展,深刻且广泛地渗透至各个领域,医疗行业更是这场变革的前沿阵地。在人口老龄化加剧、慢性疾病患病率上升以及人们对健康需求日益增长的大背景下,三甲医院作为医疗体系的核心力量,承担着极为繁重且复杂的医疗任务。…

S7-200采集频率信号

S7-200可以借助高速计数器完成频率信号采集&#xff0c;接入流量计、转速等信号。官方给出的程序块无法完成多路同时采集&#xff0c;需要自己进行修改。 首先下载官方的频率采集库 SIOS 下载后导入library&#xff0c;在library中出现Frequency(v1.0) 拖进ladder后&#xf…

专家混合(MoE)大语言模型:免费的嵌入模型新宠

专家混合&#xff08;MoE&#xff09;大语言模型&#xff1a;免费的嵌入模型新宠 今天&#xff0c;我们深入探讨一种备受瞩目的架构——专家混合&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;大语言模型&#xff0c;它在嵌入模型领域展现出了独特的魅力。 一、M…

【Vue】分享一个快速入门的前端框架以及如何搭建

先上效果图: 登录 菜单: 下载地址: 链接&#xff1a;https://pan.baidu.com/s/1m-ZlBARWU6_2n8jZil_RAQ 提取码&#xff1a;ui20 … 主要是可以自定义设置token,更改后端请求地址较为方便。 应用设置: 登录与token设置: 在这里设置不用登录,可以请求的接口: request.js i…

MySQL叶子节点为啥使用双向链表?不使用单向呢?

文章内容收录到个人网站&#xff0c;方便阅读&#xff1a;http://hardyfish.top/ 文章内容收录到个人网站&#xff0c;方便阅读&#xff1a;http://hardyfish.top/ 文章内容收录到个人网站&#xff0c;方便阅读&#xff1a;http://hardyfish.top/ MySQL 中的 B 树索引&#x…

用户界面的UML建模10

非正常的可视反馈可伴随着同步事件发生&#xff0c;而同步事件可由系统动作产生。但是&#xff0c;可以分别对它们进行建模。 在下节中将对这些特殊的事件依次进行论述。 6.1 异常处理建模 异常&#xff0c;由Meyer 定义[16],其作为运行时事件&#xff08;run-time events&a…

最新版Chrome浏览器加载ActiveX控件之CFCA安全输入控件

背景 CFCA安全输入控件用于保证用户在浏览器、桌面客户端、移动客户端中输入信息的安全性&#xff0c;防止运行在用户系统上的病毒、木马等恶意程序入侵窃取用户输入的敏感信息。确保用户输入、本地缓存、网络传输整个流程中&#xff0c;输入的敏感信息不被窃取。广泛应用于银行…

0基础跟德姆(dom)一起学AI 自然语言处理10-LSTM模型

1 LSTM介绍 LSTM&#xff08;Long Short-Term Memory&#xff09;也称长短时记忆结构, 它是传统RNN的变体, 与经典RNN相比能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时LSTM的结构更复杂, 它的核心结构可以分为四个部分去解析: 遗忘门输入门细胞状态输出门…

力扣283 移动零

void moveZeroes(int* nums, int numsSize) {int last_non_zero_found_at 0;for (int i 0; i < numsSize; i) {if (nums[i] ! 0) {// 交换 nums[last_non_zero_found_at] 和 nums[i]int temp nums[last_non_zero_found_at];nums[last_non_zero_found_at] nums[i];nums[i…

LookingGlass使用

文章目录 背景编译安装运行限制使用场景总结参考 背景 Looking Glass 是一款开源应用程序&#xff0c;可以直接使用显卡直通的windows虚拟机。 常见环境是Linux hostwindows guest&#xff0c;基本部署结构图&#xff1a; 编译 git clone --recursive https://github.com/g…

JVM学习:CMS和G1收集器浅析

总框架 一、Java自动内存管理基础 1、运行时数据区 运行时数据区可分为线程隔离和线程共享两个维度&#xff0c;垃圾回收主要是针对堆内存进行回收 &#xff08;1&#xff09;线程隔离 程序计数器 虚拟机多线程是通过线程轮流切换、分配处理器执行时间来实现的。为了线程切换…

关于 webservice 日志中 源IP是node IP的问题,是否能解决换成 真实的客户端IP呢

本篇目录 1. 问题背景2. 部署gitlab 17.52.1 添加repo源2.2 添加repo源 下载17.5.0的charts包2.3 修改values文件2.3.1 hosts修改如下2.3.2 appConfig修改如下2.3.3 gitlab下的sidekiq配置2.3.4 certmanager修改如下2.3.5 nginx-ingress修改如下2.3.6 <可选> prometheus修…

javaEE-网络编程-3 UDP

目录 Socaket套接字 UDP数据报套字节编程 1.DatagrameSocket类 DatagramSocaket构造方法: DatagramSocaket常用方法&#xff1a; 2.DatagramPacket类 DatagramPacket构造方法&#xff1a; UDP回显服务器实现 UDP服务端实现&#xff1a; 创建一个Socket类对象&#xf…

【Vim Masterclass 笔记08】第 6 章:Vim 中的文本变换及替换操作 + S06L20:文本的插入、变更、替换,以及合并操作

文章目录 Section 6&#xff1a;Transforming and Substituting TextS06L21 Inserting, Changing, Replacing, and Joining1 定位到行首非空字符&#xff0c;并启用插入模式2 在紧挨光标的下一个字符位置启动插入模式3 定位到一行末尾&#xff0c;并启用插入模式4 定位到光标的…

Transformer知识梳理

Transformer知识梳理 文章目录 Transformer知识梳理什么是Transformer&#xff1f;语言模型迁移学习 Transformer结构注意力层原始结构 总结 什么是Transformer&#xff1f; 语言模型 Transformer模型本质上都是预训练语言模型&#xff0c;大部分采用自监督学习&#xff08;S…

小程序学习06——uniapp组件常规引入和easycom引入语法

目录 一 组件注册 1.1 组件全局注册 1.2 组件全局引入 1.3 组件局部引入 页面引入组件方式 1.3.1 传统vue规范&#xff1a; 1.3.2 通过uni-app的easycom 二 组件的类型 2.1 基础组件列表 一 组件注册 1.1 组件全局注册 &#xff08;a&#xff09;新建compoents文件…

数据插入操作的深度分析:INSERT 语句使用及实践

title: 数据插入操作的深度分析:INSERT 语句使用及实践 date: 2025/1/5 updated: 2025/1/5 author: cmdragon excerpt: 在数据库管理系统中,数据插入(INSERT)操作是数据持久化的基础,也是应用程序与用户交互的核心功能之一。它不仅影响数据的完整性与一致性,还在数据建…