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;			--multiple sources
    signal next_task_state : work.task.State;
    signal index : integer range 0 to work.task.STREAM_LEN; --multiple sources
	
	--Selbst angelegte Signal:
	signal data_valid_flag : std_logic;
	signal busy_flag : std_logic;
	signal result_valid_flag : std_logic;
	signal angle_sig : signed( 31 downto 0);
	signal ergebnis :  signed( 31 downto 0 );
	signal ampl_sig : signed( 31 downto 0 );

	--Zustände für die Zustandsmaschine für die Berechnung
	type CalcState is (
		CALC_IDLE,
		CALC_START,
		CALC_SINE,
		CALC_STORE_RESULT
	);
	--Signale für die Zustandsmaschine für die Berechnung
	signal current_calc_state : CalcState;
	signal next_calc_state : CalcState;

begin
    u_float_sine : entity work.float_sine -- Das hier ist der Core!
	generic map (
		ITERATIONS => 8
	)
    port map (
        clk 	=> 		clk,
        reset 	=>		reset,
        data_valid =>	data_valid_flag,  --# load new input data
        busy  	=>		busy_flag, --# generating new result
        result_valid =>	result_valid_flag, --# flag when result is valid
        angle 		=>	angle_sig, -- angle in brads (2**size brads = 2*pi radians)
        sine =>			ergebnis										--Hierzu nachfragen
    );

	--Bei diesem task nichts ändern!
    task_state_transitions : process ( all ) 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;


	--Übergangsschaltnetz der Zustandsmaschine für die Berechnung ###Fertig
	calc_state_transitions: process (all) is
	begin
		next_calc_state <= current_calc_state; 
		case current_calc_state is
			when CALC_IDLE=>
				if (current_task_state= work.task.TASK_RUNNING) then 
					next_calc_state <= CALC_START;
				end if;
			when CALC_START=>
				next_calc_state <= CALC_SINE;
			when CALC_SINE =>
				if (result_valid_flag = '1' and busy_flag = '0') then 	--or falling_edge( busy) ?
					next_calc_state <= CALC_STORE_RESULT;
				end if;
			when CALC_STORE_RESULT =>
				if ( index = work.task.STREAM_LEN ) then
					next_calc_state <= CALC_IDLE;
				else
					next_calc_state <= CALC_START;
				end if;
		end case;
	end process calc_state_transitions;


	--Zustandsspeicher und Ausgangsschaltnetz zu der Steuerung der Tasks 
	task_sync : process (clk, reset) is
	begin
		if (reset = '1') then
			current_task_state <= work.task.TASK_IDLE;
			
		elsif (rising_edge( clk)) then
			current_task_state <= next_task_state; 
			case next_task_state is
				when work.task. TASK_IDLE => null;
				when work.task. TASK_RUNNING => null; 
				when work.task. TASK_DONE => null;
			end case;
		end if;
	end process task_sync;

	--Zustandsspeicher und Ausgangsschaltnetz zu Berechnung 
	sync : process (clk, reset) is
	begin
		if (reset = '1') then
			index <= 0;
			data_valid_flag <= '0';
			current_calc_state <= CALC_IDLE;
			--ergebnis <= (others => '0');  --Wird von IP Core gesteuert und darf deshalb hier nicht getrieben werden
			signal_writedata <= (others => '0');
			signal_write <= '0';
			angle_sig <= (others => '0');
		elsif (rising_edge( clk)) then
			current_calc_state <= next_calc_state; 
			case next_calc_state is 
				when CALC_IDLE =>
					data_valid_flag <= '0';
					signal_write <= '0';
					angle_sig <= signed (phase);
					ampl_sig <= signed (amplitude);
				when CALC_START =>
					data_valid_flag <= '1';
					signal_write <= '0';
					angle_sig <= angle_sig + signed(step_size); --step_size = 2 * PI / 32
				when CALC_SINE => 				--hier Berechnung mit IP Core?
					data_valid_flag <= '0';
				when CALC_STORE_RESULT =>
					index <= index + 1;
					signal_write <= '1';
					--Berechne float multiplikation zu Fuss. Exponent + Exponent usw.
					signal_writedata <= std_logic_vector( ergebnis(31 downto 31) & (ergebnis(30 downto 23) + (signed(ampl_sig(30 downto 23)) - 127)) & ergebnis(22 downto 0));

			end case;
		end if;
	end process sync;
	task_state <= current_task_state;

end architecture rtl;
