--------------------------------------------------------------------------------
-- 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;
use work.fixed_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 nco is
Generic
(
	nbits_wave		: integer := 12;
	nbits_phase		: integer := 16; 
	nbits_lut_depth	: integer := 12; 
	nbits_dither	: integer := 4; 
	nbits_lfsr		: integer := 12; 
	has_pipe_reg	: boolean := false;
	has_out_reg		: boolean := false
);
Port
(
	srst		: in std_logic;
	clk			: in std_logic;
	pacc_clr	: in std_logic;
	pacc_inc	: in std_logic;
	freq_in		: in unsigned(nbits_phase-1 downto 0);
	freq_load	: in std_logic;
	phase_in	: in unsigned(nbits_phase-1 downto 0);
	phase_load	: in std_logic;
	phase_out	: out unsigned(nbits_phase-1 downto 0);
	wave_out_i	: out sfixed;
	wave_out_q	: out sfixed;
	out_valid	: out std_logic
);
end nco;

architecture Behavioral of nco is

	COMPONENT wavelut
	Generic
	(
		nbits_wave		: integer;
		nbits_wave_frac	: integer;
		nbits_lut_depth	: integer; 
		has_out_reg		: boolean
	);
	Port
	(
		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
	);
	END COMPONENT;

-------------------------------------------------------------------------------
	constant nbits_wave_frac : integer := nbits_wave;

-------------------------------------------------------------------------------
	constant lfsr_gen_poly : unsigned(nbits_lfsr-1 downto 0) := to_unsigned(lfsr_poly(nbits_lfsr), nbits_lfsr);	-- 24
	
	signal valid : std_logic_vector(3 downto 0);
	signal phase : unsigned(nbits_phase-1 downto 0);
	signal freq_in_r : unsigned(nbits_phase-1 downto 0);
	signal phase_in_r : unsigned(nbits_phase-1 downto 0);
	signal lsfr_sum : unsigned(nbits_phase-1 downto 0);
	signal lut_addr : unsigned(nbits_lut_depth-1 downto 0);
	signal wave_i, wave_q : sfixed(sproto(nbits_wave, nbits_wave_frac)'high downto sproto(nbits_wave, nbits_wave_frac)'low);
	type lfsr_t is array (0 to 1) of unsigned(nbits_lfsr-1 downto 0);
	signal lfsr : lfsr_t;
	signal lfsr_out : unsigned(nbits_dither-1 downto 0);
	signal lut_addr_valid _ std_logic;
	
-------------------------------------------------------------------------------
begin

	wave_out_i <= wave_i;
	wave_out_q <= wave_q;

------------------------------------------------------------
	proc_ctrl: process(srst, clk, pacc_inc, pacc_clr)
	begin
		if rising_edge(clk) then
			if srst = '1' then
				valid <= (others => '0');
			else
				valid <= valid(valid'left-1 downto valid'right) & (pacc_inc or pacc_clr);
			end if;	
		end if;	
	end process;

------------------------------------------------------------
	proc_lfsr: process(srst, clk, lfsr, pacc_inc)
	variable lo : unsigned(nbits_dither downto 0);
	begin
	   if rising_edge(clk) then
		  if srst = '1' then
				for i in lfsr'range loop
					lfsr(i) <= to_unsigned(1234+2**i, nbits_lfsr); 
				end loop;	
		  elsif pacc_inc ='1' then 
				lo := (others => '0'); 
				for i in lfsr'range loop
					if lfsr(i)(lfsr(i)'left) = '1' then
						lfsr(i) <= (lfsr(i)(lfsr(i)'left-1 downto 0) & lfsr(i)(lfsr(i)'left)) xor lfsr_gen_poly;
			else
						lfsr(i) <= lfsr(i)(lfsr(i)'left-1 downto 0) & lfsr(i)(lfsr(i)'left);
			end if;	
				end loop;	
				for i in lfsr'range loop
					lo := lo + lfsr(i)(nbits_dither-1 downto 0); 
				end loop;	
--				lo := lfsr(0)(nbits_dither downto 0); 
				lfsr_out <= lo(nbits_dither-1 downto 0);
		  end if;
	   end if;
	end process;

------------------------------------------------------------
	proc_phase_accu: process(srst, clk, pacc_clr, pacc_inc, freq_in_r, phase_in_r)
	variable phase_accu : unsigned(nbits_phase-1 downto 0);

	begin
		if rising_edge(clk) then 
			if srst = '1' then
				phase_accu := (others => '0');
			else
				phase <= phase_accu + phase_in_r;
				if pacc_clr = '1' then
					phase_accu := (others => '0');
				elsif pacc_inc = '1' then
					phase_accu := phase_accu + freq_in_r;
				end if;	
			end if;	
		end if;
	end process;

------------------------------------------------------------
	freq_in_reg: process(srst, clk, freq_load, freq_in)
	begin
		if rising_edge(clk) then
			if srst = '1' then
				freq_in_r <= (others => '0');
			elsif freq_load = '1' then
				freq_in_r <= freq_in;
			end if;
		end if;
	end process;

------------------------------------------------------------
	phase_in_reg: process(srst, clk, phase_load, phase_in)
	begin
		if rising_edge(clk) then
			if srst = '1' then
				phase_in_r <= (others => '0');
			elsif phase_load = '1' then
				phase_in_r <= phase_in;
			end if;
		end if;
	end process;

------------------------------------------------------------
	proc_pipe_reg: process(clk)
	begin
		if rising_edge(clk) then
			lsfr_sum <= lfsr_out + phase;
			lut_addr <= lsfr_sum(nbits_phase-1 downto nbits_phase-nbits_lut_depth);
		end if;	
	end process;
	
------------------------------------------------------------
	proc_out_reg: process(clk, phase)
	begin
		if has_out_reg = true then
			if rising_edge(clk) then
				phase_out <= phase;
			end if;	
		else	
			phase_out <= phase;
		end if;	
	end process;

------------------------------------------------------------
	proc_valid_reg: process(clk, valid)
	begin
		out_valid <= valid(valid'right+1);
		if has_out_reg = true then
			out_valid <= valid(valid'right+2);
			if has_pipe_reg = true then
				out_valid <= valid(valid'right+3);
			end if;	
		elsif has_pipe_reg = true then
			out_valid <= valid(valid'right+2);
		end if;
	end process;

------------------------------------------------------------
	inst_wavelut_d: wavelut
	GENERIC MAP
	(
		nbits_wave		=> nbits_wave,
		nbits_wave_frac	=> nbits_wave_frac,
		nbits_lut_depth	=> nbits_lut_depth,
		has_out_reg		=> has_out_reg
	)
	PORT MAP
	(
		clk			=> clk,
		addr		=> lut_addr,
		addr_valid	=> lut_addr_valid,
		wave_i_out	=> wave_i,
		wave_q_out	=> wave_q
	);

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