299 lines
15 KiB
Plaintext
299 lines
15 KiB
Plaintext
READER_INTERFACE
|
|
################
|
|
|
|
|
|
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 b"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 <NAME>_lach), that are
|
|
accesible directly via a port of the same name (i.e. <NAME>).
|
|
The name of the generated decode_stage is GET_<NAME>.
|
|
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 b"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 b"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
|
|
NOTE: If there is no next declared member, the second-to-last stage should NOT call the "FETCH" stage
|
|
* 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
|
|
NOTE: If there is no next declared member, the second-to-last stage should NOT call the "FETCH" stage
|
|
|
|
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 <NAME>_mem, and the memory signals are connected via signals of
|
|
name <NAME>_mem_<SIGNALNAME>. The generated memory uses a MAX_BURST_LENGTH of 1.
|
|
Following Port signals are defined:
|
|
NAME DIRECTION CONNECTED
|
|
<NAME>_len out <NAME>_len_latch
|
|
<NAME>_addr in <NAME>_mem_addr in 'IDLE' stage
|
|
<NAME>_ready out <NAME>_mem_ready_in in 'IDLE' stage, else '0' [NOTE: ANDing for aggregated elment types (see Structures below)]
|
|
<NAME>_ren in <NAME>_mem_read and <NAME>_mem_valid_in in 'IDLE' stage
|
|
<NAME>_valid out <NAME>_mem_valid_out [NOTE: ANDing for aggregated elment types (see Structures below)]
|
|
<NAME>_ack in <NAME>_mem_ready_out in 'IDLE' stage
|
|
<NAME> out <NAME>_mem_data_out
|
|
|
|
2 decode_stages are defined:
|
|
* GET_<NAME>_LENGTH
|
|
The first decode_stage is similar to a 4-byte primitive decode stage and latches the length of the
|
|
sequence into the <NAME>_len_latch. If the length is equal zero, the decode_stage of the next
|
|
declared member is taken, instead of the GET_<NAME>. A special <NAME>_cnt counter (used to
|
|
index the type specific memory) is initialized to 0.
|
|
* GET_<NAME>
|
|
This stage is similar to the respective primitive decode_stage with following valiations:
|
|
The <NAME>_cnt is used to set the current <NAME>_mem_addr. On sucessful latch
|
|
(<NAME>_mem_valid_in and <NAME>_ready_in = '1') the align_offset is incremented by the
|
|
respective size, the <NAME>_cnt is incremented, and if the current <NAME>_cnt is equal to
|
|
<NAME>_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 <NAME>_len port signal, <NAME>_len_latch latch, and also no
|
|
GET_<NAME>_LENGTH stage.
|
|
The initialization of the <NAME>_cnt has to be done in the previous decode_stage.
|
|
The <NAME>_cnt is compared against the fixed array length consatnt form the type package.
|
|
|
|
MAP
|
|
---
|
|
Maps are basically sequences of aggregated element type [4]:
|
|
struct <NAME>_Entry {
|
|
<key_type> key;
|
|
<value_type> value;
|
|
};
|
|
sequence<<NAME>_Entry> <NAME>;
|
|
|
|
For simplicity the name of the structure is ignored.
|
|
(I.e. the generated names are <NAME>_key, <NAME>_value instead of
|
|
<NAME>_<NAME>_Entry_key and <NAME>_<NAME>_Entry_value)
|
|
|
|
AGGREGATED TYPES
|
|
================
|
|
|
|
STRUCTURE
|
|
---------
|
|
|
|
If a type member is in itself another structure, the generated <NAME> ports,signals,memories.stages,etc
|
|
are split into <NAME>_<SUB-NAME> signals,ports,memories,stages,etc.
|
|
|
|
In case the element type of a collection type is an aggregated type, a new decode_stage called
|
|
<NAME>_MEMBER_END is generated, which handles the <NAME>_cnt increment and check which
|
|
normally be handled in the GET_<NAME> decode_stage. The <NAME>_ready and <NAME>_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 <NAME> objects are split into <NAME>_<SUB-NAME> objects,
|
|
including the special <NAME>_d objects, which handle the value of the discriminator itself.
|
|
In contrast to structures, the first decode_stage (GET_<NAME>_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_NAME>_cnt of the outer collection is used to index the multidimensional signals
|
|
of the inner collection.
|
|
The <OUTER_COLLECTION_NAME>_ready and <OUTER_COLLECTION_NAME>_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_NAME>_addr_latch helper latch is defined, that latches the
|
|
<OUTER_COLLECTION_NAME>_addr port when the <OUTER_COLLECTION_NAME>_<INNER_COLLECTION_NAME>_ren
|
|
port signal is '1'. This address latch is used to connect the correct memory to the
|
|
<OUTER_COLLECTION_NAME>_<INNER_COLLECTION_NAME> and
|
|
<OUTER_COLLECTION_NAME>_<INNER_COLLECTION_NAME>_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 <NAME>_opt port signal is defined, that is connected to a <NAME>_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 <NAME> (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 <NAME>_opt_latch to '0' and selects the decode_stage of the next declared member. Otherwise
|
|
the <NAME>_opt_latch is set to '1'.
|
|
|
|
|
|
|
|
WRITER_INTERFACE
|
|
################
|
|
|
|
GENERAL
|
|
=======
|
|
|
|
In General the writer_interface is a similar layout to the reader_interface 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 <NAME> 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 <NAME> out -
|
|
add <NAME>_r out <NAME>_mem_data_out
|
|
add <NAME>_w in <NAME>_mem_data_in
|
|
add <NAME>_wen in <NAME>_mem_valid_in in 'IDLE' stage [NOTE: ORed together with <NAME>_ren]
|
|
change <NAME>_len in Used in <NAME>_cnt checks, and to see if collection is empty
|
|
|
|
The <NAME>_len_latch is removed (Since the Length is now an input).
|
|
The 'WRITE_<NAME>' 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 (<NAME>_mem_valid_out) and does the usual write
|
|
procedure. On sucessfull write the <NAME>_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 <NAME>_mem_ready_out is pulled high
|
|
in the last sub-stage.
|
|
|
|
OPTIONALS
|
|
=========
|
|
The direction of the <NAME>_opt port signal is inverted (in) and the <NAME>_opt_latch is removed.
|
|
The memory width is extended by 1 bit, and the <NAME>_opt is prepended
|
|
(i.e. <NAME>_mem_data_in <= <NAME>_opt & <NAME>;)
|
|
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 <NAME>_opt signals are also split into
|
|
<NAME>_opt_r and <NAME>_opt_w port signals.
|
|
|
|
|
|
KEY_HOLDER
|
|
##########
|
|
|
|
GENERAL
|
|
=======
|
|
|
|
Generally the key_holder is a combination of both the reader_interface and writer_interface.
|
|
|
|
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_interface procedure of the <TOPIC> type until
|
|
the last declared member that is also member of the KeyHolder(<NAME>)[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(<NAME>) members, the rest is ignored)
|
|
The second - taken on a 'PUSH_SERIALIZED_KEY' opcode - follows the reader_interface procedure of the
|
|
KeyHolder(<NAME>) 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_interface procedure of the
|
|
KeyHolder(<NAME>) Type.
|
|
The second - taken on a 'READ_KEY_HASH' opcode (if the key is not already calculated) - follows the
|
|
write_interface procedure of the <NAME>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 <NAME>KeyHolder[7] members is in ascending member ID and may not be the same as KeyHolder(<NAME>)[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 |