--------------------------------------------------------------------------------
-- 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.math_utility_pkg.all;
use ieee_proposed.fixed_pkg.all;

library work;
use work.utils_pkg.all;
use work.fixed_util_pkg.all;
use work.filter_pkg.all;
use work.fir_pkg.all;

ENTITY ddc IS
	Generic 
	(
		nbits_in			: integer := 18;
		nbits_in_frac		: integer := 16;
		nbits_out			: integer := 18;
		nbits_out_frac		: integer := 16;
		lo_nbits_phase		: integer := 20;
		lo_nbits_freq		: integer := 20;
		cic_nstages			: integer := 3;
		cic_ratio			: integer := 64;
		cic_scale_nbits		: integer := 18
	);
	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_i				: in  sfixed;
		din_q				: in  sfixed;
		din_vld				: in  std_logic;
		dec_i				: out sfixed;
		dec_q				: out sfixed;
		dec_vld				: out std_logic;
		dout_i				: out sfixed;
		dout_q				: out sfixed;
		dout_vld			: out std_logic
	);
END ddc;

ARCHITECTURE behavior OF ddc IS 

	-- Component Declaration for the Unit Under Test (UUT)
	COMPONENT mix
	GENERIC
	(
		nbits_din		: integer;
		nbits_din_frac	: integer;
		nbits_dout		: integer;
		nbits_dout_frac	: integer;
		nbits_phase		: integer;
		nbits_freq		: integer
	);
	PORT
	(
		rst				: in std_logic;
		clk				: in std_logic;
		nco_rst			: in std_logic;
		nco_en			: in std_logic;
		freq_in			: in unsigned(nbits_freq-1 downto 0);
		phase_in		: in unsigned(nbits_phase-1 downto 0);
		din_i			: in  sfixed;
		din_q			: in  sfixed;
		dout_i			: out sfixed;
		dout_q			: out sfixed;
		din_vld			: in std_logic;
		dout_vld		: out std_logic
	);
	END COMPONENT;

	COMPONENT cic_decim_pipe
	GENERIC
	(
		nstages			: integer;
		ratio			: integer;
		scale_nbits		: integer;
		in_width		: integer;
		in_width_frac	: integer;
		out_width		: integer;
		out_width_frac	: integer
	);
	PORT
	(
		clk				: in std_logic;
		rst				: in std_logic;
		vld_in			: in std_logic;
		din				: in sfixed;
		dout			: out sfixed;
		vld_out 		: out std_logic
	);
	END COMPONENT;

	COMPONENT fifo_sync
	GENERIC 
	(
		addr_width			: natural;
		data_width			: natural;
		almost_full_thresh	: integer;
		almost_empty_thresh	: integer
	);
	PORT
	(
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		we			: in  STD_LOGIC;
		re			: in  STD_LOGIC;
		fifo_full	: out STD_LOGIC;
		fifo_empty	: out STD_LOGIC;
		fifo_afull	: out STD_LOGIC;
		fifo_aempty	: out STD_LOGIC;
		data_w		: in  unsigned (data_width-1 downto 0);
		data_r		: out unsigned (data_width-1 downto 0)
	);
	END COMPONENT;

	COMPONENT fir_semi_parallel
	GENERIC
	(
		ntaps_per_stage		: integer;
		nstages				: integer;
		mul_latency			: integer;
		nbits_in			: integer;
		nbits_in_frac		: integer;
		nbits_stages		: integer;
		nbits_stages_frac	: integer;
		nbits_out			: integer;
		nbits_out_frac		: integer
	);
	PORT
	(
		rst			: in std_logic;
		clk			: in std_logic;
		h_addr		: in natural range 0 to nstages*ntaps_per_stage-1;
		h_we		: in std_logic;
		h_in		: in sfixed;
		din_vld		: in std_logic;
		din			: in sfixed;
		dout_vld	: out std_logic;
		rdy			: out std_logic;
		dout		: out sfixed
	);
	END COMPONENT;
	
	constant lo_nbits_in			: integer := nbits_in;
	constant lo_nbits_in_frac		: integer := nbits_in_frac;
	constant lo_nbits_out			: integer := nbits_in;
	constant lo_nbits_out_frac		: integer := nbits_in_frac;
	
	constant cic_nbits_in			: integer := lo_nbits_out;
	constant cic_nbits_in_frac		: integer := lo_nbits_out_frac;
	constant cic_nbits_out			: integer := nbits_out+1;
	constant cic_nbits_out_frac		: integer := nbits_out_frac+1;

	constant cfir_mul_latency		: integer := 5;
	constant cfir_ntaps				: integer := 63;
	constant cfir_nstages			: integer := 2;
	constant cfir_nbits_in			: integer := cic_nbits_out;
	constant cfir_nbits_in_frac		: integer := cic_nbits_out_frac;
	constant cfir_nbits_out			: integer := nbits_out;
	constant cfir_nbits_out_frac	: integer := nbits_out_frac;
	constant cfir_nbits_stages		: integer := cfir_nbits_in;
	constant cfir_nbits_stages_frac	: integer := cfir_nbits_in_frac;

	constant cfir_ntaps_per_stage	: integer := NextPowerOfTwo(cfir_ntaps)/cfir_nstages;

	SIGNAL mix_dout_i			: sfixed(shi(lo_nbits_out, lo_nbits_out_frac) downto slo(lo_nbits_out, lo_nbits_out_frac));
	SIGNAL mix_dout_q			: sfixed(shi(lo_nbits_out, lo_nbits_out_frac) downto slo(lo_nbits_out, lo_nbits_out_frac));
	SIGNAL mix_dout_vld			: std_logic;

	SIGNAL dec_dout_i			: sfixed(shi(cic_nbits_out, cic_nbits_out_frac) downto slo(cic_nbits_out, cic_nbits_out_frac));
	SIGNAL dec_dout_q			: sfixed(shi(cic_nbits_out, cic_nbits_out_frac) downto slo(cic_nbits_out, cic_nbits_out_frac));
	SIGNAL dec_dout_vld_i		: std_logic;
	SIGNAL dec_dout_vld_q		: std_logic;

	SIGNAL cfir_h_data			: sfixed(shi(cfir_nbits_stages, cfir_nbits_stages_frac) downto slo(cfir_nbits_stages, cfir_nbits_stages_frac));
	SIGNAL cfir_h_addr			: natural range 0 to NextPowerOfTwo(cfir_ntaps)-1;
	SIGNAL cfir_dout_i			: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));
	SIGNAL cfir_dout_q			: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));
	SIGNAL cfir_h_we			: std_logic;
	SIGNAL cfir_vld_i			: std_logic;
	SIGNAL cfir_vld_q			: std_logic;
	SIGNAL cfir_ready_i			: std_logic;
	SIGNAL cfir_ready_q			: std_logic;
	SIGNAL cfir_start_i			: std_logic;
	SIGNAL cfir_start_q			: std_logic;
	SIGNAL cfir_din_vld_i		: std_logic;
	SIGNAL cfir_din_vld_q		: std_logic;
	SIGNAL cfir_din_i			: sfixed(shi(cfir_nbits_in, cfir_nbits_in_frac) downto slo(cfir_nbits_in, cfir_nbits_in_frac));
	SIGNAL cfir_din_q			: sfixed(shi(cfir_nbits_in, cfir_nbits_in_frac) downto slo(cfir_nbits_in, cfir_nbits_in_frac));
	SIGNAL cfir_write_i			: std_logic;
	SIGNAL cfir_write_q			: std_logic;
	
	SIGNAL fifo_empty_i			: std_logic;
	SIGNAL fifo_empty_q			: std_logic;
	SIGNAL fifo_full_i			: std_logic;
	SIGNAL fifo_full_q			: std_logic;
	SIGNAL fifo_din_i			: unsigned(cfir_nbits_in-1 downto 0);
	SIGNAL fifo_din_q			: unsigned(cfir_nbits_in-1 downto 0);
	SIGNAL fifo_dout_i			: unsigned(cfir_nbits_in-1 downto 0);
	SIGNAL fifo_dout_q			: unsigned(cfir_nbits_in-1 downto 0);

	constant cfir_coef 			: real_array_t(0 to cfir_ntaps-1) := FilterCoef_Lowpass(cfir_ntaps, 0.40, 1.0);

	type h_mem_t is array (0 to cfir_ntaps-1) of sfixed(shi(cfir_nbits_stages, cfir_nbits_stages_frac) downto slo(cfir_nbits_stages, cfir_nbits_stages_frac));

	function to_h_mem(coef_real : real_array_t; ntaps : integer; proto : sfixed) return h_mem_t is
	variable res : h_mem_t;
	begin
		for i in 0 to ntaps-1 loop
			res(i) := to_sfixed(coef_real(i), proto, fixed_wrap, fixed_round);
		end loop;
		
		return res;
	end to_h_mem;	

	SIGNAL hclr_mode			: std_logic;
	CONSTANT h_mem				: h_mem_t := to_h_mem(cfir_coef, cfir_ntaps, cfir_h_data);

BEGIN

--	assert false report "cfir_h_addr_i'length = " & natural'image(cfir_h_addr_i'length) & "." severity note;
 	dec_vld		<= dec_dout_vld_i and dec_dout_vld_q;
 	dec_i		<= resize(dec_dout_i, shi(nbits_out, nbits_out_frac), slo(nbits_out, nbits_out_frac), fixed_wrap, fixed_round);
 	dec_q		<= resize(dec_dout_q, shi(nbits_out, nbits_out_frac), slo(nbits_out, nbits_out_frac), fixed_wrap, fixed_round);
	
	dout_vld	<= cfir_vld_i and cfir_vld_q;
	dout_i		<= resize(cfir_dout_i, shi(nbits_out, nbits_out_frac), slo(nbits_out, nbits_out_frac), fixed_wrap, fixed_round);
	dout_q		<= resize(cfir_dout_q, shi(nbits_out, nbits_out_frac), slo(nbits_out, nbits_out_frac), fixed_wrap, fixed_round);

--	cfir_start	<= dec_dout_vld_i and dec_dout_vld_q;
	
	-- Instantiate the Unit Under Test (UUT)
	inst_mixer: mix 
	GENERIC MAP
	(
		nbits_din			=> lo_nbits_in,
		nbits_din_frac		=> lo_nbits_in_frac,
		nbits_dout			=> lo_nbits_out,
		nbits_dout_frac		=> lo_nbits_out_frac,
		nbits_phase			=> lo_nbits_phase,
		nbits_freq			=> lo_nbits_freq
	)
	PORT MAP
	(
		rst					=> rst,
		clk					=> clk,
		nco_rst				=> lo_nco_rst,
		nco_en				=> lo_nco_en,
		freq_in				=> lo_freq_in,
		phase_in			=> lo_phase_in,
		din_i				=> din_i,
		din_q				=> din_q,
		dout_i				=> mix_dout_i,
		dout_q				=> mix_dout_q,
		din_vld				=> din_vld,
		dout_vld			=> mix_dout_vld
	);

	inst_cic_decim_pipe_I : cic_decim_pipe
	GENERIC MAP
	(
		nstages				=> cic_nstages,
		ratio				=> cic_ratio,
		scale_nbits			=> cic_scale_nbits,
		in_width			=> cic_nbits_in,
		in_width_frac		=> cic_nbits_in_frac,
		out_width			=> cic_nbits_out,
		out_width_frac		=> cic_nbits_out_frac
	)
	PORT MAP
	(
		clk					=> clk,
		rst					=> rst,
		vld_in				=> mix_dout_vld,
		din					=> mix_dout_i,
		dout				=> dec_dout_i,
		vld_out				=> dec_dout_vld_i
	);

	inst_cic_decim_pipe_Q : cic_decim_pipe
	GENERIC MAP
	(
		nstages				=> cic_nstages,
		ratio				=> cic_ratio,
		scale_nbits			=> cic_scale_nbits,
		in_width			=> cic_nbits_in,
		in_width_frac		=> cic_nbits_in_frac,
		out_width			=> cic_nbits_out,
		out_width_frac		=> cic_nbits_out_frac
	)
	PORT MAP
	(
		clk					=> clk,
		rst					=> rst,
		vld_in				=> mix_dout_vld,
		din					=> mix_dout_q,
		dout				=> dec_dout_q,
		vld_out				=> dec_dout_vld_q
	);

	cfir_I : entity work.fir_semi_parallel
	GENERIC MAP
	(
		ntaps_per_stage		=> cfir_ntaps_per_stage,
		nstages				=> cfir_nstages,
		mul_latency			=> cfir_mul_latency,
		nbits_in			=> cfir_nbits_in,
		nbits_in_frac		=> cfir_nbits_in_frac,
		nbits_stages		=> cfir_nbits_stages,
		nbits_stages_frac	=> cfir_nbits_stages_frac,
		nbits_out			=> cfir_nbits_out,
		nbits_out_frac		=> cfir_nbits_out_frac
	)
	PORT MAP
	(
		rst					=> rst,
		clk					=> clk,
		h_addr				=> cfir_h_addr,
		h_we				=> cfir_h_we,
		h_in				=> cfir_h_data,
		din_vld				=> cfir_din_vld_i,
		din					=> cfir_din_i,
		dout_vld			=> cfir_vld_i,
		start				=> cfir_start_i,
		rdy					=> cfir_ready_i,
		dout				=> cfir_dout_i
	);
	
	cfir_Q : entity work.fir_semi_parallel
	GENERIC MAP
	(
		ntaps_per_stage		=> cfir_ntaps_per_stage,
		nstages				=> cfir_nstages,
		mul_latency			=> cfir_mul_latency,
		nbits_in			=> cfir_nbits_in,
		nbits_in_frac		=> cfir_nbits_in_frac,
		nbits_stages		=> cfir_nbits_stages,
		nbits_stages_frac	=> cfir_nbits_stages_frac,
		nbits_out			=> cfir_nbits_out,
		nbits_out_frac		=> cfir_nbits_out_frac
	)
	PORT MAP
	(
		rst					=> rst,
		clk					=> clk,
		h_addr				=> cfir_h_addr,
		h_we				=> cfir_h_we,
		h_in				=> cfir_h_data,
		din_vld				=> cfir_din_vld_q,
		din					=> cfir_din_q,
		dout_vld			=> cfir_vld_q,
		start				=> cfir_start_q,
		rdy					=> cfir_ready_q,
		dout				=> cfir_dout_q
	);
	
	cfir_din_vld_i	<= not fifo_empty_i and cfir_ready_i and cfir_write_i;
	cfir_start_i	<= fifo_empty_i and cfir_write_i;

	fifo_din_i	<= to_unsigned(ufixed(dec_dout_i)); 
	cfir_din_i	<= sfixed(fifo_dout_i);
	
-- 	cfir_din_vld_i	<= dec_dout_vld_i;
-- 	cfir_start_i	<= dec_dout_vld_i;
-- 
-- 	cfir_din_i		<= dec_dout_i; 
	
inst_fifo_sync_i : fifo_sync
	GENERIC MAP 
	(
		addr_width			=> 1,
		data_width			=> cfir_nbits_in,
		almost_full_thresh	=> 1,
		almost_empty_thresh	=> 1
	)
	PORT MAP
	(
		rst	=> rst,
		clk	=> clk,
		we	=> dec_dout_vld_i,
		re	=> cfir_write_i,
		fifo_full	=> fifo_full_i,
		fifo_empty	=> fifo_empty_i,
		fifo_afull	=> open,
		fifo_aempty	=> open,
		data_w	=> fifo_din_i,
		data_r	=> fifo_dout_i
	);

	cfir_din_vld_q	<= not fifo_empty_q and cfir_ready_q and cfir_write_q;
	cfir_start_q	<= fifo_empty_q and cfir_write_q;

	fifo_din_q	<= to_unsigned(ufixed(dec_dout_q)); 
	cfir_din_q	<= sfixed(fifo_dout_q);
	
-- 	cfir_din_vld_q	<= dec_dout_vld_q;
-- 	cfir_start_q	<= dec_dout_vld_q;
-- 
-- 	cfir_din_q		<= dec_dout_q; 

inst_fifo_sync_q : fifo_sync
	GENERIC MAP 
	(
		addr_width			=> 1,
		data_width			=> cfir_nbits_in,
		almost_full_thresh	=> 1,
		almost_empty_thresh	=> 1
	)
	PORT MAP
	(
		rst	=> rst,
		clk	=> clk,
		we	=> dec_dout_vld_q,
		re	=> cfir_write_q,
		fifo_full	=> fifo_full_q,
		fifo_empty	=> fifo_empty_q,
		fifo_afull	=> open,
		fifo_aempty	=> open,
		data_w	=> fifo_din_q,
		data_r	=> fifo_dout_q
	);

cfir_write_logic_i:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				cfir_write_i <= '0';
			elsif fifo_full_i = '1' then
				cfir_write_i <= '1';
			elsif fifo_empty_i = '1' then
				cfir_write_i <= '0';
			end if;
		end if;
	end process;
			
cfir_write_logic_q:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				cfir_write_q <= '0';
			elsif fifo_full_q = '1' then
				cfir_write_q <= '1';
			elsif fifo_empty_q = '1' then
				cfir_write_q <= '0';
			end if;
		end if;
	end process;
			
coef_load_counter:
	process(clk)
	variable count			: natural range 0 to NextPowerOfTwo(cfir_ntaps)-1;
	variable we				: std_logic;
	begin
		if rising_edge(clk) then
			cfir_h_addr <= count;	
			cfir_h_we	<= we;
			if rst = '1' then
				we := '1';
				hclr_mode <= '1';
				count := 0;
			else
				if hclr_mode = '1' then
					cfir_h_data <= to_sfixed(0.0, cfir_h_data);
					if count /= NextPowerOfTwo(cfir_ntaps)-1 then
						we := '1';
						count	:= count + 1;
					else
						count := 0;
						hclr_mode <= '0';
					end if;
				else
					cfir_h_data <= h_mem(count);
					if count /= cfir_ntaps-1 then
						we := '1';
						count	:= count + 1;
					else
						we := '0';
					end if;
				end if;
			end if;
		end if;
	end process;
						

END;
