以太网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) begin
    if(!rst_n)
        cur_state <= st_idle;  
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle : begin                                     //等待接收前导码
            if(skip_en) 
                next_state = st_preamble;
            else
                next_state = st_idle;    
        end
        st_preamble : begin                                 //接收前导码
            if(skip_en) 
                next_state = st_eth_head;
            else if(error_en) 
                next_state = st_rx_end;    
            else
                next_state = st_preamble;    
        end
        st_eth_head : begin                                 //接收以太网帧头
            if(skip_en) 
                next_state = st_ip_head;
            else if(error_en) 
                next_state = st_rx_end;
            else
                next_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;
            else
                next_state = st_ip_head;       
        end 
        st_udp_head : begin                                 //接收UDP首部
            if(skip_en)
                next_state = st_rx_data;
            else
                next_state = st_udp_head;    
        end                
        st_rx_data : begin                                  //接收有效数据
            if(skip_en)
                next_state = st_rx_end;
            else
                next_state = st_rx_data;    
        end                           
        st_rx_end : begin                                   //接收结束
            if(skip_en)
                next_state = st_idle;
            else
                next_state = st_rx_end;          
        end
        default : next_state = st_idle;
    endcase                                          
end    

//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        
    end 
    else begin
        skip_en         <=  1'b0;
        error_en        <=  1'b0;
        rec_pkt_done    <=  1'b0;
        case (next_state)
            st_idle    :begin
                if ((gmii_rx_dv == 1'b1)&&(gmii_rxd == 8'h55)) begin
                    skip_en     <=  1'b1;
                end 
            end
            st_preamble:begin
                if (gmii_rx_dv) begin
                    cnt <= cnt + 1'b1;
                    if((cnt < 5'd6) && (gmii_rxd != 8'h55))  //7个8'h55  
                        error_en <= 1'b1;
                    else if(cnt==5'd6) begin
                        cnt <= 5'd0;
                        if(gmii_rxd==8'hd5)                  //1个8'hd5
                            skip_en <= 1'b1;
                        else
                            error_en <= 1'b1;    
                    end
                end 
            end
            st_eth_head:begin
                if(gmii_rx_dv) begin
                    cnt <= 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) begin
                        eth_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;
                        else
                            error_en <= 1'b1;
                    end        
                end  
            end
            st_ip_head :begin
                if (gmii_rx_dv) begin
                    cnt <= cnt + 1'b1;
                    if (cnt == 5'd0) begin
                        ip_head_byte_num <= {gmii_rxd[3:0],2'd0};
                    end
                    else if (cnt == 5'd9) begin
                        if (gmii_rxd != UDP_TYPE) begin
                            error_en    <= 1'b1;
                            cnt         <= 5'd0;
                        end
                    end 
                    else if ((cnt >= 5'd16)&&(cnt <= 5'd18))begin       //目的IP地址
                        des_ip  <=  {des_ip[23:0],gmii_rxd};
                    end
                    else if (cnt == 5'd19) begin
                        des_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;
                        end
                        else begin                                      //IP错误
                            error_en    <=  1'b0;
                            cnt         <=  5'd0;
                        end 
                    end 
                end 
            end
            st_udp_head:begin
                if (gmii_rx_dv) begin
                    cnt <= 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;
                    end
                end 
            end
            st_rx_data :begin
                if (gmii_rx_dv) begin
                    data_cnt    <=  data_cnt + 1'b1 ;                   //接收数据计数器
                    rec_data    <=  gmii_rxd        ;                   //以太网接收数据
                    rec_en      <=  1'b1            ;                   //接收数据使能信号
                    if (data_cnt == data_byte_num - 1'b1) begin
                        skip_en         <=  1'b1;
                        data_cnt        <=  16'd0;
                        rec_pkt_done    <=  1'b1;
                        rec_byte_num    <=  data_byte_num;              //以太网接收数据有效数量
                    end 
                end 
            end
            st_rx_end  :begin
                rec_en  <=  1'b0;                                       //单包数据传输完成
                if ((gmii_rx_dv == 1'b0) && (skip_en == 1'b0 ) ) begin
                    skip_en <=  1'b1;
                end
            end 
            default: ;
        endcase
    end
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) begin
    if (!rst_n) begin
        start_en_d0 <=  1'd0;
        start_en_d1 <=  1'd0;
        start_en_d2 <=  1'd0;
    end 
    else begin
        start_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) begin
    if (!rst_n) begin
        tx_data_num <=  16'd0;      //发送的有效数据字节个数
        total_num   <=  16'd0;      //总字节数
        udp_num     <=  16'd0;      //UDP字节数
    end 
    else begin
        if (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) begin
    if(!rst_n) 
        trig_tx_en <= 1'b0;
    else
        trig_tx_en <= pos_start_en;

end

//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;  
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle     : begin                               //等待发送数据
            if(skip_en)                
                next_state = st_check_sum;
            else
                next_state = st_idle;
        end  
        st_check_sum: begin                               //IP首部校验
            if(skip_en)
                next_state = st_preamble;
            else
                next_state = st_check_sum;    
        end                             
        st_preamble : begin                               //发送前导码+帧起始界定符
            if(skip_en)
                next_state = st_eth_head;
            else
                next_state = st_preamble;      
        end
        st_eth_head : begin                               //发送以太网首部
            if(skip_en)
                next_state = st_ip_head;
            else
                next_state = st_eth_head;      
        end              
        st_ip_head : begin                                //发送IP首部+UDP首部               
            if(skip_en)
                next_state = st_tx_data;
            else
                next_state = st_ip_head;      
        end
        st_tx_data : begin                                //发送数据                  
            if(skip_en)
                next_state = st_crc;
            else
                next_state = st_tx_data;      
        end
        st_crc: begin                                     //发送CRC校验值
            if(skip_en)
                next_state = st_idle;
            else
                next_state = st_crc;      
        end
        default : next_state = st_idle;   
    endcase
end                      

//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        skip_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'hd5
        preamble[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 begin
        skip_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) begin
                    skip_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;
                    else
                        ip_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];
                    end
                end   
            end
            st_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];
                end
                else 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 
            end
            st_preamble  :begin                                         //发送前导码+帧起始界定符
                gmii_tx_en  <=  1'b1;
                gmii_txd    <=  preamble[cnt];
                if (cnt == 5'd7) begin
                    skip_en <=  1'b1;
                    cnt     <=  5'd0;
                end
                else begin
                    cnt <= cnt + 1'b1;
                end
            end
            st_eth_head  :begin                                         //发送以太网首部
                gmii_tx_en  <=  1'b1;
                crc_en      <=  1'b1;
                gmii_txd    <=  eth_head[cnt];
                if (cnt == 5'd13) begin
                    skip_en <=  1'b1;
                    cnt     <=  5'd0;
                end
                else begin
                    cnt <= cnt + 1'b1;
                end
            end
            st_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) begin
                    gmii_txd    <=  ip_head[cnt][31:24];
                end
                else if (tx_bit_sel == 2'd1) begin
                    gmii_txd    <=  ip_head[cnt][23:16];
                end 
                else if (tx_bit_sel == 2'd2) begin
                    gmii_txd    <=  ip_head[cnt][15:8];
                    if (cnt == 5'd6) begin
                        //提前读请求数据,等待数据有效时发送
                        tx_req <= 1'b1;     
                    end 
                    else begin
                        tx_req <= 1'b0;   
                    end
                end
                else if (tx_bit_sel == 2'd3) begin      //tx_bit_sel自动溢出
                    gmii_txd    <=  ip_head[cnt][7:0];
                    if (cnt == 5'd6) begin
                        skip_en <=  1'b1;
                        cnt     <=  5'd0;
                    end 
                    else begin
                        cnt     <=  cnt + 1'b1;    
                    end    
                end 
            end
            st_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) begin
                    data_cnt <= data_cnt + 1'b1;
                end
                else if (data_cnt == tx_data_num - 16'd1) begin
                    //如果发送的有效数据少于18个字节,在后面填补充位
                    //补充的值为最后一次发送的有效数据
                    if (data_cnt + real_add_cnt < real_tx_data_num - 16'd1) begin
                        real_add_cnt <= real_add_cnt + 1'b1;
                    end 
                    else begin
                        skip_en         <=  1'b1;
                        data_cnt        <=  16'd0;
                        real_add_cnt    <=  5'd0;
                        tx_bit_sel      <=  3'd0;    
                    end
                end 

                if (data_cnt == tx_byte_num - 16'd2) begin
                    tx_req <= 1'b0;
                end
            end
            st_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) begin
                    gmii_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]};                              
                end
                else if(tx_bit_sel == 3'd3) begin
                    gmii_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: ;
        endcase
    end
end

//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_done <= 1'b0;
        crc_clr <= 1'b0;
    end
    else begin
        tx_done <= tx_done_t;
        crc_clr <= tx_done_t;
    end
end



endmodule

仿真结果:

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协议分析表原件!!!!

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

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

大模型推理加速调研(框架、方法)

大模型推理加速调研&#xff08;框架、方法&#xff09; 大模型推理框架调研总结推理框架TensorRT-LLMllama.cppmnn-llmfastllmmlc-llm 环境搭建&部署推理环境llama.cppfastllmmnn-llmvllm vllm_openai_completions.pylmdeployTensorRT-LLM 大模型加速技术总结模型压缩量化…

遮挡半透明效果

1、遮挡半透明效果是什么 在游戏开发中&#xff0c;遮挡半透明效果就是物体被挡住的部分&#xff0c;也能呈现出一种半透明效果而被看到&#xff08;具体效果可以自定义&#xff09;比如 当角色在建筑物之间穿行时&#xff0c;被遮挡部分能够呈现出半透明效果而被我们看到。遮…

操作系统——并发控制

学习目标 两个进程之间互斥&#xff0c;但也承担了唤醒对方得义务&#xff0c;不然就一直被自己阻塞 互斥条件与解决方案 互斥的要求

【Android项目学习】3. MVVMHabit

项目链接 文章目录 一. 项目结构1. 项目整体划分2. 模块细分 二. Android知识点学习1. registerActivityLifecycleCallbacks方法2. 一. 项目结构 1. 项目整体划分 MVVMHabit是以谷歌DataBindingLiveDataViewModel框架为基础&#xff0c;整合OkhttpRxJavaRetrofitGlide等流行…

【虚拟机】VMware 16图文安装和配置 AlmaLinux OS 9.5 教程

准备工作 下载AlmaLinux ISO文件&#xff1a;从AlmaLinux官方网站&#xff08;https://almalinux.org/&#xff09;下载最新版本的ISO文件。 安装VMware Workstation&#xff1a;确保您的计算机上已安装VMware Workstation。&#xff08;注&#xff1a;我这边使用的是VMware16…

【数据结构】链表(2):双向链表和双向循环链表

双向链表&#xff08;Doubly Linked List&#xff09; 定义&#xff1a; 每个节点包含三个部分&#xff1a; 数据域。前驱指针域&#xff08;指向前一个节点&#xff09;。后继指针域&#xff08;指向下一个节点&#xff09;。 支持从任意节点向前或向后遍历。 #define dat…

CryptoHack:Diffie-Hellman(STARTER)

1.Working with Fields 这里的主要目的是算乘法逆元d 我们有RSA中算乘法逆元的基础这里就很快了&#xff0c;找到“e”和“phi”就是题目中的“g”和"N" # Diffie-Hellman算法 import gmpy2 p991 N991 g209 dgmpy2.invert(g, N) print(d) #5692.Generators of Grou…

Transformer知识梳理

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

计算机缺失x3daudio1 7.dll怎么修复?

电脑运行时常见问题解析与修复策略&#xff1a;以“x3daudio1_7.dll缺失”为例 在软件开发与日常电脑维护的广阔领域中&#xff0c;我们时常会遇到各种系统报错和文件问题。这些问题不仅影响我们的工作效率&#xff0c;还可能对数据安全构成潜在威胁。作为一位经验丰富的软件开…

mysql找回误删除数据

查看binlog日志是否开启以及日志文件位置 SHOW VARIABLES LIKE %log_bin%; log_bin 为 ON表示开启状态 /var/mysql/data/ 为binlog日志文件位置 查看正在使用的binlog日志文件 show master status; 通过第一步和第二步可以确定文件位置 将二进制文件转为txt或者sql文件 m…

第五届神经网络、信息与通信工程国际学术会议(NNICE 2025)

在线投稿&#xff1a;学术会议-学术交流征稿-学术会议在线-艾思科蓝 征稿主题&#xff1a; 神经网络 机器人控制 优化组合 知识工程 人工智能 逻辑程序设计 人机交互 深度学习 信号处理 信息提取 自然语言推论 信号与信息处理 信息管理与集成 实时信号处理与应用、 DSP应用 图…

JVM对象内存结构

1对象内存结构说明 注意&#xff1a; 如果对象为数组对象&#xff0c;在对象头后面有4字节存储数组长度&#xff1b; 1.1对象头 对象头分为Mark Word和Class Pointer两部分&#xff1b; Mark Word&#xff1a;对象基础信息 32位操作系统中占4字节&#xff0c;64位操作系统中占8…

创建并配置华为云虚拟私有云

目录 私有云 创建虚拟私有云 私有云 私有云是一种云计算模式&#xff0c;它将云服务部署在企业或组织内部的私有基础设施上&#xff0c;仅供该企业或组织内部使用&#xff0c;不对外提供服务.私有云的主要特点包括&#xff1a; 私密性&#xff1a;私有云的资源&#xff08;如…

【FlutterDart】 拖动改变 widget 的窗口尺寸大小GestureDetector~简单实现(10 /100)

上效果 预期的是通过拖动一条边界线改变窗口大小&#xff0c;类似vscode里拖动效果。这个是简单的拖动实现 上代码&#xff1a; import package:flutter/material.dart;class MyDraggableViewDemo extends StatelessWidget {const MyDraggableViewDemo({super.key});override…

并发服务器框架——zinx

zinx框架 Zinx 是一个用 Go 语言编写的高性能、轻量级的 TCP 服务器框架&#xff0c;它被设计为简单、快速且易于使用。Zinx 提供了一系列的功能&#xff0c;包括但不限于连接管理、数据编解码、业务处理、负载均衡等&#xff0c;适用于构建各种 TCP 网络服务&#xff0c;如游戏…

前端(API)学习笔记(CLASS 4):进阶

1、日期对象 日期对象&#xff1a;用来表示事件的对象 作用&#xff1a;可以得到当前系统时间 1、实例化 在代码中发现了new关键字&#xff0c;一般将这个操作称为实例化 创建一个时间对象并获取时间 获得当前时间 const datenew Date() 使用日志查看&#xff0c;得到的…

LeetCode:98.验证二叉搜索树

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;98.验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 …

k8s基础(1)—Kubernetes-Pod

一、Pod简介 Pod是Kubernetes&#xff08;k8s&#xff09;系统中可以创建和管理的最小单元&#xff0c;是资源对象模型中由用户创建或部署的最小资源对象模型‌。Pod是由一个或多个容器组成的&#xff0c;这些容器共享存储和网络资源&#xff0c;可以看作是一个逻辑的主机‌。…

vue 项目集成 electron 和 electron 打包及环境配置

vue electron 开发桌面端应用 安装 electron npm i electron -D记得加上-D&#xff0c;electron 需添加到devDependencies&#xff0c;如果添加到dependencies后面运行可能会报错 根目录创建electron文件夹&#xff0c;在electron文件夹创建main.js&#xff08;或者backgrou…