library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

library work;
    use work.reg32.all;
    use work.task.all;

entity add is
    port (
        clk   : in std_logic;
        reset : in std_logic;

        task_start : in std_logic;
        task_state : out work.task.State;

        signal_a_read     : out std_logic;
        signal_a_readdata : in  std_logic_vector(31 downto 0);

        signal_b_read     : out std_logic;
        signal_b_readdata : in  std_logic_vector(31 downto 0);

        signal_write     : out std_logic;
        signal_writedata : out std_logic_vector(31 downto 0)
    );
end entity add;

architecture rtl of add is

    signal current_task_state : work.task.State;
    signal next_task_state    : work.task.State;
    signal index              : integer range 0 to work.task.STREAM_LEN;


    type AddState is (
        ADD_IDLE,
        ADD_READ_FIFO,
        ADD_LATCH_INPUTS,
        ADD_START_CALC,
        ADD_WAIT_DONE,
        ADD_STORE_RESULT
    );

    signal current_add_state : AddState;
    signal next_add_state    : AddState;

    signal start_proc : std_logic;
    signal done       : std_logic;

    signal A   : std_logic_vector(31 downto 0);
    signal B   : std_logic_vector(31 downto 0);
    signal sum : std_logic_vector(31 downto 0);

begin


    u_float_add : entity work.float_add
        port map (
            clk   => clk,
            reset => reset,
            start => start_proc,
            done  => done,
            A     => A,
            B     => B,
            sum   => sum
        );


    task_state_transitions : process ( current_task_state, task_start, index ) is
    begin
        next_task_state <= current_task_state;

        case current_task_state is
            when work.task.TASK_IDLE =>
                if ( task_start = '1' ) then
                    next_task_state <= work.task.TASK_RUNNING;
                end if;

            when work.task.TASK_RUNNING =>
                if ( index = work.task.STREAM_LEN - 1 ) then
                    next_task_state <= work.task.TASK_DONE;
                end if;

            when work.task.TASK_DONE =>
                if ( task_start = '1' ) then
                    next_task_state <= work.task.TASK_RUNNING;
                end if;
        end case;
    end process task_state_transitions;


    add_state_transitions : process ( current_add_state, current_task_state, done ) is
    begin
        next_add_state <= current_add_state;

        if (current_task_state /= work.task.TASK_RUNNING) then
            next_add_state <= ADD_IDLE;
        else
            case current_add_state is
                when ADD_IDLE =>
                    next_add_state <= ADD_READ_FIFO;
                when ADD_READ_FIFO =>
                    next_add_state <= ADD_LATCH_INPUTS;
                when ADD_LATCH_INPUTS =>
                    next_add_state <= ADD_START_CALC;
                when ADD_START_CALC =>
                    next_add_state <= ADD_WAIT_DONE;
                when ADD_WAIT_DONE =>
                    if done = '1' then
                        next_add_state <= ADD_STORE_RESULT;
                    end if;
                when ADD_STORE_RESULT =>
                    next_add_state <= ADD_READ_FIFO;
            end case;
        end if;
    end process;


    sync : process ( clk, reset ) is
    begin
        if ( reset = '1' ) then
            current_task_state <= work.task.TASK_IDLE;
            index              <= 0;

            current_add_state <= ADD_IDLE;

            signal_a_read   <= '0';
            signal_b_read   <= '0';
            signal_write    <= '0';
            signal_writedata <= (others => '0');

            start_proc <= '0';
            A          <= (others => '0');
            B          <= (others => '0');


        elsif ( rising_edge( clk ) ) then

            current_task_state <= next_task_state;
            current_add_state  <= next_add_state;


            signal_a_read <= '0';
            signal_b_read <= '0';
            signal_write  <= '0';
            start_proc    <= '0';


            case next_task_state is
                when work.task.TASK_IDLE =>
                    index <= 0;

                when work.task.TASK_RUNNING =>
                    if current_add_state = ADD_STORE_RESULT then
                        index <= index + 1;
                    end if;

                when work.task.TASK_DONE =>
                    index <= 0;
            end case;


            case next_add_state is
                when ADD_IDLE =>
                    null;

                when ADD_READ_FIFO =>
                    signal_a_read <= '1';
                    signal_b_read <= '1';

                when ADD_LATCH_INPUTS =>
                    A <= signal_a_readdata;
                    B <= signal_b_readdata;

                when ADD_START_CALC =>
                    start_proc <= '1';

                when ADD_WAIT_DONE =>
                    start_proc <= '1';
                    null;

                when ADD_STORE_RESULT =>
                    signal_write    <= '1';
                    signal_writedata <= sum;
            end case;


        end if;
    end process sync;

    task_state <= current_task_state;

end architecture rtl;

