-------------------------------------------------------------
-- MSS copyright 2012-2013
-- Filename:  VITERBI_DECODER.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
-- Version: Rev 5_TDRSS BRANCH
-- Last modified: 4/1/13

-- Inheritance: 	N/A
--
-- description:  Top level for high-speed Viterbi decoder.
-- 
-- Usage:
-- All key configuration parameters to be defined at synthesis time are in the com1509pkg.vhd package 
-- (to ensure consistency among all components)
--
-- Rev 2 11/30/12 AZ
-- Extended reset pulse (required) within.
-- Passed parameters through generic for more configuration flexibility instead of common com1509pkg
--
-- Rev 3 12/9/12 AZ
-- Adjusted out-of-lock threshold for R=1/2, for operation down to 0 dB Eb/No
--
-- Rev 4 12/13/12 AZ
-- Added input for external re-alignment control
-- Two options for out-of-lock threshold: low SNR or higher SNR. see BER_THRESHOLD0 constant. 
--
-- Rev 5 4/1/13 AZ
-- added adaptive BER theshold based on the measured SNR
-- for lower false lock detection at high SNR and lower false loss of lock detection at low SNR
--
-- Rev 5 TDRSS BRANCH 12/16/14
-- programmable G1 inversion
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity VITERBI_DECODER is
	generic(
		-- USER-DEFINED CONFIGURATION (PRIOR TO SYNTHESIS)
		-- The generic section describes the decoder maximum capabilities (thus determining the hardware resources)
		K_MAX: integer:= 7;	
		-- maximum constraint length. Select 3,5,7, or 9
		N_PARITY_BITS_MAX: integer:= 2;
		-- 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
		NDEC: integer := 1;  
		-- number of parallel FEC decoders
		-- Valid range: 1,2,4,8
		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.
		TB_DEPTH: integer := 84;	
		-- traceback depth. MUST BE LESS THAN 120. Typically := 6*K_MAX for non-punctured codes or 12*K_MAX for punctured codes.
		SIMULATION: std_logic := '0'
			-- enable optional simulation features
	);
    Port ( 
		-- GLOBAL CLOCKS, RESET
		CLK: in std_logic;	-- synchronous clock. Must be a global clock constrained in the project constraint file.
		SYNC_RESET: in std_logic;		-- synchronous reset. active high.
			-- MANDATORY to initialize internal variables
		
		-- 8 PARALLEL INPUT BITS
		-- All input samples are soft-quantized with 4 bits
		-- format:offset binary (0000 for strong '0', 1111 for strong '1')
		-- When using hard-decision decoding, input bits should be placed at indices 3+i*4.
		-- MSb is first (DATAIN(31 downto 28) is the first received soft-decision sample)
		DATAIN: in std_logic_vector(31 downto 0);
		DATAIN_VALID: in std_logic;
		DATAIN_READY: out std_logic;
		
		--// CONTROL
		-- Force a resynchronization if a higher/more reliable authority detects lack of lock. For example HDLC deframing.
		SKIP1BIT_PULSE: in std_logic;
			-- Skip one encoded input bit for each 1 CLK-wide pulse
			-- This feature is used to synchronize the input stream with the periodic encoding/puncturing pattern

		--// 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 K_MAX;
			-- 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
		R1: in integer range 1 to 7;
		R2: in integer range 2 to 8;
			-- Convolutional encoding rate R1/R2
			-- Valid rates: 1/2, 1/3, 1/7, 2/3, 3/4, 5/6, 7/8
		STANDARD: in integer range 0 to 2;
			-- There are some minor variations among standards, such as different puncturing scheme or inversion
			-- 0 = Intelsat 
			-- 1 = CCSDS
			-- 2 = DVB rate 1/2 only
			-- others = yet TBD
		DIFF_ENC_ON: in std_logic;
			-- input stream is differentially encoded (1) or not (0)
		HIGH_SNR: in std_logic;
			-- it is sometimes helpful to adjust in-lock/out-of-lock detection thresholds to trade-off
			-- probability of missed out of lock and probability of false detection
        FEC_G1_INVERSION: in std_logic;
            -- SPECIAL TDRSS BRANCH
		
		-- DECODER OUTPUTS
		-- 2 output types: 1-bit serial or 8-bit parallel.
		DATAOUT1b: out std_logic;
		DATAOUT1b_VALID: out std_logic;
			-- 1-bit serial
		DATAOUT8b: out std_logic_vector(7 downto 0);
		DATAOUT8b_VALID: out std_logic;
			-- 8-bit parallel, order: MSb first.
		
		--// MONITORING (when BER_MEASUREMENT_EN = '1')
		-- BER_WINDOW_LENGTH and BER_THRESHOLD are defined in com1509pkg
		-- 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
			-- a continuous '1' when measured BER is below the set threshold.
			-- toggling when unlocked
		
		
		);
end entity;

architecture Behavioral of VITERBI_DECODER is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
--	COMPONENT SIGNAL_NOISE_RATIO
--	PORT(
--		CLK : IN std_logic;
--		SYNC_RESET : IN std_logic;
--		SAMPLE_CLK_IN : IN std_logic;
--		DATA_IN : IN std_logic_vector(3 downto 0);          
--		SNR : OUT std_logic_vector(7 downto 0)
--		);
--	END COMPONENT;

	COMPONENT DATA_SPLITTER
	GENERIC(
		NDEC: integer;
		N_PARITY_BITS_MAX: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		DATAIN: in std_logic_vector(31 downto 0);
		DATAIN_VALID : IN std_logic;          
		DATAIN_READY : OUT std_logic;
		SKIP1BIT_PULSE: in std_logic;
		N_PARITY_BITS: in integer range 2 to 7;
		R1: in integer range 1 to 7;
		R2: in integer range 2 to 8;
		STANDARD: in integer range 0 to 2;
		DATAOUT: out std_logic_vector((NDEC*N_PARITY_BITS_MAX*4 - 1) downto 0); 
		ERASURES: out std_logic_vector((NDEC*N_PARITY_BITS_MAX - 1) downto 0); 
		DATAOUT_VALID: out std_logic_vector(NDEC-1 downto 0)
		);
	END COMPONENT;

	COMPONENT DATA_SPLITTER1
	GENERIC(
		N_PARITY_BITS_MAX: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		DATAIN : IN std_logic_vector(31 downto 0);
		DATAIN_VALID : IN std_logic;
		SKIP1BIT_PULSE: in std_logic;
		N_PARITY_BITS: in integer range 2 to 7;
		R1: in integer range 1 to 7;
		R2: in integer range 2 to 8;
		STANDARD: in integer range 0 to 2;
		DATAIN_READY : OUT std_logic;
		DATAOUT: out std_logic_vector(((N_PARITY_BITS_MAX * 4) -1) downto 0); 
		ERASURES: out std_logic_vector((N_PARITY_BITS_MAX-1) downto 0);
		DATAOUT_VALID : OUT std_logic
		);
	END COMPONENT;

	COMPONENT VA
	GENERIC(
		BER_MEASUREMENT_EN: std_logic;
		N_PARITY_BITS_MAX: integer;
		K_MAX: integer;	
		TB_DEPTH: integer;	
		HARD_SOFTN_DECISION: std_logic;
		SIMULATION: std_logic
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		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;      
		G: in std_logic_vector(62 downto 0);  
		N_PARITY_BITS: in integer range 2 to 7;
		K: in integer range 3 to K_MAX;
		SPECIAL: in integer;
		DECODED_BITS: out std_logic_vector(TB_DEPTH-1 downto 0);
		DECODED_BITS_VALID: out std_logic;
		DECODED_BIT: out std_logic;
		DECODED_BIT_VALID: out std_logic;
		NORMALIZATION: out std_logic;
		SM_GROWTH_INV_RATE: out std_logic_vector(15 downto 0);
		BER_THRESHOLD: in std_logic_vector(23 downto 0);
		BER: out std_logic_vector(23 downto 0);
		BER_VALID: out std_logic;
		BIT_ERROR: out std_logic;
		IN_SYNC: out std_logic;
		OUT_OF_SYNC: out std_logic;
		LOCK_STATUS: 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

--constant BER_THRESHOLD0_LOW: integer := 320;	
	-- threshold for a decoded BER of 0.16 approx. The problem with setting this threshold so high is that 
	-- the decoder may exhibit false locks at high SNR. 
constant BER_THRESHOLD0_HIGH: integer := 246;	
	-- threshold for no false lock at high SNR (but may cause out of lock condition at low SNRs)
--constant BER_THRESHOLD0_LOW: integer := 288;	
--	-- testing at 4.7 dB Eb/No threshold (K=7,R=1/2)
constant BER_THRESHOLD0_LOW: integer := 298;	
	-- lower than 4.7 dB Eb/No threshold (K=7,R=1/2)

-- Looks like the thresholds are related to R:   BER_THRESHOLD0*(1/R - 1)
constant BER_THRESHOLD_13_LOW: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_LOW*2, 24);	
constant BER_THRESHOLD_12_LOW: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_LOW, 24);	
constant BER_THRESHOLD_23_LOW: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_LOW/2, 24);	
constant BER_THRESHOLD_34_LOW: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_LOW/3, 24);	
constant BER_THRESHOLD_56_LOW: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_LOW/5, 24);	
constant BER_THRESHOLD_78_LOW: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_LOW/7, 24);	
constant BER_THRESHOLD_13_HIGH: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_HIGH*2, 24);	
constant BER_THRESHOLD_12_HIGH: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_HIGH, 24);	
constant BER_THRESHOLD_23_HIGH: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_HIGH/2, 24);	
constant BER_THRESHOLD_34_HIGH: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_HIGH/3, 24);	
constant BER_THRESHOLD_56_HIGH: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_HIGH/5, 24);	
constant BER_THRESHOLD_78_HIGH: std_logic_vector(23 downto 0) := conv_std_logic_vector(BER_THRESHOLD0_HIGH/7, 24);	
	-- BER threshold expressed as number of bit errors in a BER_WINDOW_LENGTH = 1000 bits window.
	-- When BER below this threshold, the decoder is considered locked. 
	-- BER above this threshold triggers an OUT_OF_SYNC output pulse.
	-- IMPORTANT: THESE THRESHOLDS ARE COMPUTED FOR A TRACEBACK LENGTH OF 96. 
signal BER_THRESHOLD_13: std_logic_vector(23 downto 0) := BER_THRESHOLD_13_LOW;
signal BER_THRESHOLD_12: std_logic_vector(23 downto 0) := BER_THRESHOLD_12_LOW;	
signal BER_THRESHOLD_23: std_logic_vector(23 downto 0) := BER_THRESHOLD_23_LOW;	
signal BER_THRESHOLD_34: std_logic_vector(23 downto 0) := BER_THRESHOLD_34_LOW;	
signal BER_THRESHOLD_56: std_logic_vector(23 downto 0) := BER_THRESHOLD_56_LOW;	
signal BER_THRESHOLD_78: std_logic_vector(23 downto 0) := BER_THRESHOLD_78_LOW;	

-- actual measurements
--constant BER_THRESHOLD_13: std_logic_vector(23 downto 0) := conv_std_logic_vector(626, 24);	-- tested down to 0dB Eb/No, K=9, TB_DEPTH=96
--constant BER_THRESHOLD_12: std_logic_vector(23 downto 0) := conv_std_logic_vector(320, 24);	-- tested down to 0dB Eb/No, K=7, TB_DEPTH=96
--constant BER_THRESHOLD_23: std_logic_vector(23 downto 0) := conv_std_logic_vector(160, 24);	-- tested down to 1dB Eb/No, K=7, TB_DEPTH=96
--constant BER_THRESHOLD_34: std_logic_vector(23 downto 0) := conv_std_logic_vector(115, 24);	-- tested down to 1.4dB Eb/No, K=7, TB_DEPTH=96
--constant BER_THRESHOLD_56: std_logic_vector(23 downto 0) := conv_std_logic_vector(80, 24);	-- tested down to 2.2dB Eb/No, K=7, TB_DEPTH=96
--constant BER_THRESHOLD_78: std_logic_vector(23 downto 0) := conv_std_logic_vector(62, 24);	-- tested down to 3.0dB Eb/No, K=7, TB_DEPTH=96

signal EXTENDED_SYNC_RESET: std_logic := '0';
signal SYNC_RESET_CNTR: integer range 0 to 15 := 0;

--// SNR MEASUREMENT --------------------------------
signal SNR: std_logic_vector(7 downto 0) := (others => '0');
signal DATAIN_D: std_logic_vector(31 downto 0) := (others => '0');
signal DATAIN_CNTR: integer range 0 to 7 := 0;
signal SAMPLE_CLK_IN_D: std_logic := '0';

--//-- DATA SPLITTER --------------------------------
signal DS_DATAOUT: std_logic_vector((NDEC*N_PARITY_BITS_MAX*4 - 1) downto 0) := (others => '0');  
signal DS_DATAOUT2: std_logic_vector((NDEC*N_PARITY_BITS_MAX*4 - 1) downto 0) := (others => '0');  
signal DS_ERASURES: std_logic_vector((NDEC*N_PARITY_BITS_MAX - 1) downto 0):= (others => '0'); 
signal DS_DATAOUT_VALID: std_logic_vector(NDEC-1 downto 0) := (others => '0');

--//-- PARALLEL VITERBI ALGORITHMS --------------------------------
signal BER_THRESHOLD: std_logic_vector(23 downto 0) := (others => '0');
type slvTBDEPTHxNDEC is array (integer range 0 to NDEC-1) of std_logic_vector((TB_DEPTH-1) downto 0);	
signal DECODED_BITS: slvTBDEPTHxNDEC := (others => (others => '0'));
signal DECODED_BITS_VALID: std_logic_vector(NDEC-1 downto 0) := (others => '0');
signal DECODED_BIT: std_logic := '0';
signal DECODED_BIT_VALID: std_logic := '0';
signal OUT_OF_SYNC_local: std_logic := '0';
signal SKIP1BIT_PULSE_local: std_logic := '0';
signal SPECIAL: integer range 0 to 1 := 0;

--//-- OUTPUT --------------------------------------------
signal REMAINDER: integer range 0 to 7 := 0;
signal OUTBUF: std_logic_vector(TB_DEPTH + 7 downto 0) := (others => '0');
signal WPTR: std_logic_vector(6 downto 0) := (others => '0');
signal DATAOUT1b1: std_logic := '0';
signal DATAOUT1b1_D: std_logic := '0';
signal DATAOUT1b1_VALID: std_logic := '0';
signal DATAOUT8b1: std_logic_vector(7 downto 0) := (others => '0');
signal DATAOUT8b1_D_LSB: std_logic := '0';
signal DATAOUT8b1_VALID: std_logic := '0';

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

-- extend reset pulse
EXTEND_RESET_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			SYNC_RESET_CNTR <= 15;
			EXTENDED_SYNC_RESET <= '1';
		elsif(SYNC_RESET_CNTR /= 0) then
			SYNC_RESET_CNTR <= SYNC_RESET_CNTR - 1;
		else
			EXTENDED_SYNC_RESET <= '0';
		end if;	
	end if;
end process;

----// SNR MEASUREMENT --------------------------------
---- Measure SNR by calculating the standard deviation of the 4-bit normalized and soft-quantized
---- demodulated samples.
--LATCH_INPUT_DATA: process(CLK)
--begin
--	if rising_edge(CLK) then
--		if(SYNC_RESET = '1') then
--			SAMPLE_CLK_IN_D <= '0';
--			DATAIN_CNTR <= 0;
--		elsif(DATAIN_VALID = '1') then
--			DATAIN_D <= DATAIN;	-- latch input
--			SAMPLE_CLK_IN_D <= '1';
--			DATAIN_CNTR <= 7;
--		elsif(DATAIN_CNTR /= 0) then
--			-- if there is enough separation between input samples, refine SNR measurement by using up to 7 other samples
--			DATAIN_D(27 downto 0) <= DATAIN_D(31 downto 4);	-- shift 4-bit right
--			DATAIN_CNTR <= DATAIN_CNTR - 1;
--			SAMPLE_CLK_IN_D <= '1';
--		else
--			SAMPLE_CLK_IN_D <= '0';
--		end if;
--	end if;
--end process;
--
--SNR_001: SIGNAL_NOISE_RATIO PORT MAP(
--	CLK => CLK,
--	SYNC_RESET => SYNC_RESET,
--	SAMPLE_CLK_IN => SAMPLE_CLK_IN_D,
--	DATA_IN => DATAIN_D(3 downto 0),
--	SNR => SNR	-- 1/SNR, 0 to signify noiseless demodulated samples
--);

-- adaptive threshold selection, depending on the estimated SNR
ADAPTIVE_THRESHOLD_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(HIGH_SNR = '1') then
		--if(SNR < x"20") then
			-- high SNR
			BER_THRESHOLD_13 <= BER_THRESHOLD_13_HIGH;
			BER_THRESHOLD_12 <= BER_THRESHOLD_12_HIGH;
			BER_THRESHOLD_23 <= BER_THRESHOLD_23_HIGH;
			BER_THRESHOLD_34 <= BER_THRESHOLD_34_HIGH;
			BER_THRESHOLD_56 <= BER_THRESHOLD_56_HIGH;
			BER_THRESHOLD_78 <= BER_THRESHOLD_78_HIGH;
		else
			-- low SNR
			BER_THRESHOLD_13 <= BER_THRESHOLD_13_LOW;
			BER_THRESHOLD_12 <= BER_THRESHOLD_12_LOW;
			BER_THRESHOLD_23 <= BER_THRESHOLD_23_LOW;
			BER_THRESHOLD_34 <= BER_THRESHOLD_34_LOW;
			BER_THRESHOLD_56 <= BER_THRESHOLD_56_LOW;
			BER_THRESHOLD_78 <= BER_THRESHOLD_78_LOW;
		end if;
	end if;
end process;

--//-- DATA SPLITTER --------------------------------
-- resynchronize decoder based on local out of sync detection or external command
SKIP1BIT_PULSE_local <= SKIP1BIT_PULSE or OUT_OF_SYNC_local;

SERIAL_DECODING: if(NDEC = 1) generate
	-- continuous serial decoder case:
	-- Re-insert erasures at places where the encoder puncturing occurred.
	-- Finally, group received bits and erasures into groups of N_PARITY_BITS bits.
	DATA_SPLITTER_001: DATA_SPLITTER1 
	GENERIC MAP(
		N_PARITY_BITS_MAX => N_PARITY_BITS_MAX
	)
	PORT MAP(
		CLK => CLK,
		SYNC_RESET => EXTENDED_SYNC_RESET,
		DATAIN => DATAIN,
		DATAIN_VALID => DATAIN_VALID,
		DATAIN_READY => DATAIN_READY,
		SKIP1BIT_PULSE => SKIP1BIT_PULSE_local,
		N_PARITY_BITS => N_PARITY_BITS,
		R1 => R1,
		R2 => R2,
		STANDARD => STANDARD,
		DATAOUT => DS_DATAOUT((N_PARITY_BITS_MAX*4 - 1) downto 0),
		ERASURES => DS_ERASURES(N_PARITY_BITS_MAX-1 downto 0),
		DATAOUT_VALID => DS_DATAOUT_VALID(0)
	);
	
end generate;

PARALLEL_DECODING: if(NDEC > 1) generate
	-- Parallel decoders case:
	-- Segments the high-speed input stream into fixed length blocks for parallel processing by NDEC parallel decoders.
	-- Also re-insert erasures at places where the encoder puncturing occurred.
	-- Finally, group received bits and erasures into groups of N_PARITY_BITS bits.
	DATA_SPLITTER_001: DATA_SPLITTER 
	GENERIC MAP(
		NDEC => NDEC,
		N_PARITY_BITS_MAX => N_PARITY_BITS_MAX
	)
	PORT MAP(
		CLK => CLK,
		SYNC_RESET => EXTENDED_SYNC_RESET,
		DATAIN => DATAIN,
		DATAIN_VALID => DATAIN_VALID,
		DATAIN_READY => DATAIN_READY,
		SKIP1BIT_PULSE => SKIP1BIT_PULSE_local,
		N_PARITY_BITS => N_PARITY_BITS,
		R1 => R1,
		R2 => R2,
		STANDARD => STANDARD,
		DATAOUT => DS_DATAOUT,
		ERASURES => DS_ERASURES,
		DATAOUT_VALID => DS_DATAOUT_VALID
	);
end generate;

--//-- PARALLEL VITERBI ALGORITHMS --------------------------------
-- 1st parallel decoder is 'special' because it include BER measurement
-- and possibly simulation traces

-- set BER threshold
BER_THRESHOLD_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(R1 = 1) and (R2 = 3) then
			BER_THRESHOLD <= BER_THRESHOLD_13;
		elsif(R1 = 1) and (R2 = 2) then
			BER_THRESHOLD <= BER_THRESHOLD_12;
		elsif(R1 = 2) and (R2 = 3) then
			BER_THRESHOLD <= BER_THRESHOLD_23;
		elsif(R1 = 3) and (R2 = 4) then
			BER_THRESHOLD <= BER_THRESHOLD_34;
		elsif(R1 = 5) and (R2 = 6) then
			BER_THRESHOLD <= BER_THRESHOLD_56;
		elsif(R1 = 7) and (R2 = 8) then
			BER_THRESHOLD <= BER_THRESHOLD_78;
		else
			BER_THRESHOLD <= BER_THRESHOLD_12;
		end if;
	end if;
end process;


-- special case: CCSDS K=7 R=1/2 encoder inverts the G1 output parity bit.
--SPECIAL <= 1 when (K=7) and (R1=1) and (R2=2) and (STANDARD = 1) else 0;
SPECIAL <= 1 when (FEC_G1_INVERSION = '1') else 0;
-- alternative code (untested)
--DS_DATAOUT2(3 downto 0) <= DS_DATAOUT(3 downto 0);
--DS_DATAOUT2(7 downto 4) <= (not DS_DATAOUT(7 downto 4)) when (K=7) and (R1=1) and (R2=2) and (STANDARD = 1) else  DS_DATAOUT(7 downto 4);
--DS_DATAOUT2(3 downto 0) <= DS_DATAOUT(3 downto 0);



VA_000: VA 
GENERIC MAP(
	N_PARITY_BITS_MAX => N_PARITY_BITS_MAX,
	K_MAX => K_MAX,
	TB_DEPTH => TB_DEPTH,
	HARD_SOFTN_DECISION => HARD_SOFTN_DECISION,
	BER_MEASUREMENT_EN => '1',
	SIMULATION => SIMULATION
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => EXTENDED_SYNC_RESET,
	DATAIN => DS_DATAOUT((N_PARITY_BITS_MAX*4 - 1) downto 0),
	ERASURES => DS_ERASURES(N_PARITY_BITS_MAX-1 downto 0),
	INPUT_VALID => DS_DATAOUT_VALID(0),
	G => G,
	N_PARITY_BITS => N_PARITY_BITS,
	K => K,
	SPECIAL => SPECIAL,
	DECODED_BITS => DECODED_BITS(0),
	DECODED_BITS_VALID => DECODED_BITS_VALID(0),
	DECODED_BIT => DECODED_BIT,
	DECODED_BIT_VALID => DECODED_BIT_VALID,
	NORMALIZATION => open,
	SM_GROWTH_INV_RATE => open,
	BER_THRESHOLD => BER_THRESHOLD,
	BER => BER,
	BER_VALID => BER_VALID,
	BIT_ERROR => BIT_ERROR,
	IN_SYNC => IN_SYNC,
	OUT_OF_SYNC => OUT_OF_SYNC_local,
	LOCK_STATUS => LOCK_STATUS
);

OUT_OF_SYNC <= OUT_OF_SYNC_local;	-- to output monitoring port

-- Other parallel decoders
VA_001: for I in 1 to (NDEC-1) generate
	VA_00x: VA 
	GENERIC MAP(
		N_PARITY_BITS_MAX => N_PARITY_BITS_MAX,
		K_MAX => K_MAX,
		TB_DEPTH => TB_DEPTH,
		HARD_SOFTN_DECISION => HARD_SOFTN_DECISION,
		BER_MEASUREMENT_EN => '0',
		SIMULATION => '0'
	)
	PORT MAP(
		CLK => CLK,
		SYNC_RESET => EXTENDED_SYNC_RESET,
		DATAIN => DS_DATAOUT((I*N_PARITY_BITS_MAX*4 - 1) downto ((I-1)*N_PARITY_BITS_MAX*4)),
		ERASURES => DS_ERASURES(I*N_PARITY_BITS_MAX-1 downto (I-1)*N_PARITY_BITS_MAX),
		INPUT_VALID => DS_DATAOUT_VALID(I),
		G => G,
		N_PARITY_BITS => N_PARITY_BITS,
		K => K,
		SPECIAL => SPECIAL,
		DECODED_BITS => open,
		DECODED_BITS_VALID => open,
		NORMALIZATION => open,
		SM_GROWTH_INV_RATE => open,
		BER_THRESHOLD => x"000000",
		BER => open,
		BER_VALID => open,
		IN_SYNC => open,
		OUT_OF_SYNC => open,
		LOCK_STATUS => open
	);
end generate;



--//-- OUTPUT REFORMATTING --------------------------------------------
-- 2 output types: 1-bit serial or 8-bit parallel.
OUTPUT_REFORMAT_001: if(NDEC = 1) generate
	-- 1-bit serial output. 
	DATAOUT1b1 <= DECODED_BIT;
	DATAOUT1b1_VALID <= DECODED_BIT_VALID;
	
	-- 8-bit parallel output
	-- repack decoded bits into bytes
	REMAINDER <= conv_integer(WPTR(2 downto 0));
	BYTE_PACKING_001: process(CLK)
	begin
		if rising_edge(CLK) then
			if(EXTENDED_SYNC_RESET = '1') then
				WPTR <= (others => '0');
				DATAOUT8b1_VALID <= '0';
			elsif(DECODED_BITS_VALID(0) = '1') then
				-- add 1 traceback length worth of newly decoded bits into the output elastic buffer
				OUTBUF(TB_DEPTH+REMAINDER-1 downto REMAINDER) <= DECODED_BITS(0);	
				DATAOUT8b1_VALID <= '0';
				WPTR <= WPTR + TB_DEPTH;
			else
				if(WPTR >= 8) then
					DATAOUT8b1_VALID <= '1';
					-- re-order, MSb first
					for I in 0 to 7 loop
						DATAOUT8b1(I) <= OUTBUF(7-I);
					end loop;
					WPTR <= WPTR - 8;
					OUTBUF((TB_DEPTH-1) downto 0) <= OUTBUF((TB_DEPTH+7) downto 8); -- shift right 8-bit
				else
					DATAOUT8b1_VALID <= '0';
				end if;
			end if;
		end if;
	end process;
end generate;

-- TODO: OUTPUT IN CASE NDEC > 1

--//-- DIFFERENTIALLY-ENCODED STREAM --------------------------------------------
DIFF_DEC_001: process(CLK)
begin
	if rising_edge(CLK) then
		-- 1-bit serial output. 
		DATAOUT1b_VALID <= DATAOUT1b1_VALID;
		if(DATAOUT1b1_VALID = '1') then
			DATAOUT1b1_D <= DATAOUT1b1;
			if(DIFF_ENC_ON = '1') then
				-- data stream was differentially encoded prior to FEC encoding. Reverse process here.
				DATAOUT1b <= DATAOUT1b1 xor DATAOUT1b1_D;
			else
				DATAOUT1b <= DATAOUT1b1;
			end if;
		end if;

		-- 8-bit parallel output
		DATAOUT8b_VALID <= DATAOUT8b1_VALID;
		if(DATAOUT8b1_VALID = '1') then
			DATAOUT8b1_D_LSB <= DATAOUT8b1(0);
			if(DIFF_ENC_ON = '1') then
				-- data stream was differentially encoded prior to FEC encoding. Reverse process here.
				DATAOUT8b(7) <= DATAOUT8b1(7) xor DATAOUT8b1_D_LSB;
				for I in 0 to 6 loop
					DATAOUT8b(I) <= DATAOUT8b1(I) xor DATAOUT8b1(I+1);
				end loop;
			else
				DATAOUT8b <= DATAOUT8b1;
			end if;
		end if;
		
		
	end if;
end process;


end Behavioral;

