rtps-fpga/src/rtps_endpoint.vhd

1763 lines
87 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.rtps_package.all;
use work.user_config.all;
use work.rtps_config_package.all;
-- TODO: Use TIME_INVALID instead of zero. (Backport to builtin_endpoint) [Pay atention to (0) bit of ACKNACK and HEARTBEAT Deadlines, if compatible]
-- TODO: Check for special values of time/duration?
-- TODO: Remove last_word flag from metattraffic operations
-- TODO: Replace all ('range => 0) checks with defined constants, where possible
-- TODO: Remove highest_seq_nr in stand alone git commit
entity rtps_endpoint is
generic (
ID : natural;
);
port (
clk : in std_logic;
reset : in std_logic;
time : in TIME_TYPE;
alive : out std_logic;
empty : in std_logic;
rd : out std_logic;
last_word_in : in std_logic;
data_in : in std_logic_vector(WORD_WIDTH-1 downto 0);
meta_empty : in std_logic;
meta_data_in : in std_logic_vector(WORD_WIDTH-1 downto 0);
meta_rd : out std_logic;
rtps_wr : out std_logic;
rtps_full : in std_logic;
last_word_out : out std_logic;
data_out : out std_logic_vector(WORD_WIDTH-1 downto 0);
hc_start : out std_logic;
hc_opcode : out HISTORY_CACHE_OPCODE_TYPE;
hc_ack : in std_logic;
hc_data_out : in std_logic_vector(WORD_WIDTH-1 downto 0);
hc_valid_out : in std_logic;
hc_ready_out : out std_logic;
hc_last_word_out: in std_logic;
hc_data_in : out std_logic_vector(WORD_WIDTH-1 downto 0);
hc_valid_in : out std_logic;
hc_ready_in : in std_logic;
hc_last_word_in : out std_logic
);
end entity;
architecture arch of rtps_endpoint is
--*****CONSTANT DECLARATION*****
-- Endpoint Memory Size in 4-Byte Words
constant ENDPOINT_MEMORY_SIZE : natural := TODO;
-- Endpoint Memory Address Width
constant ENDPOINT_MEMORY_WIDTH : natural := log2c(ENDPOINT_MEMORY_SIZE);
-- Highest Memory Address
constant MAX_ADDRESS : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := to_unsigned(ENDPOINT_MEMORY_SIZE-1, ENDPOINT_MEMORY_WIDTH);
-- Highest Endpoint Frame Address
constant MAX_ENDPOINT_ADDRESS : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := MAX_ADDRESS - ENDPOINT_FRAME_SIZE + 1;
-- Address pointing to the beginning of the first Endpoint Data Frame
constant FIRST_ENDPOINT_ADDRESS : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := (others => '0');
-- *UPDATE PARTICIPANT FLAG POSITIONS*
constant UPDATE_ENDPOINT_FLAG_WIDTH : natural := 4;
-- Signifies that the main Endpoint Data are updated
constant ENDPOINT_DATA_FLAG : std_logic_vector(UPDATE_ENDPOINT_FLAG_WIDTH-1 downto 0) := (0 => 1, others => '0');
-- Signifies that the Lease Deadline of the Endpoint Data is updated
constant LEASE_DEADLINE_FLAG : std_logic_vector(UPDATE_ENDPOINT_FLAG_WIDTH-1 downto 0) := (1 => 1, others => '0');
-- Signifies that the last Sequence Number of the Endpoint Data is updated
constant NEXT_SEQ_NR_FLAG : std_logic_vector(UPDATE_ENDPOINT_FLAG_WIDTH-1 downto 0) := (2 => 1, others => '0');
-- Signifies that the HEARTBEAT/ACKNACK Timeout Time of the Endpoint Data is updated
constant RES_TIME_FLAG : std_logic_vector(UPDATE_ENDPOINT_FLAG_WIDTH-1 downto 0) := (3 => 1, others => '0');
constant BITMAP_BLOCK_SIZE : natural := 16;
--*****TYPE DECLARATION*****
-- FSM states. Explained below in detail
type STAGE_TYPE is (IDLE, TODO);
-- Memory FSM Opcodes
type MEM_OPCODE_TYPE is (NOP, TODO);
type MEM_STAGE_TYPE is (IDLE, TODO);
-- Record of all Participant Data stored in memory
type ENDPOINT_DATA_TYPE is record
addr : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0);
portn : std_logic_vector(UDP_PORT_WIDTH-1 downto 0);
expects_inline_qos : std_logic;
lease_deadline : TIME_TYPE;
res_time : TIME_TYPE;
next_seq_nr : SEQUENCENUMBER_TYPE;
end record;
constant ZERO_ENDPOINT_DATA : ENDPOINT_DATA_TYPE := (
addr => IPv4_ADDRESS_INVALID,
portn => UDP_PORT_INVALID,
expects_inline_qos => '0',
lease_deadline => TIME_INVALID,
res_time => TIME_INVALID,
next_seq_nr => SEQUENCENUMBER_UNKNOWN
);
type MEM_CTRL_DATA_TYPE is record
guid : GUID_TYPE;
addr : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0);
portn : std_logic_vector(UDP_PORT_WIDTH-1 downto 0);
expects_inline_qos : std_logic;
deadline : TIME_TYPE;
next_seq_nr : SEQUENCENUMBER_TYPE;
update_flags : std_logic_vector(UPDATE_ENDPOINT_FLAG_WIDTH-1 downto 0);
mem_opcode : MEM_OPCODE_TYPE;
end record;
constant ZERO_MEM_CTRL_DATA : MEM_CTRL_DATA_TYPE := (
guid => (others => (others => '0')),
addr => (others => '0'),
portn => (others => '0'),
expects_inline_qos => '0',
deadline => TIME_INVALID,
next_seq_nr => SEQUENCENUMBER_UNKNOWN,
update_flags => (others => '0'),
mem_opcode => IDLE
);
--*****SIGNAL DECLARATION*****
-- FSM state
signal stage, stage_next : STAGE_TYPE := IDLE;
signal meta_opcode, meta_opcode_next : std_logic_vector(WORD_WIDTH-1 downto 0) := (others => '0');
signal cnt, cnt_next : natural range TODO := 0;
signal guid, guid_next : GUID_TYPE := (others => (others => '0'));
signal mem_op_done : std_logic := '0';
signal mem_opcode : MEM_OPCODE_TYPE := NOP;
signal mem_op_start : std_logic := '0';
signal addr, addr_next : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0) := (others => '0');
signal portn, portn_next : std_logic_vector(UDP_PORT_WIDTH-1 downto 0) := (others => '0');
signal expects_inline_qos, expects_inline_qos_next : std_logic := '0';
signal is_meta, is_meta_next : std_logic := '0';
signal update_endpoint_flags : std_logic_vector(UPDATE_ENDPOINT_FLAG_WIDTH-1 downto 0) := (others => '0');
signal opcode, opcode_next : std_logic_vector(SUBMESSAGE_ID_WIDTH-1 downto 0) := (others => '0');
signal flags, flags_next : std_logic_vector(SUBMESSAGE_FLAGS_WIDTH-1 downto 0) := (others => '0');
signal seq_nr, seq_nr_next : SEQUENCENUMBER_TYPE := SEQUENCENUMBER_UNKNOWN;
signal next_seq_nr, next_seq_nr_next : SEQUENCENUMBER_TYPE := SEQUENCENUMBER_UNKNOWN;
signal ts, ts_next : TIME_TYPE := TIME_INVALID;
signal deadline, deadline_next : DURATION_TYPE := DURATION_ZERO;
signal sn_latch_1, sn_latch_1_next : SEQUENCENUMBER_TYPE := SEQUENCENUMBER_UNKNOWN;
signal sn_latch_2, sn_latch_2_next : SEQUENCENUMBER_TYPE := SEQUENCENUMBER_UNKNOWN;
signal sn_latch_3, sn_latch_3_next : SEQUENCENUMBER_TYPE := SEQUENCENUMBER_UNKNOWN;
signal bitmap_cnt, bitmap_cnt_next : unsigned(log2c(MAX_BITMAP_WIDTH/CDR_LONG_WIDTH)-1 downto 0) := (others => '0');
signal bitmap_latch, bitmap_latch_next : BITMAP_TYPE := (others => (others => '0'));
signal cnt2, cnt2_next : natural range 0 to BITMAP_TYPE'length := 0;
signal bitmap_pos, bitmap_pos_next : natural range 0 to MAX_BITMAP_WIDTH-1 := 0;
signal key_hash, key_hash_next : KEY_HASH_TYPE := (others => (others => '0'));
signal key_hash_rcvd, key_hash_rcvd_next : std_logic := '0';
signal status_info, status_info_next : std_logic_vector(STATUS_INFO_WIDTH-1 downto 0) := (others => '0');
signal last_word_in_latch, last_word_in_latch_next : std_logic := '0';
-- Signal used to reset the word counter
signal reset_read_cnt : std_logic;
-- Word (4-Byte) counter (Counts words read from input fifo)
signal read_cnt : unsigned(SUBMESSAGE_LENGTH_WIDTH-3 downto 0) := (others => '0');
-- Word aligned End of Parameter
signal parameter_end, parameter_end_next : unsigned(PARAMETER_LENGTH_WIDTH-1 downto 0) := (others => '0');
signal data_in_swapped : std_logic_vector(WORD_WIDTH-1 downto 0) := (others => '0');
signal mem_stage, mem_stage_next : MEM_STAGE_TYPE := IDLE;
signal mem_wr, mem_rd : std_logic := '0';
signal mem_wr_data, mem_rd_data : std_logic_vector(WORD_WIDTH-1 downto 0) := (others => '0');
signal mem_addr, mem_addr_next : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := (others => '0');
signal mem_addr_base, mem_addr_base_next : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := (others => '0');
signal addr_res, addr_res_next : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := (others => '0');
signal last_addr, last_addr_next : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := (others => '0');
signal max_endpoint_addr, max_endpoint_addr_next : unsigned(ENDPOINT_MEMORY_WIDTH-1 downto 0) := (others => '0');
signal mem_cnt, mem_cnt_next : natural range TODO := 0;
signal mem_endpoint_data, mem_endpoint_data_next : ENDPOINT_DATA_TYPE := ZERO_ENDPOINT_DATA;
signal reset_max_pointer, reset_max_pointer_next : std_logic := '0';
signal mem_long_latch, mem_long_latch_next : std_logic_vector(CDR_LONG_WIDTH-1 downto 0) := (others => '0');
signal mem_ctrl_data, mem_ctrl_data_next : MEM_CTRL_DATA_TYPE := ZERO_MEM_CTRL_DATA;
signal stale_check, stale_check_next : std_logic := '0';
signal count, count_next : unsigned(COUNT_WIDTH-1 downto 0) := (others => '0');
signal return_stage, return_stage_next : STAGE_TYPE := IDLE;
--*****ALIAS DECLARATION*****
-- ENDPOINT FRAME HEADER
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);
-- RTPS PARAMETER LIST HEADER
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 must_understand : std_logic is parameter_id(14);
-- RTPS DATA PAYLOAD HEADER
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 qos_flag : std_logic is flags(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 flags(4);
-- HEARTBEAT
alias first_seq_nr : SEQUENCENUMBER_TYPE is sn_latch_1;
alias first_seq_nr_next : SEQUENCENUMBER_TYPE is sn_latch_1_next;
alias last_seq_nr : SEQUENCENUMBER_TYPE is sn_latch_2;
alias last_seq_nr_next : SEQUENCENUMBER_TYPE is sn_latch_2_next;
-- GAP
alias gap_start : SEQUENCENUMBER_TYPE is sn_latch_1;
alias gap_start_next : SEQUENCENUMBER_TYPE is sn_latch_1_next;
alias gap_list_base : SEQUENCENUMBER_TYPE is sn_latch_2;
alias gap_list_base_next : SEQUENCENUMBER_TYPE is sn_latch_2_next;
alias gap_list_end : SEQUENCENUMBER_TYPE is sn_latch_3;
alias gap_list_end_next : SEQUENCENUMBER_TYPE is sn_latch_3_next;
-- FUNCTION DECLARATION
function to_slv_bitmap (input : BITMAP_TYPE) return std_logic_vector is
variable ret : std_logic_vector(0 to MAX_BITMAP_WIDTH-1) := (others => '0');
begin
for i in 0 to BITMAP_TYPE'length-1 loop
ret(i*WORD_WIDTH to ((i+1)*WORD_WIDTH)-1) := input(i);
end loop;
return ret;
end function;
begin
--*****COMPONENT INSTANTIATION*****
ram_inst : single_port_ram
generic map (
ADDR_WIDTH => ENDPOINT_MEMORY_WIDTH,
DATA_WIDTH => WORD_WIDTH,
MEMORY_DEPTH => ENDPOINT_MEMORY_SIZE
)
port map (
clk => clk,
addr => std_logic_vector(end_mem_addr),
wen => mem_wr,
ren => mem_rd,
wr_data => mem_wr_data,
rd_data => mem_rd_data
);
-- Big Endian Representation
data_in_swapped <= endian_swap(endian_flag, data_in);
parse_prc : process(all)
variable tmp_dw : DOUBLE_WORD_ARRAY := (others => (others => '0'));
-- NOTE: We convert the bitamp to a slv to make operations easier (The tool should handle both cases equally)
variable tmp_bitmap : std_logic_vector(0 to MAX_BITMAP_WIDTH-1) := (others => '0');
variable rd_guard : std_logic := '0';
begin
-- DEFAULT
stage_next <= stage;
meta_opcode_next <= meta_opcode;
cnt_next <= cnt;
guid_next <= guid;
addr_next <= addr;
portn_next <= portn;
expects_inline_qos_next <= expects_inline_qos;
is_meta_next <= is_meta;
opcode_next <= opcode;
flags_next <= flags;
seq_nr_next <= seq_nr;
ts_next <= ts;
deadline_next <= deadline;
sn_latch_1_next <= sn_latch_1;
sn_latch_2_next <= sn_latch_2;
sn_latch_3_next <= sn_latch_3;
bitmap_pos_next <= bitmap_pos;
key_hash_rcvd_next <= key_hash_rcvd;
key_hash_next <= key_hash;
status_info_next <= status_info;
last_word_in_latch_next <= last_word_in_latch;
stale_check_next <= stale_check;
count_next <= count;
return_stage_next <= return_stage;
meta_rd <= '0';
rd <= '0';
mem_opcode <= NOP;
mem_op_start <= '0';
rd_guard := '0';
update_endpoint_flags <= (others => '0');
hc_start <= '0';
hc_opcode <= ADD_CACHE_CHANGE;
hc_data_in <= (others => '0');
hc_ready_out <= '0';
hc_valid_in <= '0';
hc_last_word_in <= '0';
-- Last Word Latch Setter
if (last_word_in = '1') then
last_word_in_latch_next <= '1';
end if;
case (meta_stage) is
-- OPCODE
when IDLE =>
-- RESET
deadline_next <= TIME_INVALID;
addr_next <= (others => '0');
portn_next <= (others => '0');
status_info_next <= (others => '0');
key_hash_rcvd_next <= '0';
expects_inline_qos_next <= '0';
is_meta_next <= '0';
-- Stale Entry search is initiated between every packet processing and when there is no packet to process
if (stale_check = '0' or (empty = '1' and meta_empty = '1')) then
if (mem_op_done = '1') then
mem_opcode <= FIND_STALE_ENDPOINT;
mem_op_start <= '1';
-- Stale Check Toggle (Setter)
stale_check_next <= '1';
stage_next <= ENDPOINT_STALE_CHECK;
end if;
-- Input FIFO Guard
elsif (meta_empty = '0') then
meta_rd <= '1';
-- Stale Check Toggle (Resetter)
stale_check_next <= '0';
-- Mark as METATRAFFIC
is_meta_next <= '1';
-- Latch Endpoint Metatraffic Opcode
meta_opcode_next <= meta_data_in;
stage_next <= LATCH_GUIDPREFIX;
cnt_next <= 0;
-- Input FIFO Guard
elsif (empty = '0') then
rd_guard := '1';
-- Stale Check Toggle (Resetter)
stale_check_next <= '0';
-- Latch Opcode
opcode_next <= header_opcode;
-- Latch Flags
flags_next <= header_flags;
-- Latch Source UDP Port
portn_next <= header_udp_port;
stage_next <= LATCH_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 LATCH_GUIDPREFIX =>
-- Input FIFO Guard
if ((is_meta = '1' and meta_empty = '0') or (is_meta = '0' and empty = '0')) then
if (is_meta = '1') then
meta_rd <= '1';
else
rd_guard := '1';
end if;
cnt_next <= cnt + 1;
case (cnt) is
-- GUID Prefix 1/3
when 0 =>
if (is_meta = '1') then
guid_next(0) <= meta_data_in;
else
guid_next(0) <= data_in;
end if;
-- GUID Prefix 2/3
when 1 =>
if (is_meta = '1') then
guid_next(1) <= meta_data_in;
else
guid_next(1) <= data_in;
end if;
-- GUID Prefix 3/3
when 2 =>
if (is_meta = '1') then
guid_next(2) <= meta_data_in;
else
guid_next(2) <= data_in;
end if;
if (is_meta = '1' and (meta_opcode = OPCODE_PARTICIPANT_UNMATCH or meta_opcode = OPCODE_LIVELINESS_UPDATE)) then
-- DONE Parsing
stage_next <= METATRAFFIC_OPERATION;
else
stage_next <= LATCH_ENTITYID;
end if;
end case;
end if;
when LATCH_ENTITYID =>
-- Input FIFO Guard
if ((is_meta = '1' and meta_empty = '0') or (is_meta = '0' and empty = '0')) then
if (is_meta = '1') then
meta_rd <= '1';
guid_next(3) <= meta_data_in;
if (mem_opcode = OPCODE_ENDPOINT_MATCH) then
stage_next <= LATCH_ENDPOINT_DATA;
cnt_next <= 0;
else
stage_next <= METATRAFFIC_OPERATION;
end if;
-- Memory Operation Guard
else
rd_guard := '1';
guid_next(3) <= data_in;
stage_next <= INITIATE_ENDPOINT_SEARCH;
end if;
end if;
when LATCH_ENDPOINT_DATA =>
-- Input FIFO Guard
if (meta_empty = '0') then
meta_rd <= '1';
cnt_next <= cnt + 1;
case (cnt) is
-- IPv4 Address
when 0 =>
addr_next <= meta_data_in;
-- UDP Port & EXTRA Flags (Expects InLine QoS)
when 1 =>
portn_next <= meta_data_in(WORD_WIDTH-1 downto WORD_WIDTH-UDP_PORT_WIDTH-1);
expects_inline_qos_next <= meta_data_in(0);
-- Calculate Lease Deadline
deadline_next <= time + ENDPOINT_LEASE_DURATION(ID);
stage_next <= METATRAFFIC_OPERATION;
when others =>
null;
end case;
end if;
when METATRAFFIC_OPERATION =>
-- Memory Operation Guard
if (mem_op_done = '1') then
case (meta_opcode) is
when OPCODE_ENDPOINT_MATCH =>
-- Insert Matched Remote Endpoint
-- NOTE: The Lease Duration is NOT updated in case of an update. That is the responsibility of the Liveliness Update
update_endpoint_flags <= ENDPOINT_DATA_FLAG; -- In case the Endpoint is already in the memory, we update the data
mem_opcode <= INSERT_ENDPOINT;
mem_op_start <= '1';
stage_next <= IDLE;
when OPCODE_ENDPOINT_UNMATCH =>
-- Remove Unmatched Remote Endpoint
mem_opcode <= REMOVE_ENDPOINT;
mem_op_start <= '1';
stage_next <= IDLE;
when OPCODE_PARTICIPANT_UNMATCH =>
-- Remove All Endpoint of Remote Participant
mem_opcode <= REMOVE_PARTICIPANT;
mem_op_start <= '1';
stage_next <= IDLE;
when OPCODE_LIVELINESS_UPDATE =>
-- Renew Lease of Remote Endpoint
update_endpoint_flags <= LEASE_DEADLINE_FLAG;
mem_opcode <= UDPATE_ENDPOINT;
mem_op_start <= '1';
stage_next <= IDLE;
when others =>
assert FALSE report "Uknown metatraffic endpoint frame opcode." severity FAILURE;
null;
end case;
-- DONE
stage_next <= SKIP_PACKET;
end if;
when LATCH_SRC_ADDR =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
-- Latch Source IP Address
addr_next <= data_in;
stage_next <= LATCH_GUIDPREFIX;
cnt_next <= 0;
end if;
when INITIATE_ENDPOINT_SEARCH =>
-- Memory Operation Guard
if (mem_op_done = '1') then
mem_op_start <= '1';
mem_opcode <= SEARCH_ENDPOINT;
case (opcode) is
when SID_DATA =>
stage_next <= LATCH_EXTRA_DATA;
cnt_next <= 0;
when SID_HEARTBEAT =>
stage_next <= LATCH_HEARTBEAT;
cnt_next <= 0;
when SID_GAP =>
stage_next <= LATCH_GAP;
cnt_next <= 0;
when others =>
stage_next <= SKIP_PACKET;
end case;
end if;
when LATCH_EXTRA_DATA =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
cnt_next <= cnt + 1;
case (cnt) is
-- Sequence Number 1/2
when 0 =>
seq_nr_next(0) <= unsigned(data_in);
-- Sequence Number 2/2
when 1 =>
seq_nr_next(1) <= unsigned(data_in);
-- Store Next Sequence Number
tmp_dw := (0 => seq_nr(0), 1 => unsigned(data_in_swapped));
next_seq_nr_next <= tmp_dw + 1;
-- Timestamp 1/2
when 2 =>
ts_next(0) <= unsigned(data_in);
-- Timestamp 2/2
when 3 =>
ts_next(1) <= unsigned(data_in);
if (qos_flag = '1') then
stage_next <= PROCESS_INLINE_QOS;
else
stage_next <= INITIATE_HISTORY_CACHE_REQUEST;
end if;
when others =>
null;
end case;
end if;
when LATCH_HEARTBEAT =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
cnt_next <= cnt + 1;
case (cnt) is
-- First Sequence Number 1/2
when 0 =>
first_seq_nr_next(0) <= unsigned(data_in);
-- First Sequence Number 2/2
when 1 =>
first_seq_nr_next(1) <= unsigned(data_in);
-- Last Sequence Number 1/2
when 2 =>
last_seq_nr_next(0) <= unsigned(data_in);
-- Last Sequence Number 2/2
when 3 =>
last_seq_nr_next(1) <= unsigned(data_in);
stage_next <= PROCESS_HEARTBEAT;
when others =>
null;
end case;
end if;
when PROCESS_HEARTBEAT =>
-- Wait for Endpoint Search to finish
if (mem_op_done = '1') then
-- DEFAULT
stage_next <= SKIP_PACKET;
-- Endpoint in Buffer
if (addr_res /= MAX_ADDRESS) then
-- No scheduled Heartbeat Response
if (mem_endpoint_data.res_time = 0) then
-- If current Sequence Number obsolete (removed from source history cache)
if (first_seq_nr > mem_endpoint_data.next_seq_nr and first_seq_nr <= last_seq_nr) then
-- Store new expected Sequence Number and set Response Dealy
next_seq_nr_next <= first_seq_nr;
mem_op_start <= '1';
mem_opcode <= UPDATE_ENDPOINT;
-- XXX: Assumes lease deadline will not be updated concurrently (Since they use the same latch)
deadline_next <= time + ENDPOINT_HEARTBEAT_RESPONSE_DELAY(ID);
-- NOTE: Last Bit denotes if this is Response or Suppression Delay
deadline_next(1)(0) <= '0';
update_endpoint_flags <= NEXT_SEQ_NR_FLAG or RES_TIME_FLAG;
-- If new Sequence Number is available or Writer expects ACKNACK
elsif (last_seq_nr >= mem_endpoint_data.next_seq_nr or final_flag = '0') then
-- Set Response Delay
mem_opcode <= UPDATE_ENDPOINT;
mem_op_start <= '1';
deadline_next <= time + ENDPOINT_HEARTBEAT_RESPONSE_DELAY(ID);
-- NOTE: Last Bit denotes if this is Response or Suppression Delay
deadline_next(1)(0) <= '0';
update_endpoint_flags <= RES_TIME_FLAG;
end if;
-- Currently in Heartbeat Response Delay
elsif (mem_participant_data.res_time(1)(0) = '0') then
-- If current Sequence Number obsolete (removed from source history cache)
if (first_seq_nr > mem_endpoint_data.next_seq_nr and first_seq_nr <= last_seq_nr) then
-- Store new expected Sequence Number
next_seq_nr_next <= first_seq_nr;
mem_op_start <= '1';
mem_opcode <= UPDATE_ENDPOINT;
update_endpoint_flags <= NEXT_SEQ_NR_FLAG;
end if;
end if;
end if;
end if;
when LATCH_GAP =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
cnt_next <= cnt + 1;
case (cnt) is
-- GapStart Sequence Number 1/2
when 0 =>
gap_start_next(0) <= unsigned(data_in);
-- GapStart Sequence Number 2/2
when 1 =>
gap_start_next(1) <= unsigned(data_in);
-- GapList.Base 1/2
when 2 =>
gap_list_base_next(0) <= unsigned(data_in);
-- GapList.Base 2/2
when 3 =>
gap_list_base_next(1) <= unsigned(data_in);
-- ReaderSNState.NumBits
when 4 =>
gap_list_end_next <= gap_list_base + to_integer(unsigned(data_in));
bitmap_cnt_next <= unsigned(round_slv(data_in(log2c(MAX_BITMAP_WIDTH)-1 downto 0),bitmap_cnt'length));
cnt2_next <= 0;
-- ReaderSNState.Bitmap
when 5 =>
-- Read Bitmap
if (cnt2 < bitmap_cnt) then
cnt2_next <= cnt2 + 1;
bitmap_latch_next(cnt2) <= data_in_swapped;
-- Keep Sub-State
cnt_next <= cnt;
else
stage_next <= PROCESS_GAP;
end if;
when others =>
null;
end case;
end if;
when PROCESS_GAP =>
-- Wait for Endpoint Search
if (mem_op_done = '1') then
-- DEFAULT
stage_next <= SKIP_PACKET;
-- Known Remote Endpoint
if (addr_res /= MAX_ADDRESS) then
-- GAP is relevant
if (gap_start <= mem_endpoint_data.next_seq_nr and mem_endpoint_data.next_seq_nr <= gap_list_end) then
-- Next Expected is in GAP List
if (gap_list_base <= mem_endpoint_data.next_seq_nr) then
-- Begin searching next valid SN from the current expected in the bitmap list
bitmap_pos_next <= to_integer(next_seq_nr - gap_list_base);
else
-- Begin searching next valid SN from the beginning of the bitmap list
next_seq_nr_next <= gap_list_base;
bitmap_pos_next <= 0;
end if;
stage_next <= FIND_NEXT_VALID_IN_BITMAP;
end if;
end if;
end if;
when FIND_NEXT_VALID_IN_BITMAP =>
-- Memory Operation Guard
if (mem_op_done = '1') then
tmp_bitmap := to_slv_bitmap(bitmap_latch);
-- TODO: Test GAP that has next expected not marked in GAP
-- First valid sequence number found
if (tmp_bitmap(bitmap_pos) = '0') then
-- Update next sequence number
mem_op_start <= '1';
mem_opcode <= UPDATE_ENDPOINT;
update_endpoint_flags <= NEXT_SEQ_NR_FLAG;
-- DONE
stage_next <= SKIP_PACKET;
else
-- Continue search
bitmap_pos_next <= bitmap_pos + 1;
next_seq_nr_next <= next_seq_nr + 1;
end if;
end if;
when PROCESS_INLINE_QOS =>
-- input FIFO Guard
if (empty = '0') then
rd_guard := '1';
-- Reset Word Counter
reset_read_cnt <= '1';
-- Latch Parameter End (In order to skip parameters)
parameter_end_next <= unsigned(endian_swap(endian_flag,parameter_length));
-- DEFAULT STAGE
stage_next <= SKIP_PARAMETER;
case (parameter_id) is
when PID_TOPIC_NAME =>
-- Ignore
null;
when PID_DURABILITY =>
-- Ignore
null;
when PID_PRESENTATION =>
-- Ignore
null;
when PID_DEADLINE =>
-- Ignore
null;
when PID_LATENCY_BUDGET =>
-- Ignore
null;
when PID_OWNERSHIP =>
-- Ignore
null;
when PID_OWNERSHIP_STRENGTH =>
-- Ignore
null;
when PID_LIVELINESS =>
-- Ignore
null;
when PID_PARTITION =>
-- Ignore
null;
when PID_RELIABILITY =>
-- Ignore
null;
when PID_DESTINATION_ORDER =>
-- Ignore
null;
when PID_TRANSPORT_PRIORITY =>
-- Ignore
null;
when PID_LIFESPAN =>
stage_next <= LATCH_LIFESPAN;
cnt_next <= 0;
when PID_CONTENT_FILTER_INFO =>
-- Ignore
null;
when PID_COHERENT_SET =>
-- Ignore
null;
when PID_DIRECTED_WRITE =>
-- Ignore
null;
when PID_ORIGINAL_WRITER_INFO =>
-- Ignore
null;
when PID_GROUP_COHERENT_SET =>
-- Ignore
null;
when PID_GROUP_SEQ_NUM =>
-- Ignore
null;
when PID_WRITER_GROUP_INFO =>
-- Ignore
null;
when PID_SECURE_WRITER_GROUP_INFO =>
-- Ignore
null;
when PID_KEY_HASH =>
key_hash_rcvd_next <= '1';
stage_next <= LATCH_KEY_HASH;
cnt_next <= 0;
when PID_STATUS_INFO =>
stage_next <= LATCH_STATUS_INFO;
when PID_PAD =>
-- Ignore
null;
when PID_SENTINEL =>
-- QOS DONE
stage_next <= INITIATE_HISTORY_CACHE_REQUEST;
when others =>
-- If MUST_UNDERSTAND Flag is set, we have incompatible communication. Drop Packet
if (must_understand = '1') then
stage_next <= SKIP_PACKET;
-- Else skip Unknown Parameter
else
stage_next <= SKIP_PARAMETER;
end if;
end case;
end if;
when LATCH_LIFESPAN =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
cnt_next <= cnt + 1;
case (cnt) is
-- Lifespan 1/2
when 0 =>
deadline_next(0) <= unsigned(data_in_swapped);
-- Lifespan 2/2
when 1 =>
deadline_next(1) <= unsigned(data_in_swapped);
-- DONE
stage_next <= SKIP_PARAMETER;
when others =>
null;
end case;
end if;
when LATCH_KEY_HASH =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
cnt_next <= cnt + 1;
case (cnt) is
-- Key Hash 1/4
when 0 =>
key_hash_next(0) <= data_in;
-- Key Hash 2/4
when 1 =>
key_hash_next(1) <= data_in;
-- Key Hash 3/4
when 2 =>
key_hash_next(2) <= data_in;
-- Key Hash 4/4
when 3 =>
key_hash_next(3) <= data_in;
-- DONE
stage_next <= SKIP_PARAMETER;
when others =>
null;
end case;
end if;
when LATCH_STATUS_INFO =>
-- Input FIFO Guard
if (empty = '0') then
rd_guard := '1';
status_info_next <= data_in;
-- DONE
stage_next <= SKIP_PARAMETER;
end if;
when INITIATE_HISTORY_CACHE_REQUEST =>
hc_start <= '1';
hc_opcode <= ADD_CACHE_CHANGE;
-- Wait until History Cache acknowledges request
if (hc_ack = '1') then
stage_next <= ADD_CACHE_CHANGE;
cnt_next <= 0;
end if;
when ADD_CACHE_CHANGE =>
if (hc_ready_in = '1') then
hc_valid_in <= '1';
cnt_next <= cnt + 1;
case (cnt) is
-- Status Info
when 0 =>
hc_data_in <= status_info;
hc_data_in(KEY_HASH_FLAG) <= key_hash_rcvd;
hc_data_in(PAYLOAD_FLAG) <= data_flag;
-- Key hash 1/4
when 1 =>
hc_data_in <= key_hash(0);
-- Key Hash 2/4
when 2 =>
hc_data_in <= key_hash(1);
-- Key Hash 3/4
when 3 =>
hc_data_in <= key_hash(2);
-- Key hash 4/4
when 4 =>
hc_data_in <= key_hash(3);
-- Entity ID
when 5 =>
hc_data_in <= guid(3);
-- GUID Prefix 1/3
when 6 =>
hc_data_in <= guid(0);
-- GUID Prefix 2/3
when 7 =>
hc_data_in <= guid(1);
-- GUID Prefix 3/3
when 8 =>
hc_data_in <= guid(2);
-- Sequence Number 1/2
when 9 =>
hc_data_in <= seq_nr(0);
-- Sequence Number 2/2
when 10 =>
hc_data_in <= seq_nr(1);
-- Timestamp 1/2
when 11 =>
hc_data_in <= ts(0);
-- Timestamp 2/2
when 12 =>
hc_data_in <= ts(1);
-- Lifespan Deadline 1/2
when 13 =>
hc_data_in <= deadline(0);
-- Lifespan Deadline 2/2
when 14 =>
hc_data_in <= deadline(1);
-- Payload exists
if (data_flag = '1' or key_flag = '1') then
stage_next <= PUSH_PAYLOAD;
else
-- DONE
hc_last_word_in <= '1';
-- Operation Sucessfull
if (hc_ack = '1') then
stage_next <= FINALIZE_HISTORY_CACHE_REQUEST;
else
-- Operation Failed, Skip
stage_next <= SKIP_PACKET;
end if;
end if;
when others =>
null;
end case;
end if;
when PUSH_PAYLOAD =>
-- Input/Output Guard
if (empty = '0' and hc_ready_in = '1') then
rd_guard := '1';
hc_valid_in <= '1';
-- Push Payload to History Cache
hc_data_in <= data_in;
-- Exit Condition
if (last_word_in = '1') then
hc_last_word_in <= '1';
-- XXX: Possible worst case path (The hc_ack is set from last_word signal -> round trip delay between entities)
-- Change Add Sucessfull
if (hc_ack = '1') then
-- Update Endpoint Data
stage_next <= FINALIZE_HISTORY_CACHE_REQUEST;
else
-- Operation Failed, Skip
stage_next <= SKIP_PACKET;
end if;
end if;
end if;
when FINALIZE_HISTORY_CACHE_REQUEST =>
-- Memory Operation Guard
if (mem_op_done = '1') then
-- Update next sequence number and renew Lease
mem_op_start <= '1';
mem_opcode <= UPDATE_ENDPOINT;
deadline_next <= time + ENDPOINT_LEASE_DURATION(ID);
update_endpoint_flags <= NEXT_SEQ_NR_FLAG or LEASE_DEADLINE_FLAG;
-- DONE
stage_next <= SKIP_PACKET;
end if;
when ENDPOINT_STALE_CHECK =>
-- Wait for Stale Search to finish
if (mem_op_done = '1') then
-- Found Stale Entry
if (addr_res /= MAX_ADDRESS) then
-- Endpoint Lease Expired
-- NOTE: The mem_endpoint_data is zero initialized on lease expiration, so we check if the address is set
if (mem_endpoint_data.addr = IPv4_ADDRESS_INVALID) then
-- Remove Participant
mem_opcode <= REMOVE_ENDPOINT;
mem_op_start <= '1';
stage_next <= IDLE;
-- Response Time Reached
else
-- If Suppression Delay passed, zero the time
if(mem_endpoint_data.res_time(1)(0) = '1') then
-- Zero Response Time
mem_opcode <= UPDATE_ENDPOINT;
deadline_next <= TIME_ZERO;
update_participant_flags <= RES_TIME_FLAG;
mem_op_start <= '1';
-- DONE
stage_next <= IDLE;
-- If Response Delay Passed
else
-- Set Heartbeat Suppression Time
if (ENDPOINT_HEARTBEAT_SUPPRESSION_DELAY(ID) /= 0) then
-- Set Heartbeat Suppression Time
deadline_next <= time + ENDPOINT_HEARTBEAT_SUPPRESSION_DELAY(ID);
-- NOTE: Last Bit denotes if this is Response or Suppression Delay
deadline_next(1)(0) <= '1';
else
-- Zero Heartbeat Response Time
deadline_next <= TIME_ZERO;
end if;
mem_opcode <= UPDATE_PARTICIPANT;
update_participant_flags_next <= RES_TIME_FLAG;
mem_op_start <= '1';
-- Send ACKNACK
-- Increment Heartbeat/Acknack Counter
count_next <= count + 1;
stage_next <= SEND_HEADER;
return_stage_next <= SEND_ACKNACK;
cnt_next <= 0;
end if;
end if;
-- No Stale Entry Found
else
-- DONE
stage_next <= IDLE;
end if;
end if;
when SEND_HEADER =>
if (rtps_full = '0') then
wr_sig <= '1';
cnt_next <= cnt + 1;
case (cnt) is
-- OUTPUT HEADER
-- Src IPv4 Address
when 0 =>
data_out <= DEFAULT_IPv4_ADDRESS;
-- Dest IPv4 Address
when 1 =>
data_out <= mem_endpoint_data.addr;
-- Src and Dest UDPv4 Ports
when 2 =>
data_out <= USER_IPv4_UNICAST_PORT & mem_participant_data.meta_port;
-- RTPS MESSAGE HEADER
when 3 =>
data_out <= PROTOCOL_RTPS;
when 4 =>
data_out <= PROTOCOLVERSION_2_4 & VENDORID;
when 5 =>
data_out <= GUIDPREFIX(0);
when 6 =>
data_out <= GUIDPREFIX(1);
when 7 =>
data_out <= GUIDPREFIX(2);
-- Continue with respective RTPS Submessage
stage_next <= return_stage;
cnt_next <= 0;
when others =>
null;
end case;
end if;
when SEND_ACKNACK =>
if (rtps_full = '0') then
wr_sig <= '1';
cnt_next <= cnt + 1;
case (cnt) is
-- ACKNACK RTPS SUBMESSAGE
-- RTPS Submessage Header
when 0 =>
data_out <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(28, SUBMESSAGE_LENGTH_WIDTH));
-- Reader Entity ID
when 1 =>
data_out <= ENTITYID(ID);
-- Writer Entity ID
when 2 =>
data_out <= ENTITYID_UNKNOWN;
-- Sequence Number Set (Bitmap Base 1/2)
when 3 =>
data_out <= std_logic_vector(mem_endpoint_data.next_seq_nr(0));
-- Sequence Number Set (Bitmap Base 2/2)
when 4 =>
data_out <= std_logic_vector(mem_endpoint_data.next_seq_nr(1));
-- Sequence Number Set (NumBits)
when 5 =>
data_out <= std_logic_vector(to_unsigned(CDR_LONG_WIDTH, CDR_LONG_WIDTH));
-- Sequence Number Set (Bitmap)
when 6 =>
-- NOTE: In order to avoid having to generate a variable sized bitmap, we always request the next 32 sequence numbers, even if they do not exist (yet)
-- XXX: Assumes correct implementation of the RTPS Protocol (i.e. Writer ignores requested SNs that do not exist)
data_out <= (others => '1');
-- Count
when 7 =>
data_out <= std_logic_vector(count);
last_word_out <= '1';
-- DONE
stage_next <= IDLE;
when others =>
null;
end case;
end if;
when SKIP_PARAMETER =>
-- Consumed last word of Packet
if (last_word_in_latch = '1' and last_word_in = '0') then
-- Reset Last Word In Latch
last_word_in_latch_next <= '0';
-- Continue parsing next Packet
stage_next <= IDLE;
-- Reset Parameter End
parameter_end_next <= (others => '1');
-- End of Parameter
elsif ((read_cnt & "00" ) >= parameter_end) then
-- Parse Next Parameter
-- NOTE: data_in is already showing the next parameter
stage_next <= PROCESS_PL;
-- Reset Parameter End
parameter_end_next <= (others => '1');
-- Input FIFO Guard
elsif (empty = '0') then
-- Skip-Read
rd_guard := '1';
end if;
when SKIP_PACKET =>
-- NOTE: At the end of a Stale Entry Removal this stage is entered, without having started reading a Packet from input.
-- Reset Parameter End
parameter_end_next <= (others => '1');
-- Stale Check Exit
if (stale_check = '1') then
-- DONE
stage_next <= IDLE;
-- Consumed last word of Packet
elsif (last_word_in_latch = '1' and last_word_in = '0') then
-- Reset Last Word In Latch
last_word_in_latch_next <= '0';
-- DONE
stage_next <= IDLE;
-- Input FIFO Guard
elsif (empty = '0') then
-- Skip-Read
rd_guard := '1';
end if;
when others =>
null;
end case;
-- OVERREAD GUARD
-- Read outside of packet Length
-- NOTE: If the Packet Length is smaller than expected there will be a read from input FIFO while
-- the Packet Length has been reached and will be caught by this clause.
-- The SKIP_PACKET clause prevents a read signal from occuring in this situation, and thus prevents from entering this state.
if ((last_word_in_latch = '1' and last_word_in = '0') and rd_guard = '1') then
-- Force rd_sig low
rd_sig <= '0';
-- Continue parsing next Packet
stage_next <= IDLE;
-- Reset Last Word In Latch
last_word_in_latch_next <= '0';
-- Reset Parameter End
parameter_end_next <= (others => '1');
-- Read outside of Parameter Length
-- NOTE: If the Parameter Length is smaller than expected for a particular parameter, there will be a read from input FIFO while
-- the Parameter Length has been reached and will be caught by this clause.
-- The SKIP_PARAMETER clause prevents a read signal from occuring in this situation, and thus prevents from entering this state.
elsif ((read_cnt & "00") >= parameter_end and rd_guard = '1') then
-- Force rd_sig low
rd_sig <= '0';
-- Invalid Parameter Length, Skip Packet
stage_next <= SKIP_PACKET;
-- Reset Parameter End
parameter_end_next <= (others => '1');
-- DEFAULT
else
rd_sig <= rd_guard;
end if;
end process;
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');
variable tmp_dw : DOUBLE_WORD_ARRAY := (others => (others => '0'));
begin
-- DEFAULT Registered
mem_stage_next <= mem_stage;
mem_addr_base_next <= mem_addr_base;
mem_addr_next <= mem_addr;
addr_res_next <= addr_res;
mem_cnt_next <= mem_cnt;
last_addr_next <= last_addr;
mem_endpoint_data_next <= mem_endpoint_data;
mem_guidprefix_next <= mem_guidprefix;
max_endpoint_addr_next <= max_endpoint_addr;
reset_max_pointer_next <= reset_max_pointer;
mem_long_latch_next <= mem_long_latch;
mem_ctrl_data_next <= mem_ctrl_data;
-- DEFAULT Unregistered
mem_write_data <= (others => '0');
mem_op_done <= '0';
mem_rd <= '0';
mem_wr <= '0';
case (mem_stage) is
when IDLE =>
mem_op_done <= '1';
reset_max_pointer_next <= '0';
if (mem_op_start = '1') then
-- Latch Signals needed for Mermory Operation (Use _next signals, because some signals are set in same clk)
mem_ctrl_data_next <= (
guid => guid_next,
addr => addr_next,
portn => portn_next,
expects_inline_qos => expects_inline_qos_next,
deadline => deadline_next,
next_seq_nr => next_seq_nr_next,
update_flags => update_endpoint_flags_next,
mem_opcode => mem_opcode_next
);
case(mem_opcode) is
when SEARCH_ENDPOINT =>
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
mem_stage_next <= SEARCH_ENDPOINT;
mem_cnt_next <= 0;
when INSERT_ENDPOINT =>
-- NOTE: First check if Endpoint is already inserted. If yes, just update the data.
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
mem_stage_next <= SEARCH_ENDPOINT;
mem_cnt_next <= 0;
when UPDATE_ENDPOINT =>
if ((update_endpoint_flags and ENDPOINT_DATA_FLAG) = ENDPOINT_DATA_FLAG) then
mem_stage_next <= UPDATE_ENDPOINT;
mem_addr_next <= addr_res + 4;
mem_cnt_next <= 0;
elsif ((update_endpoint_flags and LEASE_DEADLINE_FLAG) = LEASE_DEADLINE_FLAG) then
mem_stage_next <= UPDATE_ENDPPOINT;
mem_addr_next <= addr_res + 6;
mem_cnt_next <= 2;
elsif ((update_endpoint_flags and RES_TIME_FLAG) = RES_TIME_FLAG) then
mem_stage_next <= UPDATE_ENDPOINT;
mem_addr_next <= addr_res + 8;
mem_cnt_next <= 4;
elsif ((update_endpoint_flags and NEXT_SEQ_NR_FLAG) = NEXT_SEQ_NR_FLAG) then
mem_stage_next <= UPDATE_ENDPOINT;
mem_addr_next <= addr_res + 10;
mem_cnt_next <= 6;
end if;
when REMOVE_ENDPOINT =>
mem_addr_next <= addr_res;
mem_stage_next <= REMOVE_ENDPOINT;
mem_cnt_next <= 0;
when REMOVE_PARTICIPANT =>
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
mem_stage_next <= SEARCH_ENDPOINT;
mem_cnt_next <= 0;
when FIND_STALE_ENDPOINT =>
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
mem_stage_next <= FIND_STALE_ENDPOINT;
mem_cnt_next <= 0;
when FIND_FIRST_ENDPOINT =>
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
mem_stage_next <= FIND_NEXT_ENDPOINT;
mem_cnt_next <= 0;
when FIND_NEXT_ENDPOINT =>
-- Memory Bound Guard
if (addr_res /= max_endpoint_addr) then
-- Reached End of Memory, No match
tmp := addr_res + ENDPOINT_FRAME_SIZE;
mem_addr_base_next <= tmp;
mem_addr_next <= tmp;
mem_stage_next <= FIND_NEXT_ENDPOINT;
mem_cnt_next <= 0;
else
addr_res_next <= MAX_ADDRESS;
end if;
when others =>
null;
end case;
end if;
when SEARCH_ENDPOINT =>
mem_rd <= '1';
mem_cnt_next <= mem_cnt + 1;
mem_addr_next <= mem_addr + 1;
-- Next Endpoint Frame Address
tmp := mem_addr_base + ENDPOINT_FRAME_SIZE;
case (mem_cnt) is
-- Preload
when 0 =>
null;
-- Entity ID
when 1 =>
-- No Match (Ignore Entity ID match on Participant Search, but skip empty Slot)
if ((mem_read_data /= guid(3) and mem_ctrl_data.mem_opcode /= REMOVE_PARTICIPANT) or (mem_read_data = ENTITYID_UNKNOWN)) then
-- Reached End of Memory, No Match
if (mem_addr_base = max_endpoint_addr) then
case (mem_ctrl_data.mem_opcode) is
when INSERT_ENDPOINT =>
-- Insert new Endpoint
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
mem_stage_next <= FIND_ENDPOINT_SLOT;
mem_cnt_next <= 0;
when REMOVE_PARTICIPANT =>
-- Reset MAX Endpoint Pointer
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
reset_max_pointer_next <= '1';
last_addr_next <= (others => '0');
mem_stage_next <= FIND_ENDPOINT_SLOT;
mem_cnt_next <= 0;
when others =>
addr_res_next <= MAX_ADDRESS; --No match
-- DONE
mem_stage_next <= IDLE;
end case;
else
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
end if;
-- GUID Prefix 1/3
when 2 =>
-- No Match
if (mem_read_data /= guid(0)) then
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
-- GUID Prefix 2/3
when 3 =>
-- No Match
if (mem_read_data /= guid(1)) then
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
-- GUID Prefix 3/3
when 4 =>
-- No Match
if (mem_read_data /= guid(2)) then
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
-- Match
else
case (mem_ctrl_data.mem_opcode) is
when REMOVE_PARTICIPANT =>
addr_res_next <= mem_addr_base;
-- Remove Endpoint from remote Participant
mem_stage_next <= REMOVE_ENDPOINT;
mem_addr_next <= mem_addr_base;
mem_cnt_next <= 0;
when INSERT_ENDPOINT =>
addr_res_next <= mem_addr_base;
-- Update Endpoint Data
mem_stage_next <= UPDATE_ENDPOINT;
mem_addr_next <= mem_addr_base + 4;
mem_cnt_next <= 0;
when others =>
addr_res_next <= mem_addr_base;
-- Fetch Endpoint Data
mem_stage_next <= GET_ENDPOINT_DATA;
mem_cnt_next <= 1; -- No preload needed
end case;
end if;
when others =>
null;
end case;
when GET_ENDPOINT_DATA =>
mem_rd <= '1';
mem_cnt_next <= mem_cnt + 1;
mem_addr_next <= mem_addr + 1;
case (mem_cnt) is
-- Memory Preload
when 0 =>
null;
-- IPv4 Address
when 1 =>
mem_endpoint_data_next.addr <= mem_read_data;
-- UDPv4 Ports
when 2 =>
mem_endpoint_data_next.portn <= mem_read_data(31 downto 16);
mem_endpoint_data_next.expects_inline_qos <= mem_read_data(0);
-- Lease Deadline 1/2
when 3 =>
mem_endpoint_data_next.lease_deadline(0) <= unsigned(mem_read_data);
-- Lease Deadline 2/2
when 4 =>
mem_endpoint_data_next.lease_deadline(1) <= unsigned(mem_read_data);
-- Response/Suppression Time 1/2
when 5 =>
mem_endpoint_data_next.res_time(0) <= unsigned(mem_read_data);
-- Response/Suppression Time 2/2
when 6 =>
mem_endpoint_data_next.res_time(1) <= unsigned(mem_read_data);
-- Next Sequence Number 1/2
when 7 =>
mem_endpoint_data_next.next_seq_nr(0) <= unsigned(mem_read_data);
-- Next Sequence Number 2/2
when 8 =>
mem_endpoint_data_next.next_seq_nr(1) <= unsigned(mem_read_data);
-- DONE
mem_stage_next <= IDLE;
when others =>
null;
end case;
when INSERT_ENDPOINT =>
mem_wr <= '1';
mem_addr_next <= mem_addr + 1;
mem_cnt_next <= mem_cnt + 1;
case (mem_cnt) is
-- Entity ID
when 0 =>
mem_write_data <= mem_ctrl_data.guid(3);
-- GUID Prefix 1/3
when 1 =>
mem_write_data <= mem_ctrl_data.guid(0);
-- GUID Prefix 2/3
when 2 =>
mem_write_data <= mem_ctrl_data.guid(1);
-- GUID Prefix 3/3
when 3 =>
mem_write_data <= mem_ctrl_data.guid(2);
-- IPv4 Address
when 4 =>
mem_write_data <= mem_ctrl_data.addr;
-- UDPv4 Ports
when 5 =>
mem_write_data <= mem_ctrl_data.portn & (0 => mem_ctrl_data.expects_inline_qos, 16 downto 1 => '0');
-- Lease Deadline 1/2
when 6 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.deadline(0));
-- Lease Deadline 2/2
when 7 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.deadline(1));
-- Response/Suppression Time 1/2
when 8 =>
mem_write_data <= (others => '0');
-- Response/Suppression Time 2/2
when 9 =>
mem_write_data <= (others => '0');
-- Next Sequence Number 1/2
when 10 =>
mem_write_data <= (others => '0');
-- Next Sequence Number 2/2
when 11 =>
mem_write_data <= to_unsigned(1,CDR_LONG_WIDTH);
-- DONE
mem_stage_next <= IDLE;
when others =>
null;
end case;
when UPDATE_ENDPOINT =>
mem_cnt_next <= mem_cnt + 1;
mem_addr_next <= mem_addr + 1;
case (mem_cnt) is
-- IPv4 Address
when 0 =>
mem_write_data <= mem_ctrl_data.addr;
if ((mem_ctrl_data.update_flags and ENDPOINT_DATA_FLAG) = ENDPOINT_DATA_FLAG) then
mem_wr <= '1';
end if;
-- UDPv4 Ports
when 1 =>
mem_write_data <= mem_ctrl_data.portn & (0 => mem_ctrl_data.expects_inline_qos, 16 downto 1 => '0');
if ((mem_ctrl_data.update_flags and ENDPOINT_DATA_FLAG) = ENDPOINT_DATA_FLAG) then
mem_wr <= '1';
end if;
-- If nothing else to update
if ((mem_ctrl_data.update_flags and (LEASE_DEADLINE_FLAG or NEXT_SEQ_NR_FLAG or RES_TIME_FLAG)) = (mem_ctrl_data.update_flags'range => '0')) then
-- DONE
mem_stage_next <= IDLE;
end if;
-- Lease Deadline 1/2
when 2 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.deadline(0));
if ((mem_ctrl_data.update_flags and LEASE_DEADLINE_FLAG) = LEASE_DEADLINE_FLAG) then
mem_wr <= '1';
end if;
-- Lease Deadline 2/2
when 3 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.deadline(1));
if ((mem_ctrl_data.update_flags and LEASE_DEADLINE_FLAG) = LEASE_DEADLINE_FLAG) then
mem_wr <= '1';
end if;
-- If nothing else to update
if ((mem_ctrl_data.update_flags and (NEXT_SEQ_NR_FLAG or RES_TIME_FLAG)) = (mem_ctrl_data.update_flags'range => '0')) then
-- DONE
mem_stage_next <= IDLE;
end if;
-- Response/Suppression Time 1/2
when 4 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.deadline(0));
if ((mem_ctrl_data.update_flags and RES_TIME_FLAG) = RES_TIME_FLAG) then
mem_wr <= '1';
end if;
-- Response/Suppression Time 2/2
when 5 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.deadline(1));
if ((mem_ctrl_data.update_flags and RES_TIME_FLAG) = RES_TIME_FLAG) then
mem_wr <= '1';
end if;
-- If nothing else to update
if ((mem_ctrl_data.update_flags and (NEXT_SEQ_NR_FLAG)) = (mem_ctrl_data.update_flags'range => '0')) then
-- DONE
mem_stage_next <= IDLE;
end if;
-- Next Sequence Number 1/2
when 6 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.next_seq_nr(0));
if ((mem_ctrl_data.update_flags and NEXT_SEQ_NR_FLAG) = NEXT_SEQ_NR_FLAG) then
mem_wr <= '1';
end if;
-- Next Sequence Number 2/2
when 7 =>
mem_write_data <= std_logic_vector(mem_ctrl_data.next_seq_nr(1));
if ((mem_ctrl_data.update_flags and NEXT_SEQ_NR_FLAG) = NEXT_SEQ_NR_FLAG) then
mem_wr <= '1';
end if;
-- DONE
mem_stage_next <= IDLE;
when others =>
null;
end case;
when REMOVE_ENDPOINT =>
-- Mark with ENTITYID_UNKNOWN to mark slot empty
mem_wr <= '1';
mem_write_data <= ENTITYID_UNKNOWN;
if (mem_ctrl_data.mem_opcode = REMOVE_PARTICIPANT) then
-- Continue Participant Match Search
mem_addr_base_next <= addr_res;
mem_addr_next <= addr_res;
mem_stage_next <= SEARCH_ENDPOINT;
mem_cnt_next <= 0;
else
-- Reset MAX Endpoint Pointer
mem_addr_base_next <= FIRST_ENDPOINT_ADDRESS;
mem_addr_next <= FIRST_ENDPOINT_ADDRESS;
reset_max_pointer_next <= '1';
last_addr_next <= (others => '0');
mem_stage_next <= FIND_ENDPOINT_SLOT;
mem_cnt_next <= 0;
end if;
when FIND_ENDPOINT_SLOT =>
mem_rd <= '1';
mem_addr_next <= mem_addr + 1;
mem_cnt_next <= mem_cnt + 1;
-- Next Endpoint Frame Address
tmp := mem_addr_base + ENDPOINT_FRAME_SIZE;
case (mem_cnt) is
-- Preload
when 0 =>
null;
-- Entity ID
when 1 =>
-- Slot Occupied
if (mem_read_data /= ENTITYID_UNKNOWN) then
-- Reached end of Endpoint Memory Area
if (mem_addr_base = max_endpoint_addr) then
-- We are in the middle of resetting the MAX Endpoint Pointer
if (reset_max_pointer = '1') then
-- No Change
mem_stage_next <= IDLE;
-- MEMORY FULL
elsif (max_endpoint_addr = MAX_ENDPOINT_ADDRESS) then
report "Memory Full, Ignoring Endpoint Data" severity NOTE;
-- Ignore Insertion
mem_stage_next <= IDLE;
addr_res_next <= MAX_ADDRESS;
else
-- Extend Endpoint Memory Area
-- NOTE: "max_endpoint_addr" points to the first address of last Endpoint Frame
max_endpoint_addr_next <= tmp;
-- Populate Endpoint Slot
mem_stage_next <= INSERT_ENDPOINT;
mem_addr_next <= tmp;
addr_res_next <= tmp;
mem_cnt_next <= 0;
end if;
else
-- Latch last occupied Endpoint Slot
last_addr_next <= mem_addr_base;
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
-- Slot Empty
else
if (reset_max_pointer = '1') then
-- Make sure to iterate through complete Endpoint Area
if (mem_addr_base = max_endpoint_addr) then
-- Reset Pointer to last occupied Endpoint Slot
max_endpoint_addr_next <= last_addr;
-- DONE
mem_stage_next <= IDLE;
else
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
else
-- Populate Endpoint Slot
mem_stage_next <= INSERT_ENDPOINT;
mem_addr_next <= mem_addr_base;
addr_res_next <= mem_addr_base;
mem_cnt_next <= 0;
end if;
end if;
when others =>
null;
end case;
when FIND_NEXT_ENDPOINT =>
mem_rd <= '1';
mem_cnt_next <= mem_cnt + 1;
mem_addr_next <= mem_addr + 1;
-- Next Endpoint Frame Address
tmp := mem_addr_base + ENDPOINT_FRAME_SIZE;
-- Beginning of Endpoint Data
tmp2 := mem_addr_base + 4;
case (mem_cnt) is
-- Preload
when 0 =>
null;
-- Entity ID
when 1 =>
-- Slot Occupied
if (mem_read_data /= ENTITYID_UNKNOWN) then
-- Get Endpoint Data
mem_addr_next <= tmp2;
addr_res_next <= mem_addr_base;
mem_stage_next <= GET_ENDPOINT_DATA;
mem_cnt_next <= 0;
-- Slot Empty
else
-- Reached End of Memory, No Match
if (mem_addr_base = max_endpoint_addr) then
addr_res_next <= MAX_ADDRESS; --No match
-- DONE
mem_stage_next <= IDLE;
else
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
end if;
when others =>
null;
end case;
when FIND_STALE_ENDPOINT =>
mem_rd <= '1';
mem_cnt_next <= mem_cnt + 1;
mem_addr_next <= mem_addr + 1;
-- Next Endpoint Frame Address
tmp := mem_addr_base + ENDPOINT_FRAME_SIZE;
-- Beginning of Endpoint Data
tmp2 := mem_addr_base + 4;
-- Beginning of Lease Deadline
tmp3 := mem_addr_base + 6;
case (mem_cnt) is
-- Preload
when 0 =>
null;
-- Entity ID
when 1 =>
-- Slot Occupied
if (mem_read_data /= ENTITYID_UNKNOWN) then
-- Jump to Stale Check
mem_addr_next <= tmp3;
mem_cnt_next <= 2;
-- Slot Empty
else
-- Reached End of Memory, No Match
if (mem_addr_base = max_endpoint_addr) then
addr_res_next <= MAX_ADDRESS; --No match
-- DONE
mem_stage_next <= IDLE;
else
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
end if;
-- Preload
when 2 =>
null;
-- Lease Deadline 1/2
when 3 =>
mem_long_latch_next <= mem_read_data;
-- Lease Deadline 2/2
when 4 =>
tmp_dw := (0 => unsigned(mem_long_latch), 1 => unsigned(mem_read_data));
-- Lease Deadline passed
if (tmp_dw < time) then
-- Mark Endpoint as stale
addr_res_next <= mem_addr_base;
mem_endpoint_data_next <= ZERO_ENDPOINT_DATA;
-- DONE
mem_stage_next <= IDLE;
end if;
-- Response/Suppression Time 1/2
when 5 =>
mem_long_latch_next <= mem_read_data;
-- Response/Suppression Time 2/2
when 6 =>
tmp_dw := (0 => unsigned(mem_long_latch), 1 => unsigned(mem_read_data));
-- Response/Suppression Time passed
if (tmp_dw /= 0 and tmp_dw < time) then
-- Mark Endpoint and get Endpoint Data
addr_res_next <= mem_addr_base;
mem_addr_next <= tmp2;
mem_stage_next <= GET_ENDPOINT_DATA;
mem_cnt_next <= 0;
-- Endpoint not Stale
else
-- Reached End of Memory, No Match
if (mem_addr_base = max_endpoint_addr) then
addr_res_next <= MAX_ADDRESS; --No match
-- DONE
mem_stage_next <= IDLE;
else
-- Continue Search
mem_addr_next <= tmp;
mem_addr_base_next <= tmp;
mem_cnt_next <= 0;
end if;
end if;
when others =>
null;
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 <= (others => '0');
-- 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;