-------------------------------------------------------------
-- MSS copyright 1999-2001
--	Filename:  TCM_34.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 0.001
--	Date last modified: 12-30-01
-- Inheritance: 	none
--
-- description:  
-- COM-1010
-- Treillis coded modulation. Rate 3/4
-- Includes phase ambiguity resolution encoder (PARE). See Intelsat IESS-310 p 53, figure 11b.
---------------------------------------------------------------

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

entity TCM_34 is
    port ( 
		--GLOBAL CLOCKS, RESET
	   CLK : in std_logic;	-- master clock for this FPGA, synchronous 
		ASYNC_RESET: in std_logic;	-- asynchronous reset

		--// Input samples
		DATA_IN: in std_logic;
			-- input bit. Read at rising edge of CLK when SAMPLE_CLK_IN = '1';
		SAMPLE_CLK_IN: in std_logic;
			-- one CLK-wide pulse
	  	SOF_RESET: in std_logic;
			-- 1 CLK-wide pulse indicating that the encoder shall be reset. 
			-- Aligned with SAMPLE_CLK_IN.
			-- Used in the block mode. 


		--// Output samples
		DATA_OUT: out std_logic_vector(3 downto 0);
			-- output sample. Read at rising edge of CLK when SAMPLE_CLK_OUT = '1';
			-- 4-bit parallel output
		SAMPLE_CLK_OUT: out std_logic
			-- one CLK-wide pulse. same rate as SAMPLE_CLK_IN
			-- Latency is 1 CLK.
		
			);
end entity;

architecture behavioral of TCM_34 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
component ENCODER_K7R2 is
    port ( 
		--GLOBAL CLOCKS, RESET
	   CLK : in std_logic;	-- master clock for this FPGA, synchronous 
		ASYNC_RESET: in std_logic;	-- asynchronous reset

		--// Input samples
		DATA_IN: in std_logic;
			-- input bit. Read at rising edge of CLK when SAMPLE_CLK_IN = '1';
		SAMPLE_CLK_IN: in std_logic;
			-- one CLK-wide pulse
	  	SOF_RESET: in std_logic;
			-- 1 CLK-wide pulse indicating that the encoder shall be reset. 
			-- Aligned with SAMPLE_CLK_IN.
			-- Used in the block mode. 

		--// Output samples
		DATA_OUT: out std_logic_vector(1 downto 0);
			-- output sample. Read at rising edge of CLK when SAMPLE_CLK_IN = '1';
			-- 2-bit parallel output
		SAMPLE_CLK_OUT: out std_logic
			-- one CLK-wide pulse. same rate as SAMPLE_CLK_IN
			-- Latency is 1 CLK.
		
			);
end component;
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- Suffix _E 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
signal SELECT_INPUT_BIT: std_logic_vector(1 downto 0);	
signal SAMPLE_CLK_CONV: std_logic;	
signal SAMPLE_CLK_CONV_D: std_logic;	
signal SAMPLE_CLK_PARE_I: std_logic;	
signal SAMPLE_CLK_PARE_Q: std_logic;	
signal DATA_IN_CONV: std_logic;
signal DATA_IN_CONV_D: std_logic;
signal DATA_CONV: std_logic_vector(1 downto 0);
signal DATA_PARE_I_D: std_logic;
signal DATA_PARE_Q_D: std_logic;
signal DATA_PARE_I: std_logic;
signal DATA_PARE_Q: std_logic;
signal DATA_U: std_logic;
signal DATA_U_D: std_logic;
signal DATA_V: std_logic;
signal DATA_V_D: std_logic;
signal DATA_W: std_logic;
signal DATA_W_D: std_logic;
signal DATA_X: std_logic;
signal DATA_X_D: std_logic;
signal DATA_PARE_I_OUT: std_logic;
signal DATA_PARE_Q_OUT: std_logic;
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------

begin

-- separate 2 PARE paths (I,Q) and rate 1/2 encoder path
-- every other bit.
S2P: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		SELECT_INPUT_BIT <= "00";
	elsif rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1')  then
			if(SELECT_INPUT_BIT = 2) then
				-- modulo 3 counter.
				SELECT_INPUT_BIT <= "00";
			else
				SELECT_INPUT_BIT <= SELECT_INPUT_BIT + 1;
			end if;
	  	end if;
	end if;
end process;


SAMPLE_CLK_CONV <= SAMPLE_CLK_IN when(SELECT_INPUT_BIT = "10") else '0';
SAMPLE_CLK_PARE_I <= SAMPLE_CLK_IN when(SELECT_INPUT_BIT = "00") else '0';
SAMPLE_CLK_PARE_Q <= SAMPLE_CLK_IN when(SELECT_INPUT_BIT = "01") else '0';

-- Differential encoder prior to the rate 1/2 convolutional encoder
DIFF_CONV: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		DATA_IN_CONV_D <= '0';
	elsif rising_edge(CLK) then
		if(SAMPLE_CLK_CONV = '1') then
			DATA_IN_CONV_D <= DATA_IN_CONV;
	  	end if;
	end if;
end process;
DATA_IN_CONV <= DATA_IN_CONV_D xor DATA_IN;


-- Latency is 1 CLK.
ENCODER_K7R2_GEN: ENCODER_K7R2 port map(
	ASYNC_RESET => ASYNC_RESET,
	CLK => CLK,
	DATA_IN => DATA_IN_CONV,
	SOF_RESET => SOF_RESET,
	SAMPLE_CLK_IN => SAMPLE_CLK_CONV,
	DATA_OUT => DATA_CONV
	);

-- Reclock PARE input data to align with SAMPLE_CLK_CONV.
-- DATA_PARE_D is aligned timewise with DATA_CONV.
RECLOCK_001: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		DATA_PARE_I <= '0';
		DATA_PARE_I_D <= '0';
		DATA_PARE_Q <= '0';
		DATA_PARE_Q_D <= '0';
		SAMPLE_CLK_CONV_D <= '0';
	elsif rising_edge(CLK) then
		SAMPLE_CLK_CONV_D <= SAMPLE_CLK_CONV;
		if(SAMPLE_CLK_PARE_I = '1')  then
			DATA_PARE_I <= DATA_IN;
	  	end if;
		if(SAMPLE_CLK_PARE_Q = '1')  then
			DATA_PARE_Q <= DATA_IN;
	  	end if;
		if(SAMPLE_CLK_CONV = '1')  then
			DATA_PARE_I_D <= DATA_PARE_I;
			DATA_PARE_Q_D <= DATA_PARE_Q;
	  	end if;
	end if;
end process;

-- Two differential encoders U and V
DIFF_U: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		DATA_U_D <= '0';
	elsif rising_edge(CLK) then
		if(SAMPLE_CLK_CONV_D = '1') and (DATA_CONV(0) = '0') then
			-- Select = 0 path U
			DATA_U_D <= DATA_U ;
	  	end if;
	end if;
end process;
DATA_U <= DATA_U_D xor DATA_PARE_I_D;

DIFF_V: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		DATA_V_D <= '0';
	elsif rising_edge(CLK) then
		if(SAMPLE_CLK_CONV_D = '1') and (DATA_CONV(0) = '1') then
			-- Select = 1 path V
			DATA_V_D <= DATA_V ;
	  	end if;
	end if;
end process;
DATA_V <= DATA_V_D xor DATA_PARE_I_D;

-- select differential decoder output.
DIFF_SEL_I: process(DATA_CONV, DATA_U, DATA_V)
begin
	if(DATA_CONV(0) = '0') then
		DATA_PARE_I_OUT <= DATA_U;
	else
		DATA_PARE_I_OUT <= DATA_V;
	end if;
end process;


-- Two differential encoders W and X
DIFF_W: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		DATA_W_D <= '0';
	elsif rising_edge(CLK) then
		if(SAMPLE_CLK_CONV_D = '1') and (DATA_CONV(0) = '0') then
			-- Select = 0 path W
			DATA_W_D <= DATA_W ;
	  	end if;
	end if;
end process;
DATA_W <= DATA_W_D xor DATA_PARE_Q_D;

DIFF_X: process(ASYNC_RESET, CLK)
begin
	if(ASYNC_RESET = '1') then
		DATA_X_D <= '0';
	elsif rising_edge(CLK) then
		if(SAMPLE_CLK_CONV_D = '1') and (DATA_CONV(0) = '1') then
			-- Select = 1 path X
			DATA_X_D <= DATA_X ;
	  	end if;
	end if;
end process;
DATA_X <= DATA_X_D xor DATA_PARE_Q_D;

-- select differential decoder output.
DIFF_SEL_Q: process(DATA_CONV, DATA_W, DATA_X)
begin
	if(DATA_CONV(0) = '0') then
		DATA_PARE_Q_OUT <= DATA_W;
	else
		DATA_PARE_Q_OUT <= DATA_X;
	end if;
end process;


--// output
DATA_OUT(0) <= DATA_PARE_I_OUT;
DATA_OUT(1) <= DATA_PARE_Q_OUT;
DATA_OUT(2) <= DATA_CONV(0);
DATA_OUT(3) <= DATA_CONV(1);
SAMPLE_CLK_OUT <= SAMPLE_CLK_CONV_D;

end behavioral;
