IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 以太网通信(1)UDP —— 数据发送 -> 正文阅读

[网络协议]以太网通信(1)UDP —— 数据发送

目录

一、以太网通信简介

1.OSI七层模型

2.以太网数据包解析

3.IP首部校验和计算与检验

二、以太网通信实例

1.整体实验框图

2.发送部分时序图

3.实验代码及仿真结果

总结


一、以太网通信简介

? ? ? ? ? 前文我们讲述了多种通信协议(串口、IIC、SPI等协议),而以太网通信是相对比较高速的一种通信方式。目前,以太网是指遵守 IEEE 802.3 标准组成的局域网,由 IEEE 802.3 标准规定的主要是位于参考模型的物理层(PHY)和数据链路层中的介质访问控制子层(MAC)。讲到这,再简单的说一下国际标准化组织?(ISO)为了统一通信标准,将整个以太网通信结构制定了 OSI (Open System Interconnection)模型,译为开放式系统互联。

1.OSI七层模型

? ? ? ? ?OSI 定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、 表示层、应用层),即 OSI 开放互连系统参考模型。每个层功能不同,网络通信中各司其 职,整个模型包括硬件和软件定义。 OSI 模型只是理想分层,一般的网络系统只是涉及其中几层。其各层的参考模型及简介如下图所示。

? ? ? ? ? ? 在物理层中,主要规定了以太网使用的介质(水晶头网线)、数据编码方式(曼彻斯特编码)和冲突检测机制(CSMA/CD 冲突检测)等,在实际应用中主要是通过一个PHY芯片实现其功能的。

? ? ? ? ? 在数据链路层中,主要规定了数据链路层的下半部分MAC子层,它主要是负责与物理层进行的数据交换,比如是否可以发送数据,发送的数据是否正确,对数据流进行控制等。它自动对来自上层的数据包加上一些控制信号,交给物理层。接收方得到正常数据时,自动去除 MAC 控制信号,把该数据包交给上层。

2.以太网数据包解析

? ? ? 下面对以太网发送的一包数据进行解析,如下图所示:

3.IP首部校验和计算与检验

? ? ?上述协议中的IP首部校验和的其计算方法如下:

? ? ??IP 首部校验和计算

? ? ??校验字节强制置 0,将 IP 首部 20 字节 按 2 字节, 即 16 比特,分开分别相加,若如果 大于 FFFF 那么把高 16 位与低 16 位相加,直到最终结果为 16 比特数据。将计算结果取反作为 IP 首部校验和字节。

? ? ? ?例:抓取 IP 数据包,取 IP 数据报报头部分(20B),数据如下,45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d,计算 IP 首部校验和。

? ? ? ?(1) 将校验和字段 b5 2e 置为 00 00,数据变为:

? ? ? ? ? ? 45 00 00 30 80 4c 40 00 80 06 00 00 d3 43 11 7b cb 51 15 3d

? ? ? ?(2) 以 2 字节为单位,数据反码求和:

? ? ? ? ? ? 4500+0030+804c+4000+8006+0000+d343+117b+cb51+153d=34ace

? ? ? ?(3) 将进位(3)加到低 16 位(4ace)上:

? ? ? ? ? ? 0003+4ace=4ad1

? ? ? ?(4) 将 4ad1 取反得:checksum = b52e

? ? ? ??
? ? ? IP 首部校验和检验

? ? ? 对 IP 首部中每个 16 bit 进行二进制反码求和,将计算结果再取反码,若结果为 0,通过检验,否则,不通过检验。

? ? ?例:验证 IP 首部 45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d

? ? ?(1) IP 首部进行反码求和:

? ? ? ? ? 4500+0030+804c+4000+8006+b52e+d343+117b+cb51+153d=3fffc

? ? ? ? ??0003+fffc=ffff

? ??(2) 求和结果取反码:

? ? ? ??~ffff=0 ,校验正确。

? ? ? ?对于上述协议中的循环冗余码校验(CRC)校验,其详细内容可以参考V3学院——尤老师腾讯课堂FPGA从入门到实战最后一节课讲解的CRC循环冗余校验,个人感觉讲解的非常好,适合初学者学习。

二、以太网通信实例

? ? ? ?要求:FPGA采集各路实验数据,并将各路数据打包好后通过网口上传到上位机进行数据分析。本实验仅仅是完成了FPGA与上位机之间通过网口通信的过程。

1.整体实验框图

? ? ? 下边给出整个系统的实验框图:

?

? ? ? ?输入信号:sys_clk,由外部 PHY 芯片传入时钟(eth_clk)分频得到;

? ? ? ? ? ? ? ? ? ? ? ? ? ?sys_res,由外部复位按键传入

? ? ? ? ? ? ? ? ? ? ? ? ? sys_en,数据发送开始信号,由外部传入

? ? ? ? ? ? ? ? ? ? ? ? ? send_data,待发送的数据,由外部传入

? ? ? ? ? ? ? ? ? ? ? ? ??send_data_num,待发送数据有效数,由外部传入;
? ? ?
? ? ?输出信号:send_end,表示单包数据发送完成;
? ? ? ? ? ? ? ? ? ? ? ? ?read_data_req,表示待发送数据读取请求信号,传给数据存储模块,作为数据读取使能
? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ?eth_tx_en,表示输出数据使能信号
? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ?eth_tx_data,表示输出数据

2.发送部分时序图

? ? ? ? ? ? ? ? ? ? ? ??
时序图如下:

3.实验代码及仿真结果

?下面给出实验代码:

1.发送部分

module udp_send
#(
    parameter   BOARD_MAC   = 48'hFF_FF_FF_FF_FF_FF ,   //板卡MAC地址
    parameter   BOARD_IP    = 32'hFF_FF_FF_FF       ,   //板卡IP地址
    parameter   BOARD_PORT  = 16'd1234              ,   //板卡端口号
    parameter   PC_MAC      = 48'hFF_FF_FF_FF_FF_FF ,   //PC机MAC地址
    parameter   PC_IP       = 32'hFF_FF_FF_FF       ,   //PC机IP地址
    parameter   PC_PORT     = 16'd1234                  //PC机端口号
)
(
     input               sys_clk,
	 input               sys_res,
	 input               send_en,
	 input         [15:0]send_data,
	 input         [15:0]send_data_num,  
	 input         [31:0]crc_data,   
     input         [3 :0]crc_next,
	 output   reg        send_end,
	 output   reg        read_data_req,
	 output   reg        eth_tx_en,
	 output   reg  [3 :0]eth_tx_data,
	 output   reg        crc_en,
	 output   reg        crc_clr
);

parameter     IDLE = 10'b0000_0000_01;     
parameter     IP_HEAD_CHECK_SUM = 10'b0000_0000_10;   //对IP首部20个字节进行校验
parameter     PACK_HEAD = 10'b0000_0001_00;           //发送前导码和帧起始界定符
parameter     ETH_HEAD = 10'b0000_0010_00;            //发送目的MAC地址、源MAC地址、类型
parameter     IP_HEAD = 10'b0000_0100_00;             //发送IP首部20个字节
parameter     UDP_HEAD = 10'b0000_1000_00;            //发送UDP首部8个字节
parameter     DATA = 10'b0001_0000_00;                
parameter     CRC = 10'b0010_0000_00;

localparam    ETH_TYPE    =   16'h0800    ;    //协议类型 IP协议

reg     [7 :0]   mem_packet_head [7 :0];   //数据包头
reg     [7 :0]   mem_eth_head    [13:0];   //以太网首部包括目的MAC地址,源MAC地址,类型
reg     [15:0]   mem_ip_head     [9 :0];   //IP首部 
reg     [15:0]   mem_udp_head    [3 :0];   //UDP首部 
wire     [15:0]   reg_send_data;
reg     [31:0]   ip_head_check;

reg      [9:0]stata ;
reg      [7:0]cnt   ;//发送计数(计数加1代表一个字节)
reg      [7:0]cnt_4b;//计数加1代表4个bite
reg      [1:0]cnt_check;//ip首部校验和计数,共计数4次,在第一次计数进行20个字节求和,第二次计数将高16位加到低16位,第三次计数重复上一次操作,第四次计数取反
reg      [11:0]cnt_add;
wire     [15:0]data_num_add; //如果发送个数不满足46,则需补充发送数据
reg      flag;

reg           reg_send_en;
wire          rise_send_en;
//取send_en的上升沿
assign        rise_send_en = send_en & (~reg_send_en);
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	   reg_send_en <= 1'b0;
	else 
	   reg_send_en <= send_en;
//判断发送数据是否满足最小46个
assign  data_num_add = (send_data_num<23)?23:send_data_num; 

always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	   cnt_add <= 'd0;
	else if(stata == DATA && cnt_4b == 'd3 && cnt_add == send_data_num)
	   cnt_add <= 'd0;
	else if((stata == DATA && cnt_4b == 'd3 && cnt_add != 'd0)||(stata == UDP_HEAD && cnt == (8/2-1) && cnt_4b == 'd3))
	   cnt_add <= cnt_add + 1;
	else
	   cnt_add <= cnt_add;
assign  reg_send_data = (cnt_add == 0)? 16'd0:send_data;


//IP_HEAD_CHECK_SUM(IP首部校验和)
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
       	cnt_check  <= 'd0;
    else if(stata == IP_HEAD_CHECK_SUM)
	    cnt_check  <= cnt_check + 1'b1;
	else if(stata != IP_HEAD_CHECK_SUM)
	    cnt_check  <= 'd0;
    else 
        cnt_check  <= cnt_check; 	
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	    ip_head_check <= 'd0;
    else if(stata == IP_HEAD_CHECK_SUM && cnt_check == 'd0)
	    ip_head_check <= mem_ip_head[0] + mem_ip_head[1] + mem_ip_head[2] + mem_ip_head[3] + 
		                 mem_ip_head[4] + mem_ip_head[5] + mem_ip_head[6] + mem_ip_head[7] + 
						 mem_ip_head[8] + mem_ip_head[9];
	else if(stata == IP_HEAD_CHECK_SUM && cnt_check == 'd1)
	    ip_head_check <= ip_head_check[31:16]+ ip_head_check[15:0];
	else if(stata == IP_HEAD_CHECK_SUM && cnt_check == 'd2)
	    ip_head_check <= ip_head_check[31:16]+ ip_head_check[15:0];
	else 
	    ip_head_check <= ip_head_check;
	
//PACK_HEAD包括7个8’h55和1个8‘hd5
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)begin
        mem_packet_head[0]  <=  8'h00; 	
		mem_packet_head[1]  <=  8'h00; 
		mem_packet_head[2]  <=  8'h00; 	
		mem_packet_head[3]  <=  8'h00;
		mem_packet_head[4]  <=  8'h00; 	
		mem_packet_head[5]  <=  8'h00; 
		mem_packet_head[6]  <=  8'h00; 	
		mem_packet_head[7]  <=  8'h00;
    end 
	else begin
	    mem_packet_head[0]  <=  8'h55; 	
		mem_packet_head[1]  <=  8'h55; 
		mem_packet_head[2]  <=  8'h55; 	
		mem_packet_head[3]  <=  8'h55;
		mem_packet_head[4]  <=  8'h55; 	
		mem_packet_head[5]  <=  8'h55; 
		mem_packet_head[6]  <=  8'h55; 	
		mem_packet_head[7]  <=  8'hd5;
	end
//ETH_HEAD包括目的MAC地址、源MAC地址、类型
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)begin
	    mem_eth_head[0]  <= 8'h00;
		mem_eth_head[1]  <= 8'h00;
		mem_eth_head[2]  <= 8'h00;
		mem_eth_head[3]  <= 8'h00;
		mem_eth_head[4]  <= 8'h00;
		mem_eth_head[5]  <= 8'h00;
		mem_eth_head[6]  <= 8'h00;
		mem_eth_head[7]  <= 8'h00;
		mem_eth_head[8]  <= 8'h00;
		mem_eth_head[9]  <= 8'h00;
		mem_eth_head[10] <= 8'h00;
		mem_eth_head[11] <= 8'h00;
		mem_eth_head[12] <= 8'h00;
		mem_eth_head[13] <= 8'h00;	
	end 
	else begin
	    mem_eth_head[0]  <=    PC_MAC     [47:40]      ;
		mem_eth_head[1]  <=    PC_MAC     [39:32]      ;
		mem_eth_head[2]  <=    PC_MAC     [31:24]      ;
		mem_eth_head[3]  <=    PC_MAC     [23:16]      ;
		mem_eth_head[4]  <=    PC_MAC     [15:8 ]      ;
		mem_eth_head[5]  <=    PC_MAC     [7 :0 ]      ;
		mem_eth_head[6]  <=    BOARD_MAC  [47:40]      ;
		mem_eth_head[7]  <=    BOARD_MAC  [39:32]      ;
		mem_eth_head[8]  <=    BOARD_MAC  [31:24]      ;
		mem_eth_head[9]  <=    BOARD_MAC  [23:16]      ;
		mem_eth_head[10] <=    BOARD_MAC  [15:8 ]      ;
		mem_eth_head[11] <=    BOARD_MAC  [7 :0 ]      ;
		mem_eth_head[12] <=    ETH_TYPE   [15:8 ]      ;
		mem_eth_head[13] <=    ETH_TYPE   [7 :0 ]      ;
	end 
//IP_HEAD包括版本号、首部长度、服务类型、总长度、标识、标记、分段偏移、生存时间、协议、首部校验和、源IP地址、目的IP地址
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)begin
	   mem_ip_head[0] <= 'd0;
	   mem_ip_head[1] <= 'd0;
	   mem_ip_head[2] <= 'd0;
	   mem_ip_head[3] <= 'd0;
	   mem_ip_head[4] <= 'd0;
	   mem_ip_head[5] <= 'd0;
	   mem_ip_head[6] <= 'd0;
	   mem_ip_head[7] <= 'd0;
	   mem_ip_head[8] <= 'd0;
	   mem_ip_head[9] <= 'd0;
	end 
    else if(stata == IDLE && rise_send_en == 1'b1)begin
	   mem_ip_head[0] <= {4'h4,4'h5,8'h00} ;//版本号4;首部长度5;服务类型0
	   mem_ip_head[1] <=  28+send_data_num*2 ;
	   mem_ip_head[2] <=  mem_ip_head[2]+1 ;
	   mem_ip_head[3] <= {3'b010,13'b0_0000_0000_0000} ;//标记010;分段偏移是0
	   mem_ip_head[4] <= {8'h40,8'h17}  ;//生存时间40;协议17
	   mem_ip_head[5] <= 16'h00_00      ;//首部校验和,初始为0
	   mem_ip_head[6] <=  BOARD_IP[32:16]    ;
	   mem_ip_head[7] <=  BOARD_IP[15: 0]    ;
	   mem_ip_head[8] <=  PC_IP[32:16]       ;
	   mem_ip_head[9] <=  PC_IP[15: 0]       ;
	end 
	else if(stata == IP_HEAD_CHECK_SUM && cnt_check == 'd3)begin
	   mem_ip_head[5] <=    ~ip_head_check[15:0]    ;
	end 
//UDP_HEAD包括源端口号、目的端口号、UDP长度、UDP校验和
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)begin
	   mem_udp_head[0] <= 'd0;
	   mem_udp_head[1] <= 'd0;
	   mem_udp_head[2] <= 'd0;
	   mem_udp_head[3] <= 'd0;
	end 
	else begin
       mem_udp_head[0] <= BOARD_PORT;
	   mem_udp_head[1] <= PC_PORT;
	   mem_udp_head[2] <= send_data_num*2+8 ;
	   mem_udp_head[3] <= 16'h0000 ;    //udp校验和为0
	end 



always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	   cnt_4b <= 'd0;
	else if(stata == PACK_HEAD || stata == ETH_HEAD )
	   if(cnt_4b == 'd1)
	      cnt_4b <= 'd0;
       else 		  
	      cnt_4b <= cnt_4b + 1;
	else if(stata == IP_HEAD || stata == UDP_HEAD || stata == DATA)
	   if(cnt_4b == 'd3)
	      cnt_4b <= 'd0;
       else 		  
	      cnt_4b <= cnt_4b + 1;
	else if(stata == CRC)
	   if(cnt_4b == 'd7)
	      cnt_4b <= 'd0;
       else 		  
	      cnt_4b <= cnt_4b + 1;
	else 
	   cnt_4b <= 'd0;

always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	   cnt    <= 'd0;
	else if( stata == PACK_HEAD )begin
       if(cnt == 8-1 && cnt_4b == 1'b1)
	      cnt <= 'd0;
	   else if(cnt_4b == 1'b1)
	      cnt <= cnt + 1;
	   else 
	      cnt <= cnt;
	end 	  
	else if( stata == ETH_HEAD )begin
       if(cnt == 14-1 && cnt_4b == 1'b1)
	      cnt <= 'd0;
	   else if(cnt_4b == 1'b1)
	      cnt <= cnt + 1;
	   else 
	      cnt <= cnt;
	end  
	else if( stata == IP_HEAD )begin
       if(cnt == (20/2-1) && cnt_4b == 'd3)
	      cnt <= 'd0;
	   else if(cnt_4b == 'd3)
	      cnt <= cnt + 1;
	   else 
	      cnt <= cnt;
	end    
	else if( stata == UDP_HEAD )begin
       if(cnt == (8/2-1) && cnt_4b == 'd3)
	      cnt <= 'd0;
	   else if(cnt_4b == 'd3)
	      cnt <= cnt + 1;
	   else 
	      cnt <= cnt;
	end 
    else if( stata == DATA )begin
       if((cnt == (data_num_add-1)) && cnt_4b == 'd3)
	      cnt <= 'd0;
	   else if(cnt_4b == 'd3)
	      cnt <= cnt + 1;
	   else 
	      cnt <= cnt;
	end 
    else if( stata == CRC )begin
       if((cnt == (4/4-1)) && cnt_4b == 'd7)
	      cnt <= 'd0;
	   else if(cnt_4b == 'd7)
	      cnt <= cnt + 1;
	   else 
	      cnt <= cnt;
	end
    else 
	   cnt <= 'd0; 
    
 
//状态机
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
       stata <= IDLE;
    else begin
	   case(stata)
	       IDLE:                  if(rise_send_en == 1'b1)
		                             stata <= IP_HEAD_CHECK_SUM;
				                  else 
				                     stata <= IDLE;
		   IP_HEAD_CHECK_SUM:     if(cnt_check == 3)
		                             stata <= PACK_HEAD;
				                  else 
				                     stata <= IP_HEAD_CHECK_SUM;
		   PACK_HEAD:             if(cnt == 8-1 && cnt_4b == 1'b1)
		                             stata <= ETH_HEAD;
								  else 
								     stata <= PACK_HEAD;
		   ETH_HEAD:			  if(cnt == 14-1 && cnt_4b == 1'b1)
		                             stata <= IP_HEAD;
								  else 
								     stata <= ETH_HEAD;
		   IP_HEAD:               if(cnt == (20/2-1) && cnt_4b == 'd3)
		                             stata <= UDP_HEAD;
								  else 
								     stata <= IP_HEAD;              
		   UDP_HEAD:              if(cnt == (8/2-1) && cnt_4b == 'd3)
		                             stata <= DATA;
								  else 
								     stata <= UDP_HEAD;
		   DATA:                  if(cnt == (data_num_add-1) && cnt_4b == 'd3)
		                             stata <= CRC;
								  else 
								     stata <= DATA;
		   CRC:                   if(cnt == (4/4-1) && cnt_4b == 'd7)
		                             stata <= IDLE;
								  else 
								     stata <= CRC;
		   default:stata <= IDLE;
	   endcase 
    end 	
	    
//信号输出eth_tx_data、eth_tx_en	    
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	    eth_tx_en <= 1'b0;
	else if(stata == PACK_HEAD || stata == ETH_HEAD || stata == IP_HEAD || stata == UDP_HEAD || stata == DATA || stata == CRC)
	    eth_tx_en <= 1'b1;
	else
	    eth_tx_en <= 1'b0;
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)	begin	
	    flag <= 1'b0; 
        eth_tx_data <= 'd0;
	end 
    else if(stata == PACK_HEAD)
        if(cnt_4b == 0)	
            eth_tx_data <= mem_packet_head[cnt][7:4];
        else 
		    eth_tx_data <= mem_packet_head[cnt][3:0];
    else if(stata == ETH_HEAD)
        if(cnt_4b == 0)	
            eth_tx_data <= mem_eth_head[cnt][7:4];
        else 
		    eth_tx_data <= mem_eth_head[cnt][3:0];
    else if(stata == IP_HEAD)begin
        if(cnt_4b == 0)	
            eth_tx_data <= mem_ip_head[cnt][15:12];
        else if(cnt_4b == 1)
		    eth_tx_data <= mem_ip_head[cnt][11:8];
        else if(cnt_4b == 2)
		    eth_tx_data <= mem_ip_head[cnt][7:4];
		else if(cnt_4b == 3)
            eth_tx_data <= mem_ip_head[cnt][3:0];
	end 
    else if(stata == UDP_HEAD)begin
	    flag <= 1'b1;
        if(cnt_4b == 0)	begin
            
			eth_tx_data <= mem_udp_head[cnt][15:12];
	    end 
        else if(cnt_4b == 1)
		    eth_tx_data <= mem_udp_head[cnt][11:8];
        else if(cnt_4b == 2)
		    eth_tx_data <= mem_udp_head[cnt][7:4];
		else if(cnt_4b == 3)begin
            
			eth_tx_data <= mem_ip_head[cnt][3:0];
		end 
	end 
    else if(stata == DATA)begin
	    flag <= 1'b0;
        if(cnt_4b == 0)	
            eth_tx_data <= reg_send_data[15:12];
        else if(cnt_4b == 1)
		    eth_tx_data <= reg_send_data[11:8];
        else if(cnt_4b == 2)
		    eth_tx_data <= reg_send_data[7:4];
		else if(cnt_4b == 3)
            eth_tx_data <= reg_send_data[3:0];
	end
    else if(stata == CRC)begin
        if(cnt_4b == 0)	
            eth_tx_data <= {~crc_next[0], ~crc_next[1], ~crc_next[2], ~crc_next[3]};
        else if(cnt_4b == 1)
		    eth_tx_data <=  {~crc_data[24],~crc_data[25],~crc_data[26],~crc_data[27]};
        else if(cnt_4b == 2)
		    eth_tx_data <=  {~crc_data[20],~crc_data[21],~crc_data[22],~crc_data[23]};
		else if(cnt_4b == 3)
            eth_tx_data <=  {~crc_data[16],~crc_data[17],~crc_data[18],~crc_data[19]};
		else if(cnt_4b == 4)
		    eth_tx_data <=  {~crc_data[12],~crc_data[13],~crc_data[14],~crc_data[15]};
        else if(cnt_4b == 5)
		    eth_tx_data <=  {~crc_data[8],~crc_data[9],~crc_data[10],~crc_data[11]};
		else if(cnt_4b == 6)
            eth_tx_data <=  {~crc_data[4],~crc_data[5],~crc_data[6],~crc_data[7]};
		else if(cnt_4b == 7)
            eth_tx_data <=  {~crc_data[0],~crc_data[1],~crc_data[2],~crc_data[3]};
    end 
    else 
	    eth_tx_data <= eth_tx_data;


//信号输出send_end
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)
	    send_end <= 1'b0;
	else if(stata == CRC && cnt == (4/4-1) && cnt_4b == 'd7)
	    send_end <= 1'b1;
	else 
	    send_end <= 1'b0;
//输出信号read_data_req
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res)  
       read_data_req <= 1'b0;
    else if((stata == UDP_HEAD && cnt == (8/2-1) && cnt_4b == 'd3)||(stata == DATA && cnt_4b == 'd3))
	   read_data_req <= 1'b1;
	else
	   read_data_req <= 1'b0;
	   
//输出crc信号crc_en
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res) 
      	crc_en <= 1'b0;
    else if(stata == ETH_HEAD || stata == IP_HEAD || stata == UDP_HEAD || stata == DATA)
        crc_en <= 1'b1;
	else 
	    crc_en <= 1'b0;

//输出crc信号crc_clr
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res) 
	   crc_clr <= 1'b0;
	else
	   crc_clr <= send_end;

endmodule 

? ? ? 2.CRC循环冗余校验(该部分代码抄取了征途Pro《FPGA Verilog开发实战指南——基于Altera EP4CE10》2020.12.16(下)中的以太网数据换回实验中的CRC校验代码)

`timescale  1ns/1ns

// Author        : EmbedFire
// Create Date   : 2019/09/03
// Module Name   : crc32_d4
// Project Name  : eth_udp_rmii
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : CRC校验
// 
// Revision      : V1.0
// Additional Comments:
// 
// 实验平台: 野火_征途Pro_FPGA开发板
// 公司    : http://www.embedfire.com
// 论坛    : http://www.firebbs.cn
// 淘宝    : https://fire-stm32.taobao.com


module  crc32_d4
(
    input   wire            sys_clk     ,   //时钟信号
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire    [3:0]   data        ,   //待校验数据
    input   wire            crc_en      ,   //crc使能,校验开始标志
    input   wire            crc_clr     ,   //crc数据复位信号

    output  reg     [31:0]  crc_data    ,   //CRC校验数据
    output  reg     [31:0]  crc_next        //CRC下次校验完成数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
// wire     define
wire    [3:0]   data_sw;    //待校验数据高低位互换

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//data_sw:待校验数据高低位互换
assign  data_sw = {data[0],data[1],data[2],data[3]};

//crc_next:CRC下次校验完成数据
//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
always@(*)
begin
    crc_next    <=  32'b0;
    if(crc_en == 1'b1)
        begin
            crc_next[0] <=  (data_sw[0] ^ crc_data[28]);
            crc_next[1] <=  (data_sw[1] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[29]);
            crc_next[2] <=  (data_sw[2] ^ data_sw[1] ^ data_sw[0]
                            ^ crc_data[28] ^ crc_data[29] ^ crc_data[30]);
            crc_next[3] <=  (data_sw[3] ^ data_sw[2] ^ data_sw[1]
                            ^ crc_data[29] ^ crc_data[30] ^ crc_data[31]);
            crc_next[4] <=  (data_sw[3] ^ data_sw[2] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[30] ^ crc_data[31]) ^ crc_data[0];
            crc_next[5] <=  (data_sw[3] ^ data_sw[1] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[29] ^ crc_data[31]) ^ crc_data[1];
            crc_next[6] <=  (data_sw[2] ^ data_sw[1] ^ crc_data[29]
                            ^ crc_data[30]) ^ crc_data[ 2];
            crc_next[7] <=  (data_sw[3] ^ data_sw[2] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[30] ^ crc_data[31]) ^ crc_data[3];
            crc_next[8] <=  (data_sw[3] ^ data_sw[1] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[29] ^ crc_data[31]) ^ crc_data[4];
            crc_next[9] <=  (data_sw[2] ^ data_sw[1] ^ crc_data[29]
                            ^ crc_data[30]) ^ crc_data[5];
            crc_next[10]<=  (data_sw[3] ^ data_sw[2] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[30] ^ crc_data[31]) ^ crc_data[6];
            crc_next[11]<=  (data_sw[3] ^ data_sw[1] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[29] ^ crc_data[31]) ^ crc_data[7];
            crc_next[12]<=  (data_sw[2] ^ data_sw[1] ^ data_sw[0] ^ crc_data[28]
                            ^ crc_data[29] ^ crc_data[30]) ^ crc_data[8];
            crc_next[13]<=  (data_sw[3] ^ data_sw[2] ^ data_sw[1] ^ crc_data[29]
                                ^ crc_data[30] ^ crc_data[31]) ^ crc_data[9];
            crc_next[14]<=  (data_sw[3] ^ data_sw[2] ^ crc_data[30]
                            ^ crc_data[31]) ^ crc_data[10];
            crc_next[15]<=  (data_sw[3] ^ crc_data[31]) ^ crc_data[11];
            crc_next[16]<=  (data_sw[0] ^ crc_data[28]) ^ crc_data[12];
            crc_next[17]<=  (data_sw[1] ^ crc_data[29]) ^ crc_data[13];
            crc_next[18]<=  (data_sw[2] ^ crc_data[30]) ^ crc_data[14];
            crc_next[19]<=  (data_sw[3] ^ crc_data[31]) ^ crc_data[15];
            crc_next[20]<=  crc_data[16];
            crc_next[21]<=  crc_data[17];
            crc_next[22]<=  (data_sw[0] ^ crc_data[28]) ^ crc_data[18];
            crc_next[23]<=  (data_sw[1] ^ data_sw[0] ^ crc_data[29]
                            ^ crc_data[28]) ^ crc_data[19];
            crc_next[24]<=  (data_sw[2] ^ data_sw[1] ^ crc_data[30]
                            ^ crc_data[29]) ^ crc_data[20];
            crc_next[25]<=  (data_sw[3] ^ data_sw[2] ^ crc_data[31]
                            ^ crc_data[30]) ^ crc_data[21];
            crc_next[26]<=  (data_sw[3] ^ data_sw[0] ^ crc_data[31]
                            ^ crc_data[28]) ^ crc_data[22];
            crc_next[27]<=  (data_sw[1] ^ crc_data[29]) ^ crc_data[23];
            crc_next[28]<=  (data_sw[2] ^ crc_data[30]) ^ crc_data[24];
            crc_next[29]<=  (data_sw[3] ^ crc_data[31]) ^ crc_data[25];
            crc_next[30]<=  crc_data[26];
            crc_next[31]<=  crc_data[27];
        end
end

//crc_data:CRC校验数据
always @(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        crc_data    <=  32'hff_ff_ff_ff;
    else if(crc_clr == 1'b1)
        crc_data    <= 32'hff_ff_ff_ff;
    else if(crc_en == 1'b1)
        crc_data    <=  crc_next;

endmodule

? ?3.下边给出顶层及仿真tb文件,为了方便仿真观察时序,将两部分合成如下代码?

`timescale 1ns/1ns
module tb_udp_send();
reg          clk;
reg          res;
reg          send_en;
reg    [15:0]send_data;
wire   [15:0]send_data_num;

wire         send_end; 
wire         read_data_req;
wire         eth_tx_en;
wire    [3:0]eth_tx_data;
wire         crc_en;
wire         crc_clr;
wire   [31:0]crc_data;
wire   [31:0]crc_next;
parameter    BOARD_MAC   = 48'hFF_FF_FF_FF_FF_FF;
parameter    BOARD_IP    = 32'hFF_FF_FF_FF;
parameter    BOARD_PORT  = 16'd1234 ;
parameter    PC_MAC      = 48'hFF_FF_FF_FF_FF_FF;
parameter    PC_IP       = 32'hFF_FF_FF_FF ;
parameter    PC_PORT     = 16'd1234  ;

parameter    NUM  =  10;
reg      [15:0]mem_data[9:0];
reg       [7:0]cnt;
reg       reg_rd_req;
wire      rise_req;

initial begin
    clk <= 1'b0;
	res <= 1'b0;
	send_en <= 1'b0;
	send_data <= 16'd0;
	reg_rd_req <= 1'b0;
	#100 res <= 1'b1;
	#1000 send_en <= 1'b1;
	#500 send_en <= 1'b0;
end 
always #10 clk <= ~clk;

//发送数据,数据存入mem_data中
always@(posedge clk or negedge clk)
    if(!res)begin
      mem_data[0] <= 0;
	  mem_data[1] <= 0;
	  mem_data[2] <= 0;
	  mem_data[3] <= 0;
	  mem_data[4] <= 0;
	  mem_data[5] <= 0;
	  mem_data[6] <= 0;
	  mem_data[7] <= 0;
	  mem_data[8] <= 0;
	  mem_data[9] <= 0;
    end 
	else begin
	  mem_data[0] <= 16'h1111;
	  mem_data[1] <= 16'h1234;
	  mem_data[2] <= 16'h5678;
	  mem_data[3] <= 16'h9abc;
	  mem_data[4] <= 16'hdef0;
	  mem_data[5] <= 16'h1234;
	  mem_data[6] <= 16'h5678;
	  mem_data[7] <= 16'h9abc;
	  mem_data[8] <= 16'hdef0;
	  mem_data[9] <= 16'haaaa;
	end 
assign   send_data_num = NUM;

always@(clk)
    reg_rd_req <= read_data_req; 
assign rise_req = (~reg_rd_req)&read_data_req;
always@(posedge clk or negedge clk)
    if(!res)
	   cnt <= 'd0;
	else if(rise_req && cnt == NUM-1)
	   cnt <= 'd0;
	else if(rise_req)
       cnt <= cnt + 1 ;
	else
	   cnt <= cnt; 
always@(clk)
    if(rise_req)
      send_data <= mem_data[cnt];
    else
      send_data <= send_data;	
	   
	   
udp_send
#(
   .   BOARD_MAC  (BOARD_MAC) ,   //板卡MAC地址
   .   BOARD_IP   (BOARD_IP)      ,   //板卡IP地址
   .   BOARD_PORT (BOARD_PORT)             ,   //板卡端口号
   .   PC_MAC     (PC_MAC) ,   //PC机MAC地址
   .   PC_IP      (PC_IP)     ,   //PC机IP地址
   .   PC_PORT    (PC_PORT)                 //PC机端口号
)u_udp_send
(
     .sys_clk           (clk)     ,
	 .sys_res           (res)     ,
	 .send_en           (send_en)     ,
	 .send_data         (send_data)     ,
	 .send_data_num     (send_data_num)     ,
	 .crc_data          (crc_data)     ,   
     .crc_next          (crc_next[31:28])     ,
	 .send_end          (send_end)     ,
	 .read_data_req     (read_data_req)     ,
	 .eth_tx_en         (eth_tx_en)     ,
	 .eth_tx_data       (eth_tx_data)     ,
	 .crc_en            (crc_en)     , 
	 .crc_clr           (crc_clr)
);

crc32_d4 u_crc32_d4
(
    . sys_clk    (clk) ,   //时钟信号
    . sys_rst_n  (res) ,   //复位信号,低电平有效
    . data       (eth_tx_data),   //待校验数据
    . crc_en     (crc_en) ,   //crc使能,校验开始标志
    . crc_clr    (crc_clr),   //crc数据复位信号
    . crc_data   (crc_data),   //CRC校验数据
    . crc_next   (crc_next)     //CRC下次校验完成数据
);


endmodule

? ? ? ? 下图给出仿真的时序结果:图1是整体信号的时序图,图2对应PACK_HEAD状态输出的信号,图3对应ETH_HEAD状态输出的信号,图4对应IP_HEAD状态输出的信号,图5对应UDP_HEAD状态输出的信号,图6对应DATA状态输出的信号,图7对应CRC状态输出的信号。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图1

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??图2

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图3

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图4

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图5

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 图6

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图7?


总结

? ? ? ? ?对于以太网通信而言,更多的还是趋向于单片机的控制,其程序代码比Verilog要简单许多,读者可以尝试着做一下该实验。通过FPGA控制以太网通信,其时序逻辑相对比较复杂,本文仅仅给出了FPGA通过网口进行发送的时序、代码,由于疫情在家,身边没有硬件设备,后期将会对该程序应用于实践。

? ? ? ? ??初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 12:07:35  更:2022-02-26 12:11:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:43:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码