当前位置:首页 > FPGA > 正文内容

(归纳整理)一、Kintex7实现以太网通信(UDP通信)

chanra1n8个月前 (04-27)FPGA1386

一、代码结构

image.png

二、代码解析

顶层模块(目前代码实现是回环)

// 模块主要功能是实现以太网通信的RGMII接口
module rgmii_b(
    // 输入信号
    input				sys_nrst,         // 系统复位信号,低电平有效
    input				sys_clk,          // 系统时钟
    
    // 输出信号
    output				e_reset,          // 以太网复位信号
    output				e_mdc,            // 以太网MII管理数据时钟
    inout				e_mdio,           // 以太网MII管理数据输入输出线
    
    // 以太网GMII接收相关信号
    input				e_rxc,            // 125MHz以太网GMII接收时钟
    input				e_rxdv,           // 以太网GMII接收数据有效信号
    input				e_rxer,           // 以太网GMII接收错误信号
    input	[7 : 0]		e_rxd,            // 以太网GMII接收数据[7:0]
    
    // 以太网GMII发送相关信号
    output				e_gtxc,           // 125MHz以太网GMII发送时钟
    output				e_txen,           // 以太网GMII发送使能信号
    output				e_txer,           // 以太网GMII发送错误信号
    output	[7 : 0]		e_txd             // 以太网GMII发送数据[7:0]
);

// 内部信号声明
wire	[31 : 0]	ram_wr_data;      // RAM写入数据
wire	[31 : 0]	ram_rd_data;      // RAM读取数据
wire	[8 : 0]		ram_wr_addr;      // RAM写入地址
wire	[8 : 0]		ram_rd_addr;      // RAM读取地址

wire	[3 : 0]		tx_state;         // 发送状态
wire	[3 : 0]		rx_state;         // 接收状态
wire	[15 : 0]	rx_total_length;  // 接收到的总长度
wire	[15 : 0]	tx_total_length;  // 要发送的总长度
wire	[15 : 0]	rx_data_length;   // 接收数据长度
wire	[15 : 0]	tx_data_length;   // 发送数据长度

wire				data_receive;     // 数据接收完成信号
reg					ram_wr_finish;    // RAM写完成标志

reg					ram_wren_i;       // RAM写使能中间变量
reg		[8 : 0]		ram_addr_i;       // 写RAM的地址中间变量
reg		[31 : 0]	ram_data_i;       // 写RAM的数据中间变量
reg		[4 : 0]		i;                // 循环计数变量

wire				data_o_valid;     // 输出数据有效信号
wire				wea;              // RAM写使能信号
wire	[8 : 0]		addra;            // RAM的地址
wire	[31 : 0]	dina;             // 写入RAM的数据

// 初始化UDP包数据内容
reg		[31 : 0]	udp_data [4 : 0]; // UDP数据内容数组

// 以太网MDC和MDIO信号常态赋值为1
assign e_mdc = 1'b1;
assign e_mdio = 1'b1;
// 根据RAM写完成标志和数据输出有效信号来控制RAM写使能和相关地址数据的赋值
assign wea = ram_wr_finish ? data_o_valid : ram_wren_i;
assign addra = ram_wr_finish ? ram_wr_addr : ram_addr_i;
assign dina = ram_wr_finish ? ram_wr_data : ram_data_i;

// 判断是否接收到数据并相应地设置发送数据长度
assign tx_data_length = data_receive ? rx_data_length : 16'd28;
assign tx_total_length = data_receive ? rx_total_length : 16'd48;

//////////////////////////////////////////////////////////////////////////
// GMII PLL相关控制信号,用于时钟信号的生成和复位信号的控制
wire gmii_ctrl_nrst;
wire e_gtxc_local;

// GMII发送时钟赋值
assign e_gtxc = e_gtxc_local;
// 以太网复位信号赋值
assign e_reset = gmii_ctrl_nrst;

// GMII PLL实例化
gmii_pll gmii_pll_inst0(
    .gmii_clk	(e_gtxc_local),
    .locked		(gmii_ctrl_nrst),
    .resetn		(sys_nrst),
    .clk_in1	(sys_clk)
);
//////////////////////////////////////////////////////////////////////////
udp udp_inst(
            
            .reset_n			(gmii_ctrl_nrst),
            .g_clk				(e_gtxc_local),

		.e_rxc				(e_rxc),
		.e_rxd				(e_rxd),
		.e_rxdv				(e_rxdv),
		.e_txen				(e_txen),
		.e_txd				(e_txd),
		.e_txer				(e_txer),

		.data_o_valid		(data_o_valid),
		.ram_wr_data		(ram_wr_data),
		.rx_total_length	(rx_total_length),
		.rx_state			(rx_state),
		.rx_data_length		(rx_data_length),
		.ram_wr_addr		(ram_wr_addr),
		.data_receive		(data_receive),
		.ram_rd_data		(ram_rd_data),
		
		// debug use
		.tx_state			(tx_state),

		.tx_data_length		(tx_data_length),
		.tx_total_length	(tx_total_length),
		.ram_rd_addr		(ram_rd_addr)
	);
	
	ram ram_inst(
		
		.clka		(e_rxc),			// input  wrclock_sig
		.wea		(wea),				// input  wren_sig
		.addra		(addra),			// input [8:0] wraddress_sig
		.dina		(dina),				// input [31:0] data_sig
		
		.clkb		(e_rxc),			// input  rdclock_sig
		.addrb		(ram_rd_addr),		// input [8:0] rdaddress_sig
		.doutb		(ram_rd_data) 		// output [31:0] q_sig
	);
// UDP数据初始化
always@(*)begin
    // 将一些固定的字符串赋值到UDP数据缓存数组中
    udp_data[0] <= {"K","I","N","T"}; // 字符串"KINT"
    udp_data[1] <= {"E","X","7"," "}; // 字符串"EX7 "
    udp_data[2] <= {"U","D","P"," "}; // 字符串"UDP "
    udp_data[3] <= {"T","e","s","t"}; // 字符串"Test"
    udp_data[4] <= {" ","!","\r","\n"};// 字符串" !\r\n"
end
// 在每个以太网GMII接收时钟上升沿进行的操作
always@(posedge e_rxc)begin
    // 如果以太网重置信号有效,则初始化写RAM的控制信号
    if(gmii_ctrl_nrst == 1'b0)begin
        ram_wr_finish <= 1'b0; // 写完成标志复位
        ram_addr_i <= 0;       // 写地址归零
        ram_data_i <= 0;       // 写数据归零
        i <= 0;                // 计数器归零
    end else begin
        // 如果全部数据块已经写入完成,则标记写完成
        if(i == 5)begin
            ram_wr_finish <= 1'b1; // 写完成标志置1
            ram_wren_i <= 1'b0;    // 禁止写入
        end else begin
            // 否则继续写入数据到RAM
            ram_wren_i <= 1'b1;
            ram_addr_i <= ram_addr_i+1'b1; // 写地址递增
            ram_data_i <= udp_data[i];     // 从UDP数据数组中取值写入
            i <= i+1'b1;
        end
    end
end

endmodule

UDP模块

// 定义UDP模块
module udp(
    // 输入,异步复位信号,低电平有效
    input wire reset_n,
    // 输入,全局时钟信号
    input wire g_clk,
    
    // 输入,以太网接收时钟信号
    input wire e_rxc,
    // 输入,以太网接收的数据(8位宽)
    input wire [7 : 0] e_rxd,
    // 输入,标识以太网接收数据有效
    input wire e_rxdv,
    // 输出,以太网发送使能信号
    output wire e_txen,
    // 输出,以太网发送数据(8位宽)
    output wire [7 : 0] e_txd,
    // 输出,以太网发送错误信号
    output wire e_txer,
    
    // 输出,数据输出有效信号
    output wire data_o_valid,
    // 输出,写入RAM的数据(32位宽)
    output wire [31 : 0] ram_wr_data,
    // 输出,接收到的总长度(16位宽)
    output wire [15 : 0] rx_total_length,
    
    // 输出,接收状态(4位宽)
    output wire [3 : 0] rx_state,
    // 输出,接收数据长度(16位宽)
    output wire [15 : 0] rx_data_length,
    // 输出,写入RAM的地址(9位宽)
    output wire [8 : 0] ram_wr_addr,
    
    // 输入,读取RAM的数据(32位宽)
    input wire [31 : 0] ram_rd_data,
    // 输出,发送状态(4位宽)
    output wire [3 : 0] tx_state,
    
    // 输入,要发送的数据长度(16位宽)
    input wire [15 : 0] tx_data_length,
    // 输入,要发送的总长度(16位宽)
    input wire [15 : 0] tx_total_length,
    // 输出,读取RAM的地址(9位宽)
    output wire [8 : 0] ram_rd_addr,
    // 输出,表示接收到数据的信号
    output wire data_receive
);

    // 内部连线,用于CRC校验模块,提供下一个CRC值
    wire [31 : 0] crcnext;
    // 内部连线,当前CRC值
    wire [31 : 0] crc32;
    // 内部连线,CRC复位信号
    wire crcreset;
    // 内部连线,CRC使能信号
    wire crcen;
    
    // 发送模块实例化
    ipsend ipsend_inst(
        // 输入,全局时钟信号
        .clk                (g_clk),
        // 输出,以太网发送使能信号
        .txen               (e_txen),
        // 输出,以太网发送错误信号
        .txer               (e_txer),
        // 输出,以太网发送数据
        .dataout            (e_txd),
        // 输入,当前CRC值
        .crc                (crc32),
        // 输入,从RAM读取的数据
        .datain             (ram_rd_data),
        // 输入,CRC使能信号
        .crcen              (crcen),
        // 输入,CRC复位信号
        .crcre              (crcreset),
        // 输出,发送状态
        .tx_state           (tx_state),
        // 输入,要发送的数据长度
        .tx_data_length     (tx_data_length),
        // 输入,要发送的总长度
        .tx_total_length    (tx_total_length),
        // 输出,读取RAM的地址
        .ram_rd_addr        (ram_rd_addr)
    );
    
    // CRC校验模块实例化
    crc crc_inst(
        // 输入,全局时钟信号
        .Clk        (g_clk),
        // 输入,CRC复位信号
        .Reset      (crcreset),
        // 输入,CRC使能信号
        .Enable     (crcen),
        // 输入,接收到的以太网数据
        .Data_in    (e_txd),
        // 输出,当前CRC值
        .Crc        (crc32),
        // 输出,下一个CRC值
        .CrcNext    (crcnext)
    );
    
    // 接收模块实例化
    iprecieve iprecieve_inst(
        // 输入,以太网接收时钟信号
        .clk                    (e_rxc),
        // 输入,以太网接收的数据
        .datain                 (e_rxd),
        // 输入,标识以太网接收数据有效
        .e_rxdv                 (e_rxdv),
        // 输入,异步复位信号,低电平有效
        .clr                    (reset_n),
        // 以下端口未接线,一般是配置参数,如MAC地址、协议类型等
        .board_mac              (),
        .pc_mac                 (),
        .IP_Prtcl               (),
        .IP_layer               (),
        .pc_IP                  (),
        .board_IP               (),
        .UDP_layer              (),
        // 输出,向RAM写入的数据
        .data_o                 (ram_wr_data),
        // 输出,标识有效的IP协议
        .valid_ip_P             (),
        // 输出,接收到的总长度
        .rx_total_length        (rx_total_length),
        // 输出,数据输出有效信号
        .data_o_valid           (data_o_valid),
        // 输出,接收状态
        .rx_state               (rx_state),
        // 输出,接收数据长度
        .rx_data_length         (rx_data_length),
        // 输出,写入RAM的地址
        .ram_wr_addr            (ram_wr_addr),
        // 输出,表示接收到数据的信号
        .data_receive           (data_receive)
    );
    
endmodule // 结束UDP模块定义

数据接收模块

module iprecieve(
    
    input						clk,
    input			[7 : 0]		datain,          // 输入数据
    input						e_rxdv,          // 数据有效信号
    input						clr,             // 清除信号
    
    output reg		[47 : 0]	board_mac,        // 板卡MAC地址
    output reg		[47 : 0]	pc_mac,           // PC MAC地址
    output reg		[15 : 0]	IP_Prtcl,         // IP协议类型
    output reg					valid_ip_P,       // IP协议是否有效
    output reg		[159 : 0]	IP_layer,         // IP层数据
    output reg		[31 : 0]	pc_IP,            // PC IP地址
    output reg		[31 : 0]	board_IP,         // 板卡IP地址
    output reg		[63 : 0]	UDP_layer,        // UDP层数据

    output reg		[31 : 0]	data_o,           // 输出数据

    output reg		[15 : 0]	rx_total_length,  // 接收数据总长度
    output reg					data_o_valid,     // 输出数据是否有效
    output reg		[3 : 0]		rx_state,         // 接收状态
    output reg		[15 : 0]	rx_data_length,   // 接收数据长度
    output reg		[8 : 0]		ram_wr_addr,      // RAM写地址
    output reg					data_receive      // 是否接收到数据
);
    
    reg		[15 : 0]	myIP_Prtcl;                // 存储IP协议类型
    reg		[159 : 0]	myIP_layer;                // 存储IP层数据
    reg		[63 : 0]	myUDP_layer;               // 存储UDP层数据
    reg		[31 : 0]	mydata;                    // 存储数据
    reg		[2 : 0]		byte_counter;              // 字节计数器
    reg		[4 : 0]		state_counter;             // 状态计数器
    reg		[95 : 0]	mymac;                      // 存储MAC地址
    reg		[15 : 0]	data_counter;              // 数据计数器
    
    localparam idle				= 4'd0;              // 空闲状态
    localparam six_55			= 4'd1;              // 接收6个55状态
    localparam spd_d5			= 4'd2;              // 接收1个d5状态
    localparam rx_mac			= 4'd3;              // 接收MAC地址状态
    localparam rx_IP_Protocol	= 4'd4;              // 接收IP协议类型状态
    localparam rx_IP_layer		= 4'd5;              // 接收IP层数据状态
    localparam rx_UDP_layer		= 4'd6;              // 接收UDP层数据状态
    localparam rx_data			= 4'd7;              // 接收数据状态
    localparam rx_finish		= 4'd8;              // 接收完成状态
    
    // 初始化
    initial begin
        rx_state <= idle;
    end
    
    // UDP数据接收程序
    always@(posedge clk)begin
        
        if(!clr)begin
            rx_state <= idle;
            data_receive <= 1'b0;
        end else begin
            
            case(rx_state)
                
                idle: begin
                    valid_ip_P <= 1'b0;
                    byte_counter <= 3'd0;
                    data_counter <= 10'd0;
                    mydata <= 32'd0;
                    state_counter <= 5'd0;
                    data_o_valid <= 1'b0;
                    ram_wr_addr <= 0;
                    
                    // 接收数据信号高电平,开始接收数据
                    if(e_rxdv == 1'b1)begin
                        // 接收首个55
                        if(datain[7:0] == 8'h55)begin
                            rx_state <= six_55;
                            mydata <= {mydata[23:0],datain[7:0]};
                        end else
                            rx_state <= idle;
                    end
                end
                
                // 接收6个55
                six_55: begin
                    if((datain[7:0] == 8'h55) && (e_rxdv == 1'b1))begin
                        if(state_counter == 5)begin
                            state_counter <= 0;
                            rx_state <= spd_d5;
                        end else
                            state_counter <= state_counter + 1'b1;
                    end else
                        rx_state <= idle;
                end
                
                // 接收1个d5
                spd_d5: begin
                    if((datain[7:0] == 8'hd5) && (e_rxdv == 1'b1)) 
                        rx_state <= rx_mac;
                    else 
                        rx_state <= idle;
                end
                
                // 接收目标MAC地址和源MAC地址
                rx_mac: begin
                    
                    if(e_rxdv == 1'b1)begin
                        if(state_counter<5'd11)begin
                            mymac <= {mymac[87:0],datain};
                            state_counter <= state_counter + 1'b1;
                        end else begin
                            board_mac <= mymac[87:40];
                            pc_mac <= {mymac[39:0],datain};
                            state_counter <= 5'd0;
                            
                            // 源MAC地址 01-60-6E-11-02-0F 写死的,为了减少FPGA的面积
                            if((mymac[87:72] == 16'h0160) && (mymac[71:56] == 16'h6E11) && (mymac[55:40] == 16'h020F))
                                rx_state <= rx_IP_Protocol;
                            else
                                rx_state <= idle;
                        end
                        
                    end else
                        rx_state <= idle;
                end
                
                // 接收2字节的IP协议类型
                rx_IP_Protocol: begin
                    
                    if(e_rxdv == 1'b1)begin
                        if(state_counter<5'd1)begin
                            myIP_Prtcl <= {myIP_Prtcl[7:0],datain[7:0]};
                            state_counter <= state_counter + 1'b1;
                        end else begin
                            IP_Prtcl <= {myIP_Prtcl[7:0],datain[7:0]};
                            valid_ip_P <= 1'b1;
                            state_counter <= 5'd0;
                            rx_state <= rx_IP_layer;
                        end
                    end else
                        rx_state <= idle;
                end
                
                // 接收20字节的UDP, IP地址
                rx_IP_layer: begin
                    
                    valid_ip_P <= 1'b0;
                    
                    if(e_rxdv == 1'b1)begin
                        if(state_counter<5'd19)begin
                            myIP_layer <= {myIP_layer[151:0],datain[7:0]};
                            state_counter <= state_counter + 1'b1;
                        end else begin
                            IP_layer <= {myIP_layer[151:0],datain[7:0]};
                            state_counter <= 5'd0;
                            rx_state <= rx_UDP_layer;
                        end
                        
                    end else 
                        rx_state <= idle;
                end
                
                // 接收8字节的UDP端口和长度
                rx_UDP_layer: begin
                    
                    rx_total_length <= IP_layer[143:128];
                    pc_IP <= IP_layer[63:32];
                    board_IP <= IP_layer[31:0];
                    
                    if(e_rxdv == 1'b1)begin
                        
                        if(state_counter<5'd7)begin
                            myUDP_layer <= {myUDP_layer[55:0],datain[7:0]};
                            state_counter <= state_counter + 1'b1;
                        end else begin
                            UDP_layer <= {myUDP_layer[55:0],datain[7:0]};
                            // UDP长度
                            rx_data_length <=  myUDP_layer[23:8];
                            state_counter <= 5'd0;
                            rx_state <= rx_data;
                        end
                    end else 
                        rx_state <= idle;
                end
                
                // 接收UDP数据
                rx_data: begin
                    
                    if(e_rxdv == 1'b1)begin
                        // 保存最后一个数据,UDP数据总长度 = -8字节的UDP头
                        if(data_counter == rx_data_length-9)begin
                            
                            data_counter <= 0;
                            rx_state <= rx_finish;
                            ram_wr_addr <= ram_wr_addr + 1'b1;
                            // 写入RAM
                            data_o_valid <= 1'b1;
                            
                            if(byte_counter == 3'd3)begin
                                data_o <= {mydata[23:0],datain[7:0]};
                                byte_counter <= 0;
                            // 不足32位时填充0
                            end else if(byte_counter == 3'd2)begin
                                data_o <= {mydata[15:0],datain[7:0],8'h00};
                                byte_counter <= 0;
                            // 不足32位时填充0
                            end else if(byte_counter == 3'd1)begin
                                data_o <= {mydata[7:0],datain[7:0],16'h0000};
                                byte_counter <= 0;
                            // 不足32位时填充0
                            end else if(byte_counter == 3'd0)begin
                                data_o <= {datain[7:0],24'h000000};
                                byte_counter <= 0;
                            end
                            
                        end else begin
                            
                            data_counter <= data_counter + 1'b1;
                            
                            if(byte_counter<3'd3)begin
                                mydata <= {mydata[23:0],datain[7:0]};
                                byte_counter <= byte_counter + 1'b1;
                                data_o_valid <= 1'b0;
                            end else begin
                                data_o <= {mydata[23:0],datain[7:0]};
                                byte_counter <= 3'd0;
                                data_o_valid <= 1'b1;
                                ram_wr_addr <= ram_wr_addr + 1'b1;
                            end	
                        end
                    end else
                        rx_state <= idle;
                end
                
                rx_finish: begin
                    data_o_valid <= 1'b0;
                    data_receive <= 1'b1;
                    rx_state <= idle;
                end
                
                default: rx_state <= idle;
                
            endcase
        end
    end
    
endmodule

数据发送模块

timescale 1ns / 1ps   // 设置时钟精度为1ns/1ps

module ipsend(
    
    input						clk,						// 输入时钟信号
    output reg					txen,						// 输出发送使能信号
    output reg					txer,						// 输出发送错误信号
    output reg		[7 : 0]		dataout,					// 输出数据位,8位宽
    input			[31 : 0]	crc,						// 输入CRC数据,32位宽
    input			[31 : 0]	datain,						// 输入数据,32位宽
    output reg					crcen,						// 输出CRC使能信号
    output reg					crcre,						// 输出CRC使能复位信号

    output reg		[3 : 0]		tx_state,					// 输出发送状态,4位宽
    input			[15 : 0]	tx_data_length,				// 输入数据长度,16位宽
    input			[15 : 0]	tx_total_length,			// 输入总长度,16位宽
    output reg		[8 : 0]		ram_rd_addr					// 输出RAM读地址,9位宽
);

    reg		[31 : 0]	datain_reg;							// 数据暂存寄存器
    reg		[31 : 0]	ip_header		[6 : 0];				// IP头信息
    reg		[7 : 0]		preamble		[7 : 0];				// 帧前导码
    reg		[7 : 0]		mac_addr		[13 : 0];				// MAC地址
    reg		[4 : 0]		i, j;								// 计数器

    reg		[31 : 0]	check_buffer;						// 校验缓冲器
    reg		[31 : 0]	time_counter;						// 时间计数器
    reg		[15 : 0]	tx_data_counter;					// 发送数据计数器
    
    localparam idle			= 4'b0000;						// 空闲状态
    localparam start		= 4'b0001;						// 启动状态
    localparam make			= 4'b0010;						// 生成状态
    localparam send55		= 4'b0011;						// 发送55状态
    localparam sendmac		= 4'b0100;						// 发送MAC地址状态
    localparam sendheader	= 4'b0101;						// 发送IP头状态
    localparam senddata		= 4'b0110;						// 发送数据状态
    localparam sendcrc		= 4'b0111;						// 发送CRC状态
    
    initial begin
        tx_state <= idle;									// 初始状态为空闲
        
        // 设置帧前导码 7 x 55, 1 x d5
        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地址 ff-ff-ff-ff-ff-ff
        mac_addr[0] <= 8'hFF;
        mac_addr[1] <= 8'hFF;
        mac_addr[2] <= 8'hFF;
        mac_addr[3] <= 8'hFF;
        mac_addr[4] <= 8'hFF;
        mac_addr[5] <= 8'hFF;
        
        // 源MAC地址 01-60-6E-11-02-0F
        mac_addr[6] <= 8'h01;
        mac_addr[7] <= 8'h60;
        mac_addr[8] <= 8'h6E;
        mac_addr[9] <= 8'h11;
        mac_addr[10] <= 8'h02;
        mac_addr[11] <= 8'h0F;
        
        // 0800: IP类型
        mac_addr[12] <= 8'h08;
        mac_addr[13] <= 8'h00;
        
        i <= 0;
    end
    
    reg unsigned [16 : 0] temp_a;							// 临时变量a
    reg unsigned [16 : 0] temp_b;							// 临时变量b
    reg unsigned [16 : 0] temp_c;							// 临时变量c
    reg unsigned [16 : 0] temp_d;							// 临时变量d
    reg unsigned [16 : 0] temp_e;							// 临时变量e
    reg unsigned [17 : 0] temp_f;							// 临时变量f
    reg unsigned [17 : 0] temp_g;							// 临时变量g
    reg unsigned [18 : 0] temp_h;							// 临时变量h
    
    always @(negedge clk) begin
        case(tx_state)
            idle: begin
                txer <= 1'b0;								// 发送错误信号复位
                txen <= 1'b0;								// 发送使能信号复位
                crcen <= 1'b0;								// CRC使能信号复位
                crcre <= 1;									// CRC使能复位信号设为1
                j <= 0;										// 计数器j复位
                dataout <= 8'h00;							// 数据输出信号复位
                ram_rd_addr <= 9'h01;						// RAM读地址复位
                tx_data_counter <= 0;						// 发送数据计数器复位
                
                // 经过一段时间后再发送
                if(time_counter == 32'h04000000) begin
                    tx_state <= start;						// 状态转换到start
                    time_counter <= 0;						// 时间计数器复位
                end else
                    time_counter <= time_counter + 1'b1;		// 时间计数器递增
            end
            
            // IP头
            start: begin
                ip_header[0] <= {16'h4500,tx_total_length};			// 版本号4,头长度20,IP
                ip_header[1][31:16] <= ip_header[1][31:16] + 1'b1;	// 顺序号递增
                ip_header[1][15:0] <= 16'h4000;						// 分片偏移
                ip_header[2] <= 32'h80110000;						// 协议字段为17(UDP)
                ip_header[3] <= {8'd192, 8'd168, 8'd1, 8'd183};		// 源IP地址,192.168.1.xx
                ip_header[4] <= {8'd192, 8'd168, 8'd1, 8'd37};		// 目标IP地址,192.168.1.xx
                ip_header[5] <= {16'd8080, 16'd8080};					// 端口号
                ip_header[6] <= {tx_data_length,16'h0000};			// 数据长度
                tx_state <= make;									// 状态转换到make
            end
            
            // 计算校验和
            make: begin
                if(i == 0) begin
                    temp_a <= ip_header[0][15:0] + ip_header[0][31:16];
                    temp_b <= ip_header[1][15:0] + ip_header[1][31:16];
                    temp_c <= ip_header[2][15:0] + ip_header[2][31:16];
                    temp_d <= ip_header[3][15:0] + ip_header[3][31:16];
                    temp_e <= ip_header[4][15:0] + ip_header[4][31:16];
                    i <= i + 1'b1;
                    
                end else if(i == 1) begin
                    temp_f <= temp_a + temp_b;
                    temp_g <= temp_c + temp_d;
                    i <= i + 1'b1;
                    
                end else if(i == 2) begin
                    temp_h <= temp_f + temp_g;
                    i <= i + 1'b1;
                    
                end else if(i == 3) begin
                    check_buffer <= temp_h + temp_e;
                    i <= i + 1'b1;
                    
                end else if(i == 4) begin
                    check_buffer[15:0] <= check_buffer[31:16] + check_buffer[15:0];
                    i <= i + 1'b1;
                end else begin
                    // 校验和
                    ip_header[2][15:0] <= ~check_buffer[15:0];
                    i <= 0;
                    tx_state <= send55;
                end
            end
            
            // 发送7个55和1个d5
            send55: begin
                txen <= 1'b1;		// GMII发送数据有效
                crcre <= 1'b1;		// 复位CRC
                
                if(i == 7) begin
                    dataout[7:0] <= preamble[i][7:0];
                    i <= 0;
                    tx_state <= sendmac;
                end else begin
                    dataout[7:0] <= preamble[i][7:0];
                    i <= i + 1;
                end
            end
            
            // 发送目标MAC地址、源MAC地址和IP类型
            sendmac: begin
                // 启用CRC,计算32位的CRC
                crcen <= 1'b1;
                crcre <= 1'b0;
                
                if(i == 13) begin
                    dataout[7:0] <= mac_addr[i][7:0];
                    i <= 0;
                    tx_state <= sendheader;
                end else begin                        
                    dataout[7:0] <= mac_addr[i][7:0];
                    i <= i + 1'b1;
                end
            end
            
            // 发送7个32位的IP头
            sendheader: begin
                // 发送数据
                datain_reg <= datain;
                
                if(j == 6) begin
                    
                    if(i == 0) begin
                        dataout[7:0] <= ip_header[j][31:24];
                        i <= i + 1'b1;
                    end else if(i == 1) begin
                        dataout[7:0] <= ip_header[j][23:16];
                        i <= i + 1'b1;
                    end else if(i == 2) begin
                        dataout[7:0] <= ip_header[j][15:8];
                        i <= i + 1'b1;
                    end else if(i == 3) begin
                        dataout[7:0] <= ip_header[j][7:0];
                        i <= 0;
                        j <= 0;
                        tx_state <= senddata;
                    end else
                        txer <= 1'b1;
                    
                end else begin
                    
                    if(i == 0) begin
                        dataout[7:0] <= ip_header[j][31:24];
                        i <= i + 1'b1;
                    end else if(i == 1) begin
                        dataout[7:0] <= ip_header[j][23:16];
                        i <= i + 1'b1;
                    end else if(i == 2) begin
                        dataout[7:0] <= ip_header[j][15:8];
                        i <= i + 1'b1;
                    end else if(i == 3) begin
                        dataout[7:0] <= ip_header[j][7:0];
                        i <= 0;
                        j <= j + 1'b1;
                    end else
                        txer <= 1'b1;
                end
            end
            
            // 发送UDP数据
            senddata: begin
                // 发送最后一个数据
                if(tx_data_counter == tx_data_length-9) begin
                    tx_state <= sendcrc;
                    
                    if(i == 0) begin    
                        dataout[7:0] <= datain_reg[31:24];
                        i <= 0;
                    end else if(i == 1) begin
                        dataout[7:0] <= datain_reg[23:16];
                        i <= 0;
                    end else if(i == 2) begin
                        dataout[7:0] <= datain_reg[15:8];
                        i <= 0;
                    end else if(i == 3) begin
                        dataout[7:0] <= datain_reg[7:0];
                        // 准备新的数据
                        datain_reg <= datain;
                        i <= 0;
                    end
                    
                // 发送其他数据
                end else begin
                    tx_data_counter <= tx_data_counter + 1'b1;			
                    
                    if(i == 0) begin    
                        dataout[7:0] <= datain_reg[31:24];
                        i <= i + 1'b1;
                        // RAM + 1,RAM读数据
                        ram_rd_addr <= ram_rd_addr + 1'b1;
                    end else if(i == 1) begin
                        dataout[7:0] <= datain_reg[23:16];
                        i <= i + 1'b1;
                    end else if(i == 2) begin
                        dataout[7:0] <= datain_reg[15:8];
                        i <= i + 1'b1;
                    end else if(i == 3) begin
                        dataout[7:0] <= datain_reg[7:0];
                        // 准备新的数据
                        datain_reg <= datain;
                        i <= 0;
                    end
                end
            end
            
            // 发送32位的CRC
            sendcrc: begin
                crcen <= 1'b0;
                
                if(i == 0) begin
                    dataout[7:0] <= {~crc[24], ~crc[25], ~crc[26], ~crc[27], ~crc[28], ~crc[29], ~crc[30], ~crc[31]};
                    i <= i + 1'b1;
                end else begin
                    if(i == 1) begin
                        dataout[7:0] <= {~crc[16], ~crc[17], ~crc[18], ~crc[19], ~crc[20], ~crc[21], ~crc[22], ~crc[23]};
                        i <= i + 1'b1;
                    end else if(i == 2) begin
                        dataout[7:0] <= {~crc[8], ~crc[9], ~crc[10], ~crc[11], ~crc[12], ~crc[13], ~crc[14], ~crc[15]};
                        i <= i + 1'b1;
                    end else if(i == 3) begin
                        dataout[7:0] <= {~crc[0], ~crc[1], ~crc[2], ~crc[3], ~crc[4], ~crc[5], ~crc[6], ~crc[7]};
                        i <= 0;
                        tx_state <= idle;
                    end else begin
                        txer <= 1'b1;
                    end
                end
            end
            
            default:tx_state <= idle;
        endcase
    end
    
endmodule

CRC校验模块(没啥好说的,就是CRC)

timescale 1ns / 1ps

// CRC Module Definition
module crc(
    
    input					Clk,    // Clock input
    input					Reset,  // Reset input
    input		[7 : 0]		Data_in,    // Data input
    input					Enable, // Enable input
    output reg	[31 : 0]	Crc,    // CRC output
    output		[31 : 0]	CrcNext // Next CRC output
);
    
    wire [7 : 0] Data;
    
    assign Data = {Data_in[0],Data_in[1],Data_in[2],Data_in[3],Data_in[4],Data_in[5],Data_in[6],Data_in[7]};
    
    // CRC Calculation
    assign CrcNext[0]  = Crc[24] ^ Crc[30] ^ Data[0] ^ Data[6];
    assign CrcNext[1]  = Crc[24] ^ Crc[25] ^ Crc[30] ^ Crc[31] ^ Data[0] ^ Data[1] ^ Data[6] ^ Data[7];
    assign CrcNext[2]  = Crc[24] ^ Crc[25] ^ Crc[26] ^ Crc[30] ^ Crc[31] ^ Data[0] ^ Data[1] ^ Data[2] ^ Data[6] ^ Data[7];
    assign CrcNext[3]  = Crc[25] ^ Crc[26] ^ Crc[27] ^ Crc[31] ^ Data[1] ^ Data[2] ^ Data[3] ^ Data[7];
    assign CrcNext[4]  = Crc[24] ^ Crc[26] ^ Crc[27] ^ Crc[28] ^ Crc[30] ^ Data[0] ^ Data[2] ^ Data[3] ^ Data[4] ^ Data[6];
    assign CrcNext[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 CrcNext[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 CrcNext[7]  = Crc[24] ^ Crc[26] ^ Crc[27] ^ Crc[29] ^ Crc[31] ^ Data[0] ^ Data[2] ^ Data[3] ^ Data[5] ^ Data[7];
    assign CrcNext[8]  = Crc[0] ^ Crc[24] ^ Crc[25] ^ Crc[27] ^ Crc[28] ^ Data[0] ^ Data[1] ^ Data[3] ^ Data[4];
    assign CrcNext[9]  = Crc[1] ^ Crc[25] ^ Crc[26] ^ Crc[28] ^ Crc[29] ^ Data[1] ^ Data[2] ^ Data[4] ^ Data[5];
    assign CrcNext[10] = Crc[2] ^ Crc[24] ^ Crc[26] ^ Crc[27] ^ Crc[29] ^ Data[0] ^ Data[2] ^ Data[3] ^ Data[5];
    assign CrcNext[11] = Crc[3] ^ Crc[24] ^ Crc[25] ^ Crc[27] ^ Crc[28] ^ Data[0] ^ Data[1] ^ Data[3] ^ Data[4];
    assign CrcNext[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 CrcNext[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 CrcNext[14] = Crc[6] ^ Crc[26] ^ Crc[27] ^ Crc[28] ^ Crc[30] ^ Crc[31] ^ Data[2] ^ Data[3] ^ Data[4] ^ Data[6] ^ Data[7];
    assign CrcNext[15] = Crc[7] ^ Crc[27] ^ Crc[28] ^ Crc[29] ^ Crc[31] ^ Data[3] ^ Data[4] ^ Data[5] ^ Data[7];
    assign CrcNext[16] = Crc[8] ^ Crc[24] ^ Crc[28] ^ Crc[29] ^ Data[0] ^ Data[4] ^ Data[5];
    assign CrcNext[17] = Crc[9] ^ Crc[25] ^ Crc[29] ^ Crc[30] ^ Data[1] ^ Data[5] ^ Data[6];
    assign CrcNext[18] = Crc[10] ^ Crc[26] ^ Crc[30] ^ Crc[31] ^ Data[2] ^ Data[6] ^ Data[7];
    assign CrcNext[19] = Crc[11] ^ Crc[27] ^ Crc[31] ^ Data[3] ^ Data[7];
    assign CrcNext[20] = Crc[12] ^ Crc[28] ^ Data[4];
    assign CrcNext[21] = Crc[13] ^ Crc[29] ^ Data[5];
    assign CrcNext[22] = Crc[14] ^ Crc[24] ^ Data[0];
    assign CrcNext[23] = Crc[15] ^ Crc[24] ^ Crc[25] ^ Crc[30] ^ Data[0] ^ Data[1] ^ Data[6];
    assign CrcNext[24] = Crc[16] ^ Crc[25] ^ Crc[26] ^ Crc[31] ^ Data[1] ^ Data[2] ^ Data[7];
    assign CrcNext[25] = Crc[17] ^ Crc[26] ^ Crc[27] ^ Data[2] ^ Data[3];
    assign CrcNext[26] = Crc[18] ^ Crc[24] ^ Crc[27] ^ Crc[28] ^ Crc[30] ^ Data[0] ^ Data[3] ^ Data[4] ^ Data[6];
    assign CrcNext[27] = Crc[19] ^ Crc[25] ^ Crc[28] ^ Crc[29] ^ Crc[31] ^ Data[1] ^ Data[4] ^ Data[5] ^ Data[7];
    assign CrcNext[28] = Crc[20] ^ Crc[26] ^ Crc[29] ^ Crc[30] ^ Data[2] ^ Data[5] ^ Data[6];
    assign CrcNext[29] = Crc[21] ^ Crc[27] ^ Crc[30] ^ Crc[31] ^ Data[3] ^ Data[6] ^ Data[7];
    assign CrcNext[30] = Crc[22] ^ Crc[28] ^ Crc[31] ^ Data[4] ^ Data[7];
    assign CrcNext[31] = Crc[23] ^ Crc[29] ^ Data[5];
    
    // Sequential Logic
    always@(posedge Clk or posedge Reset)begin
        
        if (Reset)begin
            Crc <= {32{1'b1}};   // Reset CRC to all ones
        end else if (Enable)begin
            Crc <= CrcNext;  // Update CRC with next CRC value
        end
    end
    
endmodule

资源占用情况

image.png

扫描二维码推送至手机访问。

版权声明:本文由我的FPGA发布,如需转载请注明出处。

本文链接:http://myfpga.cn/index.php/post/418.html

分享给朋友:

“(归纳整理)一、Kintex7实现以太网通信(UDP通信)” 的相关文章

基础实验十三,DS18B20温度传感器

基础实验十三,DS18B20温度传感器

//==========================================================================// Author     : ChanRa1n// Description: Training for Intel FPGA/...

Verilog实现时钟分频(奇数分频,偶数分频)二分频 三分频 四分频 五分频

Verilog实现时钟分频(奇数分频,偶数分频)二分频 三分频 四分频 五分频

完整工程文件:clkdiv.zip//------------------------------------------------------// File Name        : clkdiv.v// Author     &nb...

Xilinx FIFO和ILA学习

Xilinx FIFO和ILA学习

`timescale 1ns / 1ps//-------------------------------------------------------//Filename       ﹕ FIFO_TOP.v//Author      ...

Verilog实现串并转换

Verilog实现串并转换

项目文件:SIPO.zip//------------------------------------------------------// File Name        : SIPO.v// Author       &n...

点亮LED灯实验

点亮LED灯实验

设计流程:设计规划 -> 波形绘制 -> 代码编写 -> 代码编译 -> 逻辑仿真 -> 波形对比 -> 绑定管脚 -> 分析综合布局布线 -> 上板验证新建项目文件夹(led):Doc:放置文档资料(数据手册、波形图、文档、项目日志)Pri:放置工程...