-------------------------------------------------------------
-- MSS copyright 2012
-- Filename:  DATA_SPLITTER1.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
-- Version: Rev 2
-- Last modified: 11/29/12 
-- See also: DATA_SPLITTER.VHD

-- Inheritance: 	N/A
--
-- description: Arrange input data by groups of N_PARITY_BITS. Mark as "erasure" bits that were punctured out 
-- by the encoder. Assumes that the follow-on Viterbi decoder can operate continuously (i.e. 
-- no splitting into fixed-length blocks).
-- Of course, the maximum input speed is restricted: no more than 1 input bit per clk in the average.
-- In the case of parallel decoders, use the full version of DATA_SPLITTER instead.
--
-- Rev2 11/29/12 AZ
-- passed parameters through generic for more flexibility instead of common com1509pkg
---------------------------------------------------------------
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 DATA_SPLITTER1 is
	generic(
		N_PARITY_BITS_MAX: integer := 3
	);
    Port ( 
		-- GLOBAL CLOCKS, RESET
		CLK: in std_logic;	-- synchronous clock
		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;
		
		-- Skip one input bit for each pulse
		-- This feature is used to synchronize the input stream with the periodic encoding/puncturing pattern
		SKIP1BIT_PULSE: in std_logic;
		
		--// CONFIGURATION 
		-- The configuration parameters below can be changed dynamically at run-time.
		-- Use SYNC_RESET after such a configuration change.
		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
		R1: in integer range 1 to 7;
		R2: in integer range 2 to 8;
			-- Convolutional encoding rate R1/R2			
		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/DVB
			-- 2 = DVB rate 1/2 only
			-- others = yet TBD

			-- Valid rates: 1/2, 1/3, 1/7, 2/3, 3/4, 5/6, 7/8

		-- OUTPUT BITS
		-- groups of N_PARITY_BITS, 4-bit soft-quantized. One set for each parallel decoder.
		DATAOUT: out std_logic_vector(((N_PARITY_BITS_MAX * 4) -1) downto 0) := (others => '0'); 
			-- received (soft-quantized) bit, grouped in a N_PARITY_BITS group 
		ERASURES: out std_logic_vector((N_PARITY_BITS_MAX-1) downto 0) := (others => '0');
			-- erasures on individual bits of a parity group, for each parallel decoder ('1' indicates a puncture)	
			-- tells the BMU to ignore a specific bit or bits during distance computation.
		DATAOUT_VALID: out std_logic := '0'
			-- 1 CLK-wide pulse to tell each decoder whether a parity group + erasure information is available for BMU processing
		
		);
end entity;

architecture Behavioral of DATA_SPLITTER1 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT P8_TO_S1_CONVERSION
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		DATA8_IN : IN std_logic_vector(7 downto 0);
		SAMPLE8_CLK_IN : IN std_logic;
		SAMPLE1_CLK_OUT_REQ : IN std_logic;          
		SAMPLE8_CLK_IN_REQ : OUT std_logic;
		DATA1_OUT : OUT std_logic;
		SAMPLE1_CLK_OUT : OUT std_logic;
		BUF_EMPTY: out std_logic
		);
	END COMPONENT;
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- Suffix _E indicates a one CLK early 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
-- Suffix G indicates a global clock

signal PUNCTURING_PATTERN: PUNCTURING2Dtype := (others => (others => '0'));
signal PUNCTURED_ENCODING_PERIOD: integer := 2;
signal N_PUNCTURING_PHASES: integer := 1;

type SLV8x4 is array (integer range 0 to 3) of std_logic_vector(7 downto 0);
signal DATA8_IN: SLV8x4 := (others => (others => '0'));
signal DATA1: std_logic_vector(3 downto 0) := (others => '0');
signal DATAIN_READY_local: std_logic_vector(3 downto 0) := (others => '0');
signal BUF_EMPTY: std_logic_vector(3 downto 0) := (others => '0');
signal SKIP1BIT_FLAG: std_logic := '0';
signal SAMPLE1_CLK_REQ: std_logic := '0';
signal SAMPLE1_CLK_E: std_logic := '0';
signal SAMPLE1_CLK: std_logic := '0';
signal ERASURE1_E: std_logic := '0';
signal ERASURE1: std_logic := '0';
signal PP_X: integer range 0 to 6 := 0;	-- puncturing pattern X-axis: puncturing phase	0-6 = 0 - N_PUNCTURING_PHASES-1 
signal PP_Y: integer range 0 to 6 := 0;	-- puncturing pattern Y-axis: parity bits = generator polynomial 0-6 = 0 - N_PARITY_BITS-1
signal PARITY_INDEX_E: integer range 0 to 6 := 0;	-- maximum size is 7x7
signal PARITY_INDEX: integer range 0 to 6 := 0;	-- maximum size is 7x7

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

-- define puncturing pattern (related to coding rate R1/R2)
-- Puncturing is used at the encoder to reduce the redundancy. 
-- It is described by a 2-dimensional array of maximum size 7(parity bits) x 7(puncturing phases)
PUNCTURING_PATTERN_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			PUNCTURING_PATTERN <= f_puncturing_pattern(R1,R2,STANDARD);
			PUNCTURED_ENCODING_PERIOD <= f_punctured_encoding_period(R1,R2);
		end if;
	end if;
end process;
N_PUNCTURING_PHASES <= R1;

-- serialize input
P8_TO_S1_CONVERSION_001: for I in 0 to 3 generate
	DATA8_IN(I) <= DATAIN(I+28) & DATAIN(I+24) & DATAIN(I+20) & DATAIN(I+16) & DATAIN(I+12) & DATAIN(I+8) & DATAIN(I+4) & DATAIN(I);
	
	P8_TO_S1_CONVERSION_00x: P8_TO_S1_CONVERSION PORT MAP(
		CLK => CLK,
		SYNC_RESET => SYNC_RESET,
		DATA8_IN => DATA8_IN(I),
		SAMPLE8_CLK_IN => DATAIN_VALID,
		SAMPLE8_CLK_IN_REQ => DATAIN_READY_local(I),
		DATA1_OUT => DATA1(I),
		SAMPLE1_CLK_OUT => open,
		SAMPLE1_CLK_OUT_REQ => SAMPLE1_CLK_REQ,
		BUF_EMPTY => BUF_EMPTY(I)
	);
end generate;

-- report flow control information to the source
DATAIN_READY <= DATAIN_READY_local(0);

-- read next input sample (when not an erasure slot and when not skipping one bit)
SAMPLE1_CLK_REQ <= SAMPLE1_CLK_E and (not ERASURE1_E) and (not SKIP1BIT_FLAG);

-- skipping mechanism
SKIP_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SKIP1BIT_PULSE = '1') then
			-- arm skip flag
			SKIP1BIT_FLAG <= '1';
		elsif(SAMPLE1_CLK_E = '1') and (ERASURE1_E = '0') then
			-- clear skip flag
			SKIP1BIT_FLAG <= '0';
		end if;	
	end if;
end process;

-- Read received bits. Insert erasures where puncturing occured at the encoder.
-- The encoding/puncturing pattern is represented as a two-dimensional matrix, with
-- X as number of parity bits (i.e. number of polynomials used for encoding, 2 or 3) and 
-- Y as puncturing phase index. 
-- Each group of N_PARITY_BITS is then passed to the BMU to compute the branch metric.

RPTR_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE1_CLK <= SAMPLE1_CLK_E;
		ERASURE1 <= ERASURE1_E;
		PARITY_INDEX <= PARITY_INDEX_E;
			-- 1 CLK latency to read data from the block ram
			
		if(SYNC_RESET = '1') then
			SAMPLE1_CLK_E <= '0';
			ERASURE1_E <= '0';
			PP_X <= 0;	-- puncturing phase index
			PP_Y <= 0;	-- parity bit index
		elsif (PUNCTURING_PATTERN(PP_X,PP_Y) = '0') then
			-- next bit was erased by puncturing at the encoder
			SAMPLE1_CLK_E <= '1';
			ERASURE1_E <= '1';
			PARITY_INDEX_E <= PP_Y;
			-- increment 2D pointer to PUNCTURING_PATTERN
			if(PP_Y = N_PARITY_BITS -1 ) then	-- modulo N_PARITY_BITS counter
				PP_Y <= 0;	
				if(PP_X = N_PUNCTURING_PHASES -1 ) then	-- modulo N_PUNCTURING_PHASES counter
					PP_X <= 0;	
				else
					PP_X <= PP_X + 1;	-- puncturing phase index
				end if;
			else
				PP_Y <= PP_Y + 1;	-- parity bit index
			end if;
		elsif (BUF_EMPTY(0) = '0') then
			-- buffer is not empty
			SAMPLE1_CLK_E <= '1';
			ERASURE1_E <= '0';
			PARITY_INDEX_E <= PP_Y;
			-- increment 2D pointer to PUNCTURING_PATTERN
			if(PP_Y = N_PARITY_BITS -1 ) then	-- modulo N_PARITY_BITS counter
				PP_Y <= 0;	
				if(PP_X = N_PUNCTURING_PHASES -1 ) then	-- modulo N_PUNCTURING_PHASES counter
					PP_X <= 0;	
				else
					PP_X <= PP_X + 1;	-- puncturing phase index
				end if;
			else
				PP_Y <= PP_Y + 1;	-- parity bit index
			end if;
		else
			SAMPLE1_CLK_E <= '0';
			ERASURE1_E <= '0';
		end if;
	end if;
end process;
	
-- form a parity group (including erasure information) for use by subsequent Branch Metric Unit (BMU)
PG_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE1_CLK = '1') then
			ERASURES(PARITY_INDEX) <= ERASURE1;
			DATAOUT((4*PARITY_INDEX + 3) downto (4*PARITY_INDEX)) <= DATA1;
			
			if(PARITY_INDEX = N_PARITY_BITS - 1) then
				-- parity group is complete
				DATAOUT_VALID <= '1';
			else
				-- parity group is still being assembled
				DATAOUT_VALID <= '0';
			end if;
		else
			DATAOUT_VALID <= '0';
		end if;
	end if;
end process;
	
end Behavioral;

