-------------------------------------------------------------
--	Filename:  CIC_INTERP4.VHD
-- Authors: Adam Kwiatkowski
--		  Alain Zarembowitch 	 
--	Version: 3
--	Date last modified: 8/23/17
-- Inheritance: 	CIC_INTERPOL3 5/27/08
--
-- Objective: Cascaded Integrated Comb interpolation filter.
-- Simplified configuration: N=2, M=1, R is programmable but always a power of 2.
-- Use R = 1 to bypass this filter.
-- Simplified version of the more versatile CIC_INTERPOL.VHD
-- output sampling frequency determined by SAMPLE_CLK_OUT_REQ
--
-- Implementation: The precision of the CIC internal signals is extended to 60-bit
-- at the input. This limits the range of recommended control parameters so that
-- Bin + Nlog2(RM) < 61. 
--
-- For more details, see "CIC Filter Introduction", Matthew P. Donadio
-- and Xilinx LogicCore documentation on "Cascaded Integrated-Comb (CIC) Filter v1.0".
--
-- rev2 6/14/13 AZ
-- Added initializations for simulation
-- Corrected bug  SAMPLE19A_CLK (instead of SAMPLE19_CLK)
-- Enforced strict ratio R between output samples and input samples (previous versions could 
-- saturate if the user did not guarantee this ratio at all times)
-- Then the final clamping stage may not be necessary (commented out)
-- Optimized code size (no need for BOUT+1 bits during comb phase)
--
-- Rev 3 8/23/17 AZ
-- Added generic parameter NBITS
---------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity CIC_INTERP4 is
	generic(
		NBITS: integer
			-- input/output samples precision
    );
    port ( 

	--// GLOBAL CLOCKS, RESET
   CLK : in std_logic;				-- Master clock for this FPGA
	SYNC_RESET: in std_logic;
		-- MANDATORY
		-- reset all upon configuration change.. otherwise an annoying DC offset 
		-- may be present on the output and cause saturation.
	
	--// INPUT DATA STREAM
	DATA_IN: in std_logic_vector(NBITS-1 downto 0);
		-- input samples, NBITS precision
		-- read at rising edge of CLK when SAMPLE_CLK_IN = '1'.
		-- Format: 2's complement 
	SAMPLE_CLK_IN: in std_logic;
		-- input sampling rate. Must equal the requested output sampling rate / R.

	--// CONTROL PARAMETERS
	R: in std_logic_vector(23 downto 0);
		-- Interpolation factor. Simplified here: MUST be a power of 2
		-- Set R = 1 to bypass.
		-- Maximum value 2^23 (limiting the range could save resources. Change BOUT in this case)

	--// OUTPUT DATA STREAM
	DATA_OUT: out std_logic_vector(NBITS-1 downto 0);   
		-- Format: 2's complement
	SAMPLE_CLK_OUT: out std_logic;
	SAMPLE_CLK_OUT_REQ: in std_logic;
		-- requested output sampling rate
	SATURATION: out std_logic
		-- '1' when interpolation circuit saturates. Reduce input signal level when this occurs.
		-- Important test point, as the effects of saturation (spectrum degradation) can be difficult to detect.
			);
end entity;

architecture behavioral of CIC_INTERP4 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------

--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- Suffix _E indicates an extended precision version of the net with the same name
-- Suffix _N indicates an inverted version of the net with the same name
-- Suffic _INC indicates increment.

--constant BOUT: integer  := 31;	-- limit range to 32 bits for simulation purposes only. compile with larger precision
--constant BOUT: integer  := NBITS-1+46;  -- NBITS-1 input + 2*log2(R(2^23 max))	
	-- computation precision to accommodate bit growth (total array length BOUT+1)
constant BOUT: integer  := NBITS+31;  -- NBITS input + 2*log2(R(2^16 max))	
	-- computation precision to accommodate bit growth (total array length BOUT+1)
constant BIN: integer  := DATA_IN'left;	
	-- computation precision to accommodate bit growth (total array length BIN+1)
signal TRUNCATE_OBITS: std_logic_vector(5 downto 0) := (others => '0');
		-- number of least significant bits to be truncated at the output. 
		-- Must be set to N*log2(RM), which is the bit growth during the CIC
		-- calculation due to the gain G = (RM)^N.
		-- Valid range 0 - BOUT 

signal DATA_IN_D: std_logic_vector((BIN+1) downto 0)  := (others => '0');
signal DATA_IN_D2: std_logic_vector((BIN+1) downto 0)  := (others => '0');

--// comb
signal SAMPLE_CLK_IN0: std_logic := '0';
signal SAMPLE1_CLK: std_logic := '0';
signal DATA1: std_logic_vector((BIN+1) downto 0) := (others => '0');
signal DATA1_X: std_logic_vector((BIN+2) downto 0) := (others => '0');
signal DATA1_D: std_logic_vector((BIN+2) downto 0) := (others => '0');
signal DATA2: std_logic_vector((BIN+2) downto 0) := (others => '0');
signal DATA2_X: std_logic_vector(BOUT downto 0) := (others => '0');

signal COMB_CLK: std_logic := '0';
signal COMB_CLK_D: std_logic := '0';

--// integrators
signal R_CNTR: std_logic_vector(23 downto 0) := (others => '0');
signal SAMPLE11_CLK: std_logic := '0';
signal DATA11: std_logic_vector(BOUT downto 0) := (others => '0');
signal SAMPLE12_CLK: std_logic := '0';
signal DATA12: std_logic_vector(BOUT downto 0) := (others => '0');
signal SAMPLE19_CLK: std_logic := '0';
signal DATA19: std_logic_vector(BOUT downto 0) := (others => '0');
signal SAMPLE19A_CLK: std_logic := '0';
signal DATA19A: std_logic_vector(BOUT downto 0) := (others => '0');

signal SAMPLE20_CLK: std_logic := '0';
signal DATA20: std_logic_vector(BOUT downto 0) := (others => '0');
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------

begin
-- compute the number of output bits to truncate
TRUNCATE_OBITS_GEN: process(CLK, R)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			if(R(0) = '1') then
				TRUNCATE_OBITS <= "000000";
			elsif(R(1) = '1') then
				TRUNCATE_OBITS <= "000010";
			elsif(R(2) = '1') then
				TRUNCATE_OBITS <= "000100";
			elsif(R(3) = '1') then
				TRUNCATE_OBITS <= "000110";
			elsif(R(4) = '1') then
				TRUNCATE_OBITS <= "001000";
			elsif(R(5) = '1') then
				TRUNCATE_OBITS <= "001010";
			elsif(R(6) = '1') then
				TRUNCATE_OBITS <= "001100";
			elsif(R(7) = '1') then
				TRUNCATE_OBITS <= "001110";
			elsif(R(8) = '1') then
				TRUNCATE_OBITS <= "010000";
			elsif(R(9) = '1') then
				TRUNCATE_OBITS <= "010010";
			elsif(R(10) = '1') then
				TRUNCATE_OBITS <= "010100";
			elsif(R(11) = '1') then
				TRUNCATE_OBITS <= "010110";
			elsif(R(12) = '1') then
				TRUNCATE_OBITS <= "011000";
			elsif(R(13) = '1') then
				TRUNCATE_OBITS <= "011010";
			elsif(R(14) = '1') then
				TRUNCATE_OBITS <= "011100";
			elsif(R(15) = '1') then
				TRUNCATE_OBITS <= "011110";
			elsif(R(16) = '1') then
				TRUNCATE_OBITS <= "100000";
			elsif(R(17) = '1') then
				TRUNCATE_OBITS <= "100010";
			elsif(R(18) = '1') then
				TRUNCATE_OBITS <= "100100";
			elsif(R(19) = '1') then
				TRUNCATE_OBITS <= "100110";
			elsif(R(20) = '1') then
				TRUNCATE_OBITS <= "101000";
			elsif(R(21) = '1') then
				TRUNCATE_OBITS <= "101010";
			elsif(R(22) = '1') then
				TRUNCATE_OBITS <= "101100";
			else
				TRUNCATE_OBITS <= "101110";
			end if;
		end if;
	end if;
end process;


--// Reclock input signal for best timing. 
-- Extend precision by 1 
COMB_CLK <= SAMPLE_CLK_IN0;	-- reclock with output clock / R for an exact interpolation ratio (otherwise algorithm diverges)
RECLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			COMB_CLK_D <= '0';
			DATA_IN_D <= (others => '0');
		else
			COMB_CLK_D <= COMB_CLK;
			if(COMB_CLK = '1') then
				-- sign extension
				DATA_IN_D <= sxt(DATA_IN, BIN+2);
			end if;	
		end if;
	end if;
end process;  
  
--// N=2 comb stages
-- Comb stage 1 
COMB1: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE1_CLK <= '0';
			DATA_IN_D2 <= (others => '0');
			DATA1 <= (others => '0');
		else
			SAMPLE1_CLK <= COMB_CLK_D;
			
			if(COMB_CLK_D = '1') then
				DATA_IN_D2 <= DATA_IN_D;
				DATA1 <= DATA_IN_D - DATA_IN_D2;
			end if;
		end if;
	end if;
end process;

-- Comb stage 2 
DATA1_X <= sxt(DATA1, BIN+3);
COMB2: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			DATA1_D <= (others => '0');
			DATA2 <= (others => '0');
		else
			if(SAMPLE1_CLK = '1') then
				DATA1_D <= DATA1_X;
				DATA2 <= DATA1_X - DATA1_D;
			end if;
		end if;
	end if;
end process;

--// Rate Expansion ----------------------------------
-- make sure the expansion ratio is always EXACTLY R, no less, no more
R_CNTR_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			R_CNTR <= R;
		elsif(SAMPLE_CLK_OUT_REQ = '1') then
			if(R_CNTR = 1) then
				-- re-arm modulo-R counter
				R_CNTR <= R;
				SAMPLE_CLK_IN0 <= '1';
			else
				R_CNTR <= R_CNTR - 1;
				SAMPLE_CLK_IN0 <= '0';
			end if;
		end if;
	end if;
end process;

--// 2 Integrator stages
-- Integrator stage 1
DATA2_X <= sxt(DATA2, BOUT+1);
INTEGRATOR_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE11_CLK <= '0';
			DATA11 <= (others => '0');
		else
			SAMPLE11_CLK <= SAMPLE_CLK_OUT_REQ;
			
			if(SAMPLE_CLK_OUT_REQ = '1') then
				DATA11 <= DATA11 + DATA2_X;
			end if;
		end if;
	end if;
end process;

-- Integrator stage 2
INTEGRATOR_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE12_CLK <= '0';
			DATA12 <= (others => '0');
		else
			SAMPLE12_CLK <= SAMPLE11_CLK;

			if(SAMPLE11_CLK = '1') then
				DATA12 <= DATA12 + DATA11;
			end if;
		end if;
	end if;
end process;


--// Select number of integrator stages N = 2
SAMPLE19_CLK <= SAMPLE12_CLK;
DATA19 <= DATA12;

--// Truncate signal
-- Shift in two steps. Coarse and Fine.
COARSE_SHIFT_001: process(CLK, TRUNCATE_OBITS)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE19A_CLK <= '0';
			DATA19A <= (others => '0');
		else
			SAMPLE19A_CLK <= SAMPLE19_CLK;		

			if(SAMPLE19_CLK = '1')  then
				case (TRUNCATE_OBITS(5 downto 3)) is 
					when "000" => DATA19A <= DATA19;
					when "001" => DATA19A <= sxt(DATA19(BOUT downto 8), BOUT+1);
					when "010" => DATA19A <= sxt(DATA19(BOUT downto 16), BOUT+1);
					when "011" => DATA19A <= sxt(DATA19(BOUT downto 24), BOUT+1);
					when "100" => DATA19A <= sxt(DATA19(BOUT downto 32), BOUT+1);
					when others => DATA19A <= sxt(DATA19(BOUT downto 40), BOUT+1);
				end case;
			end if;
		end if;
	end if;
end process;

FINE_SHIFT_001: process(CLK, TRUNCATE_OBITS)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE20_CLK <= '0';
			DATA20 <= (others => '0');
		else
			SAMPLE20_CLK <= SAMPLE19A_CLK;		

			if(SAMPLE19A_CLK = '1')  then
				case (TRUNCATE_OBITS(2 downto 1)) is 
					-- TRUNCATE_OBITS is always even
					when "00" => DATA20 <= DATA19A;
					when "01" => DATA20 <= sxt(DATA19A(BOUT downto 2), BOUT+1);
					when "10" => DATA20 <= sxt(DATA19A(BOUT downto 4), BOUT+1);
					when others => DATA20 <= sxt(DATA19A(BOUT downto 6), BOUT+1);
				end case;
			end if;
		end if;
	end if;
end process;


-- reclock output. Wait until shift/truncate process is complete to send the
-- output sample.
RECLOCK_003: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			DATA_OUT <= (others => '0');
			SATURATION <= '0';
			SAMPLE_CLK_OUT <= '0';
		else
			SAMPLE_CLK_OUT <= SAMPLE20_CLK;
			
			-- do we really need to clamp the output? Not sure
			DATA_OUT <= DATA20(NBITS-1 downto 0);

--			if(R = 1) then
--				-- bypass this stage
--				DATA_OUT <= DATA_IN;
--			else
--				-- Get the output sample after shifting.
--				-- clamp before truncating MSbs
--				if(DATA20(BOUT) = '0') and (DATA20((BOUT-1) downto 13) /= 0) then
--					-- positive saturation after clamping
--					DATA_OUT <= "01111111111111";     
--					SATURATION <= '1';
--				elsif(DATA20(BOUT) = '1') and ((not DATA20((BOUT-1) downto 13)) /= 0) then
--					-- negative saturation after clamping
--					DATA_OUT <= "10000000000001";
--					SATURATION <= '1';
--				else
--					-- just truncate MSBs
--					DATA_OUT <= DATA20(13 downto 0);
--					SATURATION <= '0';
--				end if;
--			end if;
		end if;
	end if;
end process;


end Behavioral;
