----------------------------------------------
-- MSS copyright 1999-2015
-- File: BIAS_REMOVAL.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: COM1202
-- Edit date: 2/19/15
-- Revision: 5d 
--
-- Description: Bias removal. Estimate DC bias averaged over 1000 symbols. 
--
--# Adders/Subtractors                                   : 2
-- 15-bit subtractor                                     : 2
--# Accumulators                                         : 2
-- 24-bit up accumulator                                 : 2
--# Registers                                            : 29
-- Flip-Flops                                            : 29
--# Multiplexers                                         : 2
-- 14-bit 2-to-1 multiplexer                             : 2
--Timing Summary:
-----------------
--Speed Grade: -2
--   Minimum period: 4.543ns (Maximum Frequency: 220.107MHz)
--
-- Rev 2 8/7/05 AZ
-- Added simulation initialization.
--
-- Rev 3 4/22/12 AZ
-- Extend to NBITS precision.
-- Add clear/reset
--
-- Rev 4 1/17/13 AZ
-- Prevent 'X' propagation in accumulator during simulation
-- 
-- Rev 5 7/4/14 AZ
-- Reduce jitter (previously too high for demodulating higher order modulations such as QAM)
--
-- Rev 5b 11/13/14 AZ Vivado tools misunderstood the statement (something = 'X') as true
-- had to work around it.
--
-- Rev 5c 2/5/15 Migrated SIMULATION to a generic parameter
--
-- Rev 5d 2/19/15 prevent X accumulation during simulation: covers both I and Q cases
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity BIAS_REMOVAL is
 	generic (
		NBITS: integer := 14;	-- input/output precision
		SIMULATION: std_logic := '0'
	);
	port (
	 	--// CLOCKS, RESET
		SYNC_RESET: in std_logic;
			-- clears bias history (for example at the start of a burst)
 		CLK: in std_logic;
 			-- system clock for synchronous design.

	  	--// INPUTS
      DATA_I_IN: in std_logic_vector((NBITS-1) downto 0);
      DATA_Q_IN: in std_logic_vector((NBITS-1) downto 0);
        	-- input sample from A/D converter. format: signed (2's complement) 
			-- read at rising edge of CLK when SAMPLE_CLK_IN = '1'	
		SAMPLE_CLK_IN: in std_logic;
			-- one CLK wide pulse.
			-- Maximum rate: one SAMPLE_CLK_IN per CLK.

		--// CONTROLS
		SYMBOL_CLK_IN: in std_logic;
			-- Symbol clock. Does not need to be aligned with SAMPLE_CLK_IN.
			-- It is used primarily to scale the DC bias averaging time to 
			-- approximately 1024 symbols. 
			-- one CLK wide pulse.
		ENABLE: in std_logic;
			-- '0' to disable/bypass the DC bias removal, '1' to enable.

 		--// OUTPUTS
		DATA_I_OUT: out std_logic_vector((NBITS-1) downto 0);
		DATA_Q_OUT: out std_logic_vector((NBITS-1) downto 0);
			-- signal after bias removal. 
			-- read at rising edge of CLK when SAMPLE_CLK_OUT = '1'
	  	SAMPLE_CLK_OUT: out std_logic
			-- latency is 1 CLK
	);
end entity;
    
architecture behavioral of BIAS_REMOVAL is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
	component SIM2OUTFILE is
		 Generic (
			FILENAME: string
		 );
		 Port ( 
			--// CLK, RESET
			CLK: in std_logic;
				-- Reference clock
			DATA1: in std_logic_vector(11 downto 0);
			DATA2: in std_logic_vector(11 downto 0);
			DATA3: in std_logic_vector(11 downto 0);
			SAMPLE_CLK_IN: in std_logic
				-- DATA_IN is read at the rising edge of CLK when SAMPLE_CLK_IN = '1'

	);
	END COMPONENT;


-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
signal DATA_I_IN_E: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA_I_IN_BIAS: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA_I_IN_MINUS_BIAS: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA_I_IN_MINUS_BIAS_E: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal DATA_I_IN_ACC: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal DATA_I_OUT_local: std_logic_vector((NBITS-1) downto 0) := (others => '0');
signal DATA_Q_IN_E: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA_Q_IN_BIAS: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA_Q_IN_MINUS_BIAS: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA_Q_IN_MINUS_BIAS_E: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal DATA_Q_IN_ACC: std_logic_vector((NBITS+12) downto 0) := (others => '0');

-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin

-- Input: DATA_I_IN/Q. 12-bit 2's complement.
-- Bias is computed on the basis of a running average of 1000 successive samples.
DATA_I_IN_E <= sxt(DATA_I_IN, NBITS+1);	-- extend precision to prevent overflow
DATA_I_IN_BIAS <= sxt(DATA_I_IN_ACC((NBITS+12) downto 13),NBITS+1);	-- bias (acc / 1024)
DATA_I_IN_MINUS_BIAS <= DATA_I_IN_E - DATA_I_IN_BIAS;	-- remove bias
DATA_I_IN_MINUS_BIAS_E <= sxt(DATA_I_IN_MINUS_BIAS, NBITS+13);

DATA_Q_IN_E <= sxt(DATA_Q_IN, NBITS+1);	-- extend precision to prevent overflow
DATA_Q_IN_BIAS <= sxt(DATA_Q_IN_ACC((NBITS+12) downto 13),NBITS+1);	-- bias (acc / 1024)
DATA_Q_IN_MINUS_BIAS <= DATA_Q_IN_E - DATA_Q_IN_BIAS;	-- remove bias
DATA_Q_IN_MINUS_BIAS_E <= sxt(DATA_Q_IN_MINUS_BIAS, NBITS+13);

BIAS_REMOVAL_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			DATA_I_IN_ACC <= (others => '0');
			DATA_Q_IN_ACC <= (others => '0');
		elsif(SYMBOL_CLK_IN = '1') and ((SIMULATION = '0') or ((DATA_I_IN(NBITS-1) /= 'X') and (DATA_Q_IN(NBITS-1) /= 'X'))) then
			-- Average over 8192 symbols.
			DATA_I_IN_ACC <= DATA_I_IN_ACC + DATA_I_IN_MINUS_BIAS_E;
			DATA_Q_IN_ACC <= DATA_Q_IN_ACC + DATA_Q_IN_MINUS_BIAS_E;
	  	end if;
 	end if;
end process;

-- make sure there is no overflow or underflow after removing bias
BIAS_REMOVAL_002: process(CLK, ENABLE)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_OUT <= SAMPLE_CLK_IN;
		if(SAMPLE_CLK_IN = '1') then
			if(ENABLE = '1') then
				if(DATA_I_IN_MINUS_BIAS(NBITS downto (NBITS-1)) = "10") then
					-- clamp at most negative
					DATA_I_OUT_local(NBITS-1) <= '1';
					DATA_I_OUT_local((NBITS-2) downto 1) <= (others => '0');
					DATA_I_OUT_local(0) <= '1';
				elsif(DATA_I_IN_MINUS_BIAS(NBITS downto (NBITS-1)) = "01") then
					-- clamp at most positive
					DATA_I_OUT_local(NBITS-1) <= '0';
					DATA_I_OUT_local((NBITS-2) downto 0) <= (others => '1');
				else
					-- within nominal range. Restore 12-bit sample precision.
					DATA_I_OUT_local <= DATA_I_IN_MINUS_BIAS((NBITS-1) downto 0);
				end if;
			else
				-- disable/bypass DC bias removal
				DATA_I_OUT_local <= DATA_I_IN;
		  	end if;
	  	end if;
 	end if;
end process;
DATA_I_OUT <= DATA_I_OUT_local;

BIAS_REMOVAL_003: process(CLK, ENABLE)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			if(ENABLE = '1') then
				if(DATA_Q_IN_MINUS_BIAS(NBITS downto (NBITS-1)) = "10") then
					-- clamp at most negative
					DATA_Q_OUT(NBITS-1) <= '1';
					DATA_Q_OUT((NBITS-2) downto 1) <= (others => '0');
					DATA_Q_OUT(0) <= '1';
				elsif(DATA_Q_IN_MINUS_BIAS(NBITS downto (NBITS-1)) = "01") then
					-- clamp at most positive
					DATA_Q_OUT(NBITS-1) <= '0';
					DATA_Q_OUT((NBITS-2) downto 0) <= (others => '1');
				else
					-- within nominal range. Restore 12-bit sample precision.
					DATA_Q_OUT <= DATA_Q_IN_MINUS_BIAS((NBITS-1) downto 0);
				end if;
			else
				-- disable/bypass DC bias removal
				DATA_Q_OUT <= DATA_Q_IN;
		  	end if;
	  	end if;
 	end if;
end process;

--// simulation. send to a tab-delimited text file for subsequent (Matlab) plot.
--SIM2OUTFILE_X: if(SIMULATION = '1') generate
--	Inst_SIM2OUTFILE1: SIM2OUTFILE 
--	generic map(
--		FILENAME => "br.txt"
--	)
--	PORT MAP(
--		CLK => CLK,
--		DATA1 => DATA_I_IN((NBITS-1) downto (NBITS-12)),
--		DATA2 => DATA_I_IN_BIAS(NBITS downto NBITS-11),	
--		DATA3 => DATA_I_OUT_local((NBITS-1) downto NBITS-12),	
--		SAMPLE_CLK_IN => '1'
--	);
--end generate;

end behavioral;
