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

library work;
    use work.cordic_pkg.all;

entity fixed_sine is
    generic (
        SIZE       : positive;   -- Width of parameters
        ITERATIONS : positive;   -- Number of CORDIC iterations
        FRAC_BITS  : positive;   -- Total fractional bits
        MAGNITUDE  : real := 1.0;
        RESET_ACTIVE_LEVEL : std_ulogic := '1'
    );
    port (
        clock : in std_ulogic;
        reset : in std_ulogic;
        data_valid   : in std_ulogic;  --# load new input data
        busy         : out std_ulogic; --# generating new result
        result_valid : out std_ulogic; --# flag when result is valid
        angle : in signed(size-1 downto 0); -- angle in brads (2**size brads = 2*pi radians)
        sine   : out signed(size-1 downto 0)
    );
end entity fixed_sine;

architecture rtl of fixed_sine is
    signal xa, ya, za, x_result, y_result : signed(Angle'range);
    signal rv_loc : std_ulogic;
begin

    adj: process(clock, reset) is
        constant Y : signed(Angle'range) := (others => '0');
        constant X : signed(Angle'range) := --to_signed(1, Angle'length);
        to_signed(integer(MAGNITUDE/cordic_gain(ITERATIONS) * 2.0 ** FRAC_BITS), Angle'length);
    begin
        if reset = RESET_ACTIVE_LEVEL then
            xa <= (others => '0');
            ya <= (others => '0');
            za <= (others => '0');
        elsif rising_edge(clock) then
            adjust_angle(X, Y, Angle, xa, ya, za);
        end if;
    end process;

    c: entity work.cordic
        generic map (
            SIZE => SIZE,
            ITERATIONS => ITERATIONS,
            RESET_ACTIVE_LEVEL => RESET_ACTIVE_LEVEL
        )
        port map (
            clock => clock,
            reset => reset,
            data_valid => data_valid,
            result_valid => rv_loc,
            busy => busy,
            Mode => cordic_rotate,

            X => xa,
            Y => ya,
            Z => za,

            X_result => x_result,
            Y_result => y_result,
            Z_result => open
        );

    reg: process(clock, reset) is
    begin
        if reset = RESET_ACTIVE_LEVEL then
            sine <= (others => '0');
            result_valid <= '0';
        elsif rising_edge(clock) then
            result_valid <= rv_loc;

            if rv_loc = '1' then -- Capture result
                sine <= y_result;
            end if;
        end if;
    end process;
end architecture;
