----------------------------------------------
-- MSS copyright 2012
-- Filename: com1509pkg.vhd
-- Author: Alain Zarembowitch / MSS
-- Inheritance: N/A
-- Edit date: 8/2/12
-- Revision: 1
--	
--	Purpose: This package defines supplemental types, subtypes, 
--		 constants, and functions 
----------------------------------------------      


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

package com1509pkg is

	--// USER-DEFINED CONFIGURATION (PRIOR TO SYNTHESIS) ------------------------
	constant K_MAX: integer := 9;	
		-- maximum constraint length. Select 3,5,7, or 9
	constant 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
	constant NDEC: integer := 1;  
		-- number of parallel FEC decoders
		-- Valid range: 1,2,4,8
	constant	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.
	constant BER_THRESHOLD_12: std_logic_vector(23 downto 0) := conv_std_logic_vector(200, 24);
	constant BER_THRESHOLD_23: std_logic_vector(23 downto 0) := conv_std_logic_vector(160, 24);
	constant BER_THRESHOLD_34: std_logic_vector(23 downto 0) := conv_std_logic_vector(120, 24);
	constant BER_THRESHOLD_56: std_logic_vector(23 downto 0) := conv_std_logic_vector(100, 24);
	constant BER_THRESHOLD_78: std_logic_vector(23 downto 0) := conv_std_logic_vector(85, 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.
		-- Depends on the encoding rate: 
		-- 20% for rate 1/2
		-- 16% for rate 2/3
		-- 12% for rate 3/4
		-- 10% for rate 5/6
		-- 8.5% for rate 7/8
		
	constant STANDARD: integer := 0;
		-- There are some minor variations among standards, such as different puncturing scheme.
		-- 0 = Intelsat 
		-- 1 = CCSDS
		-- others = yet TBD

	--// END OF USER-DEFINED CONFIGURATION ------------------------
	-- DO NOT CHANGE derived parameters below
	
	type slv4xN_PARITY_BITS_MAX is array (integer range 0 to (N_PARITY_BITS_MAX-1)) of std_logic_vector(3 downto 0);	
		-- used for DATAINx, soft-decision input samples, in a parity group
	type slv4xN_PARITY_BITS_MAXxNDEC is array (integer range 0 to (NDEC-1)) of slv4xN_PARITY_BITS_MAX;	
		-- used for DATAINx 4-bit soft-quantized inputs x N_PARITY_BITS_MAX in a parity group x number of parallel decoders
	type slvGENPOLYxN_PARITY_BITS_MAX is array (integer range 0 to (N_PARITY_BITS_MAX-1)) of std_logic_vector(8 downto 0);	
		-- used for G, generator polynomials
	type PUNCTURING2Dtype is array (integer range 0 to 6, integer range 0 to 6) of std_logic;	-- 7x7 matrix of std_logic
	type slvN_PARITY_BITS_MAXxNDEC is array (integer range 0 to NDEC-1) of std_logic_vector((N_PARITY_BITS_MAX-1) downto 0);	
	
	-- functions declarations
	function f_nbits_bm return integer;
	function f_nbits_sm return integer;
	function f_previous_reg(I_TRELLIS_STATE: integer; BRANCH01: std_logic) return std_logic_vector;
	function f_parity_gen(ENCODER_REG:std_logic_vector(K_MAX-1 downto 0); G:slvGENPOLYxN_PARITY_BITS_MAX) return std_logic_vector;
	function f_previous_state(I_TRELLIS_STATE:integer; BRANCH01: std_logic) return integer;
	function f_puncturing_pattern(R1: integer range 1 to 7; R2: integer range 2 to 8) return PUNCTURING2Dtype;
	function f_punctured_encoding_period(R1: integer range 1 to 7; R2: integer range 2 to 8) return integer;

	-- fixed or derived constants (DO NOT CHANGE)
	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)
	constant NBITS_BM: integer := f_nbits_bm;
		-- branch metric precision. Trying to minimize decoder complexity in cases of hard-decision decoding
	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 NBITS_SM: integer := f_nbits_sm;
		-- state metric precision. 1 extra bit added for modulo normalization technique.
	constant N_TRELLIS_STATES_MAX: integer := 2**(K_MAX-1);
		-- maximum number of trellis states. 
	constant N_TB_RAMB16: integer := (N_TRELLIS_STATES_MAX + 1)/32;
		-- number of 16Kbit RAM blocks used for traceback. 
	constant TB_DEPTH: integer := 6*K_MAX;	
		-- traceback depth. MUST BE LESS THAN 120
	type slvTBDEPTHxNDEC is array (integer range 0 to NDEC-1) of std_logic_vector((TB_DEPTH-1) downto 0);	


end com1509pkg;

package body com1509pkg is

-- branch metrics precision
-- radix-2 treillis
function f_nbits_bm return integer is
variable retval: integer;
begin
	if(HARD_SOFTN_DECISION = '1') and (N_PARITY_BITS_MAX < 4) then
		retval := 2;
	elsif(HARD_SOFTN_DECISION = '1') and (N_PARITY_BITS_MAX < 8) then
		retval := 3;
	elsif(N_PARITY_BITS_MAX = 2) then
		retval := 5;
	elsif(N_PARITY_BITS_MAX < 5) then
		retval := 6;
	elsif(N_PARITY_BITS_MAX < 8) then
		retval := 7;
	else
		report "f_nbits_bm() N_PARITY_BITS_MAX is out of range 2-7: =" & integer'image(N_PARITY_BITS_MAX);	
	end if;
	return retval;
end function;

-- state metrics precision
-- An important property of path metrics in an ACS unit is that the magnitude of the path metrics 
-- is unlimited, yet a fixed number limits the difference between them:
-- Viterbi algorithm inherently bounds the maximum dynamic range Dmax of state metrics 
-- Dmax =Lmax*log2(N) (N-number of states, Lmax maximum branch metric of the radix-2 trellis)
-- Compute new state metric with precision > 4Dmax
function f_nbits_sm return integer is
variable retval: integer := 0;
begin
	if(HARD_SOFTN_DECISION = '1') then
		if(N_PARITY_BITS_MAX = 2) then
			if(K_MAX = 3) then
				retval := 5;
			elsif(K_MAX = 5) then
				retval := 6;
			elsif(K_MAX = 7) then
				retval := 6;
			elsif(K_MAX = 9) then
				retval := 7;
			end if;
		elsif(N_PARITY_BITS_MAX = 3) then
			if(K_MAX = 3) then
				retval := 5;
			elsif(K_MAX = 5) then
				retval := 6;
			elsif(K_MAX = 7) then
				retval := 7;
			elsif(K_MAX = 9) then
				retval := 7;
			end if;
		elsif(N_PARITY_BITS_MAX < 8) then
			if(K_MAX = 3) then
				retval := 6;
			elsif(K_MAX = 5) then
				retval := 7;
			elsif(K_MAX = 7) then
				retval := 8;
			elsif(K_MAX = 9) then
				retval := 8;
			end if;
		end if;
	else
		if(N_PARITY_BITS_MAX = 2) then
			if(K_MAX = 3) then
				retval := 8;
			elsif(K_MAX = 5) then
				retval := 9;
			elsif(K_MAX = 7) then
				retval := 10;	
			elsif(K_MAX = 9) then
				retval := 10;
			end if;
		elsif(N_PARITY_BITS_MAX = 3) then
			if(K_MAX = 3) then
				retval := 9;
			elsif(K_MAX = 5) then
				retval := 10;
			elsif(K_MAX = 7) then
				retval := 11;
			elsif(K_MAX = 9) then
				retval := 11;
			end if;
		elsif(N_PARITY_BITS_MAX < 8) then
			if(K_MAX = 3) then
				retval := 10;
			elsif(K_MAX = 5) then
				retval := 11;
			elsif(K_MAX = 7) then
				retval := 12;
			elsif(K_MAX = 9) then
				retval := 12;
			end if;
		end if;
	end if;
	
	if(retval = 0) then
		report "f_nbits_sm() input(s) K_MAX or N_PARITY_BITS_MAX out of range" & integer'image(K_MAX) 
				& " " & integer'image(N_PARITY_BITS_MAX);
	end if;
	return retval;
end function;

-- for a given trellis state, compute the previous encoder register
function f_previous_reg(I_TRELLIS_STATE: integer; BRANCH01: std_logic) return std_logic_vector is
variable ENCODER_PREV_REG: std_logic_vector((K_MAX-1) downto 0) := (others => '0');  
begin
	ENCODER_PREV_REG := conv_std_logic_vector(I_TRELLIS_STATE,K_MAX-1) & BRANCH01;
	return ENCODER_PREV_REG;
end function;

-- generate N_PARITY_BITS_MAX parity bits for a given input register
-- Register format:
-- bits (K-2:0): trellis state
-- bit K-1: new input bit
-- bits (K_MAX-1:K): ignored (generator polynomial taps are zero anyway)
function f_parity_gen(ENCODER_REG:std_logic_vector(K_MAX-1 downto 0); 
	G:slvGENPOLYxN_PARITY_BITS_MAX) return std_logic_vector is
variable retval: std_logic_vector((N_PARITY_BITS_MAX -1) downto 0) := (others => '0');
begin
	for J in 0 to (N_PARITY_BITS_MAX-1) loop
		-- for each generator polynomial...
		retval(J) := '0';
		for I in 0 to (K_MAX-1) loop
			retval(J) := retval(J) xor (ENCODER_REG(I) and G(J)(I));
		end loop;
	end loop;
	return retval;
end function;

-- for a given trellis state, compute the previous state index
function f_previous_state(I_TRELLIS_STATE: integer; BRANCH01: std_logic) return integer is
variable ENCODER_PREV_REG: std_logic_vector((K_MAX-1) downto 0) := (others => '0');  -- (ignore extra MSb)
variable SM_INDEX: integer := 0;
begin
	ENCODER_PREV_REG := conv_std_logic_vector(I_TRELLIS_STATE,K_MAX-1) & BRANCH01;
	SM_INDEX := conv_integer(ENCODER_PREV_REG((K_MAX-2) downto 0));
	return SM_INDEX;
end function;

-- fills in the 2D puncturing pattern matrix
-- Y-axis: parity bits = generator polynomial 0-6 = 0 - N_PARITY_BITS-1
-- X-axis: puncturing phase	0-6 = 0 - N_PUNCTURING_PHASES-1
-- Using CCSDS telemetry channel coding specs
-- For Intelsat, flip G0/G1
--Rate 2/3
--G0 10
--G1 11
--Rate ¾
--G0 101
--G1 110
--Rate 5/6
--G0 10101
--G1 11010
--Rate 7/8
--G0 1000101
--G1 1111010
function f_puncturing_pattern(R1: integer; R2: integer) return PUNCTURING2Dtype is
variable retval: PUNCTURING2Dtype;
begin
	-- a priori all encoded bits are transmitted = '1' 
	for I in 0 to 6 loop
		for J in 0 to 6 loop
			retval(I,J) := '1';
		end loop;
	end loop;
	-- describe puncturing ('0')
	
	if(STANDARD = 1) then
		-- CCSDS standard
		if(R1 = 2) and (R2 = 3) then	-- rate 2/3
			retval(1,0) := '0';
		elsif(R1 = 3) and (R2 = 4) then	-- rate 3/4
			retval(1,0) := '0';
			retval(2,1) := '0';
		elsif(R1 = 5) and (R2 = 6) then	-- rate 5/6
			retval(1,0) := '0';
			retval(2,1) := '0';
			retval(3,0) := '0';
			retval(4,1) := '0';
		elsif(R1 = 7) and (R2 = 8) then	-- rate 7/8
			retval(1,0) := '0';
			retval(2,0) := '0';
			retval(3,0) := '0';
			retval(4,1) := '0';
			retval(5,0) := '0';
			retval(6,1) := '0';
		end if;
	else
		-- Intelsat standard
		if(R1 = 2) and (R2 = 3) then	-- rate 2/3
			retval(1,1) := '0';
		elsif(R1 = 3) and (R2 = 4) then	-- rate 3/4
			retval(1,1) := '0';
			retval(2,0) := '0';
		elsif(R1 = 5) and (R2 = 6) then	-- rate 5/6
			retval(1,1) := '0';
			retval(2,0) := '0';
			retval(3,1) := '0';
			retval(4,0) := '0';
		elsif(R1 = 7) and (R2 = 8) then	-- rate 7/8
			retval(1,1) := '0';
			retval(2,1) := '0';
			retval(3,1) := '0';
			retval(4,0) := '0';
			retval(5,1) := '0';
			retval(6,0) := '0';
		end if;
	end if;
	return retval;
end function;

-- encoding/puncturing pattern period, based on the R1/R2 encoding rate
		-- Rate 1/2, the encoding pattern is G0 G1 etc => pattern period 2 receive bits
		-- Rate 1/3, the encoding pattern is G0 G1 G2  etc => pattern period 3 receive bits
		-- Rate 2/3, the encoding + puncturing pattern is G0 G1 G0 E  etc => pattern period 3 receive bits
		-- Rate 3/4, the encoding + puncturing pattern is G0 G1 G0 E E G1  etc => pattern period 4 receive bits
		-- Rate 5/6, the encoding + puncturing pattern is G0 G1 G0 E E G1 G0 E E G1 etc => pattern period 6 receive bits
		-- Rate 7/8, the encoding + puncturing pattern is G0 G1 G0 E GO E G0 E E G1 E G0 E G1  etc => pattern period 8 receive bits
		-- (E is for erasure, bit is not explicitely transmitted)
function f_punctured_encoding_period(R1: integer; R2: integer) return integer is
variable retval: integer;
begin
	if(R1 = 1) and (R2 = 2) then	-- rate 1/2
		retval := 2;
	elsif(R1 = 1) and (R2 = 3) then	-- rate 1/3
		retval := 3;
	elsif(R1 = 1) and (R2 = 7) then	-- rate 1/7
		retval := 7;
	elsif(R1 = 2) and (R2 = 3) then	-- rate 2/3
		retval := 3;
	elsif(R1 = 3) and (R2 = 4) then	-- rate 3/4
		retval := 4;
	elsif(R1 = 5) and (R2 = 6) then	-- rate 5/6
		retval := 6;
	elsif(R1 = 7) and (R2 = 8) then	-- rate 7/8
		retval := 8;
	else
		report "f_encoding_period_after_puncturing() R1/R2 inputs out of range: R1=" & integer'image(R1) & "  R2=" & integer'image(R2);	
	end if;
	return retval;
end function;




end com1509pkg;

