--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:    12:16:14 10/02/05
-- Design Name:    
-- Module Name:    cordic_stage - Behavioral
-- Project Name:   
-- Target Device:  
-- Tool versions:  
-- Description:
--
-- Dependencies:
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.MATH_REAL.ALL;
USE ieee.numeric_std.ALL;

library ieee_proposed;
use ieee_proposed.fixed_float_types.all;
use ieee_proposed.fixed_pkg.all;

library work;
use work.fixed_util_pkg.all;
use work.nco_pkg.all;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity wavelut is
Generic
(
	nbits_wave		: integer := 12;
	nbits_wave_frac	: integer := 12;
	nbits_lut_depth	: integer := 12; 
	q_phase			: phase_relation_t := phase_90deg;
	has_out_reg		: boolean := false
);
Port
(
	rst			: in std_logic;
	clk			: in std_logic;
	addr_valid	: in std_logic;
	addr		: in unsigned(nbits_lut_depth-1 downto 0);
	wave_i_out	: out sfixed;
	wave_q_out	: out sfixed;
	valid_out	: out std_logic
);
end wavelut;

architecture Behavioral of wavelut is

	COMPONENT waverom_dual
	Generic
	(
		nbits_wave		: integer;
		nbits_wave_frac	: integer;
		nbits_rom_depth	: integer
	);
	Port
	(
		clk			: in std_logic;
		addr_i		: in unsigned(nbits_rom_depth-1 downto 0);
		addr_q		: in unsigned(nbits_rom_depth-1 downto 0);
		wave_i_out	: out sfixed;
		wave_q_out	: out sfixed
	);
	END COMPONENT;

-------------------------------------------------------------------------------
constant nbits_rom_depth : integer := nbits_lut_depth-2;

-------------------------------------------------------------------------------
signal wave_i : sfixed(sproto(nbits_wave, nbits_wave_frac)'high downto sproto(nbits_wave, nbits_wave_frac)'low);
signal wave_q : sfixed(sproto(nbits_wave, nbits_wave_frac)'high downto sproto(nbits_wave, nbits_wave_frac)'low);
signal inv_i : std_logic;
signal inv_q : std_logic;
signal addr_i : unsigned(nbits_rom_depth-1 downto 0);
signal addr_q : unsigned(nbits_rom_depth-1 downto 0);
signal zero_i : std_logic;
signal zero_q : std_logic;

-------------------------------------------------------------------------------
begin

	inst_waverom_dual: waverom_dual
	GENERIC MAP
	(
		nbits_wave		=> nbits_wave,
		nbits_wave_frac	=> nbits_wave_frac,
		nbits_rom_depth	=> nbits_rom_depth
	)
	PORT MAP
	(
		clk			=> clk,
		addr_i		=> addr_i,
		addr_q		=> addr_q,
		wave_i_out	=> wave_i,
		wave_q_out	=> wave_q
	);
	
------------------------------------------------------------
proc_valid: process(clk)
	variable v : unsigned(0 to 1);
	begin
		if rising_edge(clk) then
			if rst = '1' then
				v := (others => '0');
				valid_out <= '0';
			else
				valid_out <= v(1);
				v(1) := v(0);
				v(0) := addr_valid;
			end if;		
		end if;	
	end process;

------------------------------------------------------------
proc_tbl_addr_i: process(clk, addr)
	variable index : unsigned(addr'left downto addr'right);
	variable n2 : integer;
	constant N_2 : integer := 2**(nbits_lut_depth-1);
	constant N_4 : integer := 2**(nbits_lut_depth-2);
	begin
		n2 := to_integer(addr(addr'left-1 downto addr'right));
		if n2 > N_4 then
			index := to_unsigned(N_2 - n2, index'length);
		else
			index := to_unsigned(n2, index'length);
		end if;

		if rising_edge(clk) then
			if rst = '1' then
				addr_i <= (others => '0');
				zero_i <= '0';
				inv_i <= '0';
			elsif addr_valid = '1' then
				addr_i <= index(addr'left-2 downto addr'right);
				zero_i <= index(addr'left-1);
				inv_i <= addr(addr'left) xor addr(addr'left-1);
			end if;	
		end if;	
		
	end process;

-------------------------------------------------------------------------------
proc_tbl_addr_q: process(clk, addr)
	variable index : unsigned(addr'left downto addr'right);
	variable n2 : integer;
	constant N_4 : integer := 2**(nbits_lut_depth-2);
	
	begin
		n2 := to_integer(addr(addr'left-1 downto addr'right));
		if n2 > N_4 then
			index := to_unsigned(n2 - N_4, index'length);
		else
			index := to_unsigned(N_4 - n2, index'length);
		end if;

		if rising_edge(clk) then
			if rst = '1' then
				addr_q <= (others => '0');
				zero_q <= '0';
				inv_q <= '0';
			elsif addr_valid = '1' then
				addr_q <= index(addr'left-2 downto addr'right);
				zero_q <= index(addr'left-1);
				if q_phase = phase_90deg then
					inv_q <= addr(addr'left);
				elsif q_phase = phase_270deg then	
					inv_q <= not addr(addr'left);
				end if;	
			end if;	
		end if;	
		
	end process;

------------------------------------------------------------
proc_out_i: process(clk, wave_i, inv_i, zero_i)
	variable w : sfixed(sproto(nbits_wave, nbits_wave_frac)'high downto sproto(nbits_wave, nbits_wave_frac)'low);
	variable z, inv : std_logic;
	begin

		w := to_sfixed(0.0, w);
		if z = '0' then
			w := wave_i;
			if inv = '1' then
				w := resize(-wave_i, w, fixed_wrap, fixed_truncate);
			end if;
		end if;
		
		if has_out_reg = true then
			if rising_edge(clk) then
				wave_i_out <= w;
			end if;	
		else	
			wave_i_out <= w;
		end if;	

		if rising_edge(clk) then
			z := zero_i;
			inv := inv_i;
		end if;	

	end process;

------------------------------------------------------------
proc_out_q: process(clk, wave_q, inv_q, zero_q)
	variable w : sfixed(sproto(nbits_wave, nbits_wave_frac)'high downto sproto(nbits_wave, nbits_wave_frac)'low);
	variable z, inv : std_logic;
	begin
	
		w := to_sfixed(0.0, w);
		if z = '0' then
			w := wave_q;
			if inv = '1' then
				w := resize(-wave_q, w, fixed_wrap, fixed_truncate);
			end if;
		end if;
		
		if has_out_reg = true then
			if rising_edge(clk) then
				wave_q_out <= w;
			end if;	
		else	
			wave_q_out <= w;
		end if;	

		if rising_edge(clk) then
			z := zero_q;
			inv := inv_q;
		end if;	

	end process;

------------------------------------------------------------
end Behavioral;
