1 实验任务
上位机通过网口调试助手发送数据给
FPGA
,
FPGA
通过
PL
端以太网接口接收数据并将接收到的数据发送给上位机,完成以太网 UDP
数据的环回。
2 系统设计
PLL时钟模块将PHY芯片的RGMII输入数据时钟125Mhz,转为偏移90度的125Mhz的时钟用于RGMII发送数据时钟。
GMII接口转RGMII接口模块发送时将8位数据转为4位数据,接收时将4位数据转为8位数据。
ARP模块解析电脑上位机发送给开发板ARP请求命令,并返回开发板的 MAC 地址。
UDP顶层模块实现了以太网UDP数据包的接收、发送以及CRC校验的功能。
以太网控制模块根据输入的ARP接收完成信号,控制ARP顶层模块返回ARP应答信号,然后切换为UDP协议对数据进行发送和接收。
FIFO模块用来缓存以太网单次接收的数据,由于所使用的GMII接收时钟和GMII发送时钟实际上为同一个时钟,因此使用的是同步FIFO。
3 Verilog代码
3.1 udp回环通信顶层模块
`timescale 1ns / 1ps
//以太网通信UDP通信环回顶层模块
module udp_loop_top (
input sys_rst_n, //系统复位信号,低电平有效
//以太网RGMII接口
input eth_rxc, //RGMII输入数据时钟 125mhz
input eth_rx_ctl, //RGMII输入数据有效信号
input [3:0] eth_rxd, //RGMII输入数据
output eth_txc, //RGMII输出数据时钟
output eth_tx_ctl, //RGMII输出数据有效信号
output [3:0] eth_txd //RGMII输出数据
);
//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 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; //接收到目的MAC地址
wire [31:0] src_ip; //接收到目的IP地址
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 udp_gmii_tx_en; //UDP GMII输出数据有效信号
wire [ 7:0] udp_gmii_txd; //UDP GMII输出数据
wire rec_pkt_done; //UDP单包数据接收完成信号
wire rec_en; //UDP接收的数据使能信号
wire [31:0] rec_data; //UDP接收的数据
wire [15:0] rec_byte_num; //UDP接收的有效字节数 单位:byte
wire [15:0] tx_byte_num; //UDP发送的有效字节数 单位:byte
wire udp_tx_done; //UDP发送完成信号
wire tx_req; //UDP读数据请求信号
wire [31:0] tx_data; //UDP待发送数据
//*****************************************************
//** 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_txc = clk_125m_deg; //pll时钟输出的偏移90度的125Mhz的时钟成为RGMII发送数据时钟
//MMCM/PLL
clk_wiz u_clk_wiz (
// Clock out ports
.clk_out1(clk_125m_deg), // output clk_out1
// Status and control signals
.reset (~sys_rst_n), // input reset
.locked (locked), // output locked
// Clock in ports
.clk_in1 (rgmii_txc) // input clk_in1:RGMII发送时钟
);
//GMII接口转RGMII接口
gmii_to_rgmii u_gmii_to_rgmii (
.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 (rgmii_txc),
.rgmii_tx_ctl(eth_tx_ctl),
.rgmii_txd (eth_txd)
);
//ARP通信
arp_top #(
.BOARD_MAC(BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP),
.DES_MAC (DES_MAC),
.DES_IP (DES_IP)
) 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)
);
//UDP通信
udp_top #(
.BOARD_MAC(BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP),
.DES_MAC (DES_MAC),
.DES_IP (DES_IP)
) u_udp (
.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 (udp_gmii_tx_en),
.gmii_txd (udp_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 (udp_tx_done),
.tx_req (tx_req)
);
//同步FIFO
sync_fifo_2048x32b u_sync_fifo_2048x32b (
.clk (gmii_rx_clk), // input wire clk
.srst (~sys_rst_n), // input wire rst
.din (rec_data), // input wire [31 : 0] din
.wr_en(rec_en), // input wire wr_en
.rd_en(tx_req), // input wire rd_en
.dout (tx_data), // output wire [31 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
//以太网控制模块
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),
.udp_tx_start_en(tx_start_en),
.udp_tx_done (udp_tx_done),
.udp_gmii_tx_en (udp_gmii_tx_en),
.udp_gmii_txd (udp_gmii_txd),
.gmii_tx_en(gmii_tx_en),
.gmii_txd (gmii_txd)
);
endmodule
3.2 GMII接口转RGMII接口模块
`timescale 1ns / 1ps
//GMII接口转RGMII接口模块
//发送时将8位数据转为4位数据,接收时将4位数据转为8位数据
module gmii_to_rgmii (
//以太网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发送数据
);
//*****************************************************
//** main code
//*****************************************************
assign gmii_tx_clk = gmii_rx_clk;
//RGMII接收
rgmii_rx u_rgmii_rx (
.gmii_rx_clk (gmii_rx_clk),
.rgmii_rxc (rgmii_rxc),
.rgmii_rx_ctl(rgmii_rx_ctl),
.rgmii_rxd (rgmii_rxd),
.gmii_rx_dv(gmii_rx_dv),
.gmii_rxd (gmii_rxd)
);
//RGMII发送
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
3.3 RGMII接收模块
`timescale 1ns / 1ps
//RGMII接收模块
module rgmii_rx (
//以太网RGMII接口
input rgmii_rxc, //RGMII接收时钟
input rgmii_rx_ctl, //RGMII接收数据控制信号
input [3:0] rgmii_rxd, //RGMII接收数据
//以太网GMII接口
output gmii_rx_clk, //GMII接收时钟
output gmii_rx_dv, //GMII接收数据有效信号
output [7:0] gmii_rxd //GMII接收数据
);
//wire define
wire rgmii_rxc_bufg; //全局时钟缓存
wire rgmii_rxc_bufio; //全局时钟IO缓存
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), // 1-bit input: Clock input
.O(rgmii_rxc_bufg) // 1-bit output: Clock output
);
//全局时钟IO缓存
BUFIO BUFIO_inst (
.I(rgmii_rxc), // 1-bit input: Clock input
.O(rgmii_rxc_bufio) // 1-bit output: Clock output
);
//将输入的上下边沿DDR信号,转换成两位单边沿SDR信号
IDDRE1 #(
.DDR_CLK_EDGE ("SAME_EDGE_PIPELINED"),// IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)
.IS_CB_INVERTED(1'b0), // Optional inversion for CB
.IS_C_INVERTED(1'b0) // Optional inversion for C
) IDDRE1_inst (
.Q1(gmii_rxdv_t[0]), // 1-bit output: Registered parallel output 1
.Q2(gmii_rxdv_t[1]), // 1-bit output: Registered parallel output 2
.C (rgmii_rxc_bufio), // 1-bit input: High-speed clock
.CB(~rgmii_rxc_bufio), // 1-bit input: Inversion of High-speed clock C
.D (rgmii_rx_ctl), // 1-bit input: Serial Data Input
.R (1'b0) // 1-bit input: Active High Async Reset
);
genvar i;
generate
for (i = 0; i < 4; i = i + 1) begin : rxdata_bus
IDDRE1 #(
.DDR_CLK_EDGE ("SAME_EDGE_PIPELINED"), // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)
.IS_CB_INVERTED(1'b0), // Optional inversion for CB
.IS_C_INVERTED(1'b0) // Optional inversion for C
) IDDRE1_inst (
.Q1(gmii_rxd[i]), // 1-bit output: Registered parallel output 1
.Q2(gmii_rxd[4+i]), // 1-bit output: Registered parallel output 2
.C (rgmii_rxc_bufio), // 1-bit input: High-speed clock
.CB(~rgmii_rxc_bufio), // 1-bit input: Inversion of High-speed clock C
.D (rgmii_rxd[i]), // 1-bit input: Serial Data Input
.R (1'b0) // 1-bit input: Active High Async Reset
);
end
endgenerate
endmodule
3.4 RGMII发送模块
`timescale 1ns / 1ps
//RGMII发送模块
module rgmii_tx (
//GMII发送端口
input gmii_tx_clk, //GMII发送时钟
input gmii_tx_en, //GMII输出数据有效信号
input [7:0] gmii_txd, //GMII输出数据
//RGMII发送端口
output rgmii_txc, //RGMII发送数据时钟
output rgmii_tx_ctl, //RGMII输出数据有效信号
output [3:0] rgmii_txd //RGMII输出数据
);
//*****************************************************
//** main code
//*****************************************************
assign rgmii_txc = gmii_tx_clk;
//输出双沿采样寄存器 (rgmii_tx_ctl)
ODDRE1 #(
.IS_C_INVERTED(1'b0), // Optional inversion for C
.IS_D1_INVERTED(1'b0), // Unsupported, do not use
.IS_D2_INVERTED(1'b0), // Unsupported, do not use
.SIM_DEVICE ("ULTRASCALE"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,ULTRASCALE_PLUS_ES2)
.SRVAL(1'b0) // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
) ODDRE1_tx_ctl (
.Q (rgmii_tx_ctl), // 1-bit output: Data output to IOB
.C (gmii_tx_clk), // 1-bit input: High-speed clock input
.D1(gmii_tx_en), // 1-bit input: Parallel data input 1
.D2(gmii_tx_en), // 1-bit input: Parallel data input 2
.SR(1'b0) // 1-bit input: Active High Async Reset
);
genvar i;
generate
for (i = 0; i < 4; i = i + 1) begin : txdata_bus
ODDRE1 #(
.IS_C_INVERTED(1'b0), // Optional inversion for C
.IS_D1_INVERTED(1'b0), // Unsupported, do not use
.IS_D2_INVERTED(1'b0), // Unsupported, do not use
.SIM_DEVICE("ULTRASCALE"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,ULTRASCALE_PLUS_ES2)
.SRVAL(1'b0) // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
) ODDRE1_inst (
.Q (rgmii_txd[i]), // 1-bit output: Data output to IOB
.C (gmii_tx_clk), // 1-bit input: High-speed clock input
.D1(gmii_txd[i]), // 1-bit input: Parallel data input 1
.D2(gmii_txd[4+i]), // 1-bit input: Parallel data input 2
.SR(1'b0) // 1-bit input: Active High Async Reset
);
end
endgenerate
endmodule
3.5 ARP顶层模块
`timescale 1ns / 1ps
//arp顶层模块
module arp_top (
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
3.6 ARP接收模块
`timescale 1ns / 1ps
//arp接收模块
module arp_rx (
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
//开发板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 = 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; //操作码
reg rx_done_t; //ARP接收完成信号
//*****************************************************
//** 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;
rx_done_t <= 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;
rx_done_t <= 1'b0;
case (next_state)
st_idle: begin //检测到第一个8'h55
if ((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55)) skip_en <= 1'b1;
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
end
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
end
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;
rx_done_t <= 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
end
end
st_rx_end: begin
cnt <= 5'd0;
//单包数据接收完成
if (gmii_rx_dv == 1'b0 && skip_en == 1'b0) skip_en <= 1'b1;
end
default: ;
endcase
end
end
//输出arp_rx_done信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) arp_rx_done <= 1'b0;
else arp_rx_done <= rx_done_t;
end
endmodule
3.7 ARP发送模块
`timescale 1ns / 1ps
//arp发送模块
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;
//前导码+SFD,共8个字节
reg [7:0] preamble [ 7:0];
//以太网首部,共14个字节
reg [7:0] eth_head [13:0];
//ARP数据,共28个字节
reg [7:0] arp_data [27:0];
reg tx_en_d0; //arp_tx_en信号延时
reg tx_en_d1;
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_d1) & tx_en_d0;
//对arp开始发送信号延时打拍两次,用于采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;
end else begin
tx_en_d0 <= arp_tx_en;
tx_en_d1 <= tx_en_d0;
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
if (arp_tx_type == 1'b0) arp_data[7] <= 8'h01; //ARP请求
else arp_data[7] <= 8'h02; //ARP应答
end
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
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
3.8 CRC32校验模块
`timescale 1ns / 1ps
//CRC32校验模块
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
3.9 UDP顶层模块
`timescale 1ns / 1ps
//udp顶层模块
module udp_top (
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 rec_pkt_done, //以太网单包数据接收完成信号
output rec_en, //以太网接收的数据使能信号
output [31:0] rec_data, //以太网接收的数据
output [15:0] rec_byte_num, //以太网接收的有效字节数 单位:byte
input tx_start_en, //以太网开始发送信号
input [31:0] tx_data, //以太网待发送数据
input [15:0] tx_byte_num, //以太网发送的有效字节数 单位:byte
input [47:0] des_mac, //发送的目标MAC地址
input [31:0] des_ip, //发送的目标IP地址
output tx_done, //以太网发送完成信号
output tx_req //读数据请求信号
);
//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;
//以太网接收模块
udp_rx #(
.BOARD_MAC(BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP)
) u_udp_rx (
.clk (gmii_rx_clk),
.rst_n (rst_n),
.gmii_rx_dv (gmii_rx_dv),
.gmii_rxd (gmii_rxd),
.rec_pkt_done(rec_pkt_done),
.rec_en (rec_en),
.rec_data (rec_data),
.rec_byte_num(rec_byte_num)
);
//以太网发送模块
udp_tx #(
.BOARD_MAC(BOARD_MAC), //参数例化
.BOARD_IP (BOARD_IP),
.DES_MAC (DES_MAC),
.DES_IP (DES_IP)
) u_udp_tx (
.clk (gmii_tx_clk),
.rst_n (rst_n),
.tx_start_en(tx_start_en),
.tx_data (tx_data),
.tx_byte_num(tx_byte_num),
.des_mac (des_mac),
.des_ip (des_ip),
.crc_data (crc_data),
.crc_next (crc_next[31:24]),
.tx_done (tx_done),
.tx_req (tx_req),
.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
3.10 UDP接收模块
`timescale 1ns / 1ps
//以太网数据接收模块
//UDP接收模块简化了处理流程,不需要进行IP首部和CRC循环冗余校验。
//它仅核对目的MAC地址和IP地址是否与开发板地址匹配。
//解析顺序为:前导码+帧起始界定符→以太网帧头→IP首部→UDP首部→UDP数据(有效数据)→接收结束
//为了与IP数据报32位对齐,接收模块执行了8位到32位数据的转换。
module udp_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 [31:0] rec_data, //以太网接收的数据
output reg [15:0] rec_byte_num //以太网接收的有效字节数 单位:byte
);
//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_udp_head = 7'b001_0000; //接收UDP首部
localparam st_rx_data = 7'b010_0000; //接收有效数据
localparam st_rx_end = 7'b100_0000; //接收结束
localparam ETH_TYPE = 16'h0800; //以太网协议类型 IP协议
localparam UDP_TYPE = 8'd17; //UDP协议类型
//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] udp_byte_num; //UDP长度
reg [15:0] data_byte_num; //数据长度
reg [15:0] data_cnt; //有效数据计数
reg [ 1:0] rec_en_cnt; //8bit转32bit计数器
//*****************************************************
//** 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_udp_head;
else if (error_en) next_state = st_rx_end;
else next_state = st_ip_head;
end
st_udp_head: begin //接收UDP首部
if (skip_en) next_state = st_rx_data;
else next_state = st_udp_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;
udp_byte_num <= 16'd0;
data_byte_num <= 16'd0;
data_cnt <= 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_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;
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
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
end
end
st_ip_head: begin //解析IP首部
if (gmii_rx_dv) begin
cnt <= cnt + 5'd1;
if (cnt == 5'd0) ip_head_byte_num <= {gmii_rxd[3:0], 2'd0}; //寄存IP首部长度
else if (cnt == 5'd9) begin
if (gmii_rxd != UDP_TYPE) begin
//如果当前接收的数据不是UDP协议,停止解析数据
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
if (cnt == ip_head_byte_num - 1'b1) begin
skip_en <= 1'b1;
cnt <= 5'd0;
end
end else begin
//IP错误,停止解析数据
error_en <= 1'b1;
cnt <= 5'd0;
end
end else if (cnt == ip_head_byte_num - 1'b1) begin
skip_en <= 1'b1; //IP首部解析完成
cnt <= 5'd0;
end
end
end
st_udp_head: begin //解析UDP首部
if (gmii_rx_dv) begin
cnt <= cnt + 5'd1;
if (cnt == 5'd4) udp_byte_num[15:8] <= gmii_rxd; //解析UDP字节长度
else if (cnt == 5'd5) udp_byte_num[7:0] <= gmii_rxd;
else if (cnt == 5'd7) begin
//有效数据字节长度,(UDP首部8个字节,所以减去8)
data_byte_num <= udp_byte_num - 16'd8;
skip_en <= 1'b1;
cnt <= 5'd0;
end
end
end
st_rx_data: begin //接收数据,转换成32bit
if (gmii_rx_dv) begin
data_cnt <= data_cnt + 16'd1;
rec_en_cnt <= rec_en_cnt + 2'd1;
if (data_cnt == data_byte_num - 16'd1) begin
skip_en <= 1'b1; //有效数据接收完成
data_cnt <= 16'd0;
rec_en_cnt <= 2'd0;
rec_pkt_done <= 1'b1;
rec_en <= 1'b1;
rec_byte_num <= data_byte_num;
end
//先收到的数据放在了rec_data的高位,所以当数据不是4的倍数时,
//低位数据为无效数据,可根据有效字节数来判断(rec_byte_num)
if (rec_en_cnt == 2'd0) rec_data[31:24] <= gmii_rxd;
else if (rec_en_cnt == 2'd1) rec_data[23:16] <= gmii_rxd;
else if (rec_en_cnt == 2'd2) rec_data[15:8] <= gmii_rxd;
else if (rec_en_cnt == 2'd3) begin
rec_en <= 1'b1;
rec_data[7:0] <= gmii_rxd;
end
end
end
st_rx_end: begin //单包数据接收完成
//UDP接收模块在前导码、MAC地址或IP地址错误时,应跳至st_rx_end状态而非st_idle,以避免将有效数据误判为前导码。
//只有在eth_rxdv信号为0,即单包数据接收完毕时,st_rx_end才跳转至st_idle,准备接收下一批数据。
if (gmii_rx_dv == 1'b0 && skip_en == 1'b0) skip_en <= 1'b1;
end
default: ;
endcase
end
end
endmodule
3.11 UDP发送模块
`timescale 1ns / 1ps
//以太网数据发送模块
//UDP发送模块按照UDP的数据格式发送数据,并将32位用户数据转成8位数据,也就是接收模块的逆过程。
module udp_tx (
input clk, //时钟信号
input rst_n, //复位信号,低电平有效
input tx_start_en, //以太网开始发送信号
input [31: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.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 = 7'b000_0001; //初始状态,等待开始发送信号
localparam st_check_sum = 7'b000_0010; //IP首部校验和
localparam st_preamble = 7'b000_0100; //发送前导码+帧起始界定符
localparam st_eth_head = 7'b000_1000; //发送以太网帧头
localparam st_ip_head = 7'b001_0000; //发送IP首部+UDP首部
localparam st_tx_data = 7'b010_0000; //发送数据
localparam st_crc = 7'b100_0000; //发送CRC校验值
localparam ETH_TYPE = 16'h0800; //以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam MIN_DATA_NUM = 16'd18;
localparam UDP_TYPE = 8'd17; //UDP协议类型
//reg define
reg [6:0] cur_state;
reg [6:0] next_state;
reg [7:0] preamble[7:0]; //前导码
reg [7:0] eth_head[13:0]; //以太网首部
reg [31:0] ip_head[6:0]; //IP首部 + UDP首部
reg start_en_d0;
reg start_en_d1;
reg [15:0] tx_data_num; //发送的有效数据字节个数
reg [15:0] total_num; //总字节数
reg trig_tx_en;
reg [15:0] udp_num; //UDP字节数
reg skip_en; //控制状态跳转使能信号
reg [4:0] cnt;
reg [31:0] check_buffer; //首部校验和
reg [1:0] tx_byte_sel; //32位数据转8位数据计数器
reg [15:0] data_cnt; //发送数据个数计数器
reg tx_done_t;
reg [4:0] real_add_cnt; //以太网数据实际多发的字节数
//wire define
wire pos_start_en; //开始发送数据上升沿
wire [15:0] real_tx_data_num; //实际发送的字节数(以太网最少字节要求)
//*****************************************************
//** main code
//*****************************************************
assign pos_start_en = (~start_en_d1) & start_en_d0;
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;
end else begin
start_en_d0 <= tx_start_en;
start_en_d1 <= start_en_d0;
end
end
//寄存数据有效字节
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_data_num <= 16'd0;
total_num <= 16'd0;
udp_num <= 16'd0;
end else begin
if (pos_start_en && cur_state == st_idle) begin
//数据长度
tx_data_num <= tx_byte_num;
//UDP长度:UDP首部长度 + 有效数据
udp_num <= tx_byte_num + 16'd8;
//IP长度:IP首部长度 + UDP首部 + 有效数据
total_num <= tx_byte_num + 16'd20 + 16'd8;
end
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_preamble;
else next_state = st_check_sum;
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首部+UDP首部
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;
ip_head[1][31:16] <= 16'd0;
tx_byte_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;
tx_req <= 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;
//协议:17(udp)
ip_head[2] <= {8'h40, UDP_TYPE, 16'h0};
//源IP地址
ip_head[3] <= BOARD_IP;
//目的IP地址
if (des_ip != 32'd0) ip_head[4] <= des_ip;
else ip_head[4] <= DES_IP;
//16位源端口号:1234 16位目的端口号:1234
ip_head[5] <= {16'd1234, 16'd1234};
//16位udp长度,16位udp校验和
ip_head[6] <= {udp_num, 16'h0000};
//更新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
end
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
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首部 + UDP首部
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
tx_byte_sel <= tx_byte_sel + 2'd1;
if (tx_byte_sel == 2'd0) gmii_txd <= ip_head[cnt][31:24];
else if (tx_byte_sel == 2'd1) gmii_txd <= ip_head[cnt][23:16];
else if (tx_byte_sel == 2'd2) begin
gmii_txd <= ip_head[cnt][15:8];
if (cnt == 5'd6) begin
//提前读请求数据,等待数据有效时发送
tx_req <= 1'b1;
end
end else if (tx_byte_sel == 2'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
end
st_tx_data: begin //发送数据
crc_en <= 1'b1;
gmii_tx_en <= 1'b1;
tx_byte_sel <= tx_byte_sel + 2'd1;
if (tx_byte_sel == 1'b0) gmii_txd <= tx_data[31:24];
else if (tx_byte_sel == 2'd1) gmii_txd <= tx_data[23:16];
else if (tx_byte_sel == 2'd2) begin
gmii_txd <= tx_data[15:8];
if (data_cnt != tx_data_num - 16'd2) tx_req <= 1'b1;
end else if (tx_byte_sel == 2'd3) gmii_txd <= tx_data[7:0];
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个字节,在后面填补充位
//补充的值为最后一次发送的有效数据
tx_req <= 1'b0;
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;
tx_byte_sel <= 2'd0;
end
if (real_add_cnt > 0) begin
gmii_txd <= 8'd0;
end
end
end
st_crc: begin //发送CRC校验值
gmii_tx_en <= 1'b1;
tx_byte_sel <= tx_byte_sel + 2'd1;
if (tx_byte_sel == 2'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_byte_sel == 2'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_byte_sel == 2'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_byte_sel == 2'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
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
3.12 控制模块
`timescale 1ns / 1ps
//以太网控制模块
//GMII接口的输出信号根据当前的协议切换信号来确定是发送UDP数据还是ARP数据。
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输出数据
//UDP相关端口信号
input udp_tx_start_en, //UDP开始发送信号
input udp_tx_done, //UDP发送完成信号
input udp_gmii_tx_en, //UDP GMII输出数据有效信号
input [7:0] udp_gmii_txd, //UDP GMII输出数据
//GMII发送引脚
output gmii_tx_en, //GMII输出数据有效信号
output [7:0] gmii_txd //UDP GMII输出数据
);
//reg define
reg protocol_sw; //协议切换信号,为1时正在处理UDP,为0时正在处理ARP
reg udp_tx_busy; //UDP正在发送数据标志信号
reg arp_rx_flag; //接收到ARP请求信号的标志
//*****************************************************
//** main code
//*****************************************************
assign arp_tx_type = 1'b1; //ARP发送类型固定为ARP应答,上位机发送ARP请求,获得开发板MAC地址
assign gmii_tx_en = protocol_sw ? udp_gmii_tx_en : arp_gmii_tx_en;//为1时gmii接口发送使能为udp发送使能
assign gmii_txd = protocol_sw ? udp_gmii_txd : arp_gmii_txd; //为1时gmii接口发送udp数据
//控制UDP发送忙信号
//当udp发送开始时,udp发送忙信号置为1;当udp发送完成之后或者没有发送,udp发送忙信号置为0
always @(posedge clk or negedge rst_n) begin
if (!rst_n) udp_tx_busy <= 1'b0;
else if (udp_tx_start_en) udp_tx_busy <= 1'b1;
else if (udp_tx_done) udp_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 if (protocol_sw == 1'b0) arp_rx_flag <= 1'b0;
end
//控制protocol_sw和arp_tx_en信号
//根据UDP发送开始信号和ARP请求标志来切换协议处理状态,并控制ARP发送使能
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 (udp_tx_start_en) protocol_sw <= 1'b1;
else if (arp_rx_flag && (udp_tx_busy == 1'b0)) begin
protocol_sw <= 1'b0;
arp_tx_en <= 1'b1;
end
end
end
endmodule
3.13 引脚约束
create_clock -period 8.000 -name eth_rxc [get_ports eth_rxc]
set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n]
set_property PACKAGE_PIN AH11 [get_ports sys_rst_n]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_rxd[3]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_rxd[2]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_rxd[1]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_rxd[0]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_txd[3]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_txd[2]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_txd[1]}]
set_property IOSTANDARD LVCMOS18 [get_ports {eth_txd[0]}]
set_property IOSTANDARD LVCMOS18 [get_ports eth_rx_ctl]
set_property IOSTANDARD LVCMOS18 [get_ports eth_rxc]
set_property IOSTANDARD LVCMOS18 [get_ports eth_tx_ctl]
set_property IOSTANDARD LVCMOS18 [get_ports eth_txc]
set_property PACKAGE_PIN F7 [get_ports {eth_rxd[3]}]
set_property PACKAGE_PIN G8 [get_ports {eth_rxd[2]}]
set_property PACKAGE_PIN F6 [get_ports {eth_rxd[1]}]
set_property PACKAGE_PIN G6 [get_ports {eth_rxd[0]}]
set_property PACKAGE_PIN D9 [get_ports {eth_txd[3]}]
set_property PACKAGE_PIN E9 [get_ports {eth_txd[2]}]
set_property PACKAGE_PIN E8 [get_ports {eth_txd[1]}]
set_property PACKAGE_PIN D7 [get_ports {eth_txd[0]}]
set_property PACKAGE_PIN E5 [get_ports eth_rxc]
set_property PACKAGE_PIN F8 [get_ports eth_tx_ctl]
set_property PACKAGE_PIN D6 [get_ports eth_txc]
set_property PACKAGE_PIN D5 [get_ports eth_rx_ctl]
4 验证
使用网络调试助手进行验证
本地IP设置为 192.168.1.102 端口1234
目标IP设置为 192.168.1.10 端口1234