61.以太网数据回环实验(4)以太网数据收发器发送模块

news2024/12/27 10:41:03

(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)仿真波形:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2113182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络层_计算机网络

文章目录 网络层数据平面路由器工作原理网际协议&#xff08;*IP*&#xff09;IPv4IPv6DHCP NAT 控制平面路由选择算法因特网中自治系统内部的路由选择&#xff1a;OSPFISP 之间的路由选择&#xff1a;BGP ICMPSNMP 网络层 尽力而为服务&#xff08;best-effort services&…

尚品汇-支付宝介绍、跳转支付订单页面实现(四十六)

目录&#xff1a; &#xff08;1&#xff09;支付宝介绍 &#xff08;1&#xff09;支付宝介绍 &#xff08;3&#xff09;显示付款页面信息 &#xff08;5&#xff09;创建支付控制器PaymentController &#xff08;1&#xff09;支付宝介绍 支付宝简介 支付宝&#xf…

没资料的屏幕怎么点亮?思路分享

这次尝试调通一个没资料的屏幕&#xff0c;型号是HYT13264&#xff0c;这个是淘宝上面的老王2.9元屏&#xff0c;成色很好但是长期库存没有资料和代码能点亮&#xff0c;仅仅只有一个引脚定义。这里我使用Arduino Nano作为控制器尝试点亮这个模块。 首先&#xff0c;已知别人找…

当水泵遇上物联网:智能水务新时代的浪漫交响

在当代科技的宏伟乐章中&#xff0c;物联网&#xff08;IoT&#xff09;技术宛如一位技艺高超的指挥家&#xff0c;引领着各行各业迈向智能化的新纪元。当这股创新浪潮涌向古老的水务行业时&#xff0c;一场前所未有的“智能水务”革命便悄然上演&#xff0c;而水泵——这一传统…

vue3 自定义指令 directive

1、官方说明&#xff1a;https://cn.vuejs.org/guide/reusability/custom-directives 除了 Vue 内置的一系列指令 (比如 v-model 或 v-show) 之外&#xff0c;Vue 还允许你注册自定义的指令 (Custom Directives)。 我们已经介绍了两种在 Vue 中重用代码的方式&#xff1a;组件和…

828华为云征文 | 华为云Flexusx实例,高效部署Servas书签管理工具的优选平台

需要了解 本文章主要讲述在 华为云Flexus X 实例上使用docker快速部署Servas&#xff0c;一款功能强大的自托管书签管理工具&#xff0c;专为追求高效与个性化的用户设计。选择合适的云服务器&#xff1a; 本文采用的是 华为云服务器 Flexus X 实例&#xff08;推荐使用&#x…

Vue 中 watch 和 watchEffect 的区别

watch 和 watcheffect 都是 vue 中用于监视响应式数据的 api&#xff0c;它们的区别在于&#xff1a;watch 用于监视特定响应式属性并执行回调函数。watcheffect 用于更通用的响应式数据监视&#xff0c;但回调函数中不能更新响应式数据。Vue 中 watch 和 watchEffect 的区别 …

C++ 日历计算器的实现

日历计算器 创建一个日期类对运算符进行重载代码 创建一个日期类 年月日为类的成员变量&#xff0c;所以放到私有区域&#xff0c;又因为成员变量为内置类型&#xff0c;编译器自动生成的默认构造函数对其不做处理&#xff0c;所以需要我们显示定义一个构造函数&#xff0c;而…

亿发:中小型制造企业数字化转型典型场景、痛点、解决方案

随着全球制造业的不断发展&#xff0c;中小型制造企业正面临前所未有的挑战和机遇。数字化转型成为了企业提升竞争力、优化生产效率、应对市场变化的关键路径。然而&#xff0c;对于资源相对有限的中小型制造企业而言&#xff0c;数字化转型并非易事。他们在推进转型的过程中往…

视频的编码与传输 学习笔记2 信息论

说白了&#xff0c;关键点就三个&#xff1a;信源&#xff0c;压缩与信道 DMS就是无记忆的信源&#xff0c;该输出什么就输出什么。 马尔卡夫信源&#xff0c;准确来讲是m马尔卡夫&#xff0c;会受到前m个状态的影响。&#xff08;妈的&#xff0c;马尔卡弗还在追我&#xff0c…

【工具分享】针对加解密综合利用后渗透工具 - DecryptTools

下载地址&#xff1a; 链接: https://pan.quark.cn/s/2e451bd65d79工具介绍 支持22种OA、CMS 加解密密码查询功能 万户OA 用友NC 金蝶EAS 蓝凌OA 致远OA 宏景ERP 湖南强智 金和jc6 瑞友天翼 金和C6 Navicat 华天动力 FinalShell 亿赛通 帆软报表 H3C CAS Weblogic 金蝶云星空…

基于SSM+Vue+MySQL的可视化高校公寓管理系统

系统展示 管理员界面 宿管界面 学生界面 系统背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。这样的…

51单片机-第十二节-LCD1602液晶显示屏

一、LCD1602介绍&#xff1a; LCD1602是一种字符型液晶显示屏&#xff0c;可以显示ASCII码的标准字符和其他的内置特殊字符。 显示容量&#xff1a;16*2个字符&#xff0c;每个字符为5*7点阵。 二、引脚及应用电路&#xff1a; 其中&#xff1a;D0-7这8位数据是接在P0引脚上…

聚焦2024数博会|与天空卫士一起探索AI与数据安全的融合应用

中国国际大数据产业博览会&#xff08;简称数博会&#xff09;&#xff0c;是全球首个以大数据为主题的博览会&#xff0c;自2015年创办以来&#xff0c;经过多年的深厚沉淀&#xff0c;数博会已发展成为国际知名、引领前沿趋势的专业展示合作平台。 2024年8月28日至30日&#…

T1打卡——mnist手写数字识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 1.定义GPU import tensorflow as tfgpustf.config.list_physical_devices("GPU")if gpus:gpu0gpus[0]tf.config.experimental.set_memort_groth(gp…

重卡换电连接器的应用

电动汽车换电模式涉及在专门充电站集中存储和充电大量电池&#xff0c;实现统一配送&#xff0c;并在换电站对电动汽车进行快速电池更换服务&#xff0c;或整合充电、物流、调配和换电服务。新能源汽车面临续航限制和配套设施不完善等问题&#xff0c;影响了其大规模推广。电池…

Java创建线程(5种方法)

操作系统提供api操作线程 线程本身是操作系统提供的&#xff0c;操作系统提供API让我们操作线程&#xff0c;JVM对操作系统api进行了封装&#xff0c;在线程这一部分&#xff0c;就提供了Thread类&#xff0c;表示线程。 创建线程 创建一个MyThread类&#xff08;类的名字不…

基于PI控制算法的异步感应电机转速控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于PI控制算法的异步感应电机转速控制系统simulink建模与仿真。PI控制器是一种经典的线性控制器&#xff0c;它通过将控制量的比例部分和积分部分相结合来实现对系统输出的调…

【C-实践】网络聊天室(1.0)

概述 使用了 tcp epoll &#xff0c;实现网络聊天室 1.0 版&#xff0c;用户的显示框和输入框在一起 2.0 版&#xff0c;用户的显示框与输入框分离 功能 主要功能&#xff1a;用户连接服务器&#xff0c;就会自动进入网络聊天室与其他在线用户一起聊天 服务器搭建 创建用户数…

探索EasyCVR与AI技术深度融合:视频汇聚平台的新增长点

随着5G、AI、边缘计算、物联网&#xff08;IoT&#xff09;、云计算等技术的快速发展&#xff0c;万物互联已经从概念逐渐转变为现实&#xff0c;AIoT&#xff08;物联网人工智能&#xff09;的新时代正在加速到来。在这一背景下&#xff0c;视频技术作为信息传输和交互的重要手…