----------------------------------------------
-- MSS copyright 2012-2016
-- Filename: DESPREAD_ID.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: none
-- Edit date: 11/16/16
-- Revision: 5
-- Description: 
--		Despread, integrate and dump. Coherent integration section.
--
-- Rev 1 6/12/12  AZ
-- Added monitoring for saturation and clamping.
--
-- Rev 2 1/18/13 AZ
-- Added reset to save power when not all detectors are enabled.
--
-- Rev 3 6/3/14 AZ
-- Increased TRUNCATE_OBITS range from 0-11 to 0-13
--
-- Rev 4 4/29/15 AZ
-- Added SQPN case where two distinct codes are spreading the I and Q channels.
-- Q code transition is 1/2 chip after I code transition
--
-- Rev 5 11/16/16 AZ
-- Corrected bug with unwarranted 1/2 chip Q-code delay (input Q-code is already delayed by 1/2 chip in dsss_demod_tdrss above)
---------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity DESPREAD_ID is
 	generic (
		NBITS: integer := 14	-- input/output precision
	);
  port (
	 	--// CLOCKS
 		CLK: in std_logic;
 			-- system clock for synchronous design.
		SYNC_RESET: in std_logic;
			-- '1' when unused, to save power. DATA_X_OUT is zero.

	  	--// 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.
		CODE_I: in std_logic;
		CODE_Q: in std_logic;
			-- despreading codes
		SOID: in std_logic;
			-- start of the coherent integrate and dump. 1 CLK wide pulse. Aligned with SAMPLE_CLK_IN.
		TRUNCATE_OBITS: in integer;
			-- truncate the number of output bits so as to keep the I&D output with the same number of bits as the input.
			-- range 0 to 13
		BPSK0_SQPN1: in std_logic;
				-- BPSK (0) vs SQPN(1)

	  	--// OUTPUTS
		-- latency: 2 CLKs after start of integrate and dump SOID
      DATA_I_OUT: out std_logic_vector((NBITS-1) downto 0);
      DATA_Q_OUT: out std_logic_vector((NBITS-1) downto 0);
		SAMPLE_CLK_OUT: out std_logic;
		
		-- monitoring for saturation. aligned with SAMPLE_CLK_OUT
		SATURATION: out std_logic

    );
end entity;

architecture behavior of DESPREAD_ID is

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------

--//-- DESPREADING -------------------------------
signal SAMPLE_CLK_IN_D: std_logic := '0';
signal SOID_D: std_logic := '0';
signal DATA1IA: std_logic_vector((NBITS-1) downto 0) := (others => '0');
signal DATA1QA: std_logic_vector((NBITS-1) downto 0) := (others => '0');
signal DATA1IB: std_logic_vector((NBITS-1) downto 0) := (others => '0');
signal DATA1QB: std_logic_vector((NBITS-1) downto 0) := (others => '0');
signal DATA1I: std_logic_vector(NBITS downto 0) := (others => '0');
signal DATA1Q: std_logic_vector(NBITS downto 0) := (others => '0');

--//-- COHERENT INTEGRATE AND DUMP -------------------------------
signal DATA2I: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal DATA2Q: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal DATA3I: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal DATA3Q: std_logic_vector((NBITS+12) downto 0) := (others => '0');
signal SAMPLE3_CLK: std_logic := '0';
signal NSHIFT: integer range -1 to 13 := -1;
signal SATURATION_I: std_logic := '0';
signal SATURATION_Q: std_logic := '0';

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

--//-- DESPREADING -------------------------------
DESPREADING_GEN: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE_CLK_IN_D <= '0';
			SOID_D <= '0';
			DATA1IA <= (others => '0');
			DATA1QA <= (others => '0');
			DATA1IB <= (others => '0');
			DATA1QB <= (others => '0');
		else
			SAMPLE_CLK_IN_D <= SAMPLE_CLK_IN;
			SOID_D <= SOID;
			
			if(SAMPLE_CLK_IN = '1') then
				-- despreading code 
				if(CODE_I  = '1') then
					DATA1IA <= DATA_I_IN;
					DATA1QA <= DATA_Q_IN;
				else
					-- Note: using not instead of the more rigorous not+1 is simpler, keeps symmetry and alleviates the need to check for overflow
					DATA1IA <= not DATA_I_IN;
					DATA1QA <= not DATA_Q_IN;
				end if;
				if(BPSK0_SQPN1 = '0') then
					-- BPSK only
					DATA1IB <= (others => '0');
					DATA1QB <= (others => '0');
				elsif(CODE_Q  = '1') then
					DATA1IB <= DATA_I_IN;
					DATA1QB <= DATA_Q_IN;
				else
					-- Note: using not instead of the more rigorous not+1 is simpler, keeps symmetry and alleviates the need to check for overflow
					DATA1IB <= not DATA_I_IN;
					DATA1QB <= not DATA_Q_IN;
				end if;
			end if;
		end if;
	end if;
end process; 
DATA1I <= sxt(DATA1IA,DATA1I'length) + sxt(DATA1QB,DATA1I'length);
DATA1Q <= sxt(DATA1QA,DATA1I'length) - sxt(DATA1IB,DATA1I'length);

--//-- COHERENT INTEGRATE AND DUMP -------------------------------
ID_001: process(CLK) 
begin
	if rising_edge(CLK) then
		SAMPLE3_CLK <= SOID_D;
		
		if(SAMPLE_CLK_IN_D = '1') then
			if(SOID_D = '1') then
				-- 1st sample in the integrate and dump
				DATA2I <= sxt(DATA1I(NBITS downto 1),DATA2I'length); 
				DATA2Q <= sxt(DATA1Q(NBITS downto 1),DATA2Q'length); 
			else
				DATA2I <= DATA2I + sxt(DATA1I(NBITS downto 1),DATA2I'length); 
				DATA2Q <= DATA2Q + sxt(DATA1Q(NBITS downto 1),DATA2Q'length); 
			end if;
		end if;
	end if;
end process; 

-- scaling output depending on the number of samples being integrated
-- NSHIFT decrements from TRUNCATE_OBITS to -1 (idle)
ID_002: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SAMPLE_CLK_OUT <= '0';
			NSHIFT <= -1;
			DATA_I_OUT <= (others => '0');	
			DATA_Q_OUT <= (others => '0');	
			SATURATION_I <= '0';
			SATURATION_Q <= '0';
		elsif(SOID_D = '1') then
			NSHIFT <= TRUNCATE_OBITS;
			DATA3I <= DATA2I;
			DATA3Q <= DATA2Q;
		elsif(NSHIFT = 0) then
			NSHIFT <= NSHIFT-1;
			-- send to output
			
			-- check for saturation
			if(DATA3I(DATA3I'left) = '0') and (DATA3I((DATA3I'left -1) downto (NBITS-1)) /= 0) then
				-- positive saturation
				DATA_I_OUT(NBITS-1) <= '0';
				DATA_I_OUT((NBITS-2) downto 0) <= (others => '1');
				SATURATION_I <= '1';
			elsif(DATA3I(DATA3I'left) = '1') and ((not DATA3I((DATA3I'left -1) downto (NBITS-1))) /= 0) then
				-- negative saturation
				DATA_I_OUT(NBITS-1) <= '1';
				DATA_I_OUT((NBITS-2) downto 0) <= (others => '0');
				SATURATION_I <= '1';
			else
				DATA_I_OUT <= DATA3I((NBITS-1) downto 0);
				SATURATION_I <= '0';
			end if;
			if(DATA3Q(DATA3Q'left) = '0') and (DATA3Q((DATA3Q'left -1) downto (NBITS-1)) /= 0) then
				-- positive saturation
				DATA_Q_OUT(NBITS-1) <= '0';
				DATA_Q_OUT((NBITS-2) downto 0) <= (others => '1');
				SATURATION_Q <= '1';
			elsif(DATA3Q(DATA3Q'left) = '1') and ((not DATA3Q((DATA3Q'left -1) downto (NBITS-1))) /= 0) then
				-- negative saturation
				DATA_Q_OUT(NBITS-1) <= '1';
				DATA_Q_OUT((NBITS-2) downto 0) <= (others => '0');
				SATURATION_Q <= '1';
			else
				DATA_Q_OUT <= DATA3Q((NBITS-1) downto 0);
				SATURATION_Q <= '0';
			end if;
			SAMPLE_CLK_OUT <= '1';
		elsif(NSHIFT /= -1) then
			NSHIFT <= NSHIFT-1;
			-- shift right one notch
			DATA3I((DATA3I'left -1) downto 0) <= DATA3I(DATA3I'left downto 1);
			DATA3Q((DATA3Q'left -1) downto 0) <= DATA3Q(DATA3Q'left downto 1);
		else
			SAMPLE_CLK_OUT <= '0';
		end if;
	end if;
end process; 

SATURATION <= SATURATION_I or SATURATION_Q;
end behavior;

