library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity SHA3_BLOCK is
    port(
        CLK         : in std_logic;
        RESET       : in std_logic;
        DATA_IN     : in std_logic_vector (63 downto 0);
        DATA_VLD    : in std_logic;
        LAST_BLOCK  : in std_logic;
        BYTES_VLD   : in std_logic_vector (3 downto 0);
        BUFFER_FULL : out std_logic;
        HASH_OUT    : out std_logic_vector (511 downto 0);
        HASH_VLD    : out std_logic
    );
 end SHA3_BLOCK;

architecture FULL of SHA3_BLOCK is

    -- control signals
    signal last             : std_logic;
    signal in_used          : std_logic;
    signal buff_full        : std_logic;

    -- additional padding signals
    signal pad_06           : std_logic_vector (575 downto 0) := x"000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000";
    signal padded_data      : std_logic_vector (575 downto 0);
    signal pad_vld          : std_logic;
    signal pad_zeros        : std_logic;
    signal reset_pad        : std_logic;

    -- additional hash signals
    signal hash_in_data     : std_logic_vector (575 downto 0);
    signal hash_data_out    : std_logic_vector (511 downto 0);
    signal hash_out_vld     : std_logic;
    signal hash_in_vld      : std_logic;
    signal hash_last_pad    : std_logic;

    -- padding component
    component PADDING_BLOCK 
    port (
        CLK         : in  std_logic;
        RESET       : in  std_logic;
        DATA_IN     : in  std_logic_vector (63 downto 0);
        DATA_VLD    : in  std_logic;
        LAST_BLOCK  : in  std_logic;
        BYTES_VLD   : in  std_logic_vector (3 downto 0);
        BUFFER_FULL : out std_logic;
        PAD_DATA    : out std_logic_vector (575 downto 0);
        PAD_VLD     : out std_logic
        );
    end component;

    --hash component
    component HASH_BLOCK 
    port (
        CLK      : in std_logic;
        RESET    : in std_logic;
        IN_VLD   : in std_logic;
        HASH_IN  : in std_logic_vector (575 downto 0);
        USED     : out std_logic;
        DATA_OUT : out std_logic_vector (511 downto 0);
        DATA_VLD : out std_logic
        );
    end component;

begin

    -- hash valid if data from hash block are valid and we accepted last block of the message
    HASH_VLD <= hash_out_vld and (last and not(pad_vld)) and not(pad_zeros);

    -- take highest 512 bits thats represent our final hash
    HASH_OUT <= hash_data_out;

    -- assign BUFFER_FULL signal
    BUFFER_FULL <= buff_full;
    
    -- check if message block is last or not
    is_last_p : process(CLK)
    begin
        if(rising_edge(CLK)) then
            if (RESET = '1') then
                last <= '0';
            elsif (LAST_BLOCK = '1') then
                last <= '1';
            end if;
        end if;
    end process;
    
    -- set pading with zeros if last block of msg has all bytes vld
    pad_zeros_p : process(CLK)
    begin
        if(rising_edge(CLK)) then
            if(RESET = '1') then
                pad_zeros <= '0';
                hash_last_pad <= '0';
            else
                if(BYTES_VLD = "1000" and buff_full = '1') then
                    pad_zeros <= '1';
                elsif(hash_out_vld = '1' and pad_zeros = '1') then
                    hash_last_pad <= '1';
                    pad_zeros <= '0';
                else
                    hash_last_pad <= '0';
                end if;
            end if;
        end if;
    end process;
    
    -- reset padding component if reset or data used
    reset_pad <= RESET or in_used;

    -- connect padding block
    padding_block_c : PADDING_BLOCK 
    port map (
        CLK         => CLK,
        RESET       => reset_pad,
        DATA_IN     => DATA_IN,
        DATA_VLD    => DATA_VLD,
        LAST_BLOCK  => LAST_BLOCK,
        BYTES_VLD   => BYTES_VLD,
        BUFFER_FULL => buff_full,
        PAD_DATA    => padded_data,
        PAD_VLD     => pad_vld
        );
  
    -- hash in valid if pad out is valid or we have to pad with zeros
    hash_in_vld <= pad_vld or hash_last_pad;

    hash_in_data <= pad_06 when (hash_last_pad = '1') else padded_data;

    -- connect hash block
    permutation_block_c : HASH_BLOCK
    port map (
        CLK      => CLK,
        HASH_IN  => hash_in_data,
        RESET    => RESET,
        IN_VLD   => hash_in_vld,
        USED     => in_used,
        DATA_OUT => hash_data_out,
        DATA_VLD => hash_out_vld        
    );

end FULL;
