----------------------------------------------
-- MSS copyright 2017
-- Filename: VA_RTU.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: N/A
-- Edit date: 5/30/17
-- Revision: 0
--
-- 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
----------------------------------------------      
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 VA_RTU is
 	generic (
		SIMULATION: std_logic := '0'
	);
   port (
		--// CLOCKS, RESET
		CLK: in std_logic;
			-- reference clock for synchronous design
		SYNC_RESET: in std_logic;
			-- global reset, active high.


		--// INPUTS
		SAMPLE_CLK_IN: in std_logic;
			-- read input sample at rising_edge of CLK and SAMPLE_CLK_IN  = '1'.
			-- One CLK wide pulse.
		DELTA: in DELTA_TYPE;
		WINNER: IN std_logic_vector(N_TRELLIS_STATES_MAX - 1 downto 0);	
			-- selected best prior state for each current state
		TB: in TB_TYPE;
			-- hard decision traceback information. 
		BEST_END_STATE: in std_logic_vector((LOG2_N_TRELLIS_STATES_MAX-1) downto 0);
			-- end state with the best metric 
			
		--// OUTPUTS
		RELIABILITY_OUT: out std_logic_vector((SOVA_NBITS-2) downto 0);
			-- reliability for the decoded bit.
		SAMPLE_CLK_OUT: out std_logic
			-- Read reliability at rising edge of CLK and SAMPLE_CLK_OUT = '1'.
    );
end entity;

architecture behavioral of VA_RTU is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
-- suffix _D denotes a one-CLK delayed version of the net with the same name
type RTB_TYPE is array (integer range 0 to (TB_LENGTH-1)) of DELTA_TYPE;
signal RTB: RTB_TYPE := (others => (others => (others => '0')));
signal BEST_END_STATE_D: unsigned((LOG2_N_TRELLIS_STATES_MAX-1) downto 0) := (others => '0');
signal SAMPLE_CLK_IN_D: std_logic := '0';
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin   

-- prime the array
RTB_GEN_0: for I in 0 to N_TRELLIS_STATES_MAX-1 generate
	RTB_GEN_00: process(CLK)
	begin
		if rising_edge(CLK) then
			if(SAMPLE_CLK_IN = '1') then
				RTB(TB_LENGTH -1)(I) <= DELTA(I);
			end if;
		end if;
	end process;
end generate;

-- traceback ending with 00 as most recent state
-- end state 00, from start state A=01(0) or start state B=11(1)
RTB_GEN_00x: for J in 1 to  (TB_LENGTH-1) generate
RTB_GEN_00: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			-- shift in delta values over TB_LENGTH
			-- Trellis-dependent!
			-- get min
			if(WINNER(0) = '0') then
				-- ACS selected path A as best origin
				if(TB(1)(J) /= TB(3)(J)) and (DELTA(0) < RTB(J)(1))   then
					RTB(J-1)(0) <= DELTA(0);
				else
					RTB(J-1)(0) <= RTB(J)(1);
				end if;
			else
				-- ACS selected path B as best origin
				if(TB(1)(J) /= TB(3)(J)) and (DELTA(0) < RTB(J)(3))   then
					RTB(J-1)(0) <= DELTA(0);
				else
					RTB(J-1)(0) <= RTB(J)(3);
				end if;
			end if;
		end if;
	end if;
end process;
end generate;
-- traceback ending with 01 as most recent state
-- end state 01, from start state A=10(0) or start state B=00(1)
RTB_GEN_01x: for J in 1 to  (TB_LENGTH-1) generate
RTB_GEN_01: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			-- shift in delta values over TB_LENGTH
			-- Trellis-dependent!
			-- get min
			if(WINNER(1) = '0') then
				-- ACS selected path A as best origin
				if(TB(2)(J) /= TB(0)(J)) and (DELTA(1) < RTB(J)(2))   then
					RTB(J-1)(1) <= DELTA(1);
				else
					RTB(J-1)(1) <= RTB(J)(2);
				end if;
			else
				-- ACS selected path B as best origin
				if(TB(2)(J) /= TB(0)(J)) and (DELTA(1) < RTB(J)(0))   then
					RTB(J-1)(1) <= DELTA(1);
				else
					RTB(J-1)(1) <= RTB(J)(0);
				end if;
			end if;
		end if;
	end if;
end process;
end generate;

-- traceback ending with 10 as most recent state
-- end state 10, from start state A=11(0) or start state B=01(1)
RTB_GEN_10x: for J in 1 to  (TB_LENGTH-1) generate
RTB_GEN_10: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			-- shift in delta values over TB_LENGTH
			-- Trellis-dependent!
			-- get min
			if(WINNER(2) = '0') then
				-- ACS selected path A as best origin
				if(TB(3)(J) /= TB(1)(J)) and (DELTA(2) < RTB(J)(3))   then
					RTB(J-1)(2) <= DELTA(2);
				else
					RTB(J-1)(2) <= RTB(J)(3);
				end if;
			else
				-- ACS selected path B as best origin
				if(TB(3)(J) /= TB(1)(J)) and (DELTA(2) < RTB(J)(1))   then
					RTB(J-1)(2) <= DELTA(2);
				else
					RTB(J-1)(2) <= RTB(J)(1);
				end if;
			end if;
		end if;
	end if;
end process;
end generate;


-- traceback ending with 11 as most recent state
-- end state 11, from start state A=00(0) or start state B=10(1)
RTB_GEN_11x: for J in 1 to  (TB_LENGTH-1) generate
RTB_GEN_11: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			-- shift in delta values over TB_LENGTH
			-- Trellis-dependent!
			-- get min
			if(WINNER(3) = '0') then
				-- ACS selected path A as best origin
				if(TB(0)(J) /= TB(2)(J)) and (DELTA(3) < RTB(J)(0))   then
					RTB(J-1)(3) <= DELTA(3);
				else
					RTB(J-1)(3) <= RTB(J)(0);
				end if;
			else
				-- ACS selected path B as best origin
				if(TB(0)(J) /= TB(2)(J)) and (DELTA(3) < RTB(J)(2))   then
					RTB(J-1)(3) <= DELTA(3);
				else
					RTB(J-1)(3) <= RTB(J)(2);
				end if;
			end if;
		end if;
	end if;
end process;
end generate;

-- reliability information is at the start of the best path
-- trace back the treillis from the best end state to the origin state.
DEMOD_001: process(CLK)
begin
	if rising_edge(CLK) then
		-- wait one CLK so that we reshuffle the traceback
		SAMPLE_CLK_IN_D <= SAMPLE_CLK_IN;
		if (SAMPLE_CLK_IN = '1') then
			BEST_END_STATE_D <= unsigned(BEST_END_STATE);
		end if;
		
		SAMPLE_CLK_OUT <= SAMPLE_CLK_IN_D;
		if(SAMPLE_CLK_IN_D = '1') then
			RELIABILITY_OUT <= RTB(0)(to_integer(BEST_END_STATE_D));
		end if;
	end if;
end process;

end behavioral;					
