以太网ARP测试实验

news2024/11/17 5:50:50

1.1 ARP测试整体框架

        当上位机发送ARP请求时,FPGA返回ARP应答数据;当按下FPGA的触摸按键时,FPGA发送ARP请求,上位机返回ARP应答数据。

 PLL时钟对eth_rxc的输入时钟进行相位调整;GMII TO RGMI 模块负责将双沿(DDR)数据和单沿(SDR)数据之间的转换;ARP顶层模块实现了以太网ARP数据包的接收、发送以及CRC校验的功能;ARP控制模块根据输入的按键触摸信号和接收到的ARP请求信号,控制ARP顶层模块发送ARP请求或者ARP应答。

module eth_arp_test(
    input              sys_rst_n , //系统复位信号,低电平有效 
    input              touch_key , //触摸按键,用于触发开发板发出ARP请求
    //以太网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输出数据          
    output             eth_rst_n   //以太网芯片复位信号,低电平有效   
    );


//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};   
//目的MAC地址 ff_ff_ff_ff_ff_ff           广播MAC地址,收到上位机的请求或应答后ARP模块会替换成真正的目的MAC地址
parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102                需要与电脑的以太网IP地址一致
parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102};

//wire define      
wire          eth_rxc_deg   ; //eth_rxc时钟相位偏移 
 
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发送完成信号

//*****************************************************
//**                    main code
//*****************************************************

assign des_mac = src_mac;          //将收到的目的MAC地址和IP地址,作为FPGA发送时的目的MAC地址和IP地址
assign des_ip = src_ip;
assign eth_rst_n = sys_rst_n;

//PLL
pll u_pll(
    .inclk0  (eth_rxc),            //相位偏移180度
    .c0      (eth_rxc_deg),
    .locked  ()
    );

//GMII接口转RGMII接口
gmii_to_rgmii u_gmii_to_rgmii(

    .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         (eth_rxc_deg ),
    .rgmii_rx_ctl      (eth_rx_ctl  ),
    .rgmii_rxd         (eth_rxd     ),
    .rgmii_txc         (eth_txc     ),
    .rgmii_tx_ctl      (eth_tx_ctl  ),
    .rgmii_txd         (eth_txd     )
    );

//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    (gmii_tx_en ),
    .gmii_txd      (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       (tx_done    )
    );

//ARP控制
arp_ctrl u_arp_ctrl(
    .clk           (gmii_rx_clk),
    .rst_n         (sys_rst_n),
                   
    .touch_key     (touch_key),
    .arp_rx_done   (arp_rx_done),
    .arp_rx_type   (arp_rx_type),
    .arp_tx_en     (arp_tx_en),
    .arp_tx_type   (arp_tx_type)
    );

endmodule

1.2 GMII TO RGMII模块

//实现双沿(DDR)和单沿(SDR)数据之间的转换
module gmii_to_rgmii(
    //以太网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发送数据          
    );

//*****************************************************
//**                    main code
//*****************************************************

assign gmii_tx_clk = gmii_rx_clk;     //将GMII接收时钟赋值给GMII发送时钟,因此GMII的发送时钟和接收时钟为同一时钟

//RGMII接收
rgmii_rx u_rgmii_rx(
    .rgmii_rxc     (rgmii_rxc   ),
    .rgmii_rx_ctl  (rgmii_rx_ctl),
    .rgmii_rxd     (rgmii_rxd   ),
    
    .gmii_rx_dv    (gmii_rx_dv ),
    .gmii_rxd      (gmii_rxd   ),
    .gmii_rx_clk   (gmii_rx_clk)
    );

//RGMII发送
rgmii_tx u_rgmii_tx(
    .gmii_tx_clk   (gmii_tx_clk ),
    .gmii_tx_en    (gmii_tx_en  ),
    .gmii_txd      (gmii_txd    ),
              
    .rgmii_txc     (rgmii_txc   ),
    .rgmii_tx_ctl  (rgmii_tx_ctl),
    .rgmii_txd     (rgmii_txd   )
    );

endmodule

关于 ALTDDIO_IN、ALTDDIO_OUT这两个IP核,可以参考:Altera(Quartus) IP核 ALTDDIO_IN、ALTDDIO_OUT(双倍数据速率I/O,DDIO)的使用和仿真_孤独的单刀的博客-CSDN博客

module rgmii_rx(
    //以太网RGMII接口
    input              rgmii_rxc   , //RGMII接收时钟
    input              rgmii_rx_ctl, //RGMII接收数据控制信号
    input       [3:0]  rgmii_rxd   , //RGMII接收数据    

    //以太网GMII接口
    output             gmii_rx_clk , //GMII接收时钟
    output             gmii_rx_dv  , //GMII接收数据有效信号
    output      [7:0]  gmii_rxd      //GMII接收数据   
    );   

//wire define    
wire  [1:0]  gmii_rxdv_t;        //两位GMII接收有效信号 

//*****************************************************
//**                    main code
//*****************************************************

assign gmii_rx_clk = rgmii_rxc;
assign gmii_rx_dv = gmii_rxdv_t[0] & gmii_rxdv_t[1];

//通过ALTDDIO_IN这个IP实现双沿(DDR)和单沿(SDR)数据之间的转换
ddi_x4 ddi_x4_inst(
    .datain     (rgmii_rxd    ),    //datain,DDR输入端口
    .inclock    (rgmii_rxc    ),    //inclock,DDR输入数据的采样时钟信号,在时钟信号的每个时钟边缘上对数据端口进行采样
    .dataout_h  (gmii_rxd[7:4]),    //dataout_h,在inclock时钟上升沿输出的数据
    .dataout_l  (gmii_rxd[3:0])     //dataout_l,在inclock时钟下降沿输出的数据
);

ddi_x1 ddi_x1_inst(
    .datain     (rgmii_rx_ctl),
    .inclock    (rgmii_rxc  ),
    .dataout_h  (gmii_rxdv_t[1]),
    .dataout_l  (gmii_rxdv_t[0])
);

endmodule

Width是代表输入端的位宽,RGMII接口是4位输入,所以这里将其设为4
Asynchronous clear and asynchronous set ports选项是表示端口是否需要复位,这里选择 Not used
Use'inclocken’ports表示是否使用输入时钟使能,这里选择不使用
Invert input clock表示输入时钟上升边缘上的第一个数据位是否被捕获。如果不启用,则在输入时钟的下降沿上捕获数据的第一个位,启用表示在输入时钟的上升沿上捕获数据的第一个位,这里选择不使用

module rgmii_tx(
    //GMII发送端口
    input              gmii_tx_clk ,   //GMII发送时钟 
    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输出数据     
    );

//*****************************************************
//**                    main code
//*****************************************************

//通过ALTDDIO OUT这个IP实现SDR与DDR之间的转换
ddo_x4 ddo_x4_inst(
    .datain_h(gmii_txd[3:0]),
    .datain_l(gmii_txd[7:4]),
    .outclock(gmii_tx_clk),
    .dataout(rgmii_txd)
);

ddo_x1 ddo_x1_inst(
    .datain_h(gmii_tx_en),
    .datain_l(gmii_tx_en ), 
    .outclock(gmii_tx_clk),
    .dataout(rgmii_tx_ctl)
);

ddo_x1 ddo_x1_clk(
    .datain_h(1'b1),
    .datain_l(1'b0),
    .outclock(gmii_tx_clk), 
    .dataout(rgmii_txc)
);

endmodule

1.3 ARP模块 

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      //以太网发送完成信号    
    );

//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};   
//目的MAC地址 ff_ff_ff_ff_ff_ff           广播MAC地址,收到上位机的请求或应答后ARP模块会替换成真正的目的MAC地址
parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102                需要与电脑的以太网IP地址一致
parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102};



//wire define
wire           crc_en  ; //CRC开始校验使能
wire           crc_clr ; //CRC数据复位信号 
wire   [7:0]   crc_d8  ; //输入待校验8位数据
wire   [31:0]  crc_data; //CRC校验数据
wire   [31:0]  crc_next; //CRC下次校验完成数据

//*****************************************************
//**                    main code
//*****************************************************

assign  crc_d8 = gmii_txd;

//ARP接收模块    
arp_rx 
   #(
    .BOARD_MAC       (BOARD_MAC),         //参数例化
    .BOARD_IP        (BOARD_IP )
    )
   u_arp_rx(
    .clk             (gmii_rx_clk),
    .rst_n           (rst_n),

    .gmii_rx_dv      (gmii_rx_dv),
    .gmii_rxd        (gmii_rxd  ),
    .arp_rx_done     (arp_rx_done),
    .arp_rx_type     (arp_rx_type),
    .src_mac         (src_mac    ),
    .src_ip          (src_ip     )
    );                                           

//ARP发送模块
arp_tx
   #(
    .BOARD_MAC       (BOARD_MAC),         //参数例化
    .BOARD_IP        (BOARD_IP ),
    .DES_MAC         (DES_MAC  ),
    .DES_IP          (DES_IP   )
    )
   u_arp_tx(
    .clk             (gmii_tx_clk),
    .rst_n           (rst_n),

    .arp_tx_en       (arp_tx_en ),
    .arp_tx_type     (arp_tx_type),
    .des_mac         (des_mac   ),
    .des_ip          (des_ip    ),
    .crc_data        (crc_data  ),
    .crc_next        (crc_next[31:24]),
    .tx_done         (tx_done   ),
    .gmii_tx_en      (gmii_tx_en),
    .gmii_txd        (gmii_txd  ),
    .crc_en          (crc_en    ),
    .crc_clr         (crc_clr   )
    );     

//以太网发送CRC校验模块
crc32_d8   u_crc32_d8(
    .clk             (gmii_tx_clk),                      
    .rst_n           (rst_n      ),                          
    .data            (crc_d8     ),            
    .crc_en          (crc_en     ),                          
    .crc_clr         (crc_clr    ),                         
    .crc_data        (crc_data   ),                        
    .crc_next        (crc_next   )                         
    );

endmodule

1.3.1 ARP接收模块 

//ARP接收模块负责解析以太网的数据,判断目的MAC地址和目的IP地址是否为开发板的地址,然后按照ARP协议将数据解析出来
module arp_rx
  #(
    //开发板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}      
    )
   (
    input                clk        , //时钟信号
    input                rst_n      , //复位信号,低电平有效
                                    
    input                gmii_rx_dv , //GMII输入数据有效信号
    input        [7:0]   gmii_rxd   , //GMII输入数据
    output  reg          arp_rx_done, //ARP接收完成信号
    output  reg          arp_rx_type, //ARP接收类型 0:请求  1:应答
    output  reg  [47:0]  src_mac    , //接收到的源MAC地址
    output  reg  [31:0]  src_ip       //接收到的源IP地址
    );

//parameter define
localparam  st_idle     = 5'b0_0001; //初始状态,等待接收前导码
localparam  st_preamble = 5'b0_0010; //接收前导码状态 
localparam  st_eth_head = 5'b0_0100; //接收以太网帧头
localparam  st_arp_data = 5'b0_1000; //接收ARP数据
localparam  st_rx_end   = 5'b1_0000; //接收结束

localparam  ETH_TPYE = 16'h0806;     //以太网帧类型ARP

//reg define
reg    [4:0]   cur_state ;
reg    [4:0]   next_state;
                         
reg            skip_en   ; //控制状态跳转使能信号
reg            error_en  ; //解析错误使能信号
reg    [4:0]   cnt       ; //解析数据计数器
reg    [47:0]  des_mac_t ; //接收到的目的MAC地址
reg    [31:0]  des_ip_t  ; //接收到的目的IP地址
reg    [47:0]  src_mac_t ; //接收到的源MAC地址
reg    [31:0]  src_ip_t  ; //接收到的源IP地址
reg    [15:0]  eth_type  ; //以太网类型
reg    [15:0]  op_data   ; //操作码
reg            rx_done_t ; //ARP接收完成信号

//*****************************************************
//**                    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) 
				//在中间状态如前导码错误、MAC地址错误以及IP地址等错误时跳转到st_rx_end状态,而不是跳转到st_idle状态
				//因为中间状态在解析到数据错误时,单包数据的接收还没有结束,如果此时跳转到st_idle状态会误把有效数据当成前导码来解析
                next_state = st_rx_end;
            else
                next_state = st_preamble;   
        end
        st_eth_head : begin                 //接收以太网帧头
            if(skip_en) 
                next_state = st_arp_data;
            else if(error_en) 
                next_state = st_rx_end;     //跳转到st_rx_end状态,而非跳转到st_idle状态
            else
                next_state = st_eth_head;   
        end  
        st_arp_data : begin                 //接收ARP数据
            if(skip_en)
                next_state = st_rx_end;
            else if(error_en)
                next_state = st_rx_end;     //跳转到st_rx_end状态,而非跳转到st_idle状态
            else
                next_state = st_arp_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
        skip_en <= 1'b0;
        error_en <= 1'b0;
        cnt <= 5'd0;
        des_mac_t <= 48'd0;
        des_ip_t <= 32'd0;
        src_mac_t <= 48'd0;
        src_ip_t <= 32'd0;        
        eth_type <= 16'd0;
        op_data <= 16'd0;
        rx_done_t <= 1'b0;
        arp_rx_type <= 1'b0;
        src_mac <= 48'd0;
        src_ip <= 32'd0;
    end
    else begin
        skip_en <= 1'b0;
        error_en <= 1'b0; 
        rx_done_t <= 1'b0;
        case(next_state)
            st_idle : begin                                  //检测到第一个8'h55
                if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))   //gmii_rx_dv信号拉高表示此时有输入的数据,根据gmii_rxd来解析数据
                    skip_en <= 1'b1;
            end
            st_preamble : begin
                if(gmii_rx_dv) begin                         //解析前导码
                    cnt <= cnt + 5'd1;
                    if((cnt < 5'd6) && (gmii_rxd != 8'h55))  //如果不是连续7个字节的0x55则出现错误
                        error_en <= 1'b1;
                    else if(cnt==5'd6) begin                 //计数到6,即连续7个字节的0x55,前导码正确
                        cnt <= 5'd0;
                        if(gmii_rxd==8'hd5)                  //接下来的数据是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 + 5'b1;
                    if(cnt < 5'd6)                           //目的MAC地址小于6个字节,则将GMII输入数据添加到目标MAC地址寄存器
                        des_mac_t <= {des_mac_t[39:0],gmii_rxd};  //取des_mac_t的低40位与8位的gmii_rxd拼接,将gmii_rxd放在低八位形成左移
                    else if(cnt == 5'd6) begin
                        //判断MAC地址是否为开发板MAC地址或者公共地址
                        if((des_mac_t != BOARD_MAC)
                            && (des_mac_t != 48'hff_ff_ff_ff_ff_ff))           
                            error_en <= 1'b1;
                    end
                    else if(cnt == 5'd12)                    //计数到12,即该解析类型/长度
                        eth_type[15:8] <= gmii_rxd;
                    else if(cnt == 5'd13) begin
                        eth_type[7:0] <= gmii_rxd;
                        cnt <= 5'd0;
                        if(eth_type[15:8] == ETH_TPYE[15:8]  //判断是否为ARP协议
                            && gmii_rxd == ETH_TPYE[7:0])
                            skip_en <= 1'b1; 
                        else
                            error_en <= 1'b1;                       
                    end        
                end  
            end
            st_arp_data : begin                              //解析数据
                if(gmii_rx_dv) begin
                    cnt <= cnt + 5'd1;
                    if(cnt == 5'd6) 
                        op_data[15:8] <= gmii_rxd;           //操作码      
                    else if(cnt == 5'd7)
                        op_data[7:0] <= gmii_rxd;
                    else if(cnt >= 5'd8 && cnt < 5'd14)      //源MAC地址
                        src_mac_t <= {src_mac_t[39:0],gmii_rxd};
                    else if(cnt >= 5'd14 && cnt < 5'd18)     //源IP地址
                        src_ip_t<= {src_ip_t[23:0],gmii_rxd};
                    else if(cnt >= 5'd24 && cnt < 5'd28)     //目标IP地址
                        des_ip_t <= {des_ip_t[23:0],gmii_rxd};
                    else if(cnt == 5'd28) begin
                        cnt <= 5'd0;
                        if(des_ip_t == BOARD_IP) begin       //判断目的IP地址和OP操作码是否正确
                            if((op_data == 16'd1) || (op_data == 16'd2)) begin
                                skip_en <= 1'b1;
                                rx_done_t <= 1'b1;
                                src_mac <= src_mac_t;
                                src_ip <= src_ip_t;
                                src_mac_t <= 48'd0;
                                src_ip_t <= 32'd0;
                                des_mac_t <= 48'd0;
                                des_ip_t <= 32'd0;
                                if(op_data == 16'd1)         
                                    arp_rx_type <= 1'b0;     //ARP请求
                                else
                                    arp_rx_type <= 1'b1;     //ARP应答
                            end
                            else
                                error_en <= 1'b1;
                        end 
                        else
                            error_en <= 1'b1;
                    end
                end                                
            end
            st_rx_end : begin     
                cnt <= 5'd0;
                //单包数据接收完成   
                if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
                    skip_en <= 1'b1; 
            end    
            default : ;
        endcase                                                        
    end
end

当解析到正确的ARP数据包后,拉高arp_rx_done信号,持续一个时钟周期
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        arp_rx_done <= 1'b0;
    else
        arp_rx_done <= rx_done_t;
end

endmodule

1.3.2 ARP发送模块 

 

//ARP发送模块根据以太网格式和ARP协议发送ARP请求或者ARP应答数据
module arp_tx( 
    input                clk        , //时钟信号
    input                rst_n      , //复位信号,低电平有效
    
    input                arp_tx_en  , //ARP发送使能信号
    input                arp_tx_type, //ARP发送类型 0:请求  1:应答
    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          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.10
parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd10}; 
//目的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      = 5'b0_0001; //初始状态,等待开始发送信号
localparam  st_preamble  = 5'b0_0010; //发送前导码+帧起始界定符
localparam  st_eth_head  = 5'b0_0100; //发送以太网帧头
localparam  st_arp_data  = 5'b0_1000; //发送arp数据
localparam  st_crc       = 5'b1_0000; //发送CRC校验值

localparam  ETH_TYPE     = 16'h0806 ; //以太网帧类型 ARP协议
localparam  HD_TYPE      = 16'h0001 ; //硬件类型 以太网
localparam  PROTOCOL_TYPE= 16'h0800 ; //上层协议为IP协议
//以太网数据最小为46个字节,不足部分填充数据
localparam  MIN_DATA_NUM = 16'd46   ;    

//reg define
reg  [4:0]  cur_state     ;
reg  [4:0]  next_state    ;
                          
reg  [7:0]  preamble[7:0] ; //前导码+SFD
reg  [7:0]  eth_head[13:0]; //以太网首部
reg  [7:0]  arp_data[27:0]; //ARP数据
                            
reg         tx_en_d0      ; //arp_tx_en信号延时
reg         tx_en_d1      ; 
reg         skip_en       ; //控制状态跳转使能信号
reg  [5:0]  cnt           ; 
reg  [4:0]  data_cnt      ; //发送数据个数计数器
reg         tx_done_t     ; 
                                
//wire define                   
wire        pos_tx_en     ; //arp_tx_en信号上升沿

//*****************************************************
//**                    main code
//*****************************************************

assign  pos_tx_en = (~tx_en_d1) & tx_en_d0;
                           
//对arp_tx_en信号延时打拍两次,用于采arp_tx_en的上升沿
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_en_d0 <= 1'b0;
        tx_en_d1 <= 1'b0;
    end    
    else begin
        tx_en_d0 <= arp_tx_en;
        tx_en_d1 <= tx_en_d0;
    end
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_preamble;
            else
                next_state = st_idle;
        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_arp_data;
            else
                next_state = st_eth_head;      
        end              
        st_arp_data : begin                 //发送ARP数据                      
            if(skip_en)
                next_state = st_crc;
            else
                next_state = st_arp_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 <= 6'd0;
        data_cnt <= 5'd0;
        crc_en <= 1'b0;
        gmii_tx_en <= 1'b0;
        gmii_txd <= 8'd0;
        tx_done_t <= 1'b0; 
        
        //在复位时初始化数组    
        //前导码 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;
        //以太网帧头 
        eth_head[0] <= DES_MAC[47:40];      //目的MAC地址
        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];        
        eth_head[6] <= BOARD_MAC[47:40];    //源MAC地址
        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];      
        //ARP数据                           
        arp_data[0] <= HD_TYPE[15:8];       //硬件类型
        arp_data[1] <= HD_TYPE[7:0];
        arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
        arp_data[3] <= PROTOCOL_TYPE[7:0];
        arp_data[4] <= 8'h06;               //硬件地址长度,6
        arp_data[5] <= 8'h04;               //协议地址长度,4
        arp_data[6] <= 8'h00;               //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
        arp_data[7] <= 8'h01;
        arp_data[8] <= BOARD_MAC[47:40];    //发送端(源)MAC地址
        arp_data[9] <= BOARD_MAC[39:32];
        arp_data[10] <= BOARD_MAC[31:24];
        arp_data[11] <= BOARD_MAC[23:16];
        arp_data[12] <= BOARD_MAC[15:8];
        arp_data[13] <= BOARD_MAC[7:0];
        arp_data[14] <= BOARD_IP[31:24];    //发送端(源)IP地址
        arp_data[15] <= BOARD_IP[23:16];
        arp_data[16] <= BOARD_IP[15:8];
        arp_data[17] <= BOARD_IP[7:0];
        arp_data[18] <= DES_MAC[47:40];     //接收端(目的)MAC地址
        arp_data[19] <= DES_MAC[39:32];
        arp_data[20] <= DES_MAC[31:24];
        arp_data[21] <= DES_MAC[23:16];
        arp_data[22] <= DES_MAC[15:8];
        arp_data[23] <= DES_MAC[7:0];  
        arp_data[24] <= DES_IP[31:24];      //接收端(目的)IP地址
        arp_data[25] <= DES_IP[23:16];
        arp_data[26] <= DES_IP[15:8];
        arp_data[27] <= DES_IP[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(pos_tx_en) begin
                    skip_en <= 1'b1;  
                    //如果目标MAC地址和IP地址已经更新,则发送正确的地址
                    if((des_mac != 48'b0) || (des_ip != 32'd0)) begin  //根据输入的发送类型、目的MAC地址和IP地址,更新数组里的值
                        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];  
                        arp_data[18] <= des_mac[47:40];
                        arp_data[19] <= des_mac[39:32];
                        arp_data[20] <= des_mac[31:24];
                        arp_data[21] <= des_mac[23:16];
                        arp_data[22] <= des_mac[15:8];
                        arp_data[23] <= des_mac[7:0];  
                        arp_data[24] <= des_ip[31:24];
                        arp_data[25] <= des_ip[23:16];
                        arp_data[26] <= des_ip[15:8];
                        arp_data[27] <= des_ip[7:0];
                    end
                    if(arp_tx_type == 1'b0)
                        arp_data[7] <= 8'h01;            //ARP请求 
                    else 
                        arp_data[7] <= 8'h02;            //ARP应答
                end    
            end                                                                   
            st_preamble : begin                          //发送前导码+帧起始界定符
                gmii_tx_en <= 1'b1;
                gmii_txd <= preamble[cnt];
                if(cnt == 6'd7) begin                        
                    skip_en <= 1'b1;
                    cnt <= 1'b0;    
                end
                else    
                    cnt <= cnt + 1'b1;                     
            end
            st_eth_head : begin                          //发送以太网首部
                gmii_tx_en <= 1'b1;
                crc_en <= 1'b1;
                gmii_txd <= eth_head[cnt];
                if (cnt == 6'd13) begin
                    skip_en <= 1'b1;
                    cnt <= 1'b0;
                end    
                else    
                    cnt <= cnt + 1'b1;    
            end                    
            st_arp_data : begin                          //发送ARP数据  
                crc_en <= 1'b1;
                gmii_tx_en <= 1'b1;
                //至少发送46个字节
                if (cnt == MIN_DATA_NUM - 1'b1) begin    
                    skip_en <= 1'b1;
                    cnt <= 1'b0;
                    data_cnt <= 1'b0;
                end    
                else    
                    cnt <= cnt + 1'b1;  
                if(data_cnt <= 6'd27) begin
                    data_cnt <= data_cnt + 1'b1;
                    gmii_txd <= arp_data[data_cnt];
                end    
                else
                    gmii_txd <= 8'd0;                    //Padding,填充0
            end
            st_crc      : begin                          //发送CRC校验值
                gmii_tx_en <= 1'b1;
                cnt <= cnt + 1'b1;
                if(cnt == 6'd0)    //将输入的crc的计算结果每4位高低位互换
                    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(cnt == 6'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(cnt == 6'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(cnt == 6'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;
                    cnt <= 1'b0;
                end                                                
            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

1.3.3 CRC校验模块 

//CRC校验模块是对ARP发送模块的数据(不包括前导码和起始界定符)做校验,把校验结果值拼在以太网顿格式的FCS字段
//如果CRC校验值计算错误或者没有的话,那么电脑网卡会直接丢弃该倾导致收不到数据
//CRC32校验在FPGA实现的原理是LFSR (Linear Feedback Shift Register,线性反馈移位寄存器)
//其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值
//本次实验只对发送模块做校验,没有对接收模块做校验。
//因为可以直接通过解析出的数据来大致判断接收是否正确,而发送模块必须发送正确的校验数据,否则发送的数据直接丢弃,导致ARP请求或者应答失败
//可通过http://www.easics.com/webtools/crctool生成CRC校验的源代码
module crc32_d8(
    input                 clk     ,  //时钟信号
    input                 rst_n   ,  //复位信号,低电平有效
    input         [7:0]   data    ,  //输入待校验8位数据
    input                 crc_en  ,  //crc使能,开始校验标志
    input                 crc_clr ,  //crc数据复位信号            
    output   reg  [31:0]  crc_data,  //CRC校验数据
    output        [31:0]  crc_next   //CRC下次校验完成数据
    );

//*****************************************************
//**                    main code
//*****************************************************

//输入待校验8位数据,需要先将高低位互换
wire    [7:0]  data_t;

assign data_t = {data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]};

//CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 
//+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1

assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6];
assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31] 
                     ^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7];
assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30] 
                     ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6] 
                     ^ data_t[7];
assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31] 
                     ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7];
assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] 
                     ^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4] 
                     ^ data_t[6];
assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28] 
                     ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0] 
                     ^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6] 
                     ^ data_t[7];
assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29] 
                     ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4] 
                     ^ data_t[5] ^ data_t[6] ^ data_t[7];
assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29] 
                     ^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5] 
                     ^ data_t[7];
assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] 
                     ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28] 
                     ^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5];
assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27] 
                     ^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5];
assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27] 
                     ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26] 
                     ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0] 
                     ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6];
assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27] 
                     ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1] 
                     ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7];
assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28] 
                     ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4]
                     ^ data_t[6] ^ data_t[7];
assign crc_next[15] =  crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29]
                     ^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7];
assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29] 
                     ^ data_t[0] ^ data_t[4] ^ data_t[5];
assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30] 
                     ^ data_t[1] ^ data_t[5] ^ data_t[6];
assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31] 
                     ^ data_t[2] ^ data_t[6] ^ data_t[7];
assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7];
assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4];
assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5];
assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0];
assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30] 
                      ^ data_t[0] ^ data_t[1] ^ data_t[6];
assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31] 
                      ^ data_t[1] ^ data_t[2] ^ data_t[7];
assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3];
assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28] 
                      ^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6];
assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29] 
                      ^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7];
assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30] 
                      ^ data_t[2] ^ data_t[5] ^ data_t[6];
assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31] 
                      ^ data_t[3] ^ data_t[6] ^ data_t[7];
assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7];
assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5];

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        crc_data <= 32'hff_ff_ff_ff;
    else if(crc_clr)                             //CRC校验值复位
        crc_data <= 32'hff_ff_ff_ff;
    else if(crc_en)
        crc_data <= crc_next;
end

endmodule

1.4 ARP控制模块

ARP控制模块首先检测输入触摸按键的上升沿,当检测到上升沿之后,触发ARP顶层模块发起 ARP请求信号;同时检测输入的arp_rx_done和arp_rx_type_信号,当接收上位机的ARP请求信号后,触发ARP顶层模块发送ARP应答信号,将开发板的MAC地址发送给上位机。

module arp_ctrl(
    input                clk        , //输入时钟   
    input                rst_n      , //复位信号,低电平有效
    
    input                touch_key  , //触摸按键,用于触发开发板发出ARP请求
    input                arp_rx_done, //ARP接收完成信号
    input                arp_rx_type, //ARP接收类型 0:请求  1:应答 
    output  reg          arp_tx_en  , //ARP发送使能信号
    output  reg          arp_tx_type  //ARP发送类型 0:请求  1:应答
    );

//reg define
reg         touch_key_d0;
reg         touch_key_d1;

//wire define
wire        pos_touch_key;  //touch_key信号上升沿

//*****************************************************
//**                    main code
//*****************************************************

assign pos_touch_key = ~touch_key_d1 & touch_key_d0;

//对arp_tx_en信号延时打拍两次,用于采touch_key的上升沿
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        touch_key_d0 <= 1'b0;
        touch_key_d1 <= 1'b0;
    end
    else begin
        touch_key_d0 <= touch_key;
        touch_key_d1 <= touch_key_d0;
    end
end

//为arp_tx_en和arp_tx_type赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        arp_tx_en <= 1'b0;
        arp_tx_type <= 1'b0;
    end
    else begin
        if(pos_touch_key == 1'b1) begin  //检测到输入触摸按键上升沿
            arp_tx_en <= 1'b1;           
            arp_tx_type <= 1'b0;
        end
        //接收到ARP请求,开始控制ARP发送模块应答
        else if((arp_rx_done == 1'b1) && (arp_rx_type == 1'b0)) begin
            arp_tx_en <= 1'b1;
            arp_tx_type <= 1'b1;
        end
        else
            arp_tx_en <= 1'b0;
    end
end

endmodule

参考文献:

《开拓者之FPGA开发指南》

《FPGA Verilog开发实战指南》

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

树莓派安装mariadb

mariadb与mysql十分类似&#xff0c;他们的使用方法类似&#xff0c;默认端口也都是3306 文章参考 树莓派mysql安装配置 – 蒋智昊的博客 目录 1 树莓派系统情况 2 安装mariadb 3 启动数据库 4 设置数据库自启动 5 进入数据库 1 树莓派系统情况 用的是树莓派4&…

Linux性能调优 —— 内存篇

Linux性能调优 —— 内存篇 Linux内存的工作原理 内存映射的概念 虚存空间分布 内存分配与回收 分配 回收 内存查看与分析 查看内存使用情况 命令&#xff1a;free 命令&#xff1a;vmstat 命令&#xff1a;top 分析单个进程 命令&#xff1a;ps -p Linux内存的工作原理…

把api_key 设置成win10系统变量然后python调用

1 设置环境变量存储秘钥 将API密钥存储在环境变量中&#xff0c;而不是直接写在代码中&#xff0c;可以降低泄露密钥的风险。 新建系统变量&#xff1a; 变量名&#xff1a;OPENAI_API_KEY 变量值&#xff1a;OpenAI API秘钥(上一步复制的那个key) 2获取值 import openai i…

torch.cuda.is_available() 在有的项目中返回True有的返回Flase

问题描述&#xff0c;刚下了一个项目&#xff0c;不能用CUDA 同一个环境不同项目中 torch.cuda.is_available() 返回值不同 问题来源&#xff1a; 这里的运行配置有问题 选择编辑配置并修改对应的解释器 查看 和 是否对应。 import torch print(torch.__version__) prin…

iOS蓝牙 Connection Parameters 关键参数说明

1. 先贴苹果文档 《 Accessory Design Guidelines for Apple Devices 》 2. 几个关键词 connection Event Interval 事件间隔&#xff0c;为1.25ms的倍数。可以简单理解为,是两个连接着的蓝牙设备发送“心跳包”的时间间隔&#xff1b; 范围是 6 ~ 3200&#xff0c;即 7.5…

运算符超详细讲解(系统性学习day5)

目录 前言 一、运算符的概念与分类 二、算术运算符 三、关系运算符 四、逻辑运算符 五、赋值运算符 六、运算符的优先级 总结 前言 本篇文章是对运算符的具体讲解。 一、运算符的概念与分类 概念&#xff1a; 运算符就是一种告诉编译器执行特定的数学或逻辑操作的符…

HarmonyOS开发:解决DevEco Studio低版本导入高版本项目运行失败问题

前言 基于DevEco Studio 4.0 Beta2&#xff0c;hvigorVersion为3.0.2&#xff0c;开发了一个项目&#xff0c;上传到了远程仓库&#xff0c;当同事下载后&#xff0c;却始终无法运行&#xff0c;频繁报错&#xff0c;由于API都是使用的9&#xff0c;第一感觉就是开发环境不同&a…

【LeetCode-简单题KMP】232. 用栈实现队列

文章目录 题目方法一&#xff1a;用输入栈和输出栈模拟队列 题目 方法一&#xff1a;用输入栈和输出栈模拟队列 只有输出栈为空的时候才能将输入栈的元素补充到输出栈&#xff0c;否则输出栈不为空&#xff0c;如果再从输入栈往输出栈填充元素&#xff0c;就会弄乱队列的先进先…

Zotero的下载与使用

Zotero的下载与使用 一、Zotero的下载二、Zotero的使用1、导入文献&#xff08;1&#xff09;直接拖入&#xff08;2&#xff09;在线导入 2、wps插入文献参考3、联动sci hub 实现英文文献一键批量下载 一、Zotero的下载 下载官网&#xff1a;https://www.zotero.org/ 下载地址…

关于feign调用之间boolean类型的序列化问题

报错内容是这样的&#xff1a;这是controller层 这是feign调用层&#xff1a; 调试出错1&#xff1a; 调试出错2&#xff1a; 解决办法&#xff1a;

Pytorch史上最全torch全版本离线文件下载地址大全(9月最新)

以下为pytorch官网的全版本torch文件离线下载地址 torch全版本whl文件离线下载大全https://download.pytorch.org/whl/torch/其中的文件版本信息如下所示&#xff08;部分版本信息&#xff0c;根据需要仔细寻找进行下载&#xff09;&#xff1a;

STM32F4X UCOSIII 消息队列

STM32F4X UCOSIII 消息队列 消息队列消息队列的作用消息队列工作机制消息队列创建消息发送消息发送模式FIFO(先进先出)LIFO(后进先出) 消息接收消息队列删除消息队列常用函数消息队列创建函数消息队列发送函数消息队列接收函数消息队列删除函数 UCOSIII 消息队列例程 消息队列 …

Foxit PDF SDK Windows 9.1 Crack

Foxit PDF SDK 变更日志 Windows/Linux/Mac 2023 年 8 月 新功能/增强功能 在开始签名之前设置外观。支持使用共享字典添加签名。允许在调用 Signature::StartSign() 之前增量保存文档。在签名前修改现有未签名分页印章签名的外观。支持使用共享字典添加分页签名。忽略全角…

【c语言】指针和数组笔试题

1.指针和数组笔试题解析 一维数组 int a[] { 1,2,3,4 };printf("%d\n", sizeof(a));//a单独放在sizeof内表示求整个数组的字节-----16printf("%d\n", sizeof(a 0));//a不是单独放在sizeof内部&#xff0c;表明是首元素的地址&#xff0c;地址占4/8个字节…

五个很实用的IDEA使用技巧

日常开发中&#xff0c;相信广大 Java 开发者都使用过 IntelliJ IDEA 作为开发工具&#xff0c;IntelliJ IDEA 是一款优秀的 Java 集成开发环境&#xff0c;它提供了许多强大的功能和快捷键&#xff0c;可以帮助开发者提高编码效率和质量。除了一些常见的技巧&#xff0c;如自动…

[篇五章五]-如何禁用 Windows Defender-我的创作纪念日

################################################## 目录 禁用掉烦人的 Windows Defender 在本地组策略编辑器中禁用 Windows Defende 关闭 Microsoft Defender 防病毒 禁止 Defender 开机自动运行 重新激活 Windows Defender #######################################…

字符串函数和内存函数详解(2)

&#x1f435;本文会将会对剩余的字符串库函数和内存函数进行讲解 1.strstr&#x1f4da; 1.1函数用法✏️ strstr函数原型&#xff1a; strstr用于在字符串中找子串&#xff0c;strstr会返回str1中出现str2的起始地址&#xff0c;如果在str1中没有找到str2&#xff0c;则返回…

FreeRTOS移植以及核心功能

文章目录 freertos和ucos区别&#xff0c;优缺点比较移植步骤核心功能内存管理&#xff08;5种内存管理策略&#xff09;FreeRTOS任务调度算法有三种时间管理通信管理 栈管理 freertos和ucos区别&#xff0c;优缺点比较 FreeRTOS&#xff08;Free Real-Time Operating System&…

jdk20 download 配置(linux window mac)

download 直达链接 jdk20,17 wget https://download.oracle.com/java/20/latest/jdk-20_linux-x64_bin.deb # 类似格式替换包的名称就可以实现终端下载jdk下载登录/oracle账号 下载jdk有可能存在要求登录帐号的情况 # 好心人的帐号 账号&#xff1a; 59968873qq.com 密码&…

数据库基本概念与安装MySQL数据库

MySQL数据库基本操作与简单管理 1、数据库的基本概述1.1数据库背景1.2数据库组成1.3数据库发展1.4数据库组成1.5数据库的数据流向1.6数据库功能1.7DBMS的工作模式 2、关系性数据库和非关系性数据库2.1关系型数据库2.2非关系型数据库2.3关系型数据库和非关系型数据库的区别 3、编…