-- 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;
use work.Type2_package.all;

architecture TYPE2 of key_holder is
    
    --*****COMPONENT DECLARATION*****
    component key_hash_generator is
        port (
            clk             : in std_logic;
            reset           : in std_logic;
            
            start           : in std_logic;
            ack             : out std_logic;
            
            data_in         : in std_logic_vector(7 downto 0);
            valid_in        : in std_logic;
            ready_in        : out std_logic;
            last_word_in    : in std_logic;
            
            key_hash        : out std_logic_vector(127 downto 0);
            done            : out std_logic
        );
    end component;
    
    --*****TYPE DECLARATION*****
    -- FSM states. Explained below in detail
    type STAGE_TYPE is (IDLE,START_KEY_HASH_GENERATION,GET_PAYLOAD_HEADER,FETCH,ALIGN_IN_STREAM,SKIP_PAYLOAD,DECODE_PAYLOAD, WRITE_PAYLOAD_HEADER, PUSH,ALIGN_OUT_STREAM,ENCODE_PAYLOAD,GET_KEY_HASH,PUSH_KEY_HASH);
    -- ###GENERATED START###
    type DECODE_STAGE_TYPE is (GET_ID,GET_TESTSEQUENCE_LENGTH,GET_TESTSEQUENCE_TESTARRAY,GET_TESTSEQUENCE_TESTCHAR,GET_TESTSEQUENCE_TESTWCHAR,GET_TESTSEQUENCE_TESTLONGLONG,GET_TESTSEQUENCE_TESTLONGDOUBLE,GET_OPTIONAL_HEADER);
    type ENCODE_STAGE_TYPE is (WRITE_ID,WRITE_TESTSEQUENCE_LENGTH,WRITE_TESTSEQUENCE_TESTARRAY);
    type TESTSEQUENCE_TESTARRAY_ADDR_TYPE is array (0 to TESTSEQUENCE_MAX_DEPTH-1) of std_logic_vector(TESTSEQUENCE_TESTARRAY_ADDR_WIDTH-1 downto 0);
    type TESTSEQUENCE_TESTARRAY_DATA_TYPE is array (0 to TESTSEQUENCE_MAX_DEPTH-1) of std_logic_vector(CDR_OCTET_WIDTH-1 downto 0);
    -- ###GENERATED END###
    
    -- *MAIN PROCESS*
    signal stage, stage_next : STAGE_TYPE;
    signal cnt, cnt_next : natural range 0 to 5;
    signal endian_flag, endian_flag_next : std_logic;
    signal last_word_in_latch, last_word_in_latch_next : std_logic;
    signal decode_error_latch, decode_error_latch_next : std_logic;
    signal align_offset, align_offset_next  : unsigned(MAX_ALIGN_OFFSET_WIDTH-1 downto 0);
    signal target_align, target_align_next : ALIGN_TYPE;
    signal data_in_latch, data_in_latch_next : std_logic_vector(WORD_WIDTH-1 downto 0);
    signal data_out_latch, data_out_latch_next : std_logic_vector(WORD_WIDTH-1 downto 0);
    signal optional, optional_next : std_logic;
    signal abort_mem : std_logic;
    signal opcode_latch, opcode_latch_next : KEY_HOLDER_OPCODE_TYPE;
    signal cnt_2, cnt_2_next : natural range 0 to (WORD_WIDTH/BYTE_WIDTH)-1;
    signal align_op, align_op_next : std_logic;
    signal finalize_payload, finalize_payload_next : std_logic;
    signal ready_in_sig : std_logic;
    signal start_kh, ack_kh, done_kh : std_logic;
    signal data_in_kh : std_logic_vector(BYTE_WIDTH-1 downto 0);
    signal valid_in_kh, ready_in_kh, last_word_in_kh : std_logic;
    signal key_hash_kh : std_logic_vector(KEY_HASH_WIDTH-1 downto 0);
    signal key_hash, key_hash_next  : KEY_HASH_TYPE;
    signal decode_stage, decode_stage_next : DECODE_STAGE_TYPE;
    signal encode_stage, encode_stage_next : ENCODE_STAGE_TYPE;
    signal return_stage, return_stage_next : DECODE_STAGE_TYPE;
    -- ###GENERATED START###
    signal id_latch, id_latch_next : std_logic_vector(CDR_LONG_WIDTH-1 downto 0);
    signal TestSequence_cnt, TestSequence_cnt_next : natural range 0 to TESTSEQUENCE_MAX_DEPTH-1;
    signal TestSequence_len_latch, TestSequence_len_latch_next : unsigned(TESTSEQUENCE_ADDR_WIDTH-1 downto 0);
    signal TestSequence_addr_latch, TestSequence_addr_latch_next : std_logic_vector(TESTSEQUENCE_ADDR_WIDTH-1 downto 0);
    signal TestSequence_TestArray_cnt, TestSequence_TestArray_cnt_next : natural range 0 to TESTSEQUENCE_TESTARRAY_MAX_DEPTH-1;
    -- TestSequence_TestArray_mem SIGNALS
    signal TestSequence_TestArray_mem_addr : TESTSEQUENCE_TESTARRAY_ADDR_TYPE;
    signal TestSequence_TestArray_mem_read, TestSequence_TestArray_mem_ready_in, TestSequence_TestArray_mem_ready_out, TestSequence_TestArray_mem_valid_in, TestSequence_TestArray_mem_valid_out : std_logic_vector(0 to TESTSEQUENCE_TESTARRAY_MAX_DEPTH-1);
    signal TestSequence_TestArray_mem_data_in, TestSequence_TestArray_mem_data_out : TESTSEQUENCE_TESTARRAY_DATA_TYPE;
    -- ###GENERATED END###
    
    --*****ALIAS DECLARATION*****
    alias representation_id         : std_logic_vector(PAYLOAD_REPRESENTATION_ID_WIDTH-1 downto 0) is data_in(WORD_WIDTH-1 downto WORD_WIDTH-PAYLOAD_REPRESENTATION_ID_WIDTH);
    alias representation_options    : std_logic_vector(PAYLOAD_REPRESENTATION_ID_WIDTH-1 downto 0) is data_in(PAYLOAD_REPRESENTATION_OPTIONS_WIDTH-1 downto 0);
    alias parameter_id              : std_logic_vector(PARAMETER_ID_WIDTH-1 downto 0) is data_in_latch(WORD_WIDTH-1 downto WORD_WIDTH-PARAMETER_ID_WIDTH);
    alias parameter_length          : std_logic_vector(PARAMETER_LENGTH_WIDTH-1 downto 0) is data_in_latch(PARAMETER_LENGTH_WIDTH-1 downto 0);
    
    
begin
    
    key_hash_generator_inst : key_hash_generator
        port map (
            clk             => clk,
            reset           => reset,
            start           => start_kh,
            ack             => ack_kh,
            data_in         => data_in_kh,
            valid_in        => valid_in_kh,
            ready_in        => ready_in_kh,
            last_word_in    => last_word_in_kh,
            key_hash        => key_hash_kh,
            done            => done_kh
        );
    
    -- ###GENERATED START###
    TestSequence_TestArray_gen : for i in 0 to TESTSEQUENCE_MAX_DEPTH-1 generate
    begin
        TestSequence_TestArray_mem : entity work.mem_ctrl(arch)
        generic map (
            ADDR_WIDTH          => TESTSEQUENCE_TESTARRAY_ADDR_WIDTH,
            DATA_WIDTH          => CDR_OCTET_WIDTH,
            MEMORY_DEPTH        => TESTSEQUENCE_TESTARRAY_MAX_DEPTH,
            MAX_BURST_LENGTH    => 1
        )
        port map (
            clk         => clk,
            reset       => reset or abort_mem,
            addr        => TestSequence_TestArray_mem_addr(i),
            read        => TestSequence_TestArray_mem_read(i),
            ready_in    => TestSequence_TestArray_mem_ready_in(i),
            valid_in    => TestSequence_TestArray_mem_valid_in(i),
            data_in     => TestSequence_TestArray_mem_data_in(i),
            ready_out   => TestSequence_TestArray_mem_ready_out(i),
            valid_out   => TestSequence_TestArray_mem_valid_out(i),
            data_out    => TestSequence_TestArray_mem_data_out(i)
        );
    end generate;
    -- ###GENERATED END###
    
    decode_error    <= decode_error_latch;
    ready_in        <= ready_in_sig;
    
    main_prc : process (all)
        variable tmp_length : unsigned(WORD_WIDTH-1 downto 0);
        variable cnt2_ref   : std_logic_vector(1 downto 0);
    begin
        -- DEFAULT
        stage_next                  <= stage;
        decode_stage_next           <= decode_stage;
        encode_stage_next           <= encode_stage;
        return_stage_next           <= return_stage;
        cnt_next                    <= cnt;
        cnt_2_next                  <= cnt_2;
        endian_flag_next            <= endian_flag;
        last_word_in_latch_next     <= last_word_in_latch;
        decode_error_latch_next     <= decode_error_latch;
        align_offset_next           <= align_offset;
        target_align_next           <= target_align;
        optional_next               <= optional;
        data_in_latch_next          <= data_in_latch;
        data_out_latch_next         <= data_out_latch;
        opcode_latch_next           <= opcode_latch;
        key_hash_next               <= key_hash;
        align_op_next               <= align_op;
        finalize_payload_next       <= finalize_payload;
        abort_mem                   <= '0';
        ack                         <= '0';
        ready_in_sig                <= '0';
        last_word_out               <= '0';
        valid_out                   <= '0';
        start_kh                    <= '0';
        valid_in_kh                 <= '0';
        last_word_in_kh             <= '0';
        data_in_kh                  <= (others => '0');
        data_out                    <= (others => '0');
        -- ###GENERATED START###
        id_latch_next                           <= id_latch;
        TestSequence_len_latch_next             <= TestSequence_len_latch;
        TestSequence_cnt_next                   <= TestSequence_cnt;
        TestSequence_TestArray_cnt_next         <= TestSequence_TestArray_cnt;
        TestSequence_addr_latch_next            <= TestSequence_addr_latch;
        TestSequence_TestArray_mem_addr         <= (others => (others => '0'));
        TestSequence_TestArray_mem_read         <= (others => '0');
        TestSequence_TestArray_mem_valid_in     <= (others => '0');
        TestSequence_TestArray_mem_ready_out    <= (others => '0');
        TestSequence_TestArray_mem_data_in      <= (others => (others => '0'));
        -- ###GENERATED END###
        
        -- Last Word Latch Setter
        if (last_word_in = '1') then
            last_word_in_latch_next <= '1';
        end if;
        
        case (stage) is
            when IDLE =>
                -- Latch Opcode
                opcode_latch_next   <= opcode;
                
                if (start = '1') then
                    case (opcode) is
                        when PUSH_DATA =>
                            ack             <= '1';
                            stage_next      <= GET_PAYLOAD_HEADER;
                            -- Reset
                            key_hash_next   <= KEY_HASH_NIL;
                        when PUSH_SERIALIZED_KEY =>
                            ack             <= '1';
                            stage_next      <= GET_PAYLOAD_HEADER;
                            -- Reset
                            key_hash_next   <= KEY_HASH_NIL;
                        when READ_KEY_HASH =>
                            ack             <= '1';
                            -- Key Hash not calculated
                            if (key_hash = KEY_HASH_NIL) then
                                stage_next  <= START_KEY_HASH_GENERATION;
                            else
                                stage_next  <= PUSH_KEY_HASH;
                                cnt_next    <= 0;
                            end if;
                        when READ_SERIALIZED_KEY =>
                            ack                 <= '1';
                            endian_flag_next    <= LITTLE_ENDIAN;
                            stage_next          <= WRITE_PAYLOAD_HEADER;
                        when others =>
                            null;
                    end case;
                end if;
            when START_KEY_HASH_GENERATION =>
                start_kh    <= '1';
                
                -- <TYPENAME>KeyHolder is in PLAIN_CDR2 Big Endian encoding (see 7.6.8 DDS_XTYPES v1.3)
                endian_flag_next    <= '0';
                -- Alignment Reset
                align_offset_next   <= (others => '0');
                data_out_latch_next <= (others => '0');
                
                if (ack_kh = '1') then
                    stage_next          <= ENCODE_PAYLOAD;
                    -- ###GENERATED START###
                    encode_stage_next   <= WRITE_ID;
                    -- ###GENERATED START###
                end if;
            when GET_PAYLOAD_HEADER =>
                -- TODO: Latch Offset from Options Field?
                
                ready_in_sig    <= '1';
                -- Input Guard
                if (valid_in = '1') then
                    case (representation_id) is
                        when CDR_BE =>
                            endian_flag_next    <= '0';
                            stage_next          <= FETCH;
                            -- Alignment Reset 
                            align_offset_next   <= (others => '0');
                            if (opcode_latch = PUSH_DATA) then
                                -- ###GENERATED START###
                                decode_stage_next   <= GET_ID;
                                -- ###GENERATED END###
                            else --(opcode_latch = PUSH_SERIALIZED_KEY)
                                assert(opcode_latch = PUSH_SERIALIZED_KEY) severity FAILURE;
                                -- ###GENERATED START###
                                decode_stage_next   <= GET_ID;
                                -- ###GENERATED END###
                            end if;
                        when CDR_LE =>
                            endian_flag_next    <= '1';
                            stage_next          <= FETCH;
                            -- Alignment Reset 
                            align_offset_next   <= (others => '0');
                            if (opcode_latch = PUSH_DATA) then
                                -- ###GENERATED START###
                                decode_stage_next   <= GET_ID;
                                -- ###GENERATED END###
                            else --(opcode_latch = PUSH_SERIALIZED_KEY)
                                assert(opcode_latch = PUSH_SERIALIZED_KEY) severity FAILURE;
                                -- ###GENERATED START###
                                decode_stage_next   <= GET_ID;
                                -- ###GENERATED END###
                            end if;
                        when others =>
                            -- Unknown Payload Encoding
                            stage_next              <= SKIP_PAYLOAD;
                            decode_error_latch_next <= '1';
                    end case;
                end if;
            when FETCH =>
                ready_in_sig    <= '1';
                -- Input Guard
                if (valid_in = '1') then
                    data_in_latch_next  <= data_in;
                    -- Alignment Operation in progress
                    if (align_op = '1') then
                        stage_next      <= ALIGN_IN_STREAM;
                        -- Reset
                        align_op_next   <= '0';
                    else
                        stage_next  <= DECODE_PAYLOAD;
                    end if;
                end if;
            when ALIGN_IN_STREAM =>
                -- Target Stream Alignment reached
                if (check_align(align_offset, target_align)) then
                    -- DONE
                    stage_next  <= DECODE_PAYLOAD;
                else
                    align_offset_next   <= align_offset + 1;
                    -- Need to fetch new Input Word
                    if (align_offset(1 downto 0) = "11") then
                        align_op_next   <= '1';
                        stage_next      <= FETCH;
                    end if;
                end if;
            when SKIP_PAYLOAD =>
                if (last_word_in_latch = '0') then
                    -- Skip Read
                    ready_in_sig    <= '1';
                else
                    stage_next  <= IDLE;
                    -- Reset
                    last_word_in_latch_next <= '0';
                end if;
            when DECODE_PAYLOAD =>
                case (decode_stage) is
                    -- ###GENERATED START###
                    when GET_ID =>
                        -- ALIGN GUARD
                        -- DES: If ALIGN_8 is used, we have to to check the latched opcode and use ALIGN_4 if PUSH_SERIALIZED_KEY (since it uses PLAIN_CDR2 encoding)
                        if (not check_align(align_offset, ALIGN_4)) then
                            target_align_next   <= ALIGN_4;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            id_latch_next       <= endian_swap(endian_flag, data_in_latch);
                            align_offset_next   <= align_offset + 4;
                            stage_next          <= FETCH;
                            -- Next Member
                            decode_stage_next    <= GET_TESTSEQUENCE_LENGTH;
                        end if;
                    when GET_TESTSEQUENCE_LENGTH =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_4)) then
                            target_align_next   <= ALIGN_4;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            tmp_length          := unsigned(endian_swap(endian_flag, data_in_latch));
                            align_offset_next   <= align_offset + 4;
                            stage_next          <= FETCH;
                            
                            -- Empty Sequence
                            if (tmp_length = 0) then
                                -- Next Member
                                stage_next  <= SKIP_PAYLOAD;
                            else
                                decode_stage_next               <= GET_TESTSEQUENCE_TESTARRAY;
                                if (tmp_length > TESTSEQUENCE_MAX_DEPTH) then
                                    TestSequence_len_latch_next     <= to_unsigned(TESTSEQUENCE_MAX_DEPTH, TestSequence_len_latch_next'length);
                                else
                                    TestSequence_len_latch_next     <= resize(tmp_length, TestSequence_len_latch_next'length);
                                end if;
                                TestSequence_cnt_next           <= 0;
                                -- DES: For array types the _cnt has to be initialized in the previous member stage
                                TestSequence_TestArray_cnt_next <= 0;
                            end if;
                        end if;
                    when GET_TESTSEQUENCE_TESTARRAY =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_1)) then
                            target_align_next   <= ALIGN_1;
                            stage_next          <= ALIGN_IN_STREAM;
                        else                            
                            TestSequence_TestArray_mem_addr(TestSequence_cnt)       <= std_logic_vector(to_unsigned(TestSequence_TestArray_cnt,TESTSEQUENCE_TESTARRAY_ADDR_WIDTH));
                            TestSequence_TestArray_mem_data_in(TestSequence_cnt)    <= endian_swap(endian_flag, get_sub_vector(data_in_latch, to_integer(align_offset(1 downto 0)), CDR_OCTET_WIDTH, TRUE));
                            TestSequence_TestArray_mem_valid_in(TestSequence_cnt)   <= '1';
                            -- Memory Operation Guard
                            if (TestSequence_TestArray_mem_ready_in(TestSequence_cnt) = '1') then
                                align_offset_next   <= align_offset + 1;
                                
                                -- All Elements processed
                                if (TestSequence_TestArray_cnt = TESTSEQUENCE_TESTARRAY_MAX_DEPTH-1) then
                                    -- DES: The decoding stages are used for both PUSH_DATA and PUSH_SERIALIZED_KEY.
                                    --      We us the latched opcode to differentiate between them.
                                    if (opcode_latch = PUSH_SERIALIZED_KEY) then
                                        -- All Elements processed
                                        if (TestSequence_cnt = TestSequence_len_latch-1) then
                                            -- Next Member
                                            stage_next  <= SKIP_PAYLOAD;
                                        else
                                            TestSequence_cnt_next           <= TestSequence_cnt + 1;
                                            decode_stage_next               <= GET_TESTSEQUENCE_TESTARRAY;
                                            TestSequence_TestArray_cnt_next <= 0;
                                        end if;
                                    else
                                        decode_stage_next    <= GET_TESTSEQUENCE_TESTCHAR;
                                    end if;
                                else
                                    TestSequence_TestArray_cnt_next <= TestSequence_TestArray_cnt + 1;
                                end if;
                                
                                -- DES: Needed for ALIGN_1
                                -- Need to fetch next Word
                                if(align_offset(1 downto 0) = "11") then
                                    stage_next  <= FETCH;
                                end if;
                            end if;
                        end if;
                    when GET_TESTSEQUENCE_TESTCHAR =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_1)) then
                            target_align_next   <= ALIGN_1;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            align_offset_next   <= align_offset + 1;
                            decode_stage_next   <= GET_TESTSEQUENCE_TESTWCHAR;
                                
                            -- Need to fetch next Word
                            if(align_offset(1 downto 0) = "11") then
                                stage_next  <= FETCH;
                            end if;
                        end if;
                    when GET_TESTSEQUENCE_TESTWCHAR =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_2)) then
                            target_align_next   <= ALIGN_2;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            align_offset_next   <= align_offset + 2;
                            decode_stage_next   <= GET_TESTSEQUENCE_TESTLONGLONG;
                            cnt_next            <= 0;
                            
                            -- DES: Needed for ALIGN_2
                            -- Need to fetch next Word
                            if(align_offset(1) = '1') then
                                stage_next  <= FETCH;
                            end if;
                        end if;
                    when GET_TESTSEQUENCE_TESTLONGLONG =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_8)) then
                            target_align_next   <= ALIGN_8;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            case (cnt) is
                                -- Double Word 1/2
                                when 0 =>
                                    stage_next  <= FETCH;
                                    cnt_next    <= cnt + 1;
                                -- Double Word 2/2
                                when 1 =>
                                    cnt_next    <= cnt + 1;
                                -- Push Double Word
                                when 2 =>
                                    stage_next          <= FETCH;
                                    align_offset_next   <= align_offset + 8;
                                    decode_stage_next   <= GET_OPTIONAL_HEADER;
                                    return_stage_next   <= GET_TESTSEQUENCE_TESTLONGDOUBLE;
                                    cnt_next            <= 0;
                                when others =>
                                    null;
                            end case;
                        end if;
                    when GET_TESTSEQUENCE_TESTLONGDOUBLE =>
                        -- Optional Omitted
                        if (optional = '0') then
                            -- All Elements processed
                            if (TestSequence_cnt = TestSequence_len_latch-1) then
                                -- Next Member
                                stage_next  <= SKIP_PAYLOAD;
                            else
                                TestSequence_cnt_next           <= TestSequence_cnt + 1;
                                decode_stage_next               <= GET_TESTSEQUENCE_TESTARRAY;
                                TestSequence_TestArray_cnt_next <= 0;
                            end if;
                        -- ALIGN GUARD
                        elsif (not check_align(align_offset, ALIGN_8)) then
                            target_align_next   <= ALIGN_8;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            case (cnt) is
                                -- Quad Word 1/4
                                when 0 =>
                                    stage_next  <= FETCH;
                                    cnt_next    <= cnt + 1;
                                -- Quad Word 2/4
                                when 1 =>
                                    stage_next  <= FETCH;
                                    cnt_next    <= cnt + 1;
                                -- Quad Word 3/4
                                when 2 =>
                                    stage_next  <= FETCH;
                                    cnt_next    <= cnt + 1;
                                -- Quad Word 4/4
                                when 3 =>
                                    cnt_next    <= cnt + 1;
                                -- Push Quad Word
                                when 4 =>
                                    stage_next          <= FETCH;
                                    align_offset_next   <= align_offset + 16;
                                    
                                    -- All Elements processed
                                    if (TestSequence_cnt = TestSequence_len_latch-1) then
                                        -- Next Member
                                        stage_next  <= SKIP_PAYLOAD;
                                    else
                                        TestSequence_cnt_next           <= TestSequence_cnt + 1;
                                        decode_stage_next               <= GET_TESTSEQUENCE_TESTARRAY;
                                        TestSequence_TestArray_cnt_next <= 0;
                                    end if;
                                when others =>
                                    null;
                            end case;
                        end if;
                    -- ###GENERATED END###
                    when GET_OPTIONAL_HEADER =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_4)) then
                            target_align_next   <= ALIGN_4;
                            stage_next          <= ALIGN_IN_STREAM;
                        else
                            case (cnt) is
                                -- Optional Member Header
                                when 0 =>
                                    -- Extended Parameter Header
                                    if (endian_swap(endian_flag,parameter_id) = PID_EXTENDED) then
                                        cnt_next    <= cnt + 1;
                                        stage_next  <= FETCH;
                                    else
                                        stage_next          <= FETCH;
                                        decode_stage_next   <= return_stage;
                                        cnt_next            <= 0;
                                        -- Alignment Reset 
                                        align_offset_next   <= (others => '0');
                                        
                                        -- Optional omitted
                                        if(endian_swap(endian_flag,parameter_length) = (parameter_length'reverse_range => '0')) then
                                            optional_next   <= '0';
                                        else
                                            optional_next   <= '1';
                                        end if;
                                    end if;
                                -- eMemberHeader 
                                when 1 =>
                                    -- Ignore Parameter ID
                                    cnt_next    <= cnt + 1;
                                    stage_next  <= FETCH;
                                -- Llength
                                when 2 =>
                                    stage_next          <= FETCH;
                                    decode_stage_next   <= return_stage;
                                    cnt_next            <= 0;
                                    -- Alignment Reset 
                                    align_offset_next   <= (others => '0');
                                    
                                    -- Optional omitted
                                    if(endian_swap(endian_flag, data_in) = (data_in'reverse_range => '0')) then
                                        optional_next   <= '0';
                                    else
                                        optional_next   <= '1';
                                    end if;
                                when others =>
                                    null;
                            end case;
                        end if;
                    when others =>
                        null;
                end case;
            when WRITE_PAYLOAD_HEADER =>
                valid_out   <= '1';
                if (LITTLE_ENDIAN = '0') then
                    data_out    <= CDR_BE & x"0000";
                else
                    data_out    <= CDR_LE & x"0000";
                end if;
                -- Output Guard
                if (ready_out = '1') then
                    stage_next          <= ENCODE_PAYLOAD;
                    -- Reset 
                    align_offset_next   <= (others => '0');
                    data_out_latch_next <= (others => '0');
                    -- ###GENERATED START###
                    encode_stage_next   <= WRITE_ID;
                    -- ###GENERATED END###
                end if;
            when PUSH =>
                -- Push to Key Hash Generator
                if (opcode_latch = READ_KEY_HASH) then
                    -- XXX: Assumes data_out_latch is 32 bits (TODO)
                    cnt2_ref    := std_logic_vector(unsigned(align_offset(1 downto 0)) - to_unsigned(1,2));
                    -- Mark Last Word
                    if (finalize_payload = '1' and cnt_2 = to_integer(unsigned(cnt2_ref))) then
                        last_word_in_kh <= '1';
                    end if;
                    
                    valid_in_kh <= '1';
                    data_in_kh  <= get_sub_vector(data_out_latch, cnt_2, BYTE_WIDTH, TRUE);
                    -- Output Guard
                    if (ready_in_kh = '1') then
                        -- Last Byte
                        if (cnt_2 = to_integer(unsigned(cnt2_ref))) then
                            -- Reset
                            cnt_2_next  <= 0;
                            -- Alignment Operation in process
                            if (align_op = '1') then
                                stage_next      <= ALIGN_OUT_STREAM;
                                -- Reset
                                align_op_next   <= '0';
                            -- DONE
                            elsif (finalize_payload = '1') then
                                stage_next              <= GET_KEY_HASH;
                                -- Reset
                                finalize_payload_next   <= '0';
                            else
                                stage_next  <= ENCODE_PAYLOAD;
                            end if;
                        else
                            cnt_2_next  <= cnt_2 + 1;
                        end if;
                    end if;
                else -- (opcode_latch = READ_SERIALIZED_KEY)
                    assert(opcode_latch = READ_SERIALIZED_KEY) severity FAILURE;
                    -- Mark Last Word
                    if (finalize_payload = '1') then
                        last_word_out   <= '1';
                    end if;
                    
                    valid_out   <= '1';
                    data_out    <= data_out_latch;
                    -- Output Guard
                    if (ready_out = '1') then
                        -- NOTE: Ensures all padding is zero.
                        data_out_latch_next <= (others => '0');
                        -- Alignment Operation in process
                        if (align_op = '1') then
                            stage_next      <= ALIGN_OUT_STREAM;
                            -- Reset
                            align_op_next   <= '0';
                        -- DONE
                        elsif (finalize_payload = '1') then
                            finalize_payload_next   <= '0';
                            stage_next              <= IDLE;
                        else
                            stage_next  <= ENCODE_PAYLOAD;
                        end if;
                    end if;
                end if;
            when ALIGN_OUT_STREAM =>
                -- Target Stream Alignment reached
                if (check_align(align_offset, target_align)) then
                    -- DONE
                    stage_next  <= ENCODE_PAYLOAD;
                else
                    align_offset_next   <= align_offset + 1;
                    -- Need to push Word
                    if (align_offset(1 downto 0) = "11") then
                        align_op_next   <= '1';
                        stage_next      <= PUSH;
                    end if;
                end if;
            when ENCODE_PAYLOAD =>
                case (encode_stage) is
                    -- ###GENERATED START###
                    when WRITE_ID =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_4)) then
                            target_align_next   <= ALIGN_4;
                            stage_next          <= ALIGN_OUT_STREAM;
                        else
                            data_out_latch_next <= endian_swap(endian_flag, id_latch);
                            align_offset_next   <= align_offset + 4;
                            stage_next          <= PUSH;
                            -- Next Member
                            encode_stage_next    <= WRITE_TESTSEQUENCE_LENGTH;
                        end if;
                    when WRITE_TESTSEQUENCE_LENGTH =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_4)) then
                            target_align_next   <= ALIGN_4;
                            stage_next          <= ALIGN_OUT_STREAM;
                        else
                            data_out_latch_next <= endian_swap(endian_flag, std_logic_vector(resize(TestSequence_len_latch,WORD_WIDTH)));
                            align_offset_next   <= align_offset + 4;
                            stage_next          <= PUSH;
                            
                            -- Empty Sequence
                            if (unsigned(TestSequence_len_latch) = 0) then
                                -- DONE
                                stage_next              <= PUSH;
                                finalize_payload_next   <= '1';
                            else
                                encode_stage_next       <= WRITE_TESTSEQUENCE_TESTARRAY;
                                cnt_next                <= 0;
                                TestSequence_cnt_next   <= 0;
                                -- DES: For array types the _cnt has to be initialized in the previous member stage
                                TestSequence_TestArray_cnt_next <= 0;
                            end if;
                        end if;
                    when WRITE_TESTSEQUENCE_TESTARRAY =>
                        -- ALIGN GUARD
                        if (not check_align(align_offset, ALIGN_1)) then
                            target_align_next   <= ALIGN_1;
                            stage_next          <= ALIGN_OUT_STREAM;
                        else                            
                            case (cnt) is
                                -- GET
                                when 0 =>
                                    TestSequence_TestArray_mem_addr(TestSequence_cnt)       <= std_logic_vector(to_unsigned(TestSequence_TestArray_cnt,TESTSEQUENCE_TESTARRAY_ADDR_WIDTH));
                                    TestSequence_TestArray_mem_valid_in(TestSequence_cnt)   <= '1';
                                    TestSequence_TestArray_mem_read(TestSequence_cnt)       <= '1';
                                    -- Memory Operation Guard
                                    if (TestSequence_TestArray_mem_ready_in(TestSequence_cnt) = '1') then
                                        cnt_next    <= cnt + 1;
                                    end if;
                                -- WRITE
                                when 1 =>
                                    TestSequence_TestArray_mem_ready_out(TestSequence_cnt)  <= '1';
                                    -- Memory Operation Guard
                                    if (TestSequence_TestArray_mem_valid_out(TestSequence_cnt) = '1') then
                                        data_out_latch_next <= write_sub_vector(data_out_latch, endian_swap(endian_flag,TestSequence_TestArray_mem_data_out(TestSequence_cnt)), to_integer(align_offset(1 downto 0)), TRUE);
                                        align_offset_next   <= align_offset + 1;
                                        cnt_next            <= 0;
                                        
                                        -- All Elements processed
                                        if (TestSequence_TestArray_cnt = TESTSEQUENCE_TESTARRAY_MAX_DEPTH-1) then
                                            -- All Elements processed
                                            if (TestSequence_cnt = unsigned(TestSequence_len_latch)-1) then
                                                -- DONE
                                                stage_next              <= PUSH;
                                                finalize_payload_next   <= '1';
                                            else
                                                TestSequence_cnt_next           <= TestSequence_cnt + 1;
                                                encode_stage_next               <= WRITE_TESTSEQUENCE_TESTARRAY;
                                                TestSequence_TestArray_cnt_next <= 0;
                                                cnt_next                        <= 0;
                                            end if;
                                        else
                                            TestSequence_TestArray_cnt_next <= TestSequence_TestArray_cnt + 1;
                                        end if;
                                        
                                        -- DES: Needed for ALIGN_1
                                        -- Need to fetch next Word
                                        if(align_offset(1 downto 0) = "11") then
                                            stage_next  <= PUSH;
                                        end if;
                                    end if;
                                when others => 
                            end case;
                        end if;
                    -- ###GENERATED END###
                end case;
            when GET_KEY_HASH =>
                if (done_kh = '1') then
                    key_hash_next   <= to_key_hash(key_hash_kh);
                    stage_next      <= PUSH_KEY_HASH;
                    cnt_next        <= 0;
                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;
            -- Reset
            last_word_in_latch_next <= '0';
            align_op_next           <= '0';
            finalize_payload_next   <= '0';
        -- OVERREAD GUARD
        -- Attempted read on empty input
        elsif (last_word_in_latch = '1' and last_word_in = '0' and ready_in_sig = '1') then
            stage_next  <= SKIP_PAYLOAD;
            decode_error_latch_next <= '1';
        end if;
        
    end process;
    
    sync_prc : process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                stage               <= IDLE;
                decode_stage        <= GET_ID;
                encode_stage        <= WRITE_ID;
                return_stage        <= GET_ID;
                target_align        <= ALIGN_1;
                opcode_latch        <= NOP;
                cnt                 <= 0;
                cnt_2               <= 0;
                endian_flag         <= '0';
                last_word_in_latch  <= '0';
                decode_error_latch  <= '0';
                optional            <= '0';
                align_op            <= '0';
                finalize_payload    <= '0';
                align_offset        <= (others => '0');
                data_in_latch       <= (others => '0');
                data_out_latch      <= (others => '0');
                key_hash            <= KEY_HASH_NIL;
                -- ###GENERATED START###
                id_latch                    <= (others => '0');
                TestSequence_len_latch      <= (others => '0');
                TestSequence_cnt            <= 0;
                TestSequence_TestArray_cnt  <= 0;
                -- ###GENERATED END###
            else
                stage               <= stage_next;
                decode_stage        <= decode_stage_next;
                encode_stage        <= encode_stage_next;
                return_stage        <= return_stage_next;
                target_align        <= target_align_next;
                opcode_latch        <= opcode_latch_next;
                cnt                 <= cnt_next;
                cnt_2               <= cnt_2_next;
                endian_flag         <= endian_flag_next;
                last_word_in_latch  <= last_word_in_latch_next;
                decode_error_latch  <= decode_error_latch_next;
                optional            <= optional_next;
                align_op            <= align_op_next;
                finalize_payload    <= finalize_payload_next;
                align_offset        <= align_offset_next;
                data_in_latch       <= data_in_latch_next;
                data_out_latch      <= data_out_latch_next;
                key_hash            <= key_hash_next;
                -- ###GENERATED START###
                id_latch                    <= id_latch_next;
                TestSequence_len_latch      <= TestSequence_len_latch_next;
                TestSequence_cnt            <= TestSequence_cnt_next;
                TestSequence_TestArray_cnt  <= TestSequence_TestArray_cnt_next;
                -- ###GENERATED END###
            end if;
        end if;
    end process;
    
end architecture;