在前面我们对以太网 UDP 帧格式做了讲解,UDP 帧格式包括前导码+帧界定符、以太网头部数据、IP 头部数据、UDP 头部数据、UDP 数据、FCS 数据,以太网接收模块同样是按照该格式接收数据。
提示:任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」 ;不要急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人 。怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」
(1)UDP接收数据,无论是MAC地址,还是IP地址,或者UDP端口,其源端口都为计算机;
(2)在接收数据时不关心源端口(计算机)的MAC地址,还是IP地址,或者UDP端口,只关心目的地址,也就是说接收数据时可以将源端口MAC地址,IP地址,UDP端口全部置 0;
(3)验证时一定要弄清目的地址(开发板)的MAC和P地址,这里与UDP发送中目的MAC、IP(计算机)是相反的,此处需要注意;
(4)本实验针对千兆网,注意查看电脑是否支持。
以太网 UDP 帧的用户数据是打包在 UDP 协议中,而 UDP 协议又是基于 IP 协议之上的,IP 协议又是走 MAC 层发送的,即从包含关系来说:MAC 帧中的数据段为 IP 数据报文,IP 报文中的数据段为 UDP 报文,UDP 报文中的数据段为用户希望传输的数据内容下图为使用 UDP 协议发送数据的层层打包示意图。
其中,和以太网帧、IP 报文具有帧头一样,UDP 数据报也包含了一个 UDP 报头部分,与 UDP 协议相关的一些信息如端口号,数据包长度等会被打包进 UDP 报头中,然后再与需要传输的 UDP 报文数据一起,作为 IP 报文的数据段送往 IP 层发送。
GMII 接口信号连接关系及各信号的介绍如下。
(注:表格中的方向是站在 MAC 侧角度看的)
此处给出GMII 接口信号连接关系及各信号的介绍,是为了明确UDP发送与接收需要什么输入与输出。
下面将对各个状态的实现及功能进行简要介绍。
IDLE: begin
GMII_RX_DONE <= 0;
if(GMII_DV_reg1 && !GMII_DV_reg2)
begin
curr_state <= PREAMBLE_CODE;
crc_init <= 1;
end
else
curr_state <= curr_state;
end
上述代码中的GMII_DV_reg1 信号是将接收数据有效信号GMII_DV寄存之后打一拍得到的,GMII_DV_reg2 信号是将GMII_DV_reg1信号打一拍得到的,将GMII_DV_reg2 信号取反与
GMII_DV_reg1 相与得到接收数据有效脉冲,得到该信号之后,进入到PREAMBLE_CODE 状态,
GMII_DV_reg1 信号和GMII_DV_reg2 信号的实现代码如下所示,代码中对GMII_TXD 信号也进
行了寄存和打拍操作,得到crc_data用于计算crc校验判断与接收到的CRC值是否一致,后续有关crc_xx名称均为计算crc校验所需输入。
//输入数据寄存
always @ (posedge clk125m_o or negedge reset_n)
if(!reset_n)
begin
GMII_ER_reg <= 0;
GMII_DV_reg <= 0;
GMII_TXD_reg<= 0;
end
else begin
GMII_ER_reg <= GMII_ER;
GMII_DV_reg <= GMII_DV;
GMII_TXD_reg<= GMII_TXD;
end
//数据打两拍判断接收起始
always @ (posedge clk125m_o or negedge reset_n)
if(!reset_n)
begin
GMII_DV_reg1 <= 0;
GMII_DV_reg2 <= 0;
GMII_TXD_reg1<= 0;
GMII_TXD_reg2<= 0;
crc_data <= 0;
end
else
begin
GMII_DV_reg1 <= GMII_DV_reg;
GMII_DV_reg2 <= GMII_DV_reg1;
GMII_TXD_reg1<= GMII_TXD_reg;
GMII_TXD_reg2<= GMII_TXD_reg1;
crc_data <= GMII_TXD_reg2;
end
PREAMBLE_CODE:
begin
crc_init <= 0;
if(cnt_preamble == 4'd7)
begin
curr_state <= ETH_HEADER;
cnt_preamble <= 0;
end
else
begin
cnt_preamble <= cnt_preamble + 1'b1;
curr_state <= curr_state;
end
end
ETH_HEADER:begin
crc_en <= 1;
if(cnt_eth_header == 4'd13)
begin
curr_state <= IP_HEADER;
cnt_eth_header <= 0;
end
else if((cnt_eth_header == 4'd0) && (preamble_code_check_ok == 1'b0))
begin
curr_state <= IDLE;
cnt_eth_header <= 0;
end
else
begin
cnt_eth_header <= cnt_eth_header + 1'b1;
curr_state <= curr_state;
end
case(cnt_eth_header)
4'd0 :dst_mac_reg[47:40] <= GMII_TXD_reg2;
4'd1 :dst_mac_reg[39:32] <= GMII_TXD_reg2;
4'd2 :dst_mac_reg[31:24] <= GMII_TXD_reg2;
4'd3 :dst_mac_reg[23:16] <= GMII_TXD_reg2;
4'd4 :dst_mac_reg[15:8] <= GMII_TXD_reg2;
4'd5 :dst_mac_reg[7:0] <= GMII_TXD_reg2;
4'd6 :src_mac_reg[47:40] <= GMII_TXD_reg2;
4'd7 :src_mac_reg[39:32] <= GMII_TXD_reg2;
4'd8 :src_mac_reg[31:24] <= GMII_TXD_reg2;
4'd9 :src_mac_reg[23:16] <= GMII_TXD_reg2;
4'd10:src_mac_reg[15:8] <= GMII_TXD_reg2;
4'd11:src_mac_reg[7:0] <= GMII_TXD_reg2;
4'd12:eth_type[15:8] <= GMII_TXD_reg2;
4'd13:eth_type[7:0] <= GMII_TXD_reg2;
default: ;
endcase
end
其中preamble_code_check_ok为:
always @ (posedge clk125m_o or negedge reset_n)
if(!reset_n)
preamble_code_check_ok <= 1'b0;
else if((GMII_TXD_reg2 == 8'hd5) && (cnt_preamble == 4'd7))
preamble_code_check_ok <= 1'b1;
else
preamble_code_check_ok <= 1'b0;
IP_HEADER:begin
if(cnt_ip_header == 5'd19)
begin
curr_state <= UDP_HEADER;
udp_data_length_reg <= ip_total_len - 16'd20 - 16'd8;
cnt_ip_header <= 5'd0;
ip_cal_en <= 1;
end
else if(cnt_ip_header >= 5'd1 && eth_header_check_ok == 1'b0)
begin
curr_state <= IDLE;
cnt_ip_header <= 0;
end
else
begin
cnt_ip_header <= cnt_ip_header + 1'b1;
curr_state <= curr_state;
end
case (cnt_ip_header)
5'd0: {
ip_ver,ip_hdr_len} <= GMII_TXD_reg2;
5'd1: ip_tos <= GMII_TXD_reg2;
5'd2: ip_total_len[15:8] <= GMII_TXD_reg2;
5'd3: ip_total_len[7:0] <= GMII_TXD_reg2;
5'd4: ip_id[15:8] <= GMII_TXD_reg2;
5'd5: ip_id[7:0] <= GMII_TXD_reg2;
5'd6: {
ip_rsv,ip_df,ip_mf,ip_frag_offset[12:8]} <= GMII_TXD_reg2;
5'd7: ip_frag_offset[7:0] <= GMII_TXD_reg2;
5'd8: ip_ttl <= GMII_TXD_reg2;
5'd9: ip_protocol <= GMII_TXD_reg2;
5'd10: ip_check_sum[15:8] <= GMII_TXD_reg2;
5'd11: ip_check_sum[7:0] <= GMII_TXD_reg2;
5'd12: src_ip_reg[31:24] <= GMII_TXD_reg2;
5'd13: src_ip_reg[23:16] <= GMII_TXD_reg2;
5'd14: src_ip_reg[15:8] <= GMII_TXD_reg2;
5'd15: src_ip_reg[7:0] <= GMII_TXD_reg2;
5'd16: dst_ip_reg[31:24] <= GMII_TXD_reg2;
5'd17: dst_ip_reg[23:16] <= GMII_TXD_reg2;
5'd18: dst_ip_reg[15:8] <= GMII_TXD_reg2;
5'd19: dst_ip_reg[7:0] <= GMII_TXD_reg2;
default: ;
endcase
end
(3)在 ETH_HEADER 状态时,我们获取了以太网头部数据,进入本状态 IP_HEADER 之后,需要判断之前获取的以太网头部数据是否正确,当获取的数据类型等于 ETH_type(0x0800),得到的 MAC 地址等于代码中设定的值Local_mac 时,则代表以太网头部数据接收成功将eth_header_check_ok 信号拉高,否则为低,
always @ (posedge clk125m_o or negedge reset_n)
if(!reset_n)
eth_header_check_ok <= 1'b0;
else if((eth_type == ETH_type) && (dst_mac_reg == Local_mac) )
eth_header_check_ok <= 1'b1;
else
eth_header_check_ok <= 1'b0;
always @ (posedge clk125m_o or negedge reset_n)
if(!reset_n)
ip_header_check_ok <= 1'b0;
else if({IP_ver,IP_hdr_len,IP_protocol,Check_sum,Local_ip} ==
{ip_ver,ip_hdr_len,ip_protocol,ip_check_sum,dst_ip_reg})
ip_header_check_ok <= 1'b1;
else
ip_header_check_ok <= 1'b0;
(3)当 cnt_udp_header 计数到 1并且 ip_header_check_ok 信号为 0 时,则代表接收数据出
错,返回 IDLE 状态;当 cnt_udp_header