--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:   17:16:42 13.05.2007
-- Design Name:   tb_nco
-- Module Name:   tb_nco.vhd
-- Project Name:  nco
-- Target Device:  
-- Tool versions:  
-- Description:   
-- 
--------------------------------------------------------------------------------
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 altera;
--use altera.altera_syn_attributes.all;

library work;
use work.fixed_util_pkg.all;

	ENTITY syn_ddc IS
	Generic 
	(
		nbits_in			: integer := 16;
		nbits_in_frac		: integer := 14;
		nbits_out			: integer := 32;
		nbits_out_frac		: integer := 30;
		lo_nbits_phase		: integer := 24;
		lo_nbits_freq		: integer := 24;
		cic_nstages			: integer := 5;
		cic_ratio			: integer := 25;
		cic_scale_nbits		: integer := 22
	);
	PORT
	(
		rst					: in  std_logic;

		adc_clk				: in  std_logic;
		adc_da				: in  signed(15 downto 0);
		adc_ofa				: in  std_logic;
		adc_rand			: out std_logic;
		adc_pga				: out std_logic;
		adc_dith			: out std_logic;
		adc_shdn			: out std_logic;

		usb_usbclk			: in  std_logic;
		usb_ifclk			: in  std_logic;
		usb_fd				: out signed(15 downto 0);
		usb_pktend			: out std_logic;
		usb_fifoadr			: out unsigned(1 downto 0);
		usb_slcs_n			: out std_logic;
		usb_slwr_n			: out std_logic;
		usb_slrd_n			: out std_logic;
		usb_sloe			: out std_logic;
		usb_flaga			: in  std_logic;
		usb_flagb			: in  std_logic;
		usb_flagc			: in  std_logic;
		
		cl_clk_out			: out std_logic;
		cl_dout				: out unsigned(20 downto 0);
		cl_pd_n				: out std_logic;
		
		reg_aen				: in  std_logic;
		reg_dio				: inout unsigned(7 downto 0);
		reg_oe				: in  std_logic;
		reg_we				: in  std_logic

	);
	END syn_ddc;

ARCHITECTURE behavior OF syn_ddc IS 

	-- Component Declaration for the Unit Under Test (UUT)
	COMPONENT ddc
	GENERIC 
	(
		nbits_in			: integer;
		nbits_in_frac		: integer;
		nbits_out			: integer;
		nbits_out_frac		: integer;
		lo_nbits_phase		: integer;
		lo_nbits_freq		: integer;
		cic_nstages			: integer;
		cic_ratio			: integer;
		cic_scale_nbits		: integer
	);
	PORT
	(
		rst					: in std_logic;
		clk					: in std_logic;
		lo_nco_rst			: in std_logic;
		lo_nco_en			: in std_logic;
		lo_freq_in			: in unsigned(lo_nbits_freq-1 downto 0);
		lo_phase_in			: in unsigned(lo_nbits_phase-1 downto 0);
		din_vld				: in  std_logic;
		din_i				: in  sfixed;
		din_q				: in  sfixed;
		dec_vld				: out std_logic;
		dec_i				: out sfixed;
		dec_q				: out sfixed;
		dout_vld			: out std_logic;
		dout_i				: out sfixed;
		dout_q				: out sfixed
	);
	END COMPONENT;

	--Outputs
	SIGNAL ddc_din_i		: sfixed(shi(nbits_in, nbits_in_frac) downto slo(nbits_in, nbits_in_frac));
	SIGNAL ddc_din_q		: sfixed(shi(nbits_in, nbits_in_frac) downto slo(nbits_in, nbits_in_frac));
	SIGNAL ddc_dout_i		: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));
	SIGNAL ddc_dout_q		: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));

	SIGNAL dec_vld			: std_logic;
	SIGNAL ddc_dec_i		: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));
	SIGNAL ddc_dec_q		: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));


	SIGNAL reg_lo_nco_rst	: std_logic;
	SIGNAL reg_lo_nco_en	: std_logic;
	SIGNAL reg_lo_freq_in	: unsigned(31 downto 0);
	SIGNAL reg_lo_phase_in	: unsigned(31 downto 0);

	SIGNAL reg_en_addr		: std_logic;
	SIGNAL reg_en_data		: std_logic;
	SIGNAL reg_addr_last	: std_logic;
	SIGNAL reg_data_last	: std_logic;
	SIGNAL reg_addr_async 	: unsigned (7 downto 0);
	SIGNAL reg_data_async 	: unsigned (7 downto 0);
	SIGNAL reg_addr			: unsigned (7 downto 0);
	SIGNAL reg_data		 	: unsigned (7 downto 0);

	SIGNAL fifo_usb_we		: std_logic;
	SIGNAL fifo_usb_re		: std_logic;
	SIGNAL fifo_usb_ff		: std_logic;
	SIGNAL fifo_usb_ef		: std_logic;
	SIGNAL fifo_usb_aff		: std_logic;
	SIGNAL fifo_usb_aef		: std_logic;
	
	type fifo_usb_array_t is array (0 to 3) of unsigned (15 downto 0);
	SIGNAL fifo_usb_in	 	: fifo_usb_array_t;
	SIGNAL fifo_usb_out	 	: unsigned (15 downto 0);
	
	SIGNAL cl_clk			: std_logic;
	SIGNAL fifo_cl_we		: std_logic;
	SIGNAL fifo_cl_re		: std_logic;
	SIGNAL fifo_cl_ff		: std_logic;
	SIGNAL fifo_cl_ef		: std_logic;
	SIGNAL fifo_cl_aff		: std_logic;
	SIGNAL fifo_cl_aef		: std_logic;
	
	SIGNAL fifo_cl_in	 	: unsigned (63 downto 0);
	SIGNAL fifo_cl_out	 	: unsigned (63 downto 0);

	SIGNAL fifo_usb_we_pipe	: unsigned(3 downto 0);
	SIGNAL enable			: std_logic;
	SIGNAL ddc_din_vld		: std_logic;
	SIGNAL ddc_dout_vld		: std_logic;

	SIGNAL cl_rst_pipe	 	: unsigned (63 downto 0);
	SIGNAL cl_rst			: std_logic;
	SIGNAL cl_phase			: unsigned(1 downto 0);
	SIGNAL cl_data		 	: unsigned(20 downto 0);
	subtype cl_clkgen_cnt_t is natural range 0 to (2*cic_ratio/2/5)-1;
	SIGNAL cl_clkgen_cnt	: cl_clkgen_cnt_t;

BEGIN

	-- Instantiate the Unit Under Test (UUT)
	inst_ddc : ddc
	GENERIC MAP 
	(
		nbits_in			=> nbits_in,
		nbits_in_frac		=> nbits_in_frac,
		nbits_out			=> nbits_out,
		nbits_out_frac		=> nbits_out_frac,
		lo_nbits_phase		=> lo_nbits_phase,
		lo_nbits_freq		=> lo_nbits_freq,
		cic_nstages			=> cic_nstages,
		cic_ratio			=> cic_ratio,
		cic_scale_nbits		=> cic_scale_nbits
	)
	PORT MAP
	(
		rst					=> rst,
		clk					=> adc_clk,
		lo_nco_rst			=> reg_lo_nco_rst,
		lo_nco_en			=> reg_lo_nco_en,
		lo_freq_in			=> reg_lo_freq_in(lo_nbits_freq-1 downto 0),
		lo_phase_in			=> reg_lo_phase_in(lo_nbits_phase-1 downto 0),
		din_vld				=> ddc_din_vld,
		din_i				=> ddc_din_i,
		din_q				=> ddc_din_q,
		dec_vld				=> dec_vld,
		dec_i				=> ddc_dec_i,
		dec_q				=> ddc_dec_q,
		dout_vld			=> ddc_dout_vld,
		dout_i				=> ddc_dout_i,
		dout_q				=> ddc_dout_q
	);

reg_dio	<= reg_addr when reg_oe = '1' else (others => 'Z');

----------------------------------------------------------
-- Register control
----------------------------------------------------------
proc_reg_da_async:
	process(reg_we)
	begin
		if rising_edge(reg_we) then
			if reg_aen = '1' then
				reg_addr_async <= reg_dio;
			else	
				reg_data_async <= reg_dio;
			end if;
		end if;
	end process;
	
proc_reg_da_sync:
	process(adc_clk)
--	process(usb_usbclk)
	begin
		if rising_edge(adc_clk) then
--		if rising_edge(usb_usbclk) then
			if rst = '1' then
				reg_en_addr		<= '0';
				reg_en_data		<= '0';
				reg_addr_last	<= '0';
				reg_data_last	<= '0';
			elsif reg_we = '1' then
				reg_en_addr		<= reg_aen and not reg_addr_last;
				reg_en_data		<= not (reg_aen or reg_data_last);
				reg_addr_last	<= reg_aen;
				reg_data_last	<= not reg_aen;
			else
				reg_addr_last	<= '0';
				reg_data_last	<= '0';
			end if;	
			reg_data		<= reg_data_async;
			reg_addr		<= reg_addr_async;
		end if;
	end process;

	-- Registers
proc_register_write:
	process(adc_clk)
--	process(usb_usbclk)
	begin
		if rising_edge(adc_clk) then
--		if rising_edge(usb_usbclk) then
			if rst = '1' then
				reg_lo_nco_rst	<= '1';
				reg_lo_nco_en	<= '0';
			elsif reg_en_data = '1' then
				case reg_addr is
				when X"00" =>		-- Control
					reg_lo_nco_rst	<= reg_data(0);
					reg_lo_nco_en	<= reg_data(1);
					
				when X"10"	=>		-- freqency
					reg_lo_freq_in(7 downto 0)		<= reg_data;
					
				when X"11"	=>		-- freqency
					reg_lo_freq_in(15 downto 8)		<= reg_data;
			
				when X"12"	=>		-- freqency
					reg_lo_freq_in(23 downto 16)	<= reg_data;

				when X"13"	=>		-- freqency
					reg_lo_freq_in(31 downto 24)	<= reg_data;

				when X"20"	=>		-- phase
					reg_lo_phase_in(7 downto 0)		<= reg_data;
					
				when X"21"	=>		-- phase
					reg_lo_phase_in(15 downto 8)	<= reg_data;
			
				when X"22"	=>		-- phase
					reg_lo_phase_in(23 downto 16)	<= reg_data;

				when X"23"	=>		-- phase
					reg_lo_phase_in(31 downto 24)	<= reg_data;
					
				when others => null;
				end case;
			end if;
		end if;
	end process;

----------------------------------------------------------
-- DDC input
----------------------------------------------------------
proc_adc_input_register:
	process(adc_clk)
	begin
		if rising_edge(adc_clk) then
			ddc_din_i	<= sfixed(resize(adc_da, nbits_in));
			ddc_din_vld	<= reg_lo_nco_en;
		end if;	
	end process;

	ddc_din_q	<= to_sfixed(0.0, shi(nbits_in, nbits_in_frac), slo(nbits_in, nbits_in_frac));
	
	adc_rand	<= '0';
	adc_pga		<= '0';
	adc_dith	<= '0';
	adc_shdn	<= not reg_lo_nco_en;

----------------------------------------------------------
-- USB FIFO output
----------------------------------------------------------
proc_usb_interleave_iq:
	process(adc_clk)
	variable i, q : unsigned(31 downto 0);
	begin
		if rising_edge(adc_clk) then
			if rst = '1' then
				fifo_usb_we_pipe			<= (others => '0');
			elsif ddc_dout_vld = '1' then
				fifo_usb_we_pipe			<= (others => '1');
				i					:= to_unsigned(ufixed(ddc_dout_i));
				q					:= to_unsigned(ufixed(ddc_dout_q));
				fifo_usb_in(0)		<= i(15 downto 0);
				fifo_usb_in(1)		<= i(31 downto 16);
				fifo_usb_in(2)		<= q(15 downto 0);
				fifo_usb_in(3)		<= q(31 downto 16);
			elsif fifo_usb_ff = '0' then
				fifo_usb_we_pipe	<= fifo_usb_we_pipe(fifo_usb_we_pipe'left-1 downto 0) & '0';
				fifo_usb_in(0)		<= fifo_usb_in(1);
				fifo_usb_in(1)		<= fifo_usb_in(2);
				fifo_usb_in(2)		<= fifo_usb_in(3);
			end if;	
		end if;	
	end process;

inst_usbfifo: entity work.fifo_async
	GENERIC MAP 
	(
		addr_width			=> 7,
		data_width			=> 16,
		almost_full_thresh	=> 96,
		almost_empty_thresh	=> 32
	)
	PORT MAP
	(
		rst				=> rst,
		clk_w			=> adc_clk,
		clk_r			=> usb_ifclk,
		we				=> fifo_usb_we,
		re				=> fifo_usb_re,
		data_w			=> fifo_usb_in(0),
		data_r			=> fifo_usb_out,
		fifo_full		=> fifo_usb_ff,
		fifo_empty		=> fifo_usb_ef,
		fifo_afull		=> fifo_usb_aff,
		fifo_aempty		=> fifo_usb_aef
		
	);

	fifo_usb_we	<= fifo_usb_we_pipe(fifo_usb_we_pipe'left);
	usb_fd		<= signed(fifo_usb_out);
	fifo_usb_re	<= usb_flaga;
	usb_slcs_n	<= '0';
	usb_slwr_n	<= not usb_flaga;
	usb_slrd_n	<= '1';
	usb_sloe	<= '0';


----------------------------------------------------------
-- Channel-Link output
----------------------------------------------------------
inst_cl_fifo: entity work.fifo_async
	GENERIC MAP 
	(
		addr_width			=> 4,
		data_width			=> 64,
		almost_full_thresh	=> 96,
		almost_empty_thresh	=> 32
	)
	PORT MAP
	(
		rst				=> rst,
		clk_w			=> adc_clk,
		clk_r			=> cl_clk,
		we				=> fifo_cl_we,
		re				=> fifo_cl_re,
		data_w			=> fifo_cl_in,
		data_r			=> fifo_cl_out,
		fifo_full		=> fifo_cl_ff,
		fifo_empty		=> fifo_cl_ef,
		fifo_afull		=> fifo_cl_aff,
		fifo_aempty		=> fifo_cl_aef
		
	);

	fifo_cl_in			<= to_unsigned(ufixed(ddc_dout_i)) & to_unsigned(ufixed(ddc_dout_i));
	fifo_cl_we			<= ddc_dout_vld;
	fifo_cl_re			<= '1';
	
proc_cl_clkgen:
	process(adc_clk)
	begin
		if rising_edge(adc_clk) then
			if rst = '1' then
				cl_rst_pipe		<= (others => '1');
				cl_clkgen_cnt	<= cl_clkgen_cnt_t'high;
				cl_clk			<= '0';
			else
				if cl_clkgen_cnt = 0 then
					cl_clkgen_cnt	<= cl_clkgen_cnt_t'high;
					cl_clk			<= not cl_clk;
					cl_rst_pipe		<= cl_rst_pipe(cl_rst_pipe'left-1 downto 0) & '0';
				else
					cl_clkgen_cnt <= cl_clkgen_cnt - 1;
				end if;
			end if;
		end if;	
	end process;

	cl_rst	<= cl_rst_pipe(cl_rst_pipe'left);
	
proc_cl_interleave_iq:
	process(cl_clk)
	variable i, q : unsigned(31 downto 0);
	begin
		if rising_edge(cl_clk) then
			if cl_rst = '1' then
				cl_phase		<= (others => '0');
			else
				i := fifo_cl_out(31 downto 0);
				q := fifo_cl_out(63 downto 32);
				if cl_phase(1) = '0' then
					if cl_phase(0) = '0' then
						cl_data		<= not fifo_cl_ef & cl_phase & "00" & i(15 downto 0);
					else
						cl_data		<= not fifo_cl_ef & cl_phase & "00" & i(31 downto 16);
					end if;
				else
					if cl_phase(0) = '0' then
						cl_data		<= not fifo_cl_ef & cl_phase & "00" & q(15 downto 0);
					else
						cl_data		<= not fifo_cl_ef & cl_phase & "00" & q(31 downto 16);
					end if;
				end if;
				cl_phase	<= cl_phase + 1;
			end if;	
		end if;	
	end process;
	
	cl_dout			<= cl_data;	
	cl_clk_out		<= cl_clk;
	cl_pd_n			<= not cl_rst;

----------------------------------------------------------
END;
