1763 lines
87 KiB
VHDL
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;
|