一、ARP帧存在的作用:
在网络通讯时,源主机的应用程序知道目的的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议就起这个作用。
二、ARP帧的工作原理:
源主机发出ARP请求,询问“IP地址是 192.168.0.1的主机的硬件地址是多少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填 FF:FF:FF:FF:FF:FF表示广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应答数据包给源主机,将自己的硬件地址填写在应答包中。
三、ARP数据报格式
*** 以太网目的地址:48'hff_ff_ff_ff_ff_ff,广播到电脑;
*** 以太网源地址:48'h00_0a_35_01_fe_c0,开发板的以太网芯片地址(貌似是可以自己设定);
*** 帧类型:0x0806表示ARP帧;
*** 硬件类型:0x0001表示以太网类型值;
*** 协议类型:0x0800表示上层协议为IP协议;
*** 硬件地址长度:0x6表示以太网的地址长度是6个字节;
*** 协议地址长度:0x4表示IP地址长度为4个字节;
*** op:0x1表示ARP请求包,0x2表示ARP应答包;
*** 发送者硬件地址:源MAC地址(开发板的以太网芯片地址);
*** 发送者IP:发送方的IP地址;
*** 目的硬件地址:电脑的MAC地址;
*** 目标IP地址:电脑的IP地址;
四、MAC协议格式
*** 前导码:55_55_55_55_55_55_55,是用来实现数据的同步;
*** SFD:帧起始界定符:8'h5d,表示一帧的开始;
*** FCS:为确保数据的正确传输,在数据的尾部加入了4个字节的循环冗余校验码(CRC校验) ;
五、ARP协议是加载在MAC协议上来发送的,如下图所示:
六、代码设计(参考的小梅哥的设计思路,在小梅哥的设计的基础上,做了小小的修改)
1 // Time : 2020.04.06 21:12 2 // Describe : eth_test 3 4 module eth_test( 5 rst_n, 6 7 //MII 接口信号 8 mii_tx_clk, 9 mii_tx_en, 10 mii_tx_er, 11 mii_tx_data, 12 13 mii_rx_clk, 14 mii_rx_dv, 15 mii_rx_er, 16 mii_rx_data, 17 18 phy_rst_n 19 ); 20 21 input rst_n; 22 23 input mii_tx_clk; //MII接口发送时钟,由PHY芯片产生,25MHz 24 output mii_tx_en; //MII接口发送数据使能信号,高电平有效 25 output mii_tx_er; //发送错误,用以破坏数据包发送 26 output reg[3:0]mii_tx_data; //MII接口发送数据线,FPGA通过该数据线将需要发送的数据依次送给PHY芯片 27 output phy_rst_n; //PHY 复位信号 28 29 input mii_rx_clk; //MII接口接收时钟,由PHY芯片产生,25MHz 30 input mii_rx_dv; //MII接口接收数据有效信号,高电平有效 31 input mii_rx_er; //接收错误,本实例中暂时忽略该信号 32 input [3:0]mii_rx_data; //MII接口数据总线,FPGA通过该数据线读取PHY芯片接收到的以太网数据 33 34 crc32_d4 u0( 35 .Clk(mii_tx_clk), 36 .Rst_n(rst_n), 37 .Data(mii_tx_data), 38 .Enable(CRC_EN), 39 .Initialize(~mii_tx_en), 40 .Crc(), 41 .CrcError(), 42 .Crc_eth(CRC_Result) 43 ); 44 45 assign phy_rst_n = 1'b1; 46 47 parameter des_mac = 48'hff_ff_ff_ff_ff_ff; //目标MAC地址 48 parameter src_mac = 48'h00_0a_35_01_fe_c0; //源MAC地址 49 parameter type_length = 16'h08_06; //数据帧类型 50 parameter data_length = 12'd92; //数据长度(因为MII接口一个字节分两个时钟,每个时钟4位的方式发送,因此本值为实际数据所占字节数的2倍) 51 52 wire[31:0] CRC_Result; 53 54 wire CRC_EN; 55 56 assign CRC_EN = (lsm_cnt > 17 && lsm_cnt <= 137); 57 58 wire tx_go; // 使能发送 59 60 reg [7:0] lsm_cnt; //序列机计数器,本以太网帧发送系统使用线性序列机方式设计 61 reg en_tx; //内部的数据帧发送使能信号,高电平时将数据通过MII接口送出 62 63 reg [28:0]cnt; //发送间隔计数器 64 always@(posedge mii_tx_clk or negedge rst_n) 65 if(!rst_n) 66 cnt <= 28'd0; 67 else if(cnt == 28'd3500) // 35000000 68 cnt <= 28'd0; 69 else 70 cnt <= cnt + 1'b1; 71 72 //每671ms启动一次发送数据 73 assign tx_go = (cnt == 28'd3500) ? 1'b1 : 1'b0; // 35000000 74 75 //根据发送启动信号产生内部发送使能信号 76 always@(posedge mii_tx_clk or negedge rst_n) 77 if(!rst_n) 78 en_tx <= 1'd0; 79 else if(tx_go) 80 en_tx <= 1'd1; 81 else if(lsm_cnt >= 145) //一帧数据发送完成,清零发送使能信号 82 en_tx <= 1'd0; 83 84 always@(posedge mii_tx_clk or negedge rst_n) //主序列机计数器 85 if(!rst_n) 86 lsm_cnt <= 8'd0; 87 else if(en_tx) begin 88 if(lsm_cnt == 8'd145) 89 lsm_cnt <= 8'd0; 90 else 91 lsm_cnt <= lsm_cnt + 1'b1; 92 end 93 else 94 lsm_cnt <= 8'd0; 95 96 /* 97 always@(posedge mii_tx_clk or negedge rst_n) 98 if(!rst_n) begin 99 mii_tx_data <= 4'd0; 100 end 101 else begin 102 */ 103 always@(*) begin 104 case(lsm_cnt) 105 1: mii_tx_data <= 4'h5; // 前导码 + 分隔符的一个5 106 2: mii_tx_data <= 4'h5; 107 3: mii_tx_data <= 4'h5; 108 4: mii_tx_data <= 4'h5; 109 5: mii_tx_data <= 4'h5; 110 6: mii_tx_data <= 4'h5; 111 7: mii_tx_data <= 4'h5; 112 8: mii_tx_data <= 4'h5; 113 9: mii_tx_data <= 4'h5; 114 10:mii_tx_data <= 4'h5; 115 11:mii_tx_data <= 4'h5; 116 12:mii_tx_data <= 4'h5; 117 13:mii_tx_data <= 4'h5; 118 14:mii_tx_data <= 4'h5; 119 15:mii_tx_data <= 4'h5; 120 121 16: mii_tx_data <= 4'hd; // 分隔符 122 123 17: mii_tx_data <= des_mac[43:40]; // 目的MAC地址,先发高八位中的低四位 48'hff_ff_ff_ff_ff_ff 124 18: mii_tx_data <= des_mac[47:44]; 125 19: mii_tx_data <= des_mac[35:32]; 126 20: mii_tx_data <= des_mac[39:36]; 127 21: mii_tx_data <= des_mac[27:24]; 128 22: mii_tx_data <= des_mac[31:28]; 129 23: mii_tx_data <= des_mac[19:16]; 130 24: mii_tx_data <= des_mac[23:20]; 131 25: mii_tx_data <= des_mac[11:8]; 132 26: mii_tx_data <= des_mac[15:12]; 133 27: mii_tx_data <= des_mac[3:0]; 134 28: mii_tx_data <= des_mac[7:4]; 135 136 137 29: mii_tx_data <= src_mac[43:40];// 0 //源MAC地址 48'h00_0a_35_01_fe_c0 138 30: mii_tx_data <= src_mac[47:44];// 0 139 31: mii_tx_data <= src_mac[35:32];// a 140 32: mii_tx_data <= src_mac[39:36];// 0 141 33: mii_tx_data <= src_mac[27:24];// 5 142 34: mii_tx_data <= src_mac[31:28];// 3 143 35: mii_tx_data <= src_mac[19:16];// 1 144 36: mii_tx_data <= src_mac[23:20];// 0 145 37: mii_tx_data <= src_mac[11:8]; // e 146 38: mii_tx_data <= src_mac[15:12];// f 147 39: mii_tx_data <= src_mac[3:0]; // 0 148 40: mii_tx_data <= src_mac[7:4]; // c 149 150 151 41: mii_tx_data <= type_length[11:8]; //以太网帧类型/长度 152 42: mii_tx_data <= type_length[15:12]; 153 43: mii_tx_data <= type_length[3:0]; 154 44: mii_tx_data <= type_length[7:4]; 155 156 45: mii_tx_data = 4'h0; 157 46: mii_tx_data = 4'h0; 158 47: mii_tx_data = 4'h1; 159 48: mii_tx_data = 4'h0; 160 161 //protocol type 162 49: mii_tx_data = 4'h8; 163 50: mii_tx_data = 4'h0; 164 51: mii_tx_data = 4'h0; 165 52: mii_tx_data = 4'h0; 166 167 //hdwr size 168 53: mii_tx_data = 4'h6; 169 54: mii_tx_data = 4'h0; 170 171 //protocol size 172 55: mii_tx_data = 4'h4; 173 56: mii_tx_data = 4'h0; 174 175 //opcode 176 57: mii_tx_data = 4'h0; 177 58: mii_tx_data = 4'h0; 178 59: mii_tx_data = 4'h1; 179 60: mii_tx_data = 4'h0; 180 181 //sender mac 182 61: mii_tx_data = 4'h0; 183 62: mii_tx_data = 4'h0; 184 63: mii_tx_data = 4'ha; 185 64: mii_tx_data = 4'h0; 186 65: mii_tx_data = 4'h5; 187 66: mii_tx_data = 4'h3; 188 67: mii_tx_data = 4'h1; 189 68: mii_tx_data = 4'h0; 190 69: mii_tx_data = 4'he; 191 70: mii_tx_data = 4'hf; 192 71: mii_tx_data = 4'h0; 193 72: mii_tx_data = 4'hc; 194 195 //sender ip : 192.168.0.2 196 73: mii_tx_data = 4'h0;//192 197 74: mii_tx_data = 4'hc; 198 199 75: mii_tx_data = 4'h8;//168 200 76: mii_tx_data = 4'ha; 201 202 77: mii_tx_data = 4'h0;//0 203 78: mii_tx_data = 4'h0; 204 205 79: mii_tx_data = 4'h2; 206 80: mii_tx_data = 4'h0;//2 207 208 //target mac 209 81: mii_tx_data = 4'h4; // 4 //48'b84 7b eb 48 94 13 210 82: mii_tx_data = 4'hc; // 8 //48'b34 23 87 99 f4 61 211 83: mii_tx_data = 4'h4; // b 212 84: mii_tx_data = 4'h5; // 7 213 85: mii_tx_data = 4'h4; // b 214 86: mii_tx_data = 4'h4; // e 215 87: mii_tx_data = 4'h7; // 8 216 88: mii_tx_data = 4'h9; // 4 217 89: mii_tx_data = 4'h5; // 4 218 90: mii_tx_data = 4'hc; // 9 219 91: mii_tx_data = 4'h7; // 3 220 92: mii_tx_data = 4'hd; // 1 221 222 //target ip : 192.168.0.3 223 93: mii_tx_data = 4'h0;//192 224 94: mii_tx_data = 4'hc; 225 226 95: mii_tx_data = 4'h8;//168 227 96: mii_tx_data = 4'ha; 228 229 97: mii_tx_data = 4'h0;//0 230 98: mii_tx_data = 4'h0; 231 232 99: mii_tx_data = 4'h3;//3 233 100: mii_tx_data = 4'h0; 234 235 //填充字段,以使整个数据帧长度达到64字节 236 101: mii_tx_data = 4'h0; 237 102: mii_tx_data = 4'h0; 238 103: mii_tx_data = 4'h0; 239 104: mii_tx_data = 4'h0; 240 105: mii_tx_data = 4'hf; 241 106: mii_tx_data = 4'hf; 242 107: mii_tx_data = 4'hf; 243 108: mii_tx_data = 4'hf; 244 109: mii_tx_data = 4'hf; 245 110: mii_tx_data = 4'hf; 246 111: mii_tx_data = 4'hf; 247 112: mii_tx_data = 4'hf; 248 113: mii_tx_data = 4'hf; 249 114: mii_tx_data = 4'hf; 250 115: mii_tx_data = 4'hf; 251 116: mii_tx_data = 4'hf; 252 117: mii_tx_data = 4'h0; 253 118: mii_tx_data = 4'h0; 254 119: mii_tx_data = 4'h3; 255 120: mii_tx_data = 4'h2; 256 121: mii_tx_data = 4'hd; 257 122: mii_tx_data = 4'hc; 258 123: mii_tx_data = 4'h6; 259 124: mii_tx_data = 4'h7; 260 125: mii_tx_data = 4'h3; 261 126: mii_tx_data = 4'h6; 262 127: mii_tx_data = 4'ha; 263 128: mii_tx_data = 4'h1; 264 129: mii_tx_data = 4'h8; 265 130: mii_tx_data = 4'h0; 266 131: mii_tx_data = 4'h6; 267 132: mii_tx_data = 4'h0; 268 133: mii_tx_data = 4'h0; 269 134: mii_tx_data = 4'h0; 270 135: mii_tx_data = 4'h1; 271 136: mii_tx_data = 4'h0; 272 273 137: mii_tx_data <= CRC_Result[27:24]; //发送CRC 校验结果 274 138: mii_tx_data <= CRC_Result[31:28]; 275 139: mii_tx_data <= CRC_Result[19:16]; 276 140: mii_tx_data <= CRC_Result[23:20]; 277 141: mii_tx_data <= CRC_Result[11:8]; 278 142: mii_tx_data <= CRC_Result[15:12]; 279 143: mii_tx_data <= CRC_Result[3:0]; 280 144: mii_tx_data <= CRC_Result[7:4]; 281 282 145: mii_tx_data <= 4'd0; 283 default: mii_tx_data <= 4'd0; 284 endcase 285 end 286 287 /* 288 always@(posedge mii_tx_clk or negedge rst_n) //MII数据发送使能信号 289 if(!rst_n) 290 mii_tx_en <= 1'b0; 291 else if((lsm_cnt >= 1) && (lsm_cnt <= 144)) // lsm_cnt >= 1 292 mii_tx_en <= 1'b1; 293 else 294 mii_tx_en <= 1'b0; 295 */ 296 297 assign mii_tx_en = ((lsm_cnt >= 1) && (lsm_cnt <= 144)) ? 1'b1 : 1'b0; 298 299 endmodule 300
测试代码:
1 `timescale 1ns/1ns 2 3 module eth_test_tb; 4 5 reg rst_n; 6 //MII 接口信号 7 reg mii_tx_clk; 8 wire mii_tx_en; 9 wire mii_tx_er; 10 wire [3:0]mii_tx_data; 11 wire phy_rst_n; 12 13 eth_test eth_test( 14 .rst_n(rst_n), 15 .mii_tx_clk(mii_tx_clk), 16 .mii_tx_en(mii_tx_en), 17 .mii_tx_er(mii_tx_er), 18 .mii_tx_data(mii_tx_data), 19 .phy_rst_n(phy_rst_n) 20 ); 21 22 initial mii_tx_clk = 1; 23 always #20 mii_tx_clk = ~mii_tx_clk; 24 25 initial begin 26 rst_n = 0; 27 #201; 28 rst_n = 1; 29 30 #1000000; 31 32 $stop; 33 end 34 35 endmodule
仿真效果:
图1
图2
上板效果图:
图3
注:代码设计参考了小梅哥的设计,梅哥写的比较简单易懂,文字叙述方面也摘抄了梅哥的《基于ac620的fpga系统设计与验证实战指南20190516》;
如果无法收到ARP提示,可能是时序的问题,需要添加时序约束;
如果收到了ARP提示,但是没有应答包,那就有可能是电脑上没有绑定在代码中设定的目的IP地址(在本文中的 des_ip:192.168.0.3);
如有不当之处,还望各位道友指正~