自学内容网 自学内容网

基于FPGA的以太网设计(六)

前面分别实现了ARP协议和ICMP协议,但这俩协议都不能进行数据的传输,如果想要用以太网传输数据的话还需要实现UDP协议或者TCP协议,关于二者的差别主要包括以下几点:

1.连接性

TCP是面向连接的协议,它在传输数据之前需要建立连接(三次握手),并在数据传输完成后关闭连接(四次挥手)。这种连接是全双工的,即数据可以在两个方向上同时传输。

UDP则是无连接的协议,它不需要建立或关闭连接,发送方可以直接发送数据报,接收方也可以随时接收数据报。

2.可靠性

TCP提供可靠的传输服务。它使用确认和重传机制来确保数据正确、完整、有序地到达目的地。TCP还通过流量控制和拥塞控制机制来防止网络过载。

UDP则提供不可靠的传输服务。它不检查数据包的顺序、错误或重传。如果数据包在传输过程中丢失或损坏,UDP不会采取任何补救措施。因此,UDP通常用于对实时性要求较高、但对数据可靠性要求不高的应用(如视频流、音频流、实时游戏等)

3.头部开销

TCP的头部开销相对较大,包含更多的控制信息,如序列号、确认号、窗口大小等。

UDP的头部开销较小,只包含必要的字段,如源端口、目的端口、长度和校验和。

4.传输效率

由于TCP需要建立连接、确认数据、处理重传等,所以其传输效率相对较低。但这也使得TCP在需要可靠传输的场景下表现更好。

UDP则不需要这些额外的步骤,因为其传输效率更高。但这也意味着UDP在传输过程中可能会丢失数据。

5.应用场景

TCP通常用于需要可靠传输的场景,如文件存储、电子邮件、远程登录等。

UDP则常用于对实时性要求较高、但对数据可靠性要求不高的场景,如视频流、音频流、DNS查询、VoIP(网络电话)等。

6.流量控制和拥塞控制

TCP具有流量控制和拥塞控制机制,可以根据网络状况动态调整发送速率,以防止网络拥塞和丢包。

UDP则没有这些机制,它只负责将数据报从源端发送到目的端,则不关心网络状况和数据传输质量。

7.一对一、一对多、多对一和多对多通信

TCP通常用于一对一的通信,即一个TCP连接只能有一个发送方和一个接收方。

UDP则支持一对多、多对一和多对多的通信模式,可以实现广播和组播功能。

总而言之,UDP的优点是协议简单,传输速度快,不用进行握手,一般用于进行视频数据的传输。

UDP协议结构图如下所示:

UDP首部组成如下:

UDP首部主要包括源端口号、目的端口号、UDP总长度和校验和。

源端口号:2个字节的发送端端口号,用于区分不同的发送端口。

目的端口号:2个字节的接收端端口号。

UDP长度:UDP首部和数据段的长度,单位字节,对于接收方来说该长度其实作用不大,因为UDP数据段的长度可以通过IP首部的总长度和IP首部长度计算出来。

UDP校验和:计算方式与IP首部校验和一致,需要对UDP伪首部、UDP首部、UDP数据进行校验。伪首部包括源IP地址、目的IP地址、协议类型、UDP长度。

UDP数据组成如下图所示:

代码实现如下所示:

发送部分:

`timescale 1ns / 1ps

module udp_tx(
    input                                   clk         ,   //系统时钟
    input                                   rst_n       ,   //系统复位
    //input
    input                                   tx_start_en ,   //发送使能信号
    input                   [15:0]          tx_byte_num ,   //发送有效字节数
    input                   [7:0]           tx_data     ,   //发送数据
    input                   [47:0]          des_mac     ,   //源MAC地址
    input                   [31:0]          des_ip      ,   //源ip地址
    input                   [31:0]          crc_data    ,   //CRC校验数据
    input                   [7:0]           crc_next    ,   //CRC下次校验数据
    //ooutput
    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清零信号     
);
    //开发板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      IDLE        =       7'b000_0001     ;   //初始化状态
    localparam      CHECK_SUM   =       7'b000_0010     ;   //校验和
    localparam      PREAMBLE    =       7'b000_0100     ;   //发送前导码+SFD
    localparam      ETH_HEAD    =       7'b000_1000     ;   //发送以太网帧头
    localparam      IP_HEAD     =       7'b001_0000     ;   //发送ip首部
    localparam      TX_DATA     =       7'b010_0000     ;   //发送数据
    localparam      CRC         =       7'b100_0000     ;   //CRC校验
    localparam      ETH_TYPE    =       16'h0800        ;   //以太网协议类型 IP协议
    localparam      MIN_DATA_NUM=       16'd18          ;   //以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节,所以数据至少46-20-8=18个字节
 
    reg             [6:0]               cur_state       ;   //现态
    reg             [6:0]               next_state      ;   //次态
    reg                                 state_en        ;   //状态跳转使能
    reg             [7:0]               preamble[7:0]   ;   //前导码+帧起始界定符
    reg             [7:0]               eth_head[13:0]  ;   //以太网帧头
    reg             [31:0]              ip_head[6:0]    ;   //ip首部
    reg             [4:0]               cnt             ;   //数据发送计数器
    reg                                 start_en_r0     ;   //暂存发送使能信号
    reg                                 start_en_r1     ;
    reg                                 start_en_r2     ;
    reg             [15:0]              tx_data_num     ;   //发送有效数据字节个数
    reg             [15:0]              total_num       ;   //总字节个数
    reg                                 trig_tx_en      ;   //发送使能触发信号
    reg             [15:0]              udp_num         ;   //udp字节数
    reg             [31:0]              check_buffer    ;   //校验和
    reg             [1:0]               tx_bit_sel      ;   //
    reg             [15:0]              data_cnt        ;   //ip首部发送数据计数器
    reg                                 tx_done_t       ;   //发送结束信号寄存器
    reg             [4:0]               real_add_cnt    ;   //实际多发字节数

    wire                                posedge_start   ;   //开始发送信号上升沿
    wire            [15:0]              real_tx_data_num;   //实际发送字节数据  

    assign  posedge_start = (~start_en_r2) && start_en_r1;
    assign real_tx_data_num = (tx_data_num >= MIN_DATA_NUM) ? tx_data_num : MIN_DATA_NUM; 

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            start_en_r0 <= 1'b0;
            start_en_r1 <= 1'b0;
            start_en_r2 <= 1'b0;
        end
        else begin
            start_en_r0 <=  tx_start_en;
            start_en_r1 <=  start_en_r0;
            start_en_r2 <=  start_en_r1;
        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(posedge_start && (cur_state == IDLE))begin
                tx_data_num <= tx_byte_num;
                total_num <= tx_byte_num + 16'd28;
                udp_num <= tx_byte_num + 16'd8;
            end
            else;
        end
    end

    //发送触发信号
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            trig_tx_en <= 1'b0;
        else
            trig_tx_en <= posedge_start;
    end

    //三段式状态机第一段
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cur_state <= IDLE;
        else
            cur_state <= next_state;
    end

    //三段式状态第二段
    always @(*) begin
        next_state = IDLE;
        case (cur_state)
            IDLE:begin
                if(state_en)
                    next_state = CHECK_SUM;
                else
                    next_state = IDLE;  
            end 
            CHECK_SUM:begin
                if(state_en)
                    next_state = PREAMBLE;
                else
                    next_state = CHECK_SUM;
            end
            PREAMBLE:begin
                if(state_en)
                    next_state = ETH_HEAD;
                else
                    next_state = PREAMBLE; 
            end
            ETH_HEAD:begin
                if(state_en)
                    next_state = IP_HEAD;
                else
                    next_state = ETH_HEAD;
            end
            IP_HEAD:begin
                if(state_en)
                    next_state = TX_DATA;
                else
                    next_state = IP_HEAD;
            end
            TX_DATA:begin
                if(state_en)
                    next_state = CRC;
                else
                    next_state = TX_DATA;
            end
            CRC:begin
                if(state_en)
                    next_state = IDLE;
                else
                    next_state = CRC;
            end
            default: next_state = IDLE;
        endcase
    end

    //三段式状态机第三段
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            cnt <= 5'd0;
            check_buffer <= 32'd0;
            state_en <= 1'b0;
            ip_head[1][31:16] <= 16'd0;
            tx_bit_sel <= 2'd0;
            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;
            //初始化数组
            //前导码+帧起始界定符
            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
            crc_en <= 1'b0;
            gmii_tx_en <= 1'b0;
            tx_done_t <= 1'b0;
            state_en <= 1'b0;
            case (next_state)
                IDLE:begin
                    if(trig_tx_en)begin
                        state_en <= 1'b1;
                        //版本号:4 首部长度:5
                        ip_head[0] <= {8'h45,8'h00,total_num};
                        //16位标识,每次发送累加一
                        ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1;
                        //
                        ip_head[1][15:0] <= 16'h4000;
                        //表示udp协议
                        ip_head[2] <= {8'h40,8'd17,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;
                        //udp端口号
                        ip_head[5] <= {16'd1234,16'd1234};
                        //udp校验长度与udp校验和
                        ip_head[6] <= {udp_num,16'h0000};
                        if(des_mac != 48'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];
                        end
                        else;
                    end
                    else;
                end 
                CHECK_SUM:begin
                    cnt <= cnt + 1'b1;
                    if(cnt == 5'd0)
                        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];
                    else if(cnt == 5'd1)
                        check_buffer <= check_buffer[31:16] + check_buffer[15:0];
                    else if(cnt == 5'd2)
                        check_buffer <= check_buffer[31:16] + check_buffer[15:0];
                    else if(cnt == 5'd3)begin
                        cnt <= 5'd0;
                        ip_head[2][15:0] <= ~check_buffer[15:0];
                        state_en <= 1'b1;
                    end
                    else;
                end
                PREAMBLE:begin
                    gmii_tx_en <= 1'b1;
                    gmii_txd <= preamble[cnt];
                    if(cnt == 5'd7)begin
                        cnt <= 5'd0;
                        state_en <= 1'b1;
                    end
                    else
                        cnt <= cnt + 1'b1;
                end
                ETH_HEAD:begin
                    gmii_tx_en <= 1'b1;
                    gmii_txd <= eth_head[cnt];
                    crc_en <= 1'b1;
                    if(cnt == 5'd13)begin
                        cnt <= 5'd0;
                        state_en <= 1'b1;
                    end
                    else
                        cnt <= cnt + 1'b1;
                end
                IP_HEAD:begin
                    tx_bit_sel <= tx_bit_sel + 1'b1;
                    gmii_tx_en <= 1'b1;
                    crc_en <= 1'b1;
                    if(tx_bit_sel == 2'd0)
                        gmii_txd <= ip_head[cnt][31:24];
                    else if(tx_bit_sel == 2'd1)
                        gmii_txd <= ip_head[cnt][23:16];
                    else if(tx_bit_sel == 2'd2)begin
                        gmii_txd <= ip_head[cnt][15:8];
                        if(cnt == 5'd6)
                            tx_req <= 1'b1;//提前读请求数据
                        else;
                    end
                    else if(tx_bit_sel == 2'd3)begin
                        gmii_txd <= ip_head[cnt][7:0];
                        if(cnt == 5'd6)begin
                            cnt <= 5'd0;
                            state_en <= 1'b1;
                        end
                        else
                            cnt <= cnt + 1'b1;
                    end
                end
                TX_DATA:begin
                    crc_en <= 1'b1;
                    gmii_tx_en <= 1'b1;
                    tx_bit_sel <= tx_bit_sel + 1'b1;
                    gmii_txd <= tx_data;
                    if(data_cnt < tx_data_num - 1'b1)
                        data_cnt <= data_cnt + 1'b1;
                    else if(data_cnt == tx_data_num - 1'b1)
                        if(data_cnt + real_add_cnt < real_tx_data_num - 1'b1)
                            real_add_cnt <= real_add_cnt + 1'b1;//发送有效数据字节少于18位则在后面补充最后一次发送的有效数据
                        else begin
                            state_en <= 1'b1;
                            real_add_cnt <= 16'd0;
                            data_cnt <= 16'd0;
                            tx_bit_sel <= 2'd0;
                        end
                    else;

                    if(data_cnt == tx_data_num - 16'd2)
                        tx_req <= 1'b0;
                    else;
                end
                CRC:begin
                    gmii_tx_en <= 1'b1;
                    tx_bit_sel <= tx_bit_sel + 1'b1;
                    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;
                        state_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

仿真结果:

检测到发送开始信号上升沿,开始发送前导码和帧起始界定符。

数据发送部分。

接收部分:

`timescale 1ns / 1ps

module udp_rx(
    input                                       clk         ,   //系统时钟
    input                                       rst_n       ,   //系统复位
    //input
    input                                       gmii_rx_dv  ,   //接收数据有效
    input                   [7:0]               gmii_rxd    ,   //接收数据
    output              reg                     rec_pkt_done,   //以太网接收单包数据完成
    output              reg                     rec_en      ,   //接收使能信号
    output              reg [7:0]               rec_data    ,   
    output              reg [15:0]              rec_byte_num    //以太网接收有效字节数
);
    parameter           BOARD_MAC       =       48'h00_11_22_33_44_55       ;   //开发板MAC地址 00-11-22-33-44-55
    parameter           BOARD_IP        =       {8'd192,8'd168,8'd1,8'd10}  ;   //开发板IP地址 192.168.1.10 
    localparam          IDLE            =       7'b000_0001                 ;   //空闲状态
    localparam          PREAMBLE        =       7'b000_0010                 ;   //前导码+帧起始界定符
    localparam          ETH_HEAD        =       7'b000_0100                 ;   //接收以太网帧头
    localparam          IP_HEAD         =       7'b000_1000                 ;   //接收ip首部
    localparam          UDP_HEAD        =       7'b001_0000                 ;   //接收udp首部
    localparam          RX_DATA         =       7'b010_0000                 ;   //接收数据
    localparam          RX_DONE         =       7'b100_0000                 ;   //接收结束
    localparam          ETH_TYPE        =       16'h0800                    ;   //以太网协议类型 IP协议
    localparam          UDP_TYPE        =       8'd17                       ;   //UDP协议

    reg                 [6:0]                   cur_state                   ;   //现态
    reg                 [6:0]                   next_state                  ;   //次态
    reg                 [4:0]                   cnt                         ;   //接收数据计数器
    reg                                         state_en                    ;   //状态跳转使能
    reg                                         error_flag                  ;   //错误指示信号
    reg                 [47:0]                  des_mac                     ;   //目的MAC地址
    reg                 [31:0]                  des_ip                      ;   //目的ip地址
    reg                 [15:0]                  eth_type                    ;   //以太网类型
    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                    ;   //ip首部发送计数

    //三段式状态机第一段
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cur_state <= IDLE;
        else
            cur_state <= next_state;
    end

    //三段式状态机第二段
    always @(*) begin
        next_state = IDLE;
        case (cur_state)
            IDLE:begin
                if(state_en)
                    next_state = PREAMBLE;
                else
                    next_state = IDLE; 
            end 
            PREAMBLE:begin
                if(state_en)
                    next_state = ETH_HEAD;
                else if(error_flag)
                    next_state = RX_DONE;
                else
                    next_state = PREAMBLE; 
            end 
            ETH_HEAD:begin
                if(state_en)
                    next_state = IP_HEAD;
                else if(error_flag)
                    next_state = RX_DONE;
                else
                    next_state = ETH_HEAD; 
            end 
            IP_HEAD:begin
                if(state_en)
                    next_state = UDP_HEAD;
                else if(error_flag)
                    next_state = RX_DONE;
                else
                    next_state = IP_HEAD; 
            end 
            UDP_HEAD:begin
                if(state_en)
                    next_state = RX_DATA;
                else
                    next_state = UDP_HEAD; 
            end
            RX_DATA:begin
                if(state_en)
                    next_state = RX_DONE;
                else
                    next_state = RX_DATA; 
            end 
            RX_DONE:begin
                if(state_en)
                    next_state = IDLE;
                else
                    next_state = RX_DONE; 
            end 

            default: next_state = IDLE;
        endcase
    end

    //三段式状态机第三段
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            state_en <= 1'b0;
            error_flag <= 1'b0;
            cnt <= 5'd0;
            des_mac <= 48'd0;
            des_ip <= 32'd0;  
            eth_type <= 16'd0;
            ip_head_byte_num <= 6'd0;
            udp_byte_num <= 16'd0;
            data_byte_num <= 16'd0;
            data_cnt <= 16'd0;
            rec_data <= 32'd0;
            rec_pkt_done <= 1'b0;
            rec_en <= 1'b0;
            rec_byte_num <= 16'd0;
        end
        else begin
            state_en <= 1'b0;
            rec_pkt_done <= 1'b0;
            error_flag <= 1'b0;
            case (next_state)
                IDLE:begin 
                    if(gmii_rx_dv && (gmii_rxd == 8'h55))
                        state_en <= 1'b1;
                    else;
                end
                PREAMBLE:begin
                    if(gmii_rx_dv)begin
                        cnt <= cnt + 1'b1;
                        if((cnt < 5'd6) && (gmii_rxd != 8'h55))
                            error_flag <= 1'b1;
                        else if(cnt == 5'd6)begin
                            cnt <= 5'd0;
                            if(gmii_rxd != 8'hd5)
                                error_flag <= 1'b1;
                            else
                                state_en <= 1'b1;
                        end
                        else;
                    end
                end
                ETH_HEAD:begin
                    if(gmii_rx_dv)begin
                        cnt <= cnt + 1'b1;
                        if(cnt < 5'd6)
                            des_mac <= {des_mac[39:0],gmii_rxd};
                        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;
                            //判断目的ip地址与开发板的ip地址是否相同或者为广播地址
                            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])) 
                                state_en <= 1'b1;                                
                            else
                                error_flag <= 1'b1;
                        end
                        else;
                    end
                    else;
                end
                IP_HEAD:begin
                    if(gmii_rx_dv)begin
                        cnt <= cnt + 1'b1;
                        if(cnt == 5'd0)
                            ip_head_byte_num <= {gmii_rxd[3:0],2'b00};
                        else if(cnt == 5'd9)
                            if(gmii_rxd != UDP_TYPE)begin
                                error_flag <= 1'b1;
                                cnt <= 5'd0;
                            end
                            else;
                        else if((cnt >= 5'd16) && (cnt <= 5'd18))
                            des_ip <= {des_ip[23:0],gmii_rxd};
                        else if(cnt == 5'd19)begin
                            des_ip <= {des_ip[23:0],gmii_rxd};
                            if((des_ip[23:0] == BOARD_IP[31:8]) && (gmii_rxd == BOARD_IP[7:0]))begin
                                cnt <= 5'd0;
                                state_en <= 1'b1;
                            end
                            else begin
                                cnt <= 5'd0;
                                error_flag <= 1'b1;
                            end
                        end
                        else;
                    end
                    else;
                end
                UDP_HEAD:begin
                    if(gmii_rx_dv)begin
                        cnt <= cnt + 1'b1;
                        if(cnt == 5'd4)
                            udp_byte_num[15:8] <= gmii_rxd;
                        else if(cnt == 5'd5)
                            udp_byte_num[7:0] <= gmii_rxd;//解析UDP字节长度
                        else if(cnt == 5'd7)begin
                            data_byte_num <= udp_byte_num - 16'd8;//有效数据字节长度,UDP首部长度为8所以减去8
                            state_en <= 1'b1;
                            cnt <= 5'd0;
                        end
                        else;
                    end
                    else;
                end
                RX_DATA:begin
                    if(gmii_rx_dv)begin
                        data_cnt <= data_cnt + 1'b1;
                        rec_data <= gmii_rxd;
                        rec_en <= 1'b1;
                        if(data_cnt == data_byte_num - 16'd1)begin//有效字节接收完成
                            state_en <= 1'b1;
                            rec_pkt_done <= 1'b1;
                            data_cnt <= 16'd0;
                            rec_byte_num <= data_byte_num;
                        end
                        else;
                    end
                    else;
                end
                RX_DONE:begin
                    rec_en <= 1'b0;
                    if((gmii_rx_dv == 1'b0) && (state_en == 1'b0))
                        state_en <= 1'b1;
                    else;
                end
                default:;
            endcase
        end
    end
endmodule

仿真结果:

上板测试:

基于FPGA的UDP实现(包含源工程文件)_fpga udp-CSDN博客

TCP是什么、UDP是什么,它们有什么区别-CSDN博客


原文地址:https://blog.csdn.net/qq_69315815/article/details/143082810

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!