----------------------------------------------
-- MSS copyright 2012-2014
-- Filename: VA.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: N/A
-- Edit date: 10/7/14
-- Revision: 3
-- See also: 
--
-- VHDL code for Viterbi algorithm, including branch metric unit (BMU), add-compare-select (ACS), ....................
-- Can be instantitated multiple times for parallel decoding.
-- Input is a fixed-length block of bits, arranged in coding groups of N_PARITY_BITS and flagged for erasures.
--
-- Rev2 11/29/12 AZ
-- Passed parameters through generic for more configuration flexibility instead of common com1509pkg
--
-- Rev 3 10/7/14 AZ
-- Rephrase to work with Xilinx Vivado, ISIM, ISE 14
-- Corrected bug regarding CCSDS K=7 R=1/2  (need to invert G1 parity bit)
----------------------------------------------      
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.com1509pkg2.all;	-- defines global types, key configuration parameters to be defined at synthesis time.

entity VA is
	generic(
		BER_MEASUREMENT_EN: std_logic := '0';
			-- enable optional BER measurement and synchronization detection
		N_PARITY_BITS_MAX: integer:= 3;
		-- maximum number of parity bits. For example 3 for rate 1/3, or 2 for rate 2/3. 
		-- valid range 2 - 7
		-- size vectors/arrays accordingly.
		-- If this decoder is only used with 2-bit parity, smaller implementations may be achieved by setting N_PARITY_BITS_MAX=2
		K_MAX: integer:= 9;	
		-- maximum constraint length. Select 3,5,7, or 9
		TB_DEPTH: integer := 54;	
		-- traceback depth. MUST BE LESS THAN 120. Typically := 6*K_MAX
		HARD_SOFTN_DECISION: std_logic := '0';
		-- '1' for hard-decision (1-bit) decoding. Hamming distance (number of differing bits) is generated for each hypothesis being tested.
		-- '0' for 4-bit soft-decision decoding. Local (approximation of Euclidian) distance is generated.
		-- When configured for hard decision decoding, the input bit should be placed at DATAIN(3), while DATAIN(2 downto 0) is ignored.
		SIMULATION: std_logic := '0'
			-- enable optional simulation features
	);
   port (
		-- GLOBAL CLOCKS, RESETN
		CLK: in std_logic;	-- synchronous clock
		SYNC_RESET: in std_logic;		-- synchronous reset. active high.


		--// INPUTS
		-- Coding group
		DATAIN: in std_logic_vector((N_PARITY_BITS_MAX*4 - 1) downto 0);  	
			-- group of received (soft-quantized) bits associated with generator polynominal G0,G1,G2, etc 
			-- This array is sized for the code with the most parity bits, but only the N_PARITY_BITS least significant bits are used within.
			-- In the case of hard-decision decoding, the received encoded bit is placed at index 3, other soft-decision bits (2:0) being zero.
		ERASURES: in std_logic_vector((N_PARITY_BITS_MAX - 1) downto 0);	
			-- erasures on bit 0,1 or 2 ('1' indicates a puncture)	
			-- tells the BMU to ignore a specific bit or bits during distance computation.
		INPUT_VALID: in std_logic;
			-- 1-CLK wide pulse for each input coding group 
			-- above inputs are read at the rising edge of CLK when INPUT_VALID = '1'
			
		--// CONFIGURATION 
		-- The configuration parameters below can be changed dynamically at run-time.
		-- Use SYNC_RESET after such a configuration change.
		G: in std_logic_vector(62 downto 0);  
			-- G0,G1,etc. generator polynomials. 
			-- 1D array size is 7 (maximum number of parity bits) * 9 (maximum K).
			-- For each generator polynomial, unused generator polynominal taps at indices (K_MAX:K+1) (if K < K_MAX) must be set to '0'.
		N_PARITY_BITS: in integer range 2 to 7;
			-- actual number of parity bits (i.e. generator polynomials).
			-- valid range: 2 - 7
			-- MUST be less than or equal to the hardware capabilities set by N_PARITY_BITS_MAX defined in com1509pkg.vhd
		K: in integer range 3 to 9;
			-- constraint length.
			-- valid values: 3,5,7,9
			-- MUST be less than or equal to the hardware capabilities set by K_MAX defined in com1509pkg.vhd
		SPECIAL: in integer;
			-- 1 for CCSDS K=7, R=1/2 (inverted G1 parity bit)

		--// OUTPUTS
		-- output types: bulk (chunks of TB_DEPTH bits) or 1-bit serial
		DECODED_BITS: out std_logic_vector(TB_DEPTH-1 downto 0);
		DECODED_BITS_VALID: out std_logic;
			-- bulk.
		DECODED_BIT: out std_logic;
		DECODED_BIT_VALID: out std_logic;
			-- 1-bit serial

		--// MONITORING
		NORMALIZATION: out std_logic;
			-- 1-CLK pulse to indicate the rate at which normalization occurs (a function of SNR)
		SM_GROWTH_INV_RATE: out std_logic_vector(15 downto 0);
			-- interval between successive normalizations. Units: number of input coding groups. 
			-- = inverse state metrics growth rate
			-- Higher SNRs lead to longer intervals between normalizations.
			-- Read at rising edge of CLK when NORMALIZATION = '1'
		
		--// MONITORING (when BER_MEASUREMENT_EN = '1')
		BER_THRESHOLD: in std_logic_vector(23 downto 0);
			-- threshold for detecting OUT_OF_SYNC condition
			-- Note: at very low SNRs, spurious OUT_OF_SYNC and unreliable LOCK_STATUS may occur.
			-- In this case, it is recommended to either confirm the OUT_OF_SYNC condition over several BER measurement windows, or
			-- to increase the BER_WINDOW_LENGTH.
		BER: out std_logic_vector(23 downto 0);
			-- encoded stream bit error rate
		BER_VALID: out std_logic;
			-- read BER at rising edge of CLK when BER_VALID = '1'
		BIT_ERROR: out std_logic;
			-- 1 CLK-wide pulse for each detected bit error
			-- Helpful in understanding the bit error statistics (with an oscilloscope): bursty? or fairly uniformly distributed?
		IN_SYNC: out std_logic;
		OUT_OF_SYNC: out std_logic;
			-- 1 CLK-wide pulses every BER_WINDOW_LENGTH bits to indicate either synchronization (when BER is below threshold) or
			-- lack of synchronization (BER above threshold)

			-- OUT_OF_SYNC pulse indicates that the input coding groups are not aligned with the 
			-- transmitted coding groups, i.e. received bit in the G0 bin not actually generated by G0, etc.
			-- This information is typically used by external circuitry to realign the received encoded
			-- bit stream with the expected parity groups or puncturing pattern, or to correct for 
			-- PSK demodulator phase ambiguities, etc.
		LOCK_STATUS: out std_logic
			-- '1' when measured BER is below the set threshold.
		
    );
end entity;

architecture behavioral of VA is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
	COMPONENT VA_BMU2
	GENERIC(
		N_PARITY_BITS_MAX: integer;
		NBITS_BM: integer;
		HARD_SOFTN_DECISION: std_logic
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		HYPOTHESIS: in std_logic_vector((N_PARITY_BITS_MAX - 1) downto 0);
		DATAIN: in std_logic_vector((N_PARITY_BITS_MAX*4 - 1) downto 0);  	
		ERASURES: in std_logic_vector((N_PARITY_BITS_MAX - 1) downto 0);	
		INPUT_VALID : IN std_logic;          
		DISTANCE_OUT : OUT std_logic_vector((NBITS_BM-1) downto 0);
		OUTPUT_VALID : OUT std_logic
		);
	END COMPONENT;

	COMPONENT VA_ACS2
	GENERIC(
		NBITS_SM: integer;
		NBITS_BM: integer;
		SIMULATION: std_logic
	 );
	PORT(
		CLK: in std_logic;	
		SYNC_RESET: in std_logic;		
		INPUT_VALID: in std_logic;
		SM_A_IN: in std_logic_vector((NBITS_SM-1) downto 0);
		SM_B_IN: in std_logic_vector((NBITS_SM-1) downto 0);
		BM_A_IN: in std_logic_vector((NBITS_BM-1) downto 0);
		BM_B_IN: in std_logic_vector((NBITS_BM-1) downto 0);
		SELECT_B_AN: out std_logic;
		SM_C_OUT: out std_logic_vector((NBITS_SM-1) downto 0);	
		NORMALIZATION: out std_logic;
		OUTPUT_VALID: out std_logic
		);
	END COMPONENT;

	COMPONENT VA_TBU2
	GENERIC(
		K_MAX: integer;
		N_TRELLIS_STATES_MAX: integer;
		TB_DEPTH: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		SELECT_B_AN: in std_logic_vector((N_TRELLIS_STATES_MAX-1) downto 0);
		INPUT_VALID : IN std_logic;      
		K: in integer range 3 to 9;
		DECODED_BITS: out std_logic_vector(TB_DEPTH-1 downto 0);
		OUTPUT_VALID: out std_logic
		);
	END COMPONENT;
	
	COMPONENT BER3 is
	GENERIC (
		DATA_WIDTH: integer;
		BER_WINDOW: integer
	 );
	 PORT ( 
		CLK: in std_logic;	-- synchronous clock
		SYNC_RESET: in std_logic;		-- synchronous reset. active high.
		DATAIN_A: in std_logic_vector(DATA_WIDTH-1 downto 0);	
		MASK_A: in std_logic_vector(DATA_WIDTH-1 downto 0);	
		INPUT_VALID_A: in std_logic;
		DATAIN_B: in std_logic_vector(DATA_WIDTH-1 downto 0);	
		INPUT_VALID_B: in std_logic;
		BER: out std_logic_vector(31 downto 0);
		OUTPUT_VALID: out std_logic;
		BIT_ERROR: out std_logic
	);
	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 denotes a one-CLK delayed version of the net with the same name

constant NBITS_BM: integer := f_nbits_bm(N_PARITY_BITS_MAX, HARD_SOFTN_DECISION);
	-- branch metric precision. Trying to minimize decoder complexity in cases of hard-decision decoding
constant NBITS_SM: integer := f_nbits_sm(N_PARITY_BITS_MAX, K_MAX, HARD_SOFTN_DECISION);
	-- state metric precision. 1 extra bit added for modulo normalization technique.
constant N_BRANCHES_MAX: integer := 2**N_PARITY_BITS_MAX;
	-- maximum number of hypothesis to test for each new incoming coding group of N_PARITY_BITS_MAX
	-- The actual number of branches may be less depending on the code selected.
constant N_TRELLIS_STATES_MAX: integer := 2**(K_MAX-1);
	-- maximum number of trellis states. 
constant BER_WINDOW_LENGTH: integer := 1000;
	-- encoded stream BER measurement window length, expressed in number of encoded bits
	-- Maximum range 2^24-1. 
	-- Practically though, a threshold of 1000 is sufficient as we are trying to detect excessive BER (thus with many errors)

--//-- BMU --------------------------------------
type SLVNBITSBMxNBRANCHESMAXtype is array (integer range 0 to (N_BRANCHES_MAX-1)) of std_logic_vector((NBITS_BM-1) downto 0);
signal BMU: SLVNBITSBMxNBRANCHESMAXtype := (others => (others => '0'));
signal BMU_VALID: std_logic_vector((N_BRANCHES_MAX-1) downto 0) := (others => '0');

--//-- SMU --------------------------------------
type SLVNREGBITSxNTRELLISSTATEStype is array (integer range 0 to (N_TRELLIS_STATES_MAX-1)) of std_logic_vector((K_MAX-1) downto 0);
signal PREV_REG0: SLVNREGBITSxNTRELLISSTATEStype:= (others => (others => '0'));
signal PREV_REG1: SLVNREGBITSxNTRELLISSTATEStype:= (others => (others => '0'));
type INTxNTRELLISSTATEStype is array (integer range 0 to (N_TRELLIS_STATES_MAX-1)) of integer;
signal PARITYBITS0: INTxNTRELLISSTATEStype:= (others => 0);
signal PARITYBITS1: INTxNTRELLISSTATEStype:= (others => 0);
type SLVNBITSBMxNTRELLISSTATEStype is array (integer range 0 to (N_TRELLIS_STATES_MAX-1)) of std_logic_vector((NBITS_BM-1) downto 0);
signal BM_A: SLVNBITSBMxNTRELLISSTATEStype := (others => (others => '0'));
signal BM_B: SLVNBITSBMxNTRELLISSTATEStype := (others => (others => '0'));


type SLVNBITSSMxNTRELLISSTATEStype is array (integer range 0 to (N_TRELLIS_STATES_MAX-1)) of std_logic_vector((NBITS_SM-1) downto 0);
signal SM: SLVNBITSSMxNTRELLISSTATEStype:= (others => (others => '0'));
signal SM_VALID: std_logic_vector((N_TRELLIS_STATES_MAX-1) downto 0) := (others => '0');
signal SELECT_B_AN: std_logic_vector((N_TRELLIS_STATES_MAX-1) downto 0) := (others => '0');

--//-- MIN METRIC GROWTH --------------------------------------
signal FLAG_LOWER_QUARTER: std_logic := '0';
signal FLAG_LOWER_QUARTER_CNTR: std_logic_vector(3 downto 0) := (others => '0');
signal SM_VALID_D: std_logic := '0';
signal SM_GROWTH_CNTR: std_logic_vector(SM_GROWTH_INV_RATE'left downto 0) := (others => '0');
signal QUARTERS: std_logic_vector(3 downto 0) := (others => '0');
signal SM_MIN: std_logic_vector(NBITS_SM-1 downto 0) := (others => '0');	-- debug only
signal SM_MAX: std_logic_vector(NBITS_SM-1 downto 0) := (others => '0');	-- debug only

--//-- TBU --------------------------------------
signal DECODED_BITS_local: std_logic_vector(TB_DEPTH-1 downto 0) := (others => '0');
signal DECODED_BITS_VALID_local: std_logic := '0';

--//-- (OPTIONAL) BER MEASUREMENT --------------------------
signal DECODED_BITS2: std_logic_vector(TB_DEPTH-1 downto 0) := (others => '0');
signal SERIAL_CNTR: integer range 0 to TB_DEPTH-1;
signal SERIAL_BIT: std_logic := '0';
signal SERIAL_BIT_VALID: std_logic := '0';
signal ENCODER_REG: std_logic_vector((K_MAX-1) downto 0) := (others => '0');
signal SERIAL_BIT_X: std_logic_vector((K_MAX-1) downto 0) := (others => '0');
signal RECONSTRUCTED_PARITY_BITS: std_logic_vector(N_PARITY_BITS_MAX -1 downto 0) := (others => '0');
signal RECONSTRUCTED_PARITY_BITS_VALID: std_logic := '0'; 
signal BER3_DATAIN_A: std_logic_vector(N_PARITY_BITS_MAX -1 downto 0) := (others => '0');
signal BER_local: std_logic_vector(31 downto 0) := (others => '0');
signal BER_VALID_local: std_logic;

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



--//-- BMU --------------------------------------
-- Branch metric unit
-- branch metric computation: compute the local distance between 4 or 8 hypothesis and the received coding group.
-- There are 4 branches for N_PARITY_BITS = 2, 8 branches for  N_PARITY_BITS = 3, etc.
BMU_00x: for I in 0 to (N_BRANCHES_MAX-1) generate
	VA_BMU2_001: VA_BMU2 
	GENERIC MAP(
		N_PARITY_BITS_MAX => N_PARITY_BITS_MAX,
		NBITS_BM => NBITS_BM,
		HARD_SOFTN_DECISION => HARD_SOFTN_DECISION
	)
	PORT MAP(
		CLK => CLK,
		SYNC_RESET => SYNC_RESET,
		HYPOTHESIS => conv_std_logic_vector(I,N_PARITY_BITS_MAX),
		DATAIN => DATAIN,
		ERASURES => ERASURES,
		INPUT_VALID => INPUT_VALID,
		DISTANCE_OUT => BMU(I),
		OUTPUT_VALID => BMU_VALID(I)	-- note: all BMU output simultaneous. Use BMU_VALID(0) only
	);
end generate;

--//-- SMU --------------------------------------
-- State metrics unit

ACS_00X: for I in 0 to (N_TRELLIS_STATES_MAX-1) generate
	-- identify two previous registers leading to state I
	PREV_REG0(I) <= f_previous_reg(K_MAX, I, '0');
	PREV_REG1(I) <= f_previous_reg(K_MAX, I, '1');
	-- compute the expected parity bits for each previous registers
	PARITYBITS0(I) <= conv_integer(f_parity_gen(N_PARITY_BITS_MAX, K_MAX, PREV_REG0(I), G, SPECIAL));
	PARITYBITS1(I) <= conv_integer(f_parity_gen(N_PARITY_BITS_MAX, K_MAX, PREV_REG1(I), G, SPECIAL));
	-- look-up branch metric for each transition
	BM_A(I) <= BMU(PARITYBITS0(I));
	BM_B(I) <= BMU(PARITYBITS1(I));

	VA_ACS2_001: VA_ACS2 
	GENERIC MAP(
		NBITS_SM => NBITS_SM,
		NBITS_BM => NBITS_BM,
		SIMULATION => SIMULATION
	)
	PORT MAP(
		CLK => CLK,
		SYNC_RESET => SYNC_RESET,
		INPUT_VALID => BMU_VALID(0),
		SM_A_IN => SM(f_previous_state(K_MAX, I, '0')),  -- state metrics for the two previous states leading to state I 
		SM_B_IN => SM(f_previous_state(K_MAX, I, '1')),
 		BM_A_IN => BM_A(I),	-- branch metrics for this trellis state, if '0' or '1' is the unencoded input bit
		BM_B_IN => BM_B(I),
		SELECT_B_AN => SELECT_B_AN(I),
		SM_C_OUT => SM(I),
		NORMALIZATION => open,
		OUTPUT_VALID => SM_VALID(I)	-- note: all state metrics updated simultaneous
	);
end generate;

-- simulation debug
-- make sure the instantaneous SM dynamic range is less than 1/2 the maximum dynamic range (given the number of precision bits BITS_SM)
-- In other words, verify that there are enough BITS_SM for the normalization algorithm to work properly.
-- The state metrics should not use more than 2 quarters at any given time.
DEBUG_001: if(SIMULATION = '1') generate
	process(SM)
		variable QUARTER0: std_logic;
		variable QUARTER1: std_logic;
		variable QUARTER2: std_logic;
		variable QUARTER3: std_logic;
		begin
			QUARTER0 := '0';
			QUARTER1 := '0';
			QUARTER2 := '0';
			QUARTER3 := '0';
			for I in 0 to N_TRELLIS_STATES_MAX-1 loop
				case (SM(I)(NBITS_SM-1 downto NBITS_SM-2)) is
					when "00" => QUARTER0 := '1';
					when "01" => QUARTER1 := '1';
					when "10" => QUARTER2 := '1';
					when others => QUARTER3 := '1';
				end case;
			end loop;
			QUARTERS <= QUARTER3 & QUARTER2 & QUARTER1 & QUARTER0;
	end process;
end generate;


--//-- MIN METRIC GROWTH --------------------------------------
-- Analyze the rate of growth of the min metric. Related to the SNR.
-- Trick: look whether there are any SM values in the lower quarter. 
-- As the metrics grow, the pack of state metrics will enter and leave the lower quarter at
-- a rate related to the SNR.

SM_GROWTH_RATE_001: process(CLK)
variable ORMSB0: std_logic := '0';
variable ORMSB1: std_logic := '0';
begin
	if rising_edge(CLK) then
		ORMSB0 := '0'; 
		ORMSB1 := '0'; 
		for I in 0 to N_TRELLIS_STATES_MAX -1 loop
			ORMSB0 := ORMSB0 or SM(I)(NBITS_SM-1);
			ORMSB1 := ORMSB1 or SM(I)(NBITS_SM-2);
		end loop;
		if(SM_VALID(0) = '1') then
			FLAG_LOWER_QUARTER <= not(ORMSB0 or ORMSB1);
		end if;
	end if;
end process;

-- Distance between two 1->0 transitions for FLAG_LOWER_QUARTER  (falling edge is clean, unlike rising edge which can toggle)
SM_GROWTH_RATE_002: process(CLK)
begin
	if rising_edge(CLK) then
		SM_VALID_D <= SM_VALID(0);

		if(SYNC_RESET = '1') then
			SM_GROWTH_CNTR <= (others => '0');
			SM_GROWTH_INV_RATE <= (others => '0');
			FLAG_LOWER_QUARTER_CNTR <= (others => '0');
			NORMALIZATION <= '0';
		elsif(SM_VALID_D = '1') then
			-- count the number of successive 1's (as a way to confirm)
			if(FLAG_LOWER_QUARTER = '0') then
				FLAG_LOWER_QUARTER_CNTR <= (others => '0');
			elsif(FLAG_LOWER_QUARTER = '1') and (FLAG_LOWER_QUARTER_CNTR(3) = '0') then
				FLAG_LOWER_QUARTER_CNTR <= FLAG_LOWER_QUARTER_CNTR + 1;
			end if;
			
			if(FLAG_LOWER_QUARTER_CNTR(3) = '1') and (FLAG_LOWER_QUARTER = '0') then
				-- falling edge. Reset interval counter
				SM_GROWTH_CNTR <= (others => '0');
				-- release measured interval
				SM_GROWTH_INV_RATE <= SM_GROWTH_CNTR;
				-- or equivalently, report state metrics normalization 
				NORMALIZATION <= '1';
			elsif(SM_GROWTH_CNTR(SM_GROWTH_CNTR'left) = '0') then
				SM_GROWTH_CNTR <= SM_GROWTH_CNTR + 1;
				NORMALIZATION <= '0';
			end if;
		else
			NORMALIZATION <= '0';
		end if;
	end if;
end process;

-- simulation debug
-- find the min/max state metrics and plot them
DEBUG_002: if(SIMULATION = '1') generate
	process(SM)
		variable SM_MIN_var: std_logic_vector(NBITS_SM-1 downto 0);
		variable SM_MAX_var: std_logic_vector(NBITS_SM-1 downto 0);
		begin
			SM_MIN_var := SM(0);
			SM_MAX_var := SM(0);
			for I in 1 to N_TRELLIS_STATES_MAX-1 loop
				-- search min 
				if(SM(I)(NBITS_SM-1 downto NBITS_SM-2) = "00") and (SM_MIN_var(NBITS_SM-1 downto NBITS_SM-2) = "11") then
					-- no change. SM_MIN is still min
				elsif(SM(I)(NBITS_SM-1 downto NBITS_SM-2) = "11") and (SM_MIN_var(NBITS_SM-1 downto NBITS_SM-2) = "00") then
					-- peculiarity of the normalization algorithm. Any metric in the 4rd quarter must be considered
					-- as less than any metric in the 1st quarter)
					-- New min
					SM_MIN_var := SM(I);
				elsif(SM(I) < SM_MIN_var) then
					-- New min
					SM_MIN_var := SM(I);
				end if;

				-- search max 
				if(SM(I)(NBITS_SM-1 downto NBITS_SM-2) = "11") and (SM_MAX_var(NBITS_SM-1 downto NBITS_SM-2) = "00") then
					-- no change. SM_MAX is still max
				elsif(SM(I)(NBITS_SM-1 downto NBITS_SM-2) = "00") and (SM_MAX_var(NBITS_SM-1 downto NBITS_SM-2) = "11") then
					-- peculiarity of the normalization algorithm. Any metric in the 4rd quarter must be considered
					-- as less than any metric in the 1st quarter)
					-- New max
					SM_MAX_var := SM(I);
				elsif(SM(I) > SM_MAX_var) then
					-- New max
					SM_MAX_var := SM(I);
				end if;

			end loop;
			SM_MIN <= SM_MIN_var;
			SM_MAX <= SM_MAX_var;
	end process;
end generate;


--//-- TBU --------------------------------------
-- Traceback unit

VA_TBU2_001: VA_TBU2 
GENERIC MAP(
	K_MAX => K_MAX,
	N_TRELLIS_STATES_MAX => N_TRELLIS_STATES_MAX,
	TB_DEPTH => TB_DEPTH
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	SELECT_B_AN => SELECT_B_AN,
	INPUT_VALID => SM_VALID(0),
	K => K,
	DECODED_BITS => DECODED_BITS_local,
	OUTPUT_VALID => DECODED_BITS_VALID_local
);

DECODED_BITS <= DECODED_BITS_local;
DECODED_BITS_VALID <= DECODED_BITS_VALID_local;

--//-- CONVERSION TO 1-BIT SERIAL --------------------------
-- Serialize the decoded bit stream
SERIAL_OUT: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SERIAL_BIT_VALID <= '0';
		elsif(DECODED_BITS_VALID_local = '1') then
			DECODED_BITS2 <= DECODED_BITS_local;
			SERIAL_CNTR <= TB_DEPTH-1;	-- counts down from TB_DEPTH -1 to 0
			SERIAL_BIT_VALID <= '1';
		elsif(SERIAL_CNTR /= 0) then
			SERIAL_CNTR <= SERIAL_CNTR -1;
			DECODED_BITS2(TB_DEPTH-2 downto 0) <= DECODED_BITS2(TB_DEPTH-1 downto 1);
		else
			SERIAL_BIT_VALID <= '0';
		end if;
	end if;
end process;
SERIAL_BIT <= DECODED_BITS2(0);

-- to output
DECODED_BIT <= SERIAL_BIT;
DECODED_BIT_VALID <= SERIAL_BIT_VALID;

--//-- (OPTIONAL) BER MEASUREMENT --------------------------
-- re-encode the decoded bits and compare with incoming data stream for BER
BER_MEASUREMENT_001: if(BER_MEASUREMENT_EN = '1') generate
	-- reconstruct the parity bits by re-encoding the decoded bits
	ENCODE_001: process(CLK)
	begin
		if rising_edge(CLK) then
			RECONSTRUCTED_PARITY_BITS_VALID <= SERIAL_BIT_VALID;
			
			if(SYNC_RESET = '1') then
				ENCODER_REG <= (others => '0');
			elsif(SERIAL_BIT_VALID = '1') then
				-- shift unencoded bits left to right
--				ENCODER_REG((K-2) downto 0) <= ENCODER_REG((K-1) downto 1);
--				ENCODER_REG(K-1) <= SERIAL_BIT;
				ENCODER_REG <= ('0' & ENCODER_REG(ENCODER_REG'left downto 1)) or SERIAL_BIT_X;
			end if;
		end if;
	end process;
	
	SERIAL_BIT_X_GEN: process(SERIAL_BIT)
	begin
		for I in 0 to SERIAL_BIT_X'left loop
			if(I = (K-1)) then
				SERIAL_BIT_X(I) <= SERIAL_BIT;
			else
				SERIAL_BIT_X(I) <= '0';
			end if;
		end loop;
	end process;
			
	
	
	RECONSTRUCTED_PARITY_BITS <= f_parity_gen(N_PARITY_BITS_MAX, K_MAX, ENCODER_REG,G, SPECIAL);
	
	-- received encoded stream (by groups of N_PARITY_BITS)
	-- hard-decision
	process(DATAIN)
	begin
	for I in 0 to (N_PARITY_BITS_MAX-1) loop
		BER3_DATAIN_A(I) <= DATAIN(I*4 +3);
	end loop;
	end process;
	
	BER3_001: BER3 
	GENERIC MAP(
		DATA_WIDTH => N_PARITY_BITS_MAX,
		BER_WINDOW => BER_WINDOW_LENGTH
	)
	PORT MAP(
		CLK => CLK,
		SYNC_RESET => SYNC_RESET,
		DATAIN_A => BER3_DATAIN_A,
		MASK_A => ERASURES,
		INPUT_VALID_A => INPUT_VALID,
		DATAIN_B => RECONSTRUCTED_PARITY_BITS,
		INPUT_VALID_B => RECONSTRUCTED_PARITY_BITS_VALID,
		BER => BER_local,
		OUTPUT_VALID => BER_VALID_local,
		BIT_ERROR => BIT_ERROR
	);
	
	-- to outputs
	BER <= BER_local(23 downto 0);
	BER_VALID <= BER_VALID_local;

--//-- (OPTIONAL) OUT OF SYNC DETECTION --------------------------
-- 1 CLK-wide pulse to indicate that the input coding groups are not aligned with the 
-- transmitted coding groups, i.e. received bit in the G0 bin not actually generated by G0, etc.
-- This information is typically used by external circuitry to realign the received encoded
-- bit stream with the expected parity groups or puncturing pattern, or to correct for 
-- PSK demodulator phase ambiguities, etc.

-- requires the generic parameter BER_MEASUREMENT_EN = '1'

-- Algorithm: generate a 1 CLK-wide pulse when the BER exceeds the BER_THRESHOLD.
	OUT_OF_SYNC_GEN: process(CLK)
	begin
		if rising_edge(CLK) then
			if(SYNC_RESET = '1') then
				OUT_OF_SYNC <= '0';
				IN_SYNC <= '0';
				LOCK_STATUS <= '0';
			elsif(BER_VALID_local = '1') then
				if (BER_local(23 downto 0) > BER_THRESHOLD) then
					OUT_OF_SYNC <= '1';
					LOCK_STATUS <= '0';
				else
					IN_SYNC <= '1';
					LOCK_STATUS <= '1';
				end if;
			else
				IN_SYNC <= '0';
				OUT_OF_SYNC <= '0';
			end if;
		end if;
	end process;
end generate;

--// SIMULATION -----------------------------------------
-- Send to a tab-delimited text file for subsequent (Matlab) processing/plot.
SIM2OUTFILE_X: if(SIMULATION = '1') generate

	SIM2OUTFILE1: SIM2OUTFILE 
	generic map(
		FILENAME => "va1.txt"
	)
	PORT MAP(
		CLK => CLK,
		DATA1 => "00000000000" & SERIAL_BIT,
		DATA2 => x"000",
		DATA3 => x"000",
		SAMPLE_CLK_IN => SERIAL_BIT_VALID
	);

	SIM2OUTFILE2: SIM2OUTFILE 
	generic map(
		FILENAME => "va2.txt"
	)
	PORT MAP(
		CLK => CLK,
		DATA1 => ext(SM_MIN,12),
		DATA2 => ext(SM_MAX,12),
		DATA3 => ext(SM(0),12),
		SAMPLE_CLK_IN => SM_VALID(0)
	);
end generate;

end behavioral;					
