---------------------------------------------
-- internal bitslip in iserdes works only with 8 bits. It may happen that when we make a 16-bit word from them,
-- internal bitslip is correct, but we need to swap the whole bytes. -> o_bitslip_swap_bytes
--
-- NOTE: swapping bytes is wrong, because that means we're grabbing when frame is "00FF". That suggests, that
-- we're using LSbyte from previous sample and MSbyte from current sample. It is correct to drop the byte, to have the frame signal "FF00"
--
-- However, the bytes are not swapped, i.e. LSbyte is still LSbyte. They just do not come from the same sample.

library ieee;
use ieee.std_logic_1164.all;

library UNISIM;
use UNISIM.vcomponents.all;

--library utilities;

entity bitslip_compensation is
  port (
  
                clk : in std_logic;
                rst : in std_logic;
                
                i_data : in std_logic_vector( 15 downto 0 );
                i_valid : in std_logic;
                
                o_bitslip            : out std_logic;
                o_bitslip_done       : out std_logic;
                o_bitslip_drop_byte  : out std_logic;
                o_bitslip_failed     : out std_logic
                
        );              
                
end bitslip_compensation;

architecture behavioral of bitslip_compensation is

        component swap_endianness
  port (
                i_data : in std_logic_vector;
                o_data : out std_logic_vector
        );
        end component;

        constant C_BITSLIP_FRAME_TRAINING_PATTERN : std_logic_vector( 15 downto 0 ) := X"FF00";
        
        -- wait a while before bitslipping again
        subtype t_counter_busy is natural range 0 to 5; -- in theory 2 is enough
        signal  s_counter_busy : t_counter_busy := 0;
        
        -- count the number of bitslip attempts
        subtype t_counter_attempts is natural range 0 to 9;
        signal  s_counter_attempts : t_counter_attempts := 0;
        
        signal s_i_data_bytes_swapped : std_logic_vector( 15 downto 0 );
        
        signal s_bitslip_done : std_logic;
        signal s_bitslip_failed : std_logic;
        
        
begin

        swap_endianness_inst : swap_endianness
        port map( i_data => i_data, o_data => s_i_data_bytes_swapped ); 

        o_bitslip_done <= s_bitslip_done;
        o_bitslip_failed <= s_bitslip_failed;

        main_process : process( clk )
        
        begin
                if( rising_edge(clk) ) then
                        
                        o_bitslip <= '0';
                        o_bitslip_drop_byte <= '0';
                        
                        if( rst = '1' ) then
                                -- reset
                                s_counter_busy <= t_counter_busy'high;
                                s_counter_attempts <= 0;
                                s_bitslip_done <= '0';
                                s_bitslip_failed <= '0';
                        
                        elsif( s_bitslip_done = '1' or s_bitslip_failed = '1' ) then
                                -- do nothing, the bitslip has already been determined.
                        
                        elsif( i_valid = '1' and s_counter_busy > 0 ) then
                                -- we are busy now, do not do anything
                                s_counter_busy <= s_counter_busy - 1;
                        
                        elsif( i_valid = '1' and s_counter_busy = 0 and i_data = C_BITSLIP_FRAME_TRAINING_PATTERN ) then
                                -- we are not busy and the incoming pattern matches the training pattern.
                                s_bitslip_done <= '1';
                        
                        elsif( i_valid = '1' and s_counter_busy = 0 and s_i_data_bytes_swapped = C_BITSLIP_FRAME_TRAINING_PATTERN ) then
                                -- we are not busy and the incoming pattern matches the training pattern if we swap its bytes:
                                -- drop the byte and start over.
                                o_bitslip_drop_byte <= '1';
                                s_counter_busy <= t_counter_busy'high;
                        
                        elsif( i_valid = '1' and s_counter_busy = 0 and s_counter_attempts < t_counter_attempts'high ) then
                                -- we are not busy, we may bitslip again and the incoming pattern does not match.
                                s_counter_attempts <= s_counter_attempts + 1;
                                s_counter_busy <= t_counter_busy'high;
                                o_bitslip <= '1';
                                
                        elsif( i_valid = '1' and s_counter_busy = 0 and s_counter_attempts = t_counter_attempts'high ) then
                                -- we are not busy, but we do not have another attempt and the pattern still does not match.
                                s_bitslip_failed <= '1';
                                
                        end if; 
                end if;
        end process;
                
end architecture;