library IEEE, work;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use work.AddressMap.all;
use work.BusSignalTypes.all;
use work.AddressBook.all;

entity clusteringFBH is
    generic(
        width   : integer
    );
    Port (
        rst     : in std_logic;
        clk     : in std_logic;
        inFBH   : in std_logic_vector(NofFBH_Raw -1 downto 0);
        outFBH  : out std_logic_vector(NofFBH -1 downto 0)
    );
end clusteringFBH;

architecture RTL of clusteringFBH is
    -- Internal signal declaration ----------------------------------------------------------------------
    constant    all_one         : std_logic_vector(width -1 downto 0):=(others => '1');
    
    signal xor_fbh, and_fbh     : std_logic_vector(NofFBH_Raw-2 downto 0);
    signal xor_coin             : std_logic_vector(NofFBH_Raw-1 downto 0);
    
    signal index_mem            : std_logic_vector(3 downto 0);
    type memType is array(integer range NofFBH_Raw-1 downto 0) of std_logic_vector(width-1 downto 0);
    signal mem  : memType;
    
    signal valid_xor,out_xor    : std_logic_vector(NofFBH_Raw-1 downto 0);
    signal out_coin             : std_logic_vector(NofFBH_Raw-2 downto 0);
    
    type delayType is array(integer range NofFBH_Raw-2 downto 0) of std_logic_vector(width-2 downto 0);
    signal reg_delay    : delayType;
    
    signal edge_coin    : std_logic_vector(NofFBH_Raw-2 downto 0);
    
    component EdgeDetector
        port(
            rst : in std_logic;
            clk : in std_logic;
            dIn : in std_logic;
            dOut : out std_logic
        );
    end component;
    
    component WidthGen
        generic(
            width   : integer 
        );
        Port (
            rst     : in std_logic;
            clk     : in std_logic;
            dataIn  : in std_logic;
            dataOut : out std_logic
        );
    end component;

begin
    -- =============================================================================================
    -- body 
    -- =============================================================================================

    -- signal connection ---------------------------------------------------------------------------
    gen_connect_xor : for i in 0 to NofFBH_Raw-1 generate
        outFBH(2*i) <= out_xor(i);
    end generate;
    
    gen_connect_coin : for i in 0 to NofFBH_Raw-2 generate
        outFBH(2*i+1) <= out_coin(i);
    end generate;

    -- xor/and in signal ---------------------------------------------------------------------------
    gen_xor_and : for i in 0 to NofFBH_Raw-2 generate
        xor_fbh(i)  <= inFBH(i) XOR inFBH(i+1);
        and_fbh(i)  <= inFBH(i) AND inFBH(i+1);
    end generate;
    
    -- xor_coin ------------------------------------------------------------------------------------
    -- ch0
    xor_coin(0) <= inFBH(0) AND xor_fbh(0);
    -- ch1-ch14
    gen_xor_coin : for i in 1 to NofFBH_Raw-2 generate
        xor_coin(i) <= inFBH(i) AND xor_fbh(i-1) AND xor_fbh(i);
    end generate;
    -- ch15
    xor_coin(15) <= inFBH(15) AND xor_fbh(14);
    
    -- make xored single out -----------------------------------------------------------------------
    u_Mem : process(rst, clk)
    variable    id  : integer;
    begin
        if(rst = '1') then
            index_mem   <= (others => '0');
            for i in 0 to NofFBH_Raw-1 loop
                mem(i)  <= (others => '0');
            end loop;
        elsif(clk'event AND clk = '1') then
            id := conv_integer(index_mem(3 downto 0));
            if(index_mem = width-1) then
                index_mem   <= (others => '0');
            else
                index_mem   <= index_mem +1;
            end if;
            
            for i in 0 to NofFBH_Raw-1 loop
                mem(i)(id) <= xor_coin(i);
            end loop;
        end if;
    end process u_Mem;
    
    gen_vxor :  for i in 0 to NofFBH_Raw-1 generate
        valid_xor(i)    <= '1' when(mem(i) = all_one) else '0';
    end generate;
    
    -- expand signal ------------------------------------------------------------------------------
    gen_out_xor :  for i in 0 to NofFBH_Raw-1 generate
        u_WidthGen : WidthGen
            generic map(width => WidthMtxCoin)
            port map(rst => rst, clk => clk, dataIn => valid_xor(i), dataOut => out_xor(i));
    end generate;
    
    u_delay : process(rst, clk)
    begin
        if(rst = '1') then
            for i in 0 to NofFBH_Raw-2 loop
                reg_delay(i)    <= (others => '0');
            end loop;
        elsif(clk'event AND clk = '1') then
            for i in 0 to NofFBH_Raw-2 loop
                reg_delay(i)    <= reg_delay(i)(width-3 downto 0) & and_fbh(i);
            end loop;
        end if;
    end process u_delay;
    
    gen_edge_decttor : for i in 0 to NofFBH_Raw-2 generate
        u_ED : EdgeDetector port map(rst => rst, clk => clk, dIn => reg_delay(i)(width-2), dOut => edge_coin(i));
    end generate;
    
    gen_out_coin :  for i in 0 to NofFBH_Raw-2 generate
        u_WidthGen : WidthGen
            generic map(width => WidthMtxCoin)
            port map(rst => rst, clk => clk, dataIn => edge_coin(i), dataOut => out_coin(i));
    end generate;

end RTL;
