您的当前位置:首页正文

FPGA——以太网MAC层数据发送协议实现及验证

2022-07-23 来源:好走旅游网
FPGA——以太⽹MAC层数据发送协议实现及验证

⼀、设计思路

FPGA实现MAC层(数据链路层)的功能并连接到RTL8211物理层(PHY)芯⽚实现以太⽹数据的发送 使⽤GMII接⼝ 时钟是125MHz,⼀次发8bit数据 8bit * 125M = 1000Mbit 所以叫做千兆以太⽹RTL8211时序 来⼀个时钟上升沿就发⼀个字节的数据数据链路层(MAC帧协议)发送过程

前同步码 0x55,发七次帧开始符 0xD5

⽬的MAC地址(6字节)源MAC地址(6字节)

类型:0x800 使⽤IP上层协议,代表下⾯发送的数据是IP数据报数据

IP报⽂⾸部

IP版本 0x4 IPv4⾸部长度服务类型总长度分段标识保留位DFMF段偏移

⽣存周期TTL

上层协议 0x11 UDP报⽂校验和源IP地址⽬的IP地址

可选字段(0字节不需要)IP报⽂数据(UDP报⽂)

UDP报⽂⾸部

16位源端⼝号16位⽬的端⼝号16位UDP长度

16位UDP校验和(可以直接置0)UDP报⽂数据CRC校验

IP报⽂校验和 将IP报⽂⾸部,从前到后拼成⼀个⼀个的2字节的数据相加(其中报⽂校验和置0) 然后将所加的32bit数据的⾼16位于低16位相加,直到⾼16bit为0 将16bit取反,则为校验和

虽然⽣成了函数,但是具体使⽤函数还是要做更改的 注意:CRC校验是从发送⽬的MAC地址开始校验的

IP⾸部长度 单位是/32bit(4字节) 如果没有可选⾃动,⾸部长度就是 5(⾏)IP总长度 总长度 = IP⾸部长度 + IP报⽂数据长度 单位是/字节

MAC帧协议的数据和填充 数据和填充那个位置,⾄少要46个字节,如果要发送的UDP报⽂数据加起来没有46字节的话,就要在后⾯填0x00直到有46个字节为⽌ IP报⽂头部(20字节)+ UDP报⽂(8字节)+ 发送的数据 >= 46 发送的数据 >= 18字节FPGA状态机实现 每⼀个状态都配套⼀个计数器

⼆、以太⽹GMIII发送代码实现

module eth_udp_tx_gmii( clk , rst_n , tx_en , tx_done , dst_mac , src_mac , dst_ip , src_ip , dst_port ,

src_port , gmii_clk , data_len , data_vld , gmii_en , gmii_tx , data_in );

localparam MAC_W = 48;localparam IP_W = 32;localparam PORT_W = 16;localparam DATA_LEN_W = 16;localparam GMII_W = 8;

localparam PREAMBLE = 8'h55; //前导码localparam SFD = 8'hD5; //帧开始符

localparam ETH_TYPE = 16'h0800;//上层协议类型,IP协议localparam IP_VER = 4'h4; //IPV4

localparam IP_HDR_LEN = 4'h5; //⾸部长度有5个32bitlocalparam IP_TOS = 8'h00; //服务类型,未⽤localparam IP_HED_LEN = 20; //IP头部长度localparam UDP_HED_LEN = 8; //UDP头部长度localparam IP_ID = 16'h0000;//分段标识localparam IP_RSV = 1'b0; //保留localparam IP_DF = 1'b0; //是否分段

localparam IP_MF = 1'b0; //是否有后续分段

localparam IP_FRAG_OFFSET = 13'h0; //段偏移(以8字节为单位)localparam IP_TTL = 8'h40; //⽣存周期,能经过40个路由器localparam IP_PROTOCOL = 8'h11; //上层协议(UDP)localparam MIN_DATA = 46; //UDP数据最⼩长度localparam DATA_W = 8;

//状态机参数

localparam IDLE = 7'b0000001;

localparam TX_ETH_HED = 7'b0000010;localparam TX_IP_HED = 7'b0000100;localparam TX_UDP_HED = 7'b0001000;localparam TX_UDP_DATA = 7'b0010000;localparam TX_FILL_DATA = 7'b0100000;localparam TX_CRC = 7'b1000000;localparam STATE_W = 7;//计数器参数

localparam ETH_HED_W = 5;localparam ETH_HED_N = 22;localparam IP_HED_W = 5;localparam IP_HED_N = 20;localparam UDP_HED_N = 8;localparam UDP_HED_W = 4;localparam CNT_DATA_W = 11;localparam FILL_W = 6;localparam CNT_CRC_W = 3;localparam CNT_CRC_N = 4;//模块参数

localparam CHEC_W = 16;localparam CRC_OUT_W = 32;

input clk; input rst_n;

input tx_en; //发送使能

input [MAC_W-1:0] dst_mac; //⽬的Mac地址input [MAC_W-1:0] src_mac; //源Mac地址input [IP_W-1:0] dst_ip; //⽬的ip地址input [IP_W-1:0] src_ip; //源ip地址input [PORT_W-1:0] dst_port; //⽬的端⼝input [PORT_W-1:0] src_port; //源端⼝

input [DATA_LEN_W-1:0] data_len; //发送数据长度input [DATA_W-1:0] data_in; //输⼊数据output gmii_clk; //以太⽹接⼝时钟output gmii_en; //以太⽹使能

output [GMII_W-1:0] gmii_tx; //以太⽹数据output tx_done; //发送完成

output data_vld; //数据有效标志信号wire gmii_clk; //以太⽹接⼝时钟reg gmii_en; //以太⽹使能

reg [GMII_W-1:0] gmii_tx; //以太⽹数据reg tx_done; //发送完成

reg data_vld; //数据有效标志信号

//状态机变量

reg [STATE_W-1:0] state_c;reg [STATE_W-1:0] state_n;wire idle2tx_eth_hed;

wire tx_eth_hed2tx_ip_hed;wire tx_ip_hed2tx_udp_hed;wire tx_udp_hed2tx_udp_data;wire tx_udp_data2tx_fill_data;wire tx_udp_data2tx_crc;wire tx_fill_data2tx_crc;wire tx_crc2idle;

//计数器变量

reg [ETH_HED_W-1:0] cnt_eth_hed;wire add_cnt_eht_hed;wire end_cnt_eth_hed;reg [IP_HED_W-1:0] cnt_ip_hed;wire add_cnt_ip_hed;wire end_cnt_ip_hed;reg [UDP_HED_W-1:0] cnt_udp_hed;wire add_cnt_udp_hed;wire end_cnt_udp_hed;reg [CNT_DATA_W-1:0] cnt_data;wire add_cnt_data;wire end_cnt_data;reg [FILL_W-1:0] cnt_fill;wire add_cnt_fill;wire end_cnt_fill;reg [CNT_CRC_W-1:0] cnt_crc;wire add_cnt_crc;wire end_cnt_crc;

//中间变量

reg tx_flag;

reg [MAC_W-1:0] dst_mac_tmp; //⽬的Mac地址reg [MAC_W-1:0] src_mac_tmp; //源Mac地址reg [IP_W-1:0] dst_ip_tmp; //⽬的ip地址reg [IP_W-1:0] src_ip_tmp; //源ip地址reg [PORT_W-1:0] dst_port_tmp; //⽬的端⼝reg [PORT_W-1:0] src_port_tmp; //源端⼝

reg [DATA_LEN_W-1:0] data_len_tmp; //发送数据长度reg [DATA_LEN_W-1:0] ip_total_len; //IP总长度reg [DATA_LEN_W-1:0] udp_total_len; //UDP总长度wire [16-1:0] udp_check_sum; //UDP校验和reg [DATA_W-1:0] data_out; //以太⽹数据reg tx_nocrc_flag;

//模块变量

wire [CHEC_W-1:0] check_sum; //IP报头校验和reg sum_en;reg crc_init;reg crc_en;

wire [CRC_OUT_W-1:0] crc_out;

always @(posedge clk or negedge rst_n)begin if(!rst_n)

state_c <= IDLE; else

state_c <= state_n;end

always @(*)begin case(state_c) IDLE:begin

if(idle2tx_eth_hed)

state_n = TX_ETH_HED; else

state_n = state_c; end

TX_ETH_HED:begin

if(tx_eth_hed2tx_ip_hed) state_n = TX_IP_HED; else

state_n = state_c; end

TX_IP_HED:begin

if(tx_ip_hed2tx_udp_hed) state_n = TX_UDP_HED;

else

state_n = state_c; end

TX_UDP_HED:begin

if(tx_udp_hed2tx_udp_data) state_n = TX_UDP_DATA; else

state_n = state_c; end

TX_UDP_DATA:begin

if(tx_udp_data2tx_fill_data) state_n = TX_FILL_DATA; else if(tx_udp_data2tx_crc) state_n = TX_CRC; else

state_n = state_c; end

TX_FILL_DATA:begin if(tx_fill_data2tx_crc) state_n = TX_CRC; else

state_n = state_c; end

TX_CRC:begin if(tx_crc2idle) state_n = IDLE; else

state_n = state_c; end

default:state_n = IDLE; endcaseend

assign idle2tx_eth_hed = state_c == IDLE && tx_en;

assign tx_eth_hed2tx_ip_hed = state_c == TX_ETH_HED && end_cnt_eth_hed;assign tx_ip_hed2tx_udp_hed = state_c == TX_IP_HED && end_cnt_ip_hed;

assign tx_udp_hed2tx_udp_data = state_c == TX_UDP_HED && end_cnt_udp_hed;

assign tx_udp_data2tx_fill_data = state_c == TX_UDP_DATA && end_cnt_data && (data_len_tmp > 0 && data_len_tmp < (MIN_DATA - IP_HED_LEN - UDP_HED_LEN));assign tx_udp_data2tx_crc = state_c == TX_UDP_DATA && end_cnt_data && data_len_tmp >= (MIN_DATA - IP_HED_LEN - UDP_HED_LEN);assign tx_fill_data2tx_crc = state_c == TX_FILL_DATA && end_cnt_fill;assign tx_crc2idle = state_c == TX_CRC && end_cnt_crc;always@(posedge clk or negedge rst_n)begin if(!rst_n)

data_out <= 0;

else if(state_c == TX_ETH_HED)begin case(cnt_eth_hed)

0,1,2,3,4,5,6:data_out <= PREAMBLE; 7 :data_out <= SFD;

8 :data_out <= dst_mac_tmp[47:40]; 9 :data_out <= dst_mac_tmp[39:32]; 10:data_out <= dst_mac_tmp[31:24]; 11:data_out <= dst_mac_tmp[23:16]; 12:data_out <= dst_mac_tmp[15:8]; 13:data_out <= dst_mac_tmp[7:0]; 14:data_out <= src_mac_tmp[47:40]; 15:data_out <= src_mac_tmp[39:32]; 16:data_out <= src_mac_tmp[31:24]; 17:data_out <= src_mac_tmp[23:16]; 18:data_out <= src_mac_tmp[15:8]; 19:data_out <= src_mac_tmp[7:0]; 20:data_out <= ETH_TYPE[15:8]; 21:data_out <= ETH_TYPE[7:0]; default:data_out <= 8'h00; endcase end

else if(state_c == TX_IP_HED)begin case(cnt_ip_hed)

0 :data_out <= {IP_VER,IP_HDR_LEN}; 1 :data_out <= IP_TOS;

2 :data_out <= ip_total_len[15:8]; 3 :data_out <= ip_total_len[7:0]; 4 :data_out <= IP_ID[15:8]; 5 :data_out <= IP_ID[7:0];

6 :data_out <= {IP_RSV,IP_DF,IP_MF,IP_FRAG_OFFSET[12:8]}; 7 :data_out <= IP_FRAG_OFFSET[7:0]; 8 :data_out <= IP_TTL;

9 :data_out <= IP_PROTOCOL; 10:data_out <= check_sum[15:8]; 11:data_out <= check_sum[7:0]; 12:data_out <= src_ip_tmp[31:24]; 13:data_out <= src_ip_tmp[23:16]; 14:data_out <= src_ip_tmp[15:8]; 15:data_out <= src_ip_tmp[7:0]; 16:data_out <= dst_ip_tmp[31:24];

17:data_out <= dst_ip_tmp[23:16]; 18:data_out <= dst_ip_tmp[15:8]; 19:data_out <= dst_ip_tmp[7:0]; default:data_out <= 8'h00; endcase end

else if(state_c == TX_UDP_HED)begin case(cnt_udp_hed)

0:data_out <= src_port_tmp[15:8]; 1:data_out <= src_port_tmp[7:0]; 2:data_out <= dst_port_tmp[15:8]; 3:data_out <= dst_port_tmp[7:0]; 4:data_out <= udp_total_len[15:8]; 5:data_out <= udp_total_len[7:0]; 6:data_out <= udp_check_sum[15:8]; 7:data_out <= udp_check_sum[7:0]; default:data_out <= 8'h00; endcase end

else if(state_c == TX_UDP_DATA)begin data_out <= data_in; end

else if(state_c == TX_FILL_DATA)begin data_out <= 8'h00; end

else if(state_c == TX_CRC)begin data_out <= 0; endend

//以太⽹头部计数器

always @(posedge clk or negedge rst_n)begin if(!rst_n)

cnt_eth_hed <= 0;

else if(add_cnt_eht_hed)begin if(end_cnt_eth_hed) cnt_eth_hed <= 0; else

cnt_eth_hed <= cnt_eth_hed + 1'b1; endend

assign add_cnt_eht_hed = state_c == TX_ETH_HED;

assign end_cnt_eth_hed = add_cnt_eht_hed && cnt_eth_hed == ETH_HED_N - 1;//IP头计数器

always @(posedge clk or negedge rst_n)begin if(!rst_n)

cnt_ip_hed <= 0;

else if(add_cnt_ip_hed)begin if(end_cnt_ip_hed) cnt_ip_hed <= 0; else

cnt_ip_hed <= cnt_ip_hed + 1; endend

assign add_cnt_ip_hed = state_c == TX_IP_HED;

assign end_cnt_ip_hed = add_cnt_ip_hed && cnt_ip_hed == IP_HED_N - 1;//udp头部计数器

always @(posedge clk or negedge rst_n)begin if(!rst_n)begin

cnt_udp_hed <= 0; end

else if(add_cnt_udp_hed)begin if(end_cnt_udp_hed) cnt_udp_hed <= 0; else

cnt_udp_hed <= cnt_udp_hed + 1; endend

assign add_cnt_udp_hed = state_c == TX_UDP_HED;

assign end_cnt_udp_hed = add_cnt_udp_hed && cnt_udp_hed == UDP_HED_N - 1;

//UDP数据计数器

always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_data <= 0; end

else if(add_cnt_data)begin if(end_cnt_data) cnt_data <= 0; else

cnt_data <= cnt_data + 1; end

end

assign add_cnt_data = state_c == TX_UDP_DATA;

assign end_cnt_data = add_cnt_data && cnt_data == data_len_tmp - 1;

//填充计数器

always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_fill <= 0; end

else if(add_cnt_fill)begin if(end_cnt_fill) cnt_fill <= 0; else

cnt_fill <= cnt_fill + 1; endend

assign add_cnt_fill = state_c == TX_FILL_DATA;

assign end_cnt_fill = add_cnt_fill && cnt_fill == (MIN_DATA - IP_HED_LEN - UDP_HED_LEN - data_len_tmp) - 1;//CRC计数器

always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_crc <= 0; end

else if(add_cnt_crc)begin if(end_cnt_crc) cnt_crc <= 0; else

cnt_crc <= cnt_crc + 1; endend

assign add_cnt_crc = state_c == TX_CRC && !tx_nocrc_flag;

assign end_cnt_crc = add_cnt_crc && cnt_crc == CNT_CRC_N - 1;always @(posedge clk or negedge rst_n)begin if(!rst_n)

tx_flag <= 0; else if(tx_en) tx_flag <= 1;end

//装载写⼊数据

always @(posedge clk or negedge rst_n)begin if(!rst_n)begin

dst_mac_tmp <= 0; src_mac_tmp <= 0; dst_ip_tmp <= 0; src_ip_tmp <= 0; dst_port_tmp <= 0; src_port_tmp <= 0; data_len_tmp <= 0; end

else if(tx_en && (!tx_flag))begin dst_mac_tmp <= dst_mac; src_mac_tmp <= src_mac; dst_ip_tmp <= dst_ip; src_ip_tmp <= src_ip; dst_port_tmp <= dst_port; src_port_tmp <= src_port; data_len_tmp <= data_len; endend

//IP总长度,20字节的IP⾸部+8字节UDP⾸部+UDP数据长度always @(posedge clk or negedge rst_n)begin if(!rst_n)

ip_total_len <= 0;

else if(state_c == TX_ETH_HED && (cnt_eth_hed == 0 && add_cnt_eht_hed)) ip_total_len = IP_HED_LEN + UDP_HED_LEN + data_len_tmp;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

udp_total_len <= 0;

else if(state_c == TX_ETH_HED && (cnt_eth_hed == 0 && add_cnt_eht_hed)) udp_total_len <= UDP_HED_LEN + data_len_tmp;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

sum_en <= 0;

else if(state_c == TX_ETH_HED && end_cnt_eth_hed) sum_en <= 1; else

sum_en <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

crc_init <= 0;

else if(tx_en && (!tx_flag)) crc_init <= 1; else

crc_init <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

crc_en <= 0;

else if(state_c == TX_ETH_HED)begin

if(cnt_eth_hed == 9 - 1 && add_cnt_eht_hed) crc_en <= 1; end

else if(state_c == TX_IP_HED || state_c == TX_UDP_HED || state_c == TX_UDP_DATA || state_c == TX_FILL_DATA) crc_en <= 1; else

crc_en <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

gmii_tx <= 0;

else if(tx_nocrc_flag) gmii_tx <= data_out;

else if(state_c == TX_CRC)begin case(cnt_crc)

0:gmii_tx <= crc_out[7:0]; 1:gmii_tx <= crc_out[15:8]; 2:gmii_tx <= crc_out[23:16]; 3:gmii_tx <= crc_out[31:24]; default:gmii_tx <= 8'h00; endcase end

else if(tx_done) gmii_tx <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

tx_nocrc_flag <= 0;

else if(state_c == TX_ETH_HED || state_c == TX_IP_HED || state_c == TX_UDP_HED || state_c == TX_UDP_DATA || state_c == TX_FILL_DATA) tx_nocrc_flag <= 1; else

tx_nocrc_flag <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

tx_done <= 0; else if(end_cnt_crc) tx_done <= 1; else

tx_done <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

data_vld <= 0;

else if(end_cnt_udp_hed) data_vld <= 1; else if(end_cnt_data) data_vld <= 0;end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

gmii_en <= 0;

else if(cnt_eth_hed == 1 && add_cnt_eht_hed) gmii_en <= 1; else if(tx_done) gmii_en <= 0;end

assign gmii_clk = clk;assign udp_check_sum = 0;ip_checksum ip_checksum(

.clk (clk), .rst_n (rst_n),

.sum_en (sum_en), //使能 .ver (IP_VER), //ip版本

.hdr_len (IP_HDR_LEN), //⾸部长度(单位,字节) .tos (IP_TOS), //服务类型 .total_len (ip_total_len), //总长度 .id (IP_ID), //分段标识 .rsv (IP_RSV), //未⽤ .df (IP_DF), //DF .mf (IP_MF), //MF

.frag_offset (IP_FRAG_OFFSET), //段偏移 .ttl (IP_TTL), //⽣存周期TTL(单位,秒) .protocal (IP_PROTOCOL), //上层协议 .check_sum (check_sum), //报头校验和 .src_ip (src_ip_tmp), //源ip地址 .dst_ip (dst_ip_tmp) //⽬的ip地址);

crc32_d8 crc32_d8( .clk (clk), .rst_n (rst_n), .data (data_out), .crc_init (crc_init), .crc_en (crc_en), .crc_out (crc_out));

endmodule

三、IP校验和模块

module ip_checksum( clk , rst_n ,

sum_en , //使能 ver , //ip版本

hdr_len , //⾸部长度(单位,字节) tos , //服务类型 total_len , //总长度 id , //分段标识 rsv , //未⽤ df , //DF mf , //MF

frag_offset , //段偏移

ttl , //⽣存周期TTL(单位,秒) protocal , //上层协议

check_sum , //报头校验和 src_ip , //源ip地址 dst_ip //⽬的ip地址);

localparam VER_W = 4;localparam HDR_W = 4; localparam TOS_W = 8; localparam TOT_W = 16; localparam ID_W = 16; localparam FRAG_W = 13; localparam TTL_W = 8; localparam PTOC_W = 8; localparam CHEC_W = 16; localparam SR_W = 32; localparam DS_W = 32;localparam SUM_W = 32;localparam ACC_W = 17;

input clk;input rst_n; input sum_en;input [VER_W-1:0] ver;input [HDR_W-1:0] hdr_len;input [TOS_W-1:0] tos;

input [TOT_W-1:0] total_len;input [ID_W-1:0] id;input rsv;input df;input mf;

input [FRAG_W-1:0] frag_offset;input [TTL_W-1:0] ttl;

input [PTOC_W-1:0] protocal;input [SR_W-1:0] src_ip;input [DS_W-1:0] dst_ip;

output [CHEC_W-1:0] check_sum;reg [CHEC_W-1:0] check_sum;//中间变量

reg [SUM_W-1:0] sum;

reg [ACC_W-1:0] acc_high_low;

always @(posedge clk or negedge rst_n)begin if(!rst_n) sum <= 0; else if(sum_en)

sum <= {ver,hdr_len,tos} + total_len + id + {rsv,df,mf,frag_offset} + {ttl,protocal} + src_ip[31:16] + src_ip[15:0] + dst_ip[31:16] + dst_ip[15:0];end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

acc_high_low <= 0; else

acc_high_low <= sum[15:0] + sum[31:16];end

always @(posedge clk or negedge rst_n)begin if(!rst_n)

check_sum <= 0; else

check_sum <= ~(acc_high_low[15:0] + acc_high_low[16]);endendmodule

四、CRC校验模块

////////////////////////////////////////////////////////////////////////////////// Copyright (C) 1999-2008 Easics NV.

// This source file may be used and distributed without restriction// provided that this copyright statement is not removed from the file// and that any derivative work contains the original copyright notice// and the associated disclaimer.//

// THIS SOURCE FILE IS PROVIDED \"AS IS\" AND WITHOUT ANY EXPRESS

// OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED

// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.//

// Purpose : synthesizable CRC function

// * polynomial: 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// * data width: 8//

// Info : tools@easics.be

// http://www.easics.com

////////////////////////////////////////////////////////////////////////////////module crc32_d8( clk , rst_n , data , crc_init , crc_en , crc_out );

localparam DATA_W = 8;localparam OUT_W = 32;input clk;input rst_n;

input [DATA_W-1:0] data;input crc_init;input crc_en;

output [OUT_W-1:0] crc_out;wire [OUT_W-1:0] crc_out;//中间变量

wire [DATA_W-1:0] data_in;reg [OUT_W-1:0] crc_out_inv;

//⽣成data反转电路generate genvar i;

for(i = 0;i < DATA_W;i = i + 1)begin

assign data_in[i] = data[(DATA_W-1) - i]; end

endgenerate

//⽣成crc反转电路generate genvar j;

for(j = 0;j < OUT_W;j = j + 1)begin

assign crc_out[j] = ~crc_out_inv[(OUT_W-1) - j]; end

endgenerate

//输出CRC,初始化CRC为FFFF_FFFF,使能输出CRCalways @(posedge clk or negedge rst_n)begin if(!rst_n)

crc_out_inv <= 0; else if(crc_init)

crc_out_inv <= 32'hffff_ffff; else if(crc_en)

crc_out_inv <= nextCRC32_D8(data_in,crc_out_inv);end

// polynomial: 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// data width: 8

// convention: the first serial bit is D[7]function [31:0] nextCRC32_D8; input [7:0] Data; input [31:0] crc; reg [7:0] d; reg [31:0] c;

reg [31:0] newcrc; begin

d = Data; c = crc;

newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30];

newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31];

newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31]; newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31];

newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30];

newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31]; newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31]; newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31]; newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28]; newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29]; newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29]; newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28];

newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30]; newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31]; newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31]; newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31]; newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29]; newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30]; newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31]; newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31]; newcrc[20] = d[4] ^ c[12] ^ c[28]; newcrc[21] = d[5] ^ c[13] ^ c[29]; newcrc[22] = d[0] ^ c[14] ^ c[24];

newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30]; newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31]; newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27];

newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30]; newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31]; newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30]; newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31]; newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31]; newcrc[31] = d[5] ^ c[23] ^ c[29]; nextCRC32_D8 = newcrc; end

endfunctionendmodule

五、仿真验证

`timescale 1ns / 1nsmodule eth_tb();

localparam MAC_W = 48;localparam IP_W = 32;localparam PORT_W = 16;localparam DATA_LEN_W = 16;localparam GMII_W = 8;localparam DATA_W = 8;

parameter CYCLE = 20;

reg clk; reg rst_n;

reg tx_en; //发送使能

reg [MAC_W-1:0] dst_mac; //⽬的Mac地址reg [MAC_W-1:0] src_mac; //源Mac地址reg [IP_W-1:0] dst_ip; //⽬的ip地址reg [IP_W-1:0] src_ip; //源ip地址reg [PORT_W-1:0] dst_port; //⽬的端⼝reg [PORT_W-1:0] src_port; //源端⼝

reg [DATA_LEN_W-1:0] data_len; //发送数据长度reg [DATA_W-1:0] data_in; //输⼊数据wire gmii_clk; //以太⽹接⼝时钟wire gmii_en; //以太⽹使能

wire [GMII_W-1:0] gmii_tx; //以太⽹数据wire tx_done; //发送完成

wire data_vld; //数据有效标志信号eth_udp_tx_gmii eth_udp_tx_gmii( .clk (clk), .rst_n (rst_n), .tx_en (tx_en), .tx_done (tx_done), .dst_mac (dst_mac), .src_mac (src_mac), .dst_ip (dst_ip), .src_ip (src_ip), .dst_port (dst_port), .src_port (src_port), .gmii_clk (gmii_clk), .data_len (data_len), .data_vld (data_vld), .gmii_en (gmii_en), .gmii_tx (gmii_tx), .data_in (data_in));

initial clk = 1;

always #(CYCLE/2) clk = ~clk;initial begin rst_n = 1; #3;

rst_n = 0; #(3*CYCLE) rst_n = 1;end

initial begin dst_mac = 0; #(10*CYCLE)

dst_mac = 48'hA1_6F_5B_12_01_F8;end

initial begin src_mac = 0; #(10*CYCLE)

src_mac = 48'h00_0a_35_01_fe_c0;end

initial begin dst_ip = 0; #(10*CYCLE)

dst_ip = 32'hc0_a8_00_02;end

initial begin src_ip = 0; #(10*CYCLE)

src_ip = 32'hc0_a8_00_03;end

initial begin dst_port = 0; #(10*CYCLE)

dst_port = 16'd8080;end

initial begin src_port = 0; #(10*CYCLE)

src_port = 16'd1010;

end

initial begin data_len = 0; #(10*CYCLE) data_len = 10;end

reg [4-1:0] cnt_data_in;wire add_cnt_data_in;wire end_cnt_data_in;

always @(posedge clk or negedge rst_n)begin if(!rst_n)begin

cnt_data_in <= 0; end

else if(add_cnt_data_in)begin if(end_cnt_data_in) cnt_data_in <= 0; else

cnt_data_in <= cnt_data_in + 1; endend

assign add_cnt_data_in = data_vld;

assign end_cnt_data_in = add_cnt_data_in && cnt_data_in == 11 - 1;always @(*)begin case (cnt_data_in) 0 :data_in = \"X\"; 1 :data_in = \"I\"; 2 :data_in = \"L\"; 3 :data_in = \"I\"; 4 :data_in = \"N\"; 5 :data_in = \"X\"; 6 :data_in = \" \"; 7 :data_in = \"F\"; 8 :data_in = \"P\"; 9 :data_in = \"G\"; 10:data_in = \"A\";

default: data_in = 8'h00; endcaseend

initial begin

@(posedge tx_done) #200; $stop;end

initial begin #1;

tx_en = 0; #(15*CYCLE) tx_en = 1; #(CYCLE) tx_en = 0;endendmodule

六、板级验证

module eth_udp_tx_gmii_test( clk , rst_n , led , eth_rst_n, gmii_clk , gmii_en , gmii_tx );

//延迟0.5ms

parameter DELAY_N = 625_00000;input clk;input rst_n;output led;

output eth_rst_n;output gmii_clk ;output gmii_en ;output [8-1:0] gmii_tx;

wire led;

wire eth_rst_n;wire gmii_clk ;wire gmii_en ;wire [8-1:0] gmii_tx;wire locked;wire clk125M;wire data_vld;reg [8-1:0] data_in;reg tx_en;//PLL稳定后,开始⼯作assign eth_rst_n = locked;assign led = locked;pll pll(

.clk_out1(clk125M), .resetn(rst_n), .locked(locked), .clk_in1(clk));

eth_udp_tx_gmii eth_udp_tx_gmii( .clk (clk125M), .rst_n (eth_rst_n), .tx_en (tx_en), .tx_done (tx_done),

.dst_mac (48'h10_1E_1F_12_11_18), .src_mac (48'h00_0a_35_01_fe_c0), .dst_ip (32'hc0_a8_00_03), .src_ip (32'hc0_a8_00_02), .dst_port (16'd6000), .src_port (16'd5000), .gmii_clk (gmii_clk), .data_len (23),

.data_vld (data_vld), .gmii_en (gmii_en), .gmii_tx (gmii_tx), .data_in (data_in));

reg [26-1:0] cnt_delay;wire add_cnt_delay;wire end_cnt_delay;

always @(posedge clk125M or negedge rst_n)begin if(!rst_n)begin cnt_delay <= 0; end

else if(add_cnt_delay)begin if(end_cnt_delay) cnt_delay <= 0; else

cnt_delay <= cnt_delay + 1; endend

assign add_cnt_delay = eth_rst_n;

assign end_cnt_delay = add_cnt_delay && cnt_delay == DELAY_N - 1 ;always @(posedge clk125M or negedge rst_n)begin if(!rst_n)

tx_en <= 0;

else if(end_cnt_delay) tx_en <= 1; else

tx_en <= 0;end

reg [5-1:0] cnt_data_in;wire add_cnt_data_in;wire end_cnt_data_in;

always @(posedge clk125M or negedge rst_n)begin if(!rst_n)begin

cnt_data_in <= 0; end

else if(add_cnt_data_in)begin if(end_cnt_data_in) cnt_data_in <= 0; else

cnt_data_in <= cnt_data_in + 1; endend

assign add_cnt_data_in = data_vld;

assign end_cnt_data_in = add_cnt_data_in && cnt_data_in == 23 - 1;always @(*)begin case (cnt_data_in)

16'd0 : data_in = \"H\"; 16'd1 : data_in = \"e\"; 16'd2 : data_in = \"l\"; 16'd3 : data_in = \"l\"; 16'd4 : data_in = \"o\"; 16'd5 : data_in = \ 16'd6 : data_in = \" \"; 16'd7 : data_in = \"w\"; 16'd8 : data_in = \"e\"; 16'd9 : data_in = \"l\"; 16'd10 : data_in = \"c\"; 16'd11 : data_in = \"o\"; 16'd12 : data_in = \"m\"; 16'd13 : data_in = \"e\"; 16'd14 : data_in = \" \"; 16'd15 : data_in = \"t\"; 16'd16 : data_in = \"o\"; 16'd17 : data_in = \" \"; 16'd18 : data_in = \"F\"; 16'd19 : data_in = \"P\"; 16'd20 : data_in = \"G\"; 16'd21 : data_in = \"A\"; 16'd22 : data_in = \"!\"; default:data_in = 8'h00; endcaseendendmodule

七、问题记录

在板级验证过程中,抓取的包出现异常 原因是FPGA开发板连接的物理层芯⽚引脚没有使⽤,但是FPGA的未⽤引脚是⾼电平和低电平影响了芯⽚的正常⼯作 解决⽅法是:添加⼀条约束,将未⽤引脚置为⾼阻态

set_property BITSTREAM.CONFIG.UNUSEDPIN Pullnone [current_design]

(IOB = \"TRUE\") output gmii_en; //以太⽹使能 (IOB = \"TRUE\") output [GMII_W-1:0] gmii_tx; //以太⽹数据

因篇幅问题不能全部显示,请点此查看更多更全内容