Ethernet communication UDP - data transmission

catalogue

1, Introduction to Ethernet communication

1.OSI seven layer model

2. Ethernet packet analysis

3.IP header checksum calculation and inspection

2, Ethernet communication example

1. Overall experimental block diagram

2. Sequence diagram of transmission part

3. Experimental code and simulation results

summary

1, Introduction to Ethernet communication

Previously, we described a variety of communication protocols (serial port, IIC, SPI and other protocols), and Ethernet communication is a relatively high-speed communication mode. At present, Ethernet refers to the LAN composed of IEEE 802.3 standard. IEEE 802.3 standard mainly specifies the medium access control sublayer (MAC) in the physical layer (PHY) and data link layer of the reference model. To put it simply, in order to unify the communication standard, the international organization for Standardization (ISO) has formulated the OSI (Open System Interconnection) model of the whole Ethernet communication structure, which is translated into open system interconnection.

1.OSI seven layer model

OSI defines the seven layer framework of network interconnection (physical layer, data link layer, network layer, transport layer, session layer, presentation layer and application layer), that is, OSI open interconnection system reference model. Each layer has different functions and performs its own duties in network communication. The whole model includes hardware and software definitions. OSI model is only an ideal layer, and the general network system only involves several layers. The reference model and brief introduction of each layer are shown in the figure below.

In the physical layer, the media used in Ethernet (crystal head network cable), data coding method (Manchester coding) and conflict detection mechanism (CSMA/CD conflict detection) are mainly specified. In practical application, its function is mainly realized by a PHY chip.

In the data link layer, it mainly specifies the MAC sublayer in the lower half of the data link layer, which is mainly responsible for data exchange with the physical layer, such as whether data can be sent, whether the sent data is correct, and controlling the data flow. It automatically adds some control signals to the data packets from the upper layer and gives them to the physical layer. When the receiver gets the normal data, it will automatically remove the MAC control signal and deliver the data packet to the upper layer.

2. Ethernet packet analysis

Next, analyze a packet of data sent by Ethernet, as shown in the figure below:

3.IP header checksum calculation and inspection

The calculation method of IP header checksum in the above protocol is as follows:

IP header checksum calculation

The check byte is forcibly set to 0, and the 20 bytes of the IP header are added separately according to 2 bytes, i.e. 16 bits. If it is greater than FFFF, the high 16 bits and low 16 bits are added until the final result is 16 bit data. Invert the calculation result as the IP header checksum byte.

For example: grab the IP packet and take the header part (20B) of the IP datagram. The data is as follows: 45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d, and calculate the IP header checksum.

(1) set the checksum field b5 2e to 00 00, and the data becomes:

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

(2) data inverse code summation in 2 bytes:

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

(3) add carry (3) to the lower 16 bits (4ace):

            0003+4ace=4ad1

(4) invert 4ad1 to get: checksum = b52e

        
      IP Head verification and inspection

Sum the binary inverse code for each 16 bit in the IP header, and take the inverse code for the calculation result. If the result is 0, it passes the test, otherwise, it does not pass the test.

Example: verify IP header 45 00 00 30 80 4c 40 00 80 06 b5 2e d3 43 11 7b cb 51 15 3d

(1) reverse code summation of IP header:

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

          0003+fffc=ffff

(2) inverse code of summation result:

Verify that ffff=0 is correct.

For the cyclic redundancy code check (CRC) check in the above protocol, please refer to the CRC CRC check explained in the FPGA class of V3 College - teacher you Tencent from the introduction to the last class of actual combat. Personally, I feel that the explanation is very good and suitable for beginners.

2, Ethernet communication example

Requirements: FPGA collects all experimental data, packages all data, and uploads them to the upper computer through the network port for data analysis. This experiment only completes the process of communication between FPGA and host computer through network port.

1. Overall experimental block diagram

The experimental block diagram of the whole system is given below:

 

Input signal: sys_clk, which is obtained by dividing the frequency of the incoming clock (eth_clk) from the external PHY chip;

                           sys_res, input by external reset button

                          sys_en, data transmission start signal, transmitted from outside

                          send_data, data to be sent, imported from outside

                          send_data_num, the effective number of data to be sent, which is imported from the outside;
     
Output signal: send_end indicates that the sending of single packet data is completed;
                         read_data_req, indicating the data reading request signal to be sent, which is transmitted to the data storage module as the data reading enable
        
                         eth_tx_en indicates the output data enable signal
                         
                         eth_tx_data, indicating output data

2. Sequence diagram of transmission part

                        
The sequence diagram is as follows:

3. Experimental code and simulation results

The experimental code is given below:

1. Sending part

module udp_send
#(
    parameter   BOARD_MAC   = 48'hFF_FF_FF_FF_FF_FF ,   //Board MAC address
    parameter   BOARD_IP    = 32'hFF_FF_FF_FF       ,   //Board IP address
    parameter   BOARD_PORT  = 16'd1234              ,   //Board port number
    parameter   PC_MAC      = 48'hFF_FF_FF_FF_FF_FF ,   //MAC address of PC
    parameter   PC_IP       = 32'hFF_FF_FF_FF       ,   //IP address of PC
    parameter   PC_PORT     = 16'd1234                  //PC port number
)
(
     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;   //Verify the 20 bytes of the IP header
parameter     PACK_HEAD = 10'b0000_0001_00;           //Send preamble and frame start delimiter
parameter     ETH_HEAD = 10'b0000_0010_00;            //Sending destination MAC address, source MAC address and type
parameter     IP_HEAD = 10'b0000_0100_00;             //Send IP header 20 bytes
parameter     UDP_HEAD = 10'b0000_1000_00;            //Send UDP header 8 bytes
parameter     DATA = 10'b0001_0000_00;                
parameter     CRC = 10'b0010_0000_00;

localparam    ETH_TYPE    =   16'h0800    ;    //Protocol type IP protocol

reg     [7 :0]   mem_packet_head [7 :0];   //packet header 
reg     [7 :0]   mem_eth_head    [13:0];   //Ethernet header includes destination MAC address, source MAC address and type
reg     [15:0]   mem_ip_head     [9 :0];   //IP header 
reg     [15:0]   mem_udp_head    [3 :0];   //UDP header 
wire     [15:0]   reg_send_data;
reg     [31:0]   ip_head_check;

reg      [9:0]stata ;
reg      [7:0]cnt   ;//Send count (count plus 1 represents one byte)
reg      [7:0]cnt_4b;//Counting plus 1 represents 4 bits
reg      [1:0]cnt_check;//ip header checksum counts for 4 times in total. Sum 20 bytes in the first count, add the high 16 bits to the low 16 bits in the second count, repeat the last operation in the third count, and reverse the fourth count
reg      [11:0]cnt_add;
wire     [15:0]data_num_add; //If the number of transmissions does not meet 46, the transmission data needs to be supplemented
reg      flag;

reg           reg_send_en;
wire          rise_send_en;
//Get send_ Rising edge of 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;
//Judge whether the transmitted data meets the minimum 46 requirements
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 header checksum)
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 includes 7 8'h55 and 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 includes destination MAC address, source MAC address and type
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 includes version number, header length, service type, total length, identification, mark, segment offset, lifetime, protocol, header checksum, source IP address and destination IP address
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} ;//Version number 4; Head length 5; Service type 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} ;//Mark 010; The segment offset is 0
	   mem_ip_head[4] <= {8'h40,8'h17}  ;//Survival time 40; Agreement 17
	   mem_ip_head[5] <= 16'h00_00      ;//The initial checksum is 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 includes source port number, destination port number, UDP length and UDP checksum
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 checksum is 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; 
    
 
//State machine
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 	
	    
//Signal output 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;


//Signal output 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;
//Output signal 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;
	   
//Output crc signal_ 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;

//Output crc signal_ clr
always@(posedge sys_clk or negedge sys_res)
    if(!sys_res) 
	   crc_clr <= 1'b0;
	else
	   crc_clr <= send_end;

endmodule 

      2.CRC cyclic redundancy check (this part of the code copies the CRC check code in the Ethernet data exchange experiment in ZhengTu Pro FPGA Verilog development practice guide - based on Altera EP4CE10 2020.12.16 (Part 2))

`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 verification
// 
// Revision      : V1.0
// Additional Comments:
// 
// Experimental platform: Wildfire_ Journey Pro_FPGA development board
// Company: http://www.embedfire.com
// Forum: http://www.firebbs.cn
// TaoBao: https://fire-stm32.taobao.com


module  crc32_d4
(
    input   wire            sys_clk     ,   //clock signal
    input   wire            sys_rst_n   ,   //Reset signal, active at low level
    input   wire    [3:0]   data        ,   //Data to be verified
    input   wire            crc_en      ,   //crc enable, check start flag
    input   wire            crc_clr     ,   //crc data reset signal

    output  reg     [31:0]  crc_data    ,   //CRC check data
    output  reg     [31:0]  crc_next        //CRC next verification completion data
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
// wire     define
wire    [3:0]   data_sw;    //The high and low bits of the data to be verified are exchanged

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

//data_sw: high and low bits of data to be verified are exchanged
assign  data_sw = {data[0],data[1],data[2],data[3]};

//crc_next:CRC next verification completion data
//The generating polynomial of CRC32 is: 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 verification data
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. The top layer and simulation tb files are given below. In order to facilitate the simulation observation of timing, the two parts are combined into the following code

`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;

//Send data and store it in MEM_ In 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) ,   //Board MAC address
   .   BOARD_IP   (BOARD_IP)      ,   //Board IP address
   .   BOARD_PORT (BOARD_PORT)             ,   //Board port number
   .   PC_MAC     (PC_MAC) ,   //MAC address of PC
   .   PC_IP      (PC_IP)     ,   //IP address of PC
   .   PC_PORT    (PC_PORT)                 //PC port number
)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) ,   //clock signal
    . sys_rst_n  (res) ,   //Reset signal, active at low level
    . data       (eth_tx_data),   //Data to be verified
    . crc_en     (crc_en) ,   //crc enable, check start flag
    . crc_clr    (crc_clr),   //crc data reset signal
    . crc_data   (crc_data),   //CRC check data
    . crc_next   (crc_next)     //CRC next verification completion data
);


endmodule

The following figure shows the timing results of the simulation: Figure 1 is the timing diagram of the overall signal, and Figure 2 corresponds to pack_ The signal output in the head state, figure 3 corresponds to eth_ The signal output in the head state, as shown in Figure 4, corresponds to IP_ The signal output in the head state, as shown in Figure 5, corresponds to UDP_ For the signal output in the head state, FIG. 6 corresponds to the signal output in the DATA state, and Fig. 7 corresponds to the signal output in the CRC state.

Figure 1

Figure 2

Figure 3

Figure 4

Figure 5

Figure 6

Figure 7

summary

For Ethernet communication, it still tends to be controlled by single chip microcomputer. Its program code is much simpler than Verilog. Readers can try to do this experiment. The timing logic of Ethernet communication controlled by FPGA is relatively complex. This paper only gives the timing and code of FPGA transmission through the network port. Because the epidemic is at home and there is no hardware equipment around, the program will be applied to practice in the later stage.

It is inevitable that there are errors in the article for the first time. I hope readers can correct and give private letters in time. I hope you can make progress together!

Keywords: Verilog network Network Protocol udp

Added by MrTL on Tue, 22 Feb 2022 07:03:44 +0200