module udp_rx(
    input                clk         ,    //Ê±ÖÓÐÅºÅ
    input                rst_n       ,    //¸´Î»ÐÅºÅ£¬µÍµçÆ½ÓÐÐ§
    
    input                gmii_rx_dv  ,    //GMIIÊäÈëÊý¾ÝÓÐÐ§ÐÅºÅ
    input        [7:0]   gmii_rxd    /* synthesis PAP_MARK_DEBUG="1" */,    //GMIIÊäÈëÊý¾Ý
    output  reg          rec_pkt_done,    //ÒÔÌ«Íøµ¥°üÊý¾Ý½ÓÊÕÍê³ÉÐÅºÅ
    output  reg          rec_en      /* synthesis PAP_MARK_DEBUG="1" */,    //ÒÔÌ«Íø½ÓÊÕµÄÊý¾ÝÊ¹ÄÜÐÅºÅ
    output  reg  [31:0]  rec_data    /* synthesis PAP_MARK_DEBUG="1" */,    //ÒÔÌ«Íø½ÓÊÕµÄÊý¾Ý
    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Ð­Òé

//reg define
reg  [6:0]   cur_state       ;
reg  [6:0]   next_state      /* synthesis PAP_MARK_DEBUG="1" */;
                             
reg          skip_en         ; //¿ØÖÆ×´Ì¬Ìø×ªÊ¹ÄÜÐÅºÅ
reg          error_en        /* synthesis PAP_MARK_DEBUG="1" */; //½âÎö´íÎóÊ¹ÄÜÐÅºÅ
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µØÖ·»òÕß¹«¹²µØÖ· ÅÐ¶ÏÀàÐÍÊÇ·ñÎªIPÐ­Òé£¨0800£©
                        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
                if(gmii_rx_dv) begin
                    cnt <= cnt + 5'd1;
                    if(cnt == 5'd0)
                        ip_head_byte_num <= {gmii_rxd[3:0],2'd0}; //IP¼Ä´æÊ×²¿³¤¶È£¬×óÒÆÁ½Î»=x4£¬¼´dwX4±ä³É×Ö½ÚÊý
                    //±È½ÏÊÓÆµ´úÂë£¬ÉÙÁË¶ÔUDPÐ­ÒéµÄ½âÎö
                    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
                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                               //µ¥°üÊý¾Ý½ÓÊÕÍê³É   
                if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
                    skip_en <= 1'b1; 
            end    
            default : ;
        endcase                                                        
    end
end

endmodule