-- 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;

entity test_loopback is
    port (
        -- SYSTEM
        clk                                 : in std_logic;
        reset                               : in std_logic;
        -- READER
        start_r                             : out std_logic;
        ack_r                               : in std_logic;
        opcode_r                            : out DDS_READER_OPCODE_TYPE;
        instance_state_r                    : out std_logic_vector(INSTANCE_STATE_KIND_WIDTH-1 downto 0);
        view_state_r                        : out std_logic_vector(VIEW_STATE_KIND_WIDTH-1 downto 0);
        sample_state_r                      : out std_logic_vector(SAMPLE_STATE_KIND_WIDTH-1 downto 0);
        instance_handle_r                   : out INSTANCE_HANDLE_TYPE;
        max_samples_r                       : out std_logic_vector(MAX_SAMPLES_WIDTH-1 downto 0);
        get_data_r                          : out std_logic;
        done_r                              : in std_logic;
        return_code_r                       : in std_logic_vector(RETURN_CODE_WIDTH-1 downto 0);
        sample_info_r                       : in SAMPLE_INFO_TYPE;
        sample_info_valid_r                 : in std_logic;
        sample_info_ack_r                   : out std_logic;
        eoc_r                               : in std_logic;
        status_r                            : in std_logic_vector(STATUS_KIND_WIDTH-1 downto 0);
        decode_error_r                      : in std_logic;
        id_r                                : in std_logic_vector(CDR_LONG_WIDTH-1 downto 0);
        a_r                                 : in std_logic_vector(CDR_LONG_WIDTH-1 downto 0);
        valid_r                             : in std_logic;
        -- WRITER
        start_w                             : out std_logic;
        ack_w                               : in std_logic;
        opcode_w                            : out DDS_WRITER_OPCODE_TYPE;
        instance_handle_out_w               : out INSTANCE_HANDLE_TYPE;
        source_ts_w                         : out TIME_TYPE;
        max_wait_w                          : out DURATION_TYPE;
        done_w                              : in std_logic;
        return_code_w                       : in std_logic_vector(RETURN_CODE_WIDTH-1 downto 0);
        instance_handle_in_w                : in INSTANCE_HANDLE_TYPE;
        status_w                            : in std_logic_vector(STATUS_KIND_WIDTH-1 downto 0);
        id_w                                : out std_logic_vector(CDR_LONG_WIDTH-1 downto 0);
        a_w                                 : out std_logic_vector(CDR_LONG_WIDTH-1 downto 0);
        encode_done_w                       : in std_logic
    );
end entity;

architecture arch of test_loopback is
    
    --*****TYPE DECLARATION*****
    type STAGE_TYPE is (INITIALIZE, IDLE, READ, PROCESS_MESSAGE, WRITE);
    
    --*****SIGNAL DECLARATION*****
    signal stage, stage_next : STAGE_TYPE;
    signal cnt, cnt_next : natural range 0 to 3;
    signal inst, inst_next : INSTANCE_HANDLE_TYPE;
    signal long, long_next : std_logic_vector(CDR_LONG_WIDTH-1 downto 0);
    
    
begin
    
    main_prc : process(all)
    begin
        -- DEFAULT
        stage_next              <= stage;
        cnt_next                <= cnt;
        inst_next               <= inst;
        long_next               <= long;
        -- DEFAULT Unregistered
        start_r                 <= '0';
        opcode_r                <= NOP;
        instance_state_r        <= ANY_INSTANCE_STATE;
        view_state_r            <= ANY_VIEW_STATE;
        sample_state_r          <= ANY_SAMPLE_STATE;
        instance_handle_r       <= HANDLE_NIL;
        max_samples_r           <= (others => '0');
        get_data_r              <= '0';
        sample_info_ack_r       <= '0';
        start_w                 <= '0';
        opcode_w                <= NOP;
        instance_handle_out_w   <= HANDLE_NIL;
        source_ts_w             <= TIME_INVALID;
        max_wait_w              <= DURATION_ZERO;
        id_w                    <= (others => '0');
        a_w                     <= (others => '0');
        
        
        case (stage) is
            -- Get the Instance Handle for the target Topic Instance
            when INITIALIZE =>
                id_w    <= std_logic_vector(to_unsigned(1, CDR_LONG_WIDTH));
                case (cnt) is
                    when 0 =>
                        start_w     <= '1';
                        opcode_w    <= REGISTER_INSTANCE;
                        if (ack_w = '1') then
                            cnt_next    <= cnt + 1;
                        end if;
                    when 1 =>
                        if (done_w = '1') then
                            inst_next   <= instance_handle_in_w;
                            stage_next  <= IDLE;
                        end if;
                    when others =>
                        null;
                end case;
            when IDLE =>
                -- Reader has Available Data
                if (check_mask(status_r,DATA_AVAILABLE_STATUS)) then
                    stage_next  <= READ;
                    cnt_next    <= 0;
                end if;
            when READ =>
                case (cnt) is
                    when 0 =>
                        start_r     <= '1';
                        opcode_r    <= TAKE_NEXT_SAMPLE;
                        if (ack_r = '1') then
                            cnt_next    <= cnt + 1;
                        end if;
                    when 1 =>
                        if (done_r = '1') then
                            case (return_code_r) is
                                when RETCODE_OK =>
                                    cnt_next    <= cnt + 1;
                                when RETCODE_NO_DATA =>
                                    stage_next          <= IDLE;
                                when others =>
                                    assert FALSE report "Unexpected DDS Reader Return Code" severity FAILURE;
                            end case;
                        end if;
                    when 2 =>
                        if (sample_info_valid_r = '1') then
                            sample_info_ack_r   <= '1';
                            -- Target Instance with Data
                            if (sample_info_r.instance_handle = inst and sample_info_r.valid_data = '1') then
                                cnt_next    <= cnt + 1;
                                get_data_r  <= '1';
                            else
                                -- Read next Sample
                                cnt_next    <= 0;
                            end if;
                        end if;
                    when 3 =>
                        if (valid_r = '1') then
                            assert (id_r = std_logic_vector(to_unsigned(1,CDR_LONG_WIDTH))) severity FAILURE;
                            long_next   <= a_r;
                            stage_next  <= PROCESS_MESSAGE;
                        end if;
                    when others =>
                        null;
                end case;
            when PROCESS_MESSAGE =>
                -- Add 1k to Message
                long_next   <= std_logic_vector(unsigned(long) + to_unsigned(1000, CDR_LONG_WIDTH));
                stage_next  <= WRITE;
                cnt_next    <= 0;    
            when WRITE =>
                id_w    <= std_logic_vector(to_unsigned(2, CDR_LONG_WIDTH));
                a_w     <= long;
                case (cnt) is
                    when 0 =>
                        start_w     <= '1';
                        opcode_w    <= WRITE;
                        if (ack_w = '1') then
                            cnt_next    <= cnt + 1;
                        end if;
                    when 1 =>
                        if (done_w = '1') then
                            case (return_code_w) is
                                when RETCODE_OK =>
                                    stage_next  <= READ;
                                    cnt_next    <= 0;
                                when RETCODE_OUT_OF_RESOURCES =>
                                    -- Retry
                                    cnt_next    <= 0;
                                when others =>
                                    assert FALSE report "Unexpected DDS Writer Return Code" severity FAILURE;
                            end case;
                        end if;
                    when others =>
                        null;
                end case;
        end case;
    end process;
    
    sync_prc : process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                stage   <= INITIALIZE;
                inst    <= HANDLE_NIL;
                cnt     <= 0;
                long    <= (others => '0');
            else
                stage   <= stage_next;
                inst    <= inst_next;
                cnt     <= cnt_next;
                long    <= long_next;
            end if;
        end if;
    end process;
    
end architecture;
