⼀、设计思路
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; //以太⽹数据
因篇幅问题不能全部显示,请点此查看更多更全内容