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 is
    port(
        rst	: in std_logic;
        clk	: in std_logic;
        -- Module input --
    	tof_sig            : in std_logic_vector(NofTOF-1 downto 0);
    	fbh_sig            : in std_logic_vector(NofFBH-1 downto 0);
    	ch_sig             : in std_logic_vector(NofCH-1 downto 0);
		
		-- Module output --
		Trigger           	: 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;

architecture RTL of Matrix3D is
	-- internal signal declaration ----------------------------------------
	constant NofArray      : natural := NofTOF*NofCH;
	--constant ch_all_zero   : std_logic_vector(NofCH-1 downto 0) := (others => '0');
	constant ch_all_zero   : std_logic_vector(31 downto 0) := (others => '0');
	constant tof_all_zero  : std_logic_vector(NofTOF-1 downto 0) := (others => '0');
	
	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;
	
	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_WidthGen : WidthGen
        generic map(width => WidthOut)
        port map(rst => rst, clk => clk, dataIn => reg_all, dataOut => Trigger);
        
    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)  = ch_all_zero) else '1';
        or_chline2(i)    <= '0' when(trig_one_tofch(64*i + 63 downto 64*i + 32) = ch_all_zero) 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,
                inCH    => ch_sig(j),
                inTOF   => tof_sig(i),
                outTRIG => trig_one_tofch(64*i + j)
            );
        
        end generate;
    end generate;

    u_BusProcess : process(clk, rst)
    variable   index : integer;
	begin
        if(rst = '1') then
            state_lbus	<= Init;
        elsif(clk'event and clk = '1') then
		    index   := conv_integer(addrLocalBus(11 downto 1));
			case state_lbus is
			when Init =>
				dataLocalBusOut 	<= x"00";
				readyLocalBus		<= '0';
				for i in 0 to NofArray-1 loop
                    reg_fbh(i)  <= (others => '0');
                end loop;
				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(0) is
				when '0' =>
					tmp_reg(15 downto 0)   <= dataLocalBusIn(15 downto 0);
				when others =>
				    tmp_reg(30 downto 16)   <= dataLocalBusIn(14 downto 0);
				end case;
				state_lbus	<= Done;
				
			when Read =>
			    reg_fbh(index)   <= tmp_reg;
				state_lbus	     <= Done;
				
			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;

