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

library osvvm; -- Utility Library
context osvvm.OsvvmContext;

use work.rtps_package.all;
use work.user_config.all;
use work.rtps_config_package.all;
use work.rtps_test_package.all;

-- This testbench tests the Deadline Handling of the DDS Writer, and more specifically the GET_OFFERED_DEADLINE_MISSED_STATUS DDS Operation.

entity L0_dds_writer_test3 is
end entity;

architecture testbench of L0_dds_writer_test3 is
    
    -- *CONSTANT DECLARATION*
    constant MAX_REMOTE_ENDPOINTS   : natural := 3;
    constant NUM_WRITERS            : natural := 3;
    
    impure function gen_test_config return CONFIG_ARRAY_TYPE is
        variable ret : CONFIG_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => DEFAULT_WRITER_CONFIG);
    begin
        -- aik
        ret(0).HISTORY_QOS              := KEEP_ALL_HISTORY_QOS;
        ret(0).DEADLINE_QOS             := gen_duration(1 sec);
        ret(0).LIFESPAN_QOS             := DURATION_INFINITE;
        ret(0).LEASE_DURATION           := DURATION_INFINITE;
        ret(0).WITH_KEY                 := TRUE;
        ret(0).MAX_SAMPLES              := std_logic_vector(to_unsigned(4,CDR_LONG_WIDTH));
        ret(0).MAX_INSTANCES            := std_logic_vector(to_unsigned(3,CDR_LONG_WIDTH));
        ret(0).MAX_SAMPLES_PER_INSTANCE := std_logic_vector(to_unsigned(2,CDR_LONG_WIDTH));
        ret(0).MAX_PAYLOAD_SIZE         := 40;
        -- ain
        ret(1).HISTORY_QOS              := KEEP_ALL_HISTORY_QOS;
        ret(1).DEADLINE_QOS             := gen_duration(1 sec);
        ret(1).LIFESPAN_QOS             := DURATION_INFINITE;
        ret(1).LEASE_DURATION           := DURATION_INFINITE;
        ret(1).WITH_KEY                 := FALSE;
        ret(1).MAX_SAMPLES              := std_logic_vector(to_unsigned(4,CDR_LONG_WIDTH));
        ret(1).MAX_INSTANCES            := std_logic_vector(to_unsigned(3,CDR_LONG_WIDTH));
        ret(1).MAX_SAMPLES_PER_INSTANCE := std_logic_vector(to_unsigned(4,CDR_LONG_WIDTH));
        ret(1).MAX_PAYLOAD_SIZE         := 40;
        -- aik
        ret(2).HISTORY_QOS              := KEEP_ALL_HISTORY_QOS;
        ret(2).DEADLINE_QOS             := gen_duration(2 sec);
        ret(2).LIFESPAN_QOS             := DURATION_INFINITE;
        ret(2).LEASE_DURATION           := DURATION_INFINITE;
        ret(2).WITH_KEY                 := TRUE;
        ret(2).MAX_SAMPLES              := std_logic_vector(to_unsigned(4,CDR_LONG_WIDTH));
        ret(2).MAX_INSTANCES            := std_logic_vector(to_unsigned(3,CDR_LONG_WIDTH));
        ret(2).MAX_SAMPLES_PER_INSTANCE := std_logic_vector(to_unsigned(2,CDR_LONG_WIDTH));
        ret(2).MAX_PAYLOAD_SIZE         := 40;
        return ret;
    end function;
    constant TEST_CONFIG : CONFIG_ARRAY_TYPE := gen_test_config;
    
    -- *TYPE DECLARATION*
    type DDS_STAGE_TYPE is (IDLE, START, PUSH, DONE, CHECK_DEADLINE);
    type RTPS_STAGE_TYPE is (IDLE, START, DONE, CHECK);
    
    -- *SIGNAL DECLARATION*
    signal clk : std_logic := '0';
    signal reset : std_logic := '1';
    signal check_time : TIME_TYPE := TIME_ZERO;
    signal start_rtps, start_dds, ack_rtps, ack_dds, done_rtps, done_dds, w_map : std_logic_vector(0 to NUM_WRITERS-1) := (others => '0');
    signal opcode_rtps : HISTORY_CACHE_OPCODE_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => NOP);
    signal opcode_dds : DDS_WRITER_OPCODE_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => NOP);
    signal ret_rtps : HISTORY_CACHE_RESPONSE_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => ERROR);
    signal seq_nr_rtps, cc_seq_nr : SEQUENCENUMBER_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => SEQUENCENUMBER_UNKNOWN);
    signal ready_out_rtps, valid_out_rtps, last_word_out_rtps : std_logic_vector(0 to NUM_WRITERS-1) := (others => '0');
    signal ready_in_dds, ready_out_dds, valid_in_dds, valid_out_dds, last_word_in_dds, last_word_out_dds : std_logic_vector(0 to NUM_WRITERS-1) := (others => '0');
    signal data_out_rtps, data_in_dds, data_out_dds : WORD_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => (others => '0'));
    signal get_data_rtps, liveliness_assertion, data_available : std_logic_vector(0 to NUM_WRITERS-1) := (others => '0');
    signal cc_source_timestamp, source_ts_dds : TIME_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => TIME_INVALID);
    signal cc_kind : CACHE_CHANGE_KIND_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => ALIVE);
    signal cc_instance_handle, instance_handle_in_dds, instance_handle_out_dds : INSTANCE_HANDLE_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => HANDLE_NIL);
    signal max_wait_dds : DURATION_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => DURATION_INFINITE);
    signal return_code_dds : RETURN_CODE_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => (others => '0'));
    signal status : STATUS_KIND_ARRAY_TYPE(0 to NUM_WRITERS-1) := (others => (others => '0'));
    
    signal ind : natural := 0;
    signal dds_start, dds_done, rtps_start, rtps_done : std_logic := '0';
    signal dds_cnt, rtps_cnt : natural := 0;
    signal dds_stage : DDS_STAGE_TYPE := IDLE;
    signal rtps_stage : RTPS_STAGE_TYPE := IDLE;
    shared variable dds : DDS_WRITER_TEST_TYPE := DEFAULT_DDS_WRITER_TEST;
    shared variable rtps : RTPS_WRITER_TEST_TYPE := DEFAULT_RTPS_WRITER_TEST;
    signal inst_id, kind_id, sn_id, ts_id, data_id, ret_id, status_id, ih_id : AlertLogIDType;
    
    -- *FUNCTION DECLARATION*
    function extract_key_hash (payload : TEST_PACKET_TYPE) return INSTANCE_HANDLE_TYPE is
        variable ret : INSTANCE_HANDLE_TYPE := HANDLE_NIL;
    begin
        for i in 0 to 3 loop
            ret(i) := not payload.data(i);
        end loop;
        
        return ret;
    end function;
    
    function gen_sn(input : natural) return SEQUENCENUMBER_TYPE is
        variable ret : SEQUENCENUMBER_TYPE;
    begin
        ret(0)  := (others => '0');
        ret(1)  := unsigned(int(input, WORD_WIDTH));
        return ret;
    end function;
    
    procedure wait_on_sig(signal sig : std_logic) is
    begin
        if (sig /= '1') then
            wait on sig until sig = '1';
        end if;
    end procedure;
    
begin

    -- Unit Under Test
    uut : entity work.dds_writer(arch)
        generic map(
            NUM_WRITERS     => NUM_WRITERS,
            CONFIG_ARRAY    => to_QUARTUS_CONFIG_ARRAY_TYPE(TEST_CONFIG)
        )
        port map (
            clk                     => clk,
            reset                   => reset,
            time                    => check_time,
            start_rtps              => start_rtps,
            opcode_rtps             => opcode_rtps,
            ack_rtps                => ack_rtps,
            done_rtps               => done_rtps,
            ret_rtps                => ret_rtps,
            seq_nr_rtps             => seq_nr_rtps,
            get_data_rtps           => get_data_rtps,
            data_out_rtps           => data_out_rtps,
            valid_out_rtps          => valid_out_rtps,
            ready_out_rtps          => ready_out_rtps,
            last_word_out_rtps      => last_word_out_rtps,
            liveliness_assertion    => liveliness_assertion,
            data_available          => data_available,
            cc_instance_handle      => cc_instance_handle,
            cc_kind                 => cc_kind,
            cc_source_timestamp     => cc_source_timestamp,
            cc_seq_nr               => cc_seq_nr,
            start_dds               => start_dds,
            ack_dds                 => ack_dds,
            opcode_dds              => opcode_dds,
            instance_handle_in_dds  => instance_handle_in_dds,
            source_ts_dds           => source_ts_dds,
            max_wait_dds            => max_wait_dds,
            done_dds                => done_dds,
            return_code_dds         => return_code_dds,
            instance_handle_out_dds => instance_handle_out_dds,
            ready_in_dds            => ready_in_dds,
            valid_in_dds            => valid_in_dds,
            data_in_dds             => data_in_dds,
            last_word_in_dds        => last_word_in_dds,
            ready_out_dds           => ready_out_dds,
            valid_out_dds           => valid_out_dds,
            data_out_dds            => data_out_dds,
            last_word_out_dds       => last_word_out_dds,
            status                  => status
        );
    
    stimulus_prc : process
        variable RV                 : RandomPType;
        variable kh1, kh2, kh3, kh4 : INSTANCE_HANDLE_TYPE := HANDLE_NIL;
        variable cc                 : CACHE_CHANGE_TYPE := DEFAULT_CACHE_CHANGE;
        
        alias idle_sig is <<signal uut.idle_sig : std_logic>>;
        
        impure function gen_payload(key_hash : INSTANCE_HANDLE_TYPE; len : natural) return TEST_PACKET_TYPE is
            variable ret : TEST_PACKET_TYPE := EMPTY_TEST_PACKET;
        begin
            assert (len >= 4) report "Payload length has to be at least 16 Bytes long" severity FAILURE;
            
            for i in 0 to len-1 loop
                if (i < 4) then
                    -- NOTE: Beginning of payload is negated key to allow deterministic Key Hash generation from the kh_prc
                    ret.data(ret.length) := not key_hash(i);
                else
                    ret.data(ret.length) := RV.RandSlv(WORD_WIDTH);
                end if;
                ret.length  := ret.length + 1;
            end loop;
            ret.last(ret.length-1)  := '1';
            
            return ret;
        end function;
        
        impure function gen_key_hash return KEY_HASH_TYPE is
            variable ret : KEY_HASH_TYPE := KEY_HASH_NIL;
        begin
            for i in 0 to KEY_HASH_TYPE'length-1 loop
                ret(i) := RV.RandSlv(WORD_WIDTH);
            end loop;
            return ret;
        end function;
        
        procedure start_dds is
        begin
            dds_start  <= '1';
            wait until rising_edge(clk);
            dds_start  <= '0';
            wait until rising_edge(clk);
        end procedure;
        
        procedure start_rtps is
        begin
            rtps_start   <= '1';
            wait until rising_edge(clk);
            rtps_start   <= '0';
            wait until rising_edge(clk);
        end procedure;
        
        procedure wait_on_completion is
        begin
            if (rtps_done /= '1' or dds_done /= '1') then
                wait until rtps_done = '1' and dds_done = '1';
            end if;
        end procedure;
        
        -- NOTE: This procedure waits until the idle_sig is high for at least
        --       two consecutive clock cycles.
        procedure wait_on_idle is
            variable first : boolean := TRUE;
        begin
            loop
                if (idle_sig /= '1') then
                    wait until idle_sig = '1';
                elsif (not first) then
                    exit;
                end if;
                wait until rising_edge(clk);
                wait until rising_edge(clk);
                first := FALSE;
            end loop;
        end procedure;
        
    begin
        
        SetAlertLogName("L0_dds_writer_test3 - Deadline Handling");
        SetAlertEnable(FAILURE, TRUE);
        SetAlertEnable(ERROR, TRUE);
        SetAlertEnable(WARNING, TRUE);
        SetLogEnable(DEBUG, FALSE);
        SetLogEnable(PASSED, FALSE);
        SetLogEnable(INFO, TRUE);
        RV.InitSeed(RV'instance_name);
        inst_id     <= GetAlertLogID("Instance", ALERTLOG_BASE_ID);
        kind_id     <= GetAlertLogID("Cache Change Kind", ALERTLOG_BASE_ID);
        sn_id       <= GetAlertLogID("SequenceNumber", ALERTLOG_BASE_ID);
        ts_id       <= GetAlertLogID("TimeStamp", ALERTLOG_BASE_ID);
        ih_id       <= GetAlertLogID("Instance Handle", ALERTLOG_BASE_ID);
        data_id     <= GetAlertLogID("Data Out", ALERTLOG_BASE_ID);
        ret_id      <= GetAlertLogID("Return Code", ALERTLOG_BASE_ID);
        status_id   <= GetAlertLogID("Communication Status", ALERTLOG_BASE_ID);
        
        -- Key Hashes
        kh1 := gen_key_hash;
        kh2 := gen_key_hash;
        kh3 := gen_key_hash;
        kh4 := gen_key_hash;
        
        
        
        Log("Initiating Test", INFO);
        Log("Current Time: 0s", INFO);
        check_time  <= TIME_ZERO;
        reset       <= '1';
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        reset   <= '0';
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        Log("Current Time: 1s", INFO);
        check_time  <= gen_duration(1 sec);
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        cc                  := DEFAULT_CACHE_CHANGE;
        cc.serialized_key   := FALSE;
        cc.kind             := ALIVE;
        cc.instance         := kh1;
        cc.payload          := gen_payload(kh1,10);
        
        Log("W0,W1,W2: DDS Operation WRITE [Instance 1, HANDLE_NIL, Aligned Payload]", INFO);
        Log("W0,W1,W2: ACCEPTED", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := WRITE;
        dds.cc         := cc;
        dds.cc.instance:= HANDLE_NIL;
        dds.ret_code   := RETCODE_OK;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        -- WRITER 1
        ind <= 1;
        start_dds;
        wait_on_sig(dds_done);
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        cc                  := DEFAULT_CACHE_CHANGE;
        cc.serialized_key   := FALSE;
        cc.kind             := ALIVE;
        cc.instance         := kh2;
        cc.payload          := gen_payload(kh2,10);
        
        Log("W0,W2: DDS Operation WRITE [Instance 2, HANDLE_NIL, Aligned Payload]", INFO);
        Log("W0,W2: ACCEPTED", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := WRITE;
        dds.cc         := cc;
        dds.cc.instance:= HANDLE_NIL;
        dds.ret_code   := RETCODE_OK;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        Log("Current Time: 2s", INFO);
        check_time  <= gen_duration(2 sec);
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        cc                  := DEFAULT_CACHE_CHANGE;
        cc.serialized_key   := FALSE;
        cc.kind             := ALIVE;
        cc.instance         := kh1;
        cc.payload          := gen_payload(kh1,10);
        
        Log("W0,W2: DDS Operation WRITE [Instance 1, Aligned Payload]", INFO);
        Log("W0,W2: ACCEPTED", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := WRITE;
        dds.cc         := cc;
        dds.cc.instance:= HANDLE_NIL;
        dds.ret_code   := RETCODE_OK;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        Log("Current Time: 3s", INFO);
        check_time  <= gen_duration(3 sec);
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        Log("W0: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W0: Expected [count 1, change 1, Instance 2]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 1;
        dds.change     := 1;
        dds.inst       := kh2;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        Log("W1: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W1: Expected [count 1, change 1]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 1;
        dds.change     := 1;
        dds.inst       := HANDLE_NIL;
        -- WRITER 1
        ind <= 1;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        Log("W2: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W2: Expected [count 0, change 0, HANDLE_NIL]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 0;
        dds.change     := 0;
        dds.inst       := HANDLE_NIL;
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        cc                  := DEFAULT_CACHE_CHANGE;
        cc.serialized_key   := FALSE;
        cc.kind             := ALIVE;
        cc.instance         := kh3;
        cc.payload          := gen_payload(kh3,10);
        
        Log("W0,W2: DDS Operation WRITE [Instance 3, Aligned Payload]", INFO);
        Log("W0,W2: ACCEPTED", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := WRITE;
        dds.cc         := cc;
        dds.cc.instance:= HANDLE_NIL;
        dds.ret_code   := RETCODE_OK;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        Log("Current Time: 4s", INFO);
        check_time  <= gen_duration(4 sec);
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        
        Log("W0: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W0: Expected [count 3, change 2, Instance 1]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 3;
        dds.change     := 2;
        dds.inst       := kh1;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        
        Log("W2: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W2: Expected [count 1, change 1, Instance 2]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 1;
        dds.change     := 1;
        dds.inst       := kh2;
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        Log("Current Time: 5s", INFO);
        check_time  <= gen_duration(5 sec);
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        cc                  := DEFAULT_CACHE_CHANGE;
        cc.serialized_key   := FALSE;
        cc.kind             := ALIVE;
        cc.instance         := kh1;
        cc.payload          := gen_payload(kh1,10);
        
        Log("W1: DDS Operation WRITE [Instance 1, Aligned Payload]", INFO);
        Log("W1: ACCEPTED", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := WRITE;
        dds.cc         := cc;
        dds.cc.instance:= HANDLE_NIL;
        dds.ret_code   := RETCODE_OK;
        -- WRITER 1
        ind <= 1;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        Log("Current Time: 6s", INFO);
        check_time  <= gen_duration(6 sec);
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        
        Log("W0: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W0: Expected [count 9, change 6, Instance 1]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 9;
        dds.change     := 6;
        dds.inst       := kh1;
        -- WRITER 0
        ind <= 0;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        
        Log("W1: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W1: Expected [count 3, change 2]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 3;
        dds.change     := 2;
        dds.inst       := HANDLE_NIL;
        -- WRITER 1
        ind <= 1;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) = OFFERED_DEADLINE_MISSED_STATUS, "Expected: 1", "Received: 0");
        
        Log("W2: DDS Operation GET_OFFERED_DEADLINE_MISSED_STATUS", INFO);
        Log("W2: Expected [count 4, change 3, Instance 1]", DEBUG);
        dds            := DEFAULT_DDS_WRITER_TEST;
        dds.opcode     := GET_OFFERED_DEADLINE_MISSED_STATUS;
        dds.ret_code   := RETCODE_OK;
        dds.count      := 4;
        dds.change     := 3;
        dds.inst       := kh1;
        -- WRITER 2
        ind <= 2;
        start_dds;
        wait_on_sig(dds_done);
        wait_on_idle;
        
        AffirmIf(status_id,(status(0) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(1) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        AffirmIf(status_id,(status(2) and OFFERED_DEADLINE_MISSED_STATUS) /= OFFERED_DEADLINE_MISSED_STATUS, "Expected: 0", "Received: 1");
        
        wait_on_completion;
        TranscriptOpen(RESULTS_FILE, APPEND_MODE);
        SetTranscriptMirror;
        ReportAlerts;
        TranscriptClose;
        std.env.stop;
        wait;
    end process;
    
    clock_prc : process
    begin
        clk <= '0';
        wait for 25 ns;
        clk <= '1';
        wait for 25 ns;
    end process;
    
    dds_prc : process(all)
    begin
        if rising_edge(clk) then
            dds_done   <= '0';
            case (dds_stage) is
                when IDLE =>
                    if (dds_start = '1') then
                        dds_stage  <= START;
                    else
                        dds_done   <= '1';
                    end if;
                when START =>
                    if (ack_dds(ind) = '1') then
                        case (dds.opcode) is
                            when GET_OFFERED_DEADLINE_MISSED_STATUS =>
                                dds_stage  <= DONE;
                                dds_cnt    <= 0;
                            when others =>
                                dds_stage  <= PUSH;
                                dds_cnt    <= 0;
                        end case;
                    end if;
                when PUSH =>
                    if (ready_in_dds(ind) = '1') then
                        dds_cnt    <= dds_cnt + 1;
                        if (dds_cnt = dds.cc.payload.length-1) then
                            -- DEFAULT
                            dds_stage  <= DONE;
                        end if;
                    end if;
                when DONE =>
                    if (done_dds(ind) = '1') then
                        if (dds.opcode = REGISTER_INSTANCE or dds.opcode = LOOKUP_INSTANCE) then
                            AffirmIfEqual(ih_id, to_unsigned(instance_handle_out_dds(ind)), to_unsigned(dds.cc.instance));
                        else
                            AffirmIfEqual(ret_id, return_code_dds(ind), dds.ret_code);
                            case (dds.opcode) is
                                when GET_OFFERED_DEADLINE_MISSED_STATUS =>
                                    if (dds.ret_code = RETCODE_OK) then
                                        dds_stage  <= CHECK_DEADLINE;
                                        dds_cnt    <= 0;
                                    else
                                        dds_stage  <= IDLE;
                                    end if;
                                when others =>
                                    dds_stage  <= IDLE;
                            end case;
                        end if;
                    end if;
                when CHECK_DEADLINE =>
                    if (valid_out_dds(ind) = '1') then
                        dds_cnt <= dds_cnt + 1;
                        case (dds_cnt) is
                            when 0 =>
                                AffirmIfEqual(data_id, data_out_dds(ind), std_logic_vector(to_unsigned(dds.count,CDR_LONG_WIDTH)));
                            when 1 =>
                                AffirmIfEqual(data_id, data_out_dds(ind), std_logic_vector(to_unsigned(dds.change,CDR_LONG_WIDTH)));
                            when 2 =>
                                AffirmIfEqual(data_id, data_out_dds(ind), std_logic_vector(dds.inst(0)));
                            when 3 =>
                                AffirmIfEqual(data_id, data_out_dds(ind), std_logic_vector(dds.inst(1)));
                            when 4 =>
                                AffirmIfEqual(data_id, data_out_dds(ind), std_logic_vector(dds.inst(2)));
                            when 5 =>
                                AffirmIfEqual(data_id, data_out_dds(ind), std_logic_vector(dds.inst(3)));
                                AlertIf(data_id, last_word_out_dds(ind) /= '1', "Last Word Signal not pulled High", ERROR);
                                dds_stage  <= IDLE;
                            when others =>
                                null;
                        end case;
                    end if;
            end case;
        end if;
        
        -- DEFAULT
        start_dds               <= (others => '0');
        opcode_dds              <= (others => NOP);
        valid_in_dds            <= (others => '0');
        last_word_in_dds        <= (others => '0');
        data_in_dds             <= (others => (others => '0'));
        instance_handle_in_dds  <= (others => HANDLE_NIL);
        source_ts_dds           <= (others => TIME_INVALID);
        ready_out_dds           <= (others => '0');
        
        case (dds_stage) is
            when START =>
                start_dds(ind)              <= '1';
                opcode_dds(ind)             <= dds.opcode;
                instance_handle_in_dds(ind) <= dds.cc.instance;
                source_ts_dds(ind)          <= dds.cc.src_timestamp;
            when PUSH =>
                valid_in_dds(ind)       <= '1';
                data_in_dds(ind)        <= dds.cc.payload.data(dds_cnt);
                last_word_in_dds(ind)   <= dds.cc.payload.last(dds_cnt);
            when CHECK_DEADLINE =>
                ready_out_dds(ind)      <= '1';
            when others =>
                null;
        end case;
    end process;
    
    rtps_prc : process(all)
    begin
        if rising_edge(clk) then
            rtps_done    <= '0';
            case (rtps_stage) is
                when IDLE =>
                    if (rtps_start = '1') then
                        rtps_stage   <= START;
                    else
                        rtps_done    <= '1';
                    end if;
                when START =>
                    if (ack_rtps(ind) = '1') then
                        rtps_stage   <= DONE;
                    end if;
                when DONE =>
                    if (done_rtps(ind) = '1') then
                        -- DEFAULT
                        rtps_stage   <= IDLE;
                        
                        AffirmIfEqual(ret_id, HISTORY_CACHE_RESPONSE_TYPE'pos(ret_rtps(ind)), HISTORY_CACHE_RESPONSE_TYPE'pos(rtps.ret_code));
                        case (rtps.opcode) is
                            when GET_CACHE_CHANGE =>
                                if (rtps.ret_code = OK) then
                                    AffirmIfEqual(inst_id, to_unsigned(cc_instance_handle(ind)), to_unsigned(rtps.cc.instance));
                                    AffirmIfEqual(kind_id, CACHE_CHANGE_KIND_TYPE'pos(cc_kind(ind)), CACHE_CHANGE_KIND_TYPE'pos(rtps.cc.kind));
                                    AffirmIfEqual(sn_id, to_unsigned(cc_seq_nr(ind)), to_unsigned(rtps.cc.seq_nr));
                                    AffirmIfEqual(ts_id, to_unsigned(cc_source_timestamp(ind)), to_unsigned(rtps.cc.src_timestamp));
                                    rtps_stage   <= CHECK;
                                    rtps_cnt     <= 0;
                                end if;
                            when GET_MIN_SN =>
                                AffirmIfEqual(sn_id, to_unsigned(cc_seq_nr(ind)), to_unsigned(rtps.cc.seq_nr));
                            when GET_MAX_SN =>
                                AffirmIfEqual(sn_id, to_unsigned(cc_seq_nr(ind)), to_unsigned(rtps.cc.seq_nr));
                            when others =>
                                null;
                        end case;
                    end if;
                when CHECK =>
                    if (valid_out_rtps(ind) = '1') then
                        AffirmIfEqual(data_id, last_word_out_rtps(ind) & data_out_rtps(ind), rtps.cc.payload.last(rtps_cnt) & rtps.cc.payload.data(rtps_cnt));
                        rtps_cnt <= rtps_cnt + 1;
                        if (rtps_cnt = rtps.cc.payload.length-1) then
                            rtps_stage   <= IDLE;
                        end if;
                    end if;
            end case;
        end if;
        
        -- DEFAULT
        start_rtps      <= (others => '0');
        opcode_rtps     <= (others => NOP);
        seq_nr_rtps     <= (others => SEQUENCENUMBER_UNKNOWN);
        get_data_rtps   <= (others => '0');
        ready_out_rtps  <= (others => '0');
        
        case (rtps_stage) is
            when START =>
                start_rtps(ind)     <= '1';
                opcode_rtps(ind)    <= rtps.opcode;
                seq_nr_rtps(ind)    <= rtps.cc.seq_nr;
            when DONE =>
                if (done_rtps(ind) = '1') then
                    case (rtps.opcode) is
                        when GET_CACHE_CHANGE =>
                            get_data_rtps(ind)  <= '1';
                        when others =>
                            null;
                    end case;
                end if;
            when CHECK =>
                ready_out_rtps(ind) <= '1';
            when others =>
                null;
        end case;
    end process;
    
    watchdog : process
    begin
        wait for 1 ms;
        Alert("Test timeout", FAILURE);
        std.env.stop;
    end process;
    
end architecture;