rtps-fpga/src/IDL-VHDL_Ref.txt
Greek 830d6c1409 Bug Fix and Redesign of TEMPLATE_key_holder
Until now it was assumed that a serialized key is the PLAIN_CDR2
Big Endian encoding of a <TYPENAME>KeyHolder Object [DDS_XTYPES v1.3, 7.6.8]
(I.e. the same blob that computes the MD5 key hash).
Taking other DDS implementations as reference (e.g. Cyclone DDS), it
seems they are using a normal Payload containing a KeyHolder(<TYPENAME>)
Object [DDS_XTYPES v1.3, 7.2.2.4.7] as the serialized key.
The Key Holder Template (together with the Type1 and Type2
implementations) were updated to reflect this change.
A bug fix were the Key Hash was not reset on a 'PUSH_SERIALIZED_KEY'
opcode was also fixed (together with the testbench).
2021-12-09 19:44:38 +01:00

297 lines
15 KiB
Plaintext

READER_WRAPPER
##############
GENERAL
=======
In encoding version 1 all topic types marked with extensibility of FINAL or APPENDABLE are using the
PLAIN_CDR encoding. On the other hand types marked with extensibility MUTABLE are encoded in PL_CDR.[1]
The members of the topic type are handled in declaration order, each in it's own 'decode_stage'.
The 'GET_PAYLOAD_HEADER' stage jumps to the stage handling the first declared member in the topic type.
Each stage jumps to the decode_stage of the next declared member.
If there is no next declared member, stage 'SKIP_PAYLOAD' is selected.
The signal 'align_offset' keeps track of the current alignement offset (Basically byte_count mod 8).
The general procedure is that the input is latched ('data_in_latch') allowing to work Byte-oriented, since
the input is word-sized. Each time the 'align_offset' reaches "x11" a new input word is latched.
The CDR Encodings of all types are Byte aligned.
PRIMITIVES TYPES
================
Primitive Types are directly latched into registers of equal size (name <TYPENAME>_lach), that are
accesible directly via a port of the same name (i.e. <TYPENAME>).
The name of the generated decode_stage is GET_<TYPENAME>.
The generated decode_stage first checks the alignement and aligns the stream using the 'ALIGN_STREAM' stage.
* Primitive size 1
The input is directly latched using the get_sub_vector function together with the current 'align_offset',
1 is added to the 'align_offset', the decode_stage of the next declared member is taken, and the 'FETCH'
stage is called if the current 'align_offset' is "x11".
* Primitive size 2
The input is directly latched using the get_sub_vector function together with the current 'align_offset',
2 is added to the 'align_offset', the decode_stage of the next declared member is taken, and the 'FETCH'
stage is called if the current 'align_offset' is "x1x".
* Primitive size 4
The input is directly latched, 4 is added to the 'align_offset', the decode_stage of the next declared
member is taken, and the 'FETCH' stage is called.
* Primitive size 8
The decode_stage is divided with the help of 'cnt' in 3 sub-stages. A helper double-word latch 'dw_latch'
is used. The first 2 sub-stages latch the word into the helper latch (calling 'FETCH' stage each time),
and the last sub-stage is for latching the final signal in it's final place. In the last sub-stage 8 is
added to the 'align_offset' and the decode_stage of the next declared member is taken.
NOTE: The extra sub-stage is used to push the signal to a memory in a single operation
* Primitive size 16
The decode_stage is divided with the help of 'cnt' in 5 sub-stages. A helper quad-word latch 'qw_latch'
is used. The first 4 sub-stages latch the word into the helper latch (calling 'FETCH' stage each time),
and the last sub-stage is for latching the final signal in it's final place. In the last sub-stage 16 is
added to the 'align_offset' and the decode_stage of the next declared member is taken.
NOTE: The extra sub-stage is used to push the signal to a memory in a single operation
The alignements and sizes for IDL primitive types are following:
IDL TYPE SIZE ALIGNMENT [2]
int8/uint8 1 1
octet 1 1
boolean 1 1
char 1 1
short/unsigned short 2 2
wchar 2 2
long/unsigned long 4 4
float 4 4
long long/unsigned long long 8 8
double 8 8
long double 16 8
ENUMERATED TYPES
================
Enumerations and Bitmasks are encoded into primitives according to their bit_bound.
The width of the latch and port is the same as the specified bit_bound, but the CDR encoding is always
Byte-aligned, so care as to be taken (i.e. resizing).
Other than that the same rules as for primitives apply.
BIT_BOUND PRIMITIVE [3]
1-8 octet
9-16 short
17-32 long (DEFAULT if no bit_bound specified)
33-64 long long
COLLECTION TYPES
================
In contrast to primitive types, collection types are latched into memories of equal width and depth
equal to the maximum length. Memory access port signals are made available to the user to allow to access
the contents.
SEQUENCE
--------
The name of the generated memory is <TYPENAME>_mem, and the memory signals are connected via signals of
name <TYPENAME>_mem_<SIGNALNAME>. The generated memory uses a MAX_BURST_LENGTH of 1.
Following Port signals are defined:
NAME DIRECTION CONNECTED
<TYPENAME>_len out <TYPENAME>_len_latch
<TYPENAME>_addr in <TYPENAME>_mem_addr in 'IDLE' stage
<TYPENAME>_ready out <TYPENAME>_mem_ready_in in 'IDLE' stage, else '0' [NOTE: ANDing for aggregated elment types (see Structures below)]
<TYPENAME>_ren in <TYPENAME>_mem_read and <TYPENAME>_mem_valid_in in 'IDLE' stage
<TYPENAME>_valid out <TYPENAME>_mem_valid_out [NOTE: ANDing for aggregated elment types (see Structures below)]
<TYPENAME>_ack in <TYPENAME>_mem_ready_out in 'IDLE' stage
<TYPENAME> out <TYPENAME>_mem_data_out
2 decode_stages are defined:
* GET_<TYPENAME>_LENGTH
The first decode_stage is similar to a 4-byte primitive decode stage and latches the length of the
sequence into the <TYPENAME>_len_latch. If the length is equal zero, the decode_stage of the next
declared member is taken, instead of the GET_<TYPENAME>. A special <TYPENAME>_cnt counter (used to
index the type specific memory) is initialized to 0.
* GET_<TYPENAME>
This stage is similar to the respective primitive decode_stage with following valiations:
The <TYPENAME>_cnt is used to set the current <TYPENAME>_mem_addr. On sucessful latch
(<TYPENAME>_mem_valid_in and <TYPENAME>_ready_in = '1') the align_offset is incremented by the
respective size, the <TYPENAME>_cnt is incremented, and if the current <TYPENAME>_cnt is equal to
<TYPENAME>_len-1, the decode_stage of the next declared member is taken.
ARRAY
-----
Array is similar to the sequence, but has no length encoding (since it always has the smae size).
That means that there is no <TYPENAME>_len port signal, <TYPENAME>_len_latch latch, and also no
GET_<TYPENAME>_LENGTH stage.
The initialization of the <TYPENAME>_cnt has to be done in the previous decode_stage.
The <TYPENAME>_cnt is compared against the fixed array length consatnt form the type package.
MAP
---
Maps are basically sequences of aggregated element type [4]:
struct <TYPENAME>_Entry {
<key_type> key;
<value_type> value;
};
sequence<<TYPENAME>_Entry> <TYPENAME>;
For simplicity the name of the structure is ignored.
(I.e. the generated names are <TYPENAME>_key, <TYPENAME>_value instead of
<TYPENAME>_<TYPENAME>_Entry_key and <TYPENAME>_<TYPENAME>_Entry_value)
AGGREGATED TYPES
================
STRUCTURE
---------
If a type member is in itself another structure, the generated <TYPENAME> ports,signals,memories.stages,etc
are split into <TYPENAME>_<SUB-TYPENAME> signals,ports,memories,stages,etc.
In case the element type of a collection type is an aggregated type, a new decode_stage called
<TYPENAME>_MEMBER_END is generated, which handles the <TYPENAME>_cnt increment and check which
normally be handled in the GET_<TYPENAME> decode_stage. The <TYPENAME>_ready and <TYPENAME>_valid
port signals are generated by ANDing the respective memory signals of all sub-elements.
UNION
-----
Due to the dynamic nature of unions, all defined sub-types have to be handled equally.
Similar to structures the generated <TYPENAME> objects are split into <TYPENAME>_<SUB-TYPENAME> objects,
including the special <TYPENAME>_d objects, which handle the value of the discriminator itself.
In contrast to structures, the first decode_stage (GET_<TYPENAME>_D) selects the next decode_stage
according to the discriminator value itself. The rest of the generated decode_stages select the
decode_stage of the next declared member.
NESTED COLLECTIONS
==================
If a collection type has in itself another collection type, an array of memories is generated, and the
respective memory connection signals are also multidimensional (Meaning that custom types have to be
created).
The <OUTER_COLLECTION_TYPENAME>_cnt of the outer collection is used to index the multidimensional signals
of the inner collection.
The <OUTER_COLLECTION_TYPENAME>_ready and <OUTER_COLLECTION_TYPENAME>_valid ports of the outer collection
are no longer dependant on the inner collection (If the outer array has no other elements type except the
inner collection the ports have to be hardwired).
A new <OUTER_COLLECTION_TYPENAME>_addr_latch helper latch is defined, that latches the
<OUTER_COLLECTION_TYPENAME>_addr port when the <OUTER_COLLECTION_TYPENAME>_<INNER_COLLECTION_TYPENAME>_ren
port signal is '1'. This address latch is used to connect the correct memory to the
<OUTER_COLLECTION_TYPENAME>_<INNER_COLLECTION_TYPENAME> and
<OUTER_COLLECTION_TYPENAME>_<INNER_COLLECTION_TYPENAME>_valid port signals.
OPTIONALS
=========
If a type member is specified as optional, a special header is preceding the CDR encoding.[5]
Generally the same procedure as the type of the optional member is followed with following
modifications.
A new <TYPENAME>_opt port signal is defined, that is connected to a <TYPENAME>_opt_latch latch.
The previous decode_stage selects the 'GET_OPTIONAL_HEADER' decode_stage, and sets the 'return_stage'
to the actual next decode_stage (The decode_stage of the optional member).
The first generated decode_stage of the <TYPENAME> (i.e. the stage that was in return_stage) checks
the 'optional' signal before anything else (even before the alignement). If 'optional' is '0', it sets
the <TYPENAME>_opt_latch to '0' and selects the decode_stage of the next declared member. Otherwise
the <TYPENAME>_opt_latch is set to '1'.
WRITER_WRAPPER
##############
GENERAL
=======
In General the writer_wrapper is a similar layout to the reader_wrapper with following modifications.
All "GET_*" stages are renamed to "WRITE_*".
The "FETCH" stage is renamed to "PUSH".
'write_sub_vector' is used to write into 'data_out_latch' (instead of 'get_sub_vector' and 'data_in_latch').
The direction of the <TYPENAME> port signals are inverted (i.e. in).
Instead of calling 'SKIP_PAYLOAD' stage on the last declared member, the 'PUSH' stage is explicitly called
and the 'encode_done' and 'finalize_payload' signals are set.
COLLECTION TYPE
===============
The port signals of all collection types are modified as follows:
MOD NAME DIRECTION CONNECTED
remove <TYPENAME> out -
add <TYPENAME>_r out <TYPENAME>_mem_data_out
add <TYPENAME>_w in <TYPENAME>_mem_data_in
add <TYPENAME>_wen in <TYPENAME>_mem_valid_in in 'IDLE' stage [NOTE: ORed together with <TYPENAME>_ren]
change <TYPENAME>_len in Used in <TYPENAME>_cnt checks, and to see if collection is empty
The <TYPENAME>_len_latch is removed (Since the Length is now an input).
The 'WRITE_<TYPENAME>' encode_stage is divided with the help of 'cnt' into 2 sub-stages.
The first sub-stage is responsible for requesting the value from the memory (Memory READ request).
The second sub-stage waits until the value is valid (<TYPENAME>_mem_valid_out) and does the usual write
procedure. On sucessfull write the <TYPENAME>_mem_ready_out has to be pulsed high to finalize the memory
operation.
NOTE: If the encode_stage already is divided into sub-stages (due to the primitive type), a single
sub-stage is added that does the Memory fetch operation, and the <TYPENAME>_mem_ready_out is pulled high
in the last sub-stage.
OPTIONALS
=========
The direction of the <TYPENAME>_opt port signal is inverted (in) and the <TYPENAME>_opt_latch is removed.
The memory width is extended by 1 bit, and the <TYPENAME>_opt is prepended
(i.e. <TYPENAME>_mem_data_in <= <TYPENAME>_opt & <TYPENAME>;)
No 'optional' check is done in the respective encode_stage.
An extra sub-stage is added after the memory fetch operation, that writes the optional header depending
on the highest bit of the returned memory value (i.e. the optional bit). If the highest bit is '0' the
optional header gets a length of zero and the encode_stage of the next declared member is selected, else
the actual size of the optional member type is written. The parameter ID of the optional header is
hardcoded to the actual member ID of the optional member.
NOTE: If the optional is part of a collection type, the <TYPENAME>_opt signals are also split into
<TYPENAME>_opt_r and <TYPENAME>_opt_w port signals.
KEY_HOLDER
##########
GENERAL
=======
Generally the key_holder is a combination of both the reader_wrapper and writer_wrapper.
The port signals are predefined and fixed (no port signal generation).
Both encode_stage and decode_stage signals exist.
The 'ALIGN_STREAM' stage is split into 'ALIGN_IN_STREAM' (for decode_stage) and 'ALIGN_OUT_STREAM'
(for encode_stage).
The decode procedure (decode_stage stages) follows 2 different decoding procedures.
The first - taken on a 'PUSH_DATA' opcode - follows the reader_wrapper procedure of the <TOPIC> type until
the last declared member that is also member of the KeyHolder(<TYPENAME>)[6] Type (i.e. the last decalred
key of the type), after which the 'SKIP_PAYLOAD' stage is taken.
(Since the serialized key only uses the KeyHolder(<TYPENAME>) members, the rest is ignored)
The second - taken on a 'PUSH_SERIALIZED_KEY' opcode - follows the reader_wrapper procedure of the
KeyHolder(<TYPENAME>) directly.
Since the decode_stages for the second decoding procedure are a subset of the first decoding procedure,
the same decode stages are used, and only the 'decode_stage_next' signal is set depending on the
'opcode_latch' signal. The 'GET_PAYLAOD_HEADER' stage selects the correct first decode stage.
Similarly the encode procedure also follows 2 different encoding procedures.
The first - taken on a 'READ_SERIALIZED_KEY' opcode - follows the write_wrapper procedure of the
KeyHolder(<TYPENAME>) Type.
The second - taken on a 'READ_KEY_HASH' opcode (if the key is not already calculated) - follows the
write_wrapper procedure of the <TYPENAME>KeyHolder[7] Type. Note that this encoding is in PLAIN_CDR2
Big Endian, meaning that types wich have an ALIGN_8 in PLAIN_CDR have a ALIGN_4 in PLAIN_CDR2.
Both encoding procedures share the same encode_stages, and the 'encode_stage_next' signal is
set depending on the 'opcode_latch' signal. On a 'READ_SERIALIZED_KEY' opcode the
'WRITE_PAYLOAD_HEADER' stage selects the first encode_stage, while on a 'READ_KEY_HASH' opcode the
'START_KEY_HASH_GENERATION' stage selects the first encode_stage.
PITFALLS
########
* Make sure to initialize the 'cnt' signal to zero before calling a decode_stage/encode_stage with sub-stages
* Keep in mind the ALIGN difference between PLAIN_CDR and PLAIN_CDR2
* Note that the order of the <TYPENAME>KeyHolder[7] members is in ascending member ID and may not be the same as KeyHolder(<TYPENAME>)[6]
[1] DDS_XTYPES v1.3, 7.4.3.1
[2] DDS_XTYPES v1.3, 7.4.1.1.1
[3] DDS_XTYPES v1.3, 7.4.1.1.3
[4] DDS_XTYPES v1.3, 7.4.1.1.4
[5] DDS_XTYPES v1.3, 7.4.1.1.5.2
[6] DDS_XTYPES v1.3, 7.2.2.4.7
[7] DDS_XTYPES v1.3, 7.6.8