----------------------------------------------
-- MSS copyright 2012-2017
-- Filename: CPM_VA_PMU.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: N/A
-- Edit date: 11/13/17
-- Revision: 0
--
-- description:  Path metric unit (PMU): Viterbi algorithm tailored to CPM demodulation
-- Also include add-compare-select (ACS), traceback and SOVA.
-- Several trellis.
-- For example:
-- 2-FSK coherent: 4-state/8-branches trellis. State represents the symbol phases (0,90,180,270 deg)
-- 2-FSK non-coherent: 2-state/4-branches trellis. State represents 1 bit data hypothesis 0,1
-- PCM/FM: 8-state/16-branches trellis. State represents the 3 bits of a multi-symbol detector.
-- Input branch metrics must be separated by at least 4 CLKs
----------------------------------------------      
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.CPM_MF_PKG.all;	-- defines types for this interface

entity CPM_VA_PMU is
 	generic (
		-- NOTE: most customization parameters defined in CPM_MF_PKG (SEE cpm_mf.vhd)
		NBITS: integer := 10;	-- branch metrics precision
		NBITSM: integer := 12;  -- metrics precision
		SIMULATION: std_logic := '0'
	);
   port (
		--// CLOCKS, RESET
		CLK: in std_logic;
			-- reference clock for synchronous design
		SYNC_RESET: in std_logic;
			-- synchronous reset, active high.


		--// INPUTS
		BM_SAMPLE_CLK_IN: in std_logic;
			-- read input sample at rising_edge of CLK and SAMPLE_CLK_IN  = '1'.
			-- One CLK wide pulse.
		BM: in BM_TYPE;
			-- Branch metrics
			-- The meaning depends on the trellis.

			-- In the case of coherent demodulation (h = 0.5) 
			-- Branch metrics: real signed value. 
			-- One for each L-symbols hypothesis, and for each initial phase hypothesis.
			-- Example for MSK: BM(6) [6 = 110] is the likelihood of receiving a '1' (index MSb) while at state 10 (index LSbs)

			-- In the case of SOQPSK coherent demodulation
			-- Branch metrics: real signed value. Maximum = most likely
			-- Index 2 MSbs represent the transition (-1,0,1)
			-- Index 2 LSBs represent the start state (0,1,2,3)

			-- In the case of non-coherent L=2 demodulation (PCM-FM)
			-- Branch metrics: unsigned value (magnitude)
			-- One for each L-symbols hypothesis, and for each initial phase hypothesis.
			-- The 3-bit index represents the received bits hypothesis from earlier (bit0) to the latest(bit2)
			
			-- In the case of non-coherent L=1 demodulation (FSK,GFSK)
			-- BM(0,1,6,7)
			
		TRELLIS_TYPE: in std_logic_vector(1 downto 0);
			-- 0 = coherent demodulation,  4-state trellis (MSK,GMSK)
			-- 1 = non-coherent demodulation,  4-state trellis (PCM-FM)
			-- 2 = coherent demodulation,  8-state trellis for SOQPSK
			-- 3 = non-coherent demodulation, 2-state trellis (FSK,GFSK)

		--// OUTPUTS
		SAMPLE_CLK_OUT: out std_logic;
			-- BM_SAMPLE_CLK_IN_D7
		DATA_OUT: out std_logic;
			-- demodulated bit. Read at rising edge of CLK and SAMPLE_CLK_OUT = '1'. 
		RELIABILITY_OUT: out std_logic_vector((SOVA_NBITS-2) downto 0);
			-- reliability for the decoded bit.
		BEST_MF_INDEX: out integer range 0 to MPOWERLMAX-1;
			-- select the best MF output (for use by the carrier tracking loop in a coherent demod)
			-- Note: MF(BEST_MF_INDEX) represents the START state
		BEST_TB_STATE: out std_logic_vector(1 downto 0)
			-- best state after traceback. Delay is 9 symbols after SAMPLE_CLK_OUT
			-- for use in coherent carrier tracking and symbol tracking
			-- Beware: different tracebacks for best_state/best_mf_index (10) and data/reliability (12)
    );
end entity;

architecture behavioral of CPM_VA_PMU is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
	COMPONENT CPM_VA_ACS
	 Generic(
		NBITS: integer;
		NBITSM: integer;
		SOVA: std_logic;
		SIMULATION: std_logic
	 );
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		SAMPLE_CLK_IN : IN std_logic;
		METRIC_A_IN : IN std_logic_vector((NBITSM-1) downto 0);
		METRIC_B_IN : IN std_logic_vector((NBITSM-1) downto 0);
		BM_A : IN std_logic_vector((NBITS-1) downto 0);
		BM_B : IN std_logic_vector((NBITS-1) downto 0);          
		SHIFT_DOWN: in std_logic;
		SELECT_OUT : OUT std_logic;
		METRIC_C_OUT : OUT std_logic_vector((NBITSM-1) downto 0);
		DELTA_OUT: out std_logic_vector((SOVA_NBITS-2) downto 0);
		NEAR_SATURATION: out std_logic;
		SAMPLE_CLK_OUT : OUT std_logic
		);
	END COMPONENT;
	
	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;
	
-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
-- suffix _D denotes a one-CLK delayed version of the net with the same name

--// TREILLIS DESCRIPTION -------------------------
signal BM_SAMPLE_CLK_IN_D: std_logic := '0';
signal BM_SAMPLE_CLK_IN_D2: std_logic := '0';
type METRIC_TYPE is array (integer range 0 to N_TRELLIS_STATES_MAX-1) of std_logic_vector((NBITSM-1) downto 0);
signal METRIC_A_IN: METRIC_TYPE := (others => (others => '0')); 
signal METRIC_B_IN: METRIC_TYPE := (others => (others => '0')); 
signal BM_A: BM_TYPE := (others => (others => '0')); 
signal BM_B: BM_TYPE := (others => (others => '0')); 

--//-- ACS ----------------------------
signal METRIC_C_OUT: METRIC_TYPE := (others => (others => '0')); 
signal NEAR_SATURATION: std_logic_vector(N_TRELLIS_STATES_MAX-1 downto 0) := (others => '0');
signal SHIFT_DOWN: std_logic := '0';
signal WINNER: std_logic_vector(N_TRELLIS_STATES_MAX - 1 downto 0) := (others => '0');
signal ACS_SAMPLE_CLK: std_logic_vector(N_TRELLIS_STATES_MAX - 1 downto 0) := (others => '0');
signal DELTA: DELTA_TYPE := (others => (others => '0')); 

--//-- TRACEBACK ----------------------------------------
constant TB_LENGTH: integer := 12;	
type TB_TYPE is array (integer range 0 to (N_TRELLIS_STATES_MAX-1)) of std_logic_vector((TB_LENGTH-1) downto 0);
signal TB: TB_TYPE := (others => (others => '0'));
signal TB0: TB_TYPE := (others => (others => '0'));
signal TB1: TB_TYPE := (others => (others => '0'));
signal ACS_SAMPLE_CLK_D: std_logic := '0';

type TB_STATE_TYPE is array (integer range 0 to (N_TRELLIS_STATES_MAX-1)) of std_logic_vector(29 downto 0);
	-- stores the last start states(3-bits) for each end state. stack is 10 deep.
signal TB_STATE0: TB_STATE_TYPE	:= (others => (others => '0'));
signal TB_STATE1: TB_STATE_TYPE	:= (others => (others => '0'));
signal TB_STATE: TB_STATE_TYPE	:= (others => (others => '0'));

--//-- BEST TRACEBACK -----------------------------------
type METRIC_BESTOF2_TYPE is array (integer range 0 to N_TRELLIS_STATES_MAX/2-1) of std_logic_vector((NBITSM-1) downto 0);
signal METRIC_C_BESTOF2: METRIC_BESTOF2_TYPE := (others => (others => '0')); 
signal INDEX_BESTOF2: std_logic_vector(N_TRELLIS_STATES_MAX/2-1 downto 0) := (others => '0');
signal ACS_SAMPLE_CLK_D2: std_logic := '0';
type METRIC_BESTOF4_TYPE is array (integer range 0 to N_TRELLIS_STATES_MAX/4-1) of std_logic_vector((NBITSM-1) downto 0);
signal METRIC_C_BESTOF4: METRIC_BESTOF4_TYPE := (others => (others => '0')); 
type INDEX_BESTOF4_TYPE is array (integer range 0 to N_TRELLIS_STATES_MAX/4-1) of std_logic_vector(1 downto 0);
signal INDEX_BESTOF4: INDEX_BESTOF4_TYPE:= (others => (others => '0')); 
type INDEX_BESTOF8_TYPE is array (integer range 0 to N_TRELLIS_STATES_MAX/8-1) of std_logic_vector(2 downto 0);
signal INDEX_BESTOF8: INDEX_BESTOF8_TYPE:= (others => (others => '0')); 
signal BEST_TB_INDEX: integer range 0 to N_TRELLIS_STATES_MAX := 0;
signal ACS_SAMPLE_CLK_D3: std_logic := '0';
signal BEST_TB_STATE_local: std_logic_vector(1 downto 0) := (others => '0');
signal BEST_TB_STATE_D: std_logic_vector(1 downto 0) := (others => '0');
signal BEST_ALPHA: std_logic_vector(1 downto 0) := (others => '0');

--//-- RELIABILITY TRACEBACK --------------------------------------------
type RTB_TYPE is array (integer range 0 to (TB_LENGTH-1)) of DELTA_TYPE;
signal RTB: RTB_TYPE := (others => (others => (others => '0')));
signal RTB0: RTB_TYPE := (others => (others => (others => '0')));
signal RTB1: RTB_TYPE := (others => (others => (others => '0')));
signal BEST_SYMBOL_PHASE: std_logic_vector(11 downto 0) := (others => '0');

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

--// TREILLIS DESCRIPTION -------------------------
-- trellis depends on modulation type
TRELLIS_CONFIG_001: process(CLK)
begin
	if rising_edge(CLK) then
		BM_SAMPLE_CLK_IN_D <= BM_SAMPLE_CLK_IN;
		
		if(BM_SAMPLE_CLK_IN = '1') then
		
			if(unsigned(TRELLIS_TYPE) = 0) then
				-- 0 = coherent demodulation,  4-state trellis
				BM_A(0) <= BM(1);
				BM_B(0) <= BM(7);
				BM_A(1) <= BM(2);
				BM_B(1) <= BM(4);
				BM_A(2) <= BM(3);
				BM_B(2) <= BM(5);
				BM_A(3) <= BM(0);
				BM_B(3) <= BM(6);
				
				-- end state 00, from start state A=01(0) or start state B=11(1)
				METRIC_A_IN(0) <= METRIC_C_OUT(1);
				METRIC_B_IN(0) <= METRIC_C_OUT(3);
				-- end state 01, from start state A=10(0) or start state B=00(1)
				METRIC_A_IN(1) <= METRIC_C_OUT(2);
				METRIC_B_IN(1) <= METRIC_C_OUT(0);
				-- end state 10, from start state A=11(0) or start state B=01(1)
				METRIC_A_IN(2) <= METRIC_C_OUT(3);
				METRIC_B_IN(2) <= METRIC_C_OUT(1);
				-- end state 11, from start state A=00(0) or start state B=10(1)
				METRIC_A_IN(3) <= METRIC_C_OUT(0);
				METRIC_B_IN(3) <= METRIC_C_OUT(2);
				
			elsif(unsigned(TRELLIS_TYPE) = 1) then
				-- 1 = non-coherent demodulation,  4-state trellis
				BM_A(0) <= BM(0);
				BM_B(0) <= BM(1);
				BM_A(1) <= BM(2);
				BM_B(1) <= BM(3);
				BM_A(2) <= BM(4);
				BM_B(2) <= BM(5);
				BM_A(3) <= BM(6);
				BM_B(3) <= BM(7);
				
				METRIC_A_IN(0) <= METRIC_C_OUT(0);
				METRIC_B_IN(0) <= METRIC_C_OUT(1);
				METRIC_A_IN(1) <= METRIC_C_OUT(2);
				METRIC_B_IN(1) <= METRIC_C_OUT(3);
				METRIC_A_IN(2) <= METRIC_C_OUT(0);
				METRIC_B_IN(2) <= METRIC_C_OUT(1);
				METRIC_A_IN(3) <= METRIC_C_OUT(2);
				METRIC_B_IN(3) <= METRIC_C_OUT(3);

			elsif(unsigned(TRELLIS_TYPE) = 2) then
				-- 2 = coherent demodulation,  8-state trellis for SOQPSK
				-- State MSb = odd/even
				-- State 2 LSbs = 0/90/180/270 phase
				BM_A(0) <= BM(4);
				BM_B(0) <= BM(11);
				BM_A(1) <= BM(5);
				BM_B(1) <= BM(2);
				BM_A(2) <= BM(9);
				BM_B(2) <= BM(6);
				BM_A(3) <= BM(0);
				BM_B(3) <= BM(7);
				BM_A(4) <= BM(4);
				BM_B(4) <= BM(1);
				BM_A(5) <= BM(8);
				BM_B(5) <= BM(5);
				BM_A(6) <= BM(3);
				BM_B(6) <= BM(6);
				BM_A(7) <= BM(7);
				BM_B(7) <= BM(10);
				
				METRIC_A_IN(0) <= METRIC_C_OUT(4);
				METRIC_B_IN(0) <= METRIC_C_OUT(7);
				METRIC_A_IN(1) <= METRIC_C_OUT(5);
				METRIC_B_IN(1) <= METRIC_C_OUT(6);
				METRIC_A_IN(2) <= METRIC_C_OUT(5);
				METRIC_B_IN(2) <= METRIC_C_OUT(6);
				METRIC_A_IN(3) <= METRIC_C_OUT(4);
				METRIC_B_IN(3) <= METRIC_C_OUT(7);
				METRIC_A_IN(4) <= METRIC_C_OUT(0);
				METRIC_B_IN(4) <= METRIC_C_OUT(1);
				METRIC_A_IN(5) <= METRIC_C_OUT(0);
				METRIC_B_IN(5) <= METRIC_C_OUT(1);
				METRIC_A_IN(6) <= METRIC_C_OUT(3);
				METRIC_B_IN(6) <= METRIC_C_OUT(2);
				METRIC_A_IN(7) <= METRIC_C_OUT(3);
				METRIC_B_IN(7) <= METRIC_C_OUT(2);

			elsif(unsigned(TRELLIS_TYPE) = 3) then
				-- 3 = non-coherent demodulation,  2-state trellis
				BM_A(0) <= BM(0);
				BM_B(0) <= BM(1);
				BM_A(1) <= BM(6);
				BM_B(1) <= BM(7);
				
				METRIC_A_IN(0) <= METRIC_C_OUT(0);
				METRIC_B_IN(0) <= METRIC_C_OUT(1);
				METRIC_A_IN(1) <= METRIC_C_OUT(0);
				METRIC_B_IN(1) <= METRIC_C_OUT(1);
			end if;
		end if;
	end if;
end process;

--//-- ACS ----------------------------
-- ACS selects the condition leading to the largest metric.
-- Latency: 2 CLKs
VA_ACS_X: for I in 0 to N_TRELLIS_STATES_MAX-1 generate
	VA_ACS_00: CPM_VA_ACS 
	GENERIC MAP(
		NBITS => NBITS,
		NBITSM => NBITSM,
		SOVA => SOVA,
		SIMULATION => SIMULATION
	)
	PORT MAP(
			CLK => CLK,
			SYNC_RESET => SYNC_RESET,
			SAMPLE_CLK_IN => BM_SAMPLE_CLK_IN_D,
			BM_A => BM_A(I),
			BM_B => BM_B(I),
			METRIC_A_IN => METRIC_A_IN(I),
			METRIC_B_IN => METRIC_B_IN(I),
			SHIFT_DOWN => SHIFT_DOWN,
			SELECT_OUT => WINNER(I),	-- 0 if A best, 1 if B best
			METRIC_C_OUT => METRIC_C_OUT(I),
			DELTA_OUT => DELTA(I),
			NEAR_SATURATION => NEAR_SATURATION(I),
			SAMPLE_CLK_OUT => ACS_SAMPLE_CLK(I)	-- we only need one
	);
	
end generate;

-- prevent overflow of metric accumulators
SHIFT_DOWN <= '0' when (unsigned(NEAR_SATURATION) = 0) else '1';

--//-- TRACEBACK ----------------------------------------
-- traceback connections depends on modulation type
TB_CONFIG_001: process(TB, TRELLIS_TYPE)
begin
	if(unsigned(TRELLIS_TYPE) = 0) then
		-- 0 = coherent demodulation,  4-state trellis
		TB0(0) <= TB(1);		-- ACS selected path A as best origin
		TB1(0) <= TB(3);		-- ACS selected path B as best origin
		TB0(1) <= TB(2);		-- ACS selected path A as best origin
		TB1(1) <= TB(0);		-- ACS selected path B as best origin
		TB0(2) <= TB(3);		-- ACS selected path A as best origin
		TB1(2) <= TB(1);		-- ACS selected path B as best origin
		TB0(3) <= TB(0);		-- ACS selected path A as best origin
		TB1(3) <= TB(2);		-- ACS selected path B as best origin

		TB_STATE0(0) <= TB_STATE(1);		-- ACS selected path A as best origin
		TB_STATE1(0) <= TB_STATE(3);		-- ACS selected path B as best origin
		TB_STATE0(1) <= TB_STATE(2);		-- ACS selected path A as best origin
		TB_STATE1(1) <= TB_STATE(0);		-- ACS selected path B as best origin
		TB_STATE0(2) <= TB_STATE(3);		-- ACS selected path A as best origin
		TB_STATE1(2) <= TB_STATE(1);		-- ACS selected path B as best origin
		TB_STATE0(3) <= TB_STATE(0);		-- ACS selected path A as best origin
		TB_STATE1(3) <= TB_STATE(2);		-- ACS selected path B as best origin
		
	elsif(unsigned(TRELLIS_TYPE) = 1) then
		-- 1 = non-coherent demodulation,  4-state trellis
		TB0(0) <= TB(0);		-- ACS selected path A as best origin
		TB1(0) <= TB(1);		-- ACS selected path B as best origin
		TB0(1) <= TB(2);		-- ACS selected path A as best origin
		TB1(1) <= TB(3);		-- ACS selected path B as best origin
		TB0(2) <= TB(0);		-- ACS selected path A as best origin
		TB1(2) <= TB(1);		-- ACS selected path B as best origin
		TB0(3) <= TB(2);		-- ACS selected path A as best origin
		TB1(3) <= TB(3);		-- ACS selected path B as best origin

	elsif(unsigned(TRELLIS_TYPE) = 2) then
		-- 2 = coherent demodulation,  8-state trellis for SOQPSK
		TB0(0) <= TB(4);		-- ACS selected path A as best origin
		TB1(0) <= TB(7);		-- ACS selected path B as best origin
		TB0(1) <= TB(5);		-- ACS selected path A as best origin
		TB1(1) <= TB(6);		-- ACS selected path B as best origin
		TB0(2) <= TB(5);		-- ACS selected path A as best origin
		TB1(2) <= TB(6);		-- ACS selected path B as best origin
		TB0(3) <= TB(4);		-- ACS selected path A as best origin
		TB1(3) <= TB(7);		-- ACS selected path B as best origin
		TB0(4) <= TB(0);		-- ACS selected path A as best origin
		TB1(4) <= TB(1);		-- ACS selected path B as best origin
		TB0(5) <= TB(0);		-- ACS selected path A as best origin
		TB1(5) <= TB(1);		-- ACS selected path B as best origin
		TB0(6) <= TB(3);		-- ACS selected path A as best origin
		TB1(6) <= TB(2);		-- ACS selected path B as best origin
		TB0(7) <= TB(3);		-- ACS selected path A as best origin
		TB1(7) <= TB(2);		-- ACS selected path B as best origin
		
		TB_STATE0(0) <= TB_STATE(4);		-- ACS selected path A as best origin
		TB_STATE1(0) <= TB_STATE(7);		-- ACS selected path B as best origin
		TB_STATE0(1) <= TB_STATE(5);		-- ACS selected path A as best origin
		TB_STATE1(1) <= TB_STATE(6);		-- ACS selected path B as best origin
		TB_STATE0(2) <= TB_STATE(5);		-- ACS selected path A as best origin
		TB_STATE1(2) <= TB_STATE(6);		-- ACS selected path B as best origin
		TB_STATE0(3) <= TB_STATE(4);		-- ACS selected path A as best origin
		TB_STATE1(3) <= TB_STATE(7);		-- ACS selected path B as best origin
		TB_STATE0(4) <= TB_STATE(0);		-- ACS selected path A as best origin
		TB_STATE1(4) <= TB_STATE(1);		-- ACS selected path B as best origin
		TB_STATE0(5) <= TB_STATE(0);		-- ACS selected path A as best origin
		TB_STATE1(5) <= TB_STATE(1);		-- ACS selected path B as best origin
		TB_STATE0(6) <= TB_STATE(3);		-- ACS selected path A as best origin
		TB_STATE1(6) <= TB_STATE(2);		-- ACS selected path B as best origin
		TB_STATE0(7) <= TB_STATE(3);		-- ACS selected path A as best origin
		TB_STATE1(7) <= TB_STATE(2);		-- ACS selected path B as best origin
	else
	--elsif(unsigned(TRELLIS_TYPE) = 3) then
		-- 3 = non-coherent demodulation,  2-state trellis
		TB0(0) <= TB(0);		-- ACS selected path A as best origin
		TB1(0) <= TB(1);		-- ACS selected path B as best origin
		TB0(1) <= TB(0);		-- ACS selected path A as best origin
		TB1(1) <= TB(1);		-- ACS selected path B as best origin

	end if;
end process;

VA_TB_X: for I in 0 to N_TRELLIS_STATES_MAX-1 generate
	TB_GEN_00: process(CLK)
	variable STATEu: unsigned(LOG2_N_TRELLIS_STATES_MAX-1 downto 0) := to_unsigned(I,LOG2_N_TRELLIS_STATES_MAX);
	begin
		if rising_edge(CLK) then
			if (ACS_SAMPLE_CLK(0) = '1') then

				if(unsigned(TRELLIS_TYPE) = 0) then
					-- 0 = coherent demodulation,  4-state trellis
					-- decoded bit is the winner of each ACS comparison
					TB(I)(TB_LENGTH -1) <= WINNER(I);
					TB_STATE(I)(28 downto 27) <= std_logic_vector(STATEu(1 downto 0));
				elsif(unsigned(TRELLIS_TYPE) = 1) or (unsigned(TRELLIS_TYPE) = 3) then
					-- 1 = non-coherent demodulation,  4-state trellis
					-- 3 = non-coherent demodulation,  2-state trellis
					-- decoded bit is the state lsb
					TB(I)(TB_LENGTH -1) <= STATEu(0);
				elsif(unsigned(TRELLIS_TYPE) = 2) then
					-- 2 = coherent demodulation,  8-state trellis for SOQPSK
					TB_STATE(I)(29 downto 27) <= std_logic_vector(STATEu(2 downto 0));
					if(STATEu(2) = '0') then
						-- n even
						TB(I)(TB_LENGTH -1) <= STATEu(1);
					else
						-- n odd
						TB(I)(TB_LENGTH -1) <= STATEu(1) xor STATEu(0);
					end if;
				end if;
				if(WINNER(I) = '0') then
					-- ACS selected path A as best origin
					for J in 1 to (TB_LENGTH-1) loop
						TB(I)(J-1) <= TB0(I)(J);	-- shift right
					end loop;
					for K in 0 to 8 loop
						TB_STATE(I)(3*K+2 downto 3*K) <= TB_STATE0(I)(3*K+5 downto 3*K+3);
					end loop;
				else
					-- ACS selected path B as best origin
					for J in 1 to (TB_LENGTH-1) loop
						TB(I)(J-1) <= TB1(I)(J);	-- shift right
					end loop;
					for K in 0 to 8 loop
						TB_STATE(I)(3*K+2 downto 3*K) <= TB_STATE1(I)(3*K+5 downto 3*K+3);
					end loop;
				end if;
			end if;
		end if;
	end process;
end generate;



--//-- BEST TRACEBACK -----------------------------------
-- best end state: find the state with the highest metric
BESTOF2: process(CLK)
begin
	if rising_edge(CLK) then
		ACS_SAMPLE_CLK_D <= ACS_SAMPLE_CLK(0);
		
		if(ACS_SAMPLE_CLK(0) = '1') then
			for I in 0 to N_TRELLIS_STATES_MAX/2-1 loop
				if(signed(METRIC_C_OUT(2*I)) > signed(METRIC_C_OUT(2*I + 1))) then
					METRIC_C_BESTOF2(I) <= METRIC_C_OUT(2*I);
					INDEX_BESTOF2(I) <= '0';
				else
					METRIC_C_BESTOF2(I) <= METRIC_C_OUT(2*I+1);
					INDEX_BESTOF2(I) <= '1';
				end if;
			end loop;
		end if;
	end if;
end process;

BESTOF4: process(CLK)
begin
	if rising_edge(CLK) then
		ACS_SAMPLE_CLK_D2 <= ACS_SAMPLE_CLK_D;
		
		if(ACS_SAMPLE_CLK_D = '1') then
			for I in 0 to N_TRELLIS_STATES_MAX/4-1 loop
				if(signed(METRIC_C_BESTOF2(2*I)) > signed(METRIC_C_BESTOF2(2*I + 1))) then
					METRIC_C_BESTOF4(I) <= METRIC_C_BESTOF2(2*I);
					INDEX_BESTOF4(I)(1) <= '0';
					INDEX_BESTOF4(I)(0) <= INDEX_BESTOF2(2*I);
				else
					METRIC_C_BESTOF4(I) <= METRIC_C_BESTOF2(2*I+1);
					INDEX_BESTOF4(I)(1) <= '1';
					INDEX_BESTOF4(I)(0) <= INDEX_BESTOF2(2*I+1);
				end if;
			end loop;
		end if;
	end if;
end process;

BESTOF8x: if(N_TRELLIS_STATES_MAX >= 8) generate
	BESTOF8: process(CLK)
	begin
		if rising_edge(CLK) then
			ACS_SAMPLE_CLK_D3 <= ACS_SAMPLE_CLK_D2;
			
			if(ACS_SAMPLE_CLK_D2 = '1') then
				for I in 0 to N_TRELLIS_STATES_MAX/8-1 loop
					if(signed(METRIC_C_BESTOF4(2*I)) > signed(METRIC_C_BESTOF4(2*I + 1))) then
						--METRIC_C_BESTOF8(I) <= METRIC_C_BESTOF4(2*I);
						INDEX_BESTOF8(I)(2) <= '0';
						INDEX_BESTOF8(I)(1 downto 0) <= INDEX_BESTOF4(2*I);
					else
						--METRIC_C_BESTOF8(I) <= METRIC_C_BESTOF4(2*I+1);
						INDEX_BESTOF8(I)(2) <= '1';
						INDEX_BESTOF8(I)(1 downto 0) <= INDEX_BESTOF4(2*I+1);
					end if;
				end loop;
			end if;
		end if;
	end process;
end generate;

-- best traceback is at index INDEX_BESTOF2/4/8/etc(0)
BEST_INDEX_GEN: process(TRELLIS_TYPE, INDEX_BESTOF4, INDEX_BESTOF8)
begin
	if(unsigned(TRELLIS_TYPE) = 0) or (unsigned(TRELLIS_TYPE) = 1)  then
		-- 0 = coherent demodulation,  4-state trellis
		-- 1 = non-coherent demodulation,  4-state trellis
		BEST_TB_INDEX <= to_integer(unsigned(INDEX_BESTOF4(0)));
	elsif(unsigned(TRELLIS_TYPE) = 2) then
		-- 2 = coherent demodulation,  8-state trellis for SOQPSK
		BEST_TB_INDEX <= to_integer(unsigned(INDEX_BESTOF8(0)));
	else
		-- 3 = non-coherent demodulation,  2-state trellis
		if(INDEX_BESTOF2(0) = '0') then
			BEST_TB_INDEX <= 0;
		else
			BEST_TB_INDEX <= 1;
		end if;
	end if;
end process;

OUTPUT_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_OUT <= ACS_SAMPLE_CLK_D3;
		if(ACS_SAMPLE_CLK_D3 = '1') then
			DATA_OUT <= TB(BEST_TB_INDEX)(0);
			-- reliability information is at the start of the best path
			RELIABILITY_OUT <= RTB(0)(BEST_TB_INDEX);
		end if;
	end if;
end process;


--//-- SOVA --------------------------------------------
-- VHDL code for Soft-Output Viterbi Algorithm (SOVA), reliability traceback unit
-- Algorithm follows article "A Viterbi Algorithm with Soft-Decision Outputs and its Applications"
-- Joachim Hagenauer, Peter Hoeher, DLR, Globecom 1989

RTBU: if(SOVA = '1') generate
	-- reliability traceback connections depends on modulation type
	RTBU_001: for J in 1 to (TB_LENGTH-1) generate
		RTBU_001: process(RTB, TRELLIS_TYPE)
		begin
			if(unsigned(TRELLIS_TYPE) = 0) then
				-- 0 = coherent demodulation,  4-state trellis
				RTB0(J)(0) <= RTB(J)(1);		-- ACS selected path A as best origin
				RTB1(J)(0) <= RTB(J)(3);		-- ACS selected path B as best origin
				RTB0(J)(1) <= RTB(J)(2);		-- ACS selected path A as best origin
				RTB1(J)(1) <= RTB(J)(0);		-- ACS selected path B as best origin
				RTB0(J)(2) <= RTB(J)(3);		-- ACS selected path A as best origin
				RTB1(J)(2) <= RTB(J)(1);		-- ACS selected path B as best origin
				RTB0(J)(3) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(3) <= RTB(J)(2);		-- ACS selected path B as best origin
				
			elsif(unsigned(TRELLIS_TYPE) = 1) then
				-- 1 = non-coherent demodulation L=2,  4-state trellis
				RTB0(J)(0) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(0) <= RTB(J)(1);		-- ACS selected path B as best origin
				RTB0(J)(1) <= RTB(J)(2);		-- ACS selected path A as best origin
				RTB1(J)(1) <= RTB(J)(3);		-- ACS selected path B as best origin
				RTB0(J)(2) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(2) <= RTB(J)(1);		-- ACS selected path B as best origin
				RTB0(J)(3) <= RTB(J)(2);		-- ACS selected path A as best origin
				RTB1(J)(3) <= RTB(J)(3);		-- ACS selected path B as best origin

			elsif(unsigned(TRELLIS_TYPE) = 2) then
				-- 2 = coherent demodulation,  8-state trellis for SOQPSK
				RTB0(J)(0) <= RTB(J)(4);		-- ACS selected path A as best origin
				RTB1(J)(0) <= RTB(J)(7);		-- ACS selected path B as best origin
				RTB0(J)(1) <= RTB(J)(5);		-- ACS selected path A as best origin
				RTB1(J)(1) <= RTB(J)(6);		-- ACS selected path B as best origin
				RTB0(J)(2) <= RTB(J)(5);		-- ACS selected path A as best origin
				RTB1(J)(2) <= RTB(J)(6);		-- ACS selected path B as best origin
				RTB0(J)(3) <= RTB(J)(4);		-- ACS selected path A as best origin
				RTB1(J)(3) <= RTB(J)(7);		-- ACS selected path B as best origin
				RTB0(J)(4) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(4) <= RTB(J)(1);		-- ACS selected path B as best origin
				RTB0(J)(5) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(5) <= RTB(J)(1);		-- ACS selected path B as best origin
				RTB0(J)(6) <= RTB(J)(3);		-- ACS selected path A as best origin
				RTB1(J)(6) <= RTB(J)(2);		-- ACS selected path B as best origin
				RTB0(J)(7) <= RTB(J)(3);		-- ACS selected path A as best origin
				RTB1(J)(7) <= RTB(J)(2);		-- ACS selected path B as best origin

			else
			--elsif(unsigned(TRELLIS_TYPE) = 3) then
				-- 1 = non-coherent demodulation L=1,  2-state trellis
				RTB0(J)(0) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(0) <= RTB(J)(1);		-- ACS selected path B as best origin
				RTB0(J)(1) <= RTB(J)(0);		-- ACS selected path A as best origin
				RTB1(J)(1) <= RTB(J)(1);		-- ACS selected path B as best origin

			end if;
		end process;
	end generate;
	-- prime the reliability traceback unit (RTU) array
--	RTBU_002: for I in 0 to N_TRELLIS_STATES_MAX-1 generate
--		RTBU_002x: process(CLK)
--		begin
--			if rising_edge(CLK) then
--				if (ACS_SAMPLE_CLK(0) = '1') then
--					RTB(TB_LENGTH -1)(I) <= DELTA(I);
--					if(WINNER(I) = '0') then
--						-- ACS selected path A as best origin
--						for J in 1 to (TB_LENGTH-1) loop
--							RTB(J-1)(I) <= RTB0(J)(I);	-- shift right
--						end loop;
--					else
--						-- ACS selected path B as best origin
--						for J in 1 to (TB_LENGTH-1) loop
--							RTB(J-1)(I) <= RTB1(J)(I);	-- shift right
--						end loop;
--					end if;
--				end if;
--			end if;
--		end process;
--	end generate;

	RTBU_002a: for I in 0 to N_TRELLIS_STATES_MAX-1 generate
		RTBU_002x: process(CLK)
		begin
			if rising_edge(CLK) then
				if (ACS_SAMPLE_CLK(0) = '1') then
					RTB(TB_LENGTH -1)(I) <= DELTA(I);
				end if;
			end if;
		end process;
	end generate;

	RTBU_002b: for I in 0 to N_TRELLIS_STATES_MAX-1 generate
		RTBU_002b: for J in 1 to (TB_LENGTH-1) generate
			RTBU_002x: process(CLK)
			begin
				if rising_edge(CLK) then
					if (ACS_SAMPLE_CLK(0) = '1') then
						if(WINNER(I) = '0') then
							-- ACS selected path A as best origin
							RTB(J-1)(I) <= RTB0(J)(I);	-- shift right
						else
							-- ACS selected path B as best origin
							RTB(J-1)(I) <= RTB1(J)(I);	-- shift right
						end if;
					end if;
				end if;
			end process;
		end generate;
	end generate;

	
end generate;

--//-- BEST MATCHED FILTER OUTPUT --------------------------------------------
-- find out the best matched filter output for use in the carrier and symbol tracking loops
BEST_MF_001: process(TRELLIS_TYPE, BEST_ALPHA)
begin
	if(unsigned(TRELLIS_TYPE) = 0) then
		-- 0 = coherent demodulation,  4-state trellis
		case(BEST_ALPHA) is
			when "01" => BEST_MF_INDEX <= 1;	-- +90 deg rotation  this symbol
			when others => BEST_MF_INDEX <= 0; -- -90 deg rotation  this symbol
		end case;
	else
--	elsif(unsigned(TRELLIS_TYPE) = 2) then
		-- 2 = coherent demodulation,  8-state trellis for SOQPSK
		case(BEST_ALPHA) is
			when "00" => BEST_MF_INDEX <= 1;	-- no rotation this symbol
			when "01" => BEST_MF_INDEX <= 2;	-- +90 deg rotation  this symbol
			when others => BEST_MF_INDEX <= 0; -- -90 deg rotation  this symbol
		end case;
	end if;
				
				
--				-- State MSb = odd/even
--				-- State 2 LSbs = 0/90/180/270 phase
--				if(WINNER(BEST_TB_INDEX) = '0') then
--					-- A is best
--					case BEST_TB_INDEX is
--						when 0 => BEST_MF_INDEX <=  1;
--						when 1 => BEST_MF_INDEX <=  1;
--						when 2 => BEST_MF_INDEX <=  2;
--						when 3 => BEST_MF_INDEX <=  0;
--						when 4 => BEST_MF_INDEX <=  1;
--						when 5 => BEST_MF_INDEX <=  2;
--						when 6 => BEST_MF_INDEX <=  0;
--						when others => BEST_MF_INDEX <=  1;
--					end case;
--				else
--					-- B is best
--					case BEST_TB_INDEX is
--						when 0 => BEST_MF_INDEX <=  2;
--						when 1 => BEST_MF_INDEX <=  0;
--						when 2 => BEST_MF_INDEX <=  1;
--						when 3 => BEST_MF_INDEX <=  1;
--						when 4 => BEST_MF_INDEX <=  0;
--						when 5 => BEST_MF_INDEX <=  1;
--						when 6 => BEST_MF_INDEX <=  1;
--						when others => BEST_MF_INDEX <=  2;
--					end case;
--				end if;
end process;

-- Select the best state after a 10-bit traceback (TB_STATE)
BEST_TB_STATE_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(ACS_SAMPLE_CLK_D3 = '1') then
			if(unsigned(TRELLIS_TYPE) = 0) then
				-- 0 = coherent demodulation,  4-state trellis
				BEST_TB_STATE_local <= TB_STATE(BEST_TB_INDEX)(1 downto 0);	-- 2 LSBs are the phase 0/90/180/270 deg
				BEST_TB_STATE_D <= BEST_TB_STATE_local;
				BEST_ALPHA <= std_logic_vector(signed(TB_STATE(BEST_TB_INDEX)(1 downto 0)) - signed(BEST_TB_STATE_local));
					-- decoded alpha is the transition from this state to the next one. Can only take values -1,1
					-- Delay is 9 symbols after SAMPLE_CLK_OUT
			elsif(unsigned(TRELLIS_TYPE) = 2) then
				-- 2 = coherent demodulation,  8-state trellis for SOQPSK
				BEST_TB_STATE_local <= TB_STATE(BEST_TB_INDEX)(1 downto 0);	-- 2 LSBs are the phase 0/90/180/270 deg
				BEST_TB_STATE_D <= BEST_TB_STATE_local;
				BEST_ALPHA <= std_logic_vector(signed(TB_STATE(BEST_TB_INDEX)(1 downto 0)) - signed(BEST_TB_STATE_local));
					-- decoded alpha is the transition from this state to the next one. Can only take values -1,0,1
					-- Delay is 9 symbols after SAMPLE_CLK_OUT
			end if;
		end if;
	end if;
end process;
BEST_TB_STATE <= BEST_TB_STATE_D;

--
--//-- BEST CONSTELLATION POINT --------------------------------------------
---- output best known constellation point (on the complex circle of course) at this time.
---- This is for use by the carrier and symbol timing tracking loops. 
---- We will have a more reliable knowledge after the traceback unit, but we can't wait for that.
--
---- latency: 2 CLKs
--BEST_SYMBOL_PHASE <= BEST_END_STATE_local & "0000000000";
--BEST_CONSTELLATION_001: SIGNED_SIN_COS_TBL3 
--GENERIC MAP (
--	NADDR => 10
--)
--PORT MAP(
--	CLK => CLK,
--	PHASE => BEST_SYMBOL_PHASE,
--	SIN_OUT => BEST_SYMBOL_Q,
--	COS_OUT => BEST_SYMBOL_I
--	);
--
--BEST_CONSTELLATION_002: process(CLK)
--begin
--	if rising_edge(CLK) then
--		ACS_SAMPLE_CLK_D3 <= ACS_SAMPLE_CLK_D2;
--		BEST_SYMBOL_CLK <= ACS_SAMPLE_CLK_D3;
--	end if;
--end process;
--


end behavioral;					
