通信协议之ICMP协议的FPGA实现
整体的实现框图如下所示
arp_rx.v
module arp_rx
#(
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55,
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10}
)
(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input gmii_rx_dv , //GMII输入数据有效信号
input [7:0] gmii_rxd , //GMII输入数据
output reg arp_rx_done, //ARP接收完成信号
output reg arp_rx_type, //ARP接收类型 0:请求 1:应答
output reg [47:0] src_mac , //接收到的源MAC地址
output reg [31:0] src_ip //接收到的源IP地址
);
//parameter define
localparam st_idle = 5'b0_0001; //初始状态,等待接收前导码
localparam st_preamble = 5'b0_0010; //接收前导码状态
localparam st_eth_head = 5'b0_0100; //接收以太网帧头
localparam st_arp_data = 5'b0_1000; //接收ARP数据
localparam st_rx_end = 5'b1_0000; //接收结束
localparam ETH_TPYE = 16'h0806; //以太网帧类型 ARP
//reg define
reg [4:0] cur_state ;
reg [4:0] next_state;
reg skip_en ; //控制状态跳转使能信号
reg error_en ; //解析错误使能信号
reg [4:0] cnt ; //解析数据计数器
reg [47:0] des_mac_t ; //接收到的目的MAC地址
reg [31:0] des_ip_t ; //接收到的目的IP地址
reg [47:0] src_mac_t ; //接收到的源MAC地址
reg [31:0] src_ip_t ; //接收到的源IP地址
reg [15:0] eth_type ; //以太网类型
reg [15:0] op_data ; //操作码
//*****************************************************
//** main code
//*****************************************************
//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle;
case(cur_state)
st_idle : begin //等待接收前导码
if(skip_en)
next_state = st_preamble;
else
next_state = st_idle;
end
st_preamble : begin //接收前导码
if(skip_en)
next_state = st_eth_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_preamble;
end
st_eth_head : begin //接收以太网帧头
if(skip_en)
next_state = st_arp_data;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_eth_head;
end
st_arp_data : begin //接收ARP数据
if(skip_en)
next_state = st_rx_end;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_arp_data;
end
st_rx_end : begin //接收结束
if(skip_en)
next_state = st_idle;
else
next_state = st_rx_end;
end
default : next_state = st_idle;
endcase
end
//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
error_en <= 1'b0;
cnt <= 5'd0;
des_mac_t <= 48'd0;
des_ip_t <= 32'd0;
src_mac_t <= 48'd0;
src_ip_t <= 32'd0;
eth_type <= 16'd0;
op_data <= 16'd0;
arp_rx_done <= 1'b0;
arp_rx_type <= 1'b0;
src_mac <= 48'd0;
src_ip <= 32'd0;
end
else begin
skip_en <= 1'b0;
error_en <= 1'b0;
arp_rx_done <= 1'b0;
case(next_state)
st_idle : begin //检测到第一个8'h55
if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))
skip_en <= 1'b1;
else;
end
st_preamble : begin
if(gmii_rx_dv) begin //解析前导码
cnt <= cnt + 5'd1;
if((cnt < 5'd6) && (gmii_rxd != 8'h55)) //7个8'h55
error_en <= 1'b1;
else if(cnt==5'd6) begin
cnt <= 5'd0;
if(gmii_rxd==8'hd5) //1个8'hd5
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
else;
end
else;
end
st_eth_head : begin
if(gmii_rx_dv) begin
cnt <= cnt + 5'b1;
if(cnt < 5'd6)
des_mac_t <= {des_mac_t[39:0],gmii_rxd};
else if(cnt == 5'd6) begin
//判断MAC地址是否为开发板MAC地址或者公共地址
if((des_mac_t != BOARD_MAC)
&& (des_mac_t != 48'hff_ff_ff_ff_ff_ff))
error_en <= 1'b1;
end
else if(cnt == 5'd12)
eth_type[15:8] <= gmii_rxd; //以太网协议类型
else if(cnt == 5'd13) begin
eth_type[7:0] <= gmii_rxd;
cnt <= 5'd0;
if(eth_type[15:8] == ETH_TPYE[15:8] //判断是否为ARP协议
&& gmii_rxd == ETH_TPYE[7:0])
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
else;
end
else;
end
st_arp_data : begin
if(gmii_rx_dv) begin
cnt <= cnt + 5'd1;
if(cnt == 5'd6)
op_data[15:8] <= gmii_rxd; //操作码
else if(cnt == 5'd7)
op_data[7:0] <= gmii_rxd;
else if(cnt >= 5'd8 && cnt < 5'd14) //源MAC地址
src_mac_t <= {src_mac_t[39:0],gmii_rxd};
else if(cnt >= 5'd14 && cnt < 5'd18) //源IP地址
src_ip_t<= {src_ip_t[23:0],gmii_rxd};
else if(cnt >= 5'd24 && cnt < 5'd28) //目标IP地址
des_ip_t <= {des_ip_t[23:0],gmii_rxd};
else if(cnt == 5'd28) begin
cnt <= 5'd0;
if(des_ip_t == BOARD_IP) begin //判断目的IP地址和操作码
if((op_data == 16'd1) || (op_data == 16'd2)) begin
skip_en <= 1'b1;
arp_rx_done <= 1'b1;
src_mac <= src_mac_t;
src_ip <= src_ip_t;
src_mac_t <= 48'd0;
src_ip_t <= 32'd0;
des_mac_t <= 48'd0;
des_ip_t <= 32'd0;
if(op_data == 16'd1)
arp_rx_type <= 1'b0; //ARP请求
else
arp_rx_type <= 1'b1; //ARP应答
end
else
error_en <= 1'b1;
end
else
error_en <= 1'b1;
end
else;
end
else;
end
st_rx_end : begin
cnt <= 5'd0;
//单包数据接收完成
if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
skip_en <= 1'b1;
else;
end
default : ;
endcase
end
end
endmodule
arp_tx.v
module arp_tx(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input arp_tx_en , //ARP发送使能信号
input arp_tx_type, //ARP发送类型 0:请求 1:应答
input [47:0] des_mac , //发送的目标MAC地址
input [31:0] des_ip , //发送的目标IP地址
input [31:0] crc_data , //CRC校验数据
input [7:0] crc_next , //CRC下次校验完成数据
output reg tx_done , //以太网发送完成信号
output reg gmii_tx_en , //GMII输出数据有效信号
output reg [7:0] gmii_txd , //GMII输出数据
output reg crc_en , //CRC开始校验使能
output reg crc_clr //CRC数据复位信号
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
localparam st_idle = 5'b0_0001; //初始状态,等待开始发送信号
localparam st_preamble = 5'b0_0010; //发送前导码+帧起始界定符
localparam st_eth_head = 5'b0_0100; //发送以太网帧头
localparam st_arp_data = 5'b0_1000; //
localparam st_crc = 5'b1_0000; //发送CRC校验值
localparam ETH_TYPE = 16'h0806 ; //以太网帧类型 ARP协议
localparam HD_TYPE = 16'h0001 ; //硬件类型 以太网
localparam PROTOCOL_TYPE= 16'h0800 ; //上层协议为IP协议
//以太网数据最小为46个字节,不足部分填充数据
localparam MIN_DATA_NUM = 16'd46 ;
//reg define
reg [4:0] cur_state ;
reg [4:0] next_state ;
reg [7:0] preamble[7:0] ; //前导码+SFD
reg [7:0] eth_head[13:0]; //以太网首部
reg [7:0] arp_data[27:0]; //ARP数据
reg tx_en_d0 ; //arp_tx_en信号延时
reg tx_en_d1 ;
reg tx_en_d2 ;
reg skip_en ; //控制状态跳转使能信号
reg [5:0] cnt ;
reg [4:0] data_cnt ; //发送数据个数计数器
reg tx_done_t ;
//wire define
wire pos_tx_en ; //arp_tx_en信号上升沿
//*****************************************************
//** main code
//*****************************************************
assign pos_tx_en = (~tx_en_d2) & tx_en_d1;
//对arp_tx_en信号延时打拍两次,用于采arp_tx_en的上升沿
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_en_d0 <= 1'b0;
tx_en_d1 <= 1'b0;
tx_en_d2 <= 1'b0;
end
else begin
tx_en_d0 <= arp_tx_en;
tx_en_d1 <= tx_en_d0;
tx_en_d2 <= tx_en_d1;
end
end
//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle;
case(cur_state)
st_idle : begin //空闲状态
if(skip_en)
next_state = st_preamble;
else
next_state = st_idle;
end
st_preamble : begin //发送前导码+帧起始界定符
if(skip_en)
next_state = st_eth_head;
else
next_state = st_preamble;
end
st_eth_head : begin //发送以太网首部
if(skip_en)
next_state = st_arp_data;
else
next_state = st_eth_head;
end
st_arp_data : begin //发送ARP数据
if(skip_en)
next_state = st_crc;
else
next_state = st_arp_data;
end
st_crc: begin //发送CRC校验值
if(skip_en)
next_state = st_idle;
else
next_state = st_crc;
end
default : next_state = st_idle;
endcase
end
//时序电路描述状态输出,发送以太网数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
cnt <= 6'd0;
data_cnt <= 5'd0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
gmii_txd <= 8'd0;
tx_done_t <= 1'b0;
//初始化数组
//前导码 7个8'h55 + 1个8'hd5
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;
//以太网帧头
eth_head[0] <= DES_MAC[47:40]; //目的MAC地址
eth_head[1] <= DES_MAC[39:32];
eth_head[2] <= DES_MAC[31:24];
eth_head[3] <= DES_MAC[23:16];
eth_head[4] <= DES_MAC[15:8];
eth_head[5] <= DES_MAC[7:0];
eth_head[6] <= BOARD_MAC[47:40]; //源MAC地址
eth_head[7] <= BOARD_MAC[39:32];
eth_head[8] <= BOARD_MAC[31:24];
eth_head[9] <= BOARD_MAC[23:16];
eth_head[10] <= BOARD_MAC[15:8];
eth_head[11] <= BOARD_MAC[7:0];
eth_head[12] <= ETH_TYPE[15:8]; //以太网帧类型
eth_head[13] <= ETH_TYPE[7:0];
//ARP数据
arp_data[0] <= HD_TYPE[15:8]; //硬件类型
arp_data[1] <= HD_TYPE[7:0];
arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
arp_data[3] <= PROTOCOL_TYPE[7:0];
arp_data[4] <= 8'h06; //硬件地址长度,6
arp_data[5] <= 8'h04; //协议地址长度,4
arp_data[6] <= 8'h00; //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
arp_data[7] <= 8'h01;
arp_data[8] <= BOARD_MAC[47:40]; //发送端(源)MAC地址
arp_data[9] <= BOARD_MAC[39:32];
arp_data[10] <= BOARD_MAC[31:24];
arp_data[11] <= BOARD_MAC[23:16];
arp_data[12] <= BOARD_MAC[15:8];
arp_data[13] <= BOARD_MAC[7:0];
arp_data[14] <= BOARD_IP[31:24]; //发送端(源)IP地址
arp_data[15] <= BOARD_IP[23:16];
arp_data[16] <= BOARD_IP[15:8];
arp_data[17] <= BOARD_IP[7:0];
arp_data[18] <= DES_MAC[47:40]; //接收端(目的)MAC地址
arp_data[19] <= DES_MAC[39:32];
arp_data[20] <= DES_MAC[31:24];
arp_data[21] <= DES_MAC[23:16];
arp_data[22] <= DES_MAC[15:8];
arp_data[23] <= DES_MAC[7:0];
arp_data[24] <= DES_IP[31:24]; //接收端(目的)IP地址
arp_data[25] <= DES_IP[23:16];
arp_data[26] <= DES_IP[15:8];
arp_data[27] <= DES_IP[7:0];
end
else begin
skip_en <= 1'b0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
tx_done_t <= 1'b0;
case(next_state)
st_idle : begin
if(pos_tx_en) begin
skip_en <= 1'b1;
//如果目标MAC地址和IP地址已经更新,则发送正确的地址
if((des_mac != 48'b0) || (des_ip != 32'd0)) begin
eth_head[0] <= des_mac[47:40];
eth_head[1] <= des_mac[39:32];
eth_head[2] <= des_mac[31:24];
eth_head[3] <= des_mac[23:16];
eth_head[4] <= des_mac[15:8];
eth_head[5] <= des_mac[7:0];
arp_data[18] <= des_mac[47:40];
arp_data[19] <= des_mac[39:32];
arp_data[20] <= des_mac[31:24];
arp_data[21] <= des_mac[23:16];
arp_data[22] <= des_mac[15:8];
arp_data[23] <= des_mac[7:0];
arp_data[24] <= des_ip[31:24];
arp_data[25] <= des_ip[23:16];
arp_data[26] <= des_ip[15:8];
arp_data[27] <= des_ip[7:0];
end
else;
if(arp_tx_type == 1'b0)
arp_data[7] <= 8'h01; //ARP请求
else
arp_data[7] <= 8'h02; //ARP应答
end
else;
end
st_preamble : begin //发送前导码+帧起始界定符
gmii_tx_en <= 1'b1;
gmii_txd <= preamble[cnt];
if(cnt == 6'd7) begin
skip_en <= 1'b1;
cnt <= 1'b0;
end
else
cnt <= cnt + 1'b1;
end
st_eth_head : begin //发送以太网首部
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
gmii_txd <= eth_head[cnt];
if (cnt == 6'd13) begin
skip_en <= 1'b1;
cnt <= 1'b0;
end
else
cnt <= cnt + 1'b1;
end
st_arp_data : begin //发送ARP数据
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
//至少发送46个字节
if (cnt == MIN_DATA_NUM - 1'b1) begin
skip_en <= 1'b1;
cnt <= 1'b0;
data_cnt <= 1'b0;
end
else
cnt <= cnt + 1'b1;
if(data_cnt <= 6'd27) begin
data_cnt <= data_cnt + 1'b1;
gmii_txd <= arp_data[data_cnt];
end
else
gmii_txd <= 8'd0; //Padding,填充0
end
st_crc : begin //发送CRC校验值
gmii_tx_en <= 1'b1;
cnt <= cnt + 1'b1;
if(cnt == 6'd0)
gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
else if(cnt == 6'd1)
gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],
~crc_data[19], ~crc_data[20], ~crc_data[21],
~crc_data[22],~crc_data[23]};
else if(cnt == 6'd2) begin
gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],
~crc_data[11],~crc_data[12], ~crc_data[13],
~crc_data[14],~crc_data[15]};
end
else if(cnt == 6'd3) begin
gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
tx_done_t <= 1'b1;
skip_en <= 1'b1;
cnt <= 1'b0;
end
else;
end
default :;
endcase
end
end
//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_done <= 1'b0;
crc_clr <= 1'b0;
end
else begin
tx_done <= tx_done_t;
crc_clr <= tx_done_t;
end
end
endmodule
arp.v
module arp(
input rst_n , //复位信号,低电平有效
//GMII接口
input gmii_rx_clk, //GMII接收数据时钟
input gmii_rx_dv , //GMII输入数据有效信号
input [7:0] gmii_rxd , //GMII输入数据
input gmii_tx_clk, //GMII发送数据时钟
output gmii_tx_en , //GMII输出数据有效信号
output [7:0] gmii_txd , //GMII输出数据
//用户接口
output arp_rx_done, //ARP接收完成信号
output arp_rx_type, //ARP接收类型 0:请求 1:应答
output [47:0] src_mac , //接收到目的MAC地址
output [31:0] src_ip , //接收到目的IP地址
input arp_tx_en , //ARP发送使能信号
input arp_tx_type, //ARP发送类型 0:请求 1:应答
input [47:0] des_mac , //发送的目标MAC地址
input [31:0] des_ip , //发送的目标IP地址
output tx_done //以太网发送完成信号
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
//wire define
wire crc_en ; //CRC开始校验使能
wire crc_clr ; //CRC数据复位信号
wire [7:0] crc_d8 ; //输入待校验8位数据
wire [31:0] crc_data; //CRC校验数据
wire [31:0] crc_next; //CRC下次校验完成数据
//*****************************************************
//** main code
//*****************************************************
assign crc_d8 = gmii_txd;
//ARP接收模块
arp_rx
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP )
)
u_arp_rx(
.clk (gmii_rx_clk),
.rst_n (rst_n),
.gmii_rx_dv (gmii_rx_dv),
.gmii_rxd (gmii_rxd ),
.arp_rx_done (arp_rx_done),
.arp_rx_type (arp_rx_type),
.src_mac (src_mac ),
.src_ip (src_ip )
);
//ARP发送模块
arp_tx
#(
.BOARD_MAC (BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP ),
.DES_MAC (DES_MAC ),
.DES_IP (DES_IP )
)
u_arp_tx(
.clk (gmii_tx_clk),
.rst_n (rst_n),
.arp_tx_en (arp_tx_en ),
.arp_tx_type (arp_tx_type),
.des_mac (des_mac ),
.des_ip (des_ip ),
.crc_data (crc_data ),
.crc_next (crc_next[31:24]),
.tx_done (tx_done ),
.gmii_tx_en (gmii_tx_en),
.gmii_txd (gmii_txd ),
.crc_en (crc_en ),
.crc_clr (crc_clr )
);
//以太网发送CRC校验模块
crc32_d8 u_crc32_d8(
.clk (gmii_tx_clk),
.rst_n (rst_n ),
.data (crc_d8 ),
.crc_en (crc_en ),
.crc_clr (crc_clr ),
.crc_data (crc_data ),
.crc_next (crc_next )
);
endmodule
crc_32_d8.v
module crc32_d8(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input [7:0] data , //输入待校验8位数据
input crc_en , //crc使能,开始校验标志
input crc_clr , //crc数据复位信号
output reg [31:0] crc_data, //CRC校验数据
output [31:0] crc_next //CRC下次校验完成数据
);
//*****************************************************
//** main code
//*****************************************************
//输入待校验8位数据,需要先将高低位互换
wire [7:0] data_t;
assign data_t = {data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]};
//CRC32的生成多项式为:G(x)= 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
assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6];
assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31]
^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7];
assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30]
^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6]
^ data_t[7];
assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31]
^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7];
assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28]
^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4]
^ data_t[6];
assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28]
^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0]
^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6]
^ data_t[7];
assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29]
^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4]
^ data_t[5] ^ data_t[6] ^ data_t[7];
assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29]
^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5]
^ data_t[7];
assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27]
^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28]
^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5];
assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27]
^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5];
assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27]
^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26]
^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0]
^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6];
assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27]
^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1]
^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7];
assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28]
^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4]
^ data_t[6] ^ data_t[7];
assign crc_next[15] = crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29]
^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7];
assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29]
^ data_t[0] ^ data_t[4] ^ data_t[5];
assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30]
^ data_t[1] ^ data_t[5] ^ data_t[6];
assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31]
^ data_t[2] ^ data_t[6] ^ data_t[7];
assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7];
assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4];
assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5];
assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0];
assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30]
^ data_t[0] ^ data_t[1] ^ data_t[6];
assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31]
^ data_t[1] ^ data_t[2] ^ data_t[7];
assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3];
assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28]
^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6];
assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29]
^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7];
assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30]
^ data_t[2] ^ data_t[5] ^ data_t[6];
assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31]
^ data_t[3] ^ data_t[6] ^ data_t[7];
assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7];
assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5];
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
crc_data <= 32'hff_ff_ff_ff;
else if(crc_clr) //CRC校验值复位
crc_data <= 32'hff_ff_ff_ff;
else if(crc_en)
crc_data <= crc_next;
end
endmodule
eth_ctrl.v
module eth_ctrl(
input clk , //时钟
input rst_n , //系统复位信号,低电平有效
//ARP相关端口信号
input arp_rx_done , //ARP接收完成信号
input arp_rx_type , //ARP接收类型 0:请求 1:应答
output reg arp_tx_en , //ARP发送使能信号
output arp_tx_type , //ARP发送类型 0:请求 1:应答
input arp_tx_done , //ARP发送完成信号
input arp_gmii_tx_en , //ARP GMII输出数据有效信号
input [7:0] arp_gmii_txd , //ARP GMII输出数据
//ICMP相关端口信号
input icmp_tx_start_en , //ICMP开始发送信号
input icmp_tx_done , //ICMP发送完成信号
input icmp_gmii_tx_en , //ICMP GMII输出数据有效信号
input [7:0] icmp_gmii_txd , //ICMP GMII输出数据
//GMII发送引脚
output gmii_tx_en , //GMII输出数据有效信号
output [7:0] gmii_txd //GMII输出数据
);
//reg define
reg protocol_sw ; //协议切换信号
reg icmp_tx_busy; //ICMP正在发送数据标志信号
reg arp_rx_flag; //接收到ARP请求信号的标志
//*****************************************************
//** main code
//*****************************************************
assign arp_tx_type = 1'b1; //ARP发送类型固定为ARP应答
assign gmii_tx_en = protocol_sw ? icmp_gmii_tx_en : arp_gmii_tx_en;
assign gmii_txd = protocol_sw ? icmp_gmii_txd : arp_gmii_txd;
//控制ICMP发送忙信号
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
icmp_tx_busy <= 1'b0;
else if(icmp_tx_start_en)
icmp_tx_busy <= 1'b1;
else if(icmp_tx_done)
icmp_tx_busy <= 1'b0;
end
//控制接收到ARP请求信号的标志
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
arp_rx_flag <= 1'b0;
else if(arp_rx_done && (arp_rx_type == 1'b0))
arp_rx_flag <= 1'b1;
else
arp_rx_flag <= 1'b0;
end
//控制protocol_sw和arp_tx_en信号
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
protocol_sw <= 1'b0;
arp_tx_en <= 1'b0;
end
else
begin
arp_tx_en <= 1'b0;
if(icmp_tx_start_en)
protocol_sw <= 1'b1;
else if(arp_rx_flag && (icmp_tx_busy == 1'b0))
begin
protocol_sw <= 1'b0;
arp_tx_en <= 1'b1;
end
end
end
endmodule
eth_icmp_test.v – 这是整个的top函数
module eth_icmp_test(
input sys_clk ,
input sys_rst_n ,
// PL 浠ュお缃?
input eth_rxc , // 鎺ユ敹鏃堕挓
input eth_rx_ctl , // 杈撳叆鏈夋晥鏃堕挓
input [3 : 0] eth_rxd ,
output eth_txc ,
output eth_tx_ctl ,
output [3 : 0] eth_txd ,
output eth_rst_n
);
// -------------------------------------------- //
// parameter and define //
// -------------------------------------------- //
//parameter define
//寮?鍙戞澘MAC鍦板潃 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55 ;
//寮?鍙戞澘IP鍦板潃 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10} ;
//鐩殑MAC鍦板潃 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff ;
//鐩殑IP鍦板潃 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102} ;
//杈撳叆鏁版嵁IO寤舵椂,姝ゅ涓?0,鍗充笉寤舵椂(濡傛灉涓簄,琛ㄧず寤舵椂n*78ps)
parameter IDELAY_VALUE = 0;
// define
//wire define
wire clk_200m ; //鐢ㄤ簬IO寤舵椂鐨勬椂閽?
wire gmii_rx_clk ; //GMII鎺ユ敹鏃堕挓
wire gmii_rx_dv ; //GMII鎺ユ敹鏁版嵁鏈夋晥淇″彿
wire [7:0] gmii_rxd ; //GMII鎺ユ敹鏁版嵁
wire gmii_tx_clk ; //GMII鍙戦?佹椂閽?
wire gmii_tx_en ; //GMII鍙戦?佹暟鎹娇鑳戒俊鍙?
wire [7:0] gmii_txd ; //GMII鍙戦?佹暟鎹?
wire arp_gmii_tx_en ; //ARP GMII杈撳嚭鏁版嵁鏈夋晥淇″彿
wire [7:0] arp_gmii_txd ; //ARP GMII杈撳嚭鏁版嵁
wire arp_rx_done ; //ARP鎺ユ敹瀹屾垚淇″彿
wire arp_rx_type ; //ARP鎺ユ敹绫诲瀷 0:璇锋眰 1:搴旂瓟
wire [47:0] src_mac ; //鎺ユ敹鍒扮洰鐨凪AC鍦板潃
wire [31:0] src_ip ; //鎺ユ敹鍒扮洰鐨処P鍦板潃
wire arp_tx_en ; //ARP鍙戦?佷娇鑳戒俊鍙?
wire arp_tx_type ; //ARP鍙戦?佺被鍨? 0:璇锋眰 1:搴旂瓟
wire [47:0] des_mac ; //鍙戦?佺殑鐩爣MAC鍦板潃
wire [31:0] des_ip ; //鍙戦?佺殑鐩爣IP鍦板潃
wire arp_tx_done ; //ARP鍙戦?佸畬鎴愪俊鍙?
wire icmp_gmii_tx_en ; //ICMP GMII杈撳嚭鏁版嵁鏈夋晥淇″彿
wire [7:0] icmp_gmii_txd ; //ICMP GMII杈撳嚭鏁版嵁
wire rec_pkt_done ; //ICMP鍗曞寘鏁版嵁鎺ユ敹瀹屾垚淇″彿
wire rec_en ; //ICMP鎺ユ敹鐨勬暟鎹娇鑳戒俊鍙?
wire [ 7:0] rec_data ; //ICMP鎺ユ敹鐨勬暟鎹?
wire [15:0] rec_byte_num ; //ICMP鎺ユ敹鐨勬湁鏁堝瓧鑺傛暟 鍗曚綅:byte
wire [15:0] tx_byte_num ; //ICMP鍙戦?佺殑鏈夋晥瀛楄妭鏁? 鍗曚綅:byte
wire icmp_tx_done ; //ICMP鍙戦?佸畬鎴愪俊鍙?
wire tx_req ; //ICMP璇绘暟鎹姹備俊鍙?
wire [ 7:0] tx_data ; //ICMP寰呭彂閫佹暟鎹?
wire tx_start_en ; //ICMP鍙戦?佸紑濮嬩娇鑳戒俊鍙?
// ------------------------------------------ //
// next is main code //
// ---------------------------------------- //
assign tx_start_en = rec_pkt_done ;
assign tx_byte_num = rec_byte_num ;
assign des_mac = src_mac ;
assign des_ip = src_ip ;
assign eth_rst_n = sys_rst_n ;
// 鏃堕挓
clk_wiz_0 u_clk_wiz_0 (
.clk_out1(clk_200m) , // output clk_out1
.reset(~sys_rst_n) , // input reset
.locked(locked) , // output locked
.clk_in1(sys_clk)
);
// gmii_to_rgmii
gmii_to_rgmii u_gmii_to_rgmii(
.idelay_clk ( clk_200m ),
.gmii_rx_clk ( gmii_rx_clk ),
.gmii_rx_dv ( gmii_rx_dv ),
.gmii_rxd ( gmii_rxd ),
.gmii_tx_clk ( gmii_tx_clk ),
.gmii_tx_en ( gmii_tx_en ),
.gmii_txd ( gmii_txd ),
.rgmii_rxc ( eth_rxc ),
.rgmii_rx_ctl ( eth_rx_ctl ),
.rgmii_rxd ( eth_rxd ),
.rgmii_txc ( eth_txc ),
.rgmii_tx_ctl ( eth_tx_ctl ),
.rgmii_txd ( eth_txd )
);
// arp閫氫俊
arp u_arp(
.rst_n ( sys_rst_n ),
.gmii_rx_clk ( gmii_rx_clk ),
.gmii_rx_dv ( gmii_rx_dv ),
.gmii_rxd ( gmii_rxd ),
.gmii_tx_clk ( gmii_tx_clk ),
.gmii_tx_en ( arp_gmii_tx_en ),
.gmii_txd ( arp_gmii_txd ),
.arp_rx_done ( arp_rx_done ),
.arp_rx_type ( arp_rx_type ),
.src_mac ( src_mac ),
.src_ip ( src_ip ),
.arp_tx_en ( arp_tx_en ),
.arp_tx_type ( arp_tx_type ),
.des_mac ( des_mac ),
.des_ip ( des_ip ),
.tx_done ( arp_tx_done )
);
// icmp閫氫俊
icmp u_icmp(
.rst_n ( sys_rst_n ),
.gmii_rx_clk ( gmii_rx_clk ),
.gmii_rx_dv ( gmii_rx_dv ),
.gmii_rxd ( gmii_rxd ),
.gmii_tx_clk ( gmii_tx_clk ),
.gmii_tx_en ( icmp_gmii_tx_en ),
.gmii_txd ( icmp_gmii_txd ),
.rec_pkt_done ( rec_pkt_done ),
.rec_en ( rec_en ),
.rec_data ( rec_data ),
.rec_byte_num ( rec_byte_num ),
.tx_start_en ( tx_start_en ),
.tx_data ( tx_data ),
.tx_byte_num ( tx_byte_num ),
.des_mac ( des_mac ),
.des_ip ( des_ip ),
.tx_done ( icmp_tx_done ),
.tx_req ( tx_req )
);
鍚屾FIFO
//sync_fifo_2048x8b sync_fifo_2048x8b_inst (
fifo_generator_0 sync_fifo_2048x8b_inst (
.clk(gmii_rx_clk), // input wire clk
//.rst(~sys_rst_n), // input wire rst
.srst(~sys_rst_n), // input wire rst
.din(rec_data), // input wire [7 : 0] din
.wr_en(rec_en), // input wire wr_en
.rd_en(tx_req), // input wire rd_en
.dout(tx_data), // output wire [7 : 0] dout
.full(), // output wire full
.empty() // output wire empty
);
//eth鈥斺?攃trl
eth_ctrl u_eth_ctrl(
.clk ( gmii_rx_clk ),
.rst_n ( sys_rst_n ),
.arp_rx_done ( arp_rx_done ),
.arp_rx_type ( arp_rx_type ),
.arp_tx_en ( arp_tx_en ),
.arp_tx_type ( arp_tx_type ),
.arp_tx_done ( arp_tx_done ),
.arp_gmii_tx_en ( arp_gmii_tx_en ),
.arp_gmii_txd ( arp_gmii_txd ),
.icmp_tx_start_en ( tx_start_en ),
.icmp_tx_done ( icmp_tx_done ),
.icmp_gmii_tx_en ( icmp_gmii_tx_en ),
.icmp_gmii_txd ( icmp_gmii_txd ),
.gmii_tx_en ( gmii_tx_en ),
.gmii_txd ( gmii_txd )
);
endmodule
gmii_to_rgmii.v
module gmii_to_rgmii(
input idelay_clk , //IDELAY时钟
//以太网GMII接口
output gmii_rx_clk , //GMII接收时钟
output gmii_rx_dv , //GMII接收数据有效信号
output [7:0] gmii_rxd , //GMII接收数据
output gmii_tx_clk , //GMII发送时钟
input gmii_tx_en , //GMII发送数据使能信号
input [7:0] gmii_txd , //GMII发送数据
//以太网RGMII接口
input rgmii_rxc , //RGMII接收时钟
input rgmii_rx_ctl, //RGMII接收数据控制信号
input [3:0] rgmii_rxd , //RGMII接收数据
output rgmii_txc , //RGMII发送时钟
output rgmii_tx_ctl, //RGMII发送数据控制信号
output [3:0] rgmii_txd //RGMII发送数据
);
parameter IDEAY_VALUE = 0 ;
// ------------------------------------ //
// -------------main code ------------ //
// ------------------------------------ //
assign gmii_tx_clk = gmii_rx_clk ;
// rgmii
rgmii_rx u_rgmii_rx(
.idelay_clk ( idelay_clk ),
.rgmii_rxc ( rgmii_rxc ),
.rgmii_rx_ctl ( rgmii_rx_ctl ),
.rgmii_rxd ( rgmii_rxd ),
.gmii_rx_clk ( gmii_rx_clk ),
.gmii_rx_dv ( gmii_rx_dv ),
.gmii_rxd ( gmii_rxd )
);
// gmii
rgmii_tx u_rgmii_tx(
.gmii_tx_clk ( gmii_tx_clk ),
.gmii_tx_en ( gmii_tx_en ),
.gmii_txd ( gmii_txd ),
.rgmii_txc ( rgmii_txc ),
.rgmii_tx_ctl ( rgmii_tx_ctl ),
.rgmii_txd ( rgmii_txd )
);
endmodule
icmp_rx.v
module icmp_rx(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input gmii_rx_dv , //GMII输入数据有效信号
input [7:0] gmii_rxd , //GMII输入数据
output reg rec_pkt_done, //以太网单包数据接收完成信号
output reg rec_en , //以太网接收的数据使能信号
output reg [ 7:0] rec_data , //以太网接收的数据
output reg [15:0] rec_byte_num, //以太网接收的有效字数 单位:byte
output reg [15:0] icmp_id , //ICMP标识符
output reg [15:0] icmp_seq , //ICMP序列号
output reg [31:0] reply_checksum //接收数据校验
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
//状态机状态定义
localparam st_idle = 7'b000_0001; //初始状态,等待接收前导码
localparam st_preamble = 7'b000_0010; //接收前导码状态
localparam st_eth_head = 7'b000_0100; //接收以太网帧头
localparam st_ip_head = 7'b000_1000; //接收IP首部
localparam st_icmp_head = 7'b001_0000; //接收ICMP首部
localparam st_rx_data = 7'b010_0000; //接收有效数据
localparam st_rx_end = 7'b100_0000; //接收结束
//以太网类型定义
localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
localparam ICMP_TYPE = 8'd1 ; //ICMP协议类型
//ICMP报文类型:回显请求
localparam ECHO_REQUEST = 8'h08 ;
//reg define
reg [6:0] cur_state ;
reg [6:0] next_state ;
reg skip_en ; //控制状态跳转使能信号
reg error_en ; //解析错误使能信号
reg [4:0] cnt ; //解析数据计数器
reg [47:0] des_mac ; //目的MAC地址
reg [15:0] eth_type ; //以太网类型
reg [31:0] des_ip ; //目的IP地址
reg [5:0] ip_head_byte_num; //IP首部长度
reg [15:0] total_length ; //IP长度
reg [1:0] rec_en_cnt ; //8bit转32bit计数器
reg [7:0] icmp_type ; //ICMP报文类型:用于标识错误类型的差错报文或者查询类型的报告报文
reg [7:0] icmp_code ; //ICMP报文代码:根据ICMP差错报文的类型,进一步分析错误的原因,代码值不同对应的错误也不同
//例如:类型为11且代码为0,表示数据传输过程中超时了,超时的具体原因是TTL值为0,数据报被丢弃。
reg [15:0] icmp_checksum ; //接收校验和:数据发送到目的地后需要对ICMP数据报文做一个校验,用于检查数据报文是否有错误
reg [15:0] icmp_data_length ; //data length register
reg [15:0] icmp_rx_cnt ; //接收数据计数
reg [7:0] icmp_rx_data_d0 ;
reg [31:0] reply_checksum_add ;
//****************************************************
//** main code
//*****************************************************
//(三段式状态机)同步时序描述状态转移
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle;
case(cur_state)
st_idle : begin //等待接收前导码
if(skip_en)
next_state = st_preamble;
else
next_state = st_idle;
end
st_preamble : begin //接收前导码
if(skip_en)
next_state = st_eth_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_preamble;
end
st_eth_head : begin //接收以太网帧头
if(skip_en)
next_state = st_ip_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_eth_head;
end
st_ip_head : begin //接收IP首部
if(skip_en)
next_state = st_icmp_head;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_ip_head;
end
st_icmp_head : begin //接收ICMP首部
if(skip_en)
next_state = st_rx_data;
else if(error_en)
next_state = st_rx_end;
else
next_state = st_icmp_head;
end
st_rx_data : begin //接收有效数据
if(skip_en)
next_state = st_rx_end;
else
next_state = st_rx_data;
end
st_rx_end : begin //接收结束
if(skip_en)
next_state = st_idle;
else
next_state = st_rx_end;
end
default : next_state = st_idle;
endcase
end
//时序电路描述状态输出,解析以太网数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
error_en <= 1'b0;
cnt <= 5'd0;
des_mac <= 48'd0;
eth_type <= 16'd0;
des_ip <= 32'd0;
ip_head_byte_num <= 6'd0;
total_length <= 16'd0;
icmp_type <= 8'd0;
icmp_code <= 8'd0;
icmp_checksum <= 16'd0;
icmp_id <= 16'd0;
icmp_seq <= 16'd0;
icmp_rx_data_d0 <= 8'd0 ;
reply_checksum <= 32'd0; //累加
reply_checksum_add <= 32'd0;
icmp_rx_cnt <= 16'd0;
icmp_data_length <= 16'd0;
rec_en_cnt <= 2'd0;
rec_en <= 1'b0;
rec_data <= 32'd0;
rec_pkt_done <= 1'b0;
rec_byte_num <= 16'd0;
end
else begin
skip_en <= 1'b0;
error_en <= 1'b0;
rec_pkt_done <= 1'b0;
case(next_state)
st_idle : begin
if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))
skip_en <= 1'b1;
else;
end
st_preamble : begin
if(gmii_rx_dv) begin //解析前导码
cnt <= cnt + 5'd1;
if((cnt < 5'd6) && (gmii_rxd != 8'h55)) //7个8'h55
error_en <= 1'b1;
else if(cnt==5'd6) begin
cnt <= 5'd0;
if(gmii_rxd==8'hd5) //1个8'hd5
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
else;
end
end
st_eth_head : begin
if(gmii_rx_dv) begin
cnt <= cnt + 5'b1;
if(cnt < 5'd6)
des_mac <= {des_mac[39:0],gmii_rxd}; //目的MAC地址
else if(cnt == 5'd12)
eth_type[15:8] <= gmii_rxd; //以太网协议类型
else if(cnt == 5'd13) begin
eth_type[7:0] <= gmii_rxd;
cnt <= 5'd0;
//判断MAC地址是否为开发板MAC地址或者公共地址
if(((des_mac == BOARD_MAC) ||(des_mac == 48'hff_ff_ff_ff_ff_ff))
&& eth_type[15:8] == ETH_TYPE[15:8] && gmii_rxd == ETH_TYPE[7:0])
skip_en <= 1'b1;
else
error_en <= 1'b1;
end
else;
end
else;
end
st_ip_head : begin
if(gmii_rx_dv) begin //cnt ip首20个byte计数
cnt <= cnt + 5'd1;
if(cnt == 5'd0)
ip_head_byte_num <= {gmii_rxd[3:0],2'd0}; // ip首位长度表示有多少个(4字节 ) 这里就是多少位了
else if (cnt == 5'd2)
total_length[15:8] <= gmii_rxd;
else if (cnt == 5'd3)
total_length[7:0] <= gmii_rxd;
else if (cnt == 5'd4)
//有效数据字节长度,(IP首部20个字节,icmp首部8个字节,所以减去28)
icmp_data_length <= total_length - 16'd28;
else if(cnt == 5'd9) begin
if(gmii_rxd != ICMP_TYPE) begin
//如果当前接收的数据不是ICMP协议,停止解析数据
error_en <= 1'b1;
cnt <= 5'd0;
end
end
else if((cnt >= 5'd16) && (cnt <= 5'd18))
des_ip <= {des_ip[23:0],gmii_rxd}; //目的IP地址
else if(cnt == 5'd19) begin
des_ip <= {des_ip[23:0],gmii_rxd};
//判断IP地址是否为开发板IP地址
if((des_ip[23:0] == BOARD_IP[31:8])
&& (gmii_rxd == BOARD_IP[7:0])) begin
skip_en <=1'b1;
cnt <= 5'd0;
end
else begin
//IP错误,停止解析数据
error_en <= 1'b1;
cnt <= 5'd0;
end
end
else;
end
else;
end
st_icmp_head : begin
if(gmii_rx_dv) begin //cnt ICMP首8个byte计数
cnt <= cnt + 5'd1;
if(cnt == 5'd0)
icmp_type <= gmii_rxd;
else if(cnt == 5'd1)
icmp_code <= gmii_rxd ;
else if(cnt == 5'd2)
icmp_checksum[15:8] <= gmii_rxd;
else if(cnt == 5'd3)
icmp_checksum[7:0] <= gmii_rxd;
else if(cnt == 5'd4)
icmp_id[15:8] <= gmii_rxd;
else if(cnt == 5'd5)
icmp_id[7:0] <= gmii_rxd;
else if(cnt == 5'd6)
icmp_seq[15:8] <= gmii_rxd;
else if(cnt == 5'd7)begin
icmp_seq[7:0] <= gmii_rxd;
//判断ICMP报文类型是否是回显请求
if(icmp_type == ECHO_REQUEST) begin
skip_en <=1'b1;
cnt <= 5'd0;
end
else begin
//ICMP报文类型错误,停止解析数据
error_en <= 1'b1;
cnt <= 5'd0;
end
end
else;
end
else;
end
st_rx_data : begin
//接收数据
if(gmii_rx_dv) begin
rec_en_cnt <= rec_en_cnt + 2'd1;
icmp_rx_cnt <= icmp_rx_cnt + 16'd1;
rec_data <= gmii_rxd;
rec_en <= 1'b1;
//判断接收到数据的奇偶个数
if (icmp_rx_cnt == icmp_data_length - 1) begin
icmp_rx_data_d0 <= 8'h00;
if(icmp_data_length[0]) //判断接收到数据是否为奇数个数
reply_checksum_add <= {8'd0,gmii_rxd} + reply_checksum_add;
else
reply_checksum_add <= {icmp_rx_data_d0,gmii_rxd} + reply_checksum_add;
end
else if(icmp_rx_cnt < icmp_data_length) begin
icmp_rx_data_d0 <= gmii_rxd;
icmp_rx_cnt <= icmp_rx_cnt + 16'd1;
if (icmp_rx_cnt[0] == 1'b1)
reply_checksum_add <= {icmp_rx_data_d0,gmii_rxd} + reply_checksum_add;
else
reply_checksum_add <= reply_checksum_add;
end
else;
if(icmp_rx_cnt == icmp_data_length - 16'd1) begin
skip_en <= 1'b1; //有效数据接收完成
icmp_rx_cnt <= 16'd0;
rec_en_cnt <= 2'd0;
rec_pkt_done <= 1'b1;
rec_byte_num <= icmp_data_length;
end
else;
end
else;
end
st_rx_end : begin //单包数据接收完成
rec_en <= 1'b0;
if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)begin
reply_checksum <= reply_checksum_add ;
skip_en <= 1'b1;
reply_checksum_add <= 32'd0;
end
else;
end
default : ;
endcase
end
end
endmodule
icmp_tx.v
module icmp_tx(
input clk , //时钟信号
input rst_n , //复位信号,低电平有效
input [31:0] reply_checksum, //ICMP数据部分校验和
input [15:0] icmp_id , //ICMP标识符
input [15:0] icmp_seq , //ICMP序列号
input tx_start_en , //以太网开始发送信号
input [ 7:0] tx_data , //以太网待发送数据
input [15:0] tx_byte_num , //以太网发送的有效字节数
input [47:0] des_mac , //发送的目标MAC地址
input [31:0] des_ip , //发送的目标IP地址
input [31:0] crc_data , //CRC校验数据
input [7:0] crc_next , //CRC下次校验完成数据
output reg tx_done , //以太网发送完成信号
output reg tx_req , //读数据请求信号
output reg gmii_tx_en , //GMII输出数据有效信号
output reg [7:0] gmii_txd , //GMII输出数据
output reg crc_en , //CRC开始校验使能
output reg crc_clr //CRC数据复位信号
);
//parameter define
//开发板MAC地址 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55;
//开发板IP地址 192.168.1.123
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd123};
//目的MAC地址 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
//目的IP地址 192.168.1.102
parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
//状态机状态定义
localparam st_idle = 8'b0000_0001; //初始状态,等待开始发送信号
localparam st_check_sum = 8'b0000_0010; //IP首部校验和
localparam st_check_icmp = 8'b0000_0100; //ICMP首部+数据校验
localparam st_preamble = 8'b0000_1000; //发送前导码+帧起始界定符
localparam st_eth_head = 8'b0001_0000; //发送以太网帧头
localparam st_ip_head = 8'b0010_0000; //发送IP首部+ICMP首部
localparam st_tx_data = 8'b0100_0000; //发送数据
localparam st_crc = 8'b1000_0000; //发送CRC校验值
//以太网类型定义
localparam ETH_TYPE = 16'h0800 ; //以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+ICMP首部8个字节
//所以数据至少46-20-8=18个字节
localparam MIN_DATA_NUM = 16'd18;
//parameter define
//ICMP报文类型:回显应答
parameter ECHO_REPLY = 8'h00;
//reg define
reg [7:0] cur_state ; //1
reg [7:0] next_state ; //1
reg [7:0] preamble[7:0] ; //前导码
reg [7:0] eth_head[13:0] ; //以太网首部
reg [31:0] ip_head[6:0] ; //IP首部 + ICMP首部
reg start_en_d0 ; //1
reg start_en_d1 ; //1
reg start_en_d2 ; //1
reg [15:0] tx_data_num ; //发送的有效数据字节个数 //1
reg [15:0] total_num ; //总字节数 //1
reg trig_tx_en ; //1
reg skip_en ; //控制状态跳转使能信号 //1
reg [4:0] cnt ; //1
reg [31:0] check_buffer ; //ip首部校验和 //1
reg [31:0] check_buffer_icmp ; //ip首部校验和 //1
reg [1:0] tx_bit_sel ; //1
reg [15:0] data_cnt ; //发送数据个数计数器 //1
reg tx_done_t ; //1
reg [4:0] real_add_cnt ; //以太网数据实际多发的字节数 //1
//wire define
wire pos_start_en ;//开始发送数据上升沿 //1
wire [15:0] real_tx_data_num;//实际发送的字节数(以太网最少字节要求) //1
//*****************************************************
//** main code
//*****************************************************
assign pos_start_en = (~start_en_d2) & start_en_d1;
assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM)
? tx_data_num : MIN_DATA_NUM;
//采tx_start_en的上升沿
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
start_en_d0 <= 1'b0;
start_en_d1 <= 1'b0;
start_en_d2 <= 1'b0;
end
else begin
start_en_d0 <= tx_start_en;
start_en_d1 <= start_en_d0;
start_en_d2 <= start_en_d1;
end
end
寄存数据有效字节
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_data_num <= 16'd0;
total_num <= 16'd0;
end
else begin
if(pos_start_en && cur_state==st_idle) begin
//数据长度
tx_data_num <= tx_byte_num;
//IP长度:有效数据+IP首部长度(20bytes)+ICMP首部长度(8bytes)
total_num <= tx_byte_num + 16'd28;
end
else;
end
end
//触发发送信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
trig_tx_en <= 1'b0;
else
trig_tx_en <= pos_start_en;
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cur_state <= st_idle;
else
cur_state <= next_state;
end
always @(*) begin
next_state = st_idle;
case(cur_state)
st_idle : begin //等待发送数据
if(skip_en)
next_state = st_check_sum;
else
next_state = st_idle;
end
st_check_sum: begin //IP首部校验
if(skip_en)
next_state = st_check_icmp;
else
next_state = st_check_sum;
end
st_check_icmp: begin //ICMP首部校验
if(skip_en)
next_state = st_preamble;
else
next_state = st_check_icmp;
end
st_preamble : begin //发送前导码+帧起始界定符
if(skip_en)
next_state = st_eth_head;
else
next_state = st_preamble;
end
st_eth_head : begin //发送以太网首部
if(skip_en)
next_state = st_ip_head;
else
next_state = st_eth_head;
end
st_ip_head : begin //发送IP首部+icmp首部
if(skip_en)
next_state = st_tx_data;
else
next_state = st_ip_head;
end
st_tx_data : begin //发送数据
if(skip_en)
next_state = st_crc;
else
next_state = st_tx_data;
end
st_crc: begin //发送CRC校验值
if(skip_en)
next_state = st_idle;
else
next_state = st_crc;
end
default : next_state = st_idle;
endcase
end
//发送数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
skip_en <= 1'b0;
cnt <= 5'd0;
check_buffer <= 32'd0;
check_buffer_icmp <= 32'd0;
ip_head[1][31:16] <= 16'd0;
tx_bit_sel <= 2'b0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
gmii_txd <= 8'd0;
tx_req <= 1'b0;
tx_done_t <= 1'b0;
data_cnt <= 16'd0;
real_add_cnt <= 5'd0;
//初始化数组
//前导码 7个8'h55 + 1个8'hd5
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地址
eth_head[0] <= DES_MAC[47:40];
eth_head[1] <= DES_MAC[39:32];
eth_head[2] <= DES_MAC[31:24];
eth_head[3] <= DES_MAC[23:16];
eth_head[4] <= DES_MAC[15:8];
eth_head[5] <= DES_MAC[7:0];
//源MAC地址
eth_head[6] <= BOARD_MAC[47:40];
eth_head[7] <= BOARD_MAC[39:32];
eth_head[8] <= BOARD_MAC[31:24];
eth_head[9] <= BOARD_MAC[23:16];
eth_head[10] <= BOARD_MAC[15:8];
eth_head[11] <= BOARD_MAC[7:0];
//以太网类型
eth_head[12] <= ETH_TYPE[15:8];
eth_head[13] <= ETH_TYPE[7:0];
end
else begin
skip_en <= 1'b0;
crc_en <= 1'b0;
gmii_tx_en <= 1'b0;
tx_done_t <= 1'b0;
case(next_state)
st_idle : begin
if(trig_tx_en) begin
skip_en <= 1'b1;
//版本号:4 首部长度:5(单位:32bit,20byte/4=5)
ip_head[0] <= {8'h45,8'h00,total_num};
//16位标识,每次发送累加1
ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
//bit[15:13]: 010表示不分片
ip_head[1][15:0] <= 16'h4000;
//8'h80:表示生存时间
//8'd01:1代表ICMP,2代表IGMP,6代表TCP,17代表UDP
ip_head[2] <= {8'h80,8'd01,16'h0000};
//源IP地址
ip_head[3] <= BOARD_IP;
//目的IP地址
if(des_ip != 32'd0)
ip_head[4] <= des_ip;
else
ip_head[4] <= DES_IP;
// 8位icmp TYPE ,8位 icmp CODE
ip_head[5][31:16] <= {ECHO_REPLY,8'h00};
//16位identifier 16位sequence
ip_head[6] <= {icmp_id,icmp_seq};
//更新MAC地址
if(des_mac != 48'b0) begin
//目的MAC地址
eth_head[0] <= des_mac[47:40];
eth_head[1] <= des_mac[39:32];
eth_head[2] <= des_mac[31:24];
eth_head[3] <= des_mac[23:16];
eth_head[4] <= des_mac[15:8];
eth_head[5] <= des_mac[7:0];
end
else;
end
else;
end
st_check_sum: begin //IP首部校验
cnt <= cnt + 5'd1;
if(cnt == 5'd0) begin
check_buffer <= ip_head[0][31:16] + ip_head[0][15:0]
+ ip_head[1][31:16] + ip_head[1][15:0]
+ ip_head[2][31:16] + ip_head[2][15:0]
+ ip_head[3][31:16] + ip_head[3][15:0]
+ ip_head[4][31:16] + ip_head[4][15:0];
end
else if(cnt == 5'd1) //可能出现进位,累加一次
check_buffer <= check_buffer[31:16] + check_buffer[15:0];
else if(cnt == 5'd2) begin //可能再次出现进位,累加一次
check_buffer <= check_buffer[31:16] + check_buffer[15:0];
end
else if(cnt == 5'd3) begin //按位取反
skip_en <= 1'b1;
cnt <= 5'd0;
ip_head[2][15:0] <= ~check_buffer[15:0];
end
else;
end
st_check_icmp: begin //ICMP首部+数据校验
cnt <= cnt + 5'd1;
if(cnt == 5'd0) begin
check_buffer_icmp <= ip_head[5][31:16]
+ ip_head[6][31:16] + ip_head[6][15:0]
+ reply_checksum;
end
else if(cnt == 5'd1) //可能出现进位,累加一次
check_buffer_icmp <= check_buffer_icmp[31:16] + check_buffer_icmp[15:0];
else if(cnt == 5'd2) begin //可能再次出现进位,累加一次
check_buffer_icmp <= check_buffer_icmp[31:16] + check_buffer_icmp[15:0];
end
else if(cnt == 5'd3) begin //按位取反
skip_en <= 1'b1;
cnt <= 5'd0;
// ICMP:16位校验和
ip_head[5][15:0] <= ~check_buffer_icmp[15:0];
end
else;
end
st_preamble : begin //发送前导码+帧起始界定符
gmii_tx_en <= 1'b1;
gmii_txd <= preamble[cnt];
if(cnt == 5'd7) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
st_eth_head : begin //发送以太网首部
gmii_tx_en <= 1'b1;
crc_en <= 1'b1;
gmii_txd <= eth_head[cnt];
if (cnt == 5'd13) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
st_ip_head : begin //发送IP首部
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
tx_bit_sel <= tx_bit_sel + 2'd1;
if(tx_bit_sel == 3'd0)
gmii_txd <= ip_head[cnt][31:24];
else if(tx_bit_sel == 3'd1)
gmii_txd <= ip_head[cnt][23:16];
else if(tx_bit_sel == 3'd2) begin
gmii_txd <= ip_head[cnt][15:8];
if(cnt == 5'd6) begin
//提前读请求数据,等待数据有效时发送
tx_req <= 1'b1;
end
end
else if(tx_bit_sel == 3'd3) begin
gmii_txd <= ip_head[cnt][7:0];
if(cnt == 5'd6) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
else;
end
st_tx_data : begin //发送数据
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
gmii_txd <= tx_data;
tx_bit_sel <= 3'd0;
if(data_cnt < tx_data_num - 16'd1)
data_cnt <= data_cnt + 16'd1;
else if(data_cnt == tx_data_num - 16'd1)begin
//如果发送的有效数据少于18个字节,在后面填补充位
//补充的值为最后一次发送的有效数据
if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)
real_add_cnt <= real_add_cnt + 5'd1;
else begin
skip_en <= 1'b1;
data_cnt <= 16'd0;
real_add_cnt <= 5'd0;
end
end
else;
if(data_cnt == tx_data_num - 16'd2)
tx_req <= 1'b0;
else ;
end
st_crc : begin //发送CRC校验值
gmii_tx_en <= 1'b1;
tx_bit_sel <= tx_bit_sel + 3'd1;
tx_req <= 1'b0;
if(tx_bit_sel == 3'd0)
gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
else if(tx_bit_sel == 3'd1)
gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],~crc_data[19],
~crc_data[20], ~crc_data[21], ~crc_data[22],~crc_data[23]};
else if(tx_bit_sel == 3'd2) begin
gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],~crc_data[11],
~crc_data[12], ~crc_data[13], ~crc_data[14],~crc_data[15]};
end
else if(tx_bit_sel == 3'd3) begin
gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
tx_done_t <= 1'b1;
skip_en <= 1'b1;
end
else;
end
default :;
endcase
end
end
//发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
tx_done <= 1'b0;
crc_clr <= 1'b0;
end
else begin
tx_done <= tx_done_t;
crc_clr <= tx_done_t;
end
end
endmodule
rgmii_rx.v
module rgmii_rx(
input idelay_clk , // 200Mhz鏃堕挓锛孖DELAY鏃堕挓
// 杩欎竴娈垫槸浠ュお缃慠GNII鎺ュ彛
input rgmii_rxc , // RGMII鎺ユ敹鏃堕挓
input rgmii_rx_ctl , // RGMII鎺ユ敹鏃堕挓鎺у埗淇″彿
input [3 : 0] rgmii_rxd , // RGMII鎺ユ敹鏁版嵁
// 浠ュお锟�? GMII鎺ュ彛
output gmii_rx_clk ,
output gmii_rx_dv ,
output [7 : 0] gmii_rxd
);
// -------------------------------------------//
// ------parameter and define--------------- //
// -------------------------------------------//
parameter IDELAY_VALUE = 0 ;
wire rgmii_rxc_bufg ; //鍏ㄥ眬鏃堕挓缂撳啿
wire rgmii_rxc_bufio ; //鍏ㄥ眬鏃堕挓IO缂撳瓨 -- 鎺ュ湪浜咮UFIO鍚庨潰锟�?
wire rgmii_rx_ctl_delay ;
wire [3 : 0] rgmii_rxd_delay ;
wire [1 : 0] gmii_rxdv_t ; // 涓や綅GMII鎺ユ敹淇″彿
// ---------------------------------------------//
// ------------ main code ---------------------//
//----------------------------------------------//
assign gmii_rx_clk = rgmii_rxc_bufg ;
assign gmii_rx_dv = gmii_rxdv_t[0] & gmii_rxdv_t[1] ;
// 鍏ㄥ眬鏃堕挓缂撳瓨
BUFG BUFG_inst(
.I (rgmii_rxc) ,
.O (rgmii_rxc_bufg)
);
// 鍏ㄥ眬鏃堕挓缂撳瓨
BUFIO BUFIO_inst (
.I (rgmii_rxc),
.O (rgmii_rxc_bufio)
);
//锟斤拷锟斤拷锟斤拷时锟斤拷锟斤拷
// Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
(* IODELAY_GROUP = "rgmii_rx_delay" *)
IDELAYCTRL IDELAYCTRL_inst (
.RDY(), // 1-bit output: Ready output
.REFCLK(idelay_clk), // 1-bit input: Reference clock input
.RST(1'b0) // 1-bit input: Active high reset input
);
//rgmii_rx_ctl锟斤拷锟斤拷锟斤拷时锟斤拷双锟截诧拷锟斤拷
(* IODELAY_GROUP = "rgmii_rx_delay" *)
IDELAYE2 #(
.IDELAY_TYPE ("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.IDELAY_VALUE (IDELAY_VALUE), // Input delay tap setting (0-31)
.REFCLK_FREQUENCY(200.0) // IDELAYCTRL clock input frequency in MHz
)
u_delay_rx_ctrl (
.CNTVALUEOUT (), // 5-bit output: Counter value output
.DATAOUT (rgmii_rx_ctl_delay),// 1-bit output: Delayed data output
.C (1'b0), // 1-bit input: Clock input
.CE (1'b0), // 1-bit input: enable increment/decrement
.CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
.CNTVALUEIN (5'b0), // 5-bit input: Counter value input
.DATAIN (1'b0), // 1-bit input: Internal delay data input
.IDATAIN (rgmii_rx_ctl), // 1-bit input: Data input from the I/O
.INC (1'b0), // 1-bit input: Increment / Decrement tap delay
.LD (1'b0), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register
.REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
);
//锟斤拷锟斤拷双锟截诧拷锟斤拷锟侥达拷锟斤拷
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),// "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) u_iddr_rx_ctl (
.Q1 (gmii_rxdv_t[0]), // 1-bit output for positive edge of clock
.Q2 (gmii_rxdv_t[1]), // 1-bit output for negative edge of clock
.C (rgmii_rxc_bufio), // 1-bit clock input
.CE (1'b1), // 1-bit clock enable input
.D (rgmii_rx_ctl_delay), // 1-bit DDR data input
.R (1'b0), // 1-bit reset
.S (1'b0) // 1-bit set
);
//rgmii_rxd锟斤拷锟斤拷锟斤拷时锟斤拷双锟截诧拷锟斤拷
genvar i;
generate for (i=0; i<4; i=i+1)
(* IODELAY_GROUP = "rgmii_rx_delay" *)
begin : rxdata_bus
//锟斤拷锟斤拷锟斤拷时
(* IODELAY_GROUP = "rgmii_rx_delay" *)
IDELAYE2 #(
.IDELAY_TYPE ("FIXED"), // FIXED,VARIABLE,VAR_LOAD,VAR_LOAD_PIPE
.IDELAY_VALUE (IDELAY_VALUE), // Input delay tap setting (0-31)
.REFCLK_FREQUENCY(200.0) // IDELAYCTRL clock input frequency in MHz
)
u_delay_rxd (
.CNTVALUEOUT (), // 5-bit output: Counter value output
.DATAOUT (rgmii_rxd_delay[i]),// 1-bit output: Delayed data output
.C (1'b0), // 1-bit input: Clock input
.CE (1'b0), // 1-bit input: enable increment/decrement
.CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion
.CNTVALUEIN (5'b0), // 5-bit input: Counter value input
.DATAIN (1'b0), // 1-bit input: Internal delay data input
.IDATAIN (rgmii_rxd[i]), // 1-bit input: Data input from the I/O
.INC (1'b0), // 1-bit input: Inc/Decrement tap delay
.LD (1'b0), // 1-bit input: Load IDELAY_VALUE input
.LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register
.REGRST (1'b0) // 1-bit input: Active-high reset tap-delay
);
//锟斤拷锟斤拷双锟截诧拷锟斤拷锟侥达拷锟斤拷
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),// "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1 (1'b0), // Initial value of Q1: 1'b0 or 1'b1
.INIT_Q2 (1'b0), // Initial value of Q2: 1'b0 or 1'b1
.SRTYPE ("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) u_iddr_rxd (
.Q1 (gmii_rxd[i]), // 1-bit output for positive edge of clock
.Q2 (gmii_rxd[4+i]), // 1-bit output for negative edge of clock
.C (rgmii_rxc_bufio), // 1-bit clock input rgmii_rxc_bufio
.CE (1'b1), // 1-bit clock enable input
.D (rgmii_rxd_delay[i]), // 1-bit DDR data input
.R (1'b0), // 1-bit reset
.S (1'b0) // 1-bit set
);
end
endgenerate
endmodule
rgmii_tx.v
module rgmii_tx(
// GMII 发送端口
input gmii_tx_clk , // GMII 发送信号
input gmii_tx_en , // GMII 输出数据有效信号
input [7 : 0] gmii_txd ,
//RGMII 发送端口
output rgmii_txc ,
output rgmii_tx_ctl ,
output [3 : 0] rgmii_txd
);
// --------------------------------------------- //
// -------------- main code -------------------- //
// --------------------------------------------- //
assign rgmii_txc = gmii_tx_clk ; // 输出时钟
// 输出双沿采样
ODDR #(
.DDR_CLK_EDGE ("SAME_EDGE") ,
.INIT (1'b0) ,
.SRTYPE ("SYNC")
) ODDR_inst (
.Q (rgmii_tx_ctl) ,
.C (gmii_tx_clk) ,
.CE (1'b1) ,
.D1 (gmii_tx_en) , // positive egde
.D2 (gmii_tx_en) , // negative edge
.R (1'b0) ,
.S (1'b0)
);
genvar i;
generate for (i=0; i<4; i=i+1)
begin : txdata_bus
ODDR #(
.DDR_CLK_EDGE ("SAME_EDGE") ,
.INIT (1'b0),
.SRTYPE ("SYNC")
) ODDR_inst (
.Q (rgmii_txd[i]) ,
.C (gmii_tx_clk) ,
.CE (1'b1) ,
.D1 (gmii_txd[i]) ,
.D2 (gmii_txd[4+i]) ,
.R (1'b0) ,
.S (1'b0)
);
end
endgenerate
endmodule
其中icmp_tx.v icmp_rx.v 与icmp_top.v 有tb如下
`timescale 1ns/1ns
module tb_icmp ;
parameter T = 8 ; // 鏃堕挓鍛ㄦ湡 8ns
parameter OP_CYCLE = 100 ; // 鎿嶄綔鍛ㄦ湡 鍙戦?佸懆鏈熼棿闅?
//寮?鍙戞澘MAC鍦板潃 00-11-22-33-44-55
parameter BOARD_MAC = 48'h00_11_22_33_44_55 ;
//寮?鍙戞澘IP鍦板潃 192.168.1.10
parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10} ;
//鐩殑MAC鍦板潃 ff_ff_ff_ff_ff_ff
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff ;
//鐩殑IP鍦板潃 192.168.1.10
parameter DES_IP = {8'd192,8'd168,8'd1,8'd10} ;
// =============================================================//
// define and parameter //
// =============================================================//
//reg define
reg gmii_clk ; //鏃堕挓淇″彿
reg sys_rst_n ; //澶嶄綅淇″彿
reg tx_start_en ;
reg [31:0] tx_data ;
reg [15:0] tx_byte_num ;
reg [47:0] des_mac ;
reg [31:0] des_ip ;
reg [3:0] flow_cnt ;
reg [13:0] delay_cnt ;
//wire define
wire gmii_rx_clk ; //GMII鎺ユ敹鏃堕挓
wire gmii_rx_dv ; //GMII鎺ユ敹鏁版嵁鏈夋晥淇″彿
wire [7:0] gmii_rxd ; //GMII鎺ユ敹鏁版嵁
wire gmii_tx_clk ; //GMII鍙戦?佹椂閽?
wire gmii_tx_en ; //GMII鍙戦?佹暟鎹娇鑳戒俊鍙?
wire [7:0] gmii_txd ; //GMII鍙戦?佹暟鎹?
wire tx_done ;
wire tx_req ;
// ------------------------- main code --------------------------- //
assign gmii_rx_clk = gmii_clk ;
assign gmii_tx_clk = gmii_clk ;
assign gmii_rx_dv = gmii_tx_en ;
assign gmii_rxd = gmii_txd ;
initial
begin
gmii_clk = 0 ;
sys_rst_n = 0 ;
#(T+1)
sys_rst_n = 1 ;
end
//
always #(T/2) gmii_clk = ~gmii_clk ;
always@(posedge gmii_clk or negedge sys_rst_n)
begin
if(sys_rst_n == 0)
begin
tx_start_en <= 0 ;
tx_data <= 0 ;
tx_byte_num <= 0 ;
des_mac <= 0 ;
des_ip <= 0 ;
delay_cnt <= 0 ;
flow_cnt <= 0 ;
end
else
begin
case(flow_cnt)
0 :
begin
flow_cnt <= flow_cnt + 1 ;
end
1 :
begin
tx_start_en <= 1 ;
tx_byte_num <= 20 ;
flow_cnt <= flow_cnt + 1 ;
end
2:
begin
tx_start_en <= 0 ; // 鑴夊啿淇″彿
flow_cnt <= flow_cnt + 1 ;
end
3 :
begin
if(tx_req == 1)
begin
tx_data <= tx_data + 1 ;
end
if(tx_done == 1)
begin
flow_cnt <= flow_cnt + 1 ;
tx_data <= 0 ;
end
end
4 : // 鎷夐珮绛夊緟
begin
delay_cnt <= delay_cnt + 1 ;
if(delay_cnt == OP_CYCLE - 1)
flow_cnt <= flow_cnt + 1 ;
end
5:
begin
tx_start_en <= 1 ;
tx_byte_num <= 28 ;
flow_cnt <= flow_cnt + 1 ;
end
6:
begin
tx_start_en <= 0 ;
flow_cnt <= flow_cnt + 1 ;
end
7:
begin
if(tx_req == 1)
tx_data <= tx_data + 1 ;
if(tx_done == 1)
begin
flow_cnt <= flow_cnt + 1 ;
tx_data <= 0 ;
end
end
default:
;
endcase
end
end
icmp u_icmp(
.rst_n ( sys_rst_n ),
.gmii_rx_clk ( gmii_rx_clk ),
.gmii_rx_dv ( gmii_rx_dv ),
.gmii_rxd ( gmii_rxd ),
.gmii_tx_clk ( gmii_tx_clk ),
.gmii_tx_en ( gmii_tx_en ),
.gmii_txd ( gmii_txd ),
.rec_pkt_done ( ),
.rec_en ( ),
.rec_data ( ),
.rec_byte_num ( ),
.tx_start_en ( tx_start_en ),
.tx_data ( tx_data ),
.tx_byte_num ( tx_byte_num ),
.des_mac ( des_mac ),
.des_ip ( des_ip ),
.tx_done ( tx_done ),
.tx_req ( tx_req )
);
endmodule
其中的eth_icmp_test.v模块中的clk_wiz_0 与 fifo_generator_0都是xilinx官方IP