-------------------------------------------------------------
-- MSS copyright 2011-2017
--	Filename:  BURST_TX.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 10
--	Date last modified: 10/27/17
-- Inheritance: 	n/a
--
-- description:  Burst assembly and sync word insertion. 
-- Constraint: BURST_LENGTH*NBITSPERSYMBOLS must be a multiple of 8 bits.
--
-- Rev 2 11/16/11 added preamble extension before the sync word to give the receiver AGC time to ramp up at the start 
-- of burst. This should help the probability of sync detection when AGC is too slow.
--
-- Rev 3 12/23/11 AZ
-- Removed preamble extension for follow-on frames (i.e. not the first in the burst).
-- Corrected non-symmetrical amplitude in preamble for 4-ASK
-- 
-- Rev 4 2/12/12 AZ
-- Added continuous mode.
-- Encapsulated elastic buffer in new component for ELASTIC_BUF for better code clarity and portability.
-- 
-- Rev 5 5/17/14 AZ
-- Switched to numeric_std library
-- Added generic parameters
--
-- Rev 6 2/7/15 AZ
-- Added synchronization with externally supplied TX_SOF
--
-- Rev 7 6/7/15 AZ
-- added option to make preamble pseudo-random.
--
-- Rev 8 4/6/15 AZ
-- Added gap between sync word and data field (to give the demodulator time to perform corrections such
-- as amplitude, phase, symbol timing, etc, after detecting the sync word)
--
-- Rev 9 5/2/16 AZ
-- move to numeric_std library
-- reworked the burst transmit scheduling entirely to match the COM-1802/1902/..etc definition:
-- burst is transmitted if complete in elastic buffer. If 2nd frame is available early enough, it
-- is transmitted immediately after the first one without the preamble (only with the sync word+gap)
-- switched to more generic ELASTIC_BUFFER_NRAMB2
--
-- Rev 10 6/17/17 AZ
-- Restore continuous mode (bug)
-- Block output when input elastic buffers are empty
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

entity BURST_TX is
	generic (
		FIRST_PREAMBLE_EXTENSION: integer range 0 to 8191; 
			-- 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)
		PREAMBLE_TYPE: integer range 0 to 2;
		  -- 0 = constant
		  -- 1 = toggling 0/1
		  -- 2 = pseudo random
		SYNC_LENGTH: integer range 0 to 128;	 
			-- 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 := 5
			-- 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.
	);
    Port ( 
		--// CLK, RESET
		SYNC_RESET: in std_logic;
			-- use is MANDATORY. Also use after a configuration change.
			-- 1 CLK pulse to indicate that the configuration has changed. 
		CLK: in std_logic;		-- synchronous clock
			-- Must be a global clocks. No BUFG instantiation within 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;
			-- start of frame, 1 clk-wide pulse aligned with TX_SAMPLE_CLK. 
			-- required if SYNC_EN = '1'
			-- Indicates that the first payload data bit is in TX_DATA(7)
		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
		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
		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+gap+BURST_LENGTH < 8191
		NBITSPERSYMBOLS: in std_logic_vector(2 downto 0);
			-- number of bits per data symbol. Depends on the modulation type.
			-- valid values: 1,2,3,4,5,6,7(future)		
		SYNC_EN: in std_logic;
			-- '1' to enable sync word and/or preamble insertion. '0' to disable both. 

		--// TX BUFFER -> MODULATOR
		-- modulation data symbols (including the burst initial sync word and the data field)
		-- The follow-on modulator will convert these symbols into complex (I/Q) sampled signals,
		-- filter them, and send them to output DACs. 
		TX_MOD_DATA: out std_logic_vector(6 downto 0);
			-- symbols. LSbs are used first. For example TX_MOD_DATA(1:0) is used for a 2bit/symbol modulation.
			-- read when TX_MOD_SAMPLE_CLK = '1'
			-- Unused bits are forced to zero.
		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;
			-- 2x, 4x AND 8x oversampling are for use by the follow-on modulator
			-- when applicable (i.e. symbol rate <= fclk/2, fclk/4 or fclk/8)
		TX_MOD_TYPE: out std_logic_vector(1 downto 0);
			-- a burst may comprise several modulation types, for example during the initial sync word 
			-- and the subsequent data field.
			-- 00 for sync field
			-- 01 for data field
			-- 10 unmodulated
			-- 11 future
			

		TP: out std_logic_vector(20 downto 1)

			);
end entity;

architecture Behavioral of BURST_TX is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT ELASTIC_BUFFER_NRAMB2
	GENERIC (
		DATA_WIDTH: integer;
		ADDR_WIDTH: integer;
		READ_AHEAD_1ADDRESS: std_logic
	);	
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		DATA_IN: in std_logic_vector((DATA_WIDTH-1) downto 0);
		SAMPLE_CLK_IN : IN std_logic;
		SAMPLE_CLK_OUT_REQ : IN std_logic;          
		SAMPLE_CLK_IN_REQ : OUT std_logic;
		DATA_OUT: out std_logic_vector((DATA_WIDTH-1) downto 0);
		SAMPLE_CLK_OUT : OUT std_logic;
		SAMPLE_CLK_OUT_E : OUT std_logic;
		BUFFER_EMPTY : OUT std_logic;
		USED: out std_logic_vector((ADDR_WIDTH-1) downto 0)
		);
	END COMPONENT;

-- in case we prefer pseudo-random preambles
--	COMPONENT LFSR11
--	PORT(
--		ASYNC_RESET : IN std_logic;
--		CLK : IN std_logic;
--		BIT_CLK_REQ : IN std_logic;
--		SYNC_RESET : IN std_logic;
--		LFSR_BIT : OUT std_logic;
--		BIT_CLK_OUT : OUT std_logic;
--		SOF_OUT : OUT std_logic
--		);
--	END COMPONENT;
	
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- 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

--// SELF RESET ------------------------------------------------
signal RESET_CNTR: std_logic_vector(3 downto 0) := "0000";

--// BYTE INPUT TO SYMBOL REPACKING --------------------------------------------------
signal TX_CTS_local: std_logic := '0';
signal TX_DATA_FLIPPED: std_logic_vector(8 downto 0) := "0" & x"00";
signal SAMPLE2_CLK: std_logic := '0';
signal SAMPLE2_CLK_E: std_logic := '0';
signal SAMPLE2_CLK_REQ: std_logic := '0';
signal DATA2_X: std_logic_vector(8 downto 0) := (others => '0');
signal EB1_BUFFER_EMPTY: std_logic := '0';
signal DATA_CACHE: std_logic_vector(31 downto 0) := (others => '0');	
signal SOF_CACHE: std_logic_vector(31 downto 0) := (others => '0');	
signal CACHE_WPTR: unsigned(5 downto 0) := (others => '0');	-- range 0 - 32
signal CACHE_WPTR_D: unsigned(5 downto 0) := (others => '0');	-- range 0 - 32
signal CACHE_AVAILABLE_SPACE: unsigned(5 downto 0);	-- range 0 - 32 
signal CACHE_USED_SPACE: unsigned(5 downto 0);	-- range 0 - 32 
signal CACHE_RPTR: unsigned(5 downto 0) := (others => '0');	-- range 0 - 32
signal CACHE_RPTR_D: unsigned(5 downto 0) := (others => '0');	-- range 0 - 32
signal CACHE_RPTR_NEXT: unsigned(5 downto 0) := (others => '0');	-- range 0 - 32 
signal DATA3: std_logic_vector(6 downto 0) := (others => '0');
signal SOF3: std_logic := '0';
signal SAMPLE3_CLK: std_logic := '0';
signal SAMPLE3_CLK_E: std_logic := '0';
signal DATA_MASK: std_logic_vector(6 downto 0) := (others => '0');
signal DATA4A: std_logic_vector(6 downto 0) := (others => '0');
signal DATA4: std_logic_vector(6 downto 0) := (others => '0');
signal SOF4: std_logic := '0';
signal SAMPLE4_CLK: std_logic := '0';
signal CTS4: std_logic := '0';

--// SYMBOL INPUT ELASTIC BUFFER -----------------------------------
signal DATA4x: std_logic_vector(7 downto 0) := (others => '0');
signal DATA5x: std_logic_vector(7 downto 0) := (others => '0');
signal SAMPLE5_CLK: std_logic := '0';
signal SAMPLE5_CLK_REQ: std_logic := '0';
signal EB2_BUFFER_EMPTY: std_logic := '0';
signal EB2_BUFFER_EMPTY_D: std_logic := '0';
signal EB2_SAMPLE_CLK_OUT_REQ: std_logic := '0';
signal EB2_USED: std_logic_vector(13 downto 0) := (others => '0');
signal EB2_FLUSH: std_logic := '0';
signal EB2_FLUSH_D: std_logic := '0';
signal EB2_FLUSH_D2: std_logic := '0';

--// SYMBOL CLOCK GENERATION -----------------------------------
signal SYMBOL_RATE_CNTR: unsigned(15 downto 0) := (others => '0');
signal SYMBOL_RATE_CNTR_D: unsigned(15 downto 0) := (others => '0');
signal SYMBOL_RATE_CNTR_STEP: unsigned(15 downto 0) := (others => '0');
signal SYMBOL_RATE_CNTR_STEP_A: unsigned(15 downto 0) := (others => '0');
signal SYMBOL_RATE_NDIV_EQ0: std_logic := '0';
signal SYMBOL_CLKx8: std_logic := '0';
signal SYMBOL_CLKx8_D: std_logic := '0';
signal SYMBOL_CLKx8_D2: std_logic := '0';
signal SYMBOL_CLKx4: std_logic := '0';
signal SYMBOL_CLKx4_D: std_logic := '0';
signal SYMBOL_CLKx4_D2: std_logic := '0';
signal SYMBOL_CLKx2: std_logic := '0';
signal SYMBOL_CLKx2_D: std_logic := '0';
signal SYMBOL_CLKx2_D2: std_logic := '0';
signal SYMBOL_CLK: std_logic := '0';
signal SYMBOL_CLK_D: std_logic := '0';
signal SYMBOL_CLK_D2: std_logic := '0';

--//-- SEQUENCER ------------------------------------------------
signal EVENT1: std_logic := '0';
signal STATE: integer range 0 to 6 := 0;
signal STATE_D: integer range 0 to 6 := 0;
signal STATE_D2: integer range 0 to 6 := 0;
signal STATE_CNTR: integer range 0 to 8192 := 0;
signal ZERO_FIRST_PREAMBLE: std_logic := '0';
signal ZERO_SYNC_LENGTH: std_logic := '0';
signal ZERO_GAP_LENGTH: std_logic := '0';

--//-- PREAMBLE / SYNC / GAP ------------------------------------------------
signal TOGGLE_PREAMBLE_BIT: std_logic := '0';
signal SYNC_WORD_SHIFT: std_logic_vector(127 downto 0) := (others => '0');
signal SYNC_BIT: std_logic := '0';

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

----// SELF RESET ------------------------------------------------
---- just in case the user forgot to use SYNC_RESET 
---- Note: SYNC_RESET is mandatory to force the read/write pointers to their proper initial values.
--SYNC_RESET_GEN: process(CLK)
--begin
--	if rising_edge(CLK) then
--		if(RESET_CNTR(3) = '0') then
--			RESET_CNTR <= RESET_CNTR + 1;
--		end if;
--	end if;
--end process;
--SYNC_RESET_LOCAL <= not(RESET_CNTR(3)) or SYNC_RESET;
		

--// BYTE INPUT TO SYMBOL REPACKING --------------------------------------------------
-- flip data so that MSb is sent first
TX_DATA_FLIPPED(0) <= TX_DATA(7);
TX_DATA_FLIPPED(1) <= TX_DATA(6);
TX_DATA_FLIPPED(2) <= TX_DATA(5);
TX_DATA_FLIPPED(3) <= TX_DATA(4);
TX_DATA_FLIPPED(4) <= TX_DATA(3);
TX_DATA_FLIPPED(5) <= TX_DATA(2);
TX_DATA_FLIPPED(6) <= TX_DATA(1);
TX_DATA_FLIPPED(7) <= TX_DATA(0);
TX_DATA_FLIPPED(8) <= TX_SOF;  

-- Justification: need elastic buffer because input throughput can exceed modulation throughput in burst.
ELASTIC_BUFFER_NRAMB2_001: ELASTIC_BUFFER_NRAMB2 
generic map(
	DATA_WIDTH => 9,
	ADDR_WIDTH => 11,
	READ_AHEAD_1ADDRESS => '0'	
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	DATA_IN => TX_DATA_FLIPPED,	
	SAMPLE_CLK_IN => TX_DATA_SAMPLE_CLK,
	SAMPLE_CLK_IN_REQ => TX_CTS_local,
	DATA_OUT => DATA2_X,	
	SAMPLE_CLK_OUT => SAMPLE2_CLK,
	SAMPLE_CLK_OUT_E => SAMPLE2_CLK_E,
	SAMPLE_CLK_OUT_REQ => SAMPLE2_CLK_REQ,
	BUFFER_EMPTY => EB1_BUFFER_EMPTY,
	USED => open	
);
TX_CTS <= TX_CTS_local;

-- 8-bit parallel to symbol-bits parallel
-- circular buffer. 32-bits long.
CACHE_BUF_WPTR_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then	
			CACHE_WPTR <= (others => '0');
			CACHE_WPTR_D <= (others => '0');
		elsif(SAMPLE2_CLK_E = '1') then
--			-- 1 data byte is being written. Move wptr to next location to be written to.
			-- Note: 1 extra bit to make a distinction between full (32 bits) and empty(0 bit)
			CACHE_WPTR <= CACHE_WPTR + "001000";  -- next location
			CACHE_WPTR_D <= CACHE_WPTR;
		end if;
	end if;
end process;

CACHE_BUF_DATA_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then	
			DATA_CACHE <= (others => '0');
			SOF_CACHE <= (others => '0');
		elsif(SAMPLE2_CLK = '1') then
			-- write 1 data byte to the cache 
			-- CACHE_WPTR is always a multiple of 8 bits
			if (CACHE_WPTR_D(4 downto 3) = "00") then
				DATA_CACHE(7 downto 0) <= DATA2_X(7 downto 0);
				SOF_CACHE(0) <= DATA2_X(8);
			end if;
			if (CACHE_WPTR_D(4 downto 3) = "01") then
				DATA_CACHE(15 downto 8) <= DATA2_X(7 downto 0);
				SOF_CACHE(8) <= DATA2_X(8);
			end if;
			if (CACHE_WPTR_D(4 downto 3) = "10") then
				DATA_CACHE(23 downto 16) <= DATA2_X(7 downto 0);
				SOF_CACHE(16) <= DATA2_X(8);
			end if;
			if (CACHE_WPTR_D(4 downto 3) = "11") then
				DATA_CACHE(31 downto 24) <= DATA2_X(7 downto 0);
				SOF_CACHE(24) <= DATA2_X(8);
			end if;
		end if;
	end if;
end process;

CACHE_BUF_RPTR_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		CACHE_RPTR_D <= CACHE_RPTR;
		
		if(SYNC_RESET = '1') then	
			CACHE_RPTR <= (others => '0');
			SAMPLE3_CLK_E <= '0';
		elsif(CACHE_USED_SPACE >= unsigned(NBITSPERSYMBOLS)) and (CTS4 = '1') then
			-- 1 data symbol is read from the cache
			CACHE_RPTR <= CACHE_RPTR_NEXT;
			SAMPLE3_CLK_E <= '1';
		else
			SAMPLE3_CLK_E <= '0';
		end if;
	end if;
end process;
CACHE_RPTR_NEXT <= CACHE_RPTR + unsigned(NBITSPERSYMBOLS);

CACHE_USED_SPACE <= CACHE_WPTR - CACHE_RPTR;  
CACHE_AVAILABLE_SPACE <= (not CACHE_USED_SPACE) and "011111"; 
--SAMPLE2_CLK_REQ <= (not EB1_BUFFER_EMPTY) when (CACHE_AVAILABLE_SPACE > 16)  else '0';
SAMPLE2_CLK_REQ <= '1' when (CACHE_AVAILABLE_SPACE > 16)  else '0';	-- redundant check 061817
	-- room for at least two bytes (because of the delay in reading from dpram.
		
-- DATA3 and SOF3 
CACHE_BUF_OUTPUTDATA_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE3_CLK <= SAMPLE3_CLK_E;
		
		if(SAMPLE3_CLK_E = '1') then
			-- 1 data symbol is read from the cache
			-- number of bits per data symbol. Depends on the modulation type.
			-- valid values: 1,2,3,4,5,6,7		
			-- Transfer the maximum 7 bits, mask later.
			DATA3(0) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0)));
			DATA3(1) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0) + 1));
			DATA3(2) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0) + 2));
			DATA3(3) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0) + 3));
			DATA3(4) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0) + 4));
			DATA3(5) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0) + 5));
			DATA3(6) <= DATA_CACHE(to_integer(CACHE_RPTR_D(4 downto 0) + 6));
			SOF3 <= SOF_CACHE(to_integer(CACHE_RPTR_D(4 downto 0)));
		end if;
--		if(SYMBOL_CLK_D = '1') then
--            SOF3_E <= SOF_CACHE(to_integer(CACHE_RPTR_NEXT));
--                -- Advanced notice that the next symbol is at the start of frame.
--		end if;

	end if;
end process;
    
DATA_MASK <= 	"0000001" when (unsigned(NBITSPERSYMBOLS) = 1) else 
					"0000011" when (unsigned(NBITSPERSYMBOLS) = 2) else 
					"0000111" when (unsigned(NBITSPERSYMBOLS) = 3) else 
					"0001111" when (unsigned(NBITSPERSYMBOLS) = 4) else 
					"0011111" when (unsigned(NBITSPERSYMBOLS) = 5) else 
					"0111111" when (unsigned(NBITSPERSYMBOLS) = 6) else 	
					"1111111";

-- form modulation symbols
DATA_SYMBOL_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE4_CLK <= SAMPLE3_CLK;
		SOF4 <= SOF3;
		
		if(SAMPLE3_CLK = '1') then
			if(unsigned(NBITSPERSYMBOLS) = 1) then
				DATA4A <= "000000" & DATA3(0);
			elsif(unsigned(NBITSPERSYMBOLS) = 2) then
				DATA4A <= "00000" & DATA3(0) & DATA3(1); 
			elsif(unsigned(NBITSPERSYMBOLS) = 3) then
				DATA4A <= "0000" & DATA3(0) & DATA3(1) & DATA3(2);
			elsif(unsigned(NBITSPERSYMBOLS) = 4) then
				DATA4A <= "000" & DATA3(0) & DATA3(1) & DATA3(2) & DATA3(3);	
			elsif(unsigned(NBITSPERSYMBOLS) = 5) then
				DATA4A <= "00" & DATA3(0) & DATA3(1) & DATA3(2) & DATA3(3) & DATA3(4);
			elsif(unsigned(NBITSPERSYMBOLS) = 6) then
				DATA4A <= "0" & DATA3(0) & DATA3(1) & DATA3(2) & DATA3(3) & DATA3(4) & DATA3(5);
			else
				DATA4A <= DATA3(0) & DATA3(1) & DATA3(2) & DATA3(3) & DATA3(4) & DATA3(5) & DATA3(6);
			end if;
		end if;
	end if;
end process;

-- mask unused upper bits
DATA4 <= DATA4A and DATA_MASK;

--// SYMBOL INPUT ELASTIC BUFFER -----------------------------------
-- temporarily store symbol-wide samples
-- Latency: 1 CLKs from SAMPLE_CLK_OUT_REQ to SAMPLE_CLK_OUT
DATA4x <= SOF4 & DATA4;
ELASTIC_BUFFER_NRAMB2_002: ELASTIC_BUFFER_NRAMB2 
generic map(
	DATA_WIDTH => 8,
	ADDR_WIDTH => 14,
	READ_AHEAD_1ADDRESS => '1'	
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	DATA_IN => DATA4x,	
	SAMPLE_CLK_IN => SAMPLE4_CLK,
	SAMPLE_CLK_IN_REQ => CTS4,
	DATA_OUT => DATA5x,	
	SAMPLE_CLK_OUT => SAMPLE5_CLK,
	SAMPLE_CLK_OUT_E => open,
	SAMPLE_CLK_OUT_REQ => EB2_SAMPLE_CLK_OUT_REQ,
	BUFFER_EMPTY => EB2_BUFFER_EMPTY,
	USED => EB2_USED	
);

-- flush bits prior to the first start of frame.
-- All payload fields must start at start of frame
FLUSH_001: process(CLK)
begin
	if rising_edge(CLK) then
		EB2_BUFFER_EMPTY_D <= EB2_BUFFER_EMPTY;
		EB2_FLUSH_D <= EB2_FLUSH;
		EB2_FLUSH_D2 <= EB2_FLUSH_D;
	end if;
end process;
EB2_FLUSH <= '1' when (STATE < 5) and (EB2_BUFFER_EMPTY = '0') and (EB2_BUFFER_EMPTY_D = '0')  
						and (DATA5x(7) /= '1') and (EB2_FLUSH_D = '0') and (EB2_FLUSH_D2 = '0') else '0';
SAMPLE5_CLK_REQ <= SYMBOL_CLK_D when (STATE = 5) else '0';
EB2_SAMPLE_CLK_OUT_REQ <= EB2_FLUSH or SAMPLE5_CLK_REQ;


--// SYMBOL CLOCK GENERATION -----------------------------------
-- Divide the modulator processing clock by powers of 2
SYMBOL_CLK_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		SYMBOL_RATE_CNTR_D <= SYMBOL_RATE_CNTR;	-- to detect changes in MSBs
		
		if(SYNC_RESET = '1') then
			-- symbol rate may have just changed. Reset counter
			SYMBOL_RATE_CNTR <= (others => '0');
		else
			SYMBOL_RATE_CNTR <= SYMBOL_RATE_CNTR + SYMBOL_RATE_CNTR_STEP;
		end if;
 	end if;
end process;

-- SYMBOL_RATE_CNTR step size (depends on SYMBOL_RATE_NDIV)
-- if SYMBOL_RATE_NDIV = 15 (divide by 32678), SYMBOL_RATE_CNTR_STEP_A = x"0001"
-- etc...
-- Regular phase increment. 
SYMBOL_RATE_CNTR_STEP_A_GEN: process(SYMBOL_RATE_NDIV)
begin	
	case(SYMBOL_RATE_NDIV) is
		when "0000" => SYMBOL_RATE_CNTR_STEP_A <= x"8000";
		when "0001" => SYMBOL_RATE_CNTR_STEP_A <= x"4000";
		when "0010" => SYMBOL_RATE_CNTR_STEP_A <= x"2000";
		when "0011" => SYMBOL_RATE_CNTR_STEP_A <= x"1000";
		when "0100" => SYMBOL_RATE_CNTR_STEP_A <= x"0800";
		when "0101" => SYMBOL_RATE_CNTR_STEP_A <= x"0400";
		when "0110" => SYMBOL_RATE_CNTR_STEP_A <= x"0200";
		when "0111" => SYMBOL_RATE_CNTR_STEP_A <= x"0100";
		when "1000" => SYMBOL_RATE_CNTR_STEP_A <= x"0080";
		when "1001" => SYMBOL_RATE_CNTR_STEP_A <= x"0040";
		when "1010" => SYMBOL_RATE_CNTR_STEP_A <= x"0020";
		when "1011" => SYMBOL_RATE_CNTR_STEP_A <= x"0010";
		when "1100" => SYMBOL_RATE_CNTR_STEP_A <= x"0008";
		when "1101" => SYMBOL_RATE_CNTR_STEP_A <= x"0004";
		when "1110" => SYMBOL_RATE_CNTR_STEP_A <= x"0002";
		when others => SYMBOL_RATE_CNTR_STEP_A <= x"0001";
	end case;
end process;


-- symbol clock phase adjustments
SYMBOL_CLK_GEN_003: process(CLK)
begin
	if rising_edge(CLK) then
		SYMBOL_RATE_CNTR_STEP <= SYMBOL_RATE_CNTR_STEP_A;
		
		if(unsigned(SYMBOL_RATE_NDIV) = 0) then
			SYMBOL_RATE_NDIV_EQ0 <= '1';
		else
			SYMBOL_RATE_NDIV_EQ0 <= '0';
		end if;
	end if;
end process;

SYMBOL_CLKx1_GEN_001: process(CLK)
begin 
	if rising_edge(CLK) then  
		if(SYMBOL_RATE_CNTR_D(15) /= SYMBOL_RATE_CNTR(15)) or (SYMBOL_RATE_NDIV_EQ0 = '1') then
			SYMBOL_CLK <= '1';
		else
			SYMBOL_CLK <= '0';
		end if;
	end if;
end process;

SYMBOL_CLKx2_GEN_001: process(CLK)
begin 
	if rising_edge(CLK) then  
		SYMBOL_CLKx2_D <= SYMBOL_CLKx2;
		SYMBOL_CLKx2_D2 <= SYMBOL_CLKx2_D;

		if(SYMBOL_RATE_CNTR_D(15 downto 14) /= SYMBOL_RATE_CNTR(15 downto 14))  or (SYMBOL_RATE_NDIV_EQ0 = '1') then
			SYMBOL_CLKx2 <= '1';
		else
			SYMBOL_CLKx2 <= '0';
		end if;
	end if;
end process;

SYMBOL_CLKx4_GEN_001: process(CLK)
begin 
	if rising_edge(CLK) then  
		SYMBOL_CLKx4_D <= SYMBOL_CLKx4;
		SYMBOL_CLKx4_D2 <= SYMBOL_CLKx4_D;

		if(SYMBOL_RATE_CNTR_D(15 downto 13) /= SYMBOL_RATE_CNTR(15 downto 13))  or (SYMBOL_RATE_NDIV_EQ0 = '1') then
			SYMBOL_CLKx4 <= '1';
		else
			SYMBOL_CLKx4 <= '0';
		end if;
	end if;
end process;

SYMBOL_CLKx8_GEN_002: process(CLK)
begin 
	if rising_edge(CLK) then  
		SYMBOL_CLKx8_D <= SYMBOL_CLKx8;
		SYMBOL_CLKx8_D2 <= SYMBOL_CLKx8_D;

		if(SYMBOL_RATE_CNTR_D(15 downto 12) /= SYMBOL_RATE_CNTR(15 downto 12))  or (SYMBOL_RATE_NDIV_EQ0 = '1') then
			SYMBOL_CLKx8 <= '1';
		else
			SYMBOL_CLKx8 <= '0';
	 	end if;
	end if;
end process;

--//-- SEQUENCER ------------------------------------------------
EVENT1 <= '1' when (TX_WINDOW = '1') and (unsigned(EB2_USED) >= unsigned(BURST_LENGTH)) 
					and (DATA5x(7) = '1') else '0';
	-- input elastic buffer contains a full frame AND TX_WINDOW is enabled AND output correctly points to a SOF boundary.


-- 6 states: 0 idle, 1 = tx window triggered, 2 = preamble, 3 = sync word, , 4 = gap,  5 = payload field, 6 = dummy symbols at end of burst
-- STATE_CNTR is expressed in number of symbol periods.
-- STATE is aligned with SYMBOL_CLK_D
STATE_GEN_001: process(CLK)
begin
    if rising_edge(CLK) then
		if(FIRST_PREAMBLE_EXTENSION = 0) then
			ZERO_FIRST_PREAMBLE <= '1';
		else
			ZERO_FIRST_PREAMBLE <= '0';
		end if;
		if(SYNC_LENGTH = 0) then
			ZERO_SYNC_LENGTH <= '1';
		else
			ZERO_SYNC_LENGTH <= '0';
		end if;
		if(GAP_LENGTH = 0) then
			ZERO_GAP_LENGTH <= '1';
		else
			ZERO_GAP_LENGTH <= '0';
		end if;
    end if;
end process;

STATE_GEN_002: process(CLK)
begin
    if rising_edge(CLK) then
		SYMBOL_CLK_D <= SYMBOL_CLK;
		
    	STATE_D <= STATE;
		SYMBOL_CLK_D2 <= SYMBOL_CLK_D;
		
		STATE_D2 <= STATE_D;
    	
        if(SYNC_RESET = '1')  then
            STATE <= 0;
            STATE_CNTR <= 0;
        elsif(SYNC_EN = '0') and (TX_WINDOW = '1') then	
					-- send payload only. continuous mode, as long as there is data				
				if(EB2_BUFFER_EMPTY = '0') then
					STATE <= 5;	
				else
					STATE <= 0;
				end if;
        elsif(STATE = 0) and (EVENT1 = '1') then
            -- tx window triggered
            STATE <= 1;
        elsif(SYMBOL_CLK = '1') then
            -- other states are synchronized with symbol period
            if(STATE = 1) then
					-- first preamble 
					STATE <= 2;
					STATE_CNTR <= FIRST_PREAMBLE_EXTENSION;	
            elsif(STATE = 2) and (STATE_CNTR <= 1) then
               -- end of preamble. Start sync word
                STATE <= 3;
                STATE_CNTR <= SYNC_LENGTH;
            elsif(STATE = 3) and (STATE_CNTR <= 1) then
               -- end of sync word. 
					if(ZERO_GAP_LENGTH = '1') then
						-- no gap. send a frame of payload data 
						STATE <= 5;
						STATE_CNTR <= to_integer(unsigned(BURST_LENGTH)) ; 
					else
						-- Start gap
						 STATE <= 4;
						 STATE_CNTR <= GAP_LENGTH;
					end if;
            elsif(STATE = 4) and (STATE_CNTR <= 1) then
                 -- end of gap. send a frame of payload data 
                STATE <= 5;
                STATE_CNTR <= to_integer(unsigned(BURST_LENGTH)) ; 
            elsif(STATE = 5) and (STATE_CNTR <= 1) then
                -- end of payload field
					if(EVENT1 = '1') then
						-- continue transmission
						if(ZERO_SYNC_LENGTH = '1') then
							-- no sync. send a frame of payload data 
							STATE <= 5;
							STATE_CNTR <= to_integer(unsigned(BURST_LENGTH)) ; 
						else
							-- continue transmission. No gap between bursts. continue with sync word 
							STATE <= 3;
							STATE_CNTR <= SYNC_LENGTH;
						end if;
					else
						-- + a few extra dummy bits at the end
						STATE <= 6;
						STATE_CNTR <= 5; 
					end if;
            elsif(STATE = 6) and (STATE_CNTR = 0) then
               	-- end of burst
               	STATE <= 0;
            elsif(STATE_CNTR /= 0) then
                -- STATE_CNTR is expressed in number of symbol periods.
                STATE_CNTR <= STATE_CNTR - 1;
            end if;
        end if;
    end if;
end process;

--//-- PREAMBLE / SYNC / GAP ------------------------------------------------
-- aligned with SYMBOL_CLK_D2
TOGGLE_PREAMBLE_BIT_GEN:  process(CLK)
begin
	if rising_edge(CLK) then
		if(SYMBOL_CLK_D = '1') then
			-- Toggle preamble bit 
			TOGGLE_PREAMBLE_BIT <= not TOGGLE_PREAMBLE_BIT;	
		end if;
	end if;
end process;

-- sync word
-- aligned with SYMBOL_CLK_D2
SYNC_BIT_GEN:  process(CLK)
begin
	if rising_edge(CLK) then
		if(STATE = 1) or ((STATE = 5) and (STATE_CNTR <= 1)) then
			-- initialize/reload SYNC_WORD_SHIFT
			SYNC_WORD_SHIFT <= SYNC_WORD;
        elsif(SYMBOL_CLK_D = '1') and (STATE = 3) then
        	-- shift sync word left. MSb is first out. 
        	SYNC_WORD_SHIFT(SYNC_WORD'left downto 1) <= SYNC_WORD_SHIFT((SYNC_WORD'left - 1) downto 0);
        	-- sync bit is aligned with SYMBOL_CLK_D2, same as DATA5 
        	SYNC_BIT <= SYNC_WORD_SHIFT(SYNC_WORD'left);
        end if;
	end if;
end process;

--// OUTPUT SYMBOLS ---------------------------------------
TX_MOD_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		TX_MOD_SYMBOL_CLK <= SYMBOL_CLK_D2;
		TX_MOD_SYMBOL_CLKx2 <= SYMBOL_CLKx2_D2;
		TX_MOD_SYMBOL_CLKx4 <= SYMBOL_CLKx4_D2;
		TX_MOD_SYMBOL_CLKx8 <= SYMBOL_CLKx8_D2;
			
		if(SYMBOL_CLK_D2 = '1') then
			if(STATE_D = 2) then
					-- preamble. toggling bits
				TX_MOD_DATA <= (0 => TOGGLE_PREAMBLE_BIT, others => '0');
			elsif(STATE_D = 3) then
				-- sync word
				TX_MOD_DATA <= (0 => SYNC_BIT, others => '0');
			elsif(STATE_D = 4) then
				-- gap
				TX_MOD_DATA <= (0 => TOGGLE_PREAMBLE_BIT, others => '0');
			elsif(STATE_D = 5) then
				-- payload field
				TX_MOD_DATA <= DATA5x(6 downto 0);
			elsif(STATE_D = 6) then
				-- dummy end field
				TX_MOD_DATA <= (0 => TOGGLE_PREAMBLE_BIT, others => '0');
			else
				-- idle
				-- outside the burst
				TX_MOD_DATA <= (others => '0');
			end if;
		end if;
	end if;
end process;


-- TX_MOD_TYPE:
			-- 00 for sync field
			-- 01 for data field
			-- 10 unmodulated
TX_MOD_TYPE_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			TX_MOD_TYPE <= "10";	-- unmodulated
		elsif(SYMBOL_CLK_D2 = '1') then
			case STATE_D is
				when 1 => TX_MOD_TYPE <= "00";
				when 2 => TX_MOD_TYPE <= "00";
				when 3 => TX_MOD_TYPE <= "00";
				when 4 => TX_MOD_TYPE <= "00";
				when 5 => TX_MOD_TYPE <= "01";
				when 6 => TX_MOD_TYPE <= "01";
				when others => TX_MOD_TYPE <= "10";
			end case;
		end if;
	end if;
end process;

TX_MOD_DATA_VALID <= '0' when (STATE_D2 = 0) else '1';

----// TEST POINTS --------------------------------
--TP(1) <= TX_DATA_SAMPLE_CLK;
--TP(2) <= TX_CTS_local;
--TP(3) <= SAMPLE2_CLK;
--TP(4) <= SAMPLE2_CLK_REQ;
TP(1) <= EB1_BUFFER_EMPTY;
TP(2) <= EB2_BUFFER_EMPTY;
TP(3) <= '1' when (STATE_D = 0) else '0';
TP(4) <= '1' when (STATE_D = 5) else '0';
TP(5) <= TX_DATA_SAMPLE_CLK;
TP(6) <= TX_CTS_local;
TP(7) <= SAMPLE2_CLK;
TP(8) <= SAMPLE2_CLK_REQ;
TP(9) <= SAMPLE3_CLK;
TP(10) <= SAMPLE4_CLK;

--TP(2) <= SAMPLE2_CLK_E;
--TP(3) <= SAMPLE2_CLK_REQ;
--TP(4) <= TX_CTS_local;
--TP(5) <= '1' when CACHE_USED_SPACE = 0 else '0';
--TP(6) <= TX_DATA_SAMPLE_CLK;
--TP(8) <= '1' when (unsigned(EB2_USED) < 1040) else '0';
--TP(9) <= '1' when (unsigned(EB2_USED) < 1000) else '0';
--TP(10) <= DATA5x(7);
--TP(11) <= TX_WINDOW;
--TP(13) <= '1' when (STATE = 2) else '0';
--TP(14) <= '1' when (STATE = 3) else '0';
--TP(15) <= '1' when (STATE = 4) else '0';

end Behavioral;
