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 Matrix3D_v2 is
    port(
        rst	    : in std_logic;
        clk	    : in std_logic;
        sclk	: in std_logic;
        -- Module input --
    	tofSig            : in std_logic_vector(NofTOF-1 downto 0);
    	fbhSig            : in std_logic_vector(NofFBH-1 downto 0);
    	chSig             : in std_logic_vector(NofCH-1 downto 0);
		
		-- Module output --
		Trigger           	: out std_logic;
		VETO             	: out std_logic;
		-- Local bus --
		addrLocalBus		: in LocalAddressType;
		dataLocalBusIn		: in LocalBusInType;
		dataLocalBusOut	    : out LocalBusOutType;
		reLocalBus			: in std_logic;
		weLocalBus			: in std_logic;
		readyLocalBus		: out std_logic
	);
end Matrix3D_v2;

architecture RTL of Matrix3D_v2 is
	-- internal signal declaration ----------------------------------------
	constant NofArray      : natural := NofTOF*NofCH;
	constant ch_all_zero   : std_logic_vector(NofCH-1 downto 0) := (others => '0');
	constant tof_all_zero  : std_logic_vector(NofTOF-1 downto 0) := (others => '0');
	
	type bufFBHType is array(integer range NofTOF-1 downto 0) of std_logic_vector(NofFBH-1 downto 0);
	signal fbh_sig : bufFBHType;
	
	type regFBHType is array(integer range NofArray-1 downto 0) of std_logic_vector(NofFBH-1 downto 0);
	signal reg_fbh         : regFBHType;
	signal tmp_reg         : std_logic_vector(NofFBH-1 downto 0);
	
	signal trig_one_tofch  : std_logic_vector(NofArray-1 downto 0);
	
	signal or_chline1, or_chline2, or_chline       : std_logic_vector(NofTOF-1 downto 0);
	signal reg_chline1, reg_chline2, reg_chline    : std_logic_vector(NofTOF-1 downto 0);
	signal or_all          : std_logic;
	signal reg_all         : std_logic;
	
	signal state_lbus	: BusProcessType;
	
	type SubSeqType is (
        Init, Idle, DoSR, Done
    );
    signal state_sub    : SubSeqType;
    signal done_sub, exec_command   : std_logic;
	
	component Matrix3D_Impl_v2
        Port (
            RST     : in std_logic;
            CLK     : in std_logic;
            -- input signal --
            enFBH  : in std_logic_vector(NofFBH-1 downto 0);
            inFBH  : in std_logic_vector(NofFBH-1 downto 0);
            inCH   : in std_logic;
            inTOF  : in std_logic;
            -- output signal --
            outTRIG    : 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;

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

    u_BufFBH : process(clk)
    begin
        if(clk'event AND clk = '1') then
            for i in 0 to NofTOF-1 loop
                fbh_sig(i) <= fbhSig;
            end loop;
        end if;
    end process u_BufFBH;

    u_WidthGen : WidthGen
        generic map(width => WidthOut)
        port map(rst => rst, clk => clk, dataIn => reg_all, dataOut => Trigger);
        
    u_VetoGen : WidthGen
        generic map(width => WidthOut)
        port map(rst => rst, clk => clk, dataIn => or_all,  dataOut => VETO);
        
    u_delay_orall : process(rst, clk)
         begin
             if(rst = '1') then
                 reg_all <= '0';
             elsif(clk'event AND clk = '1') then
                 reg_all <= or_all;        
             end if;
         end process u_delay_orall;
    or_all  <= '0' when(reg_chline = tof_all_zero) else '1';

    gen_tof : for i in 0 to NofTOF-1 generate
        or_chline1(i)    <= '0' when(trig_one_tofch(64*i + 31 downto 64*i + 0)  = X"00000000") else '1';
        or_chline2(i)    <= '0' when(trig_one_tofch(64*i + 63 downto 64*i + 32) = X"00000000") else '1';
        or_chline(i)     <= reg_chline1(i) OR reg_chline2(i);
        
        u_delay_chline : process(rst, clk)
        begin
            if(rst = '1') then
                reg_chline1(i)   <= '0';
                reg_chline2(i)   <= '0';
                reg_chline(i)    <= '0';
            elsif(clk'event AND clk = '1') then
                reg_chline1(i)  <= or_chline1(i);
                reg_chline2(i)  <= or_chline2(i);
                reg_chline(i)   <= or_chline(i);
            end if;
        end process u_delay_chline;
        
        gen_ch : for j in 0 to NofCH-1 generate
            u_Matrix_Impl : Matrix3D_Impl_v2
            port map(
                RST => rst, 
                clk => clk,
                enFBH   => reg_fbh(64*i + j),
                inFBH   => fbh_sig(i),
                inCH    => chSig(j),
                inTOF   => tofSig(i),
                outTRIG => trig_one_tofch(64*i + j)
            );
        
        end generate;
    end generate;

    u_InitProcess : process(sclk, rst)
    begin
        if(rst = '1') then
            for i in 0 to NofArray-1 loop
                reg_fbh(i)  <= (others => '0');
            end loop;
            state_sub   <= Init;
            
        elsif(sclk'event and sclk = '1') then
            case state_sub is
                when Init =>
                    done_sub    <= '0';
                    state_sub   <= Idle;
                
                when Idle =>
                    if(exec_command = '1') then
                        state_sub   <= DoSR;
                    end if;
                    
                when DoSR =>
                    reg_fbh     <= reg_fbh(NofArray-2 downto 0) & tmp_reg;
                    done_sub    <= '1';
                    state_sub   <= Done;
                    
                when Done =>
                    done_sub    <= '0';
                    state_sub   <= Idle;
                    
            end case;
        end if;
    end process u_InitProcess;

    u_BusProcess : process(clk, rst)
	begin
        if(rst = '1') then
            exec_command    <= '0';
            state_lbus	<= Init;
        elsif(clk'event and clk = '1') then
			case state_lbus is
			when Init =>
			    exec_command        <= '0';
				dataLocalBusOut 	<= x"00";
				readyLocalBus		<= '0';
				state_lbus			<= Idle;
				
			when Idle =>
				readyLocalBus	<= '0';
				if(weLocalBus = '1' or reLocalBus = '1') then
					state_lbus	<= Connect;
				end if;
			
			when Connect =>
				if(weLocalBus = '1') then
					state_lbus	<= Write;
				else
					state_lbus	<= Read;
				end if;
				
			when Write =>
				case addrLocalBus is
				when MTX_low =>
					tmp_reg(15 downto 0)   <= dataLocalBusIn(15 downto 0);
        	        state_lbus	<= Done;
				when MTX_high =>
				    tmp_reg(30 downto 16)  <= dataLocalBusIn(14 downto 0);
		            state_lbus	<= Done;
				when MTX_exec =>
				    state_lbus  <= Execute;
			    when others =>
			        state_lbus	<= Done;
				end case;
				
			when Read =>
				state_lbus	     <= Done;
				
			when Execute =>
			     exec_command    <= '1';
			     state_lbus      <= Finalize;
			     
			when Finalize =>
			     if(done_sub = '1') then
			         exec_command    <= '0';
			         state_lbus      <= Done;
			     end if;
				
			when Done =>
				readyLocalBus	<= '1';
				if(weLocalBus = '0' and reLocalBus = '0') then
					state_lbus	<= Idle;
				end if;
			
			-- probably this is error --
			when others =>
				state_lbus	<= Init;
			end case;
		end if;
	end process u_BusProcess;

end RTL;

