(归纳整理)一、Kintex7实现以太网通信(UDP通信)
一、代码结构
二、代码解析
顶层模块(目前代码实现是回环)
// 模块主要功能是实现以太网通信的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