(1)状态转移图:
(2)IP数据包格式:
(3)UDP数据包格式:
(4)以太网发送模块代码:
module udp_tx
(
input wire gmii_txc ,
input wire reset_n ,
input wire tx_start_en , //以太网开始发送信号
input wire [31:0] tx_data , //以太网待发送数据,来自fifo模块,因此更新需要同步tx_req信号
input wire [15:0] tx_byte_num , //以太网发送的有些数据字节个数
input wire [31:0] crc_data , //CRC校验数据
input wire [7:0] crc_next , //CRC下次校验完成数据
output reg [7:0] gmii_txd , //32位转8位后的输出数据
output reg gmii_tx_en , //GMII传输有效信号
output reg tx_done , //以太网传输一帧数据完成的标志信号
output reg tx_req , //读取数据请求信号
output reg crc_en , //CRC开始校验使能
output reg crc_clr //CRC数据复位信号
);
/*-----------参数定义------------*/
//开发板MAC地址(48位)
parameter BOARO_MAC = 48'hff_ff_ff_ff_ff_ff ;
//开发板IP地址(32位)
parameter BOARO_IP = {8'd0,8'd0,8'd0,8'd0} ;
//目的MAC地址(48位)
parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff ;
//目的IP地址(32位)
parameter DES_IP = {8'd0,8'd0,8'd0,8'd0} ;
//状态机状态定义 独热吗编码
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_UDP_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协议
//寄存器定义
reg [7:0] preamble[7:0] ; //7个字节的前导码和一个字节的帧起始界定符
reg [7:0] eth_head[13:0] ; //14个字节的以太网帧头数据
reg [31:0] ip_udp_head[6:0] ; //IP首部(5个字节) + UDP首部(2个字节)
reg start_en_d0 ; //以太网开始发送信号打一拍后信号
reg start_en_d1 ; //以太网开始发送信号打两拍后信号
reg [31:0] check_sum ; //IP首部校验和
reg [15:0] data_len ; //有效数据字节长度
reg [15:0] ip_udp_data_len ; //ip首部加上udp首部加上有效数据字节的总长数
reg [15:0] udp_len ; //UDP长度
reg [6:0] cur_state ; //当前状态
reg [6:0] next_state ; //下一个状态
reg sw_en ; //状态跳转脉冲信号
reg [4:0] cnt ; //数据计数器
reg [1:0] cnt_tx_bit_sel ; //最小时间单位计数器
reg [15:0] cnt_data ; //有效数据发送个数计数器
reg [4:0] cnt_add ; //以太网实际多发的字节个数计数器
reg tx_done_t ; //传输完成信号(需要再延迟一拍处理)
reg posed_start_en_reg ; //上升沿开始有效脉冲信号
wire posed_start_en ; //开始信号上升沿设计
wire [15:0] real_tx_date_num ; //实际发送字节个数
/*------------开始有效信号设计------------*/
always@(posedge gmii_txc or negedge reset_n)begin
if(!reset_n)begin
start_en_d0 <= 1'd0;
start_en_d1 <= 1'd0;
end
else begin
start_en_d0 <= tx_start_en;
start_en_d1 <= start_en_d0;
end
end
assign posed_start_en = ((start_en_d0) && (!start_en_d1));
always@(posedge gmii_txc or negedge reset_n)
if(!reset_n)
posed_start_en_reg <= 1'd0;
else
posed_start_en_reg <= posed_start_en ;
/*------------以太网UDP最少数据字节设计------------*/
//以太网数据最少46字节,IP首部20个字节,UDP首部8个字节,因此数据至少46-20-8 = 18 字节
assign real_tx_date_num = (data_len >= 16'd18) ? data_len : 16'd18;
/*------------data_len 、 ip_udp_data_len 、 udp_len寄存器设计------------*/
always@(posedge gmii_txc or negedge reset_n)begin
if(!reset_n)begin
data_len <= 16'd0;
ip_udp_data_len <= 16'd0;
udp_len <= 16'd0;
end
else begin
if(posed_start_en && cur_state == ST_IDLE)begin
data_len <= tx_byte_num ;
ip_udp_data_len <= tx_byte_num + 16'd20 + 16'd8 ;
udp_len <= tx_byte_num + 16'd8 ;
end
end
end
/*--------------三段式状态机编写主程序--------*/
//第一段:时序逻辑描述状态转移
always@(posedge gmii_txc or negedge reset_n)begin
if(!reset_n)
cur_state <= ST_IDLE ;
else
cur_state <= next_state ;
end
//第二段:组合逻辑判断状态机转移条件
always@(*)begin
next_state = ST_IDLE ;
case(cur_state)
ST_IDLE :
if(sw_en)
next_state = ST_CHECK_SUM;
else
next_state = ST_IDLE;
ST_CHECK_SUM :
if(sw_en)
next_state = ST_PREAMBLE;
else
next_state = ST_CHECK_SUM;
ST_PREAMBLE :
if(sw_en)
next_state = ST_ETH_HEAD;
else
next_state = ST_PREAMBLE;
ST_ETH_HEAD :
if(sw_en)
next_state = ST_IP_UDP_HEAD;
else
next_state = ST_ETH_HEAD;
ST_IP_UDP_HEAD :
if(sw_en)
next_state = ST_TX_DATA;
else
next_state = ST_IP_UDP_HEAD;
ST_TX_DATA :
if(sw_en)
next_state = ST_CRC;
else
next_state = ST_TX_DATA;
ST_CRC :
if(sw_en)
next_state = ST_IDLE;
else
next_state = ST_CRC;
default:next_state = ST_IDLE ;
endcase
end
//第三段:时序逻辑描述寄存器变量和状态输出
always@(posedge gmii_txc or negedge reset_n)begin
if(!reset_n)begin
sw_en <= 1'd0;
cnt <= 5'd0;
check_sum <= 32'd0;
ip_udp_head[1][31:16] <= 16'd0; //把IP首部中的标识部分初始化为16'd0;
cnt_tx_bit_sel <= 2'd0;
crc_en <= 1'd0;
gmii_tx_en <= 1'd0;
gmii_txd <= 8'd0;
tx_req <= 1'd0;
tx_done_t <= 1'd0;
cnt_data <= 16'd0;
cnt_add <= 5'd0;
//前导码和帧起始界定符初始化
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] <= BOARO_MAC[47:40];
eth_head[7] <= BOARO_MAC[39:32];
eth_head[8] <= BOARO_MAC[31:24];
eth_head[9] <= BOARO_MAC[23:16];
eth_head[10] <= BOARO_MAC[15:8];
eth_head[11] <= BOARO_MAC[7:0];
//协议类型
eth_head[12] <= ETH_TYPE[15:8];
eth_head[13] <= ETH_TYPE[7:0];
end
else begin
sw_en <= 1'd0;
tx_req <= 1'd0;
crc_en <= 1'd0;
gmii_tx_en <= 1'd0;
tx_done_t <= 1'd0;
case(next_state)
ST_IDLE :begin
if(posed_start_en_reg)begin
sw_en <= 1'd1;
ip_udp_head[0] <= {8'h45,8'h00,ip_udp_data_len}; //版本:4 + 首部长度:20/4 = 5 + ip首部加上udp首部加上有效数据字节的总长数
ip_udp_head[1][31:16] <= ip_udp_head[1][31:16] + 1'd1;//16位的标识,每发一个数据累加1
ip_udp_head[1][15:0] <= 16'b010_00000_0000_0000; //前三位表示标记,010表示不分片
ip_udp_head[2] <= {8'h40,8'h11,16'h0}; //生存时间:40 + 协议:11——UDP协议 01——TCP协议 + 首部校验和(先设置为0)
ip_udp_head[3] <= BOARO_IP; //源IP地址
ip_udp_head[4] <= DES_IP; //目的IP地址
ip_udp_head[5] <= {16'd1234,16'd1234}; //源端口号(1234) + 目的端口号(1234)
ip_udp_head[6] <= {udp_len,16'h0000} ; //UDP长度 + UDP校验和
end
end
ST_CHECK_SUM :begin //IP首部校验和计算
cnt <= cnt + 5'd1;
if(cnt == 5'd0)
check_sum <= ip_udp_head[0][31:16] + ip_udp_head[0][15:0]
+ ip_udp_head[1][31:16] + ip_udp_head[1][15:0]
+ ip_udp_head[2][31:16] + ip_udp_head[2][15:0]
+ ip_udp_head[3][31:16] + ip_udp_head[3][15:0]
+ ip_udp_head[4][31:16] + ip_udp_head[4][15:0];
else if(cnt == 5'd1) //可能出现进位,因此累加一次
check_sum <= check_sum[31:16] + check_sum[15:0];
else if(cnt == 5'd2) //可能再次出现进位,再累加一次
check_sum <= check_sum[31:16] + check_sum[15:0];
else if(cnt == 5'd3)begin
sw_en <= 1'd1;
cnt <= 5'd0;
ip_udp_head[2][15:0] <= ~check_sum[15:0]; //ip_udp_head[2][15:0] 是原先设置成0的首部校验和部位
end
end
ST_PREAMBLE :begin
gmii_tx_en <= 1'd1;
gmii_txd <= preamble[cnt];
if(cnt == 5'd7)begin
sw_en <= 1'd1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
ST_ETH_HEAD :begin
gmii_tx_en <= 1'd1;
crc_en <= 1'd1;
gmii_txd <= eth_head[cnt];
if(cnt == 5'd13)begin
sw_en <= 1'd1;
cnt <= 5'd0;
end
else
cnt <= cnt + 5'd1;
end
ST_IP_UDP_HEAD:begin //发送IP首部 + UDP首部
gmii_tx_en <= 1'd1;
crc_en <= 1'd1;
cnt_tx_bit_sel <= cnt_tx_bit_sel + 2'd1;
if(cnt_tx_bit_sel == 2'd0)
gmii_txd <= ip_udp_head[cnt][31:24];
else if(cnt_tx_bit_sel == 2'd1)
gmii_txd <= ip_udp_head[cnt][23:16];
else if(cnt_tx_bit_sel == 2'd2)begin
gmii_txd <= ip_udp_head[cnt][15:8];
if(cnt == 5'd6)
//提前发送读取请求,等待有效数据发送至该模块
tx_req <= 1'd1;
end
else if(cnt_tx_bit_sel == 2'd3)begin
gmii_txd <= ip_udp_head[cnt][7:0];
if(cnt == 5'd6)begin
cnt <= 5'd0;
sw_en <= 1'd1;
end
else
cnt <= cnt + 5'd1;
end
end
ST_TX_DATA:begin
crc_en <= 1'd1;
gmii_tx_en <= 1'd1;
cnt_tx_bit_sel <= cnt_tx_bit_sel + 2'd1;
//cnt_data 、cnt_add设计
if(cnt_data < data_len - 16'd1)
cnt_data <= cnt_data + 16'd1;
else if(cnt_data == data_len - 16'd1)begin
//如果发送的有效数据少于18个字节,则在后面填补充位
//补充的值为最后一次发送的有效数据
gmii_txd <= 8'd0;
/*在Verilog中,赋值语句是按照代码的顺序执行的,后面的赋值语句会覆盖前面的赋值
因此gmii_txd <= 8'd0这条语句,虽然将gmii_txd赋值为8'd0,但根据cnt_tx_bit_sel的值,
后面的赋值语句会根据条件逻辑来覆盖这个初始的赋值*/
if(cnt_data + cnt_add < real_tx_date_num - 16'd1)
cnt_add <= cnt_add + 5'd1;
else begin
sw_en <= 1'd1;
cnt_data <= 16'd0;
cnt_add <= 5'd0;
cnt_tx_bit_sel <= 2'd0;
end
end
//gmii_txd设计
if(cnt_tx_bit_sel == 2'd0)
gmii_txd <= tx_data[31:24];
else if(cnt_tx_bit_sel == 2'd1)
gmii_txd <= tx_data[23:16];
else if(cnt_tx_bit_sel == 2'd2)begin
gmii_txd <= tx_data[15:8];
if(cnt_data != data_len - 16'd1)
tx_req <= 1'd1;
end
else if(cnt_tx_bit_sel == 2'd3)
gmii_txd <= tx_data[7:0];
end
ST_CRC :begin //发送CRC校验值
gmii_tx_en <= 1'd1;
cnt_tx_bit_sel <= cnt_tx_bit_sel + 2'd1;
if(cnt_tx_bit_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(cnt_tx_bit_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(cnt_tx_bit_sel == 2'd2)
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]};
else if(cnt_tx_bit_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'd1;
sw_en <= 1'd1;
end
end
default: ;
endcase
end
end
/*--------------发送完成信号以及crc值复位信号--------*/
always@(posedge gmii_txc or negedge reset_n)begin
if(!reset_n)begin
tx_done <= 1'd0;
crc_clr <= 1'd0;
end
else begin
tx_done <= tx_done_t;
crc_clr <= tx_done_t;
end
end
endmodule
(5)以太网CRC校验模块代码:
module crc32_d8
(
input gmii_txc , //时钟信号
input reset_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 gmii_txc or negedge reset_n) begin
if(!reset_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
(6)仿真代码:
`timescale 1ns / 1ps
module tb_udp_tx;
reg gmii_txc ;
reg reset_n ;
reg tx_start_en ;
reg [31:0] tx_data ;
reg [31:0] data_mem[2:0] ;
reg [15:0] data_cnt ;
wire [31:0] crc_next ;
wire [31:0] crc_data ;
wire [7:0] gmii_txd ;
wire gmii_tx_en ;
wire tx_done ;
wire tx_req ;
wire crc_en ;
wire crc_clr ;
initial gmii_txc = 1'd1;
always#10 gmii_txc = ~gmii_txc;
initial begin
reset_n <= 1'd0;
tx_start_en <= 1'd0;
#200;
reset_n <= 1'd1;
#100;
tx_start_en <= 1'd1;
#40;
tx_start_en <= 1'd0;
#4000;
$stop;
end
always@(posedge gmii_txc or negedge reset_n)
if(!reset_n)begin
data_mem[0] <= 32'h00_00_00_00;
data_mem[1] <= 32'h00_00_00_00;
data_mem[2] <= 32'h00_00_00_00;
end
else begin
data_mem[0] <= 32'h00_11_22_33;
data_mem[1] <= 32'h44_55_66_77;
data_mem[2] <= 32'h88_99_aa_bb;
end
always@(posedge gmii_txc or negedge reset_n)
if(!reset_n)
data_cnt <= 16'd0;
else if(tx_req)
data_cnt <= data_cnt + 16'd1;
else
data_cnt <= data_cnt ;
always@(posedge gmii_txc or negedge reset_n)
if(!reset_n)
tx_data <= 32'd0;
else if(tx_req)
tx_data <= data_mem[data_cnt];
else
tx_data <= tx_data;
defparam udp_tx_inst.BOARO_MAC = 48'h12_34_56_78_9a_bc;
defparam udp_tx_inst.BOARO_IP = 32'ha9_fe_01_17;
defparam udp_tx_inst.DES_MAC = 48'hff_ff_ff_ff_ff_ff;
defparam udp_tx_inst.DES_IP = 32'ha9_fe_00_45;
udp_tx udp_tx_inst
(
.gmii_txc (gmii_txc ),
.reset_n (reset_n ),
.tx_start_en (tx_start_en ), //以太网开始发送信号
.tx_data (tx_data ), //以太网待发送数据,来自fifo模块,因此更新需要同步tx_req信号
.tx_byte_num (16'd10 ), //以太网发送的有些数据字节个数
.crc_data (crc_data ), //CRC校验数据
.crc_next (crc_next[31:24] ), //CRC下次校验完成数据
.gmii_txd (gmii_txd ), //32位转8位后的输出数据
.gmii_tx_en (gmii_tx_en ), //GMII传输有效信号
.tx_done (tx_done ), //以太网传输一帧数据完成的标志信号
.tx_req (tx_req ), //读取数据请求信号
.crc_en (crc_en ), //CRC开始校验使能
.crc_clr (crc_clr ) //CRC数据复位信号
);
crc32_d8 crc32_d8_inst
(
.gmii_txc (gmii_txc ), //时钟信号
.reset_n (reset_n ), //复位信号,低电平有效
.data (gmii_txd ), //输入待校验8位数据
.crc_en (crc_en ), //crc使能,开始校验标志
.crc_clr (crc_clr ), //crc数据复位信号
.crc_data (crc_data ), //CRC校验数据
.crc_next (crc_next ) //CRC下次校验完成数据
);
endmodule
(7)仿真波形: