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).
297 lines
15 KiB
Plaintext
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 |