module nlms_example_top (clkin, key_begin_n, o_err, done, key_ini_n, key_rst_n, input_rdy, out_err_gather);
parameter idata_width = 18;
parameter coeffi_addr_width = 5;
parameter tapsize = 32;
parameter sim_en = 0;

input clkin;
input key_begin_n;
input key_rst_n;
input key_ini_n;
output [idata_width - 1 : 0] o_err;
output done;
output input_rdy;
output reg [idata_width - 1 : 0] out_err_gather;

reg [idata_width - 1 : 0] data [0 : 8192 - 1];
initial begin
    if (sim_en) begin
        $readmemb("../../project/src/data/idata.dat",data);
    end
    else begin
        $readmemb("idata.dat",data);
    end
end

reg signed [idata_width - 1 : 0] desire [0 : 8192 - 1];
initial begin
    if (sim_en) begin
        $readmemb("../../project/src/data/idesire.dat",desire);
    end
    else begin
        $readmemb("idesire.dat",desire);
    end
end
reg [idata_width - 1 : 0] sto_err [0 : 8192 - 1];

wire [idata_width - 1 : 0] o_err;
wire onboard_begin;
wire key_begin;
wire key_ini;
wire ini;
wire clk;
wire rst_n;
wire key_rst;
wire rst;

assign key_begin = ~key_begin_n;
assign key_ini = ~key_ini_n;
assign key_rst = ~key_rst_n;
assign rst_n = ~rst;
//---------I/O PORT of basic_FIR_top---------
reg wr_en_data;
//wire wr_en_coeffi;cnt_wr_coeffi_en
reg [idata_width - 1 : 0] din_desire;
reg [idata_width - 1 : 0] din_data;
wire done;
wire input_rdy;

//------------interal signal-------------

reg [coeffi_addr_width : 0] cnt_ini;
reg cnt_ini_en;

reg onboard_begin_flip1 = 0;
reg onboard_begin_flip2 = 0;
reg onboard_begin_flag = 0;

reg done_flag = 0;
reg done_flip1 = 0;
reg done_flip2 = 0;

reg wr_data_en_flag = 0;
reg wr_data_en_flip1 = 0;
reg wr_data_en_flip2 = 0;

reg wr_data_en = 0;
reg [13 : 0] re_data_addr = 0;
reg [11 : 0] wr_en_delay;

reg [13 : 0] sto_err_addr = 0;
reg [12 : 0] re_sto_addr = 0;
reg re_sto_en;
//--------------------test_start  onboard_begin_flag------------------------
always @(posedge clk) begin
    if (!rst_n || ini) begin
        onboard_begin_flip1 <= 0;
        onboard_begin_flip2 <= 0;
    end
    else begin
        onboard_begin_flip1 <= onboard_begin;
        onboard_begin_flip2 <= onboard_begin_flip1;
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        onboard_begin_flag <= 0;
    end
    else begin
        if (onboard_begin_flip1 && !onboard_begin_flip2) begin
            onboard_begin_flag <= 1;
        end
        else begin
            onboard_begin_flag <= 0;
        end
    end
end

//----------------------------------done_flag--------------------------------
always @(posedge clk) begin
    if (!rst_n || ini) begin
        done_flip1 <= 0;
        done_flip2 <= 0;
    end
    else begin
        done_flip1 <= done;
        done_flip2 <= done_flip1;
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        done_flag <= 0;
    end
    else begin
        if (done_flip1 && !done_flip2) begin
            done_flag <= 1;
        end
        else begin
            done_flag <= 0;
        end
    end
end

//----------------------------------wr_data_en_flag--------------------------------
always @(posedge clk) begin
    if (!rst_n || ini) begin
        wr_data_en_flip1 <= 0;
        wr_data_en_flip2 <= 0;
    end
    else begin
        wr_data_en_flip1 <= wr_data_en;
        wr_data_en_flip2 <= wr_data_en_flip1;
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        wr_data_en_flag <= 0;
    end
    else begin
        if (!wr_data_en_flip1 && wr_data_en_flip2) begin
            wr_data_en_flag <= 1;
        end
        else begin
            wr_data_en_flag <= 0;
        end
    end
end

//-----------input data to fir----------
always @(posedge clk) begin
    if (!rst_n || ini) begin
        wr_data_en <= 0;
    end
    else begin
        if (onboard_begin_flag) begin
            wr_data_en <= 1;
        end
        else begin
            if (re_data_addr == 14'b10_0000_0000_0000) begin
                wr_data_en <= 0;
            end
            else begin
                wr_data_en <= wr_data_en;
            end
        end
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        re_data_addr <= 0;
    end
    else begin
        if (wr_en_delay == 12'd350) begin
            re_data_addr <= re_data_addr + 1;
        end
        else begin
            re_data_addr <= re_data_addr;
        end
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        wr_en_delay <= 0;
    end
    else begin
        if (wr_data_en) begin
            if (wr_en_delay == 12'd350) begin
                wr_en_delay <= 0;
            end
            else begin
                wr_en_delay <= wr_en_delay + 1;
            end
        end
        else begin
            wr_en_delay <= 0;
        end
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        wr_en_data <= 0;
    end
    else begin
        if (wr_en_delay == 12'd350) begin
            wr_en_data <= 1;
        end
        else begin
            wr_en_data <= 0;
        end
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        din_data <= 0;
        din_desire <= 0;
    end
    else begin
        din_data <= data[re_data_addr];
        din_desire <= desire[re_data_addr];
    end
end
//-------------------initial-------------------
always @(posedge clk) begin
    if (!rst_n) begin
        cnt_ini_en <= 0;
    end
    else begin
        if (ini) begin
            cnt_ini_en <= 1;
        end
        else begin
            if (cnt_ini == tapsize) begin
                cnt_ini_en <= 0;
            end
            else begin
                cnt_ini_en <= cnt_ini_en;
            end
        end
    end
end

always @(posedge clk) begin
    if (!rst_n) begin
        cnt_ini <= 0;
    end
    else begin
        if (cnt_ini_en) begin
            cnt_ini <= cnt_ini + 1;
        end
        else begin
            cnt_ini <= 0;
        end
    end
end

//---------------------------store o_err-----------------------
always @(posedge clk) begin
    if (!rst_n || ini) begin
        sto_err_addr <= 0;
    end
    else begin
        if (done_flag) begin
            if (sto_err_addr == 14'b01_1111_1111_1111) begin
                sto_err_addr <= sto_err_addr;
            end
            else begin
                sto_err_addr <= sto_err_addr + 1;
            end
        end
    end
end

always  @(posedge clk) begin
    if (done_flag) begin
        sto_err[sto_err_addr] <= o_err;
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        re_sto_en <= 0;
    end
    else begin
        if ((sto_err_addr == 14'b01_1111_1111_1111) && done_flag) begin
            re_sto_en <= 1;
        end
        else begin
            if (re_sto_addr == 13'b1_1111_1111_1111) begin
                re_sto_en <= 0;
            end
        end
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        out_err_gather <= 0;
    end
    else begin
        if (re_sto_en) begin
            out_err_gather <= sto_err[re_sto_addr];
        end
    end
end

always @(posedge clk) begin
    if (!rst_n || ini) begin
        re_sto_addr <= 0;
    end
    else begin
        if (re_sto_en) begin
            re_sto_addr <= re_sto_addr + 1;
        end
    end
end


deUstb UUT_begin_deUstb(
    .out(onboard_begin),
    .in(key_begin),
    .clk(clk)
);

deUstb UUT_ini_deUstb(
    .out(ini),
    .in(key_ini),
    .clk(clk)
);

deUstb UUT_rst_deUstb(
    .out(rst),
    .in(key_rst),
    .clk(clk)
);

GW_PLL PLL_UUT(
    .clkout(clk), 
    .clkin(clkin)
);


NLMS_Adaptive_Filter_Top nlms_uut
    (
    .clk(clk),
    .ini(cnt_ini_en),
    .rst_n(rst_n),
    .wr_en(wr_en_data),
    .i_data(din_data),
    .i_desire(din_desire),
    .o_err(o_err),
    .out_sig(done),
    .input_rdy(input_rdy)
    );

endmodule