-------------------------------------------------------------
-- MSS copyright 2015-2018
-- Filename:  TC_DECODER_DVB_RCS2.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
-- Version: Rev 5
-- Last modified: 11/12/18

-- Inheritance: 	N/A
--
-- description:  DVB-RCS2 turbo code decoder 
--
-- FPGA occupancy (NQBITS = 4, NADDRBITS = 8, maximum payload size < 2048 bits)
-- Number of Slice Registers 3558  
-- Number of Slice LUTs 10652
-- Number of Block RAM/FIFO 15 
-- Number of BUFG/BUFGCTRLs 1
-- Number of DSP48E1s 1
--
-- Rev 2 1/13/17 AZ
-- Added SOF_OUT, EOF_OUT
--
-- Rev 3 9/7/17 AZ
-- Corrected EOF_OUT
--
-- Rev 4 5/19/18 AZ
-- Corrected rate7/8 Y puncturing period bug
--
-- Rev 5 11/12/18 AZ
-- Corrected missing circular initialization of A_IN. Now matches the matlab turbo.m code.
-------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity TC_DECODER_DVB_RCS2 is
	generic (
		NQBITS: integer range 3 to 8 := 4;
			-- number of soft-quantized bits at the decoder input
		NADDRBITS: integer range 2 to 10 := 10;
			-- number of bits in the payload bytes counter
			-- = ceiling(log2(maximum burst payload size))
			-- Affects the number of RAM blocks used.
		SIMULATION: std_logic := '0'
			-- 1 during simulation, 0 for release	
	);
    Port ( 
		--GLOBAL CLOCKS
		CLK: std_logic;
		SYNC_RESET: in std_logic;
		
		-- INPUTS
		DATA_A_IN: in std_logic_vector((NQBITS-1) downto 0);
		DATA_B_IN: in std_logic_vector((NQBITS-1) downto 0);
			-- two soft-quantized input bits A,B
			-- signed format
			-- Expects best '0' = most negative = 1001	-- FOR SYMMETRY. try to avoid 1000
			-- Expects best '1' = most positive = 0111
		DATA_IN_VALID: in std_logic;
			-- 2 input bits are read at the rising edge of CLK when DATA_IN_VALID = '1'
		SOF_IN: in std_logic;
			-- 1 CLK pulse indicating the first DATAx_IN in the frame. Aligned with DATA_IN_VALID pulse
			-- NEVER send SOF_IN when CTS_OUT = '0'
		EOF_IN: in std_logic;
			-- 1 CLK pulse indicating the last DATAx_IN in the frame. Aligned with DATA_IN_VALID pulse
		CTS_OUT: out std_logic;
			-- flow control. Always check the Clear-To-Send flag before sending a new DATAx_IN
			
		
		-- CONTROLS
		-- See ETSI EN 300 545 table 9-2
		-- one MUST apply a SYNC_RESET pulse to activate these controls
		BURST_PAYLOAD_SIZE: in std_logic_vector((NADDRBITS-1) downto 0);
			-- range 1 - 1000 BYTES
		P: in std_logic_vector(6 downto 0);
			-- range 9 - 81
		Q0: in std_logic_vector(3 downto 0);	
		Q1: in std_logic_vector(3 downto 0);
		Q2: in std_logic_vector(3 downto 0);
			-- range 0 - 15
		Q3: in std_logic_vector(2 downto 0);
			-- range 0 - 5
		Y_PUNCTURING_PERIOD: in std_logic_vector(4 downto 0);
			-- 1 - 28 bits
		Y_PUNCTURING_PATTERN: in std_logic_vector(27 downto 0);
			-- read from right (bit 0) to left (bit Y_PUNCTURING_PERIOD-1) 
			-- right-aligned. MSbs may be unused, depending on the period.
			-- therefore the patterns in Annex A must be flipped.
		W_PUNCTURING_PATTERN: in std_logic;
		N_ITER: in std_logic_vector(3 downto 0);
			-- number of iterations. MUST BE ODD (starts with SISO1 and ends with SISO1)
		CRC16_EN: in std_logic;
			-- enable 16-bit CRC 
			
		-- OUTPUTS
		DATA_OUT: out std_logic_vector(1 downto 0);
		DATA_OUT_VALID: out std_logic;
		SOF_OUT: out std_logic;
		EOF_OUT: out std_logic;
		CTS_IN: in std_logic;
		
		-- MONITORING
		CRC_VALID: out std_logic;
		BAD_CRC: out std_logic;
		FRAME_CNTR: out std_logic_vector(31 downto 0);
		FRAME_ERROR_CNTR: out std_logic_vector(31 downto 0)
			-- only valid when CRC16_EN = '1' at both encoder and decoder
		  
			  );
end entity;

architecture Behavioral of TC_DECODER_DVB_RCS2 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
-- Size optimization possible if encoder/decoder are configured identically. Use PERMUTATION_TABLE at top level and share.
	COMPONENT PERMUTATION_TABLE
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		BURST_PAYLOAD_SIZE : IN std_logic_vector(9 downto 0);
		R1 : IN std_logic_vector(2 downto 0);
		R2 : IN std_logic_vector(3 downto 0);
		P : IN std_logic_vector(6 downto 0);
		Q0 : IN std_logic_vector(3 downto 0);
		Q1 : IN std_logic_vector(3 downto 0);
		Q2 : IN std_logic_vector(3 downto 0);
		Q3 : IN std_logic_vector(2 downto 0);
		J_IN : IN std_logic_vector(11 downto 0);
		JPERM_IN : IN std_logic_vector(11 downto 0);          
		JPERM_OUT : OUT std_logic_vector(11 downto 0);
		J_OUT : OUT std_logic_vector(11 downto 0);
		TABLE_READY : OUT std_logic
		);
	END COMPONENT;

	COMPONENT BRAM_DP2
	GENERIC(
		DATA_WIDTHA: integer;
		ADDR_WIDTHA: integer;
		DATA_WIDTHB: integer;
		ADDR_WIDTHB: integer
	);
	PORT(
		CLKA   : in  std_logic;
		CSA: in std_logic;	
		WEA    : in  std_logic;	
		OEA : in std_logic;	
		ADDRA  : in  std_logic_vector(ADDR_WIDTHA-1 downto 0);
		DIA   : in  std_logic_vector(DATA_WIDTHA-1 downto 0);
		DOA  : out std_logic_vector(DATA_WIDTHA-1 downto 0);
		CLKB   : in  std_logic;
		CSB: in std_logic;	-- chip select, active high
		WEB    : in  std_logic;	-- write enable, active high
		OEB : in std_logic;	-- output enable, active high
		ADDRB  : in  std_logic_vector(ADDR_WIDTHB-1 downto 0);
		DIB   : in  std_logic_vector(DATA_WIDTHB-1 downto 0);
		DOB  : out std_logic_vector(DATA_WIDTHB-1 downto 0)
		);
	END COMPONENT;

	COMPONENT BM_GEN
	GENERIC(
		NQBITS: integer;
		NLLRBITS: integer;
		NFEWERBITS: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET: in std_logic;
		A : IN std_logic_vector((NQBITS-1) downto 0);
		B : IN std_logic_vector((NQBITS-1) downto 0);
		Y : IN std_logic_vector((NQBITS-1) downto 0);
		W : IN std_logic_vector((NQBITS-1) downto 0);
		ERASURE_Y : IN std_logic;
		ERASURE_W : IN std_logic;          
		LLR00 : in std_logic_vector(NLLRBITS-1 downto 0);  
		LLR01 : in std_logic_vector(NLLRBITS-1 downto 0);  
		LLR10 : in std_logic_vector(NLLRBITS-1 downto 0);  
		LLR11 : in std_logic_vector(NLLRBITS-1 downto 0);  
		IN_VALID: in std_logic;
		BM0 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM1 : out std_logic_vector(NLLRBITS-1 downto 0);           
		BM2 : out std_logic_vector(NLLRBITS-1 downto 0);      
		BM3 : out std_logic_vector(NLLRBITS-1 downto 0);           
		BM4 : out std_logic_vector(NLLRBITS-1 downto 0);           
		BM5 : out std_logic_vector(NLLRBITS-1 downto 0);         
		BM6 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM7 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM8 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM9 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM10 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM11 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM12 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM13 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM14 : out std_logic_vector(NLLRBITS-1 downto 0);          
		BM15 : out std_logic_vector(NLLRBITS-1 downto 0);
		OUT_VALID: out std_logic
		);
	END COMPONENT;

	COMPONENT FORWARD_STATE_GEN
	GENERIC(
		NLLRBITS: integer;
		NSMBITS: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		A_IN0 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN1: IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN2 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN3 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN4 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN5 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN6 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN7 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN8 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN9 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN10 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN11 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN12 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN13 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN14 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN15 : IN std_logic_vector((NSMBITS-1) downto 0);      
		BM0 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM1 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM2 : in std_logic_vector(NLLRBITS-1 downto 0);      
		BM3 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM4 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM5 : in std_logic_vector(NLLRBITS-1 downto 0);         
		BM6 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM7 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM8 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM9 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM10 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM11 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM12 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM13 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM14 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM15 : in std_logic_vector(NLLRBITS-1 downto 0);
		A_OUT0 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT1 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT2 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT3 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT4 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT5 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT6 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT7 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT8 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT9 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT10 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT11 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT12 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT13 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT14 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		A_OUT15 : OUT std_logic_vector((NSMBITS-1) downto 0)       
		);
	END COMPONENT;

	COMPONENT BACKWARD_STATE_GEN
	GENERIC(
		NLLRBITS: integer;
		NSMBITS: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		B_IN0 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN1: IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN2 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN3 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN4 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN5 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN6 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN7 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN8 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN9 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN10 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN11 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN12 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN13 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN14 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN15 : IN std_logic_vector((NSMBITS-1) downto 0);      
		BM0 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM1 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM2 : in std_logic_vector(NLLRBITS-1 downto 0);      
		BM3 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM4 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM5 : in std_logic_vector(NLLRBITS-1 downto 0);         
		BM6 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM7 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM8 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM9 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM10 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM11 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM12 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM13 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM14 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM15 : in std_logic_vector(NLLRBITS-1 downto 0);
		B_OUT0 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT1 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT2 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT3 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT4 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT5 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT6 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT7 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT8 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT9 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT10 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT11 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT12 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT13 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT14 : OUT std_logic_vector((NSMBITS-1) downto 0);          
		B_OUT15 : OUT std_logic_vector((NSMBITS-1) downto 0)       
		);
	END COMPONENT;

	COMPONENT LLR
	GENERIC(
		NLLRBITS: integer;
		NSMBITS: integer
	);
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		INPUT_VALID: in std_logic;
		A_IN0 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN1: IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN2 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN3 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN4 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN5 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN6 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN7 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN8 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN9 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN10 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN11 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN12 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN13 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN14 : IN std_logic_vector((NSMBITS-1) downto 0);          
		A_IN15 : IN std_logic_vector((NSMBITS-1) downto 0);      
		BM0 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM1 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM2 : in std_logic_vector(NLLRBITS-1 downto 0);      
		BM3 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM4 : in std_logic_vector(NLLRBITS-1 downto 0);           
		BM5 : in std_logic_vector(NLLRBITS-1 downto 0);         
		BM6 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM7 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM8 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM9 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM10 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM11 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM12 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM13 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM14 : in std_logic_vector(NLLRBITS-1 downto 0);          
		BM15 : in std_logic_vector(NLLRBITS-1 downto 0);
		B_IN0 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN1: IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN2 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN3 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN4 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN5 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN6 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN7 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN8 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN9 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN10 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN11 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN12 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN13 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN14 : IN std_logic_vector((NSMBITS-1) downto 0);          
		B_IN15 : IN std_logic_vector((NSMBITS-1) downto 0);      
		LLR00: out std_logic_vector((NLLRBITS-1) downto 0);
		LLR01: out std_logic_vector((NLLRBITS-1) downto 0);
		LLR10: out std_logic_vector((NLLRBITS-1) downto 0);
		LLR11: out std_logic_vector((NLLRBITS-1) downto 0);
		DECODED_DATA_OUT: out std_logic_vector(1 downto 0)
		);
	END COMPONENT; 

	COMPONENT CRC16_8b
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		CRC16_IN : IN std_logic_vector(15 downto 0);
		DATA_IN : IN std_logic_vector(7 downto 0);
		DATA_VALID_IN : IN std_logic;          
		CRC16_OUT : OUT std_logic_vector(15 downto 0);
		CRC16_VALID : 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 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
-- _INC for increment
-- _DEC for decrement

--//-- PERMUTATION INDICES ---------------------------------------
signal PT_BURST_BYTE_SIZE: std_logic_vector(9 downto 0) := (others => '0');
signal PT_JPERM_IN: std_logic_vector(11 downto 0) := (others => '0');
signal PT_J_OUT: std_logic_vector(11 downto 0) := (others => '0');

-- Input state machine
signal INPUT_STATE: integer range 0 to 3 := 0;
signal INPUT_STATE_D: integer range 0 to 3 := 0;
signal NEW_SYSTEMATIC_INPUT_PAIR: std_logic := '0';
signal DATA_A_IN_SYM: std_logic_vector((NQBITS-1) downto 0);
signal DATA_B_IN_SYM: std_logic_vector((NQBITS-1) downto 0);
signal DATA_A_IN_D: std_logic_vector((NQBITS-1) downto 0);
signal DATA_B_IN_D: std_logic_vector((NQBITS-1) downto 0);
signal DATA_IN_VALID_D: std_logic;
signal EOF_IN_D: std_logic;
signal WPTR1: unsigned((BURST_PAYLOAD_SIZE'left+2) downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 AB symbols/frame.
signal N0: unsigned(WPTR1'left downto 0):= (others => '0');
signal N: unsigned(WPTR1'left downto 0):= (others => '0');
signal CTS_OUT_local: std_logic := '0';

-- depuncturing
signal Y_PUNCTURING: std_logic_vector(28 downto 0) := (others => '0');
signal PTR_PUNCTURING: unsigned(WPTR1'length-1 downto 0) := (others => '0');  
signal PUNCTURING_TBL_WPTR: unsigned(WPTR1'length downto 0) := (others => '0');  
signal PUNCTURING_TBL_RPTR: unsigned(WPTR1'length downto 0) := (others => '0');  
signal PUNCTURING_TBL_ADDRA: unsigned(WPTR1'length downto 0) := (others => '0'); 
signal PUNCTURING_TBL_ADDRB: unsigned(WPTR1'length downto 0) := (others => '0'); 
signal PUNCTURING_TBL_WEA: std_logic := '0';
signal PUNCTURING_TBL_WEB: std_logic := '0';
signal PUNCTURING_TBL_DIA: unsigned(WPTR1'length downto 0) := (others => '0');  
signal PUNCTURING_TBL_DIB: unsigned(WPTR1'length downto 0) := (others => '0');  
signal PTR_PUNCTURING2A: std_logic_vector(WPTR1'length downto 0) := (others => '0');  
signal PTR_PUNCTURING2B: std_logic_vector(WPTR1'length downto 0) := (others => '0');  
signal N_PARITY_BITS: unsigned(WPTR1'length downto 0) := (others => '0');

-- data AB input buffer
signal IBUF1_ADDRA: std_logic_vector(WPTR1'left downto 0) := (others => '0'); 
signal WPTR1_INC: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 8000 bits/frame.
signal FLIP_IBUF12_AB: std_logic := '0';
signal DATA_IN: std_logic_vector(2*NQBITS-1 downto 0) := (others => '0');
signal DATA_AB: std_logic_vector(2*NQBITS-1 downto 0) := (others => '0');
signal DATA_A: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal DATA_B: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal DATA_AB_PERMUTATED: std_logic_vector(2*NQBITS-1 downto 0) := (others => '0');
signal DATA_A_PERMUTATED: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal DATA_B_PERMUTATED: std_logic_vector((NQBITS-1) downto 0) := (others => '0');

-- parity Y1 input buffer
signal IBUF3_ADDRA: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF3_ADDRB: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF3_WEA: std_logic := '0';
signal IBUF3_WEB: std_logic := '0';
signal IBUF3_DIA: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF3_DIB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF3_DOB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal DATA_Y1: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal ERASURE_Y1: std_logic := '0';

-- parity W1 input buffer
signal IBUF4_ADDRA: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF4_ADDRB: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF4_WEA: std_logic := '0';
signal IBUF4_WEB: std_logic := '0';
signal IBUF4_DIA: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF4_DIB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF4_DOB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal DATA_W1: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal ERASURE_W1: std_logic := '0';

-- parity Y2 input buffer
signal IBUF5_ADDRA: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF5_ADDRB: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF5_WEA: std_logic := '0';
signal IBUF5_WEB: std_logic := '0';
signal IBUF5_DIA: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF5_DIB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF5_DOB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal DATA_Y2: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal ERASURE_Y2: std_logic := '0';

-- parity W2 input buffer
signal IBUF6_ADDRA: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF6_ADDRB: unsigned(WPTR1'left downto 0) := (others => '0'); 
signal IBUF6_WEA: std_logic := '0';
signal IBUF6_WEB: std_logic := '0';
signal IBUF6_DIA: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF6_DIB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal IBUF6_DOB: std_logic_vector(NQBITS downto 0) := (others => '0');
signal DATA_W2: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal ERASURE_W2: std_logic := '0';

--//-- A PRIORI LLR BUFFERS --------------------------------------------------
-- Best performance, lowest FPGA clock frequency
--constant NLLRBITS: integer := 11; -- limiting LLR and BM range to small positive / slightly less than -511
--constant NFEWERBITS: integer := 0;	-- tradeoff performance vs speed
--constant NSMBITS: integer := 16; -- Verified with Matlab (worst-case: noiseless, N=4*254 symbols, 15 iterations)
	-- maximum range 28 (4 bits, -+ 7 symmetrical) * Nmax  (perfect match) + 1 extra bit for some negative cells
	-- Example: Nmax = 254*4, 4-soft quantized bits => max range is slight negative to +28448 => 16 bits


-- better speed, slightly degraded decoding performance
constant NLLRBITS: integer := 9; -- limiting LLR and BM range to small positive / slightly less than -127
constant NFEWERBITS: integer := 2;	-- tradeoff performance vs speed
constant NSMBITS: integer := 14; -- Verified with Matlab (worst-case: noiseless, N=4*254 symbols, 15 iterations)


signal LLR_IN: std_logic_vector(4*NLLRBITS-1 downto 0) := (others => '0');
signal LLR_WEA: std_logic := '0';
signal ADDRA7: std_logic_vector(WPTR1'left downto 0) := (others => '0'); 
signal ADDRB7: std_logic_vector(WPTR1'left downto 0) := (others => '0'); 
signal DOB8: std_logic_vector(4*NLLRBITS-1 downto 0) := (others => '0');
signal LLR_OUT: std_logic_vector(4*NLLRBITS-1 downto 0) := (others => '0');
signal LLR_OUT2: std_logic_vector(4*NLLRBITS-1 downto 0) := (others => '0');
signal LLR00_OUT: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
signal LLR01_OUT: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
signal LLR10_OUT: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
signal LLR11_OUT: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');

--//-- SEQUENCERS --------------------------------------------------
signal EVENT1: std_logic := '0';
signal EVENT4: std_logic := '0';
signal DECSTATE: integer range 0 to 5 := 0; 
signal DECSTATE1: std_logic := '0';
signal DECSTATE1_D: std_logic := '0';
signal DECSTATE1_D2: std_logic := '0';
signal DECSTATE1_D3: std_logic := '0';
signal DECSTATE1_D4: std_logic := '0';
signal DECSTATE3: std_logic := '0';
signal DECSTATE3_D: std_logic := '0';
signal DECSTATE3_D2: std_logic := '0';
signal DECSTATE3_D3: std_logic := '0';
signal DECSTATE3_D4: std_logic := '0';
signal DECSTATE3_D5: std_logic := '0';
signal DECSTATE3_D6: std_logic := '0';
signal DECSTATE3_D7: std_logic := '0';
signal SCNTR: unsigned(WPTR1'left downto 0) := (others => '0');  
signal ITER: unsigned(3 downto 0) := (others => '0');	-- iteration index
signal ITER_INC: unsigned(3 downto 0) := (others => '0');	

--//-- SEQUENCER --------------------------------------------------
signal RPTR1: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_D: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_D2: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_D3: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_D4: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_D5: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_D6: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR2: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR2_D: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR2_D2: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED_D: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED_D2: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED_D3: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED_D4: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED_D5: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal RPTR1_INV_PERMUTATED_D6: std_logic_vector(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.

--//-- BRANCH METRICS --------------------------------------------------------
signal BM_A: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal BM_B: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal BM_Y: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal BM_W: std_logic_vector((NQBITS-1) downto 0) := (others => '0');
signal BM_ERASURE_Y: std_logic := '0';
signal BM_ERASURE_W: std_logic := '0';
signal BM_LLR00: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
signal BM_LLR01: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
signal BM_LLR10: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
signal BM_LLR11: std_logic_vector(NLLRBITS-1 downto 0) := (others => '0');
type BRANCH_METRICS_k is array (15 downto 0) of std_logic_vector(NLLRBITS-1 downto 0);
type BRANCH_METRICS_plus is array (15 downto 0) of std_logic_vector(NLLRBITS downto 0);
signal BM: BRANCH_METRICS_k := (others => (others => '0'));
signal BMF: BRANCH_METRICS_k := (others => (others => '0'));
signal BMB: BRANCH_METRICS_k := (others => (others => '0'));
signal BMF_INPUT_VALID: std_logic := '0'; 
signal BMF_INPUT_VALID_D: std_logic := '0'; 
signal BM_GEN_IN_VALID: std_logic := '0'; 
signal BM_MIN: signed(NLLRBITS-1 downto 0) := (others => '0');
signal BM_MAX: signed(NLLRBITS-1 downto 0) := (others => '0');

--//-- FORWARD STATE METRICS --------------------------------------------------------
type STATE_METRICS_k is array (15 downto 0) of std_logic_vector((NSMBITS-1) downto 0);
type STATE_METRICS_kU is array (15 downto 0) of unsigned((NSMBITS-1) downto 0);
	-- 16 states at time k
signal A_IN: STATE_METRICS_k := (others => (others => '0'));
signal A0NEXT: STATE_METRICS_ku := (others => (others => '0'));
signal A0NEXT2: STATE_METRICS_ku := (others => (others => '0'));
signal A_IN_MASK: STATE_METRICS_k := (others => (others => '0'));
signal A_OUT: STATE_METRICS_k := (others => (others => '0'));
signal A0_CNTR: unsigned(5 downto 0) := (others => '0');
signal A0MAX: unsigned((NSMBITS-1) downto 0) := (others => '0');

-- backward state metrics
signal BMB_INPUT_VALID: std_logic := '0';
signal B_IN: STATE_METRICS_k := (others => (others => '0'));
signal B_IN_MASK: STATE_METRICS_k := (others => (others => '0'));
signal B_OUT: STATE_METRICS_k := (others => (others => '0'));
signal B_OUT_D: BRANCH_METRICS_plus := (others => (others => '0'));	-- reduced precision (NLLRBITS)
signal B_OUT2: STATE_METRICS_k := (others => (others => '0'));
signal B_OUT3: STATE_METRICS_k := (others => (others => '0'));
signal BSM_WEA: std_logic := '0';
signal BSM_WEA_D: std_logic := '0';
signal BSM_ADDRA: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal BSM_ADDRA_D: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 4000 symbols/frame.
signal BSM_ADDRB: unsigned(WPTR1'left downto 0) := (others => '0');  -- up to 1000 information BYTES = 8000 bits/frame.

--//-- A POSTERIORI LOG LIKELIHOOD --------------------------------------------------------
signal LLR_INPUT_VALID: std_logic := '0';
signal LLR1_B_IN: STATE_METRICS_k := (others => (others => '0'));

--//-- CRC CHECK ----------------------------
signal DATA1b1_VALID: std_logic := '0';
signal CRC_CNTR: unsigned((NADDRBITS+1) downto 0) := (others => '0');
signal CRC_CNTR_INC: unsigned((NADDRBITS+1) downto 0) := (others => '0');
signal DATA8b1_VALID: std_logic := '0';
signal DATA8b1: std_logic_vector(7 downto 0) := x"00";
signal SOF8b1: std_logic := '0';
signal SOF8b1_FLAG: std_logic := '0';
signal CRC_SYNC_RESET: std_logic := '0';
signal CRC_IN: std_logic_vector(15 downto 0) := x"0000";
signal CRC_OUT: std_logic_vector(15 downto 0) := x"0000";
signal CRC_RDY: std_logic := '0';
signal DEC_PAYLOAD_FIELD: std_logic := '0';
signal FRAME_CNTR_local: unsigned(31 downto 0) := (others => '0');
signal FRAME_ERROR_CNTR_local: unsigned(31 downto 0) := (others => '0');

--//-- OUTPUT ELASTIC BUFFER ----------------------------
signal DATA_AB2: std_logic_vector(1 downto 0) := (others => '0');
signal DATA_AB2x: std_logic_vector(3 downto 0) := (others => '0');
signal DATA_OUTx: std_logic_vector(3 downto 0) := (others => '0');
signal OB_WEA: std_logic := '0';
signal OB_WEA_D: std_logic := '0';
signal OB_ADDRA: unsigned(WPTR1'length downto 0) := (others => '0');
signal OB_ADDRA_CONFIRMED: unsigned(WPTR1'length downto 0) := (others => '0');
signal OB_ADDRB: unsigned(WPTR1'length downto 0) := (others => '0');
signal OB_SIZE: unsigned(WPTR1'length downto 0) := (others => '0');
signal OB_FULL: std_logic := '0';
signal DATA_OUT_VALID_local: std_logic := '0';
signal DATA_OUT_VALID_E: std_logic := '0';
signal OB_CNTR: unsigned((NADDRBITS+1) downto 0) := (others => '0');
signal OB_CNTR_INC: unsigned((NADDRBITS+1) downto 0) := (others => '0');

--//-- SIM2OUTFILE ---------------------------
signal S2OF1_DATA1: std_logic_vector(11 downto 0) := (others => '0');
signal S2OF1_DATA2: std_logic_vector(11 downto 0) := (others => '0');
signal S2OF1_DATA3: std_logic_vector(11 downto 0) := (others => '0');
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

-- to prevent saturation, eliminate the most negative value (-8 turns into -7 for a 4-bit soft-quantized input)
DATA_A_IN_SYM(3 downto 1) <= DATA_A_IN(3 downto 1);
DATA_A_IN_SYM(0) <= '1' when (DATA_A_IN(3 downto 1) = "100") else DATA_A_IN(0);
DATA_B_IN_SYM(3 downto 1) <= DATA_B_IN(3 downto 1);
DATA_B_IN_SYM(0) <= '1' when (DATA_B_IN(3 downto 1) = "100") else DATA_B_IN(0);



--//-- PERMUTATION INDICES ---------------------------------------
PT_BURST_BYTE_SIZE(NADDRBITS-1 downto 0) <= std_logic_vector(N(N'left downto 2));	-- includes CRC when enabled
PT_BURST_BYTE_SIZE(PT_BURST_BYTE_SIZE'left downto NADDRBITS) <= (others => '0');
PT_JPERM_IN(NADDRBITS+1 downto 0) <= std_logic_vector(RPTR1);
PERMUTATION_TABLE_001: PERMUTATION_TABLE PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	BURST_PAYLOAD_SIZE => PT_BURST_BYTE_SIZE,
	R1 => "000",	-- unused here
	R2 => "0000",	-- unused here
	P => P,
	Q0 => Q0,
	Q1 => Q1,
	Q2 => Q2,
	Q3 => Q3,
	J_IN => (others => '0'),	-- TODO COULD BE USED TO LOOK UP RPTR1_INV_PERMUTATED_D6. NO NEED FOR  PERM DIRECTION
	JPERM_OUT => open,
	JPERM_IN => PT_JPERM_IN,
	J_OUT => PT_J_OUT,	-- 1 CLK latency after RPTR1. Aligned with DECSTATE1_D or DECSTATE3_D
	TABLE_READY => open
);
RPTR1_INV_PERMUTATED <= PT_J_OUT(NADDRBITS+1 downto 0);

-- need up to 5 delays for RPTR1_INV_PERMUTATED	
PERMUTATION_TABLE_002: process(CLK)
begin
	if rising_edge(CLK) then
		RPTR1_INV_PERMUTATED_D <= RPTR1_INV_PERMUTATED;
		RPTR1_INV_PERMUTATED_D2 <= RPTR1_INV_PERMUTATED_D;
		RPTR1_INV_PERMUTATED_D3 <= RPTR1_INV_PERMUTATED_D2;
		RPTR1_INV_PERMUTATED_D4 <= RPTR1_INV_PERMUTATED_D3;
		RPTR1_INV_PERMUTATED_D5 <= RPTR1_INV_PERMUTATED_D4;
		RPTR1_INV_PERMUTATED_D6 <= RPTR1_INV_PERMUTATED_D5;
	end if;
end process;

-- Input state machine

-- number of N couples (K/2), includes optional CRC16 field appended after the payload. 
N0(WPTR1'left downto 2) <= unsigned(BURST_PAYLOAD_SIZE); 	-- no CRC16
N0(1 downto 0) <= "00"; 	
N_GEN: process(N0, CRC16_EN)
begin
	if(CRC16_EN = '0') then
		N <= N0;	-- no CRC16 
	else
		N <= N0 + to_unsigned(8,N'length);	-- CRC16 appended after payload.
	end if;
end process;


WPTR1_INC <= WPTR1 + 1;
INPUT_STATE_001: process(CLK)
begin
	if rising_edge(CLK) then
		INPUT_STATE_D <= INPUT_STATE;
		
		if(SYNC_RESET = '1') then
			INPUT_STATE <= 0;
			WPTR1 <= (others => '0');
		elsif(SOF_IN = '1') then
			INPUT_STATE <= 1;
			WPTR1 <= (0 => '1', others => '0');
		elsif(DATA_IN_VALID = '1') and (INPUT_STATE = 1) then
			if(WPTR1_INC = N) then
				-- last systematic double input bits in a frame, next double input bits are (punctured) parity bits
				INPUT_STATE <= 2;
				WPTR1 <= (others => '0');
			else
				WPTR1 <= WPTR1_INC;
			end if;
		elsif(DATA_IN_VALID = '1') and (INPUT_STATE = 2) then
			if(WPTR1_INC = N_PARITY_BITS(N_PARITY_BITS'left downto 1)) then
				-- got all the Y1,W1 punctured parity bits
				INPUT_STATE <= 3;
				WPTR1 <= (others => '0');
			else
				WPTR1 <= WPTR1_INC;
			end if;
		elsif(DATA_IN_VALID = '1') and (INPUT_STATE = 3) then
			if(WPTR1_INC = N_PARITY_BITS(N_PARITY_BITS'left downto 1)) then
				-- got all the Y2,W2 punctured parity bits
				INPUT_STATE <= 0;
				WPTR1 <= (others => '0');
			else
				WPTR1 <= WPTR1_INC;
			end if;
		
		end if;
	end if;
end process;

NEW_SYSTEMATIC_INPUT_PAIR <=  DATA_IN_VALID when (INPUT_STATE = 1) else
										SOF_IN;

INPUT_DELAY_001: process(CLK)
begin
	if rising_edge(CLK) then
		DATA_IN_VALID_D <= DATA_IN_VALID;
		DATA_A_IN_D <= DATA_A_IN_SYM;
		DATA_B_IN_D <= DATA_B_IN_SYM;
		EOF_IN_D <= EOF_IN;
	end if;
end process;

-- depuncturing
-- While receiving the systematic bits AB, compute the write addresses for the follow on
-- punctured bits
Y_DEPUNCTURING_001: process(CLK) -- *091316
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (INPUT_STATE = 2) then
			Y_PUNCTURING <= "0" & Y_PUNCTURING_PATTERN;  -- one spare bit on the left to prevent syntax error below
		elsif(NEW_SYSTEMATIC_INPUT_PAIR = '1') then
			-- shift right
			for I in 0 to 27 loop
				if(I = (unsigned(Y_PUNCTURING_PERIOD)-1)) then
					Y_PUNCTURING(I) <= Y_PUNCTURING(0);
				else
					Y_PUNCTURING(I) <= Y_PUNCTURING(I+1);
				end if;
			end loop;
		end if;
	end if;
end process;


-- reconstruct the time k (PTR_PUNCTURING) and the parity type (Y1 vs W1) before puncturing
-- and save it into a dual port RAM
Y_DEPUNCTURING_002: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (INPUT_STATE = 2) then
			PTR_PUNCTURING <= (others => '0');
			PUNCTURING_TBL_WPTR <= (others => '0');
		elsif(NEW_SYSTEMATIC_INPUT_PAIR = '1') then
			PTR_PUNCTURING <= PTR_PUNCTURING + 1;
			if(Y_PUNCTURING(0) = '1') and (W_PUNCTURING_PATTERN = '0') then
				-- no W1 parity bits, only Y1
				PUNCTURING_TBL_WPTR <= PUNCTURING_TBL_WPTR + 1;
			elsif(Y_PUNCTURING(0) = '1') and (W_PUNCTURING_PATTERN = '1') then
				-- both Y1 & W1 parity bits
				PUNCTURING_TBL_WPTR <= PUNCTURING_TBL_WPTR + 2;
			elsif(Y_PUNCTURING(0) = '0') and (W_PUNCTURING_PATTERN = '1') then
				-- no Y1 parity bits, only W1
				PUNCTURING_TBL_WPTR <= PUNCTURING_TBL_WPTR + 1;
			end if;
		end if;
	end if;
end process;

-- count the number of Y1,W1 parity bits (after puncturing). Excludes Y2,W2.
Y_DEPUNCTURING_003: process(CLK) 
begin
	if rising_edge(CLK) then
		if(INPUT_STATE = 2) and (INPUT_STATE_D = 1) then
			N_PARITY_BITS <= PUNCTURING_TBL_WPTR;
		end if;
	end if;
end process;

PUNCTURING_TBL_DIA(WPTR1'length-1 downto 0) <= PTR_PUNCTURING;	-- associated symbol #
PUNCTURING_TBL_DIA(WPTR1'length) <= not Y_PUNCTURING(0);	-- select bit: Y1(0) or W1(1)   
PUNCTURING_TBL_DIB(WPTR1'length-1 downto 0) <= PTR_PUNCTURING;
PUNCTURING_TBL_DIB(WPTR1'length) <= W_PUNCTURING_PATTERN;	-- select bit: Y1(0) or W1(1) 
PUNCTURING_TBL_WEA <= NEW_SYSTEMATIC_INPUT_PAIR and (Y_PUNCTURING(0) or W_PUNCTURING_PATTERN);
PUNCTURING_TBL_WEB <= NEW_SYSTEMATIC_INPUT_PAIR and Y_PUNCTURING(0) and W_PUNCTURING_PATTERN;
	-- write two successive locations
PUNCTURING_TBL_ADDRA <= PUNCTURING_TBL_WPTR when (INPUT_STATE < 2) else (PUNCTURING_TBL_RPTR);
PUNCTURING_TBL_ADDRB <= PUNCTURING_TBL_WPTR+1 when (INPUT_STATE < 2) else (PUNCTURING_TBL_RPTR+1);
	-- we write to one side or both and read from both sides of the DPRAM

Y_DEPUNCTURING_004: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => WPTR1'length+1,		
	ADDR_WIDTHA => WPTR1'length+1,	
	DATA_WIDTHB => WPTR1'length+1,		 
	ADDR_WIDTHB => WPTR1'length+1
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => PUNCTURING_TBL_WEA,      
	ADDRA => std_logic_vector(PUNCTURING_TBL_ADDRA),  
	DIA => std_logic_vector(PUNCTURING_TBL_DIA),      
	OEA => '1',
	DOA => PTR_PUNCTURING2A,
	CLKB => CLK,
	CSB => '1',
	WEB => PUNCTURING_TBL_WEB,
	ADDRB => std_logic_vector(PUNCTURING_TBL_ADDRB),  
	DIB => std_logic_vector(PUNCTURING_TBL_DIB),           
	OEB => '1',
	DOB => PTR_PUNCTURING2B      
);


Y_DEPUNCTURING_005: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (INPUT_STATE = 0) then
			PUNCTURING_TBL_RPTR <= (others => '0');
		elsif(DATA_IN_VALID = '1') and ((INPUT_STATE = 2) or (INPUT_STATE = 3)) then
			-- receiving 2 parity bits 
			if(WPTR1_INC = N_PARITY_BITS(N_PARITY_BITS'left downto 1)) then
				-- end of state 2 or 3. Rewind
				PUNCTURING_TBL_RPTR <= (others => '0');
			else
				PUNCTURING_TBL_RPTR <= PUNCTURING_TBL_RPTR + 2;
			end if;
		end if;
	end if;
end process;

-- 5 block RAMs to store a complete input frame (A,B storage is combined)
-- A,B,Y1,W1,Y2,W2
-- MSb is '0' when valid, '1' in the case of erasure
DATA_IN <= DATA_A_IN_SYM & DATA_B_IN_SYM;

IBUF1_ADDRA <= std_logic_vector(WPTR1) when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else RPTR1_INV_PERMUTATED;

-- flip AB output when reading the permutated AB bits
FLIP_AB_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		FLIP_IBUF12_AB <= RPTR1_INV_PERMUTATED(0);
	end if;
end process;

-- systematic data input buffer
-- simultaneously reading in natural and PERMUTATED order
INPUT_BUF_001: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => 2*NQBITS,		
	ADDR_WIDTHA => WPTR1'length,	
	DATA_WIDTHB => 2*NQBITS,		 
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => NEW_SYSTEMATIC_INPUT_PAIR,      
	ADDRA => IBUF1_ADDRA,  
	DIA => DATA_IN,      
	OEA => '1',
	DOA => DATA_AB_PERMUTATED,
	CLKB => CLK,
	CSB => '1',
	WEB => '0',
	ADDRB => std_logic_vector(RPTR1),  
	DIB => (others => '0'),      
	OEB => '1',
	DOB => DATA_AB      
);
DATA_A <= DATA_AB(2*NQBITS-1 downto NQBITS);
DATA_B <= DATA_AB(NQBITS-1 downto 0);

DATA_A_PERMUTATED <= DATA_AB_PERMUTATED(2*NQBITS-1 downto NQBITS) when (FLIP_IBUF12_AB = '0') else 
							DATA_AB_PERMUTATED(NQBITS-1 downto 0);
DATA_B_PERMUTATED <= DATA_AB_PERMUTATED(2*NQBITS-1 downto NQBITS) when (FLIP_IBUF12_AB = '1') else 
							DATA_AB_PERMUTATED(NQBITS-1 downto 0);


-- parity Y1 input buffer
-- clearing during systematic bits inputs (INPUT_STATEs 0 and 1)
IBUF3_ADDRA <= WPTR1 when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else unsigned(PTR_PUNCTURING2A((WPTR1'length-1) downto 0));
IBUF3_ADDRB <= RPTR1 when (DECSTATE = 1) or (DECSTATE = 3) else unsigned(PTR_PUNCTURING2B((WPTR1'length-1) downto 0));

IBUF3_WEA <= 	'1' when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else	-- clearing contents
			DATA_IN_VALID_D and (not PTR_PUNCTURING2A(WPTR1'length)) when (INPUT_STATE_D = 2) else	-- saving Y1 parity bit at time k=WPTR1
			'0';
			
IBUF3_WEB <= DATA_IN_VALID_D and (not PTR_PUNCTURING2B(WPTR1'length)) when (INPUT_STATE_D = 2) else	-- saving Y1 parity bit at time k=WPTR1
			'0';
	-- write two Y input bits 

IBUF3_DIA <= (NQBITS => '1', others => '0') when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else	-- Clear all previous entries (while receiving systematic input bits)
				('0' & DATA_A_IN_D);
IBUF3_DIB <= ('0' & DATA_B_IN_D);

INPUT_BUF_003B: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => NQBITS+1,	-- 1 extra bit for erasure (MSb)		
	ADDR_WIDTHA => WPTR1'length,	
	DATA_WIDTHB => NQBITS+1,	-- 1 extra bit for erasure (MSb)	 
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => IBUF3_WEA,      
	ADDRA => std_logic_vector(IBUF3_ADDRA),  
	DIA => IBUF3_DIA,      
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => IBUF3_WEB,
	ADDRB => std_logic_vector(IBUF3_ADDRB),  
	DIB => IBUF3_DIB,      
	OEB => '1',
	DOB => IBUF3_DOB     
);
DATA_Y1 <= IBUF3_DOB((NQBITS-1) downto 0);
ERASURE_Y1 <= IBUF3_DOB(NQBITS);

-- parity W1 input buffer
-- clearing during systematic bits inputs (INPUT_STATEs 0 and 1)
IBUF4_ADDRA <= WPTR1 when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else unsigned(PTR_PUNCTURING2A((WPTR1'length-1) downto 0));
IBUF4_ADDRB <= RPTR1 when (ITER(0) = '0') and ((DECSTATE = 1) or (DECSTATE = 3)) else unsigned(PTR_PUNCTURING2B((WPTR1'length-1) downto 0));

IBUF4_WEA <= 	'1' when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else	-- clearing contents
			DATA_IN_VALID_D and PTR_PUNCTURING2A(WPTR1'length) when (INPUT_STATE_D = 2) else	-- saving W1 parity bit at time k=WPTR1
			'0';
			
IBUF4_WEB <= DATA_IN_VALID_D and PTR_PUNCTURING2B(WPTR1'length) when (INPUT_STATE_D = 2) else	-- saving W1 parity bit at time k=WPTR1
			'0';
	-- write two W input bits 

IBUF4_DIA <= (NQBITS => '1', others => '0') when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else		-- Clear all previous entries (while receiving systematic input bits)
				('0' & DATA_A_IN_D);
IBUF4_DIB <= ('0' & DATA_B_IN_D);

INPUT_BUF_004: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => NQBITS+1,	-- 1 extra bit for erasure (MSb)		
	ADDR_WIDTHA => WPTR1'length,	
	DATA_WIDTHB => NQBITS+1,	-- 1 extra bit for erasure (MSb)	 
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => IBUF4_WEA,      
	ADDRA => std_logic_vector(IBUF4_ADDRA),  
	DIA => IBUF4_DIA,      
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => IBUF4_WEB,
	ADDRB => std_logic_vector(IBUF4_ADDRB),  
	DIB => IBUF4_DIB,      
	OEB => '1',
	DOB => IBUF4_DOB     
);
DATA_W1 <= IBUF4_DOB((NQBITS-1) downto 0);
ERASURE_W1 <= IBUF4_DOB(NQBITS);

-- parity Y2 input buffer
-- clearing during systematic bits inputs (INPUT_STATEs 0 and 1)
IBUF5_ADDRA <= WPTR1 when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else unsigned(PTR_PUNCTURING2A((WPTR1'length-1) downto 0));
IBUF5_ADDRB <= RPTR1_D when (ITER(0) = '1') and ((DECSTATE1_D = '1') or (DECSTATE3_D = '1')) else unsigned(PTR_PUNCTURING2B((WPTR1'length-1) downto 0));

IBUF5_WEA <= 	'1' when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else	-- clearing contents
			DATA_IN_VALID_D and (not PTR_PUNCTURING2A(WPTR1'length)) when (INPUT_STATE_D = 3) else	-- saving Y2 parity bit at time k=WPTR1
			'0';
			
IBUF5_WEB <= DATA_IN_VALID_D and (not PTR_PUNCTURING2B(WPTR1'length)) when (INPUT_STATE_D = 3) else  -- saving Y2 parity bit at time k=WPTR1
				'0';	

IBUF5_DIA <= (NQBITS => '1', others => '0') when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else		-- Clear all previous entries (while receiving systematic input bits)
				('0' & DATA_A_IN_D);
IBUF5_DIB <= ('0' & DATA_B_IN_D);

INPUT_BUF_005: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => NQBITS+1,	-- 1 extra bit for erasure (MSb)		
	ADDR_WIDTHA => WPTR1'length,	
	DATA_WIDTHB => NQBITS+1,	-- 1 extra bit for erasure (MSb)	 
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => IBUF5_WEA,      
	ADDRA => std_logic_vector(IBUF5_ADDRA),  
	DIA => IBUF5_DIA,      
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => IBUF5_WEB,
	ADDRB => std_logic_vector(IBUF5_ADDRB),  
	DIB => IBUF5_DIB,      
	OEB => '1',
	DOB => IBUF5_DOB     
);
DATA_Y2 <= IBUF5_DOB((NQBITS-1) downto 0);
ERASURE_Y2 <= IBUF5_DOB(NQBITS);

-- parity W2 input buffer
-- clearing during systematic bits inputs (INPUT_STATEs 0 and 1)
IBUF6_ADDRA <= WPTR1 when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else unsigned(PTR_PUNCTURING2A((WPTR1'length-1) downto 0));
IBUF6_ADDRB <=  RPTR1_D when (ITER(0) = '1') and ((DECSTATE1_D = '1') or (DECSTATE3_D = '1')) else unsigned(PTR_PUNCTURING2B((WPTR1'length-1) downto 0));

IBUF6_WEA <= 	'1' when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else	-- clearing contents
			DATA_IN_VALID_D and PTR_PUNCTURING2A(WPTR1'length) when (INPUT_STATE_D = 3) else	-- saving W2 parity bit at time k=WPTR1
			'0';
			
IBUF6_WEB <= DATA_IN_VALID_D and PTR_PUNCTURING2B(WPTR1'length) when (INPUT_STATE_D = 3) else -- saving W2 parity bit at time k=WPTR1
			'0';
	-- write two W input bits 

IBUF6_DIA <= (NQBITS => '1', others => '0') when (NEW_SYSTEMATIC_INPUT_PAIR = '1') else		-- Clear all previous entries (while receiving systematic input bits)
				('0' & DATA_A_IN_D);
IBUF6_DIB <= ('0' & DATA_B_IN_D);

INPUT_BUF_006: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => NQBITS+1,	-- 1 extra bit for erasure (MSb)		
	ADDR_WIDTHA => WPTR1'length,	
	DATA_WIDTHB => NQBITS+1,	-- 1 extra bit for erasure (MSb)	 
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => IBUF6_WEA,      
	ADDRA => std_logic_vector(IBUF6_ADDRA),  
	DIA => IBUF6_DIA,      
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => IBUF6_WEB,
	ADDRB => std_logic_vector(IBUF6_ADDRB),  
	DIB => IBUF6_DIB,      
	OEB => '1',
	DOB => IBUF6_DOB     
);
DATA_W2 <= IBUF6_DOB((NQBITS-1) downto 0);
ERASURE_W2 <= IBUF6_DOB(NQBITS);

--//-- A PRIORI LLR BUFFERS --------------------------------------------------
-- LLR from/to SISO1/2 or SISO2/1  
-- In-place (no Ping/pong buffer)
-- Stored in the natural order
--ADDRA7 <= 	std_logic_vector(RPTR1_D6) when ITER(0) = '0' else   -- SISO1 
--				RPTR1_INV_PERMUTATED_D6;		-- SISO2 rearrange permutated LLR to natural order 
--	-- write pointer. 

ADDRB7 <= 	std_logic_vector(RPTR1) when ITER(0) = '0' else   -- SISO1 
				RPTR1_INV_PERMUTATED;		-- SISO2 rearrange permutated LLR to natural order  
	-- read pointer. natural order, index k

LLR_BUF_007A: process(CLK)
begin
	if rising_edge(CLK) then
		if(ITER(0) = '0') then
			-- SISO1
			ADDRA7 <= std_logic_vector(RPTR1_D6);
			LLR_WEA <= DECSTATE3_D6;
		else
			-- SISO2 rearrange permutated LLR to natural order 
			ADDRA7 <= RPTR1_INV_PERMUTATED_D6;
			LLR_WEA <= DECSTATE3_D7;
		end if;
	end if;
end process;


-- flip A/B bits back to natural order
LLR_OUT2(NLLRBITS-1 downto 0) <= LLR_OUT(NLLRBITS-1 downto 0);
LLR_OUT2(2*NLLRBITS-1 downto NLLRBITS) <= 
				LLR_OUT(2*NLLRBITS-1 downto NLLRBITS) when (ADDRA7(0) = '0') or (ITER(0) = '0') else
				LLR_OUT(3*NLLRBITS-1 downto 2*NLLRBITS);
LLR_OUT2(3*NLLRBITS-1 downto 2*NLLRBITS) <= 
				LLR_OUT(3*NLLRBITS-1 downto 2*NLLRBITS) when (ADDRA7(0) = '0') or (ITER(0) = '0')else
				LLR_OUT(2*NLLRBITS-1 downto NLLRBITS);
LLR_OUT2(4*NLLRBITS-1 downto 3*NLLRBITS) <= LLR_OUT(4*NLLRBITS-1 downto 3*NLLRBITS);

--LLR_WEA <= DECSTATE3_D6 when (ITER(0) = '0') else DECSTATE3_D7;
LLR_BUF_007: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => 4*NLLRBITS,	-- 4 data hypothesis (AB bits) at each symbol sample k	
	ADDR_WIDTHA => WPTR1'length,	-- symbol sample k, double ping-pong buffers
	DATA_WIDTHB => 4*NLLRBITS,	
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => LLR_WEA,      
	ADDRA => ADDRA7,  
	DIA => LLR_OUT2,   -- from SISO1 or SISO2   	
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => '0',
	ADDRB => ADDRB7,  
	DIB => (others => '0'),      
	OEB => '1',
	DOB => LLR_IN 	-- to SISO1 or SISO2
);


--//-- SEQUENCERS --------------------------------------------------
-- One sequencer. SISO1 and SISO2 are concurrent.

EVENT1 <= EOF_IN_D;
		-- we collected a complete set of systematic data AB and Y1,W1, Y2,W2 parity bits
		-- could start SISO2

EVENT4 <= '1' when (DECSTATE = 4) and  (SCNTR = 0) and (ITER_INC = unsigned(N_ITER)) else '0';
	-- end of decoding. Decoder is now ready for next input frame


ITER_INC <= ITER + 1;

DECSTATE_GEN:  process(CLK)
begin
	if rising_edge(CLK) then
		DECSTATE1_D <= DECSTATE1;
		DECSTATE1_D2 <= DECSTATE1_D;
		DECSTATE1_D3 <= DECSTATE1_D2;
		DECSTATE1_D4 <= DECSTATE1_D3;
		DECSTATE3_D <= DECSTATE3;
		DECSTATE3_D2 <= DECSTATE3_D;
		DECSTATE3_D3 <= DECSTATE3_D2;
		DECSTATE3_D4 <= DECSTATE3_D3;
		DECSTATE3_D5 <= DECSTATE3_D4;
		DECSTATE3_D6 <= DECSTATE3_D5;
		DECSTATE3_D7 <= DECSTATE3_D6;
		
		if(SYNC_RESET = '1') then
			DECSTATE <= 0;	-- idle state
			SCNTR <= (others => '0');	-- state timer/counter
			ITER <= (others => '0');
		elsif(DECSTATE = 0) and (EVENT1 = '1') then
			-- we collected a complete set of systematic data AB and Y1,W1,Y2,W3 parity bits
			-- start backward state metrics computation for SISO1 (even iteration) or SISO2 (odd iteration) 
			DECSTATE <= 1;
			SCNTR <= resize(N,SCNTR'length)-1;
		elsif(DECSTATE = 1) and  (SCNTR = 0) then
			-- completed backward state metrics computation 
			-- gap state
			DECSTATE <= 2;
			SCNTR <= to_unsigned(3,SCNTR'length);
		elsif(DECSTATE = 2) and  (SCNTR = 0) then
			-- end of gap
			-- start forward state metrics computation for SISO1 (even iteration) or SISO2 (odd iteration) 
			DECSTATE <= 3;
			SCNTR <= resize(N,SCNTR'length)-1;
		elsif(DECSTATE = 3) and  (SCNTR = 0) then
			-- completed forward state metrics computation
			-- gap state
			DECSTATE <= 4;
			SCNTR <= to_unsigned(6,SCNTR'length);
		elsif(DECSTATE = 4) and  (SCNTR = 0) then
			-- 1 iteration done. Do one more?  
			if(ITER_INC = unsigned(N_ITER)) then
				-- completed the user-specified number of iterations
				-- back to idle
				DECSTATE <= 0;
				ITER <= (others => '0');
			elsif(OB_FULL = '0') or (ITER_INC = unsigned(N_ITER) -1) then
				-- one more iteration.. unless it is the last iteration and the output buffer is full
				ITER <= ITER_INC;
				DECSTATE <= 1;
				SCNTR <= resize(N,SCNTR'length)-1;
			end if;
		elsif(SCNTR /= 0) then
			SCNTR <= SCNTR -1;
		end if;
	end if;
end process;
DECSTATE1 <= '1' when (DECSTATE = 1) else '0';
DECSTATE3 <= '1' when (DECSTATE = 3) else '0';

RPTR1_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		RPTR1_D <= RPTR1;
		RPTR1_D2 <= RPTR1_D;
		RPTR1_D3 <= RPTR1_D2;
		RPTR1_D4 <= RPTR1_D3;
		RPTR1_D5 <= RPTR1_D4;
		RPTR1_D6 <= RPTR1_D5;
		
		if(SYNC_RESET = '1') then
			RPTR1 <= (others => '0');
		elsif(DECSTATE = 0) and (EVENT1 = '1') then
			RPTR1 <= N - 1;
		elsif(DECSTATE = 1) and (SCNTR /= 0) then
			-- backward state metrics computation
			RPTR1 <= RPTR1 - 1;
		elsif(DECSTATE = 3) and (SCNTR /= 0) then
			-- forward state metrics computation
			RPTR1 <= RPTR1 + 1;
		end if;
	end if;
end process;

-- input flow control
INPUT_CTS_GEN: process(CLK) 
begin	
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			CTS_OUT_local <= '1';
		elsif(EOF_IN = '1') then
			-- complete frame in. Wait for end of frame decoding.
			CTS_OUT_local <= '0';
		elsif(EVENT4 = '1') then
			-- completed decoding. Resume decoder input flow
			CTS_OUT_local <= '1';
		end if;
	end if;
end process;
CTS_OUT <= CTS_OUT_local and (not EOF_IN);			

--//-- BRANCH METRICS --------------------------------------------------------
-- compute the branch metric in real time at each time k. Includes adding the apriori LLR from previous SISO.


-- SISO1/SISO2 branch metrics alternating at each iteration, 
-- used twice for each iteration: first backwards, then forwards.
-- Inputs aligned with DECSTATEx_D (SISO1) or DECSTATEx_D2 (SISO2)
-- Outputs aligned with DECSTATEx_D2 (SISO1) or DECSTATEx_D3 (SISO2)
-- Timing for SISO2 is one CLK later than for SISO1 branch metrics because of the 1 CLK latency in performing index permutation.
-- Latency 1 CLK
BM_GEN_IN_VALID <= 	(DECSTATE1_D or DECSTATE3_D) when (ITER(0) = '0') else	-- SISO1
							(DECSTATE1_D2 or DECSTATE3_D2);	-- SISO2

-- input data samples
BM_A <= DATA_A when (ITER(0) = '0') else DATA_A_PERMUTATED;
BM_B <= DATA_B when (ITER(0) = '0') else DATA_B_PERMUTATED;
BM_Y <= DATA_Y1 when (ITER(0) = '0') else DATA_Y2;
BM_W <= DATA_W1 when (ITER(0) = '0') else DATA_W2;
BM_ERASURE_Y <= ERASURE_Y1 when (ITER(0) = '0') else ERASURE_Y2;
BM_ERASURE_W <= ERASURE_W1 when (ITER(0) = '0') else ERASURE_W2;

-- LLR inputs
-- Null during first iteration. Flip 01/10 in SISO2 input when FLIP_IBUF12_AB = '1'
BM_LLR00 <= (others => '0') when (ITER = 0) else 
				LLR_IN(NLLRBITS-1 downto 0);
BM_LLR01 <= (others => '0') when (ITER = 0) else 
				LLR_IN(2*NLLRBITS-1 downto NLLRBITS) when (ITER(0) = '0') or (FLIP_IBUF12_AB = '0') else
				LLR_IN(3*NLLRBITS-1 downto 2*NLLRBITS);				
BM_LLR10 <= (others => '0') when (ITER = 0) else 
				LLR_IN(3*NLLRBITS-1 downto 2*NLLRBITS) when (ITER(0) = '0') or (FLIP_IBUF12_AB = '0') else
				LLR_IN(2*NLLRBITS-1 downto NLLRBITS);
BM_LLR11 <= (others => '0') when (ITER = 0) else 
				LLR_IN(4*NLLRBITS-1 downto 3*NLLRBITS);
BM_GEN_001: BM_GEN 
generic map(
	NQBITS => NQBITS,
	NLLRBITS => NLLRBITS,
	NFEWERBITS => NFEWERBITS
)
PORT MAP(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	A => BM_A,	
	B => BM_B,
	Y => BM_Y,
	W => BM_W,
	ERASURE_Y => BM_ERASURE_Y,
	ERASURE_W => BM_ERASURE_W,
	LLR00 => BM_LLR00,	-- apriori Lk(delta) for hypothesis AB
	LLR01 =>	BM_LLR01,	
	LLR10 =>	BM_LLR10,		
	LLR11 =>	BM_LLR11,		
	IN_VALID => BM_GEN_IN_VALID,
	BM0 => BM(0),
	BM1 => BM(1),	-- branch metrics for hypothesis ABYW = 1000 (for consistency with matlab turbo.m, A is LSb)
	BM2 => BM(2),
	BM3 => BM(3),
	BM4 => BM(4),
	BM5 => BM(5),
	BM6 => BM(6),
	BM7 => BM(7),
	BM8 => BM(8),
	BM9 => BM(9),
	BM10 => BM(10),
	BM11 => BM(11),
	BM12 => BM(12),
	BM13 => BM(13),
	BM14 => BM(14),
	BM15 => BM(15),
	OUT_VALID => open  -- Outputs aligned with DECSTATEx_D2 (SISO1) or DECSTATEx_D3 (SISO2)
);

-- during simulation, check for upper/lower bounds. Helps determine NLLRBITS
SIM_001: if (SIMULATION = '1') generate
	MIN_MAX_001: process(CLK)
	variable BM_MIN_VAR: signed(NLLRBITS-1 downto 0) := (NLLRBITS-1 => '0', others => '1');
	variable BM_MAX_VAR: signed(NLLRBITS-1 downto 0) := (NLLRBITS-1 => '1', others => '0');
	begin
		if rising_edge(CLK) then
			if (SYNC_RESET = '1') then
				BM_MIN <= (NLLRBITS-1 => '0', others => '1');
				BM_MAX <= (NLLRBITS-1 => '1', others => '0');
			elsif((ITER(0) = '0') and ((DECSTATE1_D2 = '1') or (DECSTATE3_D2 = '1'))) or
					((ITER(0) = '1') and ((DECSTATE1_D3 = '1') or (DECSTATE3_D3 = '1')))then
				for I in 0 to 15 loop
					BM_MIN_VAR := BM_MIN;
					if(signed(BM(I)) < BM_MIN_VAR) then
						BM_MIN_VAR := signed(BM(I));
					end if;
				end loop;
				BM_MIN <= BM_MIN_VAR;
				
				for I in 0 to 15 loop
					BM_MAX_VAR := BM_MAX;
					if(signed(BM(I)) > BM_MAX_VAR) then
						BM_MAX_VAR := signed(BM(I));
					end if;
				end loop;
				BM_MAX <= BM_MAX_VAR;
				
			end if;
		end if;
	end process;
end generate;					
		


--//-- FORWARD STATE METRICS --------------------------------------------------------
-- Time-shared between SISO1 and SISO2
-- Latency 1 CLK
-- Inputs aligned with DECSTATE3_D2 (SISO1) or DECSTATE3_D3 (SISO2)
-- Outputs aligned with DECSTATE3_D3 (SISO1) or DECSTATE3_D4 (SISO2)

A_IN_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if (BMF_INPUT_VALID = '1') then
			A_IN_MASK <= (others => (others => '1'));
		else
			A_IN_MASK <= (others => (others => '0'));
		end if;	
		
		if ((DECSTATE3_D = '1') and (ITER(0) = '0')) or ((DECSTATE3_D2 = '1') and (ITER(0) = '1')) then
			BMF_INPUT_VALID <= '1';
		else
			BMF_INPUT_VALID <= '0';
		end if;
	end if;
end process;

-- initialize first state metrics to zero (iter = 0), then  b(k+1)
A_IN_GEN_002: process(A_OUT, A_IN_MASK, BMF_INPUT_VALID, BMF_INPUT_VALID_D)
begin
	if(BMF_INPUT_VALID = '1') and (BMF_INPUT_VALID_D = '0') then
		-- initialize the next SISOx state metrics
      --  a(1,:) = alastSISOx; 
		for I in 0 to 15 loop
			A_IN(I) <= std_logic_vector(A0NEXT2(I));
		end loop;
	else
		for I in 0 to 15 loop
			A_IN(I) <= A_OUT(I) and A_IN_MASK(I);
		end loop;
	end if;
end process;

			
BMF <= BM when (BMF_INPUT_VALID = '1')  else
		(others => (others => '0'));
		
FORWARD_STATE_GEN_001: FORWARD_STATE_GEN
generic map(
	NLLRBITS => NLLRBITS,
	NSMBITS => NSMBITS
)
port map(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	A_IN0 => A_IN(0),
	A_IN1 => A_IN(1),
	A_IN2 => A_IN(2),
	A_IN3 => A_IN(3),
	A_IN4 => A_IN(4),
	A_IN5 => A_IN(5),
	A_IN6 => A_IN(6),
	A_IN7 => A_IN(7),
	A_IN8 => A_IN(8),
	A_IN9 => A_IN(9),
	A_IN10 => A_IN(10),
	A_IN11 => A_IN(11),
	A_IN12 => A_IN(12),
	A_IN13 => A_IN(13),
	A_IN14 => A_IN(14),
	A_IN15 => A_IN(15),
	BM0 => BMF(0),
	BM1 => BMF(1),
	BM2 => BMF(2),
	BM3 => BMF(3),
	BM4 => BMF(4),
	BM5 => BMF(5),
	BM6 => BMF(6),
	BM7 => BMF(7),
	BM8 => BMF(8),
	BM9 => BMF(9),
	BM10 => BMF(10),
	BM11 => BMF(11),
	BM12 => BMF(12),
	BM13 => BMF(13),
	BM14 => BMF(14),
	BM15 => BMF(15),
	A_OUT0 => A_OUT(0),
	A_OUT1 => A_OUT(1),
	A_OUT2 => A_OUT(2),
	A_OUT3 => A_OUT(3),
	A_OUT4 => A_OUT(4),
	A_OUT5 => A_OUT(5),
	A_OUT6 => A_OUT(6),
	A_OUT7 => A_OUT(7),
	A_OUT8 => A_OUT(8),
	A_OUT9 => A_OUT(9),
	A_OUT10 => A_OUT(10),
	A_OUT11 => A_OUT(11),
	A_OUT12 => A_OUT(12),
	A_OUT13 => A_OUT(13),
	A_OUT14 => A_OUT(14),
	A_OUT15 => A_OUT(15)
);

-- Because of the circular nature of the treillis, initialize the state
-- metrics for the next SISO1 or SISO2 iteration
-- Algorithm: upon completion of SISOx, save the last state and subtract the max
--  alastSISOx = a(N+1,:) - max(a(N+1,:));
--  a(1,:) = alastSISOx;
-- We can use a recursive loop as we have time during the follow-on SISOx iteration
A0NEXT_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		BMF_INPUT_VALID_D <= BMF_INPUT_VALID;
		
		if (BMF_INPUT_VALID = '0') and (BMF_INPUT_VALID_D = '1') then
			-- step 1: capture the last a(N+1,:)
			for I in 0 to 15 loop
				A0NEXT(I) <= unsigned(A_OUT(I));
			end loop;
			A0NEXT2 <= A0NEXT;
			A0_CNTR <= to_unsigned(31,A0_CNTR'length);	-- worst case: 31 clocks needed when the max is at index 15
		elsif(A0_CNTR /= 0) then
			-- step2: find positive value and subtract from all. Repeat until all negative or zero
			A0_CNTR <= A0_CNTR - 1;
			if(A0_CNTR(0) = '0') then
				for I in 0 to 15 loop
					A0NEXT(I) <= A0NEXT(I) - A0MAX;
				end loop;
			end if;
		end if;
	end if;
end process;

A0NEXT_GEN_002: process(CLK)
variable A0MAXv: unsigned((NSMBITS-1) downto 0);
begin
	if rising_edge(CLK) then
		if(A0_CNTR /= 0) then
			-- step2: find positive value and subtract from all. Repeat until all A0
			if(A0_CNTR(0) = '1') then
				-- find first positive (every other clocks)
				A0MAXv := (others => '0');
				for I in 0 to 15 loop
					if(A0NEXT(I)(NSMBITS-1) = '0') and (A0NEXT(I) /= 0) then
						-- positive
						A0MAXv := A0NEXT(I);
						exit;
					end if;
				end loop;
				A0MAX <= A0MAXv;
			end if;
		end if;
	end if;
end process;


--//-- BACKWARD STATE METRICS --------------------------------------------------------
-- Time-shared between SISO2 and SISO1
-- Inputs aligned with DECSTATE1_D2 (SISO1) or DECSTATE1_D3 (SISO2)
-- Outputs aligned with DECSTATE1_D3 (SISO1) or DECSTATE1_D4 (SISO2)
-- Latency 1 CLK

B_IN_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if (BMB_INPUT_VALID = '1') then
			B_IN_MASK <= (others => (others => '1'));
		else
			B_IN_MASK <= (others => (others => '0'));
		end if;	
		
		if ((DECSTATE1_D = '1') and (ITER(0) = '0')) or ((DECSTATE1_D2 = '1') and (ITER(0) = '1')) then
			BMB_INPUT_VALID <= '1';
		else
			BMB_INPUT_VALID <= '0';
		end if;
	end if;
end process;

-- initialize first state metrics to zero (iter = 0), then  b(k+1)
B_IN_GEN_002: process(B_OUT, B_IN_MASK)
begin
	for I in 0 to 15 loop
		B_IN(I) <= B_OUT(I) and B_IN_MASK(I);
	end loop;
end process;


BMB <= BM when (BMB_INPUT_VALID = '1')  else
		(others => (others => '0'));

BACKWARD_STATE_GEN_001: BACKWARD_STATE_GEN
generic map(
	NLLRBITS => NLLRBITS,
	NSMBITS => NSMBITS
)
port map(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	B_IN0 => B_IN(0),
	B_IN1 => B_IN(1),
	B_IN2 => B_IN(2),
	B_IN3 => B_IN(3),
	B_IN4 => B_IN(4),
	B_IN5 => B_IN(5),
	B_IN6 => B_IN(6),
	B_IN7 => B_IN(7),
	B_IN8 => B_IN(8),
	B_IN9 => B_IN(9),
	B_IN10 => B_IN(10),
	B_IN11 => B_IN(11),
	B_IN12 => B_IN(12),
	B_IN13 => B_IN(13),
	B_IN14 => B_IN(14),
	B_IN15 => B_IN(15),
	BM0 => BMB(0),
	BM1 => BMB(1),
	BM2 => BMB(2),
	BM3 => BMB(3),
	BM4 => BMB(4),
	BM5 => BMB(5),
	BM6 => BMB(6),
	BM7 => BMB(7),
	BM8 => BMB(8),
	BM9 => BMB(9),
	BM10 => BMB(10),
	BM11 => BMB(11),
	BM12 => BMB(12),
	BM13 => BMB(13),
	BM14 => BMB(14),
	BM15 => BMB(15),
	B_OUT0 => B_OUT(0),
	B_OUT1 => B_OUT(1),
	B_OUT2 => B_OUT(2),
	B_OUT3 => B_OUT(3),
	B_OUT4 => B_OUT(4),
	B_OUT5 => B_OUT(5),
	B_OUT6 => B_OUT(6),
	B_OUT7 => B_OUT(7),
	B_OUT8 => B_OUT(8),
	B_OUT9 => B_OUT(9),
	B_OUT10 => B_OUT(10),
	B_OUT11 => B_OUT(11),
	B_OUT12 => B_OUT(12),
	B_OUT13 => B_OUT(13),
	B_OUT14 => B_OUT(14),
	B_OUT15 => B_OUT(15)
);

-- save the entire backward state metrics (each iteration starts with backward state metrics)
BSM_WEA <= '1' when ((ITER(0) = '0') and (DECSTATE1_D3 = '1'))  or ((ITER(0) = '1') and (DECSTATE1_D4 = '1')) else '0';

BSM_ADDRA_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		BSM_WEA_D <= BSM_WEA;
		BSM_ADDRA_D <= BSM_ADDRA;

		if(BSM_WEA = '1')then
			BSM_ADDRA <= BSM_ADDRA - 1;
		else
			BSM_ADDRA <= N-1;
		end if;
	end if;
end process;

-- B_OUT(0) is saved at full precision (NSMBITS) whereas the others B_OUT(I) are subsequently reduced to NLLRBITS
BSM_BUF_001: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => NSMBITS,			
	ADDR_WIDTHA => WPTR1'length,	
	DATA_WIDTHB => NSMBITS,	 
	ADDR_WIDTHB => WPTR1'length
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => BSM_WEA,      
	ADDRA => std_logic_vector(BSM_ADDRA),  
	DIA => B_OUT(0),      
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => '0',
	ADDRB => std_logic_vector(BSM_ADDRB),  
	DIB => (others => '0'),      
	OEB => '1',
	DOB => B_OUT2(0)  -- ready at DECSTATE3_D (SISO1) or DECSTATE3_D2 (SISO2)
);

-- to reduce storage size, subtract B_OUT(0) from B_OUT(I, I != 0) and drop MSbs since the dynamic range is reduced.
-- Of course B2_OUT(0) is added back upon reading B_OUT2(I). This is just a cheap trick to save several (23) 18K BRAMs
BSM_STORAGE_002: for I in 1 to 15 generate
	BSM_STORAGE_002x: process(CLK)
	begin
		if rising_edge(CLK) then
			B_OUT_D(I) <= std_logic_vector(resize(signed(B_OUT(I)) - signed(B_OUT(0)), NLLRBITS +1 ));
		end if;
	end process;

	BSM_BUF_001x: BRAM_DP2 
	GENERIC MAP(
		DATA_WIDTHA => NLLRBITS+1,			
		ADDR_WIDTHA => WPTR1'length,	
		DATA_WIDTHB => NLLRBITS+1,	 
		ADDR_WIDTHB => WPTR1'length
	)
	PORT MAP(
		CLKA => CLK,
		CSA => '1',
		WEA => BSM_WEA_D,      
		ADDRA => std_logic_vector(BSM_ADDRA_D),  
		DIA => B_OUT_D(I),      
		OEA => '0',
		DOA => open,
		CLKB => CLK,
		CSB => '1',
		WEB => '0',
		ADDRB => std_logic_vector(BSM_ADDRB),  
		DIB => (others => '0'),      
		OEB => '1',
		DOB => B_OUT2(I)(NLLRBITS downto 0)  -- ready at DECSTATE3_D (SISO1) or DECSTATE3_D2 (SISO2)
	);
end generate;

BSM_ADDRB <= RPTR1 when (ITER(0) = '0') and (DECSTATE3 = '1') else 
				 RPTR1_D when (ITER(0) = '1') and (DECSTATE3_D = '1') else 
				 (others => '1');	-- important: point to 0 output. save power: no need to read in the first half of an iteration, when computing the backward state metrics.

B_OUT3(0) <= B_OUT2(0);
BSM_STORAGE_003: for I in 1 to 15 generate
	B_OUT3(I) <= std_logic_vector(resize(signed(B_OUT2(0)),NSMBITS) + resize(signed(B_OUT2(I)(NLLRBITS downto 0)), NSMBITS));	-- restore original full dynamic range
end generate;


--//-- A POSTERIORI LOG LIKELIHOOD  --------------------------------------------------------
-- Time-shared between SISO1 and SISO2
-- At the same time as computing the forward state metrics
-- Inputs B_OUT3, A_OUT, BM are ready at DECSTATE3_D2 (SISO1) or DECSTATE3_D3 (SISO2) (compared with Matlab turbo.m code)
-- Outputs aligned with DECSTATE3_D6 (SISO1) or DECSTATE3_D7 (SISO2)

-- Latency: 5 CLKs
LLR_INPUT_VALID <= DECSTATE3_D2 when (ITER(0) = '0') else DECSTATE3_D3;

LLR_GEN_001: LLR
generic map(
	NLLRBITS => NLLRBITS,
	NSMBITS => NSMBITS
)
port map(
	CLK => CLK,
	SYNC_RESET => SYNC_RESET,
	INPUT_VALID => LLR_INPUT_VALID,
	A_IN0 => A_IN(0),
	A_IN1 => A_IN(1),
	A_IN2 => A_IN(2),
	A_IN3 => A_IN(3),
	A_IN4 => A_IN(4),
	A_IN5 => A_IN(5),
	A_IN6 => A_IN(6),
	A_IN7 => A_IN(7),
	A_IN8 => A_IN(8),
	A_IN9 => A_IN(9),
	A_IN10 => A_IN(10),
	A_IN11 => A_IN(11),
	A_IN12 => A_IN(12),
	A_IN13 => A_IN(13),
	A_IN14 => A_IN(14),
	A_IN15 => A_IN(15),
	BM0 => BM(0),
	BM1 => BM(1),
	BM2 => BM(2),
	BM3 => BM(3),
	BM4 => BM(4),
	BM5 => BM(5),
	BM6 => BM(6),
	BM7 => BM(7),
	BM8 => BM(8),
	BM9 => BM(9),
	BM10 => BM(10),
	BM11 => BM(11),
	BM12 => BM(12),
	BM13 => BM(13),
	BM14 => BM(14),
	BM15 => BM(15),
	B_IN0 => B_OUT3(0),
	B_IN1 => B_OUT3(1),
	B_IN2 => B_OUT3(2),
	B_IN3 => B_OUT3(3),
	B_IN4 => B_OUT3(4),
	B_IN5 => B_OUT3(5),
	B_IN6 => B_OUT3(6),
	B_IN7 => B_OUT3(7),
	B_IN8 => B_OUT3(8),
	B_IN9 => B_OUT3(9),
	B_IN10 => B_OUT3(10),
	B_IN11 => B_OUT3(11),
	B_IN12 => B_OUT3(12),
	B_IN13 => B_OUT3(13),
	B_IN14 => B_OUT3(14),
	B_IN15 => B_OUT3(15),
	LLR00 => LLR00_OUT,	-- apriori Lk(delta) for hypothesis AB
	LLR01 =>	LLR01_OUT,	
	LLR10 =>	LLR10_OUT,		
	LLR11 =>	LLR11_OUT,
	DECODED_DATA_OUT => DATA_AB2
);

LLR_OUT(NLLRBITS-1 downto 0) <= LLR00_OUT;
LLR_OUT(2*NLLRBITS-1 downto NLLRBITS) <= LLR01_OUT;	
LLR_OUT(3*NLLRBITS-1 downto 2*NLLRBITS) <= LLR10_OUT;		
LLR_OUT(4*NLLRBITS-1 downto 3*NLLRBITS) <= LLR11_OUT;

--//-- CRC CHECK ----------------------------
DATA1b1_VALID <= '1' when (DECSTATE3_D6 = '1') and (ITER(0) = '0') and (ITER_INC = unsigned(N_ITER)) else '0';

-- 2 bit parallel to byte packing
-- Also count bytes to delineate payload
CRC_CNTR_INC <= CRC_CNTR + 1;
CRC_GEN_001: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (CRC16_EN = '0') or (DATA1b1_VALID = '0') then
			CRC_CNTR <= (others => '0');
			DATA8b1 <= (others => '0');
			DATA8b1_VALID <= '0';
		else
			-- receiving a complete block of decoded data, including optional CRC
			CRC_CNTR <= CRC_CNTR_INC;
			DATA8b1(7 downto 2) <= DATA8b1(5 downto 0);	-- shift left
			DATA8b1(1 downto 0) <= DATA_AB2(0) & DATA_AB2(1);	-- shift MSb (A) first
			if(CRC_CNTR(1 downto 0) = 3) then
				DATA8b1_VALID <= '1';
			else
				DATA8b1_VALID <= '0';
			end if;
		end if;
	end if;
end process;

CRC_GEN_002: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (CRC16_EN = '0') then
			SOF8b1_FLAG <= '0';
		elsif(DATA1b1_VALID = '0') then
			-- re-arm SOF flag
			SOF8b1_FLAG <= '1';
		elsif(DATA8b1_VALID = '1') then
			-- clear SOF flag
			SOF8b1_FLAG <= '0';
		end if;
	end if;
end process;

SOF8b1 <= SOF8b1_FLAG and DATA8b1_VALID;

-- 1 CLK latency
CRC_IN <= CRC_OUT when (SOF8b1 = '0') else (others => '1');
CRC_SYNC_RESET <= SYNC_RESET or (not CRC16_EN);
CRC_GEN_003: CRC16_8b PORT MAP(
	CLK => CLK,
	SYNC_RESET => CRC_SYNC_RESET,
	CRC16_IN => CRC_IN,
	DATA_IN => DATA8b1,
	DATA_VALID_IN => DATA8b1_VALID,
	CRC16_OUT => CRC_OUT,
	CRC16_VALID => open
);

-- outline payload field
CRC_GEN_004: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			DEC_PAYLOAD_FIELD <= '0';
		elsif (DECSTATE3_D5 = '1') and (DECSTATE3_D6 = '0') and (ITER_INC = unsigned(N_ITER))then
			DEC_PAYLOAD_FIELD <= '1';
		elsif(CRC_CNTR_INC(CRC_CNTR_INC'left downto 2) = unsigned(BURST_PAYLOAD_SIZE)) then
			DEC_PAYLOAD_FIELD <= '0';
		end if;
	end if;
end process;

-- CRC ready? 
CRC_GEN_005: process(CLK) 
begin
	if rising_edge(CLK) then
		if (DECSTATE3_D6 = '0') and (DECSTATE3_D7 = '1') and (ITER_INC = unsigned(N_ITER)) then
			-- decoded data complete (true even if CRC is disabled)
			CRC_RDY <= '1';
		else
			CRC_RDY <= '0';
		end if;
	end if;
end process;

CRC_GEN_006: process(CLK) 
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			CRC_VALID <= '0';
			BAD_CRC <= '0';
			FRAME_CNTR_local <= (others => '0');
			FRAME_ERROR_CNTR_local <= (others => '0');
			OB_ADDRA_CONFIRMED <= (others => '0');
		elsif(CRC_RDY = '1') then
			FRAME_CNTR_local <= FRAME_CNTR_local + 1;
			if(CRC16_EN = '1') then
				if (CRC_OUT = x"0000") then
					CRC_VALID <= '1';
					OB_ADDRA_CONFIRMED <= OB_ADDRA;
				else
					BAD_CRC <= '1';
					FRAME_ERROR_CNTR_local <= FRAME_ERROR_CNTR_local + 1;
				end if;
			else
				-- no CRC16
				OB_ADDRA_CONFIRMED <= OB_ADDRA;
			end if;
		else
			CRC_VALID <= '0';
			BAD_CRC <= '0';
		end if;
	end if;
end process;
-- report frame counters
FRAME_CNTR <= std_logic_vector(FRAME_CNTR_local);
FRAME_ERROR_CNTR <= std_logic_vector(FRAME_ERROR_CNTR_local);

--//-- OUTPUT ELASTIC BUFFER ----------------------------
-- output elastic buffer and frame. No reordering as the last iteration MUST be SISO1 (in natural order)
OB_WEA <= '1' when (DECSTATE3_D6 = '1') and (ITER(0) = '0') and (ITER_INC = unsigned(N_ITER)) and (DEC_PAYLOAD_FIELD = '1') else '0';
	-- N_ITER must be odd to end with SISO1 iteration

-- output elastic buffer write pointer managment
-- expressed in decoded dibits AB 
OB_CNTR_INC <= OB_CNTR + 1;
OB_ADDRA_GEN: process(CLK) 
begin
	if rising_edge(CLK) then
		OB_WEA_D <= OB_WEA;
		if(SYNC_RESET = '1') then
			OB_ADDRA <= (others => '0');
			OB_CNTR <= (others => '0');
		elsif (DECSTATE3_D5 = '1') and (DECSTATE3_D6 = '0') and (ITER_INC = unsigned(N_ITER)) then
			-- about to receive the next frame. Re-position the write pointer
			OB_ADDRA <= OB_ADDRA_CONFIRMED;
			OB_CNTR <= (others => '0');
		elsif(OB_WEA = '1') then
			-- new decoded AB bots
			OB_ADDRA <= OB_ADDRA + 1;
			OB_CNTR <= OB_CNTR_INC;
		end if;
	end if;
end process;

OB_WEA <= '1' when (DECSTATE3_D6 = '1') and (ITER(0) = '0') and (ITER_INC = unsigned(N_ITER)) and (DEC_PAYLOAD_FIELD = '1') else '0';

DATA_AB2x(3) <= '1' when (OB_WEA = '1') and (OB_WEA_D = '0') else '0';	-- SOF
DATA_AB2x(2) <= '1' when (OB_WEA = '1') and (OB_CNTR_INC = unsigned(N0)) else '0';	-- EOF
DATA_AB2x(1 downto 0) <= DATA_AB2;	-- A/B bits

OB: BRAM_DP2 
GENERIC MAP(
	DATA_WIDTHA => 4,		
	ADDR_WIDTHA => WPTR1'length+1,	-- buffer is sized for at least two full frames
	DATA_WIDTHB => 4,		 
	ADDR_WIDTHB => WPTR1'length+1
)
PORT MAP(
	CLKA => CLK,
	CSA => '1',
	WEA => OB_WEA,      
	ADDRA => std_logic_vector(OB_ADDRA),  
	DIA => DATA_AB2x,      
	OEA => '0',
	DOA => open,
	CLKB => CLK,
	CSB => '1',
	WEB => '0',
	ADDRB => std_logic_vector(OB_ADDRB),  
	DIB => "0000",           
	OEB => '1',
	DOB => DATA_OUTx      
);

OB_SIZE <= OB_ADDRA_CONFIRMED + (not OB_ADDRB);
OB_FULL <= OB_SIZE(OB_SIZE'left);

-- output buffer read pointer management
-- expressed in dibits
OB_ADDRB_GEN: process(CLK) 
begin
	if rising_edge(CLK) then
		DATA_OUT_VALID_local <= DATA_OUT_VALID_E;	

		if(SYNC_RESET = '1') then
			OB_ADDRB <= (others => '1');
			DATA_OUT_VALID_E <= '0';
		elsif(CTS_IN = '1') and (OB_SIZE /= 0) then
			OB_ADDRB <= OB_ADDRB + 1;	
			DATA_OUT_VALID_E <= '1';
		else
			DATA_OUT_VALID_E <= '0';
		end if;
	end if;
end process;

DATA_OUT <= DATA_OUTx(1 downto 0);
DATA_OUT_VALID <= DATA_OUT_VALID_local;
SOF_OUT <= DATA_OUTx(3) and DATA_OUT_VALID_local;
EOF_OUT <= DATA_OUTx(2) and DATA_OUT_VALID_local;


--//-- SIM2OUTFILE ---------------------------
--simulation. send to a tab-delimited text file for subsequent (Matlab) plot.
SIM2OUTFILE_X: if(SIMULATION = '1') generate
	S2OF1_DATA1((NQBITS-1) downto 0) <= DATA_A_IN;
	S2OF1_DATA2((NQBITS-1) downto 0) <= DATA_B_IN;
	S2OF1_DATA3(0) <= SOF_IN;

	SIM2OUTFILE_001: SIM2OUTFILE 
	generic map(
		FILENAME => "TC_DEC_IN.txt"
	)
	PORT MAP(
		CLK => CLK,
		DATA1 => S2OF1_DATA1,
		DATA2 => S2OF1_DATA2,
		DATA3 => S2OF1_DATA3,
		SAMPLE_CLK_IN => DATA_IN_VALID
	);
end generate;

end Behavioral;

