library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.math_pkg.all; use work.rtps_package.all; entity rtps_builtin_endpoint is port ( clk : in std_logic; -- Input Clock reset : in std_logic; -- Synchronous Reset empty : in std_logic; -- Input FIFO empty flag rd : out std_logic; -- Input FIFO read signal data_in : in std_logic_vector(31 downto 0); -- Input FIFO data signal last_word_in : in std_logic; time : in DOUBLE_WORD_ARRAY; endpoint_output : out USER_ENDPOINT_OUTPUT; endpoint_full : in std_logic_vector(0 to MAX_ENDPOINTS-1); endpoint_wr : out std_logic_vector(0 to MAX_ENDPOINTS-1); rtps_output : out std_logic_vector(31 downto 0); rtps_wr : out std_logic; rtps_full : in std_logic; last_word_out : out std_logic; alive : in std_logic_vector(0 to MAX_ENDPOINTS-1); ); end entity; architecture arch of rtps_builtin_endpoint is --*****COMPONENT DECLARATION****** component single_port_ram is generic ( ADDR_WIDTH : natural := 8; DATA_WIDTH : natural := 12; MEMORY_SIZE : natural := DATA_WIDTH*(2**ADDR_WIDTH) ); port ( clk : in std_logic; addr : in std_logic_vector(ADDR_WIDTH-1 downto 0); wen : in std_logic; ren : in std_logic; wr_data : in std_logic_vector(DATA_WIDTH-1 downto 0); rd_data : out std_logic_vector(DATA_WIDTH-1 downto 0) ); end component; --*****TYPE DECLARATION***** -- FSM states. Explained below in detail type STAGE_TYPE is (TODO); type MEM_STAGE_TYPE is (TODO); type MESSAGE_TYPE_TYPE is (PDP, EDP, MESSAGE, NONE); type MEM_OPCODE_TYPE is (NOP, SEARCH_PARTICIPANT, SEARCH_ENDPOINT); type PARTICIPANT_DATA_TYPE is record meta_addr : std_logic_vector(31 downto 0); def_addr : std_logic_vector(31 downto 0); meta_port : std_logic_vector(15 downto 0); def_port : std_logic_vector(15 downto 0); extra_flags : std_logic_vector(31 downto 0); lease_duration : DOUBLE_WORD_ARRAY; lease_deadline : DOUBLE_WORD_ARRAY; heartbeat_res_time : DOUBLE_WORD_ARRAY; acknack_res_time : DOUBLE_WORD_ARRAY; spdp_seq_nr : DOUBLE_WORD_ARRAY; pub_seq_nr : DOUBLE_WORD_ARRAY; sub_seq_nr : DOUBLE_WORD_ARRAY; mes_seq_nr : DOUBLE_WORD_ARRAY; end record; --*****CONSTANT DECLARATION***** constant BUILTIN_BUFFER_SIZE : natural := MAX_REMOTE_PARTICIPANTS*PARTICIPANT_FRAME_SIZE; constant BUILTIN_BUFFER_ADDR_WIDTH : natural := log2c(BUILTIN_BUFFER_SIZE); constant PARTICIPANT_DATA_FLAG : natural := 0; constant LEASE_DEADLINE_FLAG : natural := 1; constant EXTRA_FLAGS_FLAG : natural := 2; constant ACKNACK_RES_TIME_FLAG : natural := 3; constant HEARTBEAT_RES_TIME_FLAG : natural := 4; constant EDP_SEQ_NR_FLAG : natural := 5; constant PUB_DATA_FLAG : natural := 31; constant SUB_DATA_FLAG : natural := 30; constant MES_DATA_FLAG : natural := 29; constant PUB_SEQUENCE_NR : DOUBLE_WORD_ARRAY := convert_to_double_word(to_unsigned(NUM_WRITERS, 64)); constant SUB_SEQUENCE_NR : DOUBLE_WORD_ARRAY := convert_to_double_word(to_unsigned(NUM_READERS, 64)); constant ZERO_PARTICIPANT_DATA : PARTICIPANT_DATA_TYPE := ( meta_addr => (others => '0'), def_addr => (others => '0'), meta_port => (others => '0'), def_port => (others => '0'), extra_flags => (others => '0'), lease_duration => (others => (others => '0')), lease_deadline => (others => (others => '0')), heartbeat_res_time => (others => (others => '0')), acknack_res_time => (others => (others => '0')), spdp_seq_nr => (others => (others => '0')), pub_seq_nr => (others => (others => '0')), sub_seq_nr => (others => (others => '0')), mes_seq_nr => (others => (others => '0')) ); --*****SIGNAL DECLARATION***** -- FSM state signal stage, stage_next : STAGE_TYPE := SRC_ADDR_HEADER; signal mem_stage, mem_stage_next : MEM_STAGE_TYPE := TODO; -- Intermediate input read signal. (Read from output port not allowed) signal rd_sig : std_logic := '0'; -- Signal used to reset the word counter signal reset_read_cnt : std_logic; -- 32-bit Word counter (Counts words read from input fifo) signal read_cnt : unsigned(13 downto 0) := (others => '0'); -- 32-bit Word aligned total packet length signal packet_length, packet_length_next : unsigned(13 downto 0) := (others => '0'); signal opcode, opcode_next : std_logic_vector(7 downto 0) := (others => '0'); signal flags, flags_next : std_logic_vector(7 downto 0) := (others => '0'); signal src_port, src_port_next : std_logic_vector(15 downto 0) := (others => '0'); signal src_addr, src_addr_next : std_logic_vector(31 downto 0) := (others => '0'); -- TODO: Initialise metatraffic with default locator, since it may not be in the message (Take src Locator?) -- TODO: Default locator should always be in the message signal addr_latch_1, addr_latch_1_next, addr_latch_2, addr_latch_2_next : std_logic_vector(31 downto 0) := (others => '0'); signal port_latch_1, port_latch_1_next, port_latch_2, port_latch_2_next : std_logic_vector(15 downto 0) := (others => '0'); signal src_entityid, src_entityid_next : std_logic_vector(31 downto 0) := (others => '0'); signal dest_entityid, dest_entityid_next : std_logic_vector(31 downto 0) := (others => '0'); signal parameter_end, parameter_end_next : unsigned(13 downto 0) := (others => '0'); signal message_type, message_type_next : MESSAGE_TYPE_TYPE := PDP; signal data_in_swapped : std_logic_vector(31 downto 0) := (others => '0'); signal string_length, string_length_next : unsigned(31 downto 0) := (others => '0'); --TODO: Always reset before COMPARE_STRING stage signal compare_length, compare_length_next : unsigned(31 downto 0) := (others => '0'); signal endpoint_mask, endpoint_mask_next : std_logic_vector(0 to MAX_ENDPOINTS-1) := (others => '0'); signal endpoint_match, endpoint_match_next : std_logic_vector(0 to MAX_ENDPOINTS-1) := (others => '0'); signal endpoint_unmatch, endpoint_unmatch_next : std_logic_vector(0 to MAX_ENDPOINTS-1) := (others => '0'); signal endpoint_mask_array, endpoint_mask_array_next : ENDPOINT_BITMASK_ARRAY_TYPE := (others => (others => '0')); signal participant_match, participant_match_next : std_logic := '0'; signal is_subscriber, is_subscriber_next : std_logic := '0'; signal lease_duration, lease_duration_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal deadline, deadline_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal lifespan_duration, lifespan_duration_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal addr_latch_index, addr_latch_index_next : std_logic := '0'; -- General Purpose counter -- TODO: Constrain range signal cnt, cnt_next : natural := 0; signal expects_inline_qos, expects_inline_qos_next : std_logic := '0'; signal guid, guid_next : GUID_ARRAY_TYPE := (others => (others => '0')); signal start_mem_op, mem_op_busy, mem_op_done : std_logic := '0'; signal mem_opcode : MEM_OPCODE_TYPE := NOP; signal participant_message_best_effort, participant_message_best_effort_next : std_logic := '0'; signal mem_addr, mem_addr_next, mem_addr_base, mem_addr_base_next : unsigned(BUILTIN_BUFFER_ADDR_WIDTH-1 downto 0) := (others => '0'); signal addr_res, addr_res_next : unsigned(BUILTIN_BUFFER_ADDR_WIDTH-1 downto 0) := (others => '0'); signal last_addr, last_addr_next : unsigned(BUILTIN_BUFFER_ADDR_WIDTH-1 downto 0) := (others => '0'); signal max_participant_addr, max_participant_addr_next : unsigned(BUILTIN_BUFFER_ADDR_WIDTH-1 downto 0) := (others => '0'); signal max_endpoint_addr, max_endpoint_addr_next : unsigned(BUILTIN_BUFFER_ADDR_WIDTH-1 downto 0) := (others => '0'); signal mem_read_data, mem_write_data : std_logic_vector(31 downto 0) := (others => '0'); signal mem_rd, mem_wr, mem_busy, mem_done : std_logic := '0'; -- TODO: Re-check range signal mem_cnt, mem_cnt_next : natural range 0 to 3 := 0; -- NOTE: Participant Index limited to 32 bits signal participant_index, participant_index_next : std_logic_vector(31 downto 0) := (others => '0'); signal seq_nr, seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal mem_seq_nr, mem_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal next_seq_nr, next_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal first_seq_nr, first_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal last_seq_nr, last_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal is_orphan_search, is_orphan_search_next : std_logic := '0'; signal orphan_entityid, orphan_entityid_next : std_logic_vector(ENTITYID_WIDTH-1 downto 0) := (others => '0'); signal output_sig : std_logic_vector(31 downto 0) := (others => '0'); signal mem_def_addr, mem_def_addr_next : std_logic_vector(31 downto 0) := (others => '0'); signal mem_def_port, mem_def_port_next : std_logic_vector(15 downto 0) := (others => '0'); signal mem_xflags, mem_xflags_next : std_logic_vector(15 downto 0) := (others => '0'); signal reset_max_pointer, reset_max_pointer_next : std_logic := '0'; signal stale_check, stale_check_next : std_logic := '0'; signal mem_guidprefix, mem_guidprefix_next : GUIDPREFIX_ARRAY_TYPE : (others => (others => '0')); signal last_word_in_latch, last_word_in_latch_next : std_logic := '0'; signal update_participant_flags, update_participant_flags_next : std_logic_vector(5 downto 0) := (others => '0'); signal mem_participant_data, mem_participant_data_next : PARTICIPANT_DATA_TYPE := ZERO_PARTICIPANT_DATA; signal is_heartbeat_res, is_heartbeat_res_next : std_logic := '0'; signal seq_prc_done, seq_prc_done_next : std_logic := '0'; signal count, count_next : unsigned(31 downto 0) := (others => '0'); signal endpoint_alive : std_logic := '0'; signal reset_endpoint_alive : std_logic := '0'; -- TODO: Make sure sequences are initialized to 1 signal auto_live_seq_nr, auto_live_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal man_live_seq_nr, man_live_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal live_gap_start, live_gap_start_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal live_gap_end, live_gap_end_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal extra_flags, extra_flags_next : std_logic_vector(31 downto 0) := (others => '0'); --*****ALIAS DEFINATION***** alias header_opcode : std_logic_vector(7 downto 0) is data_in(31 downto 24); alias header_flags : std_logic_vector(7 downto 0) is data_in(23 downto 16); alias header_udp_port : std_logic_vector(15 downto 0) is data_in(15 downto 0); alias parameter_id : std_logic_vector(15 downto 0) is data_in(31 downto 16); alias parameter_length : std_logic_vector(15 downto 0) is data_in(15 downto 0); alias representation_id : std_logic_vector(15 downto 0) is data_in(31 downto 16); alias representation_options : std_logic_vector(15 downto 0) is data_in(15 downto 0); -- RTPS SUBMESSAGE FLAGS alias endian_flag : std_logic is flags(0); alias endian_flag_next : std_logic is flags_next(0); alias qos_flag : std_logic is flags(1); alias qos_flag_next : std_logic is flags_next(1); alias data_flag : std_logic is flags(2); alias key_flag : std_logic is flags(3); alias final_flag : std_logic is flags(1); alias payload_flag : std_logic is header_flags(4); alias must_undersand : std_logic is parameter_id(14); --*****FUNCTION DECLARATION***** -- Truncates the lower 2 bits of the input, and if they are not equal zero, adds one to the result. -- This is used to round the byte length to 32-bit word length. function normalize_length (len : std_logic_vector(15 downto 0)) return std_logic_vector is variable tmp : std_logic_vector(13 downto 0) := (others => '0'); begin tmp := len(15 downto 2); if(len(1 downto 0) /= "00") then tmp := std_logic_vector(unsigned(tmp) + 1); end if; return tmp; end function; function convert_to_bitmask_array (bitmask : std_logic_vector) return ENDPOINT_BITMASK_ARRAY is variable ret : ENDPOINT_BITMASK_ARRAY := (others => (others => '0')); begin ret := (others => (others => '0')); for i in 0 to ENDPOINT_BITMASK_ARRAY'length-1 loop if (i = ENDPOINT_BITMASK_ARRAY'length-1) then ret(i) := bitmask(i*32 to i*32+31); else ret(i)(0 to (bitmask'length mod 32)-1) := bitmask(i*32 to i*32+(bitmask'length mod 32)-1); end if; end loop; return ret; end function; function convert_from_bitmask_array (bitmask : ENDPOINT_BITMASK_ARRAY, len : natural) return std_logic_vector is variable ret : std_logic_vector(0 to len-1) := (others => '0'); begin ret := (others => '0'); for i in 0 to ENDPOINT_BITMASK_ARRAY'length-1 loop if (i = ENDPOINT_BITMASK_ARRAY'length-1) then ret(i*32 to i*32+31) := bitmask(i); else ret(i*32 to i*32+(len mod 32)-1) := bitmask(i)(0 to (len mod 32)-1); end if; end loop; return ret; end function; begin --*****COMPONENT INSTANTIATION***** ram_inst : single_port_ram is generic map ( ADDR_WIDTH => BUILTIN_BUFFER_ADDR_WIDTH, DATA_WIDTH => 32, MEMORY_SIZE => BUILTIN_BUFFER_SIZE ) port map ( clk => clk, addr => mem_addr, wen => mem_wr, ren => mem_rd, wr_data => mem_write_data, rd_data => mem_read_data ); rd <= rd_sig; endian_swap_prc: process(all) begin data_in_swapped <= data_in; if (endian_flag = '1') then for i in 0 to 3 loop data_in_swapped(i*8+8-1 downto i*8) <= data((3-i)*8+8-1 downto (3-i)*8); end loop; end if; end process; endpoint_alive_prc: process(all) begin if (rising_edge(clk)) then -- Reset Endpoint Alive Signal if (reset = '1' or reset_endpoint_alive = '1') then endpoint_alive <= '0'; -- Set Endpoint Alive Signal, if at least one endpoint asserts liveliness -- XXX: VHDL-2008 unary logic operator elsif (or alive = '1') then endpoint_alive <= '1'; end if; end if; end process; seq_nr_prc: process(all) begin -- DEFAULT seq_prc_done_next <= seq_prc_done; mem_seq_nr_next <= mem_seq_nr; next_seq_nr_next <= next_seq_nr; if (mem_op_done = '1') then seq_prc_done_next <= '1'; case (message_type) is when PDP => mem_seq_nr_next <= mem_participant_data.spdp_seq_nr; next_seq_nr_next <= mem_participant_data.spdp_seq_nr + 1; when EDP => if (is_subscriber = '1') then mem_seq_nr_next <= mem_participant_data.sub_seq_nr; next_seq_nr_next <= mem_participant_data.sub_seq_nr + 1; else mem_seq_nr_next <= mem_participant_data.pub_seq_nr; next_seq_nr_next <= mem_participant_data.pub_seq_nr + 1; end if; when MESSAGE => mem_seq_nr_next <= mem_participant_data.mes_seq_nr; next_seq_nr_next <= mem_participant_data.mes_seq_nr + 1; when others => null; end case; else seq_prc_done_next <= '0'; mem_seq_nr_next <= (others => '0'); next_seq_nr_next <= (others => '0'); end if; end process; -- This process connects the Intermediate Output Signals to the actual output FIFOs output_prc : process(all) begin -- Data Signal for i in 0 to NUM_DOMAIN-1 loop endpoint_output(i) <= output_sig; end loop; --Write Enable Signal endpoint_wr <= (others => '0'); case (stage) is when INFORM_ENDPOINTS_MATCH => if (wr_sig = '1') then endpoint_wr <= endpoint_match; end if; when INFORM_ENDPOINTS_UNMATCH => if (wr_sig = '1') then endpoint_wr <= endpoint_unmatch; end if; when others => null; end case; end process; parse_prc: process(all) variable tmp_endpoint_mask : std_logic_vector(0 to MAX_ENDPOINTS-1) := (others => '0'); begin --DEFAULT stage_next <= stage; packet_length_next <= packet_length; rd_sig <= '0'; reset_read_cnt <= '0'; opcode_next <= opcode; flags_next <= flags; src_port_next <= src_port; src_addr_next <= src_addr; src_entityid_next <= src_entityid; dest_entityid_next <= dest_entityid; parameter_end_next <= parameter_end; message_type_next <= message_type; string_length_next <= string_length; endpoint_mask_next <= endpoint_mask; endpoint_match_next <= endpoint_match; endpoint_unmatch_next <= endpoint_unmatch; participant_match_next <= participant_match_next; is_subscriber_next <= is_subscriber; lease_duration_next <= lease_duration; lifespan_duration_next <= lifespan_duration; addr_latch_1_next <= addr_latch_1; addr_latch_2_next <= addr_latch_2; port_latch_1_next <= port_latch_1; port_latch_2_next <= port_latch_2; addr_latch_index_next <= addr_latch_index; cnt_next <= cnt; expects_inline_qos_next <= expects_inline_qos; mem_opcode <= NOP; start_mem_op <= '0'; is_orphan_search_next <= is_orphan_search; extra_flags_next <= extra_flags; output_sig <= (others => '0'); wr_sig <= '0'; stale_check_next <= stale_check; first_seq_nr_next <= first_seq_nr; last_seq_nr_next <= last_seq_nr; update_participant_flags_next <= update_participant_flags; deadline_next <= deadline; last_word_out <= '0'; reset_endpoint_alive <= '0'; auto_live_seq_nr_next <= auto_live_seq_nr; man_live_seq_nr_next <= man_live_seq_nr; live_gap_start_next <= live_gap_start; live_gap_end_next <= live_gap_end; -- Last Word Latch Setter if (last_word_in = '1') then last_word_in_latch_next <= '1'; -- TODO: Also set in AUTO_PURGE to exit from SKIP_PACKET stage else last_word_in_latch_next <= last_word_in_latch; end if; -- TODO: Reset Latches case(stage) is -- Initial/Idle State when IDLE => -- No Packets to process if (empty = '1') then mem_opcode <= FIND_STALE_PARTICIPANT; start_mem_op <= '1'; stale_check_next <= '1'; stage_next <= STALE_CHECK; elsif (stale_check = '0') then mem_opcode <= FIND_STALE_PARTICIPANT; start_mem_op <= '1'; stale_check_next <= '1'; stage_next <= STALE_CHECK; else -- Process Packet stale_check_next <= '0'; stage_next <= PACKET_HEADER; end if; when PACKET_HEADER => if (empty = '0') then rd_sig <= '1'; -- Latch Opcode opcode_next <= header_opcode; -- Latch Flags flags_next <= header_flags; -- Latch Source UDP Port src_port_next <= header_udp_port; -- Next Stage stage_next <= PACKET_SRC_ADDR; -- SANITY CHECK -- Skip Packet if non-standard Payload if(header_opcode = SID_DATA and payload_flag = '1') then stage_next <= SKIP_PACKET; end if; end if; when PACKET_SRC_ADDR => if (empty = '0') then rd_sig <= '1'; -- Latch Source IP Address src_addr_next <= data_in; -- Next Stage stage_next <= PACKET_SRC_ENTITYID; end if; when PACKET_SRC_ENTITYID => if (empty = '0') then rd_sig <= '1'; -- Latch Source Entity ID src_entityid_next <= data_in; -- Reset Counter cnt_next <= 1; -- Next Stage stage_next <= PACKET_SRC_GUIDPREFIX; end if; when PACKET_SRC_GUIDPREFIX => if (empty = '0' and mem_op_done = '1') then rd_sig <= '1'; -- Latch Src GUIDPrefix case (cnt) is when 1 => guid_next(0) <= data_in; when 2 => guid_next(1) <= data_in; when 3 => guid_next(2) <= data_in; guid_next(3) <= (others => '0'); -- Start Participant Search mem_opcode <= SEARCH_PARTICIPANT; start_mem_op <= '1'; -- Next Stage stage_next <= PACKET_DEST_ENTITYID; when others => null; end case; end if; when PACKET_DEST_ENTITYID => if (empty = '0') then rd_sig <= '1'; -- DEFAULT STAGE stage_next <= SKIP_PACKET; -- *Check Dest Entity ID* case (data_in) is when ENTITYID_UNKNOWN => -- Wildcard Destination -- Content has to be determined through src stage_next <= CHECK_SRC_ENTITYID; when ENTITYID_SPDP_BUILTIN_PARTICIPANT_DETECTOR => -- Only DATA relevant if (opcode = SID_DATA) then -- Packet Contains Participant Data message_type_next <= PDP; stage_next <= LATCH_SEQ_NR; -- Initialise counter cnt_next <= 1; -- Reset Participant Match participant_match_next <= '1'; end if; when ENTITYID_SEDP_BUILTIN_PUBLICATIONS_ANNOUNCER => -- SANITY CHECK: Ignore if no Writers -- Only ACKNACKs are relevant if (NUM_WRITERS /= 0 and opcode = SID_ACKNACK) then message_type_next <= EDP; stage_next <= PROCESS_ACKNACK; --ACKNACK Processing -- Initialise Counter cnt_next <= 1; end if; when ENTITYID_SEDP_BUILTIN_PUBLICATIONS_DETECTOR => -- SANITY CHECK: Ignore if no Readers if (NUM_READERS /= 0) then -- Only HEARTBEATs and DATA are relevant (GAPs irrelevant, since depth is 1) case (opcode) is when SID_HEARTBEAT => message_type_next <= EDP; stage_next <= PROCESS_HEARTBEAT; -- HEARTBEAT Processing -- Initialise counter cnt_next <= 1; when SID_DATA => message_type_next <= EDP; stage_next <= LATCH_SEQ_NR; -- DATA Processing -- Initialise counter cnt_next <= 1; -- QoS is publisher-offered is_subscriber_next <= '0'; -- Reset Endpoint Mask (ALL READERS) endpoint_mask_next <= (others => '0'); endpoint_mask_next(MAX_ENDPOINTS-1 downto MAX_ENDPOINTS-NUM_READERS) <= (others => '1'); end case; end if; when ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_ANNOUNCER => -- SANITY CHECK: Ignore if no Readers -- Only ACKNACKs are relevant if (NUM_READERS /= 0 and opcode = SID_ACKNACK) then message_type_next <= EDP; stage_next <= PROCESS_ACKNACK; --ACKNACK Processing -- Initialise counter cnt_next <= 1; end if; when ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_DETECTOR => -- SANITY CHECK: Ignore if no Writers if (NUM_WRITERS /= 0) then -- Only HEARTBEATs and DATA are relevant (GAPs irrelevant, since depth is 1) case (opcode) is when SID_HEARTBEAT => message_type_next <= EDP; stage_next <= PROCESS_HEARTBEAT; -- HEARTBEAT Processing -- Initialise counter cnt_next <= 1; when SID_DATA => message_type_next <= EDP; stage_next <= LATCH_SEQ_NR; -- DATA Processing -- Initialise counter cnt_next <= 1; -- QoS is subscriber-requested is_subscriber_next <= '1'; -- Reset Endpoint Mask (ALL WRITERS) endpoint_mask_next <= (others => '0'); endpoint_mask_next(NUM_WRITERS-1 downto 0) <= (others => '1'); end case; end if; when ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER => -- Only ACKNACKs are relevant if (opcode = SID_ACKNACK) then message_type_next <= MESSAGE; stage_next <= PROCESS_ACKNACK; --ACKNACK Processing -- Initialise counter cnt_next <= 1; end if; when ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER => -- Only HEARTBEATs and DATA are relevant (GAPs irrelevant, since depth is 1) case (opcode) is when SID_HEARTBEAT => message_type_next <= MESSAGE; stage_next <= PROCESS_HEARTBEAT; -- HEARTBEAT Processing -- Initialise counter cnt_next <= 1; when SID_DATA => message_type_next <= MESSAGE; stage_next <= LATCH_SEQ_NR; -- DATA Processing -- Initialise counter cnt_next <= 1; end case; when others => null; end case; end if; when CHECK_SRC_ENTITYID => -- DEFAULT STAGE stage_next <= SKIP_PACKET; -- *Check Dest Entity ID* case (src_entityid) is when ENTITYID_SPDP_BUILTIN_PARTICIPANT_ANNOUNCER => -- Only DATA relevant if (opcode = SID_DATA) then -- Packet Contains Participant Data message_type_next <= PDP; stage_next <= LATCH_SEQ_NR; -- Initialise counter cnt_next <= 1; -- Reset Participant Match participant_match_next <= '1'; end if; when ENTITYID_SEDP_BUILTIN_PUBLICATIONS_DETECTOR => -- SANITY CHECK: Ignore if no Writers -- Only ACKNACKs are relevant if (NUM_WRITERS /= 0 and opcode = SID_ACKNACK) then message_type_next <= EDP; stage_next <= PROCESS_ACKNACK; --ACKNACK Processing -- Initialise counter cnt_next <= 1; end if; when ENTITYID_SEDP_BUILTIN_PUBLICATIONS_ANNOUNCER => -- SANITY CHECK: Ignore if no Readers if (NUM_READERS /= 0) then -- Only HEARTBEATs and DATA are relevant (GAPs irrelevant, since depth is 1) case (opcode) is when SID_HEARTBEAT => message_type_next <= EDP; stage_next <= PROCESS_HEARTBEAT; -- HEARTBEAT Processing -- Initialise counter cnt_next <= 1; when SID_DATA => message_type_next <= EDP; stage_next <= LATCH_SEQ_NR; -- DATA Processing -- Initialise counter cnt_next <= 1; -- QoS is publisher-offered is_subscriber_next <= '0'; -- Reset Endpoint Mask (ALL READERS) endpoint_mask_next <= (others => '0'); endpoint_mask_next(MAX_ENDPOINTS-1 downto MAX_ENDPOINTS-NUM_READERS) <= (others => '1'); end case; end if; when ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_DETECTOR => -- SANITY CHECK: Ignore if no Readers -- Only ACKNACKs are relevant if (NUM_READERS /= 0 and opcode = SID_ACKNACK) then message_type_next <= EDP; stage_next <= PROCESS_ACKNACK; --ACKNACK Processing -- Initialise counter cnt_next <= 1; end if; when ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_ANNOUNCER => -- SANITY CHECK: Ignore if no Writers if (NUM_WRITERS /= 0) then -- Only HEARTBEATs and DATA are relevant (GAPs irrelevant, since depth is 1) case (opcode) is when SID_HEARTBEAT => message_type_next <= EDP; stage_next <= PROCESS_HEARTBEAT; -- HEARTBEAT Processing -- Initialise counter cnt_next <= 1; when SID_DATA => message_type_next <= EDP; stage_next <= LATCH_SEQ_NR; -- DATA Processing -- Initialise counter cnt_next <= 1; -- QoS is subscriber-requested is_subscriber_next <= '1'; -- Reset Endpoint Mask (ALL WRITERS) endpoint_mask_next <= (others => '0'); endpoint_mask_next(NUM_WRITERS-1 downto 0) <= (others => '1'); end case; end if; when ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER => -- Only ACKNACKs are relevant if (opcode = SID_ACKNACK) then message_type_next <= MESSAGE; stage_next <= PROCESS_ACKNACK; --ACKNACK Processing -- Initialise counter cnt_next <= 1; end if; when ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER => -- Only HEARTBEATs and DATA are relevant (GAPs irrelevant, since depth is 1) case (opcode) is when SID_HEARTBEAT => message_type_next <= MESSAGE; stage_next <= PROCESS_HEARTBEAT; -- HEARTBEAT Processing -- Initialise counter cnt_next <= 1; when SID_DATA => message_type_next <= MESSAGE; stage_next <= LATCH_SEQ_NR; -- DATA Processing -- Initialise counter cnt_next <= 1; end case; when others => null; end case; when LATCH_SEQ_NR => if (empty = '0') then rd_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; -- Latch Sequence Number case (cnt) is when 1 => seq_nr_next(0) <= unsigned(data_in); when 2 => seq_nr_next(1) <= unsigned(data_in); stage_next <= PROCESS_DATA; when others => null; end case; end if; when PROCESS_DATA => -- Data Message contains inline QoS if (qos_flag = '1') then stage_next <= PROCESS_PL; -- Data Message contains Payload elsif (data_flag = '1') then -- Process Payload Header if (empty = '0') then rd_sig <= '1'; -- DEFAULT Stage Next stage_next <= SKIP_PACKET; -- Process Payload Header case(representation_id) is when PL_CDR_BE => endian_flag_next <= '0'; stage_next <= PROCESS_PL; when PL_CDR_LE => endian_flag_next <= '1'; stage_next <= PROCESS_PL; when CDR_BE => if (message_type = MESSAGE) then endian_flag_next <= '0'; stage_next <= PROCESS_MESSAGE; end if; when CDR_LE => if (message_type = MESSAGE) then endian_flag_next <= '1'; stage_next <= PROCESS_MESSAGE; end if; when others => null; end case; end if; end if; when PROCESS_PL => if (empty = '0') then rd_sig <= '1'; -- Reset Word Counter reset_read_cnt <= '1'; -- Latch Parameter End parameter_end_next <= unsigned(normalize_length(endian_swap(endian_flag,parameter_length))); -- DEFAULT stage_next <= SKIP_PARAMETER; -- TODO: Use inline QoS? -- TODO: DDS-XTYPES: When reading data, implementations of this specification shall be robust to any setting of the FLAG_MUST_UNDERSTAND bit and accept the parameter nevertheless. case (parameter_id) is when PID_TOPIC_NAME => -- Topic Name only relevant for EDP if(qos_flag = '0' and message_type = EDP) then stage_next <= LATCH_STRING_LENGTH; end if; when PID_TYPE_NAME => -- Type Name only relevant for EDP if(qos_flag = '0' and message_type = EDP) then stage_next <= LATCH_STRING_LENGTH; end if; when PID_USER_DATA => -- Ignore null; when PID_GROUP_DATA => -- Ignore null; when PID_TOPIC_DATA => -- Ignore null; when PID_DURABILITY => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_DURABILITY; end if; when PID_DURABILITY_SERVICE => -- Ignore when PID_DEADLINE => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_DEADLINE_1; end if; when PID_LATENCY_BUDGET => -- Ignore when PID_LIVELINESS => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_LIVELINESS; end if; when PID_RELIABILITY => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_RELIABILITY; end if; when PID_LIFESPAN => if(qos_flag = '0' and message_type = EDP) then -- TODO: Do not latch lifespan. Request it via inline QoS stage_next <= LATCH_LIFESPAN_1; end if; when PID_DESTINATION_ORDER => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_DESTINATION_ORDER; end if; when PID_HISTORY => -- Ignore when PID_RESOURCE_LIMITS => -- Ignore when PID_OWNERSHIP => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_OWNERSHIP; end if; when PID_OWNERSHIP_STRENGTH => -- Ignore when PID_PRESENTATION => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_PRESENTATION; end if; when PID_PARTITION => if(qos_flag = '0' and message_type = EDP) then stage_next <= RXO_PARTITION; end if; when PID_TIME_BASED_FILTER => -- Ignore when PID_TRANSPORT_PRIORITY => -- Ignore when PID_DOMAIN_ID => if(qos_flag = '0' and message_type = PDP) then stage_next <= MATCH_DOMAIN_ID; end if; when PID_DOMAIN_TAG => if(qos_flag = '0' and message_type = PDP) then stage_next <= LATCH_STRING_LENGTH; end if; when PID_PROTOCOL_VERSION => if(qos_flag = '0' and message_type = PDP) then stage_next <= MATCH_PROTOCOL_VERSION; end if; when PID_VENDORID => -- Ignore when PID_UNICAST_LOCATOR => if(qos_flag = '0' and message_type = EDP) then stage_next <= LATCH_LOCATOR; addr_latch_index_next <= '0'; -- Initialise counter cnt_next <= 1; end if; when PID_MULTICAST_LOCATOR => if(qos_flag = '0' and message_type = EDP) then stage_next <= LATCH_LOCATOR; addr_latch_index_next <= '0'; -- Initialise counter cnt_next <= 1; end if; when PID_DEFAULT_UNICAST_LOCATOR => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_LOCATOR; addr_latch_index_next <= '0'; -- Initialise counter cnt_next <= 1; end if; when PID_DEFAULT_MULTICAST_LOCATOR => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_LOCATOR; addr_latch_index_next <= '0'; -- Initialise counter cnt_next <= 1; end if; when PID_METATRAFFIC_UNICAST_LOCATOR => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_LOCATOR; addr_latch_index_next <= '1'; -- Initialise counter cnt_next <= 1; end if; when PID_METATRAFFIC_MULTICAST_LOCATOR => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_LOCATOR; addr_latch_index_next <= '1'; -- Initialise counter cnt_next <= 1; end if; when PID_EXPECTS_INLINE_QOS => stage_next <= LATCH_EXPECTS_INLINE_QOS; when PID_PARTICIPANT_MANUAL_LIVELINESS_COUNT => -- Ignore when PID_PARTICIPANT_LEASE_DURATION => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_LEASE_DURATION_1; end if; when PID_CONTENT_FILTER_PROPERTY => -- Ignore when PID_PARTICIPANT_GUID => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_GUID; -- Initialise counter cnt_next <= 1; end if; when PID_GROUP_GUID => -- Ignore when PID_BUILTIN_ENDPOINT_SET => if(qos_flag = '0' and message_type = DPD) then stage_next <= CHECK_REMOTE_BUILTIN_ENDPOINTS; end if; when PID_BUILTIN_ENDPOINT_QOS => if(qos_flag = '0' and message_type = DPD) then stage_next <= LATCH_BUILTIN_ENDPOINT_QOS; end if; when PID_PROPERTY_LIST => -- Ignore when PID_TYPE_MAX_SIZE_SERIALIZED => -- Ignore when PID_ENTITY_NAME => -- Ignore when PID_ENDPOINT_GUID => if(qos_flag = '0' and message_type = EPD) then stage_next <= LATCH_GUID; -- Initialise counter cnt_next <= 1; end if; when PID_CONTENT_FILTER_INFO => -- Ignore when PID_COHERENT_SET => -- Ignore when PID_DIRECTED_WRITE => -- Ignore when PID_ORIGINAL_WRITER_INFO => -- Ignore when PID_GROUP_COHERENT_SET => -- Ignore when PID_GROUP_SEQ_NUM => -- Ignore when PID_WRITER_GROUP_INFO => -- Ignore when PID_SECURE_WRITER_GROUP_INFO => -- Ignore when PID_KEY_HASH => -- Ignore when PID_STATUS_INFO => -- Ignore when PID_EXTENDED => -- TODO when PID_PAD => -- Ignore when PID_IGNORE => -- Ignore when PID_SENTINEL => -- If Processing in-line QoS until now if(qos_flag = '1') then qos_flag_next <= '0'; stage_next <= PROCESS_DATA; else stage_next <= PARTICIPANT_MATCH_STAGE; end if; when PID_LIST_END => -- TODO when others => -- If MUST_UNDERSTAND Flag is set, we have incompatible communication. Drop Packet if (must_undersand = '1') then stage_next <= SKIP_PACKET; -- Else skip Uknown Parameter else stage_next <= SKIP_PARAMETER; end if; end case; end if; when LATCH_STRING_LENGTH => if (empty = '0') then rd_sig <= '1'; string_length_next <= unsigned(data_in_swapped); stage_next <= COMPARE_STRING; compare_length_next <= (others => '0'); end if; when COMPARE_STRING => -- TODO: FIX (Type Name not handled) if (empty = '0') then rd_sig <= '1'; -- Increment compare position counter compare_length_next <= compare_length + 32; -- Compare Strings if (message_type = EDP) then for i in 0 to ENDPOINT_TOPIC'length-1 loop if (data_in /= ENDPOINT_TOPIC(i)(to_integer(compare_length) to to_integer(compare_length)+7)) then endpoint_mask_next(i) <= '0'; end if; end loop; elsif (message_type = DPD) then if (data_in /= DOMAIN_TAG(to_integer(compare_length) to to_integer(compare_length)+7)) then participant_match_next <= '0'; end if; end if; -- End of String (Exit Condition) -- NOTE: This assumes that the Parameter is padded with zeroes -- TODO: XTypes 7.4.1.1 requires padding bytes for "PLAIN_CDR" to be zero, but does not specify the value for "PL_CDR" if (compare_length >= string_length) then stage_next <= SKIP_PARAMETER; end if; end if; when RXO_DURABILITY => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped > ENDPOINT_DURABILITY(i)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped < ENDPOINT_DURABILITY(i)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_DEADLINE_1 => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped < ENDPOINT_DEADLINE(i)(1)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped > ENDPOINT_DEADLINE(i)(1)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= RXO_DEADLINE_2; end if; when RXO_DEADLINE_2 => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped < ENDPOINT_DEADLINE(i)(0)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped > ENDPOINT_DEADLINE(i)(0)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_LIVELINESS => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped > ENDPOINT_LIVELINESS(i)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped < ENDPOINT_LIVELINESS(i)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= RXO_LEASE_DURATION; cnt_next <= 0; end if; when RXO_LEASE_DURATION => -- TODO: Convert to two stages to avoid counter complexity? if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped > ENDPOINT_LEASE_DURATION(i)(cnt)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped < ENDPOINT_LIVELINESS(i)(cnt)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; --Increment Counter cnt_next <= cnt + 1; -- Exit Condition if (cnt = 1) then -- Next Stage stage_next <= SKIP_PARAMETER; end if; end if; when LATCH_LEASE_DURATION_1 => if (empty = '0') then rd_sig <= '1'; -- Latch Lease Duration lease_duration_next(0) <= data_in_swapped; -- Next Stage stage_next <= LATCH_LEASE_DURATION_2; end if; when LATCH_LEASE_DURATION_2 => if (empty = '0') then rd_sig <= '1'; -- Latch Lease Duration lease_duration_next(1) <= data_in_swapped; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_RELIABILITY => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped > ENDPOINT_RELIABILITY(i)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped < ENDPOINT_RELIABILITY(i)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage -- NOTE: The max_blocking_time value is ignored stage_next <= SKIP_PARAMETER; end if; when LATCH_LIFESPAN_1 => if (empty = '0') then rd_sig <= '1'; -- Latch Lease Duration lifespan_duration_next(1) <= data_in_swapped; -- Next Stage stage_next <= LATCH_LIFESPAN_2; end if; when LATCH_LIFESPAN_2 => if (empty = '0') then rd_sig <= '1'; -- Latch Lease Duration lifespan_duration_next(0) <= data_in_swapped; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_DESTINATION_ORDER => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped > ENDPOINT_DESTINATION_ORDER(i)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped < ENDPOINT_DESTINATION_ORDER(i)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_OWNERSHIP => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility if (data_in_swapped /= (data_in_swapped'reverse_range => '0')) then endpoint_mask_next <= (others => '0'); end if; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_PRESENTATION_1 => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in_swapped > ENDPOINT_PRESENTATION(i)) then endpoint_mask_next(i) <= '0'; end if; -- data-in is Publisher-Offered else if (data_in_swapped < ENDPOINT_PRESENTATION(i)) then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= RXO_PRESENTATION_2; end if; when RXO_PRESENTATION_2 => if (empty = '0') then rd_sig <= '1'; -- Check QoS Compatibility for i in 0 to ENDPOINT_TOPIC'length-1 loop -- data-in is Subscriber-Requested if (is_subscriber = '1') then if (data_in(23) = '1' and ENDPOINT_COHERENT_ACCESS(i) = '0') then endpoint_mask_next(i) <= '0'; end if; if (data_in(15) = '1' and ENDPOINT_ORDERED_ACCESS(i) = '0') then endpoint_mask_next(i) <= '0'; end if; end if; end loop; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when RXO_PARTITION => if (empty = '0') then rd_sig <= '1'; --TODO: Implement full partition Name support? -- NOTE: Endpoints are only matched against the default empty partition. -- Check QoS Compatibility if (data_in_swapped /= (data_in_swapped'reverse_range => '0')) then endpoint_mask_next <= (others => '0'); end if; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when MATCH_DOMAIN_ID => if (empty = '0') then rd_sig <= '1'; -- DEAFULT Stage stage_next <= SKIP_PARAMETER; -- MATCH DOMAIN ID if (data_in_swapped /= DOMAIN_ID) then -- No Match participant_match_next <= '0'; end if; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when MATCH_PROTOCOL_VERSION => if (empty = '0') then rd_sig <= '1'; -- DEAFULT Stage stage_next <= SKIP_PARAMETER; -- If RTPS Protocol Major Version is not 2, skip packet if(data_in(31 downto 24) /= PROTOCOLVERSION_2_4(15 downto 8)) then -- No Match participant_match_next <= '0'; end if; -- Next Stage stage_next <= SKIP_PARAMETER; end if; when LATCH_LOCATOR => if (empty = '0') then rd_sig <= '1'; -- Increment Counter (Default) cnt_next <= cnt + 1; -- Locator Kind if (cnt = 1) then -- Check if UDPv4 Locator if (data_in_swapped /= LOCATOR_KIND_UDPv4) then stage_next <= SKIP_PARAMETER; end if; -- Locator Port elsif (cnt = 2) then -- Latch Source Port if (addr_latch_index = '0') then port_latch_1 <= data_in_swapped(port_latch_1'length-1 downto 0); else port_latch_2 <= data_in_swapped(port_latch_2'length-1 downto 0); end if; -- Locator Addr (IPv4) elsif (cnt = 6) then -- Latch Src Addr if (addr_latch_index = '0') then addr_latch_1 <= data_in_swapped; else addr_latch_2 <= data_in_swapped; end if; -- Exit Stage stage_next <= SKIP_PARAMETER; end if; end if; when LATCH_EXPECTS_INLINE_QOS => if (empty = '0') then rd_sig <= '1'; -- Latch 'expectsInlineQoS' expects_inline_qos_next <= data_in(24); stage_next <= SKIP_PARAMETER; end if; when LATCH_GUID => if (empty = '0') then rd_sig <= '1'; -- Increment Counter (Default) cnt_next <= cnt + 1; -- Check if GUID Prefix valid (Should be the same as the GUID Prefix of the Packet) -- and update the Entity ID case (cnt) is when 1 => if (data_in /= guid(0)) then stage_next <= SKIP_PACKET; end if; when 2 => if (data_in /= guid(1)) then stage_next <= SKIP_PACKET; end if; when 3 => if (data_in /= guid(2)) then stage_next <= SKIP_PACKET; end if; when 4 => -- NOTE: Even though the mem_ctrl_prc is currently using the guid signal, it only uses the first -- 3 bytes (GUIDPrefix) for the SEARCH_PARTICIPANT guid_next(3) <= data_in; stage_next <= SKIP_PARAMETER; end case; end if; when CHECK_REMOTE_BUILTIN_ENDPOINTS => if (empty = '0') then rd_sig <= '1'; -- Next Stage stage_next <= SKIP_PARAMETER; -- SANITY CHECK (Builtin Participant and Participant Message Endpoints have to be always available) if ( data_in_swapped(DISC_BUILTIN_ENDPOINT_PARTICIPANT_ANNOUNCER) = '0' or data_in_swapped(DISC_BUILTIN_ENDPOINT_PARTICIPANT_DETECTOR) = '0' or data_in_swapped(BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER) = '0' or data_in_swapped(BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER) = '0') then -- No Match participant_match_next <= '0'; end if; -- Check for necessary Builtin Endpoints for Readers if (NUM_READERS > 0) then if ( data_in_swapped(DISC_BUILTIN_ENDPOINT_PUBLICATIONS_ANNOUNCER) = '0' or data_in_swapped(DISC_BUILTIN_ENDPOINT_SUBSCRIPTIONS_DETECTOR) = '0') then -- No Match participant_match_next <= '0'; end if; end if; -- Check for necessary Builtin Endpoints for Writers if (NUM_WRITERS > 0) then if ( data_in_swapped(DISC_BUILTIN_ENDPOINT_SUBSCRIPTIONS_ANNOUNCER) = '0' or data_in_swapped(DISC_BUILTIN_ENDPOINT_PUBLICATIONS_DETECTOR) = '0') then -- No Match participant_match_next <= '0'; end if; end if; end if; when LATCH_BUILTIN_ENDPOINT_QOS => if (empty = '0') then rd_sig <= '1'; -- Latch QoS extra_flags_next <= data_in_swapped; -- DEFAULT Next Stage stage_next <= SKIP_PARAMETER; end if; when PARTICIPANT_MATCH_STAGE_1 => -- Wait for Participant Search and Sequence Number Process if (mem_op_done = '1' and seq_prc_done = '1') then -- No Match in Buffer if (addr_res = BUILTIN_BUFFER_SIZE) then -- Participant Match if (message_type = PDP and participant_match = '1') then -- Add participant in buffer deadline_next <= time + lease_duration; mem_opcode <= INSERT_PARTICIPANT; start_mem_op <= '1'; -- DONE stage_next <= SKIP_PACKET; else -- Ignore all other messages, since there is no corresponding participant in the buffer. stage_next <= SKIP_PACKET; end if; -- Match in Buffer, Next Sequence Number -- NOTE: We only process the Sequences in order. This may lead to more traffic being exchanged -- (since all Data messages with a higher sequence number than the next expected will be ignored), -- but keeps the logic simple. elsif (next_seq_nr = seq_nr) -- Participant if (message_type = PDP) then -- Participant Unmatch if (participant_match = '0') then -- Remove participant from buffer mem_opcode <= REMOVE_PARTICIPANT; start_mem_op <= '1'; -- Find and delete all orphaned endpoints in Buffer stage_next <= FIND_ORPHAN_ENDPOINT; else -- Update Participant Data mem_opcode <= UPDATE_PARTICIPANT; -- Update Lease deadline_next <= time + lease_duration; update_participant_flags_next <= (PARTICIPANT_DATA_FLAG => '1', LEASE_DEADLINE_FLAG => '1', EXTRA_FLAGS_FLAG => '1', others => '0'); start_mem_op <= '1'; -- DONE stage_next <= SKIP_PACKET; end if; -- Endpoint elsif (message_type = EDP) then -- TODO: Should we renew the Participant Lease on Endpoint Activity? - No, it is clearly stated that a participant anouncement has to be received during the lease duration -- Store new Sequence Number mem_opcode <= UPDATE_PARTICIPANT; update_participant_flags_next <= (EDP_SEQ_NR_FLAG => '1', others => '0'); start_mem_op <= '1'; stage_next <= INITIATE_ENDPOINT_SEARCH; else -- Ignore stage_next <= SKIP_PACKET; end if; -- Old Sequence Number else -- If Participant Anouncement, renew lease deadline if (message_type = PDP) then -- Update Lease mem_opcode <= UPDATE_PARTICIPANT; deadline_next <= time + lease_duration; update_participant_flags_next <= (LEASE_DEADLINE_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; -- Done stage_next <= SKIP_PACKET; end if; end if; -- Help stage, because we need to do two consecutive memory operations when INITIATE_ENDPOINT_SEARCH => if (mem_op_done = '1') then -- Search Endpoint in Buffer mem_opcode <= SEARCH_ENDPOINT; start_mem_op <= '1'; -- DONE stage_next <= ENDPOINT_MATCH_STAGE; end if; when ENDPOINT_MATCH_STAGE => -- Wait for Endpoint Search if (mem_op_done = '1') then -- No Match in Buffer (New remote Endpoint) if (addr_res = BUILTIN_BUFFER_SIZE) then -- At least one local endpoint match if (endpoint_mask /= (endpoint_mask'range => '0')) then -- Add endpoint in buffer mem_opcode <= INSERT_ENDPOINT; start_mem_op <= '1'; -- Mark UNMATCHES endpoint_match_next <= endpoint_mask; endpoint_unmatch_next <= (others => '0'); -- Propagate Match Changes to local Endpoints stage_next <= INFORM_ENDPOINTS_MATCH; cnt_next <= 1; end if; -- Match in buffer (Existing Endpoint) else -- At least one local endpoint match if (endpoint_mask /= (endpoint_mask'range => '0')) then tmp_endpoint_mask := convert_from_bitmask_array(endpoint_mask_array, MAX_ENDPOINTS); -- Mark Endpoint match changes tmp_endpoint_mask := tmp_endpoint_mask xor endpoint_mask; -- Mark UNMATCHES endpoint_unmatch_next <= tmp_endpoint_mask xor endpoint_mask; -- Mark NEW MATCHES endpoint_match_next <= tmp_endpoint_mask and endpoint_mask; -- Update endpoint in buffer mem_opcode <= UPDATE_ENDPOINT; start_mem_op <= '1'; -- Propagate Match Changes to local Endpoints stage_next <= INFORM_ENDPOINTS_MATCH; cnt_next <= 1; else -- Remove endpoint from buffer mem_opcode <= REMOVE_ENDPOINT; start_mem_op <= '1'; -- Mark UNMATCHES endpoint_match_next <= (others => '0'); endpoint_unmatch_next <= convert_from_bitmask_array(endpoint_mask_array, MAX_ENDPOINTS); -- Propagate Match Changes to local Endpoints stage_next <= INFORM_ENDPOINTS_UNMATCH; cnt_next <= 1; end if; end if; end if; when FIND_ORPHAN_ENDPOINT => if (mem_op_done = '1') then is_orphan_search_next <= '1'; mem_opcode <= SEARCH_ENDPOINT; start_mem_op <= '1'; stage_next <= PURGE_ORPHAN_ENDPOINT; end if; when PURGE_ORPHAN_ENDPOINT => if (mem_op_done = '1') then -- Orphan Match if (addr_res /= BUILTIN_BUFFER_SIZE) then -- Remove orphaned endpoint from buffer mem_opcode <= REMOVE_ENDPOINT; start_mem_op <= '1'; -- Mark UNMATCHES endpoint_match_next <= (others => '0'); endpoint_unmatch_next <= convert_from_bitmask_array(endpoint_mask_array, MAX_ENDPOINTS); -- Propagate Match Changes to local Endpoints stage_next <= INFORM_ENDPOINTS_UNMATCH; cnt_next <= 1; -- Buffer has no more orphans else -- DONE stage_next <= SKIP_PACKET; end if; end if; when INFORM_ENDPOINTS_MATCH => if ((endpoint_match and endpoint_full) = (endpoint_full'range => '0')) then -- Increment Counter cnt_next <= cnt + 1; -- Enable Write wr_sig <= '1'; case (cnt) is when 1 => output_sig <= OPCODE_MATCH; when 2 => output_sig <= guid(0); when 3 => output_sig <= guid(1); when 4 => output_sig <= guid(2); when 5 => output_sig <= guid(3); when 6 => -- Use Address set by PID if (addr_latch_1 /= (addr_latch_1'reverse_range => '0')) then output_sig <= addr_latch_1; -- Else use the Default Address from Participant else output_sig <= mem_def_addr; end if; when 7 => -- Use Port set by PID if (port_latch_1 /= (port_latch_1'reverse_range => '0')) then output_sig <= port_latch_1 & mem_xflags; -- Else use the Default Port from Participant else output_sig <= mem_def_port & mem_xflags; end if; -- If there are endpoints to unmatch, inform them if (endpoint_unmatch /= (endpoint_unmatch'range => '0')) then stage_next <= INFORM_ENDPOINTS_UNMATCH; else -- DONE stage_next <= SKIP_PACKET; end if; end case; end if; when INFORM_ENDPOINTS_UNMATCH => if ((endpoint_unmatch and endpoint_full) = (endpoint_full'range => '0')) then -- Increment Counter cnt_next <= cnt + 1; -- Enable Write wr_sig <= '1'; case (cnt) is when 1 => output_sig <= OPCODE_UNMATCH; when 2 => output_sig <= guid(0); when 3 => output_sig <= guid(1); when 4 => output_sig <= guid(2); when 5 => output_sig <= guid(3); -- If we are in the middle of an orphan purge process, return to the search stage if (is_orphan_search = '1') then stage_next <= FIND_ORPHAN_ENDPOINT; else -- DONE stage_next <= SKIP_PACKET; end if; end case; end if; when STALE_CHECK => -- Wait for memory OP if (mem_op_done = '1') then -- Found Stale Entry if (addr_res /= BUILTIN_BUFFER_SIZE) then -- Participant Lease Expired -- NOTE: The mem_participant_data is zero initialized on lease expiration, so we check the default address for zero -- (since the default address should always be set) if (mem_participant_data.def_addr /= (mem_participant_data.def_addr'reverse_range => '0')) then -- Remove Participant mem_opcode <= REMOVE_PARTICIPANT; start_mem_op <= '1'; stage_next <= AUTO_PURGE; -- Participant Response Time Reached else -- Heartbeat Response if (is_heartbeat_res = '1') then -- If Suppression Delay passed, zero the time if(mem_participant_data.heartbeat_res_time(0) = '1') then mem_opcode <= UPDATE_PARTICIPANT; -- Zero Heartbeat Response Time deadline_next <= (others => '0'); update_participant_flags_next <= (HEARTBEAT_RES_TIME_FLAG_FLAG => '1', others => '0'); start_mem_op <= '1'; -- DONE stage_next <= IDLE; -- If Response Delay Passed else -- Set Heartbeat Suppression Time mem_opcode <= UPDATE_PARTICIPANT; if (TODO /= 0) then -- Set Heartbeat Suppression Time deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '1'; else -- Zero Heartbeat Response Time deadline_next <= (others => '0'); end if; update_participant_flags_next <= (HEARTBEAT_RES_TIME_FLAG_FLAG => '1', others => '0'); start_mem_op <= '1'; -- Send Acknack stage_next <= SEND_ACKNACK; cnt_next <= 1; end if; -- Acknack Response Time else -- If Suppression Delay passed, zero the time if(mem_participant_data.acknack_res_time(0) = '1') then mem_opcode <= UPDATE_PARTICIPANT; -- Zero Acknack Response Time deadline_next <= (others => '0'); update_participant_flags_next <= (ACKNACK_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; -- DONE stage_next <= IDLE; -- If Response Delay Passed else mem_opcode <= UPDATE_PARTICIPANT; if (TODO /= 0) then -- Set Acknack Suppression Time deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '1'; else -- Zero Acknack Response Time deadline_next <= (others => '0'); end if; -- Zero Data Response Flags extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(PUB_DATA_FLAG) <= '0'; extra_flags_next(SUB_DATA_FLAG) <= '0'; extra_flags_next(MES_DATA_FLAG) <= '0'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', ACKNACK_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; -- Send Requested Data stage_next <= SEND_PUB_DATA; cnt_next <= 0; end if; end if; end if; else -- No Stale Entry Found, DONE stage_next <= IDLE; end if; end if; when AUTO_PURGE => if (mem_op_done = '1') then -- Help Stage needed to latch the GUID Prefix of the removed staled participant (Needed for the orphan search) guid_next(0) <= mem_guidprefix(0); guid_next(1) <= mem_guidprefix(1); guid_next(2) <= mem_guidprefix(2); stage_next <= FIND_ORPHAN_ENDPOINT; end if; when PROCESS_HEARTBEAT => if (empty = '0') then rd_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; -- Latch Sequence Number case (cnt) is when 1 => first_seq_nr_next(0) <= data_in; when 2 => first_seq_nr_next(1) <= data_in; when 3 => last_seq_nr_next(0) <= data_in; when 4 => last_seq_nr_next(1) <= data_in; stage_next <= PROCESS_HEARTBEAT_SEQUENCE_NUMBERS; end case; end if; when PROCESS_HEARTBEAT_SEQUENCE_NUMBERS => -- Wait for Sequence Number to be fetched from buffer if (mem_done = '1' and seq_prc_done = '1') then -- No scheduled Heartbeat Response if (mem_participant_data.heartbeat_res_time /= (mem_participant_data.heartbeat_res_time'reverse_range => '0')) then -- If current sequence number obsolete (removed from source history cache), -- reset sequence number to expect the first available and request it if (first_seq_nr > next_seq_nr) then -- Store expected sequence number -1, since we process only stored sequence number +1 seq_nr_next <= first_seq_nr - 1; mem_opcode <= UPDATE_PARTICIPANT; deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '0'; update_participant_flags_next <= (EDP_SEQ_NR_FLAG => '1', HEARTBEAT_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; -- If new sequence number is available or Writer expects ACKNACK, send it elsif (last_seq_nr > mem_seq_nr or final_flag = '0') then mem_opcode <= UPDATE_PARTICIPANT; deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '0'; update_participant_flags_next <= (HEARTBEAT_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; -- Currently in Heartbeat Response Delay elsif (mem_participant_data.heartbeat_res_time(0) = '0') then -- If current sequence number obsolete (removed from source history cache), -- reset sequence number to expect the first available and request it if (first_seq_nr > next_seq_nr) then -- Store expected sequence number -1, since we process only stored sequence number +1 seq_nr_next <= first_seq_nr - 1; mem_opcode <= UPDATE_PARTICIPANT; update_participant_flags_next <= (EDP_SEQ_NR_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; end if; -- Done stage_next <= SKIP_PACKET; end if; when SEND_ACKNACK => if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; case (cnt) is -- OUTPUT HEADER -- Src IPv4 Address when 1 => output_sig <= DEFAULT_IPv4_MULTICAST_ADDRESS; -- Dest IPv4 Address when 2 => output_sig <= mem_participant_data.meta_addr; -- Src and Dest UDPv4 Ports when 3 => output_sig <= META_IPv4_UNICAST_PORT & mem_participant_data.meta_port; -- RTPS MESSAGE HEADER when 4 => output_sig <= PROTOCOL_RTPS; when 5 => output_sig <= PROTOCOLVERSION_2_4 & VENDORID; when 6 => output_sig <= GUIDPREFIX(0); when 7 => output_sig <= GUIDPREFIX(1); when 8 => output_sig <= GUIDPREFIX(2); -- ACKNACK RTPS SUBMESSAGE (Publication) -- RTPS Submessage Header when 9 => output_sig <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(24, 16)); -- Reader Entity ID when 10 => output_sig <= ENTITYID_SEDP_BUILTIN_PUBLICATIONS_DETECTOR; -- Writer Entity ID when 11 => output_sig <= ENTITYID_SEDP_BUILTIN_PUBLICATIONS_ANNOUNCER; -- Sequence Number Set (Bitmap Base 1/2) when 12 => output_sig <= mem_participant_data.pub_seq_nr(0); -- Sequence Number Set (Bitmap Base 2/2) when 13 => output_sig <= mem_participant_data.pub_seq_nr(1); -- Sequence Number Set (NumBits) when 14 => output_sig <= (others => '0'); -- Count when 15 => output_sig <= count; -- ACKNACK RTPS SUBMESSAGE (Subscription) -- RTPS Submessage Header when 16 => output_sig <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(24, 16)); -- Reader Entity ID when 17 => output_sig <= ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_DETECTOR; -- Writer Entity ID when 18 => output_sig <= ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_ANNOUNCER; -- Sequence Number Set (Bitmap Base 1/2) when 19 => output_sig <= mem_participant_data.sub_seq_nr(0); -- Sequence Number Set (Bitmap Base 2/2) when 20 => output_sig <= mem_participant_data.sub_seq_nr(1); -- Sequence Number Set (NumBits) when 21 => output_sig <= (others => '0'); -- Count when 22 => output_sig <= count; -- ACKNACK RTPS SUBMESSAGE (Message) -- RTPS Submessage Header when 23 => output_sig <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(24, 16)); -- Reader Entity ID when 24 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER; -- Writer Entity ID when 25 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER; -- Sequence Number Set (Bitmap Base 1/2) when 26 => output_sig <= mem_participant_data.mes_seq_nr(0); -- Sequence Number Set (Bitmap Base 2/2) when 27 => output_sig <= mem_participant_data.mes_seq_nr(1); -- Sequence Number Set (NumBits) when 28 => output_sig <= (others => '0'); -- Count when 29 => output_sig <= count; -- Signal Last Word last_word_out <= '1'; -- DONE stage_next <= IDLE; end case; end if; when PROCESS_ACKNACK => if (empty = '0') then rd_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; -- Latch Sequence Number -- NOTE: Because we always sent the entire history cache, we only need to look at the SequenceNumberSetBase to determine if we need to sent data or not case (cnt) is when 1 => first_seq_nr_next(0) <= data_in; when 2 => first_seq_nr_next(1) <= data_in; stage_next <= PROCESS_ACKNACK_SEQUENCE_NUMBERS end case; end if; when PROCESS_ACKNACK_SEQUENCE_NUMBERS => -- Wait for Memory Operation to finish if (mem_done = '1') then -- No scheduled Acknack Response if (mem_participant_data.acknack_res_time /= (mem_participant_data.acknack_res_time'reverse_range => '0')) then case (message_type) is when EDP => -- Subscriber Acknack if (is_subscriber = '1') then -- If reader has not acked all Publisher history cache, mark for send if (first_seq_nr <= PUB_SEQUENCE_NR) then mem_opcode <= UPDATE_PARTICIPANT; -- Set Acknack Response Time deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '0'; -- Set Publisher Data as Acknack Response extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(PUB_DATA_FLAG) <= '1'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', ACKNACK_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; -- Publisher Acknack else -- If reader has not acked all Subscriber history cache, mark for send if (first_seq_nr <= SUB_SEQUENCE_NR) then mem_opcode <= UPDATE_PARTICIPANT; -- Set Acknack Response Time deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '0'; -- Set Subscriber Data as Acknack Response extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(SUB_DATA_FLAG) <= '1'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', ACKNACK_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; end if; -- Message Acknack when MESSAGE => -- NOTE: "auto_live_seq_nr" always has the higher sequence number by design, so we just need to -- check against that -- If reader has not acked all Message history cache, mark for send if (first_seq_nr <= auto_live_seq_nr) then mem_opcode <= UPDATE_PARTICIPANT; -- Set Acknack Response Time deadline_next <= time + TODO; -- NOTE: Last Bit denotes if this is Response or Suppression Delay deadline_next(0) <= '0'; -- Set Message Data as Acknack Response extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(MES_DATA_FLAG) <= '1'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', ACKNACK_RES_TIME_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; end case; -- Currently in Acknack Response Delay elsif (mem_participant_data.acknack_res_time(0) = '0') then case (message_type) is when EDP => -- Subscriber Acknack if (is_subscriber = '1') then -- Publisher Data not scheduled for response if (mem_participant_data.extra_flags(PUB_DATA_FLAG)) then -- If reader has not acked all Publisher history cache, mark for send if (first_seq_nr <= PUB_SEQUENCE_NR) then mem_opcode <= UPDATE_PARTICIPANT; -- Set Publisher Data as Acknack Response extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(PUB_DATA_FLAG) <= '1'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; end if; -- Publisher Acknack else -- Subscriber Data not scheduled for response if (mem_participant_data.extra_flags(SUB_DATA_FLAG)) then -- If reader has not acked all Subscriber history cache, mark for send if (first_seq_nr <= SUB_SEQUENCE_NR) then mem_opcode <= UPDATE_PARTICIPANT; -- Set Subscriber Data as Acknack Response extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(SUB_DATA_FLAG) <= '1'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; end if; -- Message Acknack when MESSAGE => -- Message Data not scheduled for response if (mem_participant_data.extra_flags(MES_DATA_FLAG)) then -- NOTE: "auto_live_seq_nr" always has the higher sequence number by design, so we just need to -- check against that -- If reader has not acked all Message history cache, mark for send if (first_seq_nr <= auto_live_seq_nr) then mem_opcode <= UPDATE_PARTICIPANT; -- Set Message Data as Acknack Response extra_flags_next <= mem_participant_data.extra_flags; extra_flags_next(MES_DATA_FLAG) <= '1'; update_participant_flags_next <= (EXTRA_FLAGS_FLAG => '1', others => '0'); start_mem_op <= '1'; end if; end if; end case; end if; -- Done stage_next <= SKIP_PACKET; end if; when SEND_PUB_DATA => -- If Publisher Data not scheduled for response or no Writers available, skip if (mem_participant_data.extra_flags(PUB_DATA_FLAG) = '0' or NUM_WRITERS = 0) then stage_next <= SEND_SUB_DATA; end if; if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; -- Send Data output_sig <= WRITER_ENDPOINT_DATA.data(cnt); -- Exit Condition if (cnt = (WRITER_ENDPOINT_DATA.length-1)) then last_word_out <= '1'; cnt_next <= 0; stage_next <= SEND_SUB_DATA; end if; end if; when SEND_SUB_DATA => -- If Subscriber Data not scheduled for response or no Readers available, skip if (mem_participant_data.extra_flags(PUB_DATA_FLAG) = '0' or NUM_READERS = 0) then stage_next <= SEND_MES_DATA; end if; if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; -- Send Data output_sig <= READER_ENDPOINT_DATA.data(cnt); -- Exit Condition if (cnt = (READER_ENDPOINT_DATA.length-1)) then last_word_out <= '1'; cnt_next <= 1; stage_next <= SEND_MES_DATA; end if; end if; when SEND_MES_DATA => -- If Message Data not scheduled for response, skip if (mem_participant_data.extra_flags(MES_DATA_FLAG) = '0') then stage_next <= IDLE; end if; if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; case (cnt) is -- OUTPUT HEADER -- Src IPv4 Address when 1 => output_sig <= DEFAULT_IPv4_MULTICAST_ADDRESS; -- Dest IPv4 Address when 2 => output_sig <= mem_participant_data.meta_addr; -- Src and Dest UDPv4 Ports when 3 => output_sig <= META_IPv4_UNICAST_PORT & mem_participant_data.meta_port; -- RTPS MESSAGE HEADER when 4 => output_sig <= PROTOCOL_RTPS; when 5 => output_sig <= PROTOCOLVERSION_2_4 & VENDORID; when 6 => output_sig <= GUIDPREFIX(0); when 7 => output_sig <= GUIDPREFIX(1); when 8 => output_sig <= GUIDPREFIX(2); cnt_next <= 1; stage_next <= SEND_MAN_LIVE; end case; end if; when SEND_MES_MAN_LIVE => if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; case (cnt) is -- DATA RTPS SUBMESSAGE (Participant Message) -- RTPS Submessage Header when 1 => output_sig <= SID_DATA & "00000100" & std_logic_vector(to_unsigned(44, 16)); -- ExtraFlags, octetsToInlineQoS when 2 => output_sig <= x"0000" & std_logic_vector(to_unsigned(16, 16)); -- Reader Entity ID when 3 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER; -- Writer Entity ID when 4 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER; -- Sequence Number 1/2 when 5 => output_sig <= man_live_seq_nr(0); -- Sequence Number 2/2 when 6 => output_sig <= man_live_seq_nr(1); -- Serialized Payload Header when 7 => output_sig <= CDR_BE & x"0000"; -- Serialized Payload BEGIN -- GUID Prefix 1/3 when 8 => output_sig <= GUIDPREFIX(0); -- GUID Prefix 2/3 when 9 => output_sig <= GUIDPREFIX(1); -- GUID Prefix 3/3 when 10 => output_sig <= GUIDPREFIX(3); -- Participant Message Kind when 11 => output_sig <= PARTICIPANT_MESSAGE_DATA_KIND_MANUAL_LIVELINESS_UPDATE; -- Data Length when 12 => output_sig <= (others => '0'); -- If manual and automatic sequence numbers are not consecutive, we need to send a GAP Message if (live_gap_start /= auto_live_seq_nr) then stage_next <= SEND_MES_GAP; cnt_next <= 1; -- Else Continue with Automatic Liveliness else stage_next <= SEND_MES_AUTO_LIVE; cnt_next <= 1; end if; end case; end if; when SEND_MES_GAP => if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; case (cnt) is -- DATA RTPS SUBMESSAGE (Participant Message) -- RTPS Submessage Header when 1 => output_sig <= SID_GAP & "00000000" & std_logic_vector(to_unsigned(28, 16)); -- Reader Entity ID when 2 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER; -- Writer Entity ID when 3 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER; -- GAP Start Sequence Number 1/2 when 4 => output_sig <= live_gap_start(0); -- GAP Start Sequence Number 2/2 when 5 => output_sig <= live_gap_start(1); -- GAP End Sequence Number Set (Bitmap Base 1/2) when 6 => output_sig <= live_gap_end(0); -- GAP End Sequence Number Set (Bitmap Base 2/2) when 7 => output_sig <= live_gap_end(1); -- GAP End Sequence Number Set (NumBits) when 8 => output_sig <= (others => '0'); stage_next <= SEND_MES_AUTO_LIVE; cnt_next <= 1; end case; end if; when SEND_MES_AUTO_LIVE => if (rtps_full = '0') then wr_sig <= '1'; --Increment Counter cnt_next <= cnt + 1; case (cnt) is -- DATA RTPS SUBMESSAGE (Participant Message) -- RTPS Submessage Header when 1 => output_sig <= SID_DATA & "00000100" & std_logic_vector(to_unsigned(44, 16)); -- ExtraFlags, octetsToInlineQoS when 2 => output_sig <= x"0000" & std_logic_vector(to_unsigned(16, 16)); -- Reader Entity ID when 3 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER; -- Writer Entity ID when 4 => output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER; -- Sequence Number 1/2 when 5 => output_sig <= auto_live_seq_nr(0); -- Sequence Number 2/2 when 6 => output_sig <= auto_live_seq_nr(1); -- Serialized Payload Header when 7 => output_sig <= CDR_BE & x"0000"; -- Serialized Payload BEGIN -- GUID Prefix 1/3 when 8 => output_sig <= GUIDPREFIX(0); -- GUID Prefix 2/3 when 9 => output_sig <= GUIDPREFIX(1); -- GUID Prefix 3/3 when 10 => output_sig <= GUIDPREFIX(3); -- Participant Message Kind when 11 => output_sig <= PARTICIPANT_MESSAGE_DATA_KIND_AUTOMATIC_LIVELINESS_UPDATE; -- Data Length when 12 => output_sig <= (others => '0'); last_word_out <= '1'; stage_next <= IDLE; end case; end if; --############################# when SKIP_PARAMETER => -- End of Parameter if (read_cnt > parameter_end) then -- Begin parsing of next parameter stage_next <= PROCESS_PL; elsif (empty = '0') then rd_sig <= '1'; if (read_cnt = parameter_end) then -- Begin parsing of next parameter stage_next <= PROCESS_PL; end if; end if; when SKIP_PACKET => if (last_word_in_latch = '1') then -- Begin parsing of next parameter stage_next <= IDLE; elsif (empty = '0') then rd_sig <= '1'; -- End of Packet if (last_word_in = '1') then -- Continue parsing next packet stage_next <= IDLE; end if; end if; when others => null; end case; end process; mem_op_busy <= not mem_op_done; mem_ctrl_prc : process(all) variable tmp : unsigned(mem_addr_base'range) := (others => '0'); variable tmp2 : unsigned(mem_addr_base'range) := (others => '0'); variable tmp3 : unsigned(mem_addr_base'range) := (others => '0'); begin -- DEFAULT mem_op_done <= '0'; mem_stage_next <= mem_stage; mem_addr_base_next <= mem_addr_base; mem_addr_next <= mem_addr; mem_rd <= '0'; mem_wr <= '0'; addr_res_next <= addr_res; mem_cnt_next <= mem_cnt; mem_seq_nr_next <= mem_seq_nr; orphan_entityid_next <= orphan_entityid; mem_def_addr_next <= mem_def_addr; mem_def_port_next <= mem_def_port; mem_xflags_next <= mem_xflags; last_addr_next <= last_addr; mem_participant_data_next <= mem_participant_data; is_heartbeat_res_next <= is_heartbeat_res; case (mem_stage) is when IDLE => mem_op_done <= '1'; reset_max_pointer_next <= '0'; if (start_mem_op = '1') then case(mem_opcode) is when SEARCH_PARTICIPANT => mem_stage_next <= SEARCH_PARTICIPANT; tmp := (others => '0'); mem_addr_base_next <= tmp; mem_addr_next <= tmp; when SEARCH_ENDPOINT => mem_stage_next <= SEARCH_ENDPOINT; tmp := to_unsigned(BUILTIN_BUFFER_SIZE - ENDPOINT_FRAME_SIZE, mem_addr_base'length); mem_addr_base_next <= tmp; mem_addr_next <= tmp; when REMOVE_PARTICIPANT => mem_stage_next <= REMOVE_PARTICIPANT; mem_addr_next <= addr_res; mem_cnt_next <= 1; when REMOVE_ENDPOINT => mem_stage_next <= REMOVE_ENDPOINT; mem_addr_next <= addr_res; mem_cnt_next <= 1; when UPDATE_ENDPOINT => mem_stage_next <= UPDATE_ENDPOINT; mem_addr_next <= addr_res + 4; mem_cnt_next <= 0; endpoint_mask_array_next <= convert_to_bitmask_array(endpoint_mask); when INSERT_PARTICIPANT => mem_stage_next <= FIND_PARTICIPANT_SLOT; tmp := (others => '0'); mem_addr_base_next <= tmp; mem_addr_next <= tmp; mem_cnt_next <= 0; when INSERT_ENDPOINT => mem_stage_next <= FIND_ENDPOINT_SLOT; tmp := to_unsigned(BUILTIN_BUFFER_SIZE - ENDPOINT_FRAME_SIZE, mem_addr_base'length); mem_addr_base_next <= tmp; mem_addr_next <= tmp; mem_cnt_next <= 0; when RESET_MAX_PARTICIPANT_POINTER => mem_stage_next <= FIND_PARTICIPANT_SLOT; tmp := (others => '0'); mem_addr_base_next <= tmp; mem_addr_next <= tmp; mem_cnt_next <= 0; reset_max_pointer_next <= '1'; last_addr_next <= (others => '0'); when RESET_MAX_ENDPOINT_POINTER => mem_stage_next <= FIND_ENDPOINT_SLOT; tmp := to_unsigned(BUILTIN_BUFFER_SIZE - ENDPOINT_FRAME_SIZE, mem_addr_base'length); mem_addr_base_next <= tmp; mem_addr_next <= tmp; mem_cnt_next <= 0; reset_max_pointer_next <= '1'; last_addr_next <= to_unsigned(BUILTIN_BUFFER_SIZE, mem_addr_base'length); when FIND_STALE_PARTICIPANT => mem_stage_next <= FIND_STALE_PARTICIPANT; tmp := (others => '0'); mem_addr_base_next <= tmp; mem_addr_next <= tmp; mem_cnt_next <= 0; when UPDATE_PARTICIPANT => mem_stage_next <= UPDATE_PARTICIPANT; if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_addr_next <= addr_res + 3; mem_cnt_next <= 1; elsif (update_participant_flags(LEASE_DEADLINE_FLAG) = '1') then mem_addr_next <= addr_res + 10; mem_cnt_next <= 8; elsif (update_participant_flags(EXTRA_FLAGS_FLAG) = '1') then mem_addr_next <= addr_res + 12; mem_cnt_next <= 10; elsif (update_participant_flags(ACKNACK_RES_TIME_FLAG) = '1') then mem_addr_next <= addr_res + 13; mem_cnt_next <= 11; elsif (update_participant_flags(HEARTBEAT_RES_TIME_FLAG) = '1') then mem_addr_next <= addr_res + 15; mem_cnt_next <= 13; elsif (update_participant_flags(EDP_SEQ_NR_FLAG) = '1') then mem_addr_next <= addr_res + 17; mem_cnt_next <= 15; end if; when others => null; end case; end if; when SEARCH_PARTICIPANT => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Next Participant Frame Address tmp := mem_addr_base + PARTICIPANT_FRAME_SIZE; case (mem_cnt) is when 0 => -- Preload -- Reached MAX Addr, No Match Found if (mem_addr_base = max_participant_addr) then addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length); --No match mem_stage_next <= IDLE; end if; when 1 => -- No Match if (mem_read_data /= guid(0)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; end if; when 2 => -- No Match if (mem_read_data /= guid(1)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; end if; when 3 => -- No Match if (mem_read_data /= guid(2)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; -- Match else -- Fetch Participant Data mem_stage_next <= GET_PARTICIPANT_DATA; -- No preload needed mem_cnt_next <= 1; end if; end case; when GET_PARTICIPANT_DATA => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment mem_addr_next <= mem_addr + 1; case (mem_cnt) is when 0 => -- Memory Preload null; when 1 => -- Metatraffic IPv4 Address mem_participant_data_next.meta_addr <= mem_read_data; when 2 => -- Default Endpoint IPv4 Address mem_participant_data_next.def_addr <= mem_read_data; when 3 => -- UDPv4 Ports mem_participant_data_next.meta_port <= mem_read_data(31 downto 16); mem_participant_data_next.def_port <= mem_read_data(15 downto 0); when 4 => -- SPDP Sequence Number 1/2 mem_participant_data_next.spdp_seq_nr(0) <= unsigned(mem_read_data); when 5 => -- SPDP Sequence Number 2/2 mem_participant_data_next.spdp_seq_nr(1) <= unsigned(mem_read_data); when 6 => -- Lease Duration 1/2 mem_participant_data_next.lease_duration(0) <= unsigned(mem_read_data); when 7 => -- Lease Duration 2/2 mem_participant_data_next.lease_duration(1) <= unsigned(mem_read_data); when 8 => -- Lease Deadline 1/2 mem_participant_data_next.lease_deadline(0) <= unsigned(mem_read_data); when 9 => -- Lease Deadline 2/2 mem_participant_data_next.lease_deadline(1) <= unsigned(mem_read_data); when 10 => -- Extra Flags mem_participant_data_next.extra_flags <= mem_read_data; when 11 => -- ACKNACK DEADLINE 1/2 mem_participant_data_next.acknack_res_time(0) <= unsigned(mem_read_data); when 12 => -- ACKNACK DEADLINE 2/2 mem_participant_data_next.acknack_res_time(1) <= unsigned(mem_read_data); when 13 => -- HEARTBEAT DEADLINE 1/2 mem_participant_data_next.heartbeat_res_time(0) <= unsigned(mem_read_data); when 14 => -- HEARTBEAT DEADLINE 2/2 mem_participant_data_next.heartbeat_res_time(1) <= unsigned(mem_read_data); when 15 => -- Publication Sequence Number 1/2 mem_participant_data_next.pub_seq_nr(0) <= unsigned(mem_read_data); when 16 => -- Publication Sequence Number 2/2 mem_participant_data_next.pub_seq_nr(1) <= unsigned(mem_read_data); when 17 => -- Subscription Sequence Number 1/2 mem_participant_data_next.sub_seq_nr(0) <= unsigned(mem_read_data); when 18 => -- Subscription Sequence Number 2/2 mem_participant_data_next.sub_seq_nr(1) <= unsigned(mem_read_data); when 19 => -- Participant Message Sequence Number 1/2 mem_participant_data_next.mes_seq_nr(0) <= unsigned(mem_read_data); when 20 => -- Participant Message Sequence Number 2/2 mem_participant_data_next.mes_seq_nr(1) <= unsigned(mem_read_data); -- DONE mem_stage_next <= IDLE; end case; when SEARCH_ENDPOINT => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Next Endpoint Frame Address tmp := mem_addr_base - ENDPOINT_FRAME_SIZE; case (mem_cnt) is when 0 => -- Reached MAX Addr, No Match Found if (mem_addr_base = max_endpoint_addr) then addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length);; -- No match mem_stage_next <= IDLE; end if; when 1 => -- If we search for orphan endpoint, ignore Entity id match and latch it for later use if (is_orphan_search = '1') then orphan_entityid_next <= mem_read_data; else -- No Match -- NOTE: Endpoint GUID is stored with Entity ID first, and then the GUID Prefix if (mem_read_data /= guid(3)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; end if; end if; when 2 => -- No Match if (mem_read_data /= guid(0)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; end if; when 3 => -- No Match if (mem_read_data /= guid(1)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; end if; when 4 => -- No Match if (mem_read_data /= guid(2)) then -- Continue Search mem_addr_next <= tmp; mem_addr_base <= tmp; mem_cnt_next <= 0; -- Match Found else mem_stage_next <= GET_ENDPOINT_MASK; mem_cnt_next <= 0; end if; end case; when GET_ENDPOINT_MASK => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Latch Endpoint Bitmask -- TODO: Use a different integer ranged in the smalled possible range? endpoint_mask_array_next(mem_cnt) <= mem_read_data; -- Exit Condition if (mem_cnt = ENDPOINT_BITMASK_SIZE-1) then -- DONE mem_stage_next <= IDLE; end if; when REMOVE_PARTICIPANT => -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Latch Participant GUID Prefix, and then overwrite with GUIDPREFIX_UNKNOWN to mark as empty case (mem_cnt) is when 0 => -- Preload mem_rd <= '1'; when 1 => mem_rd <= '1'; mem_guidprefix_next(0) <= mem_read_data; when 2 => mem_rd <= '1'; mem_guidprefix_next(1) <= mem_read_data; when 3 => mem_rd <= '1'; mem_guidprefix_next(2) <= mem_read_data; mem_addr_next <= addr_res; when 4 => mem_wr <= '1'; mem_write_data <= GUIDPREFIX_UNKNOWN_ARRAY(0); when 5 => mem_wr <= '1'; mem_write_data <= GUIDPREFIX_UNKNOWN_ARRAY(1); when 6 => mem_wr <= '1'; mem_write_data <= GUIDPREFIX_UNKNOWN_ARRAY(2); -- DONE mem_stage_next <= RESET_MAX_PARTICIPANT_POINTER; when others => null; end case; when REMOVE_ENDPOINT => mem_wr <= '1'; mem_write_data <= ENTITYID_UNKNOWN; -- DONE mem_stage_next <= RESET_MAX_ENDPOINT_POINTER; when UPDATE_ENDPOINT => mem_wr <= '1'; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; mem_write_data <= endpoint_mask_array(mem_cnt); -- Exit Condition if (mem_cnt = ENDPOINT_BITMASK_SIZE-1) then -- DONE mem_stage_next <= IDLE; end if; when FIND_PARTICIPANT_SLOT => mem_rd <= '1'; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Next Participant Frame Address tmp := mem_addr_base + PARTICIPANT_FRAME_SIZE; case (mem_cnt) is when 0 => -- Preload -- Reached MAX Addr if (mem_addr_base = max_participant_addr) then if (reset_max_pointer = '1') then -- Reset "max_participant_addr" to first free slot after last occupied slot if (last_addr /= (last_addr'reverse_range => '0')) then max_participant_addr_next <= last_addr; end if; -- DONE (reset pointer) mem_stage_next <= IDLE; -- MEMORY COLLISION -- XXX: Posible worst case path (addition and comparison same clock) elsif (tmp > max_endpoint_addr) then -- Ignore Insertion mem_stage_next <= IDLE; else -- Extend Participant Memory Area -- NOTE: "max_participant_addr" points to the first address after the last participant frame max_participant_addr_next <= tmp; -- DONE mem_stage_next <= INSERT_PARTICIPANT; mem_cnt_next <= 1; end if; end if; when 1 => if (mem_read_data /= GUIDPREFIX_UNKNOWN_ARRAY(0)) then mem_addr_next <= tmp; mem_addr_base_next <= tmp; mem_cnt_next <= 0; last_addr_next <= (others => '0'); end if; when 2 => if (mem_read_data /= GUIDPREFIX_UNKNOWN_ARRAY(1)) then mem_addr_next <= tmp; mem_addr_base_next <= tmp; mem_cnt_next <= 0; last_addr_next <= (others => '0'); end if; when 3 => if (mem_read_data /= GUIDPREFIX_UNKNOWN_ARRAY(2)) then mem_addr_next <= tmp; mem_addr_base_next <= tmp; mem_cnt_next <= 0; last_addr_next <= (others => '0'); else -- If "reset_max_pointer" is set, go through all the participant memory area to reset the pointer if (reset_max_pointer = '1') then -- Max pointer reset logic -- Store first free slot address after occupied slot if (last_addr /= (last_addr'reverse_range => '0')) then last_addr_next <= mem_addr_base; end if; else -- Found Empty Slot, DONE mem_stage_next <= INSERT_PARTICIPANT; mem_cnt_next <= 1; end if; end if; end case; when INSERT_PARTICIPANT => mem_wr <= '1'; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; case (mem_cnt) is when 1 => -- GUIDPrefix 1/3 mem_write_data <= guid(0); when 2 => -- GUIDPrefix 2/3 mem_write_data <= guid(1); when 3 => -- GUIDPrefix 3/3 mem_write_data <= guid(2); when 4 => -- Metatraffic IPv4 Address mem_write_data <= addr_latch_2; when 5 => -- Default Endpoint IPv4 Address mem_write_data <= addr_latch_1; when 6 => -- UDPv4 Ports mem_write_data <= port_latch_2 & port_latch_1; when 7 => -- SPDP Sequence Number 1/2 mem_write_data <= std_logic_vector(seq_nr(0)); when 8 => -- SPDP Sequence Number 2/2 mem_write_data <= std_logic_vector(seq_nr(1)); when 9 => -- Lease Duration 1/2 mem_write_data <= std_logic_vector(lease_duration(0)); when 10 => -- Lease Duration 2/2 mem_write_data <= std_logic_vector(lease_duration(1)); when 11 => -- Lease Deadline 1/2 mem_write_data <= std_logic_vector(lease_deadline(0)); when 12 => -- Lease Deadline 2/2 mem_write_data <= std_logic_vector(lease_deadline(1)); when 13 => -- Extra Flags mem_write_data <= extra_flags; when 14 => -- ACKNACK DEADLINE 1/2 mem_write_data <= (others => '0'); when 15 => -- ACKNACK DEADLINE 2/2 mem_write_data <= (others => '0'); when 16 => -- HEARTBEAT DEADLINE 1/2 mem_write_data <= (others => '0'); when 17 => -- HEARTBEAT DEADLINE 2/2 mem_write_data <= (others => '0'); when 18 => -- Publication Sequence Number 1/2 mem_write_data <= (others => '0'); when 19 => -- Publication Sequence Number 2/2 mem_write_data <= (others => '0'); when 20 => -- Subscription Sequence Number 1/2 mem_write_data <= (others => '0'); when 21 => -- Subscription Sequence Number 2/2 mem_write_data <= (others => '0'); when 22 => -- Participant Message Sequence Number 1/2 mem_write_data <= (others => '0'); when 23 => -- Participant Message Sequence Number 2/2 mem_write_data <= (others => '0'); end case; when FIND_ENDPOINT_SLOT => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Next Endpoint Frame Address tmp := mem_addr_base - ENDPOINT_FRAME_SIZE; case (mem_cnt) is when 0 => -- Preload -- Exceeded MAX Addr if (mem_addr_base > max_endpoint_addr) then if (reset_max_pointer = '1') then -- Reset "max_endpoint_addr" to last occupied slot if (last_addr /= BUILTIN_BUFFER_SIZE) then max_endpoint_addr_next <= last_addr; end if; -- DONE (reset pointer) mem_stage_next <= IDLE; -- MEMORY COLLISION elsif (mem_addr_base < max_participant_addr) then -- Ignore Insertion mem_stage_next <= IDLE; else -- Extend Participant Memory Area -- NOTE: "max_endpoint_addr" points to the beginning of the last endpoint frame max_endpoint_addr <= mem_addr_base; -- DONE mem_stage_next <= INSERT_PARTICIPANT; mem_cnt_next <= 1; end if; end if; when 1 => if (mem_read_data /= ENTITYID_UNKNOWN) then mem_addr_next <= tmp; mem_addr_base_next <= tmp; mem_cnt_next <= 0; -- Store last occupied endpoint slot last_addr_next <= mem_addr_base; else -- If "reset_max_pointer" is set, go through all the endpoint memory area to reset the pointer if (reset_max_pointer = '0') then -- Found Empty Slot, DONE mem_stage_next <= INSERT_ENDPOINT; mem_cnt_next <= 1; end if; end if; end case; when INSERT_ENDPOINT => mem_wr <= '1'; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; case (mem_cnt) is when 1 => -- Entity ID mem_write_data <= guid(3); when 2 => -- GUIDPrefix 1/3 mem_write_data <= guid(0); when 3 => -- GUIDPrefix 2/3 mem_write_data <= guid(1); when 4 => -- GUIDPrefix 3/3 mem_write_data <= guid(2); -- Write endpoint bitmask via update method mem_stage_next <= UPDATE_ENDPOINT; mem_cnt_next <= 0; endpoint_mask_array_next <= convert_to_bitmask_array(endpoint_mask); end case; when FIND_STALE_PARTICIPANT => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment mem_addr_next <= mem_addr + 1; -- Next Participant Frame Address tmp := mem_addr_base + PARTICIPANT_FRAME_SIZE; tmp2 := mem_addr_base + 11; tmp3 := mem_addr_base + 3; case (mem_cnt) is when 0 => -- Preload -- Reached MAX Addr, No Match Found if (mem_addr_base = max_participant_addr) then addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length); --No match mem_stage_next <= IDLE; end if; when 1 => -- If slot occupied, jump to stale check if (mem_read_data /= GUIDPREFIX_UNKNOWN_ARRAY(0)) then mem_addr_next <= tmp2; mem_cnt_next <= 4; end if; when 2 => -- If slot occupied, jump to stale check if (mem_read_data /= GUIDPREFIX_UNKNOWN_ARRAY(1)) then mem_addr_next <= tmp2; mem_cnt_next <= 4; end if; when 3 => -- If slot occupied, jump to stale check if (mem_read_data /= GUIDPREFIX_UNKNOWN_ARRAY(2)) then mem_addr_next <= tmp2; mem_cnt_next <= 4; -- Slot empty, check next slot else mem_addr_next <= tmp; mem_addr_base_next <= tmp; mem_cnt_next <= 0; end if; when 4 => -- Preload null; when 5 => -- Lease Deadline passed, mark participant if (unsigned(mem_read_data) < time(0)) then addr_res_next <= mem_addr_base; -- Mark that this is a stale participant mem_participant_data_next <= ZERO_PARTICIPANT_DATA; -- DONE mem_stage_next <= IDLE; end if; when 6 => -- Lease Deadline passed, mark participant if (unsigned(mem_read_data) < time(1)) then addr_res_next <= mem_addr_base; -- Mark that this is a stale participant mem_participant_data_next <= ZERO_PARTICIPANT_DATA; -- DONE mem_stage_next <= IDLE; end if; when 7 => -- Heartbeat Deadline passed, mark participant if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(0)) then addr_res_next <= mem_addr_base; -- Fetch Participant Data mem_addr_next <= tmp3; mem_cnt_next <= 0; mem_stage_next <= GET_PARTICIPANT_DATA; -- Mark as Heartbeat deadline miss is_heartbeat_res_next <= '1'; end if; when 8 => -- Heartbeat Deadline passed, mark participant if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(1)) then addr_res_next <= mem_addr_base; -- Fetch Participant Data mem_addr_next <= tmp3; mem_cnt_next <= 0; mem_stage_next <= GET_PARTICIPANT_DATA; -- Mark as Heartbeat deadline miss is_heartbeat_res_next <= '1'; end if; when 9 => -- Acknack Deadline passed, mark participant if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(0)) then addr_res_next <= mem_addr_base; -- Fetch Participant Data mem_addr_next <= tmp3; mem_cnt_next <= 0; mem_stage_next <= GET_PARTICIPANT_DATA; -- Mark as Acknack deadline miss is_heartbeat_res_next <= '0'; end if; when 10 => -- Acknack Deadline passed, mark participant if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(1)) then addr_res_next <= mem_addr_base; -- Fetch Participant Data mem_addr_next <= tmp3; mem_cnt_next <= 0; mem_stage_next <= GET_PARTICIPANT_DATA; -- Mark as Acknack deadline miss is_heartbeat_res_next <= '0'; -- No deadline expired, check next slot else mem_addr_next <= tmp; mem_addr_base_next <= tmp; mem_cnt_next <= 0; end if; end case; when UPDATE_PARTICIPANT => -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment mem_addr_next <= mem_addr + 1; case (mem_cnt) is when 1 => -- Metatraffic IPv4 Address mem_write_data <= addr_latch_2; if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; when 2 => -- Default Endpoint IPv4 Address mem_write_data <= addr_latch_1; if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; when 3 => -- UDPv4 Ports mem_write_data <= port_latch_2 & port_latch_1; if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; when 4 => -- SPDP Sequence Number 1/2 mem_write_data <= std_logic_vector(seq_nr(0)); if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; when 5 => -- SPDP Sequence Number 2/2 mem_write_data <= std_logic_vector(seq_nr(1)); if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; when 6 => -- Lease Duration 1/2 mem_write_data <= std_logic_vector(lease_duration(0)); if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; when 7 => -- Lease Duration 2/2 mem_write_data <= std_logic_vector(lease_duration(1)); if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; -- If nothing else to update, skip if (update_participant_flags(5 downto 1) = (5 downto 1 => '0')) then mem_stage_next <= IDLE; end if; when 8 => -- Lease Deadline 1/2 mem_write_data <= std_logic_vector(lease_deadline(0)); if (update_participant_flags(LEASE_DEADLINE_FLAG) = '1') then mem_wr <= '1'; end if; when 9 => -- Lease Deadline 2/2 mem_write_data <= std_logic_vector(lease_deadline(1)); if (update_participant_flags(LEASE_DEADLINE_FLAG) = '1') then mem_wr <= '1'; end if; -- If nothing else to update, skip if (update_participant_flags(5 downto 2) = (5 downto 2 => '0')) then mem_stage_next <= IDLE; end if; when 10 => -- Extra Flags mem_write_data <= extra_flags; if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then mem_wr <= '1'; end if; -- If nothing else to update, skip if (update_participant_flags(5 downto 3) = (5 downto 3 => '0')) then mem_stage_next <= IDLE; end if; when 11 => -- ACKNACK DEADLINE 1/2 mem_write_data <= std_logic_vector(acknack_res_time(0)); if (update_participant_flags(ACKNACK_RES_TIME_FLAG) = '1') then mem_wr <= '1'; end if; when 12 => -- ACKNACK DEADLINE 2/2 mem_write_data <= std_logic_vector(acknack_res_time(1)); if (update_participant_flags(ACKNACK_RES_TIME_FLAG) = '1') then mem_wr <= '1'; end if; -- If nothing else to update, skip if (update_participant_flags(5 downto 4) = (5 downto 4 => '0')) then mem_stage_next <= IDLE; end if; when 13 => -- HEARTBEAT DEADLINE 1/2 mem_write_data <= std_logic_vector(heartbeat_res_time(0)); if (update_participant_flags(HEARTBEAT_RES_TIME_FLAG) = '1') then mem_wr <= '1'; end if; when 14 => -- HEARTBEAT DEADLINE 2/2 mem_write_data <= std_logic_vector(heartbeat_res_time(1)); if (update_participant_flags(HEARTBEAT_RES_TIME_FLAG) = '1') then mem_wr <= '1'; end if; -- If nothing else to update, skip if (update_participant_flags(5 downto 5) = (5 downto 5 => '0')) then mem_stage_next <= IDLE; end if; when 15 => -- Publication Sequence Number 1/2 mem_write_data <= std_logic_vector(seq_nr(0)); if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '0') then mem_wr <= '1'; end if; when 16 => -- Publication Sequence Number 2/2 mem_write_data <= std_logic_vector(seq_nr(1)); if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '0') then mem_wr <= '1'; end if; when 17 => -- Subscription Sequence Number 1/2 mem_write_data <= std_logic_vector(seq_nr(0)); if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '1') then mem_wr <= '1'; end if; when 18 => -- Subscription Sequence Number 2/2 mem_write_data <= std_logic_vector(seq_nr(1)); if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '1') then mem_wr <= '1'; end if; when 19 => -- Participant Message Sequence Number 1/2 mem_write_data <= std_logic_vector(seq_nr(0)); if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = MESSAGE) then mem_wr <= '1'; end if; when 20 => -- Participant Message Sequence Number 2/2 mem_write_data <= std_logic_vector(seq_nr(1)); if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = MESSAGE) then mem_wr <= '1'; end if; mem_stage_next <= IDLE; end case; when others => null; end case; end process; -- Process responsible for counting read words -- This process uses the actual FIFO read signals to determine reads word_counter_prc : process(clk, reset) begin if rising_edge(clk) then -- Reset Read counter if (reset = '1' or reset_read_cnt = '1') then read_cnt <= to_unsigned(1, read_cnt'length); -- Increment read counter each time rd is high elsif (rd_sig = '1') then read_cnt <= read_cnt + 1; end if; end if; end process; end architecture;