----------------------------------------------------
--         DDS : sinus
----------------------------------------------------
-- ESIEE
-- creation     : A. Exertier, 06/2009
-- modification : A. Exertier, 12/2011
----------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-----------------------------------------------------
--                GENERIC PARAMETER
-----------------------------------------------------
-- N_data         : output data number of bits
-- M              : phase accumulator precision
-- amplitude      : sinus amplitude 
--                  should be < 2**N_data
-----------------------------------------------------
--                INPUTS
-----------------------------------------------------
-- clk            : clock
-- resetn         : asynchronous reset (active low)
-- incr_step      : incrementation step (N_adr_ROM bits)
--                  => sinus frequency
-----------------------------------------------------
--                OUTPUT
-----------------------------------------------------
-- dds_out        : output data (N_data bits)
-----------------------------------------------------

entity dds_sinus is 
  generic(
    N_data     : positive := 16;
    M          : positive := 12
  );
  port (    
    resetn      : in  std_logic;
    clk         : in  std_logic;
    en          : in  std_logic;    
    dds_out     : out std_logic_vector(N_data-1 downto 0)
    );
end dds_sinus;

architecture RTL of dds_sinus is
  constant amplitude   : positive :=2**(N_data-1)-1;
  constant incr_step   : std_logic_vector(M-1 downto 0):= (0=>'1', others => '0');
  constant N_adr_ROM   : positive := M-2;
  constant middle      : unsigned (N_data-1 downto 0) := to_unsigned(2**(dds_out'length-1),dds_out'length); --(N_data-1=>'1', others => '0');
  
  signal counter       : unsigned(N_adr_ROM+1 downto 0);  
  signal address_sinus : std_logic_vector(N_adr_ROM-1 downto 0);
  signal data_sinus    : std_logic_vector(N_data-2 downto 0);
  signal dds_out_int   : std_logic_vector(dds_out'range);
  
  begin
------------------------------------
--   counter & output register
------------------------------------  
 process(resetn, clk) is
 begin
     if resetn = '0'        then 
       counter               <= (others => '0');
       dds_out               <= (others => '0');
       dds_out(dds_out'high) <= '1';
     elsif rising_edge(clk) then 
       if en = '1' then 
         counter  <= counter+unsigned(incr_step);
         dds_out  <= dds_out_int;  
       end if;   
     end if;
 end process;

-------------------------------------
-- address & signed output
-------------------------------------
 
address_sinus <= std_logic_vector(counter(address_sinus'range)) 
                  when counter <2**(N_adr_ROM) or (counter >= 2**(N_adr_ROM+1) and counter <2**(N_adr_ROM)*3)
          else std_logic_vector(2**(N_adr_ROM)-1 - counter(address_sinus'range)) ;
            
dds_out_int <= std_logic_vector(middle - unsigned(data_sinus)) 
	               when  (counter >=(2**(N_adr_ROM+1)) + unsigned(incr_step)) or (counter <unsigned(incr_step))
	             else  std_logic_vector(middle + unsigned(data_sinus));

------------------------------------------------------
--        sinus : fonction tabulee du quart de sinus
------------------------------------------------------            
tab_sinus: entity work.rom_sinus 
  generic map (
    N_data     => N_data,
    N_adr_ROM  => N_adr_ROM,
    amplitude  => amplitude
  )  
  port map(		   
    address   => address_sinus,
    clk       => clk, 
    data      => data_sinus
  );
	 
end architecture;