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 test of key_holder is
    
    --*****TYPE DECLARATION*****
    -- FSM states. Explained below in detail
    type STAGE_TYPE is (IDLE,SKIP_PAYLOAD,GET_KEY_HASH,PUSH_KEY_HASH);
    -- ###GENERATED END###
    
    -- *MAIN PROCESS*
    signal stage, stage_next : STAGE_TYPE;
    signal cnt, cnt_next : natural range 0 to 5;
    signal key_hash, key_hash_next  : KEY_HASH_TYPE;
    
begin
    
    main_prc : process (all)
    begin
        -- DEFAULT
        stage_next      <= stage;
        key_hash_next   <= key_hash;
        cnt_next        <= cnt;
        ready_in        <= '0';
        valid_out       <= '0';
        last_word_out   <= '0';
        decode_error    <= '0';
        ack             <= '0';
        data_out        <= (others => '0');
        
        case (stage) is
            when IDLE =>
                if (start = '1') then
                    case (opcode) is
                        when PUSH_DATA =>
                            ack             <= '1';
                            stage_next      <= GET_KEY_HASH;
                            cnt_next        <= 0;
                            -- Reset
                            key_hash_next   <= KEY_HASH_NIL;
                        when PUSH_SERIALIZED_KEY =>
                            ack             <= '1';
                            stage_next      <= GET_KEY_HASH;
                            cnt_next        <= 0;
                            -- Reset
                            key_hash_next   <= KEY_HASH_NIL;
                        when READ_KEY_HASH =>
                            ack             <= '1';
                            stage_next      <= PUSH_KEY_HASH;
                            cnt_next        <= 0;
                        when READ_SERIALIZED_KEY =>
                            ack             <= '1';
                            stage_next      <= PUSH_KEY_HASH;
                            cnt_next        <= 0;
                        when others =>
                            null;
                    end case;
                end if;
            when GET_KEY_HASH =>
                ready_in    <= '1';
                -- Input Guard
                if (valid_in = '1') then
                    key_hash_next(cnt)  <= not data_in;
                    if (cnt = 3) then
                        if (last_word_in = '1') then
                            stage_next  <= IDLE;
                        else
                            stage_next  <= SKIP_PAYLOAD;
                        end if;
                    else
                        assert (last_word_in /= '1') report "Test Key Holder expects a Payload of at least 4 Words" severity FAILURE;
                        cnt_next    <= cnt + 1;
                    end if;
                end if;
            when SKIP_PAYLOAD =>
                -- Skip Read
                ready_in    <= '1';
                
                -- EXIT
                if (last_word_in = '1' and valid_in = '1') then
                    stage_next  <= IDLE;
                end if;
            when PUSH_KEY_HASH =>
                case (cnt) is
                    -- Key Hash 1/4
                    when 0 =>
                        data_out    <= key_hash(0);
                        valid_out   <= '1';
                        -- Output Guard
                        if (ready_out = '1') then
                            cnt_next    <= cnt + 1;
                        end if;
                    -- Key Hash 2/4
                    when 1 =>
                        data_out    <= key_hash(1);
                        valid_out   <= '1';
                        -- Output Guard
                        if (ready_out = '1') then
                            cnt_next    <= cnt + 1;
                        end if;
                    -- Key Hash 3/4
                    when 2 =>
                        data_out    <= key_hash(2);
                        valid_out   <= '1';
                        -- Output Guard
                        if (ready_out = '1') then
                            cnt_next    <= cnt + 1;
                        end if;
                    -- Key Hash 4/4
                    when 3 =>
                        data_out        <= key_hash(3);
                        valid_out       <= '1';
                        last_word_out   <= '1';
                        -- Output Guard
                        if (ready_out = '1') then
                            -- DONE
                            stage_next  <= IDLE;
                        end if;
                    when others =>
                        null;
                end case;
            when others =>
                null;
        end case;
        
        -- ABORT
        if (abort = '1' and stage /= IDLE) then
            stage_next  <= IDLE;
        end if;
        
    end process;
    
    sync_prc : process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                stage               <= IDLE;
                cnt                 <= 0;
                key_hash            <= KEY_HASH_NIL;
            else
                stage               <= stage_next;
                cnt                 <= cnt_next;
                key_hash            <= key_hash_next;
            end if;
        end if;
    end process;
    
end architecture;