//异步释放,同步复位
always @(posedge clk or negedge s_rst_n)begin
if(s_rst_n==1'b0)begin
rst_n_r <= 0;
end
else begin
rst_n_r <= {rst_n_r[0],1'b1};
end
end
assign rst_n = rst_n_r[1];
在网络通讯时,源主机的应用程序知道目的主机的 IP 地址和端口号,却不知道目的主机的硬件地址,
而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。
因此在通讯前必须获得目的主机的硬件地址。 ARP 协议就起到这个作用。
源主机发出 ARP 请求,询问“IP 地址是 192.168.0.1 的主机的硬件地址是多少”,并将这
个请求广播到本地网段(以太网帧首部的硬件地址填 FF:FF:FF:FF:FF:FF 表示广播),目的主机
接收到广播的 ARP 请求,发现其中的 IP 地址与本机相符,则发送一个 ARP 应答数据包给源
主机,将自己的硬件地址填写在应答包中。
每台主机都维护一个 ARP 缓存表,可以用 arp -a 命令查看。缓存表中的表项有过期时间
(一般为 20 分钟),如果 20 分钟内没有再次使用某个表项,则该表项失效,下次还要发 ARP
请求来获得目的主机的硬件地址。想一想,为什么表项要有过期时间而不是一直有效?
ARP 数据报的格式如下图所示(该图出自[TCPIP]):
注意到源 MAC 地址、目的 MAC 地址在以太网首部和 ARP 请求中各出现一次,对于链路层为以太网的情况
是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型, 1 为以太网,
协议类型指要转换的地址类型, 0x0800 为 IP 地址,后面两个地址长度对于以太网地址和 IP 地址分别为
6 和 4(字节), op 字段为 1 表示 ARP 请求, op字段为 2 表示 ARP 应答。
完整的以太网数据包结尾是帧校验(FCS), 即 CRC32 值, 由于整个过程中都没有出现
CRC 校验字段的内容,而我们使用 FPGA 发包需要计算 CRC 校验字段, CRC 校验字段的产生
可以使用软件或硬件的形式产生,本节实验,为了降低难度, 使用 CRC 计算软件手动计算出
数据报的 CRC 值, 因此使用专用 CRC 校验工具对数据内容进行计算以得到校验字:
CRC 校验软件计算校验内容
第一次发的时候,我将arp中的des_mac默认为00_00_00_00_00_00的,后面通过arp应答包,解析出des_mac的值后,crc就要做相应的更新,为了方便,我是直接用crc计算校正值的,不同的电脑mac不一样,所以需要自己计算一下。
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
crc_value <= 32'h967d1d69;
end
else begin
crc_value <= 32'h24495d2f; //这个值要根据你们自己电脑的mac来计算
end
end
自己看代码研究一下吧,关键点是要时序对齐,注意下降沿采集的技巧(可提前半拍把crc送走,否则时序会出问题!!!),这个crc算法倒是不用过于深究,直接用就可以了。
/************************************************************************
* Author : Wen Chunyang
* Email : [email protected]
* Create time : 2018-11-19 22:27
* Last modified : 2018-11-19 22:27
* Filename : crc.v
* Description :
* *********************************************************************/
module crc(
input clk ,
input rst_n ,
input [ 7: 0] din ,
input en ,
input init ,
output reg [31: 0] crc
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
wire [ 7: 0] data ;
wire [31: 0] crc_next ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
assign data={din[0],din[1],din[2],din[3],din[4],din[5],din[6],din[7]};
assign crc_next[0] = crc[24] ^ crc[30] ^ data[0] ^ data[6];
assign crc_next[1] = crc[24] ^ crc[25] ^ crc[30] ^ crc[31] ^ data[0] ^ data[1] ^ data[6] ^ data[7];
assign crc_next[2] = crc[24] ^ crc[25] ^ crc[26] ^ crc[30] ^ crc[31] ^ data[0] ^ data[1] ^ data[2] ^ data[6] ^ data[7];
assign crc_next[3] = crc[25] ^ crc[26] ^ crc[27] ^ crc[31] ^ data[1] ^ data[2] ^ data[3] ^ data[7];
assign crc_next[4] = crc[24] ^ crc[26] ^ crc[27] ^ crc[28] ^ crc[30] ^ data[0] ^ data[2] ^ data[3] ^ data[4] ^ data[6];
assign crc_next[5] = crc[24] ^ crc[25] ^ crc[27] ^ crc[28] ^ crc[29] ^ crc[30] ^ crc[31] ^ data[0] ^ data[1] ^ data[3] ^ data[4] ^ data[5] ^ data[6] ^ data[7];
assign crc_next[6] = crc[25] ^ crc[26] ^ crc[28] ^ crc[29] ^ crc[30] ^ crc[31] ^ data[1] ^ data[2] ^ data[4] ^ data[5] ^ data[6] ^ data[7];
assign crc_next[7] = crc[24] ^ crc[26] ^ crc[27] ^ crc[29] ^ crc[31] ^ data[0] ^ data[2] ^ data[3] ^ data[5] ^ data[7];
assign crc_next[8] = crc[0] ^ crc[24] ^ crc[25] ^ crc[27] ^ crc[28] ^ data[0] ^ data[1] ^ data[3] ^ data[4];
assign crc_next[9] = crc[1] ^ crc[25] ^ crc[26] ^ crc[28] ^ crc[29] ^ data[1] ^ data[2] ^ data[4] ^ data[5];
assign crc_next[10] = crc[2] ^ crc[24] ^ crc[26] ^ crc[27] ^ crc[29] ^ data[0] ^ data[2] ^ data[3] ^ data[5];
assign crc_next[11] = crc[3] ^ crc[24] ^ crc[25] ^ crc[27] ^ crc[28] ^ data[0] ^ data[1] ^ data[3] ^ data[4];
assign crc_next[12] = crc[4] ^ crc[24] ^ crc[25] ^ crc[26] ^ crc[28] ^ crc[29] ^ crc[30] ^ data[0] ^ data[1] ^ data[2] ^ data[4] ^ data[5] ^ data[6];
assign crc_next[13] = crc[5] ^ crc[25] ^ crc[26] ^ crc[27] ^ crc[29] ^ crc[30] ^ crc[31] ^ data[1] ^ data[2] ^ data[3] ^ data[5] ^ data[6] ^ data[7];
assign crc_next[14] = crc[6] ^ crc[26] ^ crc[27] ^ crc[28] ^ crc[30] ^ crc[31] ^ data[2] ^ data[3] ^ data[4] ^ data[6] ^ data[7];
assign crc_next[15] = crc[7] ^ crc[27] ^ crc[28] ^ crc[29] ^ crc[31] ^ data[3] ^ data[4] ^ data[5] ^ data[7];
assign crc_next[16] = crc[8] ^ crc[24] ^ crc[28] ^ crc[29] ^ data[0] ^ data[4] ^ data[5];
assign crc_next[17] = crc[9] ^ crc[25] ^ crc[29] ^ crc[30] ^ data[1] ^ data[5] ^ data[6];
assign crc_next[18] = crc[10] ^ crc[26] ^ crc[30] ^ crc[31] ^ data[2] ^ data[6] ^ data[7];
assign crc_next[19] = crc[11] ^ crc[27] ^ crc[31] ^ data[3] ^ data[7];
assign crc_next[20] = crc[12] ^ crc[28] ^ data[4];
assign crc_next[21] = crc[13] ^ crc[29] ^ data[5];
assign crc_next[22] = crc[14] ^ crc[24] ^ data[0];
assign crc_next[23] = crc[15] ^ crc[24] ^ crc[25] ^ crc[30] ^ data[0] ^ data[1] ^ data[6];
assign crc_next[24] = crc[16] ^ crc[25] ^ crc[26] ^ crc[31] ^ data[1] ^ data[2] ^ data[7];
assign crc_next[25] = crc[17] ^ crc[26] ^ crc[27] ^ data[2] ^ data[3];
assign crc_next[26] = crc[18] ^ crc[24] ^ crc[27] ^ crc[28] ^ crc[30] ^ data[0] ^ data[3] ^ data[4] ^ data[6];
assign crc_next[27] = crc[19] ^ crc[25] ^ crc[28] ^ crc[29] ^ crc[31] ^ data[1] ^ data[4] ^ data[5] ^ data[7];
assign crc_next[28] = crc[20] ^ crc[26] ^ crc[29] ^ crc[30] ^ data[2] ^ data[5] ^ data[6];
assign crc_next[29] = crc[21] ^ crc[27] ^ crc[30] ^ crc[31] ^ data[3] ^ data[6] ^ data[7];
assign crc_next[30] = crc[22] ^ crc[28] ^ crc[31] ^ data[4] ^ data[7];
assign crc_next[31] = crc[23] ^ crc[29] ^ data[5];
//crc 这里用下降沿采集,是为了crc能提前半拍送到eth_mac_send模块中,这个很重要
//否则crc高8位会发送出错,自己可以修改代码试试看
always @(negedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
crc <= {32{1'b1}};
end
else if(init)begin
crc <= {32{1'b1}};
end
else if(en)begin
crc <= crc_next;
end
end
endmodule
/************************************************************************
* Author : Wen Chunyang
* Email : [email protected]
* Create time : 2018-11-16 22:20
* Last modified : 2018-11-16 22:20
* Filename : eth_send_test.v
* Description :
* *********************************************************************/
module eth_send(
input clk ,
input rst_n ,
//Eth
input [47: 0] des_mac ,
input update ,
output wire tx_trig ,
output wire [31: 0] crc ,
output reg [ 9: 0] wr_addr ,
output wire wr_clk ,
output reg wr_en ,
output reg [ 7: 0] wr_data
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
parameter DELAY_1S = 27'd125_000_000 ;
parameter DELAY_10MS = 25'd125_000_0 ;
//cnt0
reg [24: 0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
//cnt
reg [27: 0] cnt ;
wire add_cnt ;
wire end_cnt ;
//cnt1
reg [ 9: 0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg flag_wr ;
reg flag ;
reg [31: 0] crc_value ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
assign crc = {crc_value[7:0],crc_value[15:8],crc_value[23:16],crc_value[31:24]};
//crc_value 后续的会专门写crc生成电路的,现在只是为了简单起见,比较刚刚入门
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
crc_value <= 32'h967D1D69;
end
else if(update)begin
crc_value <= 32'h24495D2F;
end
end
//cnt0 上电后等待10ms,使电平稳定后
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = flag;
assign end_cnt0 = add_cnt0 && cnt0 == DELAY_10MS-1;
//flag
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
flag <= 1'b1;
end
else if(end_cnt0)begin
flag <= 1'b0;
end
end
//cnt 1s发送一次
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = flag == 1'b0;
assign end_cnt = add_cnt && cnt == DELAY_1S-1;
assign tx_trig = end_cnt;
//cnt1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = flag_wr;
assign end_cnt1 = add_cnt1 && cnt1 == 46-1;
//flag_wr
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
flag_wr <= 1'b0;
end
else if(end_cnt0 || update)begin
flag_wr <= 1'b1;
end
else if(end_cnt1)begin
flag_wr <= 1'b0;
end
end
//wr_en
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
wr_en <= 1'b0;
end
else begin
wr_en <= flag_wr;
end
end
//wr_addr
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
wr_addr <= 10'd0;
end
else begin
wr_addr <= cnt1;
end
end
//wr_clk
assign wr_clk = clk;
//wr_data
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
wr_data <= 8'h00;
end
else if(flag_wr)begin
case(cnt1)
//hdwr_type
00:wr_data <= 8'h00;
01:wr_data <= 8'h01;
//protocol_type
02:wr_data <= 8'h08;
03:wr_data <= 8'h00;
//hdwr_size
04:wr_data <= 8'h06;
//protocol_size
05:wr_data <= 8'h04;
//opcode
06:wr_data <= 8'h00;
07:wr_data <= 8'h01;
//sender_mac
08:wr_data <= 8'h00;
09:wr_data <= 8'h0a;
10:wr_data <= 8'h35;
11:wr_data <= 8'h01;
12:wr_data <= 8'hfe;
13:wr_data <= 8'hc0;
//sender_ip
14:wr_data <= 8'd192;
15:wr_data <= 8'd168;
16:wr_data <= 8'd0;
17:wr_data <= 8'd2;
//target_mac
18:wr_data <= des_mac[47:40];
19:wr_data <= des_mac[39:32];
20:wr_data <= des_mac[31:24];
21:wr_data <= des_mac[23:16];
22:wr_data <= des_mac[15: 8];
23:wr_data <= des_mac[ 7: 0];
//target_ip
24:wr_data <= 8'd192;
25:wr_data <= 8'd168;
26:wr_data <= 8'd0;
27:wr_data <= 8'd3;
//填充字段
28:wr_data <= 8'h00;
29:wr_data <= 8'h00;
30:wr_data <= 8'hff;
31:wr_data <= 8'hff;
32:wr_data <= 8'hff;
33:wr_data <= 8'hff;
34:wr_data <= 8'hff;
35:wr_data <= 8'hff;
36:wr_data <= 8'h00;
37:wr_data <= 8'h23;
38:wr_data <= 8'hcd;
39:wr_data <= 8'h76;
40:wr_data <= 8'h63;
41:wr_data <= 8'h1a;
42:wr_data <= 8'h08;
43:wr_data <= 8'h06;
44:wr_data <= 8'h00;
45:wr_data <= 8'h01;
default:begin
wr_data <= 8'h00;
end
endcase
end
end
endmodule
/************************************************************************
* Author : Wen Chunyang
* Email : [email protected]
* Create time : 2018-11-18 16:37
* Last modified : 2018-11-18 16:37
* Filename : eth_receive.v
* Description :
* *********************************************************************/
module eth_receive(
input clk ,
input rst_n ,
//gmii
input gmii_rx_dv ,
input [ 7: 0] gmii_rx_data ,
input gmii_rx_er ,
output reg update ,
output reg [47: 0] des_mac
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
reg [ 6: 0] cnt ;
wire add_cnt ;
wire end_cnt ;
reg preamble_ok ;
reg frame_start ;
reg [47: 0] src_mac ;
reg [47: 0] des_mac_tmp ;
reg src_mac_ok ;
reg [15: 0] eth_type ;
reg is_arp ;
reg [15: 0] opcode ;
reg opcode_ack ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/
//cnt
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
else begin
cnt <= 0;
end
end
assign add_cnt = gmii_rx_dv && (gmii_rx_er == 1'b0);
assign end_cnt = add_cnt && cnt == 72-1;
//preamble_ok
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
preamble_ok <= 1'd0;
end
else if(add_cnt && (cnt <= 7-1) && (gmii_rx_data == 8'h55))begin
preamble_ok <= 1'b1;
end
else begin
preamble_ok <= 1'b0;
end
end
//frame_start
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
frame_start <= 1'b0;
end
else if(preamble_ok && add_cnt && (cnt == 8-1) && (gmii_rx_data == 8'hd5))begin
frame_start <= 1'b1;
end
else if(end_cnt)begin
frame_start <= 1'b0;
end
end
//src_mac fpga的mac地址
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
src_mac <= 48'd0;
end
else if(frame_start && add_cnt && (cnt >= 9-1) && (cnt <= 14-1))begin
src_mac <= {src_mac[39:0],gmii_rx_data};
end
else if(end_cnt)begin
src_mac <= 48'd0;
end
end
//src_mac_ok
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
src_mac_ok <= 1'b0;
end
else if(add_cnt && (cnt == 15-1) && (src_mac == 48'h00_0a_35_01_fe_c0))begin
src_mac_ok <= 1'b1;
end
else if(end_cnt)begin
src_mac_ok <= 1'b0;
end
end
//eth_type
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
eth_type <= 16'd0;
end
else if(src_mac_ok && add_cnt && (cnt >= 21-1) && (cnt <= 22-1))begin
eth_type <= {eth_type[7:0],gmii_rx_data};
end
else if(end_cnt)begin
eth_type <= 16'd0;
end
end
//is_arp
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
is_arp <= 1'b0;
end
else if(src_mac_ok && add_cnt && (cnt == 23-1) && (eth_type == 16'h0806))begin
is_arp <= 1'b1;
end
else if(end_cnt)begin
is_arp <= 1'b0;
end
end
//opcode
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
opcode <= 16'd0;
end
else if(is_arp && add_cnt && (cnt >= 29-1) && (cnt <= 30-1))begin
opcode <= {opcode[7:0],gmii_rx_data};
end
else if(end_cnt)begin
opcode <= 16'd0;
end
end
//opcode_ack
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
opcode_ack <= 1'b0;
end
else if(add_cnt && (cnt == 31-1) && (opcode == 16'h0002))begin
opcode_ack <= 1'b1;
end
else if(end_cnt)begin
opcode_ack <= 1'b0;
end
end
//des_mac_tmp
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
des_mac_tmp <= 48'd0;
end
else if(add_cnt && (cnt >= 31-1) && (cnt <= 36-1))begin
des_mac_tmp <= {des_mac_tmp[39:0],gmii_rx_data};
end
end
//des_mac
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
des_mac <= 48'd0;
end
else if(opcode_ack && add_cnt && (cnt == 37-1))begin
des_mac <= des_mac_tmp;
end
end
//update
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
update <= 1'b0;
end
else if(opcode_ack)begin
update <= end_cnt;
end
end
endmodule
为了能及时回复大家,现在获取源码方式如下:
微信扫描下面的二维码关注【春哥笔记】公众号,回复“arp”即可Get源码的获取方式: