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