-------------------------------------------------------------
-- MSS copyright 2011-2013
--	Filename:  AGC17.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 3
--	Date last modified: 4/13/13
-- Inheritance: 	n/a
--
-- Description:  AGC for burst demodulator. User is responsible for adjusting the 
-- response time to meet convergence and stability requirements. 
-- Overall objective is to prevent saturation at the external A/D converters while
-- making full use of the A/D converters dynamic range. 
-- The actual gain control is implemented in the analog front-end circuit. 
-- KEY ASSUMPTION: GAIN CONTROL IS INVERTED. 0 FOR MAXIMUM GAIN, ALL 1'S FOR MINIMUM GAIN
-- CONSISTENT WITH COM-3XXX GAIN CONTROL
-- 
-- Algorithm: From the magnitude of the complex or real input samples, 
-- generate an error signal. Feedback a fraction of the error signal
-- to the gain. The fraction determines the response time of this first-order loop. 
-- Measures the input signal magnitude. Finds peak magnitude within each symbol period.
-- A much higher loop gain (x16) is used during burst preambles (ACCELERATED_AGC_RESPONSE = '1') 
-- to converge quickly to the target gain.
--
-- Rev 1  12/6/11 AZ
-- corrected bug in peak detection.
--
-- Rev 2 11/4/12 AZ
-- added enable and initial gain 
--
-- Rev 3 4/13/13 AZ
-- The AGC_DAC interface has the same precision as the internal computation (before 4 LSbs were thrown out)
---------------------------------------------------------------

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

entity AGC17 is
    port ( 

	--// GLOBAL CLOCKS
   CLK : in std_logic;				-- System clock for this FPGA

	--// INPUT DATA STREAM
	DATA_M_IN: in std_logic_vector(13 downto 0);
		-- input magnitude. Unsigned
	SAMPLE_CLK_IN: in std_logic;
		-- sample clock. 

	--// CONTROLS 
	SYMBOL_CLK_IN: in std_logic;
		-- tells the AGC about the user-selected symbol rate
		-- used to control the AGC loop gain and prevent instabilities.
	AGC_ENABLE: IN std_logic;
		-- enable(1)/disable(0)
	AGC_INITIAL_GAIN: IN std_logic_vector(11 downto 0); 
		-- initial gain setting for the internal/external AGC. Also fixed gain setting when AGC is disabled.
		-- 12-bit unsigned. FFF represents the minimum gain, 000 the maximum gain.
	AGC_RESPONSE: in std_logic_vector(4 downto 0);
		-- Adjust the AGC response time. approximately log2(NSymbols). 
	ACCELERATED_AGC_RESPONSE : IN std_logic;		
		-- x4 increase in AGC responsiveness (for example during burst preamble)

 	--// AGC OUTPUTS
	AGC_DAC: out std_logic_vector(15 downto 0);
		-- gain control. May need to be inverted depending on the analog front-end.
		-- Here, maximum AGC_DAC really means minimum gain.
	AGC_DAC_SAMPLE_CLK: out std_logic;
		-- read AGC_DAC at the rising edge of CLK when AGC_DAC_SAMPLE_CLK = 1.
	
	--// TEST POINTS
	TP: out std_logic_vector(10 downto 1);
	CS1: out std_logic_vector(7 downto 0);
	CS1_CLK: out std_logic;
	CS2: out std_logic_vector(7 downto 0);
	CS2_CLK: out std_logic


			);
end entity;

architecture behavioral of AGC17 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
--------------------------------------------------------
-- 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

signal SYMBOL_CLK_IN_FLAG: std_logic := '0';
signal PEAK_M: std_logic_vector(13 downto 0) := (others => '0');
signal PEAK_M_FINAL: std_logic_vector(13 downto 0) := (others => '0');
constant PEAK_TARGET: std_logic_vector(13 downto 0) := "01" & x"000";	-- half of the ADC positive or negative range
--constant PEAK_TARGET: std_logic_vector(13 downto 0) := "00" & x"800";	-- to test AGC response in loopback mode, set lower target.
signal ERROR: std_logic_vector(14 downto 0) := (others => '0');
signal ERROR_SCALED: std_logic_vector(16 downto 0) := (others => '0');	-- signed
signal GAIN_PLUS: std_logic_vector(17 downto 0) := (others => '0');	-- signed, + extra room to detect overflow
signal GAIN_LOCAL: std_logic_vector(15 downto 0) := (others => '0');	-- positive
signal AGC_RESPONSE2: std_logic_vector(4 downto 0) := (others => '0');
signal SYMBOL_CLK_IN_D: std_logic := '0';
signal SYMBOL_CLK_IN_D2: std_logic := '0';


signal SIM_DATA1_X: std_logic_vector(11 downto 0) := (others => '0');
signal SIM_DATA2_X: std_logic_vector(11 downto 0) := (others => '0');
signal SIM_DATA3_X: std_logic_vector(11 downto 0) := (others => '0');

constant SIMULATION: std_logic := '0';
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------

begin

-- peak detection. Detect the peak magnitude between two successive symbols.
-- Similar function to the acquire peak detect in an oscilloscope.

-- SYMBOL_CLK_IN and SAMPLE_CLK_IN are not necessarily aligned. Update peak measurement at the 
-- first input sample following the SYMBOL_CLK_IN tick.
PEAK_DETECT_001:  process(CLK)
begin
	if rising_edge(CLK) then
		if(SYMBOL_CLK_IN = '1') then
			SYMBOL_CLK_IN_FLAG <= '1';
		elsif(SAMPLE_CLK_IN = '1') then
			SYMBOL_CLK_IN_FLAG <= '0';
		end if;
	end if;
end process;


PEAK_DETECT_002:  process(CLK)
begin
	if rising_edge(CLK) then
		SYMBOL_CLK_IN_D <= SAMPLE_CLK_IN and SYMBOL_CLK_IN_FLAG;
		
		if(SAMPLE_CLK_IN = '1') then
			if(SYMBOL_CLK_IN_FLAG = '1') then
				-- new symbol boundary
				PEAK_M <= DATA_M_IN;
				PEAK_M_FINAL <= PEAK_M;
			elsif(DATA_M_IN > PEAK_M) then
				PEAK_M <= DATA_M_IN;
			end if;
		end if;
	end if;
end process;

-- error signal is difference between actual peak magnitude and target value
ERROR <=  ext(PEAK_M_FINAL,15) - ext(PEAK_TARGET,15);

---- toggling prevention mechanism
---- Reason: every time the rx gain is changed, there could be unwanted impairements (i.e. bias being introduced)
--TOGGLING_PREVENTION: process(CLK)
--begin
--	if rising_edge(CLK) then
--		if(
--	end if;
--end process;

-- accelerated response? (x16 higher gain during during burst preamble when ACCELERATED_AGC_RESPONSE = '1'
ACCELERATED: process(CLK)
begin
	if rising_edge(CLK) then
		if(ACCELERATED_AGC_RESPONSE = '0') then
			AGC_RESPONSE2 <= AGC_RESPONSE;
		elsif(AGC_RESPONSE <= 4) then
			AGC_RESPONSE2 <= (others => '0');
		else
			AGC_RESPONSE2 <= AGC_RESPONSE -4;
		end if;
	end if;
end process;


-- scale error as a way to change the AGC loop response time.
-- keep 12-bits (same as DAC + 1 sign bit)
SCALE_001: process(CLK)
begin
	if rising_edge(CLK) then
		SYMBOL_CLK_IN_D2 <= SYMBOL_CLK_IN_D;
		
		if(SYMBOL_CLK_IN_D = '1') then
			case(AGC_RESPONSE2) is
				when "00000" => ERROR_SCALED <= sxt(ERROR(14 downto 0),17);
				when "00001" => ERROR_SCALED <= sxt(ERROR(14 downto 1),17);
				when "00010" => ERROR_SCALED <= sxt(ERROR(14 downto 2),17);
				when "00011" => ERROR_SCALED <= sxt(ERROR(14 downto 3),17);
				when "00100" => ERROR_SCALED <= sxt(ERROR(14 downto 4),17);
				when "00101" => ERROR_SCALED <= sxt(ERROR(14 downto 5),17);
				when "00110" => ERROR_SCALED <= sxt(ERROR(14 downto 6),17);
				when "00111" => ERROR_SCALED <= sxt(ERROR(14 downto 7),17);
				when "01000" => ERROR_SCALED <= sxt(ERROR(14 downto 8),17);
				when "01001" => ERROR_SCALED <= sxt(ERROR(14 downto 9),17);
				when "01010" => ERROR_SCALED <= sxt(ERROR(14 downto 10),17);
				when "01011" => ERROR_SCALED <= sxt(ERROR(14 downto 11),17);
				when "01100" => ERROR_SCALED <= sxt(ERROR(14 downto 12),17);
				when "01101" => ERROR_SCALED <= sxt(ERROR(14 downto 13),17);
				when others => ERROR_SCALED <= sxt(ERROR(14 downto 14),17);
			end case;
		end if;
	end if;
end process;

GAIN_PLUS <= ("00" & GAIN_LOCAL) + sxt(ERROR_SCALED, 18);	
	-- extra precision to detect underflow and overflow conditions

GAIN_CORRECTION_001:  process(CLK)
begin
	if rising_edge(CLK) then
		if (SYMBOL_CLK_IN_D2 = '1') then
			AGC_DAC_SAMPLE_CLK <= '1';

			if(AGC_ENABLE = '0') then
				-- fixed gain
				GAIN_LOCAL <= AGC_INITIAL_GAIN & "0000";
			elsif(GAIN_PLUS(17) = '1') then
				-- can't have negative gain
				GAIN_LOCAL <= (others => '0');
			elsif(GAIN_PLUS(17 downto 16) = "01") then
				-- positive overflow.
				GAIN_LOCAL <= (others => '1');
			else
				GAIN_LOCAL <= GAIN_PLUS(15 downto 0);
			end if;
		else
			AGC_DAC_SAMPLE_CLK <= '0';
		end if;
	end if;
end process;

AGC_DAC <= GAIN_LOCAL;

--// Test points TBD
TP(1) <= '1' when (GAIN_LOCAL = 0) else '1';

--// simulation. send to a tab-delimited text file for subsequent (Matlab) plot.
SIM2OUTFILE_X: if(SIMULATION = '1') generate
	SIM_DATA1_X <= ext(DATA_M_IN(13 downto 3),12);
	SIM_DATA2_X <= ext(PEAK_M_FINAL(13 downto 3),12);
	SIM_DATA3_X <= ext(GAIN_LOCAL(15 downto 5),12);
	
	Inst_SIM2OUTFILE: SIM2OUTFILE 
	generic map(
		FILENAME => "agc17.txt"
	)
	PORT MAP(
		CLK => CLK,
		DATA1 => SIM_DATA1_X,
		DATA2 => SIM_DATA2_X,
		DATA3 => SIM_DATA3_X,
		SAMPLE_CLK_IN => '1'
	);
end generate;

end behavioral;
