【米联客-安路飞龙DR1-FPSOC】UDP通信篇连载-07 ICMP层程序设计

软件版本:Anlogic -TD5.9.1-DR1_ES1.1

操作系统:WIN10 64bit

硬件平台:适用安路(Anlogic)FPGA

实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板

板卡获取平台:https://milianke.tmall.com/

登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!

3.6 ICMP层
该层在程序中为IP层的子层,设计了接收ICMP请求包并回复的功能。

3.6.1 ICMP接收模块
该模块首先过滤掉ICMP报文头部,并且对头部数据进行校验,校验方式和IP首部校验相同。

//ICMP报文的校验和
always@(posedge I_clk or posedge I_reset) begin
    if(I_reset) begin
        accum1              <=  16'd0;
        accum2              <=  32'd0;
        checksum_state      <=  2'd0;
        checksum_correct    <=  1'b1;
    end
    else begin
        case(checksum_state)
            0:begin
                if(I_icmp_pkg_valid) begin
                    accum1[15:8]    <=  I_icmp_pkg_data;
                    checksum_state  <=  2'd1;
                end
                else begin
                    accum1[15:8]    <=  8'd0;
                    checksum_state  <=  2'd0;
                end
            end
            1:begin
                accum1[7:0]     <=  I_icmp_pkg_data;
                checksum_state  <=  2'd2;
            end
            2:begin
                if(!I_icmp_pkg_valid) begin
                    checksum_state      <=  2'd3;
                    if((tmp_accum1[15:0] + tmp_accum1[31:16]) != 16'hffff)
                        checksum_correct    <=  1'b0;
                    else
                        checksum_correct    <=  1'b1;
                end
                else begin
                    accum2          <=  tmp_accum1;
                    accum1[15:8]    <=  I_icmp_pkg_data;
                    checksum_state  <=  2'd1;
                end
            end
            3:begin
                accum1          <=  16'd0;
                accum2          <=  32'd0;
                checksum_state  <=  2'd0;
            end
        endcase
    end
end

将报文的附加数据存入FIFO中,并向icmp_pkg_tx模块发送ping应答信息,请求发送ping应答包。

//以下模块完成ICMP报文包echo ping应答的请求,并且先缓存到ip_layer的FIFO中
always@(posedge I_clk or posedge I_reset) begin
    if(I_reset) begin
        cnt                         <=  4'd0;
        type1                       <=  8'd0;
        code                        <=  8'd0;
        echo_data_cnt               <=  10'd0;
        checksum                    <=  16'd0;
        O_icmp_req_en               <=  1'b0;
        O_icmp_req_id               <=  16'd0;
        O_icmp_req_sq_num           <=  16'd0;
        O_icmp_ping_echo_data_valid <=  1'b0;
        O_icmp_ping_echo_data       <=  8'd0;
        O_icmp_req_checksum         <=  16'd0;
        STATE                       <=  RECORD_ICMP_HEADER;
    end
    else begin
        case(STATE)
            RECORD_ICMP_HEADER:begin
                O_icmp_req_en       <=  1'b0;
                echo_data_cnt       <=  10'd0;
                if(I_icmp_pkg_valid)        //ICMP报文有效
                    case(cnt)
                        0:  begin   type1                   <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-类型:8位数表示错误类型的差错报文或者查询类型的报告报文,一般是查询报文(0代表回显应答(ping应答);1代表查
                        1:  begin   code                    <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-类型:代码占用8位数据,根据ICMP差错报文的类型,进一步分析错误的原因
                        2:  begin   checksum[15:8]          <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-校验和:16位校验和的计算方法与IP首部校验和计算方法一致,该校验和需要对ICMP首部和ICMP数据做校验
                        3:  begin   checksum[7:0]           <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-校验和:16位校验和的计算方法与IP首部校验和计算方法一致,该校验和需要对ICMP首部和ICMP数据做校验
                        4:  begin   O_icmp_req_id[15:8]     <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-标识符:16位标识符对每一个发送的数据报进行标识
                        5:  begin   O_icmp_req_id[7:0]      <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-标识符:16位标识符对每一个发送的数据报进行标识
                        6:  begin   O_icmp_req_sq_num[15:8] <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-序列号:16位对发送的每一个数据报文进行编号
                        7:  begin   O_icmp_req_sq_num[7:0]  <=  I_icmp_pkg_data;    cnt <=  cnt + 1'b1;end  //ICMP报文首部-序列号:16位对发送的每一个数据报文进行编号
                        8:  begin
                            if(type1 == PING_REQUEST && code == 8'h00) begin        //如果是远端主机发的ping请求包,那么本地主机需要返回一个ping应答包
                                O_icmp_ping_echo_data_valid <=  1'b1;               //ping应答有效
                                O_icmp_ping_echo_data       <=  I_icmp_pkg_data;
                            end
                            else begin
                                O_icmp_ping_echo_data_valid <=  1'b0;
                                O_icmp_ping_echo_data       <=  8'd0;
                            end
                            cnt     <=  4'd0;
                            STATE   <=  WAIT_PACKET_END;
                        end
                        default:    STATE   <=  RECORD_ICMP_HEADER;
                    endcase
                else
                    STATE   <=  RECORD_ICMP_HEADER;
            end
            WAIT_PACKET_END:begin
                if(I_icmp_pkg_valid) begin          //继续接收ICMP 报文
                    if(O_icmp_ping_echo_data_valid) //ping应答有效
                        echo_data_cnt   <=  echo_data_cnt + 1'b1;//ICMP包计数器
                    else
                        echo_data_cnt   <=  10'd0;
                    O_icmp_ping_echo_data_valid <=  O_icmp_ping_echo_data_valid;
                    O_icmp_ping_echo_data       <=  I_icmp_pkg_data;
                    STATE               <=  WAIT_PACKET_END;
                end
                else begin
                    if(O_icmp_ping_echo_data_valid) begin
                        O_icmp_req_en       <=  1'b1;           //通知ip_tx模块接收到ICMP报文包ping请求,并且发送一个echo ping应答
                        O_icmp_req_checksum <=  checksum_temp;  //输出校验和
                    end
                    else begin
                        O_icmp_req_en       <=  1'b0;
                        O_icmp_req_checksum <=  16'd0;
                    end
                    echo_data_cnt               <=  echo_data_cnt;
                    O_icmp_ping_echo_data_valid <=  1'b0;
                    O_icmp_ping_echo_data       <=  8'd0;
                    STATE                       <=  RECORD_ICMP_HEADER;
                end
            end
        endcase
    end
end

3.6.2 ICMP发送模块
该模块收到icmp_pkg_ctrl模块发送的ping应答包使能信号,寄存接收到的信息包括标识符、序列号、校验和、地址和数据长度,通过计数器添加报文头部,再将附加数据从FIFO中读出,组成ping应答包发送到ip_tx模块。

always@(posedge I_clk or posedge I_reset) begin
    if(I_reset) begin
        cnt1                    <=  4'd0;
        cnt2                    <=  10'd0;
        request_id              <=  16'd0;
        request_sq_num          <=  16'd0;
        request_ip_taddress     <=  32'd0;
        checksum                <=  16'd0;
        echo_data_length        <=  10'd0;
        O_icmp_pkg_req          <=  1'b0;
        O_icmp_pkg_valid        <=  1'b0;
        O_icmp_pkg_data         <=  8'd0;
        O_icmp_ping_echo_ren    <=  1'b0;
        STATE                   <=  WAIT_ICMP_PACKET;
    end
    else begin
        case(STATE)
            WAIT_ICMP_PACKET:begin
                if(I_icmp_req_en) begin//当接收到ICMP echo ping包,先保存该包的基本信息到寄存器
                    request_id              <=  I_icmp_req_id;              //ICMP包的标识符
                    request_sq_num          <=  I_icmp_req_sq_num;          //ICMP包的序列号
                    request_ip_taddress     <=  I_icmp_req_ip_addr;         //ICMP包的地址
                    checksum                <=  I_icmp_req_checksum;        //ICMP包的校验和
                    echo_data_length        <=  I_icmp_ping_echo_data_len;  //ICMP包的长度
                    O_icmp_pkg_req          <=  1'b1;                       //请求ip_tx模块发送部分,发送ICMP报文
                    STATE                   <=  WAIT_PACKET_SEND;           //发送ICMP包状态
                end
                else begin
                    request_id              <=  16'd0;
                    request_sq_num          <=  16'd0;
                    request_ip_taddress     <=  32'd0;
                    checksum                <=  16'd0;
                    echo_data_length        <=  10'd0;
                    O_icmp_pkg_req          <=  1'b0;
                    STATE                   <=  WAIT_ICMP_PACKET;
                end
            end
            WAIT_PACKET_SEND:begin
                if(I_icmp_pkg_busy) begin//该信号来自ip_tx模块,当有效代表ip_tx模块已经开始准备发送ICMP包,这里需要对ip_tx代码部分时序逻辑确保数据正确给到ip_tx模块
                    O_icmp_pkg_req          <=  1'b0;
                    O_icmp_pkg_valid        <=  1'b1;
                    O_icmp_pkg_data         <=  PING_REPLY_TYPE;//回显应答(ping应答)的类型
                    STATE                   <=  SEND_PACKET;
                end
                else begin
                    O_icmp_pkg_req          <=  1'b1;
                    O_icmp_pkg_valid        <=  1'b0;
                    O_icmp_pkg_data         <=  8'd0;
                    STATE                   <=  WAIT_PACKET_SEND;
                end
            end
            SEND_PACKET:begin
                case(cnt1)
                    0   :begin  O_icmp_pkg_data <=  8'h00;                  cnt1    <=  cnt1 + 1'b1;end//回显应答(ping应答)的代码
                    1   :begin  O_icmp_pkg_data <=  checksum[15:8];         cnt1    <=  cnt1 + 1'b1;end//ICMP报文包校验和,直接获取远程主机发送的Ping包校验和
                    2   :begin  O_icmp_pkg_data <=  checksum[7:0];          cnt1    <=  cnt1 + 1'b1;end//ICMP报文包校验和,直接获取远程主机发送的Ping包校验和
                    3   :begin  O_icmp_pkg_data <=  request_id[15:8];       cnt1    <=  cnt1 + 1'b1;end//ICMP报文标识符,直接获取远程主机发送的Ping包标识符
                    4   :begin  O_icmp_pkg_data <=  request_id[7:0];        cnt1    <=  cnt1 + 1'b1;end//ICMP报文标识符,直接获取远程主机发送的Ping包标识符
                    5   :begin  O_icmp_pkg_data <=  request_sq_num[15:8];   cnt1    <=  cnt1 + 1'b1;end//ICMP报文编码,直接获取远程主机发送的Ping序列号
                    6   :begin  //从echo FIFO中读取ICMP echo报文的数据部分
                        O_icmp_pkg_data         <=  request_sq_num[7:0];
                        cnt1                    <=  cnt1 + 1'b1;
                        O_icmp_ping_echo_ren    <=  1'b1;
                    end
                    7   :begin//ICMP报文包的数据有效部分
                        O_icmp_pkg_valid        <=  1'b1;
                        O_icmp_pkg_data         <=  I_icmp_ping_echo_data;
                        if(cnt2 == (echo_data_length - 1)) begin
                            cnt2                    <=  10'd0;
                            O_icmp_ping_echo_ren    <=  1'b0;
                            cnt1                    <=  cnt1 + 1'b1;
                        end
                        else begin
                            cnt2                    <=  cnt2 + 1'b1;
                            O_icmp_ping_echo_ren    <=  1'b1;
                            cnt1                    <=  cnt1;
                        end
                    end
                    8   :begin
                        cnt1                    <=  4'd0;
                        O_icmp_pkg_data         <=  8'd0;
                        O_icmp_pkg_valid        <=  1'b0;
                        STATE                   <=  WAIT_ICMP_PACKET;
                    end
                    default:;
                endcase
            end
        endcase
    end
end

本文来米联客(milianke),作者:米联客(milianke),转载请注明原文链接:https://www.cnblogs.com/milianke/p/18351347

最新文章

最新文章