module FSM#(
    parameter NB_ADDRESS= `NB_ADDRESS,
    parameter NB_IMAGE  = `NB_IMAGE,
    parameter NB_STATES = `NB_STATES,
    parameter  N_CONV   = `N_CONV,
    parameter  LATENCIA = `LATENCIA
    )(
    //Definicion de puertos
    output [NB_ADDRESS-1:0]  o_writeAdd,
    output [NB_ADDRESS-1:0]  o_readAdd,
    output                   o_EoP,
    output                   o_fsm_SOP,
    output                   o_changeBlock,
    output                   o_fsm_valid_conv,
    input  [NB_IMAGE-1:0]    i_imgLength,
    input                    i_CLK,
    input                    i_reset,
    input                    i_SoP,
    input                    i_valid,
    input                    i_load
    );
    //Latcheo de salidas 
    reg [NB_ADDRESS-1:0]                     counterAdd;
    reg [NB_ADDRESS-1:0]           counter_with_latency;
    reg [clog2(N_CONV)-1:0]                endOfProcess;
    //registro utulizado para el estado de carga a las memoriras
    reg                                     changeBlock;
    reg                                      sopControl;
    reg                            valid_previous_state;
    //registro utilizado para el valid de convolucionador
    reg                                      fms2conVld;
    //Ni bien se crean los registros, toman esos valores.
    reg [NB_STATES-1:0]  states;

    reg                 w_eop;  

     //Assign          
    assign     {o_writeAdd} =  (sopControl) ? counter_with_latency:counterAdd;//counterAdd;
    assign     {o_readAdd}  =  counterAdd;
    assign          {o_EoP} =  w_eop;
    assign  {o_changeBlock} =  changeBlock;
    assign  o_fsm_valid_conv    = fms2conVld;
    assign  o_fsm_SOP       = sopControl;
    
    initial begin
        counterAdd              = `NB_ADDRESS'd0;
        counter_with_latency    = `NB_ADDRESS'd0;
        endOfProcess            = `N_CONV'd0;
        changeBlock             = 1'b0;
        sopControl              = 1'b0;
        valid_previous_state    = 1'b0;
        fms2conVld              = 1'b0;
        states                  = `NB_STATES'd0;
    end        
    
    always @(posedge i_CLK) begin  
        valid_previous_state<=i_valid;
        if(i_reset)begin
            counterAdd           <= `NB_ADDRESS'd0;
            counter_with_latency <= `NB_ADDRESS'd0;
            endOfProcess         <= `N_CONV'd0;
            changeBlock          <= 1'b0;
            sopControl           <= 1'b0;
            fms2conVld           <= 1'b0;
            states               <= `NB_STATES'd0;
        end
        else begin
            if(states == 2'b00) begin
                counter_with_latency  <= `NB_ADDRESS'd0;
                counterAdd            <= `NB_ADDRESS'd0;
                changeBlock           <= 1'b0;
                if(i_load && ~i_SoP && endOfProcess==0) begin
                    //estado de carga
                    states          <= 2'b01;
                    sopControl      <= 1'b0;
                    fms2conVld      <= 1'b0;
                end
                else if(~i_load  && i_SoP && endOfProcess==0) begin
                    //estado de procesamiento
                    states <= 2'b10;
                    sopControl      <= 1'b1;
                    fms2conVld      <= 1'b1;
                end
                else if(~i_load && ~i_SoP && endOfProcess>0)begin
                    //estado de lectura
                    endOfProcess    <= endOfProcess-1;
                    states          <= 2'b01;
                    sopControl      <= 1'b0;
                    fms2conVld      <= 1'b0;
                end
                else begin
                    states          <= states;
                    sopControl      <= 1'b0;
                    fms2conVld      <= 1'b0;
                end
            end
            else if(states == 2'b01)begin
                //Manejo de direcciones en función del valid
                if (i_valid && !valid_previous_state) begin 
                    //Verificación si termino de leer/cargar un bloque
                    if (endOfProcess > 0 && counterAdd==i_imgLength-2)begin
                        counterAdd      <= counterAdd;
                        changeBlock     <= 1'b1;
                        states          <= 2'b00;
                    end
                    else if (counterAdd == i_imgLength) begin
                        counterAdd      <= counterAdd;
                        changeBlock     <= 1'b1;
                        states          <= 2'b00;
                    end
                    else begin
                        counterAdd      <= counterAdd+1;
                        changeBlock     <= changeBlock;
                        states          <= states; 
                    end
                end 
                else 
                    counterAdd   <= counterAdd;
            end
            else if(states == 2'b10) begin
             
                if(counterAdd < i_imgLength)  
                    counterAdd   <= counterAdd+1;
                else 
                    counterAdd   <= counterAdd;
                //Shifteo para el write address, teniendo en cuenta la latencia.
                if(counterAdd>=LATENCIA && counter_with_latency < i_imgLength-2) begin
                    if(counter_with_latency == i_imgLength-3)begin 
                        fms2conVld              <= 1'b0;
                    end
                    counter_with_latency    <= counter_with_latency +1;
                    states                  <= states;
                end
                else if(counter_with_latency == i_imgLength-2)begin 
                    //si se llega la tamaño de la imagen reseteo los contadores 
                    changeBlock     <= 1'b1;
                    counter_with_latency    <= counter_with_latency;
                    states                  <= 2'b11;
                end
                else begin
                    counter_with_latency    <= counter_with_latency;
                    states                  <= states;
                    end
            end
            else if(states == 2'b11)begin
                endOfProcess    <= N_CONV+1;
                if (~i_SoP) 
                    states  <= 2'b00;
                else 
                    states  <= states;  
            end
        end 
    end 
    //end always
    /*always @(*) begin
        w_eop = 0;
        for(nconv=0; nconv<N_CONV; nconv = nconv +1)
            w_eop = w_eop | endOfProcess[nconv];
    end*/
    always@(*)
        w_eop = endOfProcess > 0;
    
    
    function integer clog2;
        input integer   depth;
        for (clog2=0; depth>0; clog2=clog2+1)
            depth = depth >> 1;
    endfunction
endmodule