diff --git a/src/TODO.txt b/src/TODO.txt index 40e7703..3a8bd48 100644 --- a/src/TODO.txt +++ b/src/TODO.txt @@ -1,288 +1,104 @@ -RULES 8.4.2 -=========== +* https://github.com/osrf/ros_dds/issues/7 tries to determine the feasibility of an FPGA-only DDS implementation + It was suggested using a 'OpenMS430' soft-microcontroller to run DDS middleware on-top. + - Compare resource utilization and performance with this approach +* DDS FPGA Vendors + - TwinOaks Computing Inc (CoreDX) + - OpenVPX +* Implementation makes unnecessary transitions, that are ignored in later stages. + This was a design decision to simplify complexity of each stage (and probably FMAX), but increases power consumtion. +* Is the Timestamp used by something else except ordering by source? If not, does it have to be "sane"? + 2.2.3.16 + This QoS relies on the sender and receiving applications having their clocks sufficiently synchronized. If this is not the case + and the Service can detect it, the DataReader is allowed to use the reception timestamp instead of the source timestamp in its + computation of the ‘expiration time.’ + 2.2.3.17 + The mechanism to set the source timestamp is middleware dependent. +* Are the Builtin Endpoints part of the DDS Specification or the RTPS specification? + - Both +* ALL Data Fields are sent on each change: + https://community.rti.com/forum-topic/sending-only-fields-have-changed +* OFFERED_INCOMPATIBLE_QOS, REQUESTED_INCOMPATIBLE_QOS? (DDS-1.4, 2.2.4.1 Communication Status) +* Does a RTPS Reader subscribe and receive ALL Instances of a keyed Topic? + i.e. where are the instances differentiated? + S.29 - Topic Kind +* Does a RTPS reader subscribe to more than one Writer (If not using Groups)? + - Yes +* Since only positive Sequence Numbers are valid, why is the type signed? +* Replace also Locator? (In order to prevent storing the Locators in multiple Endpoints) +* Store only one locator? Select the first supported multicast? Check for active ping response? Verify with source address? +* What is the purpose of "manualLivelinessCount" in "SPDPdiscoveredParticipantData"? Since the liveliness is asserted by use of "ParticipantMessageData", what is the purpose of also sending an updated "SPDPdiscoveredParticipantData"? (Duplicates is out of the question, since it is not sent with the actual liveliness assertion) +* What does that mean?: + 2.2.3.19 + If reliability is BEST_EFFORT then the Service is allowed to drop samples. If the reliability is + RELIABLE, the Service will block the DataWriter or discard the sample at the DataReader in order not to lose existing + samples. +* What is now the valid Parameter List length? + According to DDSI-RTPS 9.4.2.11 + The length encodes the number of octets following the length to reach the ID of the next parameter (or the ID of the sentinel). Because every parameterId starts on a 4-byte boundary, the length is always a multiple of four. + According to DDS XTypes 7.4.1.2 + Unlike it is stated in [RTPS] Sub Clause 9.4.2.11 “ParameterList”, the value of the parameter length is the exact length of the serialized member. It does not account for any padding bytes that may follow the serialized member. Padding bytes may be added in order to start the next parameterID at a 4 byte offset relative to the previous parameterID. +* 32-bit Timestamps? Seriously? Ever heard of Y2k38? +* Use generic package with unconstrained arrays (VHDL-2008), and assert bounds/length inside the package. +* Count repository lines + git ls-files | grep vhd | xargs wc -l +* Count Field in Heartbeat/Acknack + The following sentence is quite self-explanatory: + "A counter that is incremented each time a new message is sent. Provides the means to detect + duplicate messages that can result from the presence of redundant communication paths." + But then, in 8.4.15.7 it says: + "So, an implementation should ensure that same logical HEARTBEATs are tagged with the same Count." + Does that mean there are cases were I have to put the same count? What is a logical HEARTBEAT? -GENERAL -------- -* All communications must take place using RTPS Messages -* All implementations must implement the RTPS Message Receiver -* The timing characteristics of all implementations must be tunable -* Implementations must implement the Simple Participant and Endpoint Discovery Protocols -WRITER ------- -* Writers must not send data out-of-order -* Writers must include in-line QoS values if requested by a Reader -* Writers must send periodic HEARTBEAT Messages (reliable only) -* Writers must eventually respond to a negative acknowledgment (reliable only) -* Sending Heartbeats and Gaps with Writer Group Information (Writer belonging to a Group) +* Fast-RTPS doen not follow DDSI-RTPS Specification + - Open Github Issue + https://github.com/eProsima/Fast-RTPS/issues/1221 -READER ------- -A best-effort Reader is completely passive as it only receives data and does not send messages itself. -Therefore, the requirements below only apply to reliable Readers. -* Readers must respond eventually after receiving a HEARTBEAT with final flag not set -* Readers must respond eventually after receiving a HEARTBEAT that indicates a sample is missing -* Once acknowledged, always acknowledged -* Readers can only send an ACKNACK Message in response to a HEARTBEAT Message - -RELIABILITY -=========== -* Best Effort Writer can only be matched with Best Effort Reader -* Stateless Reader can only be Best Effort (maintains absolutely no state, does not handle duplicate and out-of-order changes) - -STATELESS WRITER + +* DDSI-RTPS 2.3 ISSUES + - 8.3.7.7 InfoDestination + 'This message is sent from an RTPS Writer to an RTPS Reader to modify the GuidPrefix used to interpret the Reader entityIds appearing in the Submessages that follow it.' + But state is changed as follows 'Receiver.destGuidPrefix = InfoDestination.guidPrefix'. + Isn't Reader -> Writer also valid? Does it have a specific direction? + - 9.4.5.3 Data Submessage + writerSN is incorrectly shown as only 32 bits in width + - 8.3.4 The RTPS Message Receiver, Table 8.16 - Initial State of the Receiver + Port of UnicastReplyLocatorList should be initialized to Source Port. + - 8.3.7.10.3 Validity + 'This Submessage is invalid when the following is true: + submessageLength in the Submessage header is too small' + But if InvalidateFlag is set, Length can be Zero. Since the length is unsigned, there cannot be an invalid length. + - 9.4.5.1.2 Flags + Clarify from where the endianness begins. + One might think it would begin after the Submessage Header, but the length is also endian dependent. + +* Source Port of SPDP is irrelevant, since it is BEST EFFORT and we do not reply (only Destination Port is of significance) + + + +DESIGN DECISIONS ================ -Note that the processing of this message uses the reply locators in the RTPS Receiver. -This is the only source of information for the StatelessWriter to determine where to send the reply to. -Proper functioning of the protocol requires that the RTPS Reader inserts an InfoReply Submessage ahead of the AckNack such that these fields are properly set. -Writer Liveness Protocol -======================== -ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER -ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER +* !REJECTED! + In order to save memory GUID should only be saved once. + Decision was made to replace GUID with internal reference index. + Discovery module is responsible for saving the GUID and map it to a refernec eindex, that can then be used by other entities. + Writer Endpoints may need access to the real GUID for message fields. + 2 options exist: + - All Endpoints have access to the central memory where the real GUID is saved (needs Arbiter, handle starvation) + - Writer Endpoints fill the fields with the reference index as placeholder, and a seperate Entity will access the central memory and replace the actual values + The Second option was chosen (Less resources) + RTPS Handler should lookup received message GUID in central memory (The lookup should happen in parallel with the actual message handling): + - If not stored, and messegae not for Built-in ENdpoints, drop message + - If in memory, replace with refernece index + The central memory is accessd by 3 Entities: + - RTPS Handler (READ, GUID Lookup) + - Placeholder Handler (READ, GUID Lookup) + - Discovery Module (WRITE, GUID Save) [Need initial Lookup? RTPS Handler should have already handled it. How does DM know if actual GUID or reference index?] + Use a 2-port RAM with an arbiter for READ operations (Give Placeholder Handler priority to prevent DoS starvation) -OPTIONAL 8.4.14 -=============== -Optional features may not be supported by all RTPS implementations. +* !REJECTED! (Use the unused extra flags in the stored participant data) + Use the lowest bit of the Heartbeat/Acknack Deadline stored in the Participant Data to differentiate + between Delay and Suppression. This reduces the resolution from 0.23 ns to 0.47 ns + -* LARGE DATA (Fragmented Data) - --------------------------------------------- - -ENTITYID_UKNOWN also for Built-In? -Ignore Participant/Topic/Publication/Subscription (handle argument of Sampleinfo) - - - -ENDIANNESS -========== -You have to see what datatypes PSM maps to each element. -If the datatype is bigger than a byte, byte swaping has to occur. -The elements of an array are in order (but the elements themselves may need to be swapped if bigger than a Byte) - -ENDPOINT FIFO PACKET FORMAT -=========================== - -31............24..............16..............8...............0 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-------------+---------------+---------------+---------------+ -| OPCODE | FLAGS | SRC_UDP_PORT | -+-------------+---------------+---------------+---------------+ -| SRC_IPv4_ADDR | -+-------------------------------------------------------------+ -| SRC_ENTITYID | -+-------------------------------------------------------------+ -| | -+ + -| SRC_GUIDPREFIX | -+ + -| | -+-------------------------------------------------------------+ -| DEST_ENTITYID [only for Builtin Destinations] | -+-------------------------------------------------------------+ -| | -+ Sequence Number [only for DATA Submessage] + -| | -+-------------------------------------------------------------+ -| | -~ PAYLOAD (SUBMESSAGE CONTENT) ~ -| | -+-------------------------------------------------------------+ - -ENDPOINT_ID -=========== - -0...MAX_ENDPOINTS -READERS...WRITERS - -BUILT-IN ENDPOINTS -================== - -2.2.5 Built-In Topics -The QoS of the built-in Subscriber and DataReader objects is given by the following table: -HISTORY: kind = KEEP_LAST, depth = 1 - -Since the Built-In Endpoints have a history depth of 1, we can safely reduce the processing complexity of ACKNACK and -HEARTBEAT messages (and even ignore GAP messages). - -PARTICICPANT DATA -================= - 31............24..............16..............8...............0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-------------------------------------------------------------+ -01| | - + + -02| GUIDPREFIX | - + + -03| | - +-------------------------------------------------------------+ -04| META_IPv4_ADDRESS | - +-------------------------------------------------------------+ -05| DEFAULT_IPv4_ADDRESS | - +-------------------------------------------------------------+ -06| META_UDP_PORT | DEFAULT_UDP_PORT | - +-------------------------------------------------------------+ -07| UNUSED | EXTRA_FLAGS |Q| - +-------------------------------------------------------------+ -08| LEASE_DURATION | - + + -09| | - +-------------------------------------------------------------+ -10| LEASE_DEADLINE | - + + -11| | - +-------------------------------------------------------------+ -12| | - + SPDP_SEQ_NR + -13| | - +-------------------------------------------------------------+ -14| | - + PUBLICATION_SEQ_NR + -15| | - +-------------------------------------------------------------+ -16| | - + SUBSCRIPTION_SEQ_NR + -17| | - +-------------------------------------------------------------+ -18| | - + MESSAGE_SEQ_NR + -19| | - +-------------------------------------------------------------+ - -ENDPOINT DATA -============= - 31............24..............16..............8...............0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-------------------------------------------------------------+ -01| ENTITYID | - +-------------------------------------------------------------+ -02| | - + + -03| GUIDPREFIX | - + + -04| | - +-------------------------------------------------------------+ -05| | - ~ ENDPOINT_BITMASK ~ -**| | - +-------------------------------------------------------------+ - -ENDPOINT MATCH FRAME -==================== - 31............24..............16..............8...............0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-------------------------------------------------------------+ -01| OPCODE | - +-------------------------------------------------------------+ -02| | - + + -03| GUIDPREFIX | - + + -04| | - +-------------------------------------------------------------+ -05| ENTITYID | - +-------------------------------------------------------------+ -06| IPv4_ADDRESS | - +-------------------------------------------------------------+ -07| UDP_PORT | EXTRA_FLAGS |Q| - +-------------------------------------------------------------+ - -ENDPOINT UNMATCH FRAME -====================== - 31............24..............16..............8...............0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - +-------------------------------------------------------------+ -01| OPCODE | - +-------------------------------------------------------------+ -02| | - + + -03| GUIDPREFIX | - + + -04| | - +-------------------------------------------------------------+ -05| ENTITYID | - +-------------------------------------------------------------+ - -LOCAL ENDPOINT BUFFER -===================== -31............24..............16..............8...............0 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -+-------------------------------------------------------------+ -| | -+ + -| GUIDPREFIX | -+ + -| | -+-------------------------------------------------------------+ -| ENTITYID | -+-------------------------------------------------------------+ -| IPv4_ADDRESS | -+-------------------------------------------------------------+ -| UDP_PORT | EXTRA_FLAGS | -+-------------------------------------------------------------+ -| LIFESPAN | -+ + -| (READER_ONLY) | -+-------------------------------------------------------------+ - -OUTPUT DATA -=========== - 31............24..............16..............8...............0 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -01| PACKET_LENGTH | - +-------------------------------------------------------------+ -02| SRC_IPv4_ADDRESS | - +-------------------------------------------------------------+ -03| DEST_IPv4_ADDRESS | - +-------------------------------------------------------------+ -04| SRC_UDP_PORT | DEST_UDP_PORT | - +-------------------------------------------------------------+ -05| | - ~ RTPS_MESSAGE ~ - | | - +-------------------------------------------------------------+ - - -TOPIC KEYS -========== -Nominally the key is part of the serialized data of a data submessage. -Using the key hash benefits implementations by providing a faster alternative than deserializing the full key from the received data-object. - -MISC -==== - -8.2.9 Relation to DDS Entities -How exactly a DDS Entity interacts with the HistoryCache however, is implementation specific and not -formally modeled by the RTPS protocol. Instead, the Behavior Module of the RTPS protocol only specifies how -CacheChange changes are transferred from the HistoryCache of the RTPS Writer to the HistoryCache of -each matching RTPS Reader. - -8.2.9.1 -When using strict reliable communication, a change can -only be removed when it has been acknowledged by all readers the change was sent to and which are still -active and alive. - -8.2.9.2 -Each matching Writer will attempt to transfer all relevant samples from its HistoryCache to the HistoryCache of the Reader. - -8.4.3 -It is also not able to drop out-of-order samples on the Reader side as this -requires keeping track of the largest sequence number received from each remote Writer. ---> Suggests there is multiple writers - -8.5.4.2 -For the purpose of interoperability, it is sufficient that an -implementation provides the required built-in Endpoints and reliable communication that satisfies the general -requirements listed in 8.4.2. - -8.5.4.4 -An implementation of the protocol need not necessarily send all information contained in the DataTypes. If any -information is not present, the implementation can assume the default values, as defined by the PSM. - -8.7.2.2.6 -In order to implement the DDS_BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS policy, -implementations must include an InfoTimestamp Submessage with every update from a Writer. - - - -DDS_Advanced_Tutorial_2006_00-T1-2_Pardo.pdf (P.16) diff --git a/src/rtps_builtin_endpoint.vhd b/src/rtps_builtin_endpoint.vhd index 4bafc11..b351dff 100644 --- a/src/rtps_builtin_endpoint.vhd +++ b/src/rtps_builtin_endpoint.vhd @@ -6,10 +6,6 @@ use work.math_pkg.all; use work.rtps_package.all; entity rtps_builtin_endpoint is - generic ( - DOMAIN_ID : integer := 0; - DOMAIN_TAG : std_logic_vector(0 to (256*8)-1) := (others => '0') - ) port ( clk : in std_logic; -- Input Clock reset : in std_logic; -- Synchronous Reset @@ -17,12 +13,15 @@ entity rtps_builtin_endpoint is rd : out std_logic; -- Input FIFO read signal data_in : in std_logic_vector(31 downto 0); -- Input FIFO data signal last_word_in : in std_logic; + time : in DOUBLE_WORD_ARRAY; endpoint_output : out USER_ENDPOINT_OUTPUT; endpoint_full : in std_logic_vector(0 to MAX_ENDPOINTS-1); endpoint_wr : out std_logic_vector(0 to MAX_ENDPOINTS-1); rtps_output : out std_logic_vector(31 downto 0); rtps_wr : out std_logic; rtps_full : in std_logic; + last_word_out : out std_logic; + alive : in std_logic_vector(0 to MAX_ENDPOINTS-1); ); end entity; @@ -31,9 +30,9 @@ architecture arch of rtps_builtin_endpoint is --*****COMPONENT DECLARATION****** component single_port_ram is generic ( - ADDR_WIDTH : integer := 8; - DATA_WIDTH : integer := 12; - MEMORY_SIZE : integer := DATA_WIDTH*(2**ADDR_WIDTH) + ADDR_WIDTH : natural := 8; + DATA_WIDTH : natural := 12; + MEMORY_SIZE : natural := DATA_WIDTH*(2**ADDR_WIDTH) ); port ( @@ -46,16 +45,53 @@ architecture arch of rtps_builtin_endpoint is ); end component; - --*****CONSTANT DECLARATION***** - constant BUILTIN_BUFFER_SIZE : integer := MAX_REMOTE_PARTICIPANTS*PARTICIPANT_FRAME_SIZE; - constant BUILTIN_BUFFER_ADDR_WIDTH : integer := log2c(BUILTIN_BUFFER_SIZE); - --*****TYPE DECLARATION***** -- FSM states. Explained below in detail type STAGE_TYPE is (TODO); type MEM_STAGE_TYPE is (TODO); type MESSAGE_TYPE_TYPE is (PDP, EDP, MESSAGE, NONE); type MEM_OPCODE_TYPE is (NOP, SEARCH_PARTICIPANT, SEARCH_ENDPOINT); + type PARTICIPANT_DATA_TYPE is record + meta_addr : std_logic_vector(31 downto 0); + def_addr : std_logic_vector(31 downto 0); + meta_port : std_logic_vector(15 downto 0); + def_port : std_logic_vector(15 downto 0); + extra_flags : std_logic_vector(31 downto 0); + lease_duration : DOUBLE_WORD_ARRAY; + lease_deadline : DOUBLE_WORD_ARRAY; + heartbeat_res_time : DOUBLE_WORD_ARRAY; + acknack_res_time : DOUBLE_WORD_ARRAY; + spdp_seq_nr : DOUBLE_WORD_ARRAY; + pub_seq_nr : DOUBLE_WORD_ARRAY; + sub_seq_nr : DOUBLE_WORD_ARRAY; + mes_seq_nr : DOUBLE_WORD_ARRAY; + end record; + + --*****CONSTANT DECLARATION***** + constant BUILTIN_BUFFER_SIZE : natural := MAX_REMOTE_PARTICIPANTS*PARTICIPANT_FRAME_SIZE; + constant BUILTIN_BUFFER_ADDR_WIDTH : natural := log2c(BUILTIN_BUFFER_SIZE); + constant PARTICIPANT_DATA_FLAG : natural := 0; + constant LEASE_DEADLINE_FLAG : natural := 1; + constant HEARTBEAT_RES_TIME_FLAG : natural := 2; + constant ACKNACK_RES_TIME_FLAG : natural := 3; + constant EDP_SEQ_NR_FLAG : natural := 4; + constant PUB_SEQUENCE_NR : DOUBLE_WORD_ARRAY := convert_to_double_word(to_unsigned(NUM_WRITERS, 64)); + constant SUB_SEQUENCE_NR : DOUBLE_WORD_ARRAY := convert_to_double_word(to_unsigned(NUM_READERS, 64)); + constant ZERO_PARTICIPANT_DATA : PARTICIPANT_DATA_TYPE := ( + meta_addr => (others => '0'), + def_addr => (others => '0'), + meta_port => (others => '0'), + def_port => (others => '0'), + extra_flags => (others => '0'), + lease_duration => (others => (others => '0')), + lease_deadline => (others => (others => '0')), + heartbeat_res_time => (others => (others => '0')), + acknack_res_time => (others => (others => '0')), + spdp_seq_nr => (others => (others => '0')), + pub_seq_nr => (others => (others => '0')), + sub_seq_nr => (others => (others => '0')), + mes_seq_nr => (others => (others => '0')) + ); --*****SIGNAL DECLARATION***** @@ -74,6 +110,8 @@ architecture arch of rtps_builtin_endpoint is signal flags, flags_next : std_logic_vector(7 downto 0) := (others => '0'); signal src_port, src_port_next : std_logic_vector(15 downto 0) := (others => '0'); signal src_addr, src_addr_next : std_logic_vector(31 downto 0) := (others => '0'); + -- TODO: Initialise metatraffic with default locator, since it may not be in the message (Take src Locator?) + -- TODO: Default locator should always be in the message signal addr_latch_1, addr_latch_1_next, addr_latch_2, addr_latch_2_next : std_logic_vector(31 downto 0) := (others => '0'); signal port_latch_1, port_latch_1_next, port_latch_2, port_latch_2_next : std_logic_vector(15 downto 0) := (others => '0'); signal src_entityid, src_entityid_next : std_logic_vector(31 downto 0) := (others => '0'); @@ -90,13 +128,13 @@ architecture arch of rtps_builtin_endpoint is signal endpoint_mask_array, endpoint_mask_array_next : ENDPOINT_BITMASK_ARRAY_TYPE := (others => (others => '0')); signal participant_match, participant_match_next : std_logic := '0'; signal is_subscriber, is_subscriber_next : std_logic := '0'; - signal lease_duration, lease_duration_next : DURATION_TYPE := (others => (others => '0')); - signal lease_deadline : DURATION_TYPE := (others => (others => '0')); - signal lifespan_duration, lifespan_duration_next : DURATION_TYPE := (others => (others => '0')); + signal lease_duration, lease_duration_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal deadline, deadline_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal lifespan_duration, lifespan_duration_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal addr_latch_index, addr_latch_index_next : std_logic := '0'; -- General Purpose counter -- TODO: Constrain range - signal cnt, cnt_next : integer := 0; + signal cnt, cnt_next : natural := 0; signal expects_inline_qos, expects_inline_qos_next : std_logic := '0'; signal guid, guid_next : GUID_ARRAY_TYPE := (others => (others => '0')); signal start_mem_op, mem_op_busy, mem_op_done : std_logic := '0'; @@ -110,14 +148,14 @@ architecture arch of rtps_builtin_endpoint is signal mem_read_data, mem_write_data : std_logic_vector(31 downto 0) := (others => '0'); signal mem_rd, mem_wr, mem_busy, mem_done : std_logic := '0'; -- TODO: Re-check range - signal mem_cnt, mem_cnt_next : integer range 0 to 3 := 0; + signal mem_cnt, mem_cnt_next : natural range 0 to 3 := 0; -- NOTE: Participant Index limited to 32 bits signal participant_index, participant_index_next : std_logic_vector(31 downto 0) := (others => '0'); - signal seq_nr, seq_nr_next : unsigned(SEQUENCE_NR_WIDTH-1 downto 0) := (others => '0'); - signal mem_seq_nr, mem_seq_nr_next : unsigned(SEQUENCE_NR_WIDTH-1 downto 0) := (others => '0'); - signal next_seq_nr, next_seq_nr_next : unsigned(SEQUENCE_NR_WIDTH-1 downto 0) := (others => '0'); - signal first_seq_nr, first_seq_nr_next : unsigned(SEQUENCE_NR_WIDTH-1 downto 0) := (others => '0'); - signal last_seq_nr, last_seq_nr_next : unsigned(SEQUENCE_NR_WIDTH-1 downto 0) := (others => '0'); + signal seq_nr, seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal mem_seq_nr, mem_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal next_seq_nr, next_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal first_seq_nr, first_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal last_seq_nr, last_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); signal is_orphan_search, is_orphan_search_next : std_logic := '0'; signal orphan_entityid, orphan_entityid_next : std_logic_vector(ENTITYID_WIDTH-1 downto 0) := (others => '0'); signal output_sig : std_logic_vector(31 downto 0) := (others => '0'); @@ -128,6 +166,16 @@ architecture arch of rtps_builtin_endpoint is signal stale_check, stale_check_next : std_logic := '0'; signal mem_guidprefix, mem_guidprefix_next : GUIDPREFIX_ARRAY_TYPE : (others => (others => '0')); signal last_word_in_latch, last_word_in_latch_next : std_logic := '0'; + signal update_participant_flags, update_participant_flags_next : std_logic_vector(3 downto 0) := (others => '0'); + signal mem_participant_data, mem_participant_data_next : PARTICIPANT_DATA_TYPE := ZERO_PARTICIPANT_DATA; + signal is_heartbeat_res, is_heartbeat_res_next : std_logic := '0'; + signal seq_prc_done, seq_prc_done_next : std_logic := '0'; + signal count, count_next : unsigned(31 downto 0) := (others => '0'); + signal endpoint_alive : std_logic := '0'; + signal reset_endpoint_alive : std_logic := '0'; + signal auto_live_seq_nr, auto_live_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + signal man_live_seq_nr, man_live_seq_nr_next : DOUBLE_WORD_ARRAY := (others => (others => '0')); + --*****ALIAS DEFINATION***** @@ -158,7 +206,7 @@ architecture arch of rtps_builtin_endpoint is begin tmp := len(15 downto 2); if(len(1 downto 0) /= "00") then - tmp := std_logic_vector(unsigned(tmp) + to_unsigned(1, tmp'length)); + tmp := std_logic_vector(unsigned(tmp) + 1); end if; return tmp; end function; @@ -177,7 +225,7 @@ architecture arch of rtps_builtin_endpoint is return ret; end function; - function convert_from_bitmask_array (bitmask : ENDPOINT_BITMASK_ARRAY, len : integer) return std_logic_vector is + function convert_from_bitmask_array (bitmask : ENDPOINT_BITMASK_ARRAY, len : natural) return std_logic_vector is variable ret : std_logic_vector(0 to len-1) := (others => '0'); begin ret := (others => '0'); @@ -221,13 +269,52 @@ begin end if; end process; - lease_deadline_prc : process(all) - variable tmp : std_logic_vector(63 downto 0) := (others => '0'); + endpoint_alive_prc: process(all) begin - tmp := lease_duration(0) & lease_duration(1); - tmp := tmp + TODO; - lease_deadline(0) <= tmp(63 downto 32); - lease_deadline(1) <= tmp(31 downto 0); + if (rising_edge(clk)) then + -- Reset Endpoint Alive Signal + if (reset = '1' or reset_endpoint_alive = '1') then + endpoint_alive <= '0'; + -- Set Endpoint Alive Signal, if at least one endpoint asserts liveliness + -- XXX: VHDL-2008 unary logic operator + elsif (or alive = '1') then + endpoint_alive <= '1'; + end if; + end if; + end process; + + seq_nr_prc: process(all) + begin + -- DEFAULT + seq_prc_done_next <= seq_prc_done; + mem_seq_nr_next <= mem_seq_nr; + next_seq_nr_next <= next_seq_nr; + + if (mem_op_done = '1') then + seq_prc_done_next <= '1'; + case (message_type) is + when PDP => + mem_seq_nr_next <= mem_participant_data.spdp_seq_nr; + next_seq_nr_next <= mem_participant_data.spdp_seq_nr + 1; + when EDP => + if (is_subscriber = '1') then + mem_seq_nr_next <= mem_participant_data.sub_seq_nr; + next_seq_nr_next <= mem_participant_data.sub_seq_nr + 1; + else + mem_seq_nr_next <= mem_participant_data.pub_seq_nr; + next_seq_nr_next <= mem_participant_data.pub_seq_nr + 1; + end if; + when MESSAGE => + mem_seq_nr_next <= mem_participant_data.mes_seq_nr; + next_seq_nr_next <= mem_participant_data.mes_seq_nr + 1; + when others => + null; + end case; + else + seq_prc_done_next <= '0'; + mem_seq_nr_next <= (others => '0'); + next_seq_nr_next <= (others => '0'); + end if; end process; -- This process connects the Intermediate Output Signals to the actual output FIFOs @@ -293,6 +380,12 @@ begin stale_check_next <= stale_check; first_seq_nr_next <= first_seq_nr; last_seq_nr_next <= last_seq_nr; + update_participant_flags_next <= update_participant_flags; + deadline_next <= deadline; + last_word_out <= '0'; + reset_endpoint_alive <= '0'; + auto_live_seq_nr_next <= auto_live_seq_nr; + man_live_seq_nr_next <= man_live_seq_nr; -- Last Word Latch Setter if (last_word_in = '1') then @@ -312,12 +405,12 @@ begin mem_opcode <= FIND_STALE_PARTICIPANT; start_mem_op <= '1'; stale_check_next <= '1'; - stage_next <= AUTO_PURGE; + stage_next <= STALE_CHECK; elsif (stale_check = '0') then mem_opcode <= FIND_STALE_PARTICIPANT; start_mem_op <= '1'; stale_check_next <= '1'; - stage_next <= AUTO_PURGE; + stage_next <= STALE_CHECK; else -- Process Packet stale_check_next <= '0'; @@ -592,10 +685,10 @@ begin -- Latch Sequence Number case (cnt) is when 1 => - seq_nr_next(63 downto 32) <= unsigned(data_in); + seq_nr_next(0) <= unsigned(data_in); when 2 => - seq_nr_next(31 downto 0) <= unsigned(data_in); - stage_next <= PROCESS_DATA; + seq_nr_next(1) <= unsigned(data_in); + stage_next <= PROCESS_DATA; when others => null; end case; @@ -871,7 +964,7 @@ begin if (empty = '0') then rd_sig <= '1'; -- Increment compare position counter - compare_length_next <= compare_length + to_unsigned(32, compare_length'length); + compare_length_next <= compare_length + 32; -- Compare Strings if (message_type = EDP) then for i in 0 to ENDPOINT_TOPIC'length-1 loop @@ -1148,7 +1241,7 @@ begin -- DEAFULT Stage stage_next <= SKIP_PARAMETER; -- MATCH DOMAIN ID - if (data_in_swapped /= std_logic_vector(to_unsigned(DOMAIN_ID,data_in_swapped'length))) then + if (data_in_swapped /= DOMAIN_ID) then -- No Match participant_match_next <= '0'; end if; @@ -1277,13 +1370,14 @@ begin stage_next <= SKIP_PARAMETER; end if; when PARTICIPANT_MATCH_STAGE_1 => - -- Wait for Participant Search - if (mem_op_done = '1') then + -- Wait for Participant Search and Sequence Number Process + if (mem_op_done = '1' and seq_prc_done = '1') then -- No Match in Buffer - if (addr_res = to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length)) then + if (addr_res = BUILTIN_BUFFER_SIZE) then -- Participant Match if (message_type = PDP and participant_match = '1') then -- Add participant in buffer + deadline_next <= time + lease_duration; mem_opcode <= INSERT_PARTICIPANT; start_mem_op <= '1'; -- DONE @@ -1294,7 +1388,7 @@ begin end if; -- Match in Buffer, Next Sequence Number -- NOTE: We only process the Sequences in order. This may lead to more traffic being exchanged - -- (since if a Data message with a higher sequence number than the next expected will be ignored), + -- (since all Data messages with a higher sequence number than the next expected will be ignored), -- but keeps the logic simple. elsif (next_seq_nr = seq_nr) -- Participant @@ -1305,26 +1399,40 @@ begin mem_opcode <= REMOVE_PARTICIPANT; start_mem_op <= '1'; -- Find and delete all orphaned endpoints in Buffer - stage_next <= FIND_ORPHAN_ENDPOINT; + stage_next <= FIND_ORPHAN_ENDPOINT; else - -- Store new Sequence Number - mem_opcode <= UPDATE_PARTICIPANT; - start_mem_op <= '1'; + -- Update Participant Data + mem_opcode <= UPDATE_PARTICIPANT; + -- Update Lease + deadline_next <= time + lease_duration; + update_participant_flags_next <= (PARTICIPANT_DATA_FLAG => '1', LEASE_DEADLINE_FLAG => '1', others => '0'); + start_mem_op <= '1'; -- DONE - stage_next <= SKIP_PACKET; + stage_next <= SKIP_PACKET; end if; -- Endpoint elsif (message_type = EDP) then + -- TODO: Should we renew the Participant Lease on Endpoint Activity? -- Store new Sequence Number - mem_opcode <= UPDATE_PARTICIPANT; - start_mem_op <= '1'; - stage_next <= INITIATE_ENDPOINT_SEARCH; + mem_opcode <= UPDATE_PARTICIPANT; + update_participant_flags_next <= (EDP_SEQ_NR_FLAG => '1', others => '0'); + start_mem_op <= '1'; + stage_next <= INITIATE_ENDPOINT_SEARCH; else -- Ignore stage_next <= SKIP_PACKET; end if; + -- Old Sequence Number else - -- Old Sequence Number, Ignore + -- If Participant Anouncement, renew lease deadline + if (message_type = PDP) then + -- Update Lease + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + lease_duration; + update_participant_flags_next <= (LEASE_DEADLINE_FLAG => '1', others => '0'); + start_mem_op <= '1'; + end if; + -- Done stage_next <= SKIP_PACKET; end if; end if; @@ -1341,7 +1449,7 @@ begin -- Wait for Endpoint Search if (mem_op_done = '1') then -- No Match in Buffer (New remote Endpoint) - if (addr_res = to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length)) then + if (addr_res = BUILTIN_BUFFER_SIZE) then -- At least one local endpoint match if (endpoint_mask /= (endpoint_mask'range => '0')) then -- Add endpoint in buffer @@ -1394,7 +1502,7 @@ begin when PURGE_ORPHAN_ENDPOINT => if (mem_op_done = '1') then -- Orphan Match - if (addr_res /= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length)) then + if (addr_res /= BUILTIN_BUFFER_SIZE) then -- Remove orphaned endpoint from buffer mem_opcode <= REMOVE_ENDPOINT; start_mem_op <= '1'; @@ -1480,19 +1588,77 @@ begin end if; end case; end if; - when AUTO_PURGE_1 => + when STALE_CHECK => -- Wait for memory OP if (mem_op_done = '1') then -- Found Stale Entry - if (addr_res /= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length)) then - mem_opcode <= REMOVE_PARTICIPANT; - start_mem_op <= '1'; - stage_next <= AUTO_PURGE_2; + if (addr_res /= BUILTIN_BUFFER_SIZE) then + -- Participant Lease Expired + -- NOTE: The mem_participant_data is zero initialized on lease expiration, so we check the default address for zero + -- (since the default address should always be set) + if (mem_participant_data.def_addr /= (mem_participant_data.def_addr'reverse_range => '0')) then + -- Remove Participant + mem_opcode <= REMOVE_PARTICIPANT; + start_mem_op <= '1'; + stage_next <= AUTO_PURGE; + -- Participant Response Time Reached + else + -- Heartbeat Response + if (is_heartbeat_res = '1') then + -- If Suppression Delay passed, zero the time + if(mem_participant_data.heartbeat_res_time(0) = '1') then + -- Zero Heartbeat Response Time + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= (others => '0'); + update_participant_flags_next <= (HEARTBEAT_RES_TIME_FLAG_FLAG => '1', others => '0'); + start_mem_op <= '1'; + -- DONE + stage_next <= IDLE; + -- If Response Delay Passed + else + -- Set Heartbeat Suppression Time + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '1'; + update_participant_flags_next <= (HEARTBEAT_RES_TIME_FLAG_FLAG => '1', others => '0'); + start_mem_op <= '1'; + -- Send Acknack + stage_next <= SEND_ACKNACK; + cnt_next <= 1; + end if; + -- Acknack Response Time + else + -- If Suppression Delay passed, zero the time + if(mem_participant_data.acknack_res_time(0) = '1') then + -- Zero Acknack Response Time + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= (others => '0'); + update_participant_flags_next <= (ACKNACK_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + -- DONE + stage_next <= IDLE; + -- If Response Delay Passed + else + -- Set Acknack Suppression Time + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '1'; + update_participant_flags_next <= (ACKNACK_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + -- Send Acknack + stage_next <= TODO; + cnt_next <= 1; + end if; + end if; + end if; else + -- No Stale Entry Found, DONE stage_next <= IDLE; end if; end if; - when AUTO_PURGE_2 => + when AUTO_PURGE => if (mem_op_done = '1') then -- Help Stage needed to latch the GUID Prefix of the removed staled participant (Needed for the orphan search) guid_next(0) <= mem_guidprefix(0); @@ -1508,41 +1674,55 @@ begin -- Latch Sequence Number case (cnt) is when 1 => - first_seq_nr_next(63 downto 32) <= data_in; + first_seq_nr_next(0) <= data_in; when 2 => - first_seq_nr_next(31 downto 0) <= data_in; + first_seq_nr_next(1) <= data_in; when 3 => - last_seq_nr_next(63 downto 32) <= data_in; + last_seq_nr_next(0) <= data_in; when 4 => - last_seq_nr_next(31 downto 0) <= data_in; - stage_next <= COMPARE_SEQUENCE_NUMBERS; + last_seq_nr_next(1) <= data_in; + stage_next <= PROCESS_HEARTBEAT_SEQUENCE_NUMBERS; end case; end if; - when COMPARE_SEQUENCE_NUMBERS => + when PROCESS_HEARTBEAT_SEQUENCE_NUMBERS => -- Wait for Sequence Number to be fetched from buffer - if (mem_done = '1') then - -- If current sequence number obsolete (removed from source history cache), - -- reset sequence number to expect the first available and request it - if (first_seq_nr > next_seq_nr) then - -- Store expected sequence number -1, since we process only stored sequence number +1 - seq_nr_next <= first_seq_nr - to_unsigned(1, seq_nr'length); - mem_opcode <= UPDATE_PARTICIPANT; - start_mem_op <= '1'; - -- Use "last_seq_nr" as temporal store for the ACKNACK sequence - last_seq_nr_next <= first_seq_nr; - stage_next <= SEND_ACKNACK; - cnt_next <= 1; - -- If new sequence number is available or Writer expects ACKNACK, send it - elsif (last_seq_nr > mem_seq_nr or final_flag = '0') then - -- Use "last_seq_nr" as temporal store for the ACKNACK sequence - last_seq_nr_next <= next_seq_nr; - stage_next <= SEND_ACKNACK; - cnt_next <= 1; - -- Ignore - else - -- Done - stage_next <= SKIP_PACKET; + if (mem_done = '1' and seq_prc_done = '1') then + -- No scheduled Heartbeat Response + if (mem_participant_data.heartbeat_res_time /= (mem_participant_data.heartbeat_res_time'reverse_range => '0')) then + -- If current sequence number obsolete (removed from source history cache), + -- reset sequence number to expect the first available and request it + if (first_seq_nr > next_seq_nr) then + -- Store expected sequence number -1, since we process only stored sequence number +1 + seq_nr_next <= first_seq_nr - 1; + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '0'; + update_participant_flags_next <= (EDP_SEQ_NR_FLAG => '1', HEARTBEAT_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + -- If new sequence number is available or Writer expects ACKNACK, send it + elsif (last_seq_nr > mem_seq_nr or final_flag = '0') then + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '0'; + update_participant_flags_next <= (HEARTBEAT_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + end if; + -- Currently in Heartbeat Response Delay + elsif (mem_participant_data.heartbeat_res_time(0) = '0') then + -- If current sequence number obsolete (removed from source history cache), + -- reset sequence number to expect the first available and request it + if (first_seq_nr > next_seq_nr) then + -- Store expected sequence number -1, since we process only stored sequence number +1 + seq_nr_next <= first_seq_nr - 1; + mem_opcode <= UPDATE_PARTICIPANT; + update_participant_flags_next <= (EDP_SEQ_NR_FLAG => '1', others => '0'); + start_mem_op <= '1'; + end if; end if; + -- Done + stage_next <= SKIP_PACKET; end if; when SEND_ACKNACK => if (rtps_full = '0') then @@ -1552,53 +1732,165 @@ begin case (cnt) is -- OUTPUT HEADER - -- Packet Length - when 1 => - output_sig <= TODO; -- Src IPv4 Address - when 2 => + when 1 => output_sig <= DEFAULT_IPv4_MULTICAST_ADDRESS; -- Dest IPv4 Address - when 3 => - output_sig <= src_addr; + when 2 => + output_sig <= mem_participant_data.meta_addr; -- Src and Dest UDPv4 Ports - when 4 => - output_sig <= META_IPv4_UNICAST_PORT & src_port; + when 3 => + output_sig <= META_IPv4_UNICAST_PORT & mem_participant_data.meta_port; -- RTPS MESSAGE HEADER - when 5 => + when 4 => output_sig <= PROTOCOL_RTPS; - when 6 => + when 5 => output_sig <= PROTOCOLVERSION_2_4 & VENDORID; - when 7 => + when 6 => output_sig <= GUIDPREFIX(0); - when 8 => + when 7 => output_sig <= GUIDPREFIX(1); - when 9 => + when 8 => output_sig <= GUIDPREFIX(2); - -- ACKNACK RTPS SUBMESSAGE + -- ACKNACK RTPS SUBMESSAGE (Publication) -- RTPS Submessage Header - when 10 => - output_sig <= SID_ACKNACK & "00000010" & TODO; + when 9 => + output_sig <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(24, 16)); -- Reader Entity ID - when 11 => - output_sig <= src_entityid; + when 10 => + output_sig <= ENTITYID_SEDP_BUILTIN_PUBLICATIONS_DETECTOR; -- Writer Entity ID - when 12 => - output_sig <= dest_entityid; + when 11 => + output_sig <= ENTITYID_SEDP_BUILTIN_PUBLICATIONS_ANNOUNCER; -- Sequence Number Set (Bitmap Base 1/2) - when 13 => - output_sig <= last_seq_nr(63 downto 32); + when 12 => + output_sig <= mem_participant_data.pub_seq_nr(0); -- Sequence Number Set (Bitmap Base 2/2) - when 14 => - output_sig <= last_seq_nr(31 downto 0); + when 13 => + output_sig <= mem_participant_data.pub_seq_nr(1); -- Sequence Number Set (NumBits) + when 14 => output_sig <= (others => '0'); -- Count when 15 => - output_sig <= TODO + output_sig <= count; + -- ACKNACK RTPS SUBMESSAGE (Subscription) + -- RTPS Submessage Header when 16 => + output_sig <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(24, 16)); + -- Reader Entity ID + when 17 => + output_sig <= ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_DETECTOR; + -- Writer Entity ID + when 18 => + output_sig <= ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_ANNOUNCER; + -- Sequence Number Set (Bitmap Base 1/2) + when 19 => + output_sig <= mem_participant_data.sub_seq_nr(0); + -- Sequence Number Set (Bitmap Base 2/2) + when 20 => + output_sig <= mem_participant_data.sub_seq_nr(1); + -- Sequence Number Set (NumBits) + when 21 => + output_sig <= (others => '0'); + -- Count + when 22 => + output_sig <= count; + -- ACKNACK RTPS SUBMESSAGE (Message) + -- RTPS Submessage Header + when 23 => + output_sig <= SID_ACKNACK & "00000010" & std_logic_vector(to_unsigned(24, 16)); + -- Reader Entity ID + when 24 => + output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER; + -- Writer Entity ID + when 25 => + output_sig <= ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER; + -- Sequence Number Set (Bitmap Base 1/2) + when 26 => + output_sig <= mem_participant_data.mes_seq_nr(0); + -- Sequence Number Set (Bitmap Base 2/2) + when 27 => + output_sig <= mem_participant_data.mes_seq_nr(1); + -- Sequence Number Set (NumBits) + when 28 => + output_sig <= (others => '0'); + -- Count + when 29 => + output_sig <= count; + -- Signal Last Word + last_word_out <= '1'; + -- DONE + stage_next <= IDLE; end case; end if; + when PROCESS_ACKNACK => + if (empty = '0') then + rd_sig <= '1'; + --Increment Counter + cnt_next <= cnt + 1; + -- Latch Sequence Number + -- NOTE: Because we always sent the entire history cache, we only need to look at the SequenceNumberSetBase to determine if we need to sent data or not + case (cnt) is + when 1 => + first_seq_nr_next(0) <= data_in; + when 2 => + first_seq_nr_next(1) <= data_in; + stage_next <= PROCESS_ACKNACK_SEQUENCE_NUMBERS + end case; + end if; + when PROCESS_ACKNACK_SEQUENCE_NUMBERS => + -- NOTE: This guard is not because we need the data from memory, but because we initiate a new memory operation + -- Wait for Memory Operation to finish + if (mem_done = '1') then + -- No scheduled Acknack Response + if (mem_participant_data.acknack_res_time /= (mem_participant_data.acknack_res_time'reverse_range => '0')) then + case (message_type) is + when EDP => + -- Subscriber Acknack + if (is_subscriber = '1') then + -- If reader has not acked all history cache, send data + if (first_seq_nr <= PUB_SEQUENCE_NR) then + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '0'; + update_participant_flags_next <= (ACKNACK_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + end if; + -- Publisher Acknack + else + -- If reader has not acked all history cache, send data + if (first_seq_nr <= SUB_SEQUENCE_NR) then + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '0'; + update_participant_flags_next <= (ACKNACK_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + end if; + end if; + -- Message Acknack + when MESSAGE => + -- NOTE: "auto_live_seq_nr" always has the higher sequence number by design, so we just need to + -- check against that + -- If reader has not acked all history cache, send data + if (first_seq_nr <= auto_live_seq_nr) then + mem_opcode <= UPDATE_PARTICIPANT; + deadline_next <= time + TODO; + -- NOTE: Last Bit denotes if this is Response or Suppression Delay + deadline_next(0) <= '0'; + update_participant_flags_next <= (ACKNACK_RES_TIME_FLAG => '1', others => '0'); + start_mem_op <= '1'; + end if; + end case; + end if; + -- Done + stage_next <= SKIP_PACKET; + end if; + when SEND_PUB_DATA => + when SEND_SUB_DATA => + when SEND_MES_DATA => --############################# when SKIP_PARAMETER => -- End of Parameter @@ -1634,22 +1926,25 @@ begin mem_ctrl_prc : process(all) variable tmp : unsigned(mem_addr_base'range) := (others => '0'); variable tmp2 : unsigned(mem_addr_base'range) := (others => '0'); + variable tmp3 : unsigned(mem_addr_base'range) := (others => '0'); begin -- DEFAULT - mem_op_done <= '0'; - mem_stage_next <= mem_stage; - mem_addr_base_next <= mem_addr_base; - mem_addr_next <= mem_addr; - mem_rd <= '0'; - mem_wr <= '0'; - addr_res_next <= addr_res; - mem_cnt_next <= mem_cnt; - mem_seq_nr_next <= mem_seq_nr; - orphan_entityid_next <= orphan_entityid; - mem_def_addr_next <= mem_def_addr; - mem_def_port_next <= mem_def_port; - mem_xflags_next <= mem_xflags; - last_addr_next <= last_addr; + mem_op_done <= '0'; + mem_stage_next <= mem_stage; + mem_addr_base_next <= mem_addr_base; + mem_addr_next <= mem_addr; + mem_rd <= '0'; + mem_wr <= '0'; + addr_res_next <= addr_res; + mem_cnt_next <= mem_cnt; + mem_seq_nr_next <= mem_seq_nr; + orphan_entityid_next <= orphan_entityid; + mem_def_addr_next <= mem_def_addr; + mem_def_port_next <= mem_def_port; + mem_xflags_next <= mem_xflags; + last_addr_next <= last_addr; + mem_participant_data_next <= mem_participant_data; + is_heartbeat_res_next <= is_heartbeat_res; case (mem_stage) is when IDLE => @@ -1678,7 +1973,7 @@ begin mem_cnt_next <= 1; when UPDATE_ENDPOINT => mem_stage_next <= UPDATE_ENDPOINT; - mem_addr_next <= addr_res + to_unsigned(4, mem_addr_base'length); + mem_addr_next <= addr_res + 4; mem_cnt_next <= 0; endpoint_mask_array_next <= convert_to_bitmask_array(endpoint_mask); when INSERT_PARTICIPANT => @@ -1717,164 +2012,164 @@ begin mem_cnt_next <= 0; when UPDATE_PARTICIPANT => mem_stage_next <= UPDATE_PARTICIPANT; - mem_cnt_next <= 1; - case (message_type) is - when PDP => - mem_addr_next <= addr_res + to_unsigned(11, mem_addr'length); - when EDP => - -- Publisher - if (is_subscriber = '0') then - mem_addr_next <= addr_res + to_unsigned(13, mem_addr'length); - -- Subscriber - else - mem_addr_next <= addr_res + to_unsigned(15, mem_addr'length); - end if; - when MESSAGE => - mem_addr_next <= addr_res + to_unsigned(17, mem_addr'length); - when others => - -- Uknown Message Type. - mem_stage_next <= IDLE; - end case; + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_addr_next <= addr_res + 3; + mem_cnt_next <= 1; + elsif (update_participant_flags(LEASE_DEADLINE_FLAG) = '1') then + mem_addr_next <= addr_res + 11; + mem_cnt_next <= 9; + elsif (update_participant_flags(HEARTBEAT_RES_TIME_FLAG) = '1') then + mem_addr_next <= addr_res + 13; + mem_cnt_next <= 11; + elsif (update_participant_flags(ACKNACK_RES_TIME_FLAG) = '1') then + mem_addr_next <= addr_res + 15; + mem_cnt_next <= 13; + elsif (update_participant_flags(EDP_SEQ_NR_FLAG) = '1') then + mem_addr_next <= addr_res + 17; + mem_cnt_next <= 15; + end if; when others => null; end case; end if; when SEARCH_PARTICIPANT => - addr_res_next <= mem_addr_base; - mem_cnt_next <= 1; - mem_rd <= '1'; - mem_stage_next <= COMPARE_GUIDPREFIX; - -- Initial Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); - -- Reached MAX Addr, No Match Found - if (mem_addr_base = max_participant_addr) then - addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length); --No match - mem_stage_next <= IDLE; - end if; - when COMPARE_GUIDPREFIX => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); - -- TODO: Use this variant (Adder result in variable assignement) to denote that there is only one adder synthesized everywhere in the code? + mem_addr_next <= mem_addr + 1; -- Next Participant Frame Address - tmp := mem_addr_base + to_unsigned(PARTICIPANT_FRAME_SIZE, mem_addr_base'length); + tmp := mem_addr_base + PARTICIPANT_FRAME_SIZE; + case (mem_cnt) is + when 0 => + -- Preload + -- Reached MAX Addr, No Match Found + if (mem_addr_base = max_participant_addr) then + addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length); --No match + mem_stage_next <= IDLE; + end if; when 1 => -- No Match if (mem_read_data /= guid(0)) then -- Continue Search - mem_stage_next <= SEARCH_PARTICIPANT; mem_addr_next <= tmp; mem_addr_base <= tmp; + mem_cnt_next <= 0; end if; when 2 => -- No Match if (mem_read_data /= guid(1)) then -- Continue Search - mem_stage_next <= SEARCH_PARTICIPANT; mem_addr_next <= tmp; mem_addr_base <= tmp; + mem_cnt_next <= 0; end if; when 3 => -- No Match if (mem_read_data /= guid(2)) then -- Continue Search - mem_stage_next <= SEARCH_PARTICIPANT; mem_addr_next <= tmp; mem_addr_base <= tmp; - -- Match Found - else - -- Get Default Endpoint Address - mem_stage_next <= GET_DEFAULT_ADDRESS; mem_cnt_next <= 0; - mem_addr_next <= mem_addr_base + to_unsigned(4, mem_addr'length); + -- Match + else + -- Fetch Participant Data + mem_stage_next <= GET_PARTICIPANT_DATA; + -- No preload needed + mem_cnt_next <= 1; end if; end case; - when GET_DEFAULT_ADDRESS => + when GET_PARTICIPANT_DATA => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; case (mem_cnt) is when 0 => -- Memory Preload null; when 1 => - -- Latch IPv4 Address - mem_def_addr_next <= mem_read_data; + -- Metatraffic IPv4 Address + mem_participant_data_next.meta_addr <= mem_read_data; when 2 => - -- Latch UDPv4 Port - mem_def_port_next <= mem_read_data; + -- Default Endpoint IPv4 Address + mem_participant_data_next.def_addr <= mem_read_data; when 3 => - -- Latch Extra Flags - mem_xflags_next <= mem_read_data(15 downto 0); - - -- DEFAULT Next Stage - mem_stage_next <= GET_SEQ_NR; - mem_cnt_next <= 0; - -- NOTE: This assumes the message type has been correctly set by this time (Race Condition). - -- The mem_ctrl_prc needs at least 8 clk cycles before reaching this state, while the parse_prc takes - -- at most 3 clk cycles after issuing the mem_op before updating the message type. - case (message_type) is - when PDP => - mem_addr_next <= mem_addr_base + to_unsigned(11, mem_addr'length); - when EDP => - -- Publisher - if (is_subscriber = '0') then - mem_addr_next <= mem_addr_base + to_unsigned(13, mem_addr'length); - -- Subscriber - else - mem_addr_next <= mem_addr_base + to_unsigned(15, mem_addr'length); - end if; - when MESSAGE => - mem_addr_next <= mem_addr_base + to_unsigned(17, mem_addr'length); - when others => - -- Uknown Message Type. - mem_stage_next <= IDLE; - end case; - end case; - when GET_SEQ_NR => - mem_rd <= '1'; - -- Increment counter - mem_cnt_next <= mem_cnt + 1; - -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); - case (mem_cnt) is - when 0 => - -- Memory Preload - null; - when 1 => - mem_seq_nr_next(63 downto 32) <= unsigned(mem_read_data); - when 2 => - mem_seq_nr_next(31 downto 0) <= unsigned(mem_read_data); - when 3 => - next_seq_nr_next <= mem_seq_nr + to_unsigned(1,mem_seq_nr'length); - mem_stage_next <= IDLE; + -- UDPv4 Ports + mem_participant_data_next.meta_port <= mem_read_data(31 downto 16); + mem_participant_data_next.def_port <= mem_read_data(15 downto 0); + when 4 => + -- Extra Flags + mem_participant_data_next.extra_flags <= mem_read_data; + when 5 => + -- SPDP Sequence Number 1/2 + mem_participant_data_next.spdp_seq_nr(0) <= unsigned(mem_read_data); + when 6 => + -- SPDP Sequence Number 2/2 + mem_participant_data_next.spdp_seq_nr(1) <= unsigned(mem_read_data); + when 7 => + -- Lease Duration 1/2 + mem_participant_data_next.lease_duration(0) <= unsigned(mem_read_data); + when 8 => + -- Lease Duration 2/2 + mem_participant_data_next.lease_duration(1) <= unsigned(mem_read_data); + when 9 => + -- Lease Deadline 1/2 + mem_participant_data_next.lease_deadline(0) <= unsigned(mem_read_data); + when 10 => + -- Lease Deadline 2/2 + mem_participant_data_next.lease_deadline(1) <= unsigned(mem_read_data); + when 11 => + -- HEARTBEAT DEADLINE 1/2 + mem_participant_data_next.heartbeat_res_time(0) <= unsigned(mem_read_data); + when 12 => + -- HEARTBEAT DEADLINE 2/2 + mem_participant_data_next.heartbeat_res_time(1) <= unsigned(mem_read_data); + when 13 => + -- ACKNACK DEADLINE 1/2 + mem_participant_data_next.acknack_res_time(0) <= unsigned(mem_read_data); + when 14 => + -- ACKNACK DEADLINE 2/2 + mem_participant_data_next.acknack_res_time(1) <= unsigned(mem_read_data); + when 15 => + -- Publication Sequence Number 1/2 + mem_participant_data_next.pub_seq_nr(0) <= unsigned(mem_read_data); + when 16 => + -- Publication Sequence Number 2/2 + mem_participant_data_next.pub_seq_nr(1) <= unsigned(mem_read_data); + when 17 => + -- Subscription Sequence Number 1/2 + mem_participant_data_next.sub_seq_nr(0) <= unsigned(mem_read_data); + when 18 => + -- Subscription Sequence Number 2/2 + mem_participant_data_next.sub_seq_nr(1) <= unsigned(mem_read_data); + when 19 => + -- Participant Message Sequence Number 1/2 + mem_participant_data_next.mes_seq_nr(0) <= unsigned(mem_read_data); + when 20 => + -- Participant Message Sequence Number 2/2 + mem_participant_data_next.mes_seq_nr(1) <= unsigned(mem_read_data); + -- DONE + mem_stage_next <= IDLE; end case; when SEARCH_ENDPOINT => - addr_res_next <= mem_addr_base; - mem_addr_next <= mem_addr_base + to_unsigned(1, mem_addr'length); - mem_cnt_next <= 1; - mem_rd <= '1'; - mem_stage_next <= COMPARE_GUID; - - -- Reached MAX Addr, No Match Found - if (mem_addr_base = max_endpoint_addr) then - addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length);; --No match - mem_stage_next <= IDLE; - end if; - when COMPARE_GUID => mem_rd <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); - -- Next Participant Frame Address - tmp := mem_addr_base - to_unsigned(ENDPOINT_FRAME_SIZE, mem_addr'length); + mem_addr_next <= mem_addr + 1; + -- Next Endpoint Frame Address + tmp := mem_addr_base - ENDPOINT_FRAME_SIZE; + case (mem_cnt) is + when 0 => + -- Reached MAX Addr, No Match Found + if (mem_addr_base = max_endpoint_addr) then + addr_res_next <= to_unsigned(BUILTIN_BUFFER_SIZE, addr_res'length);; -- No match + mem_stage_next <= IDLE; + end if; when 1 => -- If we search for orphan endpoint, ignore Entity id match and latch it for later use if (is_orphan_search = '1') then @@ -1884,34 +2179,34 @@ begin -- NOTE: Endpoint GUID is stored with Entity ID first, and then the GUID Prefix if (mem_read_data /= guid(3)) then -- Continue Search - mem_stage_next <= SEARCH_ENDPOINT; mem_addr_next <= tmp; mem_addr_base <= tmp; + mem_cnt_next <= 0; end if; end if; when 2 => -- No Match if (mem_read_data /= guid(0)) then -- Continue Search - mem_stage_next <= SEARCH_ENDPOINT; mem_addr_next <= tmp; mem_addr_base <= tmp; + mem_cnt_next <= 0; end if; when 3 => -- No Match if (mem_read_data /= guid(1)) then -- Continue Search - mem_stage_next <= SEARCH_ENDPOINT; mem_addr_next <= tmp; mem_addr_base <= tmp; + mem_cnt_next <= 0; end if; when 4 => -- No Match if (mem_read_data /= guid(2)) then -- Continue Search - mem_stage_next <= SEARCH_ENDPOINT; mem_addr_next <= tmp; mem_addr_base <= tmp; + mem_cnt_next <= 0; -- Match Found else mem_stage_next <= GET_ENDPOINT_MASK; @@ -1923,7 +2218,7 @@ begin -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Latch Endpoint Bitmask endpoint_mask_array_next(mem_cnt) <= mem_read_data; -- Exit Condition @@ -1933,7 +2228,7 @@ begin end if; when REMOVE_PARTICIPANT => -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; @@ -1975,7 +2270,7 @@ begin when UPDATE_ENDPOINT => mem_wr <= '1'; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; @@ -1988,11 +2283,11 @@ begin when FIND_PARTICIPANT_SLOT => mem_rd <= '1'; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Next Participant Frame Address - tmp := mem_addr_base + to_unsigned(PARTICIPANT_FRAME_SIZE, mem_addr_base'length); + tmp := mem_addr_base + PARTICIPANT_FRAME_SIZE; case (mem_cnt) is when 0 => @@ -2058,7 +2353,7 @@ begin when INSERT_PARTICIPANT => mem_wr <= '1'; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; @@ -2085,39 +2380,51 @@ begin -- Extra Flags mem_write_data <= (0 => expects_inline_qos, others => '0'); when 8 => - -- Lease Duration 1/2 - mem_write_data <= lease_duration(0); - when 9 => - -- Lease Duration 2/2 - mem_write_data <= lease_duration(1); - when 10 => - -- Lease Deadline 1/2 - mem_write_data <= lease_deadline(0); - when 11 => - -- Lease Deadline 2/2 - mem_write_data <= lease_deadline(1); - when 12 => -- SPDP Sequence Number 1/2 - mem_write_data <= std_logic_vector(seq_nr(63 downto 32)); - when 13 => + mem_write_data <= std_logic_vector(seq_nr(0)); + when 9 => -- SPDP Sequence Number 2/2 - mem_write_data <= std_logic_vector(seq_nr(31 downto 0)); + mem_write_data <= std_logic_vector(seq_nr(1)); + when 10 => + -- Lease Duration 1/2 + mem_write_data <= std_logic_vector(lease_duration(0)); + when 11 => + -- Lease Duration 2/2 + mem_write_data <= std_logic_vector(lease_duration(1)); + when 12 => + -- Lease Deadline 1/2 + mem_write_data <= std_logic_vector(lease_deadline(0)); + when 13 => + -- Lease Deadline 2/2 + mem_write_data <= std_logic_vector(lease_deadline(1)); when 14 => - -- Publication Sequence Number 1/2 + -- HEARTBEAT DEADLINE 1/2 mem_write_data <= (others => '0'); when 15 => - -- Publication Sequence Number 2/2 + -- HEARTBEAT DEADLINE 2/2 mem_write_data <= (others => '0'); when 16 => - -- Subscription Sequence Number 1/2 + -- ACKNACK DEADLINE 1/2 mem_write_data <= (others => '0'); when 17 => - -- Subscription Sequence Number 2/2 + -- ACKNACK DEADLINE 2/2 mem_write_data <= (others => '0'); when 18 => - -- Participant Message Sequence Number 1/2 + -- Publication Sequence Number 1/2 mem_write_data <= (others => '0'); when 19 => + -- Publication Sequence Number 2/2 + mem_write_data <= (others => '0'); + when 20 => + -- Subscription Sequence Number 1/2 + mem_write_data <= (others => '0'); + when 21 => + -- Subscription Sequence Number 2/2 + mem_write_data <= (others => '0'); + when 22 => + -- Participant Message Sequence Number 1/2 + mem_write_data <= (others => '0'); + when 23 => -- Participant Message Sequence Number 2/2 mem_write_data <= (others => '0'); end case; @@ -2126,7 +2433,7 @@ begin -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Next Endpoint Frame Address - tmp := mem_addr_base - to_unsigned(ENDPOINT_FRAME_SIZE, mem_addr_base'length); + tmp := mem_addr_base - ENDPOINT_FRAME_SIZE; case (mem_cnt) is when 0 => @@ -2135,7 +2442,7 @@ begin if (mem_addr_base > max_endpoint_addr) then if (reset_max_pointer = '1') then -- Reset "max_endpoint_addr" to last occupied slot - if (last_addr /= to_unsigned(BUILTIN_BUFFER_SIZE, mem_addr_base'length)) then + if (last_addr /= BUILTIN_BUFFER_SIZE) then max_endpoint_addr_next <= last_addr; end if; -- DONE (reset pointer) @@ -2172,7 +2479,7 @@ begin when INSERT_ENDPOINT => mem_wr <= '1'; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Increment counter mem_cnt_next <= mem_cnt + 1; @@ -2200,10 +2507,11 @@ begin -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; -- Next Participant Frame Address - tmp := mem_addr_base + to_unsigned(PARTICIPANT_FRAME_SIZE, mem_addr_base'length); - tmp2 := mem_addr_base + to_unsigned(7, mem_addr_base'length); + tmp := mem_addr_base + PARTICIPANT_FRAME_SIZE; + tmp2 := mem_addr_base + 11; + tmp3 := mem_addr_base + 3; case (mem_cnt) is when 0 => @@ -2240,19 +2548,67 @@ begin -- Preload null; when 5 => - -- Deadline passed, mark participant - if (mem_read_data < TODO) then - addr_res_next <= mem_addr_base; + -- Lease Deadline passed, mark participant + if (unsigned(mem_read_data) < time(0)) then + addr_res_next <= mem_addr_base; + -- Mark that this is a stale participant + mem_participant_data_next <= ZERO_PARTICIPANT_DATA; -- DONE - mem_stage_next <= IDLE; + mem_stage_next <= IDLE; end if; when 6 => - -- Deadline passed, mark participant - if (mem_read_data < TODO) then - addr_res_next <= mem_addr_base; + -- Lease Deadline passed, mark participant + if (unsigned(mem_read_data) < time(1)) then + addr_res_next <= mem_addr_base; + -- Mark that this is a stale participant + mem_participant_data_next <= ZERO_PARTICIPANT_DATA; -- DONE - mem_stage_next <= IDLE; - -- Lease not expired, check next slot + mem_stage_next <= IDLE; + end if; + when 7 => + -- Heartbeat Deadline passed, mark participant + if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(0)) then + addr_res_next <= mem_addr_base; + -- Fetch Participant Data + mem_addr_next <= tmp3; + mem_cnt_next <= 0; + mem_stage_next <= GET_PARTICIPANT_DATA; + -- Mark as Heartbeat deadline miss + is_heartbeat_res_next <= '1'; + end if; + when 8 => + -- Heartbeat Deadline passed, mark participant + if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(1)) then + addr_res_next <= mem_addr_base; + -- Fetch Participant Data + mem_addr_next <= tmp3; + mem_cnt_next <= 0; + mem_stage_next <= GET_PARTICIPANT_DATA; + -- Mark as Heartbeat deadline miss + is_heartbeat_res_next <= '1'; + end if; + when 9 => + -- Acknack Deadline passed, mark participant + if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(0)) then + addr_res_next <= mem_addr_base; + -- Fetch Participant Data + mem_addr_next <= tmp3; + mem_cnt_next <= 0; + mem_stage_next <= GET_PARTICIPANT_DATA; + -- Mark as Acknack deadline miss + is_heartbeat_res_next <= '0'; + end if; + when 10 => + -- Acknack Deadline passed, mark participant + if (mem_read_data /= (mem_read_data'reverse_range => '0') and unsigned(mem_read_data) < time(1)) then + addr_res_next <= mem_addr_base; + -- Fetch Participant Data + mem_addr_next <= tmp3; + mem_cnt_next <= 0; + mem_stage_next <= GET_PARTICIPANT_DATA; + -- Mark as Acknack deadline miss + is_heartbeat_res_next <= '0'; + -- No deadline expired, check next slot else mem_addr_next <= tmp; mem_addr_base_next <= tmp; @@ -2260,16 +2616,147 @@ begin end if; end case; when UPDATE_PARTICIPANT => - mem_wr <= '1'; -- Increment counter mem_cnt_next <= mem_cnt + 1; -- Default Address Increment - mem_addr_next <= mem_addr + to_unsigned(1, mem_addr'length); + mem_addr_next <= mem_addr + 1; case (mem_cnt) is - when 0 => - mem_write_data <= std_logic_vector(seq_nr(63 downto 32)); when 1 => - mem_write_data <= std_logic_vector(seq_nr(31 downto 0)); + -- Metatraffic IPv4 Address + mem_write_data <= addr_latch_2; + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 2 => + -- Default Endpoint IPv4 Address + mem_write_data <= addr_latch_1; + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 3 => + -- UDPv4 Ports + mem_write_data <= port_latch_2 & port_latch_1; + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 4 => + -- Extra Flags + mem_write_data <= (0 => expects_inline_qos, others => '0'); + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 5 => + -- SPDP Sequence Number 1/2 + mem_write_data <= std_logic_vector(seq_nr(0)); + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 6 => + -- SPDP Sequence Number 2/2 + mem_write_data <= std_logic_vector(seq_nr(1)); + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 7 => + -- Lease Duration 1/2 + mem_write_data <= std_logic_vector(lease_duration(0)); + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 8 => + -- Lease Duration 2/2 + mem_write_data <= std_logic_vector(lease_duration(1)); + if (update_participant_flags(PARTICIPANT_DATA_FLAG) = '1') then + mem_wr <= '1'; + end if; + -- If nothing else to update, skip + if (update_participant_flags(4 downto 1) = (4 downto 1 => '0')) then + mem_stage_next <= IDLE; + end if; + when 9 => + -- Lease Deadline 1/2 + mem_write_data <= std_logic_vector(lease_deadline(0)); + if (update_participant_flags(LEASE_DEADLINE_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 10 => + -- Lease Deadline 2/2 + mem_write_data <= std_logic_vector(lease_deadline(1)); + if (update_participant_flags(LEASE_DEADLINE_FLAG) = '1') then + mem_wr <= '1'; + end if; + -- If nothing else to update, skip + if (update_participant_flags(4 downto 2) = (4 downto 2 => '0')) then + mem_stage_next <= IDLE; + end if; + when 11 => + -- HEARTBEAT DEADLINE 1/2 + mem_write_data <= std_logic_vector(heartbeat_res_time(0)); + if (update_participant_flags(HEARTBEAT_RES_TIME_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 12 => + -- HEARTBEAT DEADLINE 2/2 + mem_write_data <= std_logic_vector(heartbeat_res_time(1)); + if (update_participant_flags(HEARTBEAT_RES_TIME_FLAG) = '1') then + mem_wr <= '1'; + end if; + -- If nothing else to update, skip + if (update_participant_flags(4 downto 3) = (4 downto 3 => '0')) then + mem_stage_next <= IDLE; + end if; + when 13 => + -- ACKNACK DEADLINE 1/2 + mem_write_data <= std_logic_vector(acknack_res_time(0)); + if (update_participant_flags(ACKNACK_RES_TIME_FLAG) = '1') then + mem_wr <= '1'; + end if; + when 14 => + -- ACKNACK DEADLINE 2/2 + mem_write_data <= std_logic_vector(acknack_res_time(1)); + if (update_participant_flags(ACKNACK_RES_TIME_FLAG) = '1') then + mem_wr <= '1'; + end if; + -- If nothing else to update, skip + if (update_participant_flags(4 downto 4) = (4 downto 4 => '0')) then + mem_stage_next <= IDLE; + end if; + when 15 => + -- Publication Sequence Number 1/2 + mem_write_data <= std_logic_vector(seq_nr(0)); + if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '0') then + mem_wr <= '1'; + end if; + when 16 => + -- Publication Sequence Number 2/2 + mem_write_data <= std_logic_vector(seq_nr(1)); + if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '0') then + mem_wr <= '1'; + end if; + when 17 => + -- Subscription Sequence Number 1/2 + mem_write_data <= std_logic_vector(seq_nr(0)); + if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '1') then + mem_wr <= '1'; + end if; + when 18 => + -- Subscription Sequence Number 2/2 + mem_write_data <= std_logic_vector(seq_nr(1)); + if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = EDP and is_subscriber = '1') then + mem_wr <= '1'; + end if; + when 19 => + -- Participant Message Sequence Number 1/2 + mem_write_data <= std_logic_vector(seq_nr(0)); + if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = MESSAGE) then + mem_wr <= '1'; + end if; + when 20 => + -- Participant Message Sequence Number 2/2 + mem_write_data <= std_logic_vector(seq_nr(1)); + if (update_participant_flags(EDP_SEQ_NR_FLAG) = '1' and message_type = MESSAGE) then + mem_wr <= '1'; + end if; mem_stage_next <= IDLE; end case; when others => @@ -2287,7 +2774,7 @@ begin read_cnt <= to_unsigned(1, read_cnt'length); -- Increment read counter each time rd is high elsif (rd_sig = '1') then - read_cnt <= read_cnt + to_unsigned(1, read_cnt'length); + read_cnt <= read_cnt + 1; end if; end if; end process; diff --git a/src/rtps_package.vhd b/src/rtps_package.vhd index 3648b92..f667f67 100644 --- a/src/rtps_package.vhd +++ b/src/rtps_package.vhd @@ -4,38 +4,40 @@ use ieee.numeric_std.all; use work.math_pkg.all; +-- TODO: Convert ALL integers to natural, and remove all conversions (Arithmetics between natural and unsigned are directly supported) + package rtps_package is --*****USER CONFIG***** -- Unicast IPv4 Address used by all RTPS Entities [Default 192.168.0.80] constant IPv4_UNICAST_ADDRESS : std_logic_vector(31 downto 0) := x"C0A80080"; -- Number of RTPS Writer Endpoints - constant NUM_WRITERS : integer := 0; + constant NUM_WRITERS : natural := 0; -- Number of RTPS Reader Endpoints - constant NUM_READERS : integer := 1; + constant NUM_READERS : natural := 1; ----------------------------------------------------------------------------------------------------- -- *DO NOT MODIFY BEGIN* - constant MAX_ENDPOINTS : integer := NUM_READERS+NUM_WRITERS; + constant MAX_ENDPOINTS : natural := NUM_READERS+NUM_WRITERS; -- *DO NOT MODIFY END* ----------------------------------------------------------------------------------------------------- -- PB Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_PB : integer := 7400; + constant PORT_CONFIG_PB : natural := 7400; -- DG Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_DG : integer := 250; + constant PORT_CONFIG_DG : natural := 250; -- PG Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_PG : integer := 2; + constant PORT_CONFIG_PG : natural := 2; -- D0 Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_D0 : integer := 0; + constant PORT_CONFIG_D0 : natural := 0; -- D1 Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_D1 : integer := 10; + constant PORT_CONFIG_D1 : natural := 10; -- D2 Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_D2 : integer := 1; + constant PORT_CONFIG_D2 : natural := 1; -- D3 Value of Default Port Generation (see DDSI-RTPS 2.3 Section 9.6.1) - constant PORT_CONFIG_D3 : integer := 11; + constant PORT_CONFIG_D3 : natural := 11; -- MAC Address of underlying network stack (Used to generate GUIDs) constant MAC_ADDRESS : std_logic_vector(47 downto 0) := x"97917E0BA8CF"; -- Domain ID - constant USER_DOMAIN_ID : integer := 1; + constant USER_DOMAIN_ID : natural := 1; -- Domain TAG constant USER_DOMAIN_TAG : string := ""; @@ -47,10 +49,10 @@ package rtps_package is type ENDPOINT_STRING_SLV_TYPE is array (0 to MAX_ENDPOINTS-1) of STRING_SLV_WORD_TYPE; subtype QOS_TYPE is array (0 to MAX_ENDPOINTS-1) of std_logic_vector(31 downto 0); subtype QOS_SLV_TYPE is array (0 to MAX_ENDPOINTS-1) of std_logic_vector(31 downto 0); - type DURATION_TYPE is array (0 to 1) of unsigned(31 downto 0); - type ENDPOINT_DURATION_TYPE is array (0 to MAX_ENDPOINTS-1) of DURATION_TYPE; + type DOUBLE_WORD_ARRAY is array (0 to 1) of unsigned(31 downto 0); + type ENDPOINT_DURATION_TYPE is array (0 to MAX_ENDPOINTS-1) of DOUBLE_WORD_ARRAY; - constant DURATION_INFINITE : DURATION_TYPE := (x"7fffffff", x"ffffffff"); + constant DURATION_INFINITE : DOUBLE_WORD_ARRAY := (x"7fffffff", x"ffffffff"); -- DURABILITY KIND constant VOLATILE_DURABILITY_QOS : std_logic_vector(31 downto 0) := std_logic_vector(to_unsigned(0,32)); @@ -112,30 +114,30 @@ package rtps_package is constant ENDPOINT_MAX_INSTANCES : QOS_SLV_TYPE := (0 => std_logic_vector(to_unsigned(0,32))); --TODO: Assert constant ENDPOINT_MAX_SAMP_PER_INST : QOS_SLV_TYPE := (0 => std_logic_vector(to_unsigned(0,32))); --TODO: Assert - constant DEFAULT_PARTICIPANT_LEASE_DURATION : DURATION_TYPE := (to_unsigned(100,32),to_unsigned(0,32); - constant PARTICIPANT_LEASE_DURATION : DURATION_TYPE := DEFAULT_PARTICIPANT_LEASE_DURATION; + constant DEFAULT_PARTICIPANT_LEASE_DURATION : DOUBLE_WORD_ARRAY := (to_unsigned(100,32),to_unsigned(0,32); + constant PARTICIPANT_LEASE_DURATION : DOUBLE_WORD_ARRAY := DEFAULT_PARTICIPANT_LEASE_DURATION; -- NOTE: The buffer will not only store participants, but also endpoint data -- Used to determine the size of the built-inendpoint buffer - constant MAX_REMOTE_PARTICIPANTS : integer := 50; + constant MAX_REMOTE_PARTICIPANTS : natural := 50; --*****DDSI-RTPS 2.3***** -- Default Multicast Ipv4 Address (239.255.0.1) constant DEFAULT_IPv4_MULTICAST_ADDRESS : std_logic_vector(31 downto 0) := x"EFFF0001"; - constant GUIDPREFIX_WIDTH : integer := 96; - constant PROTOCOLVERSION_WIDTH : integer := 16; - constant VENDORID_WIDTH : integer := 16; - constant SUBMESSAGE_ID_WIDTH : integer := 8; - constant DOMAIN_ID_WIDTH : integer := 32; - constant UDP_PORT_WIDTH : integer := 16; - constant ENTITYID_WIDTH : integer := 32; - constant PROTOCOL_WIDTH : integer := 32; - constant PARAMETER_ID_WIDTH : integer := 16; - constant PAYLOAD_REPRESENTATION_ID : integer := 16; - constant PAYLOAD_REPRESENTATION_OPTIONS : integer := 16; - constant SEQUENCE_NR_WIDTH : integer := 64; + constant GUIDPREFIX_WIDTH : natural := 96; + constant PROTOCOLVERSION_WIDTH : natural := 16; + constant VENDORID_WIDTH : natural := 16; + constant SUBMESSAGE_ID_WIDTH : natural := 8; + constant DOMAIN_ID_WIDTH : natural := 32; + constant UDP_PORT_WIDTH : natural := 16; + constant ENTITYID_WIDTH : natural := 32; + constant PROTOCOL_WIDTH : natural := 32; + constant PARAMETER_ID_WIDTH : natural := 16; + constant PAYLOAD_REPRESENTATION_ID : natural := 16; + constant PAYLOAD_REPRESENTATION_OPTIONS : natural := 16; + constant SEQUENCE_NR_WIDTH : natural := 64; -- 'RTPS' in Ascii code constant PROTOCOL_RTPS : std_logic_vector(PROTOCOL_WIDTH-1 downto 0) := x"52545053"; @@ -237,7 +239,7 @@ package rtps_package is constant DOMAIN_ID : std_logic_vector(DOMAIN_ID_WIDTH-1 downto 0) := to_unsigned(USER_DOMAIN_ID, DOMAIN_ID'length); - constant DOMAIN_TAG : STRING_SLV_WORD_TYPE; -- Deferred to packgae body + constant DOMAIN_TAG : STRING_SLV_WORD_TYPE; -- Deferred to package body -- Since this implementation runs on the same network stack and the RTPS Endpoints (Readers & Writers) -- can be differentiated based on their Entity ID, it makes no sense to have multiple IP Addresses @@ -297,36 +299,36 @@ package rtps_package is constant ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_WRITER: std_logic_vector(ENTITYID_WIDTH-1 downto 0) := (x"000200c2"); constant ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_READER: std_logic_vector(ENTITYID_WIDTH-1 downto 0) := (x"000200c7"); - constant LOCATOR_KIND_WIDTH : integer := 32; + constant LOCATOR_KIND_WIDTH : natural := 32; constant LOCATOR_KIND_INVALID : std_logic_vector := std_logic_vector(to_signed(-1,LOCATOR_KIND_WIDTH)); constant LOCATOR_KIND_RESERVERD : std_logic_vector := std_logic_vector(to_signed(0,LOCATOR_KIND_WIDTH)); constant LOCATOR_KIND_UDPv4 : std_logic_vector := std_logic_vector(to_signed(1,LOCATOR_KIND_WIDTH)); constant LOCATOR_KIND_UDPv6 : std_logic_vector := std_logic_vector(to_signed(2,LOCATOR_KIND_WIDTH)); -- BUILTIN ENDPOINT SET POSITIONS - constant DISC_BUILTIN_ENDPOINT_PARTICIPANT_ANNOUNCER : integer := 0; - constant DISC_BUILTIN_ENDPOINT_PARTICIPANT_DETECTOR : integer := 1; - constant DISC_BUILTIN_ENDPOINT_PUBLICATIONS_ANNOUNCER : integer := 2; - constant DISC_BUILTIN_ENDPOINT_PUBLICATIONS_DETECTOR : integer := 3; - constant DISC_BUILTIN_ENDPOINT_SUBSCRIPTIONS_ANNOUNCER : integer := 4; - constant DISC_BUILTIN_ENDPOINT_SUBSCRIPTIONS_DETECTOR : integer := 5; - constant BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER : integer := 10; - constant BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER : integer := 11; - constant DISC_BUILTIN_ENDPOINT_TOPICS_ANNOUNCER : integer := 28; - constant DISC_BUILTIN_ENDPOINT_TOPICS_DETECTOR : integer := 29; + constant DISC_BUILTIN_ENDPOINT_PARTICIPANT_ANNOUNCER : natural := 0; + constant DISC_BUILTIN_ENDPOINT_PARTICIPANT_DETECTOR : natural := 1; + constant DISC_BUILTIN_ENDPOINT_PUBLICATIONS_ANNOUNCER : natural := 2; + constant DISC_BUILTIN_ENDPOINT_PUBLICATIONS_DETECTOR : natural := 3; + constant DISC_BUILTIN_ENDPOINT_SUBSCRIPTIONS_ANNOUNCER : natural := 4; + constant DISC_BUILTIN_ENDPOINT_SUBSCRIPTIONS_DETECTOR : natural := 5; + constant BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_WRITER : natural := 10; + constant BUILTIN_ENDPOINT_PARTICIPANT_MESSAGE_DATA_READER : natural := 11; + constant DISC_BUILTIN_ENDPOINT_TOPICS_ANNOUNCER : natural := 28; + constant DISC_BUILTIN_ENDPOINT_TOPICS_DETECTOR : natural := 29; -- BUILTIN ENDPOINT QOS BITMASK - constant BEST_EFFORT_PARTICIPANT_MESSAGE_DATA_READER : integer := 0; + constant BEST_EFFORT_PARTICIPANT_MESSAGE_DATA_READER : natural := 0; --*****CUSTOM***** - constant PARTICIPANT_FRAME_SIZE : integer := 19; - constant ENDPOINT_BITMASK_SIZE : integer := round_div(MAX_ENDPOINTS, 32); - constant ENDPOINT_FRAME_SIZE : integer := 4 + ENDPOINT_BITMASK_SIZE; + constant PARTICIPANT_FRAME_SIZE : natural := 19; + constant ENDPOINT_BITMASK_SIZE : natural := round_div(MAX_ENDPOINTS, 32); + constant ENDPOINT_FRAME_SIZE : natural := 4 + ENDPOINT_BITMASK_SIZE; -- Limit Buffer to 32 bit Addresses - constant BUILTIN_BUFFER_SIZE : integer := min(MAX_ENDPOINTS*PARTICIPANT_FRAME_SIZE, 2**32); + constant BUILTIN_BUFFER_SIZE : natural := min(MAX_ENDPOINTS*PARTICIPANT_FRAME_SIZE, 2**32); --**************** - constant ENDPOINT_MATCH_OPCODE_WIDTH: integer := 32; + constant ENDPOINT_MATCH_OPCODE_WIDTH: natural := 32; constant OPCODE_MATCH : std_logic_vector(ENDPOINT_MATCH_OPCODE_WIDTH-1 downto 0) := x"55000000"; constant OPCODE_UNMATCH : std_logic_vector(ENDPOINT_MATCH_OPCODE_WIDTH-1 downto 0) := x"55000001"; @@ -337,13 +339,29 @@ package rtps_package is type WORD_ARRAY_TYPE is array (range <>) of std_logic_vector(31 downto 0); type OUTPUT_DATA_TYPE is record data : WORD_ARRAY_TYPE; - length : integer; + length : natural; end record; constant READER_ENDPOINT_DATA : OUTPUT_DATA_TYPE; --Deferred to package body constant WRITER_ENDPOINT_DATA : OUTPUT_DATA_TYPE; --Deferred to package body constant PARTICIPANT_DATA : OUTPUT_DATA_TYPE; --Deferred to package body + -- OVERLOAD FUNCTIONS + function convert_from_double_word (seq: DOUBLE_WORD_ARRAY) return unsigned; + function convert_to_double_word (seq: unsigned(63 downto 0)) return DOUBLE_WORD_ARRAY; + function ">" (L,R: DOUBLE_WORD_ARRAY) return boolean; + function "<" (L,R: DOUBLE_WORD_ARRAY) return boolean; + function ">=" (L,R: DOUBLE_WORD_ARRAY) return boolean; + function "<=" (L,R: DOUBLE_WORD_ARRAY) return boolean; + function "=" (L,R: DOUBLE_WORD_ARRAY) return boolean; + function "/=" (L,R: DOUBLE_WORD_ARRAY) return boolean; + function "+" (L,R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY; + function "+" (L: DOUBLE_WORD_ARRAY, R: natural) return DOUBLE_WORD_ARRAY; + function "+" (L: natural, R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY; + function "-" (L,R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY; + function "-" (L: DOUBLE_WORD_ARRAY, R: natural) return DOUBLE_WORD_ARRAY; + function "-" (L: natural, R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY; + end package; package body rtps_package is @@ -424,8 +442,8 @@ package body rtps_package is constant ENDPOINT_TYPE : ENDPOINT_STRING_SLV_TYPE := convert_endpoint_string(ENDPOINT_TYPE_STRING); constant DOMAIN_TAG : STRING_SLV_WORD_TYPE := convert_string(USER_DOMAIN_TAG); - function string_len (str : STRING_SLV_WORD_TYPE) return integer is - ret : integer := 0; + function string_len (str : STRING_SLV_WORD_TYPE) return natural is + ret : natural := 0; begin ret := 0; for i in 0 to str'length loop @@ -444,9 +462,9 @@ package body rtps_package is function gen_reader_endpoint_data return OUTPUT_DATA_TYPE is -- Limit DATA to MAX UDPv4 Payload Size (65,507 Bytes) variable ret : OUTPUT_DATA_TYPE(data(0 to 16376)) := (data => (others => (others => '0')), length => 0); - variable ind : integer := 0; - variable len : integer := 0; - variable tmp : integer := 0; + variable ind : natural := 0; + variable len : natural := 0; + variable tmp : natural := 0; begin ret.data := (others => (others => '0')); ret.length := 0; @@ -596,9 +614,9 @@ package body rtps_package is function gen_writer_endpoint_data return OUTPUT_DATA_TYPE is -- Limit DATA to MAX UDPv4 Payload Size (65,507 Bytes) variable ret : OUTPUT_DATA_TYPE(data(0 to 16376)) := (data => (others => (others => '0')), length => 0); - variable ind : integer := 0; - variable len : integer := 0; - variable tmp : integer := 0; + variable ind : natural := 0; + variable len : natural := 0; + variable tmp : natural := 0; begin ret.data := (others => (others => '0')); ret.length := 0; @@ -613,7 +631,7 @@ package body rtps_package is ret.data(4) := GUIDPREFIX(2); ind := 5; -- RTPS Submessages - -- One DATA Submessage for each Endpoint + -- One DATA Submessage for each Writer Endpoint for i in NUM_READERS to MAX_ENDPOINTS-1 loop -- RTPS Submessage Header ret.data(ind) := SID_DATA & "00000100" & x"0000"; @@ -757,9 +775,9 @@ package body rtps_package is function gen_participant_data return OUTPUT_DATA_TYPE is -- Limit DATA to MAX UDPv4 Payload Size (65,507 Bytes) variable ret : OUTPUT_DATA_TYPE(data(0 to 16376)) := (data => (others => (others => '0')), length => 0); - variable ind : integer := 0; - variable len : integer := 0; - variable tmp : integer := 0; + variable ind : natural := 0; + variable len : natural := 0; + variable tmp : natural := 0; begin ret.data := (others => (others => '0')); ret.length := 0; @@ -932,4 +950,80 @@ package body rtps_package is constant PARTICIPANT_DATA : OUTPUT_DATA_TYPE := gen_participant_data; + function convert_from_double_word (seq: DOUBLE_WORD_ARRAY) return unsigned is + variable ret : unsigned(SEQUENCE_NR_WIDTH-1 downto 0) := (others => '0'); + begin + ret(63 downto 32) := seq(0); + ret(31 downto 0) := seq(1); + return ret; + end function; + + function convert_to_double_word (seq: unsigned(63 downto 0)) return DOUBLE_WORD_ARRAY is + variable ret : DOUBLE_WORD_ARRAY := (others => (others => '0')); + begin + ret(0) := seq(63 downto 32); + ret1) := seq(31 downto 0); + return ret; + end function; + + function ">" (L,R: DOUBLE_WORD_ARRAY) return boolean is + begin + return convert_from_double_word(L) > convert_from_double_word(R); + end function; + + function "<" (L,R: DOUBLE_WORD_ARRAY) return boolean is + begin + return convert_from_double_word(L) < convert_from_double_word(R); + end function; + + function ">=" (L,R: DOUBLE_WORD_ARRAY) return boolean is + begin + return convert_from_double_word(L) >= convert_from_double_word(R); + end function; + + function "<=" (L,R: DOUBLE_WORD_ARRAY) return boolean is + begin + return convert_from_double_word(L) <= convert_from_double_word(R); + end function; + + function "=" (L,R: DOUBLE_WORD_ARRAY) return boolean; + begin + return convert_from_double_word(L) = convert_from_double_word(R); + end function; + + function "/=" (L,R: DOUBLE_WORD_ARRAY) return boolean is + begin + return convert_from_double_word(L) /= convert_from_double_word(R); + end function; + + function "+" (L,R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY is + begin + return convert_to_double_word(convert_from_double_word(L) + convert_from_double_word(R)); + end function; + + function "+" (L: DOUBLE_WORD_ARRAY, R: natural) return DOUBLE_WORD_ARRAY is + begin + return convert_to_double_word(convert_from_double_word(L) + R); + end function; + + function "+" (L: natural, R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY is + begin + return convert_to_double_word(L + convert_from_double_word(R)); + end function; + + function "-" (L,R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY is + begin + return convert_to_double_word(convert_from_double_word(L) - convert_from_double_word(R)); + end function; + + function "-" (L: DOUBLE_WORD_ARRAY, R: natural) return DOUBLE_WORD_ARRAY is + begin + return convert_to_double_word(convert_from_double_word(L) - R); + end function; + + function "-" (L: natural, R: DOUBLE_WORD_ARRAY) return DOUBLE_WORD_ARRAY is + begin + return convert_to_double_word(L - convert_from_double_word(R)); + end function; + end package body; diff --git a/src/test.vhd b/src/test.vhd index 207971e..991db65 100644 --- a/src/test.vhd +++ b/src/test.vhd @@ -12,11 +12,26 @@ entity test is port ( clk : in std_logic; -- Input Clock reset : in std_logic; -- Synchronous Reset - output : out std_logic + cnt : in natural; + output : out unsigned(31 downto 0) ); end entity; architecture arch of test is + + + signal output_sig : unsigned(31 downto 0) := (others => '0'); + begin - + output <= output_sig; + + bitmap: process(all) + begin + output_sig <= (others => '0'); + + for i in 0 to cnt loop + output_sig(i) <= '1'; + end loop; + end process; + end architecture; diff --git a/syn/DE10-Nano/top.qsf b/syn/DE10-Nano/top.qsf index cdc732e..57a8d30 100644 --- a/syn/DE10-Nano/top.qsf +++ b/syn/DE10-Nano/top.qsf @@ -41,7 +41,7 @@ set_global_assignment -name DEVICE 5CSEBA6U23I7 set_global_assignment -name TOP_LEVEL_ENTITY test set_global_assignment -name ORIGINAL_QUARTUS_VERSION 18.1.0 set_global_assignment -name PROJECT_CREATION_TIME_DATE "12:05:11 MAY 29, 2020" -set_global_assignment -name LAST_QUARTUS_VERSION "19.1.0 Lite Edition" +set_global_assignment -name LAST_QUARTUS_VERSION "18.1.0 Lite Edition" set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files set_global_assignment -name MIN_CORE_JUNCTION_TEMP "-40" set_global_assignment -name MAX_CORE_JUNCTION_TEMP 100