module data_transmit(
    clk       ,
    rst_n     ,

    mac_sour  ,
    mac_self  ,
    ip_sour   ,
    ip_self   ,

    cmd_arq   ,
    cmd_start ,
    cmd_stop  ,

    ff_tx_vld  ,
    ff_tx_data ,
    ff_tx_sop  ,
    ff_tx_eop  ,
    ff_tx_rdy  
    );

    //参数定义
    parameter      DATA_W =         8;

    //输入信号定义
    input                               clk          ;
    input                               rst_n        ;
    input                               cmd_arq      ;
    input                               cmd_start    ;
    input                               cmd_stop     ;
    input                               ff_tx_rdy    ;
    input [48-1:0]                      mac_sour     ;
    input [48-1:0]                      mac_self     ;
    input [32-1:0]                      ip_sour      ;
    input [32-1:0]                      ip_self      ;

    output                              ff_tx_vld    ;
    output                              ff_tx_sop    ;
    output                              ff_tx_eop    ;
    output [32-1:0]                     ff_tx_data   ;

    reg                                 ff_tx_vld    ;
    reg                                 ff_tx_sop    ;
    reg                                 ff_tx_eop    ;
    reg [ 2-1:0]                        ff_tx_mod    ;
    reg [32-1:0]                        ff_tx_data   ;
    //中间信号定义
    reg                                 flag_f012    ;
    reg                                 start_en     ;
    reg [ 16-1:0]                       x            ;
    reg [  2-1:0]                       cnt_zu       ;
    reg [ 16-1:0]                       cnt_data     ;
    reg [ 16-1:0]                       cnt_pack     ;
    reg [ 32-1:0]                       cnt_time     ;
    reg [  8-1:0]                       cnt_sec      ;

  
    reg [ 32-1:0]                       ip_sum0_0tmp ;
    reg [ 32-1:0]                       ip_sum0_1tmp ;
    reg [ 32-1:0]                       ip_sum0_2tmp ;
    reg [ 32-1:0]                       ip_sum0_3tmp ;
    reg [ 32-1:0]                       ip_sum0_4tmp ;
    reg [ 32-1:0]                       ip_sum1_0tmp ;
    reg [ 32-1:0]                       ip_sum1_1tmp ;
    reg [ 32-1:0]                       ip_sum1_2tmp ;
    reg [ 32-1:0]                       ip_sum2_0tmp ;
    reg [ 32-1:0]                       ip_sum2_1tmp ;
    reg [ 32-1:0]                       ip_sum3_0tmp ;
    reg [ 16-1:0]                       ip_sum0_0    ;
    reg [ 16-1:0]                       ip_sum0_1    ;
    reg [ 16-1:0]                       ip_sum0_2    ;
    reg [ 16-1:0]                       ip_sum0_3    ;
    reg [ 16-1:0]                       ip_sum0_4    ;
    reg [ 16-1:0]                       ip_sum1_0    ;
    reg [ 16-1:0]                       ip_sum1_1    ;
    reg [ 16-1:0]                       ip_sum1_2    ;
    reg [ 16-1:0]                       ip_sum2_0    ;
    reg [ 16-1:0]                       ip_sum2_1    ;
    reg [ 16-1:0]                       ip_sum3_0    ;

    wire                                add_cnt_data ;
    wire                                add_cnt_zu   ;
    wire                                add_cnt_time ;
    wire                                add_cnt_sec  ;
    wire                                end_cnt_data ;
    wire                                end_cnt_zu   ;
    wire                                end_cnt_time ;
    wire                                end_cnt_sec  ;
 
    wire [352-1:0]                      data_head    ;
    wire [112-1:0]                      eth_head     ;
    wire [160-1:0]                      ip_head      ; 
    wire [ 64-1:0]                      udp_head     ;
    wire [ 32-1:0]                      ip_head_1    ;
    wire [ 32-1:0]                      ip_head_2    ;
    wire [ 32-1:0]                      ip_head_3    ;
    wire [ 32-1:0]                      ip_head_4    ;
    wire [ 32-1:0]                      ip_head_5    ;
    wire [ 16-1:0]                      udp_len      ;
    wire [ 16-1:0]                      udp_sum      ;
    wire [ 16-1:0]                      ip_sum       ;
    wire [ 16-1:0]                      total_len    ;

    wire [256-1:0]                      data_f012   ;
    wire [ 32-1:0]                      data_f012_1 ;
    wire [ 32-1:0]                      data_f012_2 ;
    wire [ 32-1:0]                      data_f012_3 ;
    wire [ 32-1:0]                      data_f012_4 ;
    wire [ 32-1:0]                      data_f012_5 ;
    wire [ 32-1:0]                      data_f012_6 ;
    wire [ 32-1:0]                      data_f012_7 ; 
    wire [ 32-1:0]                      data_f012_8 ;

    assign data_f012_1 = 32'haa_aa_aa_aa;
    assign data_f012_2 = 32'h55_55_55_55;
    assign data_f012_3 = 32'haa_aa_aa_aa;
    assign data_f012_4 = 32'h55_55_55_55;
    assign data_f012_5 = 32'haa_aa_aa_aa;
    assign data_f012_6 = 32'h55_55_55_55;
    assign data_f012_7 = 32'haa_aa_aa_aa;
    assign data_f012_8 = 32'h55_55_55_55;

    assign data_f012 = {data_f012_1,data_f012_2,data_f012_3,data_f012_4,
                        data_f012_5,data_f012_6,data_f012_7,data_f012_8};

    assign ip_head_1 = {16'h45_00,total_len};
    assign ip_head_2 = {cnt_pack,16'h00_00};
    assign ip_head_3 = {16'h40_11,ip_sum};//生存时间为64 UDP协议
    assign ip_head_4 = ip_self;
    assign ip_head_5 = ip_sour;
    assign udp_sum = 16'h00_00;

    assign data_head = {16'h00_00,eth_head,ip_head,udp_head};
    assign eth_head = {mac_sour,mac_self,16'h08_00};//IP协议
    assign ip_head = {ip_head_1,ip_head_2,ip_head_3,ip_head_4,ip_head_5};
    assign udp_head = {16'h13_88,16'h17_70,udp_len,udp_sum};

    assign total_len = 16'd32 + 16'd8 + 16'd20;
    assign udp_len = 40;

    /*******************************************************************/
    always  @(*)begin
        ip_sum0_0tmp = ip_head_1[31:16] + ip_head_1[15:0];
        ip_sum0_1tmp = ip_head_2[31:16] + ip_head_2[15:0];
        ip_sum0_2tmp = ip_head_3[31:16] + ip_head_3[15:0];
        ip_sum0_3tmp = ip_head_4[31:16] + ip_head_4[15:0];
        ip_sum0_4tmp = ip_head_5[31:16] + ip_head_5[15:0];
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ip_sum0_0 <= 0;
            ip_sum0_1 <= 0;
            ip_sum0_2 <= 0;
            ip_sum0_3 <= 0;
            ip_sum0_4 <= 0;
        end
        else begin
            ip_sum0_0 <= ip_sum0_0tmp[31:16] + ip_sum0_0tmp[15:0];
            ip_sum0_1 <= ip_sum0_1tmp[31:16] + ip_sum0_1tmp[15:0];
            ip_sum0_2 <= ip_sum0_2tmp[31:16] + ip_sum0_2tmp[15:0];
            ip_sum0_3 <= ip_sum0_3tmp[31:16] + ip_sum0_3tmp[15:0];
            ip_sum0_4 <= ip_sum0_4tmp[31:16] + ip_sum0_4tmp[15:0];
        end
    end

    always  @(*)begin
        ip_sum1_0tmp = ip_sum0_0 + ip_sum0_1;
        ip_sum1_1tmp = ip_sum0_2 + ip_sum0_3;
        ip_sum1_2tmp = ip_sum0_4;
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ip_sum1_0 <= 0;
            ip_sum1_1 <= 0;
            ip_sum1_2 <= 0;
        end
        else begin
            ip_sum1_0 <= ip_sum1_0tmp[31:16] + ip_sum1_0tmp[15:0];
            ip_sum1_1 <= ip_sum1_1tmp[31:16] + ip_sum1_1tmp[15:0];
            ip_sum1_2 <= ip_sum1_2tmp[31:16] + ip_sum1_2tmp[15:0];
        end
    end

    always  @(*)begin
        ip_sum2_0tmp = ip_sum1_0 + ip_sum1_1; 
        ip_sum2_1tmp = ip_sum1_2; 
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ip_sum2_0 <= 0;
            ip_sum2_1 <= 0;
        end
        else begin
            ip_sum2_0 <= ip_sum2_0tmp[31:16] + ip_sum2_0tmp[15:0];
            ip_sum2_1 <= ip_sum2_1tmp[31:16] + ip_sum2_1tmp[15:0];
        end
    end

    always  @(*)begin
        ip_sum3_0tmp = ip_sum2_0 + ip_sum2_1;
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ip_sum3_0 <= 16'hff_ff;
        end
        else if(add_cnt_data && cnt_data==4 && cnt_zu==0)begin
            ip_sum3_0 <= ip_sum3_0tmp[31:16] + ip_sum3_0tmp[15:0];
        end
        else if(add_cnt_data && cnt_data==6 && cnt_zu==0)begin
            ip_sum3_0 <= 16'hff_ff;
        end
    end

    assign ip_sum = ~ip_sum3_0;  //IP首部校验和
    /***********************************************************************/

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_data <= 0;
        end
        else if(add_cnt_data)begin
            if(end_cnt_data)
                cnt_data <= 0;
            else
                cnt_data <= cnt_data + 1'b1;
        end
    end

    assign add_cnt_data = ff_tx_rdy && (start_en==1'b1);       
    assign end_cnt_data = add_cnt_data && cnt_data== x - 1'b1;

    always  @(*)begin
        if(add_cnt_data && cnt_zu==0)begin//传输数据头/以太网帧头+IP头+UDP头
            x = 11;
        end
        else if(add_cnt_data && cnt_zu==1)begin//传输有效数据
            x = 8;
        end
        else begin
            x = 0;
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_zu <= 0;
        end
        else if(add_cnt_zu)begin
            if(end_cnt_zu)
                cnt_zu <= 0;
            else
                cnt_zu <= cnt_zu + 1'b1;
        end
    end

    assign add_cnt_zu = end_cnt_data;       
    assign end_cnt_zu = add_cnt_zu && cnt_zu== 2 - 1'b1;

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            flag_f012 <= 1'b0;
        end
        else if(cmd_arq)begin
            flag_f012 <= 1'b1;
        end
        else if(end_cnt_zu)begin
            flag_f012 <= 1'b0;
        end
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ff_tx_data <= 0;
        end
        else if(add_cnt_data && cnt_zu==0)begin
            ff_tx_data <= data_head[352-cnt_data*32-1 -: 32];
        end
        else if(add_cnt_data && cnt_zu==1)begin
            ff_tx_data <= data_f012[256-cnt_data*32-1 -: 32];
        end
        else begin
            ff_tx_data <= 0;
        end
    end

    //一秒钟发送一次数据计数器
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_time <= 0;
        end
        else if(add_cnt_time)begin
            if(end_cnt_time)
                cnt_time <= 0;
            else
                cnt_time <= cnt_time + 1'b1;
        end
    end

    assign add_cnt_time = 1;       
    assign end_cnt_time = add_cnt_time && cnt_time== 100_000_000 - 1'b1;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_sec <= 0;
        end
        else if(add_cnt_sec)begin
            if(end_cnt_sec)
                cnt_sec <= 0;
            else
                cnt_sec <= cnt_sec + 1'b1;
        end
    end

    assign add_cnt_sec = end_cnt_time;       
    assign end_cnt_sec = add_cnt_sec && cnt_sec==10 - 1 ;   

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            start_en <= 1'b0;
        end
        else if(end_cnt_sec)begin
            start_en <= 1'b1;
        end
        else if(end_cnt_zu)begin
            start_en <= 1'b0;
        end
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ff_tx_sop <= 1'b0;
        end
        else if(add_cnt_data && cnt_zu==0 && cnt_data==0)begin
            ff_tx_sop <= 1'b1;
        end
        else begin
            ff_tx_sop <= 1'b0;
        end
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ff_tx_vld <= 0;
        end
        else begin
            ff_tx_vld <= add_cnt_data;
        end
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ff_tx_eop <= 1'b0;
        end
        else if(end_cnt_zu)begin
            ff_tx_eop <= 1'b1;
        end
        else begin
            ff_tx_eop <= 1'b0;
        end
    end

    /*always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            ff_tx_mod <= 2'b00;
        end
        else if(end_cnt_zu)begin
            ff_tx_mod <= 2'b00;
        end
    end*/

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            cnt_pack <= 0;
        end
        else if(end_cnt_zu)begin
            cnt_pack <= cnt_pack + 1'b1;
        end
    end

endmodule
