-------------------------------------------------------------
-- MSS copyright 2017
-- Filename:  COM1827_TX.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
--	Version: 2
--	Date last modified: 10/21/17
-- Inheritance: 	 BURST_PSK_QAM_APSK_MODULATOR.VHD  1/5/16
--
-- description:  Flexible continuous-phase modulator for burst or continuous mode.
-- Modulations are included in a look-up table. They include
-- OOK, ASK, PSK, QAM, APSK 
-- The input DATA_IN comprises 8 parallel data bits. 
-- Generic code, but tested over Xilinx 7 series FPGA target (Artix-7)

-- Usage: SYNC_RESET is MANDATORY!
--
-- Rev 2 10/21/17 AZ
-- Corrected bug spectrum inversion control
-- Increased ROM_FIL1 precision to 24 bits to minimize frequency error bias
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity COM1827_TX is
	generic (
		FIRST_PREAMBLE_EXTENSION: integer := 0; 
			-- Depending on the hardware AGC speed, a preamble may be needed to improve the sync word
			-- detection. 
			-- Expressed as a number of symbols.
			-- Depends on the receiver AGC design and on the modulation symbol rate. 
			-- Valid range 0 to max. 
			-- composite limit: preamble+sync+BURST_LENGTH < 8191
			-- Note: could be disabled at run-time by SYNC_EN (see below)
		SYNC_LENGTH: integer range 0 to 128 := 32;	 
			-- sync word length, expressed in number of symbols.
			-- Typical value 32 for a 2048 symbol frame (1.5% overhead)
			-- composite limit: preamble+sync+BURST_LENGTH < 8191
			-- Note: could be disabled at run-time by SYNC_EN (see below)
		SYNC_WORD: std_logic_vector(127 downto 0) := x"00000000000000000000000000000000";
			-- sync pattern. LEFT-aligned when SYNC_LENGTH is less than the maximum 128 bits.
			-- MSb is sent first.
		GAP_LENGTH: integer := 0;
			-- optional gap between sync word and payload field
			-- expressed in number of symbols
			-- justification: demodulator may need a short time to correct phase/amplitude/etc after detecting the sync word.
			-- In the case of multi-frame bursts, the gap is inserted only once, after the first sync word.
		SIMULATION: std_logic := '0'
			-- 1 during simulation. 
	);
    Port ( 
		--// CLK, RESET
		CLK: in std_logic;
			-- mod processing clock Global.
		SYNC_RESET: in std_logic;
			-- synchronous reset 
		-- usage: MANDATORY to start using this component.

		--// TX DATA INTERFACE -> TX BUFFER
		TX_DATA: in std_logic_vector(7 downto 0);
		TX_DATA_SAMPLE_CLK: in std_logic;
		TX_SOF: in std_logic;
		  -- 1 CLK-wide pulse at start of frame. Aligned with TX_DATA_SAMPLE_CLK.
        -- MANDATORY in burst mode
		TX_CTS: out std_logic;	
			-- byte-wide tx data path.  
			-- Clear To Send = transmit flow control. 
			-- check the CTS signal before sending TX_DATA
			-- MSb is sent first
			
		--// CONTROLS
		SYMBOL_RATE_NDIV: in std_logic_vector(3 downto 0);
			-- symbol rate expressed as (modulator processing clock)/2^NDIV
			-- Range 0 to 15 for division ratios ranging from 1 to 32768
		MODULATION_INDEX: in std_logic_vector(15 downto 0);
			-- user-specified modulation index. unsigned fixed-point format 4.12
		GAIN: in std_logic_vector(15 downto 0);
			-- output amplitude scaling factor. 15-bit unsigned
		TX_WINDOW : in std_logic;
			-- start of frame transmission is only allowed if TX_WINDOW = '1'
			-- Note: a single frame transmission can be triggered by making TX_WINDOW a one CLK pulse.
			-- Usage: 1 CLK-wide pulse for one frame, solid '1' for continuous transmission
			-- Usage: wait until after SYNC_RESET otherwise spurious transmission may occur
		CENTER_FREQ: in std_logic_vector(31 downto 0);
			-- modulated signal center frequency. Expressed as fc/modulator processing clock * 2^32
		MASTER_SLAVEN: in std_logic;
			-- only the master unit (base station) is allowed to send start of superframe sync words
			-- to synchronize the network. Slave units (remote units) use the regular sync word.
		BURST_LENGTH: in std_logic_vector(12 downto 0);	
			-- payload data field length in a burst or frame. Expressed in number of symbols.
			-- Constraint: BURST_LENGTH*NBITSPERSYMBOLS is a multiple of 8 bits.
			-- composite limit: preamble+sync+BURST_LENGTH < 8191
	   CONTROL: in std_logic_vector(15 downto 0);
 			-- bits 2:0 modulation order M. 
				-- 0: M=2, 1: M=4, 2: M=8, 3: M=3 SOQPSK precoder
			-- bits 7:4 frequency shaping filter selection
				-- 0 = rectangle (FSK, MSK)
				-- 1 = PCM/FM
				-- 2 = Gaussian BT=0.7
				-- 3 = Gaussian BT=0.5
				-- 4 = Gaussian BT=0.3
				-- 5 = SOQPSK-MIL
				-- 6 = SOQPSK-TG
				-- 7 = multi-h ARTM (Tier 2)
				-- 8 = GFSK BT=0.25
			-- bits 9:8 test mode: 00 no test, 01 = PRBS11, 11 = unmodulated carrier
			-- bit 10: '1' to enable sync word and/or preamble insertion. '0' to disable both.
			-- bit 11:  spectrum inversion. invert Q. on (1) or off (0)
			-- bit 13-15: unused (future)

		--// MODULATOR -> DACs
		-- modulated samples. 2's complement. Left aligned.
		-- Minimum effective precision 12-13 bits depending on the code within and the filtering type.
		DATA_I_OUT : OUT std_logic_vector(15 downto 0);
		DATA_Q_OUT : OUT std_logic_vector(15 downto 0);
		TX_EN_OUT: OUT std_logic;
            -- transmit enable, includes some margins around start and end of frame

		-- COMSCOPE test points
		CS1: out std_logic_vector(7 downto 0); 
		CS1_CLK: out std_logic;

		--// MONITORING
		SATURATION: out std_logic_vector(1 downto 0);
		SYMBOL_CLKx4_OUT: OUT std_logic;
			-- 1CLK-wide pulses at 4*symbol rate to set the AWGN bandwidth
		TP: out std_logic_vector(20 downto 1)

			);
end entity;

architecture Behavioral of COM1827_TX is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT LFSR11P
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		SAMPLE_CLK_OUT_REQ : IN std_logic;          
		DATA_OUT : OUT std_logic_vector(7 downto 0);
		SAMPLE_CLK_OUT : OUT std_logic;
		SOF_OUT : OUT std_logic
		);
	END COMPONENT;

    COMPONENT BURST_TX
	 GENERIC(
		FIRST_PREAMBLE_EXTENSION: integer;
		PREAMBLE_TYPE: integer range 0 to 2;
		SYNC_LENGTH: integer range 0 to 128;	 
		SYNC_WORD: std_logic_vector(127 downto 0);
		GAP_LENGTH: integer
	 );
	PORT(
		SYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		TX_DATA : IN std_logic_vector(7 downto 0);
		TX_DATA_SAMPLE_CLK : IN std_logic;
		TX_SOF: in std_logic;
		SYMBOL_RATE_NDIV : IN std_logic_vector(3 downto 0);
		TX_WINDOW : in std_logic;
		MASTER_SLAVEN: in std_logic;
		BURST_LENGTH : IN std_logic_vector(12 downto 0);
		NBITSPERSYMBOLS : IN std_logic_vector(2 downto 0);          
		SYNC_EN: in std_logic;
		TX_CTS : OUT std_logic;
		TX_MOD_DATA : OUT std_logic_vector(6 downto 0);
		TX_MOD_DATA_VALID : OUT std_logic;
		TX_MOD_SYMBOL_CLK : OUT std_logic;
		TX_MOD_SYMBOL_CLKx2 : OUT std_logic;
		TX_MOD_SYMBOL_CLKx4 : OUT std_logic;
		TX_MOD_SYMBOL_CLKx8 : OUT std_logic;
		TX_MOD_TYPE : OUT std_logic_vector(1 downto 0);
		TP : OUT std_logic_vector(20 downto 1)
		);
	END COMPONENT;

	COMPONENT ROM_FIL1
	GENERIC (
		NBITS: integer;
		NSAMPLESPERSYMBOL: integer;
		LMAX: integer range 1 to 8;
		SIMULATION: std_logic
	);	
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		FILT_SEL : IN std_logic_vector(3 downto 0);
		L_OUT: out std_logic_vector(3 downto 0);
		DATA_IN: in std_logic_vector((NBITS-1) downto 0);
		SYMBOL_CLK_IN : IN std_logic;
		SAMPLE_CLK_IN : IN std_logic;          
		SAMPLE_CLK_OUT : OUT std_logic;
		DATA_OUT : OUT std_logic_vector((NBITS-1) downto 0)
		);
	END COMPONENT;

	COMPONENT FIRHALFBAND3
	PORT(
		CLK : IN std_logic;
		SAMPLE_CLK_IN : IN std_logic;
		DATA_IN : IN std_logic_vector(13 downto 0);          
		SAMPLE_CLK_OUT : OUT std_logic;
		DATA_OUT : OUT std_logic_vector(13 downto 0)
		);
	END COMPONENT;

	COMPONENT CIC_INTERP4
	GENERIC (
		NBITS: integer
	);	
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		DATA_IN : IN std_logic_vector(NBITS-1 downto 0);
		SAMPLE_CLK_IN : IN std_logic;
		R : IN std_logic_vector(23 downto 0);
		SAMPLE_CLK_OUT_REQ : IN std_logic;          
		DATA_OUT : OUT std_logic_vector(NBITS-1 downto 0);
		SAMPLE_CLK_OUT : OUT std_logic;
		SATURATION : OUT std_logic
		);
	END COMPONENT;

	-- sin/cos look-up table, 4 quadrants, 16K steps x 18 bits precision
	component SIGNED_SIN_COS_TBL3 is
		generic (
			NADDR: integer
		);
		port (
			CLK: in std_logic;
			PHASE: in std_logic_vector(NADDR+1 downto 0);
			SIN_OUT: out std_logic_vector(17 downto 0);
			COS_OUT: out std_logic_vector(17 downto 0)
		);
	end component;
	
	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 _DR indicates rising edge (1st half of the symbol clock)
-- Suffix _DF indicates falling edge (2nd half of the symbol clock)
-- Suffix _X 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
-- Suffix _E indicates a one CLK early version of the net with the same name

signal SYNC_EN: std_logic := '0';

-- Pseudo-random generator PRBS-11. Test mode.
signal DATA1: std_logic_vector(7 downto 0) := x"00";
signal SAMPLE1_CLK: std_logic := '0';
signal SAMPLE1_CLK_REQ: std_logic := '0';

-- input selection: payload data or test sequences?
signal DATA2: std_logic_vector(7 downto 0) := x"00";
signal SAMPLE2_CLK: std_logic := '0';
signal SAMPLE2_CLK_REQ: std_logic := '0';
signal SOF2: std_logic := '0';

-- Data to symbols, burst or continuous mode
signal NBITSPERSYMBOLS: std_logic_vector(2 downto 0);
	-- number of bits per data symbol. Depends on the modulation type.
	-- valid values: 1,2,3		
signal DATA3: std_logic_vector(6 downto 0) := (others => '0');
signal DATA3_VALID: std_logic := '0';
signal SYMBOL3_CLK: std_logic := '0';
signal SYMBOL3_CLKx2: std_logic := '0';
signal SYMBOL3_CLKx4: std_logic := '0';
signal TX_MOD_TYPE3 : std_logic_vector(1 downto 0) := "11";
signal TX_EN_CNTR: integer range 0 to 20 := 0;

--//-- BIT TO SYMBOL MAPPING -----------------------------
signal DATA30_D: std_logic := '0';
signal DATA30_D2: std_logic := '0';
signal TOGGLE_DATA30: std_logic := '0';
signal DATA4: std_logic_vector(9 downto 0):= (others => '0');
signal SYMBOL4_CLK: std_logic := '0';
signal SYMBOL4_CLKx4: std_logic := '0';
signal DATA4_VALID: std_logic := '0';
signal TX_MOD_TYPE4 : std_logic_vector(1 downto 0) := "11";

--//-- MODULATION INDEX ---------------------------------
signal TOGGLE_ARTM_CPM: std_logic := '0'; 
signal MODULATION_INDEX1: signed(15 downto 0):= (others => '0');
signal DATA5: signed(25 downto 0):= (others => '0');
signal DATA5_VALID: std_logic := '0';
signal SYMBOL5_CLK: std_logic := '0';
signal SYMBOL5_CLKx4: std_logic := '0';
signal L: std_logic_vector(3 downto 0):= (others => '0');
signal TX_MOD_TYPE5 : std_logic_vector(1 downto 0) := "11";

--//-- PREMOD FREQUENCY SHAPING FILTER --------------------------------
signal DATA6: std_logic_vector(23 downto 0):= (others => '0');
signal DATA6A: std_logic_vector(23 downto 0):= (others => '0');
signal DATA6B: std_logic_vector(23 downto 0):= (others => '0');
signal SYMBOL6_CLKx4: std_logic := '0'; 
signal SYMBOL6A_CLKx4: std_logic := '0'; 
signal SYMBOL6A2_CLKx4: std_logic := '0'; 
signal SYMBOL6B_CLKx4: std_logic := '0'; 
signal DATA6_VALID_SHIFT: std_logic_vector(3 downto 0):= (others => '0');
signal DATA6_VALID2_SHIFT: std_logic_vector(31 downto 0):= (others => '0');
signal DATA6_VALID: std_logic := '0';
signal DATA6_VALID_D: std_logic := '0';
signal DATA6_VALID_D2: std_logic := '0';

signal PHASE_DEV: signed(23 downto 0):= (others => '0');
signal PHASE_DEV_SHIFT_CNTR: unsigned(3 downto 0) := (others => '0');
signal PHASE_DEV_SHIFT : signed(23 downto 0):= (others => '0');	
signal PHASE_DEV2 : signed(23 downto 0):= (others => '0');			
signal PHASE_DEV2B : signed(23 downto 0):= (others => '0');			
signal PHASE_DEV3 : std_logic_vector(23 downto 0):= (others => '0');			
signal CIC_R: std_logic_vector(23 downto 0):= (others => '0');

--//-- PHASE MODULATION  --------------------------------
signal FM_NCO_ACC: signed(23 downto 0) := (others => '0');
signal DATA7_I: std_logic_vector(17 downto 0):= (others => '0');
signal DATA7_Q: std_logic_vector(17 downto 0):= (others => '0');
signal DATA7_VALID: std_logic := '0';

--//-- GAIN CONTROL -------------------------
signal GAINx: std_logic_vector(16 downto 0):= (others => '0');
signal GAINs: signed(16 downto 0):= (others => '0');
signal DATA11_Is: signed(15 downto 0):= (others => '0');
signal DATA11_Qs: signed(15 downto 0):= (others => '0');
signal DATA11_VALID: std_logic := '0';
signal DATA12_I: signed(32 downto 0):= (others => '0');
signal DATA12_Q: signed(32 downto 0):= (others => '0');
signal DATA12_I_D: signed(32 downto 0):= (others => '0');
signal DATA12_Q_D: signed(32 downto 0):= (others => '0');
signal DATA12_VALID_D: std_logic := '0';

signal DATA13_I: std_logic_vector(15 downto 0):= (others => '0');
signal DATA13_Q: std_logic_vector(15 downto 0):= (others => '0');
signal SATURATION13_I: std_logic:= '0';
signal SATURATION13_Q: std_logic:= '0';


signal BURST_TX_TP: std_logic_vector(20 downto 1) := (others => '0');
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

SYNC_EN <= CONTROL(10);

-- MAY BE REDUNDANT WITH EXTERNAL LFSR11P
-- Pseudo-random generator PRBS-11. Test mode.
Inst_LFSR11P: LFSR11P PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	DATA_OUT => DATA1,
	SAMPLE_CLK_OUT => SAMPLE1_CLK,
	SOF_OUT => open,
	SAMPLE_CLK_OUT_REQ => SAMPLE1_CLK_REQ
);
SAMPLE1_CLK_REQ <= SAMPLE2_CLK_REQ when (CONTROL(9 downto 8) = "01") else '0';

-- input selection: payload data or test sequences?
DATA2 <= TX_DATA when (CONTROL(9 downto 8) = "00") else
			DATA1; --when (CONTROL(9 downto 8) = "01");
SAMPLE2_CLK <= TX_DATA_SAMPLE_CLK when (CONTROL(9 downto 8) = "00") else
			SAMPLE1_CLK; --when (CONTROL(9 downto 8) = "01");

SOF2 <= TX_SOF when (CONTROL(9 downto 8) = "00") else
			'0'; --when (CONTROL(9 downto 8) = "01");

-- number of bits per symbol depends on the selected modulation order M
DATA0_MASK_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(unsigned(CONTROL(2 downto 0)) = 0) then
			NBITSPERSYMBOLS <= "001";	-- M = 2
		elsif(unsigned(CONTROL(2 downto 0)) = 1) then
			NBITSPERSYMBOLS <= "010";	-- M = 4
		elsif(unsigned(CONTROL(2 downto 0)) = 2) then
			NBITSPERSYMBOLS <= "011";	-- M = 8
		else
		--elsif(unsigned(CONTROL(2 downto 0)) = 3) then
			NBITSPERSYMBOLS <= "001";	-- M = 3
		end if;
	end if;
end process;


-- Data to symbols, burst or continuous mode
-- Note: in this application, the maximum symbol rate is implemented with 2 samples per symbol. So we do not 
-- need to use the extra rising edge/ falling edge features.
BURST_TX_001: BURST_TX 
GENERIC MAP(
	FIRST_PREAMBLE_EXTENSION => FIRST_PREAMBLE_EXTENSION,
	--PREAMBLE_TYPE => 2,    -- pseudo random for cleaner spectrum
	PREAMBLE_TYPE => 1,    -- *033016 toggling 0/1 (less likely to trigger a false sync word detection than random)
	SYNC_LENGTH => SYNC_LENGTH,
	SYNC_WORD => SYNC_WORD, 
	GAP_LENGTH => GAP_LENGTH
)
PORT MAP(
	SYNC_RESET => SYNC_RESET,
	CLK => CLK,
	TX_DATA => DATA2,
	TX_DATA_SAMPLE_CLK => SAMPLE2_CLK,
	TX_SOF => SOF2, 
	TX_CTS => SAMPLE2_CLK_REQ,
	SYMBOL_RATE_NDIV => SYMBOL_RATE_NDIV,
	TX_WINDOW => TX_WINDOW,
	MASTER_SLAVEN => MASTER_SLAVEN,
	BURST_LENGTH => BURST_LENGTH,
	NBITSPERSYMBOLS => NBITSPERSYMBOLS,
	SYNC_EN => SYNC_EN,
	TX_MOD_DATA => DATA3,	
	TX_MOD_DATA_VALID => DATA3_VALID,
	TX_MOD_SYMBOL_CLK => SYMBOL3_CLK,
	TX_MOD_SYMBOL_CLKx2 => SYMBOL3_CLKx2,	-- for RRC filter
	TX_MOD_SYMBOL_CLKx4 => SYMBOL3_CLKx4,	-- for HBF interpolation
	TX_MOD_SYMBOL_CLKx8 => open,  -- unused
	TX_MOD_TYPE => TX_MOD_TYPE3,	-- whenever different modulation types are used during sync field and data field.			
	TP => BURST_TX_TP
);
TX_CTS <= SAMPLE2_CLK_REQ when (CONTROL(9 downto 8) = "00") else '0';

-- control external transmitter (including margins around the burst)
TX_EN_OUT_GEN: process(CLK)
begin
    if rising_edge(CLK) then
        if(TX_MOD_TYPE3(1) = '0') then
            -- preamble + data field. Enable the output
            TX_EN_OUT <= '1';
            TX_EN_CNTR <= 20;    -- re-arm tail margin counter. Extend by x symbols (to account for filter group delay + others)
        elsif(TX_EN_CNTR = 0) then
            TX_EN_OUT <= '0';
        elsif(SYMBOL3_CLK = '1') then
            TX_EN_CNTR <= TX_EN_CNTR - 1;
        end if;
    end if;
end process;

--//-- BIT TO SYMBOL MAPPING -----------------------------
-- Special case: SOQPSK precoding an = (-1)n+1(2an-1 -1)(an -an-2).
SOQPSK_MAP_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYMBOL3_CLK = '1') then
			DATA30_D <= DATA3(0);
			DATA30_D2 <= DATA30_D;
			TOGGLE_DATA30 <= not TOGGLE_DATA30;
		end if;
	end if;
end process;

-- Convert to unsigned representation before doing any maths on the samples.
-- Format is 4.6
REMAP_001: process(CLK)
begin
	if rising_edge(CLK) then
		SYMBOL4_CLK <= SYMBOL3_CLK;
		SYMBOL4_CLKx4 <= SYMBOL3_CLKx4;
		DATA4_VALID <= DATA3_VALID;
		TX_MOD_TYPE4 <= TX_MOD_TYPE3; 

		
		if(SYMBOL3_CLK = '1') then
			if(unsigned(CONTROL(2 downto 0)) = 2) then
				-- 8-ary FSK.  0 -> -7, 3 -> -1, 4 -> +1, 7 -> +7
				DATA4(9) <= not DATA3(2);
				DATA4(8 downto 7) <= DATA3(1 downto 0);
				DATA4(6) <= '1';
				DATA4(5 downto 0) <= (others => '0');
			elsif(unsigned(CONTROL(2 downto 0)) = 1) then
				-- 4-ary FSK.  0 -> -3, 1 -> -1, 2 -> +1, 3 -> +3
				DATA4(9) <= not DATA3(1);
				DATA4(8) <= not DATA3(1);
				DATA4(7) <= DATA3(0);
				DATA4(6) <= '1';
				DATA4(5 downto 0) <= (others => '0');
			elsif(unsigned(CONTROL(2 downto 0)) = 3) then
				-- SOQPSK case: binary to ternary sequence
				if(DATA3(0) = DATA30_D2) then
					DATA4 <= (others => '0');
				elsif(DATA3(0) = DATA30_D) then
					DATA4(9) <= TOGGLE_DATA30;
					DATA4(8) <= TOGGLE_DATA30;
					DATA4(7) <= TOGGLE_DATA30;
					DATA4(6) <= '1';
					DATA4(5 downto 0) <= (others => '0');
				else
					DATA4(9) <= not TOGGLE_DATA30;
					DATA4(8) <= not TOGGLE_DATA30;
					DATA4(7) <= not TOGGLE_DATA30;
					DATA4(6) <= '1';
					DATA4(5 downto 0) <= (others => '0');
				end if;

		--	elsif(unsigned(CONTROL(2 downto 0)) = 0) then
			else
				-- 2-ary FSK 0 -> -1, 1 -> +1
				DATA4(9) <= not DATA3(0);
				DATA4(8) <= not DATA3(0);
				DATA4(7) <= not DATA3(0);
				DATA4(6) <= '1';
				DATA4(5 downto 0) <= (others => '0');
			end if;
		end if;
	end if;
end process;

--//-- MODULATION INDEX ---------------------------------
-- Modulation index h: format 4.12 
-- DESIGN NOTE: modulation index scaling is moved before the frequency impulse filter
-- because of the multi-h ARTM CPM special case 
MOD_INDEX_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(unsigned(CONTROL(7 downto 4)) = 7) then
			-- special case: multi-h ARTM CPM case (h toggles between 4/16 and 5/16 every symbol)
			if(SYMBOL4_CLK = '1') then
				TOGGLE_ARTM_CPM <= not TOGGLE_ARTM_CPM;
				if(TOGGLE_ARTM_CPM = '0') then
					MODULATION_INDEX1 <= "0000010000000000";	-- 4/16
				else
					MODULATION_INDEX1 <= "0000010100000000";	-- 5/16
				end if;
			end if;
		else
			MODULATION_INDEX1 <= signed(MODULATION_INDEX);
		end if;
	end if;
end process;

-- scale the phase deviation with the modulation index
-- signed fixed 
MOD_INDEX_002: process(CLK)
begin
	if rising_edge(CLK) then
		SYMBOL5_CLK <= SYMBOL4_CLK;
		SYMBOL5_CLKx4 <= SYMBOL4_CLKx4;
		DATA5 <= MODULATION_INDEX1 * signed(DATA4);
			-- fixed point signed 8.18 <= 4.12 * 4.6. Note: this is the phase shift over a complete symbol.
		DATA5_VALID <= DATA4_VALID;
		TX_MOD_TYPE5 <= TX_MOD_TYPE4; 
	end if;
end process;

--//-- FREQUENCY IMPULSE FILTER  --------------------------------
-- As with every FIR filter, input must be an impulse so that output is the FIR filter impulse response.
-- In other words, one must blank all input samples except one per symbol.
-- Input format 6.14, Output format 6.14.   1 = pi, 0.5 = pi/2 etc.
-- Latency is 4-7 CLKs depending on filter selection
-- impulse lengths L=1 to 8 symbols
ROM_FIL1_001: ROM_FIL1 
GENERIC MAP(
	NBITS => 24,
	NSAMPLESPERSYMBOL => 4,
	LMAX => 8,
	SIMULATION => SIMULATION
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	FILT_SEL => CONTROL(7 downto 4),
	L_OUT => L,	-- unused
	DATA_IN => std_logic_vector(DATA5(23 downto 0)),	-- Note: this is the phase shift over a complete symbol 0.5 = pi/2 .
	SYMBOL_CLK_IN => SYMBOL5_CLK,
	SAMPLE_CLK_IN => SYMBOL5_CLKx4,
	SAMPLE_CLK_OUT => SYMBOL6A_CLKx4,
	DATA_OUT => DATA6A	-- Note: this is the phase shift over a 1/4 symbol.
);

-- re-align DATA6_VALID after the filter
GF_RECLOCK: process(CLK)
begin
	if rising_edge(CLK) then
		-- min latency 4 CLK
		DATA6_VALID_SHIFT <= DATA6_VALID_SHIFT(2 downto 0) & DATA5_VALID;
		if(SYMBOL6A_CLKx4 = '1') then
			-- extend the valid signal because of LMAX*4 = 32 samples taps maximum propagation time within the filter
			DATA6_VALID2_SHIFT <= DATA6_VALID2_SHIFT(30 downto 0) & DATA5_VALID;
		end if;
	end if;
end process;
DATA6_VALID <= DATA6_VALID2_SHIFT(3) or DATA6_VALID2_SHIFT(31);

-- In the case of PCM/FM, an additional premodulation filter is needed to supress the second sidelobe 
-- and comply with the IRIG106 RF mask
SYMBOL6A2_CLKx4 <= SYMBOL6A_CLKx4 when (unsigned(CONTROL(7 downto 4)) = 1) else '0';	-- save power when not PCM/FM
PREMOD_FILT_001: FIRHALFBAND3 PORT MAP(
	CLK => CLK,
	SAMPLE_CLK_IN => SYMBOL6A2_CLKx4,		
	DATA_IN => DATA6A(23 downto 10),
	SAMPLE_CLK_OUT => SYMBOL6B_CLKx4,		
	DATA_OUT => DATA6B(23 downto 10)
);

-- bypass above premodulation filter
PREMOD_FILT_002: process(CLK)
begin
	if rising_edge(CLK) then
		if (unsigned(CONTROL(7 downto 4)) = 1) then
			-- PCM/FM case: enable premodulation filter
			SYMBOL6_CLKx4 <= SYMBOL6B_CLKx4;
			DATA6 <= DATA6B;
		else
			-- bypass above premodulation filter
			SYMBOL6_CLKx4 <= SYMBOL6A_CLKx4;
			DATA6 <= DATA6A;
		end if;
	end if;
end process;

--//-- PHASE MODULATION  + FREQUENCY TRANSLATION --------------------------------
-- Scale so that a 0.5 modulation index causes a pi/2 phase deviation over a symbol period (or pi/8 over a 1/4 symbol)
PHASE_DEV <= signed(DATA6(18 downto 0) & "00000");

-- Divide PHASE_DEV: (shift right SYMBOL_RATE_NDIV-2 times) to compute the phase deviation
-- at each clock (as opposed to every 1/4 symbol period)
-- The NCO will thus perform a kind of linear interpolation, eliminating the need for subsequent 
-- interpolation filters (HBF, CIC)
PHASE_DEV_SHIFT_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		-- DATA6 samples are generated at 4*symbol rate
		if(SYMBOL6_CLKx4 = '1') then
			PHASE_DEV_SHIFT <= PHASE_DEV;
			PHASE_DEV_SHIFT_CNTR <= unsigned(SYMBOL_RATE_NDIV);
		elsif(PHASE_DEV_SHIFT_CNTR > 2) then
			-- shift right
			PHASE_DEV_SHIFT <= PHASE_DEV_SHIFT(23) & PHASE_DEV_SHIFT(23 downto 1);
			PHASE_DEV_SHIFT_CNTR <= PHASE_DEV_SHIFT_CNTR - 1;
		end if;
	end if;
end process;

PHASE_DEV_SHIFT_GEN_002: process(CLK)
begin
	if rising_edge(CLK) then
		-- DATA6 samples are generated at 4*symbol rate
		if(PHASE_DEV_SHIFT_CNTR = 2) then
			if(SYNC_RESET = '1') or (CONTROL(9 downto 8) = "11") then
				-- unmodulated carrier
				PHASE_DEV2 <= (others => '0');
			elsif (CONTROL(11) = '0') then
				-- no spectrum inversion
				PHASE_DEV2 <= PHASE_DEV_SHIFT;
			else
				-- spectrum inversion
				PHASE_DEV2 <= - PHASE_DEV_SHIFT;
			end if;

		end if;
	end if;
end process;
			
-- One last filter to suppress sidelobe at 4x symbol rate
-- CIC Interpolation filter
CIC_R_GEN: process(SYMBOL_RATE_NDIV)
begin	
	case(SYMBOL_RATE_NDIV) is
		when "0011" => CIC_R <= x"000002";
		when "0100" => CIC_R <= x"000004";
		when "0101" => CIC_R <= x"000008";
		when "0110" => CIC_R <= x"000010";
		when "0111" => CIC_R <= x"000020";
		when "1000" => CIC_R <= x"000040";
		when "1001" => CIC_R <= x"000080";
		when "1010" => CIC_R <= x"000100";
		when "1011" => CIC_R <= x"000200";
		when "1100" => CIC_R <= x"000400";
		when "1101" => CIC_R <= x"000800";
		when "1110" => CIC_R <= x"001000";
		when "1111" => CIC_R <= x"002000";
		when others => CIC_R <= x"000001";
	end case;
end process;

-- Note: the common CIC_INTERP4 component can be modified to use fewer resources given the maximum 
-- value R. For example if Rmax = 0x800 => computation precision BOUT: integer  := NBITS-1+22;  -- NBITS-1 input + 2*log2(R(2^11 max))	
PHASE_DEV2B <= PHASE_DEV2 when (SIMULATION = '0') or (PHASE_DEV2(PHASE_DEV2'left) /= 'X') else (others => '0');
CIC_INTERPOL4_001: CIC_INTERP4 
GENERIC MAP(
	NBITS => 24
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	DATA_IN => std_logic_vector(PHASE_DEV2B),
	SAMPLE_CLK_IN => SYMBOL6_CLKx4,
	R => CIC_R,
	DATA_OUT => PHASE_DEV3,
	SAMPLE_CLK_OUT => open,
	SAMPLE_CLK_OUT_REQ => '1',
	SATURATION => open
);
			
-- frequency modulator and frequency translation
FM_MOD_001: process (CLK)
begin 
	if rising_edge(CLK) then
		DATA6_VALID_D <= DATA6_VALID;

		if(SYNC_RESET = '1') then
			FM_NCO_ACC <= (others => '0');
		elsif((SIMULATION = '0') or (PHASE_DEV3(PHASE_DEV3'left) /= 'X'))then
			-- every clock, add the phase deviation
			FM_NCO_ACC <= FM_NCO_ACC + signed(PHASE_DEV3) + signed(CENTER_FREQ(31 downto 8));
	  	end if;
	end if;
end process;

--// Convert FM_NCO_ACC to I & Q samples through sine/cosine table
-- Latency is 2 CLK. Output is registered for best timing. 
SIN_COS001: SIGNED_SIN_COS_TBL3 
GENERIC MAP(
	NADDR => 12
)
port map (
	CLK => CLK, 
	PHASE => std_logic_vector(FM_NCO_ACC(23 downto 10)), 
	SIN_OUT => DATA7_Q, 
	COS_OUT => DATA7_I 
	);   


-- realign sampling clock with look-up table output
RECLOCK_052: process (CLK)
begin 
	if rising_edge(CLK) then
		DATA6_VALID_D2 <= DATA6_VALID_D;
		DATA7_VALID <= DATA6_VALID_D2;
	end if;
end process;

--//-- TX GAIN CONTROL -------------------------
-- reclock immediately before multiplier for better timing
GAINx <= "0" & GAIN;
OSC_RECLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		-- maximum gain: x4.0
		GAINs <= signed(GAINx);	
		DATA11_Is <= signed(DATA7_I(17 downto 2));
		DATA11_Qs <= signed(DATA7_Q(17 downto 2));
		DATA11_VALID <= DATA7_VALID;
	end if;
end process;

DATA12_I <= DATA11_Is * GAINs;
DATA12_Q <= DATA11_Qs * GAINs;

-- reclock immediately after multiplier for better timing
OSC_RECLOCK_002: process(CLK)
begin
	if rising_edge(CLK) then
		DATA12_I_D <= DATA12_I;
		DATA12_Q_D <= DATA12_Q;
		DATA12_VALID_D <= DATA11_VALID;
	end if;
end process;


CLAMP_OUTPUT_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(DATA12_VALID_D = '0') and (CONTROL(9 downto 8) /= "11")  then
			-- force to output zero, unless unmodulated mode
			DATA13_I <= (others => '0'); 
		elsif(DATA12_I_D(32) = '0') and (DATA12_I_D(31 downto 30) /= "00") then
			-- positive saturation
			DATA13_I <= (15 => '0', others => '1'); 
			SATURATION13_I <= '1';
		elsif(DATA12_I_D(32) = '1') and (DATA12_I_D(31 downto 30) /= "11") then
			-- negative saturation
			DATA13_I <= (15 => '1', 0 => '1', others => '0'); 
			SATURATION13_I <= '1';
		else
			DATA13_I <= std_logic_vector(DATA12_I_D(30 downto 15));
			SATURATION13_I <= '0';
		end if;
		
		if(DATA12_VALID_D = '0') and (CONTROL(9 downto 8) /= "11")  then
			-- force to output zero, unless unmodulated mode
			DATA13_Q <= (others => '0'); 
		elsif(DATA12_Q_D(32) = '0') and (DATA12_Q_D(31 downto 30) /= "00") then
			-- positive saturation
			DATA13_Q <= (15 => '0', others => '1'); 
			SATURATION13_Q <= '1';
		elsif(DATA12_Q_D(32) = '1') and (DATA12_Q_D(31 downto 30) /= "11") then
			-- negative saturation
			DATA13_Q <= (15 => '1', 0 => '1', others => '0'); 
			SATURATION13_Q <= '1';
		else
			DATA13_Q <= std_logic_vector(DATA12_Q_D(30 downto 15));
			SATURATION13_Q <= '0';
		end if;
	end if;
end process;

DATA_I_OUT <= DATA13_I;
DATA_Q_OUT <= DATA13_Q;

-- 4*symbol rate for use by external AWGN (to determine the noise bandwidth)
SYMBOL_CLKx4_OUT <= SYMBOL6_CLKx4;
--
-- report saturation
OUTPUT_RECLOCK_SATURATION: process(CLK)
begin
	if rising_edge(CLK) then
		SATURATION <= SATURATION13_I & SATURATION13_Q;
	end if;
end process;

--// TEST POINTS, MONITORING ----------------------------------
TP(10 downto 1) <= BURST_TX_TP(10 downto 1);
TP(11) <= DATA6_VALID;
-- test test test
--TP(1) <= DATA3(0);
--TP(2) <= SYMBOL3_CLK;
--TP(3) <= DATA4(9);
--TP(4) <= SYMBOL4_CLK;
--TP(5) <= PHASE_DEV2(PHASE_DEV2'left);
--TP(6) <= SYMBOL6_CLKx4;
--TP(7) <= FM_NCO_ACC(FM_NCO_ACC'left);
--TP(8) <= DATA7_I(DATA7_I'left);
--TP(9) <= DATA13_I(DATA13_I'left);


-- ComScope
CS1 <= DATA7_I(17 downto 10);
CS1_CLK <= '1';

--// simulation. send to a space-delimited text file for subsequent Matlab plot.
SIM2OUTFILE_X: if(SIMULATION = '1') generate

--	SIM2OUTFILE_002: SIM2OUTFILE 
--	generic map(
--		FILENAME => "mod1.txt"
--	)
--	PORT MAP(
--		CLK => CLK,
--		DATA1 => DATA13_I(15 downto 4),
--		DATA2 => DATA13_Q(15 downto 4),
--		DATA3 => DATA6(19 downto 8),
--		SAMPLE_CLK_IN => '1'
--	);

end generate;

end Behavioral;

