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

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

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

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

        step_size : in work.reg32.word;
        phase : in work.reg32.word;
        amplitude : in work.reg32.word;

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

architecture rtl of sine 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;
    
    signal angle_reg      : signed(31 downto 0);
    signal step_size_s    : signed(31 downto 0);
    signal phase_s        : signed(31 downto 0);

    signal fs_data_valid  : std_logic;
    signal fs_busy        : std_logic;
    signal fs_result_valid: std_logic;
    signal fs_sine        : signed(31 downto 0);

    signal sine_sample    : std_logic_vector(31 downto 0);
    signal fs_result_valid_d : std_logic;

begin
    u_float_sine : entity work.float_sine
        generic map (
            ITERATIONS => 8
        )
        port map (
            clk          => clk,
            reset        => reset,
            data_valid   => fs_data_valid,
            busy         => fs_busy,
            result_valid => fs_result_valid,
            angle        => angle_reg,
            sine         => fs_sine
        );
            
    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;

    sync : process ( clk, reset ) is
        
        variable sine_word      : std_logic_vector(31 downto 0);
        variable sign_bit       : std_logic;
        variable exp_bits       : unsigned(30 downto 23);
        variable man_bits       : std_logic_vector(22 downto 0);

        variable amp_exp        : unsigned(30 downto 23);
        variable new_exp        : unsigned(30 downto 23);
    
    begin
        if ( reset = '1' ) then
            current_task_state <= work.task.TASK_IDLE;
            index <= 0;
            angle_reg          <= (others => '0');
            step_size_s        <= (others => '0');
            phase_s            <= (others => '0');
            fs_data_valid      <= '0';
            signal_write       <= '0';
            signal_writedata   <= (others => '0');
            sine_sample        <= (others => '0');
            fs_result_valid_d <= '0';
            
        elsif ( rising_edge( clk ) ) then
            current_task_state <= next_task_state;

            fs_data_valid <= '0';
            signal_write  <= '0';
            
            fs_result_valid_d <= fs_result_valid;

            case next_task_state is
                
                when work.task.TASK_IDLE =>
                    index       <= 0;
                    step_size_s <= signed( step_size );
                    phase_s     <= signed( phase );
                    angle_reg   <= signed( phase );

                
                when work.task.TASK_RUNNING =>

                    if (index = 0 and fs_busy = '0') then
                        fs_data_valid <= '1';
                    end if;

                    if (fs_result_valid = '1' and fs_result_valid_d = '0') then
                        sine_word := std_logic_vector(fs_sine);
                        sign_bit  := sine_word(31);
                        exp_bits  := unsigned(sine_word(30 downto 23));
                        man_bits  := sine_word(22 downto 0);

                        amp_exp := unsigned(amplitude(30 downto 23));
                        new_exp := exp_bits + (amp_exp - to_unsigned(127, 8));

                        sine_word(31)           := sign_bit;
                        sine_word(30 downto 23) := std_logic_vector(new_exp);
                        sine_word(22 downto 0)  := man_bits;

                        signal_write     <= '1';
                        signal_writedata <= sine_word;

                        angle_reg <= angle_reg + step_size_s;
                        index     <= index + 1;

                        fs_data_valid <= '1';
                    end if;



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

    task_state <= current_task_state;

end architecture rtl;
