-- altera vhdl_input_version vhdl_2008
-- XXX: QSYS Fix (https://www.intel.com/content/www/us/en/support/programmable/articles/000079458.html)

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.rtps_package.all;
use work.rtps_config_package.all;

architecture arch of key_hash_generator is
    
    constant MAX_CNT_BOUND : natural := (KEY_HASH_WIDTH/data_in'length);
    
    type STAGE_TYPE is (IDLE, GEN_KEY_HASH, FINALIZE);
    
    signal stage, stage_next : STAGE_TYPE;
    signal cnt, cnt_next : natural range 0 to MAX_CNT_BOUND-1;
    signal kh, kh_next : std_logic_vector(KEY_HASH_WIDTH-1 downto 0);
    
begin
    key_hash    <= kh;
    
    kh_prc : process(all)
    begin
        -- DEFAULT
        stage_next      <= stage;
        kh_next         <= kh;
        cnt_next        <= cnt;
        -- DEFAULT Unregistered
        ack         <= '0';
        ready_in    <= '0';
        done        <= '0';
        
        case (stage) is
            when IDLE =>
                if (start = '1') then
                    ack         <= '1';
                    stage_next  <= GEN_KEY_HASH;
                    -- Reset
                    kh_next     <= (others => '0');
                    cnt_next    <= 0;
                end if;
            when GEN_KEY_HASH =>
                ready_in    <= '1';
                -- Input Guard
                if (valid_in = '1') then
                    kh_next <= write_sub_vector(kh, data_in, cnt, TRUE);
                    
                    -- Last Word
                    if (last_word_in = '1') then
                        stage_next  <= FINALIZE;
                    elsif (cnt = MAX_CNT_BOUND-1) then
                        -- Reset (Prevent overflow)
                        cnt_next    <= 0;
                    else
                        cnt_next    <= cnt + 1;
                    end if;
                end if;
            when FINALIZE =>
                done        <= '1';
                stage_next  <= IDLE;
            when others =>
                null;
        end case;
    end process;
    
    sync_prc : process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                stage       <= IDLE;
                cnt         <= 0;
                kh          <= (others => '0');
            else
                stage       <= stage_next;
                cnt         <= cnt_next;
                kh          <= kh_next;
            end if;
        end if;
    end process;
    
end architecture;
