当前位置:   article > 正文

FPGA实现千兆以太网发送_fpga 采集数据通过千兆以太网 传出

fpga 采集数据通过千兆以太网 传出

科研要求,使用手上的DE2-115开发板实现千兆以太网的数据发送
千兆以太网使用的时钟频率为125MHz,一般的GMII接口由于收发数据所使用的数据线为8根即一个时钟周期的上升沿可以发送8bit数据,而DE2-115开发板所使用的接口为RGMII,收发数据所使用的数据线为4根,所以需要在一个时钟周期的上升沿和下降沿都进行数据的传输。如下图TX_DATA和RX_DATA。
DE2-115 千兆以太网接口
接下来就是具体的verilog代码的编写了,在这里参考了黑金开发板百兆网口的代码。以太网一帧的数据并不只包括数据,还有以太网协议用来检验是否为一帧数据的开始、结束、传输协议、校验码等参数,所以在发送一帧数据前需要发送这些参数,CRC校验码在一帧数据的最后被发送,这些具体格式可以去网上参考。

module ethernet
	#(
    //开发板MAC地址 00-11-22-33-44-65
    parameter BOARD_MAC = 48'h00_11_22_33_44_65,       
    //开发板IP地址 192.168.1.12    
    parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd12},   
    //目的MAC地址 2C_56_DC_19_01_F9  
    parameter  DES_MAC  = 48'h2C_56_DC_19_01_F9, 
    //目的IP地址 192.168.1.13     
    parameter  DES_IP   = {8'd192,8'd168,8'd1,8'd13} 
    )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

首先设置源IP地址,源MAC地址,目的IP地址以及目的MAC地址。

//上升沿发送改为上升沿和下降沿都发送
always @(posedge eth_tx_clk_250m or negedge rst_n)begin
	if(!rst_n)
		cnt 			<= 1'b1;
	else if(!cnt_1)begin
		if(cnt)begin
			cnt			<= 1'b0;
			eth_tx_data <= eth_tx_data_s[3:0];
			//eth_rx_data_s[3:0] <= eth_rx_data;
		end
		else if(!cnt) begin
			cnt			<= 1'b1;
			eth_tx_data <= eth_tx_data_s[7:4];
			//eth_rx_data_s[7:4] <= eth_rx_data;
		end
	end
end

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里使用了250MHz的时钟作为RGMII接口在125MHZ时钟的上升沿和下降沿都进行数据的发送。这里本来可以使用quartus提供的IP核Addioout将单沿数据转换为双沿数据,但在实际使用过程采用SignalTap进行观测的时候发现抓取不到该IP核输出的数据,而使用ModelSim进行仿真的时候是有输出的。最后在网上查到有帖子说是SignalTap抓取不到双沿数据,但是本着科学探索的精神,使用WireShark在PC上抓取传输上来的数据包,结果发现问题比较复杂,遂放弃……

/***********************************************/

//Project Name : UDP_Send
//Email		   :
//Create Time	: 2021/01/09 13:36
//Editor       : Liu
//Version		: Rev1.0.0

/***********************************************/

module udp_send
	#(
		//开发板MAC地址 00-11-22-33-44-65
		parameter BOARD_MAC = 48'h00_11_22_33_44_65,      
		//开发板IP地址 192.168.1.12     
		parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd12},   
		//目的MAC地址 2C_56_DC_19_01_F9
		parameter DES_MAC   = 48'h2C_56_DC_19_01_F9, 
		//目的IP地址 192.168.1.13     
		parameter DES_IP    = {8'd192,8'd168,8'd1,8'd13} 
	)
(
          
		input                clk,			    //时钟信号
		input						rst_n,        	 //复位信号,低电平有效	 
		input						tx_start_en,    //以太网开始发送数据信号
		input			 [31:0]	tx_data,	    	 //以太网待发送数据
		input        [15:0]  tx_byte_num,    //以太网发送的有效字节数
	   input        [31:0]  crc_data   ,    //CRC校验数据
		input        [3:0]   crc_next   ,    //CRC下次校验完成数据
		output  reg          tx_done    ,    //以太网发送完成信号
		output  reg          tx_req     ,    //读数据请求信号
		output  reg          eth_tx_en  ,    //MII输出数据有效信号
		output  reg  [7:0]   eth_tx_data_s,  //MIIH输出数据
		output  reg          crc_en     ,    //CRC开始校验使能
		output  reg          crc_clr        //CRC数据复位信号 
);


//状态机
localparam  st_idle      = 7'b0000001;   //初始状态,等待开始发送信号
localparam  st_check_sum = 7'b0000010;   //IP首部校验和
localparam  st_preamble  = 7'b0000100;   //发送前导码+帧起始界定符
localparam  st_eth_head  = 7'b0001000;   //发送以太网帧头
localparam  st_ip_head   = 7'b0010000;   //发送IP首部+UDP首部
localparam  st_tx_data   = 7'b0100000;   //发送数据
localparam  st_crc       = 7'b1000000;   //发送CRC校验值

localparam  ETH_TYPE     = 16'h0800;    	//以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam  MIN_DATA_NUM = 16'd18;

//reg define
reg    [6:0]       cur_state        ;
reg    [6:0]       next_state       ;
                                    
reg    [7:0]       preamble[7:0]    ;     //前导码
reg    [7:0]       eth_head[13:0]   ;     //以太网首部
reg    [31:0]      ip_head[6:0]     ;     //IP首部 + UDP首部
                                    
reg                start_en_d0      ;
reg                start_en_d1      ;
reg    [15:0]      tx_data_num      ;     //发送的有效数据字节个数
reg    [15:0]      total_num        ;     //总字节数
reg    [15:0]      udp_num          ;     //UDP字节数
reg                skip_en          ;     //控制状态跳转使能信号
reg    [4:0]       cnt              ;
reg    [31:0]      check_buffer     ;     //首部校验和
reg    [2:0]       tx_bit_sel       ;
reg    [15:0]      data_cnt         ;     //发送数据个数计数器
reg                tx_done_t        ;
reg    [4:0]       real_add_cnt     ;     //以太网数据实际多发的字节数
                                    
//wire define                       
wire               pos_start_en     ;     //开始发送数据上升沿
wire   [15:0]      real_tx_data_num ;     //实际发送的字节数(以太网最少字节要求)

assign  pos_start_en = (~start_en_d1) & start_en_d0;
assign  real_tx_data_num = (tx_data_num >= MIN_DATA_NUM) 
                           ? tx_data_num : MIN_DATA_NUM; 

//采tx_start_en的上升沿
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        start_en_d0 <= 1'b0;
        start_en_d1 <= 1'b0;
    end    
    else begin
        start_en_d0 <= tx_start_en;
        start_en_d1 <= start_en_d0;
    end
end 

//寄存数据有效字节
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_data_num <= 16'd0;
        total_num <= 16'd0;
        udp_num <= 16'd0;
    end
    else begin
        if(pos_start_en && cur_state == st_idle) begin
            //数据长度
            tx_data_num <= 16'd4;	 //tx_byte_num;        
            //IP长度:有效数据+IP首部长度            
            total_num <= 16'd4 + 16'd28;  //tx_byte_num
            //UDP长度:有效数据+UDP首部长度            
            udp_num <= 16'd4 + 16'd8;     //tx_byte_num          
        end    
    end
end   

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;  
    else
        cur_state <= next_state;
end

always @(*) begin
    next_state = st_idle;
    case(cur_state)
        st_idle     : begin                               //等待发送数据
            if(skip_en)                
                next_state = st_check_sum;
            else
                next_state = st_idle;
        end  
        st_check_sum: begin                               //IP首部校验
            if(skip_en)
                next_state = st_preamble;
            else
                next_state = st_check_sum;    
        end                             
        st_preamble : begin                               //发送前导码+帧起始界定符
            if(skip_en)
                next_state = st_eth_head;
            else
                next_state = st_preamble;      
        end
        st_eth_head : begin                               //发送以太网首部
            if(skip_en)
                next_state = st_ip_head;
            else
                next_state = st_eth_head;      
        end              
        st_ip_head : begin                                //发送IP首部+UDP首部               
            if(skip_en)
                next_state = st_tx_data;
            else
                next_state = st_ip_head;      
        end
        st_tx_data : begin                                //发送数据                  
            if(skip_en)
                next_state = st_crc;
            else
                next_state = st_tx_data;      
        end
        st_crc: begin                                     //发送CRC校验值
            if(skip_en)
                next_state = st_idle;
            else
                next_state = st_crc;      
        end
        default : next_state = st_idle;   
    endcase
end    

//发送数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        skip_en <= 1'b0; 
        cnt <= 5'd0;
        check_buffer <= 32'd0;
        ip_head[1][31:16] <= 16'd0;
        tx_bit_sel <= 3'b0;
        crc_en <= 1'b0;
        eth_tx_en <= 1'b0;
        eth_tx_data_s <= 8'd0;
        tx_req <= 1'b0;
        tx_done_t <= 1'b0; 
        data_cnt <= 16'd0;
        real_add_cnt <= 5'd0;
        //初始化数组    
        //前导码 7个8'h55 + 1个8'hd5
        preamble[0] <= 8'h55;                 
        preamble[1] <= 8'h55;
        preamble[2] <= 8'h55;
        preamble[3] <= 8'h55;
        preamble[4] <= 8'h55;
        preamble[5] <= 8'h55;
        preamble[6] <= 8'h55;
        preamble[7] <= 8'hd5;
        //目的MAC地址
        eth_head[0] <= DES_MAC[47:40];
        eth_head[1] <= DES_MAC[39:32];
        eth_head[2] <= DES_MAC[31:24];
        eth_head[3] <= DES_MAC[23:16];
        eth_head[4] <= DES_MAC[15:8];
        eth_head[5] <= DES_MAC[7:0];
        //源MAC地址
        eth_head[6] <= BOARD_MAC[47:40];
        eth_head[7] <= BOARD_MAC[39:32];
        eth_head[8] <= BOARD_MAC[31:24];
        eth_head[9] <= BOARD_MAC[23:16];
        eth_head[10] <= BOARD_MAC[15:8];
        eth_head[11] <= BOARD_MAC[7:0];
        //以太网类型
        eth_head[12] <= ETH_TYPE[15:8];
        eth_head[13] <= ETH_TYPE[7:0];        
    end
	 else begin
        skip_en <= 1'b0;
        tx_req <= 1'b0;
        crc_en <= 1'b0;
        eth_tx_en <= 1'b0;
        tx_done_t <= 1'b0;
        case(cur_state)
            st_idle     : begin
                if(pos_start_en)
                    skip_en <= 1'b1; 
                if(skip_en) begin
                    //版本号:4 首部长度:5(单位:32bit,20byte/4=5)
						  //IPV4:4 IPV6:6  
                    ip_head[0] <= {8'h45, 8'h00, total_num};  
                    //16位标识,每次发送累加1      
                    ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1; 
                    //bit[15:13]: 010表示不分片
                    ip_head[1][15:0] <= 16'h4000;    
                    //协议:17(udp)                  
                    ip_head[2] <= {8'h40,8'd17,16'h0};//8'd17 = 8'h11   
                    //源IP地址               
                    ip_head[3] <= BOARD_IP;    
                    //目的IP地址                        
                    ip_head[4] <= DES_IP;       
                    //16位源端口号:1234  16位目的端口号:1234                      
                    ip_head[5] <= {16'd1234,16'd1234};  
                    //16位udp长度,16位udp校验和              
                    ip_head[6] <= {udp_num,16'h0000};                   
                end                                                   
            end
				st_check_sum: begin                           //IP首部校验
                cnt <= cnt + 5'd1;
                if(cnt == 5'd0) begin                   
                    check_buffer <=   ip_head[0][31:16] + ip_head[0][15:0]
                                    + ip_head[1][31:16] + ip_head[1][15:0]
                                    + ip_head[2][31:16] + ip_head[2][15:0]
                                    + ip_head[3][31:16] + ip_head[3][15:0]
                                    + ip_head[4][31:16] + ip_head[4][15:0];
                end
                else if(cnt == 5'd1)                      //可能出现进位,累加一次
                    check_buffer <= check_buffer[31:16] + check_buffer[15:0];
                else if(cnt == 5'd2) begin                //可能再次出现进位,累加一次
                    check_buffer <= check_buffer[31:16] + check_buffer[15:0];
                    skip_en <= 1'b1;
                end                             
                else if(cnt == 5'd3) begin                //按位取反 
                    cnt <= 5'd0;            
                    ip_head[2][15:0] <= ~check_buffer[15:0];
                end    
            end 
				st_preamble : begin                           //发送前导码+帧起始界定符
                eth_tx_en <= 1'b1;
                eth_tx_data_s <= preamble[cnt][7:0];    
                  if(cnt == 5'd6)                       
                      skip_en <= 1'b1; 
						if(skip_en)cnt <= 5'd0;
						else
							 cnt <= cnt + 5'd1;
				 end
            st_eth_head : begin                           //发送以太网首部
                eth_tx_en <= 1'b1;
                crc_en <= 1'b1;
                eth_tx_data_s <= eth_head[cnt][7:0];
                if(cnt == 5'd12)
                    skip_en <= 1'b1;
					 if(skip_en)
						  cnt <= 5'd0;                                 
                else
						  cnt <= cnt + 5'd1;
              
            end 
				 st_ip_head  : begin                           //发送IP首部 + UDP首部
                crc_en <= 1'b1;
                eth_tx_en <= 1'b1; 
					 tx_bit_sel <= tx_bit_sel + 3'd1; 
                if(tx_bit_sel == 3'd1)
                    eth_tx_data_s <= ip_head[cnt][31:24];
                else if(tx_bit_sel == 3'd2)
                    eth_tx_data_s <= ip_head[cnt][23:16];
					 else if(tx_bit_sel == 3'd3)begin
                    eth_tx_data_s <= ip_head[cnt][15:8];
						  if(cnt == 5'd6)skip_en <= 1'b1;
					 end		   
                else if(tx_bit_sel == 3'd4) begin
                    eth_tx_data_s <= ip_head[cnt][7:0];
						  tx_bit_sel <= 3'd1; 
                    if(cnt == 5'd6) begin
								tx_bit_sel <= 3'd0;
                        //提前读请求数据,等待数据有效时发送
                        tx_req <= 1'b1;
							   cnt <= 5'd0;
                    end 
						  else
                        cnt <= cnt + 5'd1; 	  
						  end                            
            end
            st_tx_data  : begin                           //发送数据
                crc_en <= 1'b1;
                eth_tx_en <= 1'b1;
                tx_bit_sel <= tx_bit_sel + 3'd1;  
                if(tx_bit_sel[0] == 1'b0) begin
                    if(data_cnt < tx_data_num - 16'd1)
                        data_cnt <= data_cnt + 16'd1;                        
                    else if(data_cnt == tx_data_num - 16'd1)begin
                        //如果发送的有效数据少于18个字节,在后面填补充位
                        //补充的值为最后一次发送的有效数据
                        if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)
                            real_add_cnt <= real_add_cnt + 5'd1;  
                        else
                            skip_en <= 1'b1;
                    end        
                end        
                if(tx_bit_sel == 3'd0)
                    eth_tx_data_s <= tx_data[31:24];
                else if(tx_bit_sel == 3'd1)
                    eth_tx_data_s <= tx_data[23:16];
                else if(tx_bit_sel == 3'd2)
                    eth_tx_data_s <= tx_data[15:8];
                else if(tx_bit_sel == 3'd3) begin
                    eth_tx_data_s <= tx_data[7:0];
						  tx_bit_sel <= 3'd0; 
                    if(data_cnt != tx_data_num - 16'd1)
                        tx_req <= 1'b1;  
                end                                                                                                                                                   
                if(skip_en) begin
                    data_cnt <= 16'd0;
                    real_add_cnt <= 5'd0;
                    tx_bit_sel <= 3'd0;
                end                                                          
            end  
            st_crc      : begin                          //发送CRC校验值
                eth_tx_en <= 1'b1;
                tx_bit_sel <= tx_bit_sel + 3'd1;
                if(tx_bit_sel == 3'd0)
                    //注意是crc_next
                    eth_tx_data_s <= {~crc_data[24],~crc_data[25],~crc_data[26],
                                   ~crc_data[27], ~crc_next[0], ~crc_next[1], ~crc_next[2], ~crc_next[3]};
                else if(tx_bit_sel == 3'd1)
                    eth_tx_data_s <= {~crc_data[16],~crc_data[17],~crc_data[18],
                                   ~crc_data[19],~crc_data[20],~crc_data[21],~crc_data[22], ~crc_data[23]};
					 
                else if(tx_bit_sel == 3'd2)
						  eth_tx_data_s <= {~crc_data[8],~crc_data[9],~crc_data[10],
                                   ~crc_data[11], ~crc_data[12],~crc_data[13],~crc_data[14], ~crc_data[15]};
                else if(tx_bit_sel == 3'd3)begin
                    eth_tx_data_s <= {~crc_data[0],~crc_data[1],~crc_data[2],
                                   ~crc_data[3], ~crc_data[4],~crc_data[5],~crc_data[6], ~crc_data[7]}; 
                    skip_en <= 1'b1;
                    tx_done_t <= 1'b1;
						  tx_bit_sel <= 3'd0;
                end                                                                                                                                                                         
            end                          
            default :;  
        endcase                                             
    end
end            

//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_done <= 1'b0;
        crc_clr <= 1'b0;
    end
    else begin
        tx_done <= tx_done_t;
        crc_clr <= tx_done_t;
    end
end
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381

上边就是整个数据发送的代码,主要包括一个有限状态机。这块需要自己看代码理解,配合SignalTap观察具体状态,可以配合WireShark抓包,根据抓取到的数据分析问题。
千兆以太网发送代码
verilog代码
百度云链接
链接:https://pan.baidu.com/s/13ErHI6FA_ibCeTBW2vQAiw
提取码:oq2s
百度云链接为最新代码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/966966
推荐阅读
相关标签
  

闽ICP备14008679号