10G UDP协议栈 (7)ARP设计

目录

一、ARP协议的作用

二、代码实现

模块的主要作用

代码实现

ARP_RX模块

ARP_TX模块

ARP_Table

三、功能仿真


一、ARP协议的作用

 ARP(Address Resolution Protocol,地址解析协议)是用来将IP地址解析为MAC地址的协议。主机或三层网络设备上会维护一张ARP表,用于存储IP地址和MAC地址的映射关系,一般ARP表项包括动态ARP表项和静态ARP表项。

为什么需要ARP?

在局域网中,当主机或其它三层网络设备有数据要发送给另一台主机或三层网络设备时,需要知道对方的网络层地址(即IP地址)。但是仅有IP地址是不够的,因为IP报文必须封装成帧才能通过物理网络发送,因此发送方还需要知道接收方的物理地址(即MAC地址),这就需要一个通过IP地址获取物理地址的协议,以完成从IP地址到MAC地址的映射。地址解析协议ARP即可实现将IP地址解析为MAC地址。

ARP 工作的基本流程

ARP 工作流程分为两个阶段,一个是 ARP 请求过程,另一个是 ARP 响应过程。

工作流程如下所示。
 

ARP协议工作流程-请求示意图


 

ARP协议工作流程-响应示意图


在上面图片中,主机 A 的 IP 地址为 192.168.1.1,主机 B 的 IP 地址为 192.168.1.2。

主机 A 与主机 B 进行通信,需要获取其 MAC 地址,基本流程如下:

  • 主机 A 以广播形式向网络中所有主机发送 ARP 请求,请求包中包含了目标 IP 地址 192.168.1.2。
  • 主机 B 接收到请求,发现自己就是主机 A 要找的主机,返回响应,响应包中包含自己的 MAC 地址

 ARP报文格式

ARP请求和应答报文格式

报文的长度是42字节。前14字节的内容表示以太网首部,后28字节的内容表示ARP请求或应答报文的内容。报文中相关字段的解释如下图所示。

 注意:标准以太网帧的最小长度为64字节,上述全部的字节数为42字节,所以在发送ARP报文的时候需要补充0。

二、代码实现

模块的主要作用

ARP_RX模块的主要作用

ARP_RX接收ARP请求报文或者ARP的响应报文,对于所有的报文,将ARP中相应的的IP、MAC地址写入ARP Table中。对于请求报文,通知ARP_TX模块发送本机的IP、MAC地址。

ARP_TX模块的主要作用

1、是在接收到ARP_RX模块发送过来的目的IP、目的MAC后回复关于本节点的ARP响应报文

 2、当接收到本机的ARP激活信号后,发送ARP请求报文

ARP_Table模块的主要作用

利用寄存器简单实现一个ARP表,包括一个IP表和MAC表,两者之间的表项是一一映射的关系。当收到ARP_RX发送过来的IP和MAC地址时,会更新ARP表中的内容。

代码实现

参考FPGA奇哥网课

ARP_RX模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/05/16 09:29:41
// Design Name: 
// Module Name: ARP_RX
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// ARP_RX接收ARP请求报文或者ARP的响应报文,对于所有的报文,将ARP中相应的的IP、MAC地址写入ARP Table中
// 对于请求报文,通知ARP_TX模块发送本机的IP、MAC地址
// 0x0806属于ARP的帧类型
//
module ARP_RX(
    input           i_clk               ,
    input           i_rst               ,

    input  [63:0]   s_axis_data         ,
    input  [79:0]   s_axis_user         ,//16'dlen,48'dsource_mac,16'dtype
    input  [7 :0]   s_axis_keep         ,
    input           s_axis_last         ,
    input           s_axis_valid        ,

    output [47:0]   o_target_mac        ,
    output [31:0]   o_target_ip         ,
    output          o_target_valid      ,
    output          o_replay_valid      
);

reg [63:0]          rs_axis_data        ;
reg [79:0]          rs_axis_user        ;
reg [7 :0]          rs_axis_keep        ;
reg                 rs_axis_last        ;
reg                 rs_axis_valid       ;

reg [47:0]          ro_target_mac       ;
reg [31:0]          ro_target_ip        ;
reg                 ro_target_valid     ;
reg                 ro_replay_valid     ;

reg                 r_arp               ;
reg [15:0]          r_cnt               ;
reg [15:0]          r_op                ;

assign o_target_mac   =  ro_target_mac   ;
assign o_target_ip    =  ro_target_ip    ;
assign o_target_valid =  ro_target_valid ;
assign o_replay_valid =  ro_replay_valid ;

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)begin
        rs_axis_data  <= 'd0; 
        rs_axis_user  <= 'd0;
        rs_axis_keep  <= 'd0;
        rs_axis_last  <= 'd0;
        rs_axis_valid <= 'd0;
    end
    else begin
        rs_axis_data  <= s_axis_data  ;  
        rs_axis_user  <= s_axis_user  ;
        rs_axis_keep  <= s_axis_keep  ;
        rs_axis_last  <= s_axis_last  ;
        rs_axis_valid <= s_axis_valid ;
    end
end
//通过USER字段判断是否为ARP包
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_arp <= 'd0;
    else
        if(s_axis_valid && !rs_axis_valid && s_axis_user[15:0] == 16'h0806)
            r_arp <= 1'b1;
        else if(s_axis_valid && !rs_axis_valid && s_axis_user[15:0] != 16'h0806)
            r_arp <= 1'b0;
        else
            r_arp <= r_arp;
end

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_cnt <= 'd0;
    else
        if(r_cnt == 2)
            r_cnt <= 'd0;
        else if(r_arp || r_cnt)
            r_cnt <= r_cnt + 1;
        else
            r_cnt <= r_cnt ;
end
//解析出操作类型字段
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_op <= 'd0;
    else
        if(r_arp && rs_axis_valid && r_cnt == 0)
            r_op <= rs_axis_data[15:0];
        else
            r_op <= r_op;
end
//不管是不是响应报文,当接收到ARP报文时,都去更新ARP表
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ro_replay_valid <= 'd0;
    else
        if(r_arp && rs_axis_valid && r_cnt == 1)
            ro_replay_valid <= 1'b1;
        else 
            ro_replay_valid <= 'd0;

end
//如果是响应报文,则需要将源IP地址和源MAC地址解析出来
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ro_target_mac <= 'd0;
    else
        if(r_cnt == 1 && rs_axis_valid) //不管是不是ARP包都解出此字段,如果是ARP包去控制valid拉高就可以了
            ro_target_mac <= rs_axis_data[63:16];
        else
            ro_target_mac <= ro_target_mac;
end
//将源IP地址取出
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ro_target_ip <= 'd0;
    else
        if(r_cnt == 1 && rs_axis_valid)
            ro_target_ip <= {rs_axis_data[15:0],s_axis_data[63:48]};
        else
            ro_target_ip <= ro_target_ip;
end
//如果是ARP请求包则拉高valid,通知ARP_TX模块请求端的ip、mac地址已经解析出来
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ro_target_valid <= 'd0;
    else
        if(r_arp && r_cnt == 1 && rs_axis_valid && r_op == 1)
            ro_target_valid <= 1'b1;
        else
            ro_target_valid <= 'd0;
end


endmodule

ARP_TX模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/05/16 09:29:41
// Design Name: 
// Module Name: ARP_TX
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// ARP_TX模块有两个功能,1、是在接收到ARP_RX模块发送过来的目的IP、目的MAC后回复关于本节点的ARP响应报文
// 2、当接收到本机的ARP激活信号后,发送ARP请求报文
//
module  ARP_TX#(
    parameter       P_SOURCE_IP  = {8'd192,8'd168,8'd100,8'd100}    ,
    parameter       P_SOURCE_MAC = 48'h00_01_02_03_04_05            
)(
    input           i_clk               ,
    input           i_rst               ,

    input  [47:0]   i_set_source_mac    ,
    input           i_set_smac_valid    ,
    input  [31:0]   i_set_source_ip     ,
    input           i_set_sip_valid     ,  

    input           i_arp_active        ,//主动请求ARP报文

    input  [47:0]   i_target_mac        ,
    input  [31:0]   i_target_ip         ,
    input           i_target_valid      ,

    output [63:0]   m_axis_data         ,
    output [79:0]   m_axis_user         ,//16'dlen,48'dsource_mac,16'dtype
    output [7 :0]   m_axis_keep         ,
    output          m_axis_last         ,
    output          m_axis_valid        
);

reg [47:0]   ri_set_source_mac          ;
reg [31:0]   ri_set_source_ip           ;
reg [47:0]   ri_target_mac              ;
reg [31:0]   ri_target_ip               ;
reg          ri_target_valid            ;
reg          ri_arp_active              ;

reg [63:0]   rm_axis_data               ;
reg [79:0]   rm_axis_user               ;
reg [7 :0]   rm_axis_keep               ;
reg          rm_axis_last               ;
reg          rm_axis_valid              ;

reg [15:0]   r_cnt                      ;
reg [15:0]   r_op                       ;

assign m_axis_data  = rm_axis_data      ;
assign m_axis_user  = rm_axis_user      ;
assign m_axis_keep  = rm_axis_keep      ;
assign m_axis_last  = rm_axis_last      ;
assign m_axis_valid = rm_axis_valid     ;



always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ri_set_source_mac <= P_SOURCE_MAC;
    else
        if(i_set_smac_valid)
            ri_set_source_mac <= i_set_source_mac;
        else
            ri_set_source_mac <= ri_set_source_mac;
end

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ri_set_source_ip <= P_SOURCE_IP;
    else
        if(i_set_sip_valid)
            ri_set_source_ip <= i_set_source_ip;
        else
            ri_set_source_ip <= ri_set_source_ip;
end

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)begin
        ri_target_mac <= 'd0;
        ri_target_ip  <= 'd0;
    end
    else begin
        if(i_target_valid)begin
            ri_target_mac <= i_target_mac ;
            ri_target_ip  <= i_target_ip  ;
        end
        else begin
            ri_target_mac <= ri_target_mac;
            ri_target_ip  <= ri_target_ip ;
        end

    end
end

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ri_arp_active <= 'd0;
    else
        ri_arp_active <= i_arp_active;
end

always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        ri_target_valid <= 'd0;
    else
        ri_target_valid <= i_target_valid;
end
//当接收到i_target_valid时说明有请求包到来,需要发送应答包
//当i_arp_active时说明需要发送请求包
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_cnt <= 'd0;
    else
        if(r_cnt == 5)
            r_cnt <= 0;
        else if(ri_target_valid || ri_arp_active || r_cnt)
            r_cnt <= r_cnt + 1;
        else 
            r_cnt <= r_cnt ;
end
//判断操作类型
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        r_op <= 'd0;
    else
        if(ri_target_valid)
            r_op <= 16'd2;
        else if(ri_arp_active)
            r_op <= 16'd1;
        else
            r_op <= r_op;
end
//发送数据
//以太网最小帧应该为64字节,除去以太网帧头和FCS,数据段应该为46字节
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        rm_axis_data <= 'd0;
    else
        case(r_cnt)
            0       : rm_axis_data <= ri_target_valid ?{16'd1,16'h0800,8'd6,8'd4,16'd2}:{16'd1,16'h0800,8'd6,8'd4,16'd1};
            1       : rm_axis_data <= {ri_set_source_mac,ri_set_source_ip[31:16]};
            2       : rm_axis_data <= r_op == 1 ? {ri_set_source_ip[15:0],48'h00_00_00_00_00_00} :{ri_set_source_ip[15:0],ri_target_mac};
            3       : rm_axis_data <= r_op == 1 ? {64'h00_00_00_00_00_00_00_00} : {ri_target_ip,32'h00_00_00_00};
            4       : rm_axis_data <= {64'h00_00_00_00_00_00_00_00};
            5       : rm_axis_data <= {64'h00_00_00_00_00_00_00_00};
            default : rm_axis_data <= 'd0; 
        endcase
end
//控制keep信号
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        rm_axis_keep <= 8'b1111_1111;
    else 
        if(r_cnt == 5)
            rm_axis_keep <= 8'b1111_1100;
        else
            rm_axis_keep <= 8'b1111_1111;
end
//控制User信号
always@(posedge i_clk,posedge i_rst)begin
    if(i_rst)
        rm_axis_user <= 'd0;
    else
        rm_axis_user <= {16'd6,48'hff_ff_ff_ff_ff_ff,16'h0806};//MAC地址写入广播地址
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        rm_axis_last <= 'd0;
    else if(r_cnt == 5)
        rm_axis_last <= 'd1;
    else 
        rm_axis_last <= 'd0;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        rm_axis_valid <= 'd0;
    else if(rm_axis_last)   
        rm_axis_valid <= 'd0;
    else if(ri_target_valid || ri_arp_active)
        rm_axis_valid <= 'd1;
    else 
        rm_axis_valid <= rm_axis_valid;
end


endmodule

ARP_Table

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/05/16 14:47:41
// Design Name: 
// Module Name: ARP_Table
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 简单的ARP表
// 表采用触发器实现
//
module ARP_Table(
    input           i_clk           ,
    input           i_rst           ,

    input  [47:0]   i_write_mac     ,
    input  [31:0]   i_write_ip      ,
    input           i_write_valid   ,

    input  [31:0]   i_query_ip      ,//不能打拍,因为想一次就读出来
    input           i_query_valid   ,
    output [47:0]   o_read_mac      ,
    output          o_read_valid      
);

reg  [47:0]         ri_write_mac    ;
reg  [31:0]         ri_write_ip     ;
reg                 ri_write_valid  ;
reg                 r_rewrite       ;
reg                 r_rewrite_1d    ;
reg  [2 :0]         r_readdr        ;
reg  [2 :0]         r_write_addr    ;
reg  [2 :0]         r_write_addr_ed ;
reg  [2 :0]         r_read_addr     ;  
reg  [31:0]         r_ram_ip[0:7]   ;
reg  [47:0]         r_ram_mac[0:7]  ;
reg  [47:0]         ro_read_mac     ;
reg                 ro_read_valid   ;

assign o_read_mac   = ro_read_mac   ;
assign o_read_valid = ro_read_valid ;

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst) begin
        ri_write_mac   <= 'd0;
        ri_write_ip    <= 'd0;
        ri_write_valid <= 'd0;
        r_rewrite_1d   <= 'd0;
    end else begin
        ri_write_mac   <= i_write_mac  ;  
        ri_write_ip    <= i_write_ip   ; 
        ri_write_valid <= i_write_valid; 
        r_rewrite_1d   <= r_rewrite;
    end
end
//查看是否需要重新更新IP地址对应的MAC地址
//并获取IP所在的表项的地址
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_readdr <= 'd0;
    else if(r_ram_ip[0] == i_write_ip && i_write_valid)
        r_readdr <= 'd0;
    else if(r_ram_ip[1] == i_write_ip && i_write_valid)
        r_readdr <= 'd1;
    else if(r_ram_ip[2] == i_write_ip && i_write_valid)
        r_readdr <= 'd2;
    else if(r_ram_ip[3] == i_write_ip && i_write_valid)
        r_readdr <= 'd3;
    else if(r_ram_ip[4] == i_write_ip && i_write_valid)
        r_readdr <= 'd4;
    else if(r_ram_ip[5] == i_write_ip && i_write_valid)
        r_readdr <= 'd5;
    else if(r_ram_ip[6] == i_write_ip && i_write_valid)
        r_readdr <= 'd6;
    else if(r_ram_ip[7] == i_write_ip && i_write_valid)
        r_readdr <= 'd7;
    else 
        r_readdr <= 'd0;

end
//查看是否需要重新更新IP地址对应的MAC地址
//并获取重写信号
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_rewrite <= 'd0;
    else if(r_ram_ip[0] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[1] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[2] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[3] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[4] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[5] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[6] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else if(r_ram_ip[7] == i_write_ip && i_write_valid)
        r_rewrite <= 'd1;
    else 
        r_rewrite <= 'd0;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_write_addr <= 'd0;
    else if(r_rewrite)              //如果需要重新写入表项,则将表项的地址给到写地址
        r_write_addr <= r_readdr;
    else if(!r_rewrite && r_rewrite_1d)//返回之前地址指针所在的位置
        r_write_addr <= r_write_addr_ed;
    else if(ri_write_valid)             //当需要更新表项时,进行地址自加1
        r_write_addr <= r_write_addr + 1;
    else 
        r_write_addr <= r_write_addr;
end

always@(posedge i_clk,posedge i_rst)//暂时存储目前的地址指针
begin
    if(i_rst)
        r_write_addr_ed <= 'd0;
    else 
        r_write_addr_ed <= r_write_addr;
end

genvar i;
generate for(i = 0;i < 8;i = i + 1) 
begin

    always@(posedge i_clk,posedge i_rst)
    begin
        if(i_rst)
            r_ram_ip[i] <= 'd0;
        else if((i == r_write_addr && ri_write_valid && !r_rewrite) || (r_rewrite_1d && i == r_write_addr))//进行ARP IP表项更新
            r_ram_ip[i] <= ri_write_ip;
        else
            r_ram_ip[i] <= r_ram_ip[i];
    end

    always@(posedge i_clk,posedge i_rst)
    begin
        if(i_rst)
            r_ram_mac[i] <= 48'hff_ff_ff_ff_ff_ff;
        else if((i == r_write_addr && ri_write_valid && !r_rewrite) || (r_rewrite_1d && i == r_write_addr))//进行ARP MAC表项的更新
            r_ram_mac[i] <= ri_write_mac;
        else
            r_ram_mac[i] <= r_ram_mac[i];
    end
end

endgenerate
//获取查询IP对应的MAC地址
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_read_mac <= 'd0;
    else if(i_query_valid && i_query_ip == r_ram_ip[0])
        ro_read_mac <= r_ram_mac[0];
    else if(i_query_valid && i_query_ip == r_ram_ip[1])
        ro_read_mac <= r_ram_mac[1];
    else if(i_query_valid && i_query_ip == r_ram_ip[2])
        ro_read_mac <= r_ram_mac[2];
    else if(i_query_valid && i_query_ip == r_ram_ip[3])
        ro_read_mac <= r_ram_mac[3];
    else if(i_query_valid && i_query_ip == r_ram_ip[4])
        ro_read_mac <= r_ram_mac[4];
    else if(i_query_valid && i_query_ip == r_ram_ip[5])
        ro_read_mac <= r_ram_mac[5];
    else if(i_query_valid && i_query_ip == r_ram_ip[6])
        ro_read_mac <= r_ram_mac[6];
    else if(i_query_valid && i_query_ip == r_ram_ip[7])
        ro_read_mac <= r_ram_mac[7];
    else 
        ro_read_mac <= ro_read_mac;
end
//标识当前的输出有效
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_read_valid <= 'd0;
    else if(i_query_valid)
        ro_read_valid <= 'd1;
    else 
        ro_read_valid <= 'd0;
end   



endmodule

三、功能仿真

仿真步骤:

1、例化两个ARP模块,其中一个模块发送ARP请求,查看另外一个模块的应答

2、读取ARP_Table中其中一个IP地址的MAC

发送ARP请求包

ARP应答

查询一个IP的MAC地址

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值