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
⾸部长度
服务类型
总长度
分段标识
保留位
DF
市民公园MF
段偏移
⽣存周期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 ,
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个32bit localparam 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 @(podge clk or negedge rst_n)begin
if(!rst_n)
state_c <= IDLE;
el
state_c <= state_n;
end
always @(*)begin
ca(state_c)
IDLE:begin
if(idle2tx_eth_hed)
state_n = TX_ETH_HED;
el
state_n = state_c;
end
TX_ETH_HED:begin
if(tx_eth_hed2tx_ip_hed)
state_n = TX_IP_HED;
el
state_n = state_c;
end
TX_IP_HED:begin
if(tx_ip_hed2tx_udp_hed)
state_n = TX_UDP_HED;
state_n = state_c;
end
TX_UDP_HED:begin
if(tx_udp_hed2tx_udp_data)
state_n = TX_UDP_DATA;
el
state_n = state_c;
end
TX_UDP_DATA:begin
if(tx_udp_data2tx_fill_data)
state_n = TX_FILL_DATA;
el if(tx_udp_data2tx_crc)
state_n = TX_CRC;
el
state_n = state_c;
end
TX_FILL_DATA:begin
if(tx_fill_data2tx_crc)
state_n = TX_CRC;
el
state_n = state_c;
end
TX_CRC:begin
if(tx_crc2idle)
state_n = IDLE;
el
state_n = state_c;
end
default:state_n = IDLE;
endca
end
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@(podge clk or negedge rst_n)begin
木兰诗作者if(!rst_n)
data_out <= 0;
el if(state_c == TX_ETH_HED)begin
ca(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;
endca
end
el if(state_c == TX_IP_HED)begin
ca(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;
endca
end
el if(state_c == TX_UDP_HED)begin
ca(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;
endca
end
el if(state_c == TX_UDP_DATA)begin
data_out <= data_in;
end
el if(state_c == TX_FILL_DATA)begin
data_out <= 8'h00;
end
el if(state_c == TX_CRC)begin
data_out <= 0;
end
end
//以太⽹头部计数器
always @(podge clk or negedge rst_n)begin
if(!rst_n)
cnt_eth_hed <= 0;
最美的季节
el if(add_cnt_eht_hed)begin
if(end_cnt_eth_hed)
cnt_eth_hed <= 0;
el
cnt_eth_hed <= cnt_eth_hed + 1'b1;
end
end
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 @(podge clk or negedge rst_n)begin
if(!rst_n)
cnt_ip_hed <= 0;
el if(add_cnt_ip_hed)begin
if(end_cnt_ip_hed)
cnt_ip_hed <= 0;
el
cnt_ip_hed <= cnt_ip_hed + 1;
end
end
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 @(podge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_udp_hed <= 0;
end
el if(add_cnt_udp_hed)begin
gta5怎么全屏if(end_cnt_udp_hed)
cnt_udp_hed <= 0;
el
cnt_udp_hed <= cnt_udp_hed + 1;
end
end
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 @(podge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_data <= 0;
end
el if(add_cnt_data)begin
if(end_cnt_data)
cnt_data <= 0;
el
cnt_data <= cnt_data + 1;
end