# Describing CPSW Hierarchies with YAML ## Copyright Notice This file is part of CPSW. It is subject to the license terms in the LICENSE.txt file found in the top-level directory of this distribution and [here](https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html). No part of CPSW, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the LICENSE.txt file. ## Introduction This document describes how a CPSW hierarchy can be defined by writing a YAML file. Such a YAML file provides a convenient interface to the CPSW 'builder' API as it does not require writing any C++ code. The document does not discuss YAML itself. Consult the YAML [specification](http://www.yaml.org/spec/1.2/spec.html) for details about the syntax and other features. ## 1. YAML Extensions CPSW adds a few extensions to YAML: ### 1.1 YAML Include Files and Preprocessor YAML provides no way to include one YAML file from another one. The YAML specification (and yaml-cpp implementation) makes it impossible to define file inclusion within YAML itself. For this reason and because re-using snippets of YAML was deemed very desirable a simple *preprocessor* was implemented in CPSW. The preprocessor recursively parses commented headers in YAML files for 'include' and 'once' directives and assembles a single stream of YAML which is then forwarded to the YAML parser library (yaml-cpp). #### 1.1.1 Preprocessor Header Format The preprocessor scans an initial block of header lines: header_block = { header_line } ; header_line = '#' , ( include_directive | once_directive | comment ) , ?EOL? ; i.e., it scans until a line does not start with `#`. #### 1.1.3 #include Directive The 'include' directive (a line starting with '`#include `' [notice the blank]): whitespace = ' ' | ?TAB? | ?CR? ; nonwhite_char = ?ANYCHAR? - whitespace ; nonbra_char = nonwhite_char - '<' ; nonket_char = nonwhite_char - '>' ; include_directive = 'include ', { whitespace }, include_filename, { whitespace } ; include_filename = bracketed_name | nonbracketed_name ; nonbracketed_name = nonbra_char, { nonwhite_char } ; bracketed_name = '<', nonket_char, { nonket_char }, '>' ; directs the preprocessor to (recursively) continue with processing the file `include_filename` before resuming processing the current file. `include_filename` currently cannot not contain any whitespace characters (escaping not implemented at this time) but it may contain path components. All YAML files are assumed to reside in a single directory which defaults to the current-working directory but the preprocessor can be pointed to an alternate location. #### 1.1.4 #once Directive If the preprocessor encounters a 'once' directive (a line starting with '`#once `' [notice the blank]) once_directive := 'once ' , { whitespace } , once_tag ; once_tag := nonwhite_char, { nonwhite_char } ; then it reads the `once_tag` (any non-empty string following '`#once `') and checks if it has encountered the same `once_tag` already earlier. If this is the case then the preprocessor skips to the end of the file. Otherwise it records the tag and continues processing. Thus, the `#once` directive serves as a simple multiple inclusion guard. However, unlike the familiar `#ifdef`, `#once` always skips to the end of the file. E.g., if `me.yaml` contains: # This file includes itself but does not result in infinite recursion # # Note that here cannot be whitespace in '#include' nor '#once' # #once protect_me # #include me.yaml a_yaml_node: something then the preprocessor will emit `a_yaml_node: something` just once. ### 1.2 YAML Merge Key --- `<<` #### 1.2.1 Purpose of the Merge Key With the standard YAML 'alias' feature and the preprocessor's '#include' directive there are powerful ways to re-use pieces of YAML at different locations. Often, however, it is desirable to reuse *most* parts of an existing piece while being able to *override* select parts. This cannot be achieved with standard YAML. As a result, the 'Merge Key' for YAML 'maps' has been [proposed:](http://yaml.org/type/merge.html). #### 1.2.2 The Semantics of the Merge Key Assume we have a definition default: &default_tag setting_1: value_1 setting_2: value_2 which we want to use in our map 'mymap': mymap: *default_tag However, what if we want to override (just) `setting_2` in `mymap` with let's say `my_value_2`. In standard YAML that would not be possible because a key must be present at most *once* in a map. Thus we might try: mymap: *default_tag setting_2: my_value_2 but this would not be valid YAML code. (mymap already has the 'default' node as its value and there is no way to add additional entries to that node. And even if there were, it would be illegal to use the `setting_2` key again.) This is where the merge key comes in: mymap: << : *default_tag setting_2: my_value_2 Thus 'mymap has now two keys: * `<<` with the default node (itself a map) as its value * `setting_2` with `my_value_2` as its value The idea of the merge key defines semantics for key lookup in a map: lookup_key: if ( map["key"] ) return map["key"] else if ( map["<<"] ) return map["<<"]["key"] I.e.: look for the key in the map. If it is not found then check if there is a merge key and if there is then look for the original key in the merged node (value of the merge key). NOTE: - the merge key ONLY works for maps, not sequences. I.e., a map must hold the merge key and another map must be attached to the merge key. - the merge key is not 'natively' supported by YAML. For it to work the user-code (CPSW in this case) must obey/implement its semantics during lookup operations. #### 1.2.3 Complex Merging The idea of merging looks simple enough. However, as described above it works only across a single level. Imagine our default definition looks like this: default: &default_tag nested_map: setting_1: value_1 setting_2: value_2 I.e., the default map contains nested maps and we want to override a setting located at a deeper level. mymap: << : *default_tag setting_2: my_value_2 no longer works, since `setting_2` is not at the same hierarchy level as `setting_2` inside `nested_map`. What we mean is mymap: << : *default_tag nested_map: setting_2: my_value_2 but in this case the simple lookup algorithm presented above no longer works (`setting_2` is not found in `mymap["nested_map"]` but there is also no merge key in `mymap["nested_map"]`). As a result, the merging algorithm implemented by CPSW is more sophisticated and actually supports the notation just presented in the example. ## 2. CPSW Building Blocks This section describes the building blocks made available by the CPSW framework which can be used to assemble hierarchies in YAML. CPSW is extensible and user-defined classes which derive from the core classes may add additional YAML properties. ### 2.1 Basic Node Every CPSW node described in YAML must be an entry in a map and is itself a YAML map. The key of the CPSW Node will become its name in the hierarchy: myname: property: value will define a CPSW node called 'myname' and it's map contains a key 'property'. The exact properties which are supported and/or expected by a particular node depend in its underlying c++ class. All core classes shall be described in the following sections. #### 2.1.1 `class` Property Every node *must* define a map entry with the key `class`. Its value must correspond to one of the classes registered with CPSW. CPSW will then dispatch interpreting the YAML node data to the factory method of this class which takes care of constructing the underlying CPSW object. Thus, the basic node is defined by myname: class: <className> The value of this key may also be a sequence of class names. In this case, CPSW tries to locate a class in the specified order. E.g., myname: class: - myClass - MMIODev CPSW would then try to locate 'myClass' first and if that fails 'MMIODev' would be used as a fallback. The typical use case is a device for which a driver is initially not available. At least its registers could be made available by MMIODev. ##### 2.1.1.1 Dynamic Class Loading CPSW implements dynamic loading of classes. If it doesn't find a class in its registry it tries to load "<className>.so". A properly written class contains initialization code which would register the class with CPSW so that the ensuing second attempt to find the class will succeed. #### 2.1.2 `instantiate` Property Every node also supports an optional map entry with the key `instantiate` which has a boolean value (defaults to 'true'). When set to 'false' then CPSW will skip instantiation of this node and any potential children. This is useful (especially in combination with the merge key) to disable select nodes. ### 2.2 'Field' Class The lowest-level class which can actually be instantiated is a 'Field'. 'Field' implements the 'Stream' user API. It supports the following properties (in addition to 'Basic Node'): myField: # class of this node class: Field # *mandatory* # brief description of the purpose and semantics of this Field description: <string> # optional # suggested polling interval (not used by CPSW) pollSecs: <double> # optional # size (in bytes) of this field. Note that some subclasses # don't accept a size of zero (default). size: <int> # optional # hint to software whether this Field can be cached. # Legal values are: # NOT_CACHEABLE # WT_CACHEABLE (write-through cacheable) # WB_CACHEABLE (write-back cacheable) # If a Field is WB_CACHEABLE then the software may # e.g., combine transactions scheduled for this field # with others and/or reorder them. cacheable: <Cacheable> # optional # definition of this property allows you to establish an # order in which Fields are dumped/saved to a configuration # file (ignored if a configuration 'template' is used). # Consult 'README.configData' for more information. configPrio: <int> # optional The default value for `cacheable` is <unknown> for leaves and `WT_CACHEABLE` for containers. During the build process <unknown> settings are resolved by using the parent container's value. This makes it easier to manage a default (via the container) and override select Field(s) only. ### 2.3 'Dev' Class The Dev is the base class for all containers. It is derived from Field and inherits all of its properties (but the default value for `cacheable` is `WB_CACHEABLE`). The Dev adds support for a map of children. The `class` entry must be 'Dev' or a subclass thereof. myDev: class: Dev # *mandatory* children: # optional <child_entry> at: # *mandatory* <child_address_entry> # *mandatory* <child_entry> at: # *mandatory* <child_address_entry> # *mandatory* ... After the `children` key all children are listed (as maps themselves). Note that the Dev class also requires for each child a map with *addressing information* to be attached to the child. The properties in the `<child_address_entry>` are interpreted and used by the *Dev* class and not by the child itself. However, the information is specific to each child and is needed for communication between the Dev and its children. E.g., in the case of a conventional memory-mapped device the addressing information contains an 'offset'. In other cases it may be a 'i2c' address or a device filename etc. Each Subclass of Dev supports a distinct subclass or set of subclasses of 'Address' which are usually implemented together with the container class. #### 2.3.1 The 'Address' Class 'Address' is the base class for addressing information which defines the connection of a child to its container. The basic Address supports the following YAML properties (which must be defined in a map under the `at` key inside the child definition): # How many replicas of the child are to be # attached. This allows for the definition # of arrays. # The default value is '1'. # Note that not all subclasses of Address # may support values other than 1. nelms: <int> # optional # Associate a byte order with this address. # The default is 'unknown' in which case the # build process will propagate the container's # byte order here. # # Legal values are # "BE" (big-endian) # "LE" (little-endian) # "UNKNOWN" byteOrder: <ByteOrder> # optional The byte order may not make sense for all subclasses of 'Address' and is best left to be resolved by the container. It may occasionally required to be overridden, e.g., if select registers in a memory-mapped device appear swapped. Here is an example for a little-endian memory map which has an 'abnormal' register presented in big-endian: register: &aregister class: IntField size: 4 sizeBits: 32 myDevice: class: MMIODev byteOrder: LE size: 0x10 children: <<: *normal_children # assume to be defined elsewhere abnormal_register: <<: *aregister at: offset: 12 byteOrder: BE Regarding the `nelms` key it is important to realize that the definition of arrays of elements does *not* rest in the definition of the element itself but with the 'address' where the element is attached. (Similar to the 'C' language: an array is usually an 'array of element-types' and not an 'array-of-element type'. #### 2.3.1 The 'File-System Address' Class This class provides access to files (or devices) on the host computer via the operating system. The presence of `fileName` (inside `at`) triggers instantiation of this class, e.g.,: bytes: class: IntField sizeBits: 8 at: nelms: 1024 # # Path to the file fileName: <string> # *mandatory* # indicate whether the # file is seekable. Some devices # are not... seekable: <bool> # optional # map the child entry at # an offset into the file. # This requires the file # to be seekable! offset: <int> # optional # optional timeout for *read* # operations (in micro-seconds). timeoutUS: <int> # optional ### 2.4 The 'SequenceCommand' Class The SequenceCommand class is derived from Field and is a 'leaf' element. It implements the user-API 'Command' interface and can execute sequences of - delays - writing values to 'ScalVal' interfaces - executing system commands (unix' `system()`) In addition to Field's properties SequenceCommand supports `sequence`. It's value must be a YAML sequence of maps: mySequence: class: SequenceCommand # *mandatory* sequence: - entry: <string> value: <number> - entry: <string> value: <number> ... The `entry` value strings identify a cpsw Path (starting at the sequence node's parent) to which the numbers are written in the sequence defined by the list. E.g., - entry: sibling value: 1234 - entry: usleep value: 10000 - entry: ../aunt/cousin value: 5678 would write 1234 to 'sibling' which resides in the same container as the SequenceCommand itself and 5678 to 'cousin' which resides in another container 'aunt' which is a sibling of the SequenceCommand's parent. The 'usleep' Path is treated special and will cause the sequence to delay for the specified number of microseconds. Note that this shadows any sibling named 'usleep'. The 'system(cmds)' Path is also treated special and will cause a process to be forked executing the `cmds` using the unix `system()` library call. #### 2.4.1 Choice of Multiple Sequences The SequenceCommand also supports a 'two-dimensional' sequence of sequences to offer a choice between multiple commands. The user can then pick one of the sequences at run-time. An `enum` menu may also be associated with the sequence command (more about 'enum' menus in the 'IntField' section. This menu must specify exactly as many entries as there are sequences. In particular: if only a single sequence is defined then the menu can only contain a single entry (mapping to the integer '0'). This can be used to 'label' the command sequence. A SequenceCommand with multiple sequences is not accessible by the user-API 'Command' interface but by 'ScalVal_WO'. Here is an example of a SequenceCommand with two choices: mySequence: class: SequenceCommand # *mandatory* sequence: - - entry: "system(echo running choice one)" value: 0 # ignored - - entry: "system(echo running choice two)" value: 0 enums: - name: "Choice One" value: 0 - name: "Choice Two" value: 1 ### 2.5 The 'IntField' Class IntField is derived from Field and is a 'leaf' class. It implements the `ScalVal`, `ScalVal_RO`, `DoubleVal` and `DoubleVal_RO` user-APIs. IntField supports signed or unsigned integer numerical values of arbitrary length in bits (up to 64). IntField also supports arbitrary alignment within a byte, i.e., the bits that make up an IntField need not be byte-aligned (but the must be contiguous). In addition, IntField supports single- or double-precision IEEE-754 floating-point numbers. Such numbers must have a length of exactly 32 or 64 bits and marked with the `encoding` property set to `IEEE_754`. Bit-shift and endianness-conversion etc. are handled the in the same way as for integers ('enum' mappings as described below are not supported for floating-point entities). Note that if an entity has IEEE_754 encoding then it may only be accessed via the `DoubleVal`/`DoubleVal_RO` interfaces. Thus, IntFields are ideal to represent registers or bits thereof. Optionally, a list of 'enum' mappings can be associated with an IntField which allows mapping of numerical values to strings and back (e.g., a bit can be mapped to 'True'/'False'). IntField also takes care of byte-swapping numbers to host byte order and it can optionally word-swap (suitably aligned) sub-words to handle more obscure cases. Depending on an IntField's signed-ness numbers are sign-extended to the size requested by the user. IntField can be configured to a 'read-only' mode (statically defined by YAML - cannot be switched later) in which only `ScalVal_RO` and `DoubleVal_RO` are supported and values cannot be changed. An optional 'encoding' attribute can be defined for IntField. This e.g., allows the user-application to properly decode numbers into strings if the underlying entity actually represents a character. The 'encoding' attribute is also used for registers containing floating-point numbers. myIntField: class: IntField # *mandatory* # Whether to interpret the underlying bits # as a signed or unsigned number. Signed # numbers are sign-extended to the size # requested by the user. Defaults to 'false'. isSigned: <bool> # optional # Size of the IntField in bits. The Field's # corresponding size in bytes is automatically # computed (YAML setting ignored). # Defaults to 32. # I.e.,: size = (sizeBits + lsBit + 7) / 8 sizeBits: <int> # optional # Alignment of the least-significant bit # within a byte. Legal values are 0..7. # # A number written is left-shifted by # this amount before being (merged and) # written to hardware. It is right-shifted # after being extracted from hardware and # before being handed to the user. # Defaults to 0. lsBit : <int> # optional # Access-mode of this IntField # - "RW" (read-write; default) # - "RO" (read-only) # - "WO" (write-only -- *currently not supported*) mode : <string> # optional # Swap words of specified length (in bytes; # the IntField's bit-size *must* be a multiple # of the word-length!) # # Defaults to 0. wordSwap: <int> # optional # If this IntField represents a character or a # floating-point number etc., then the encoding # used by this IntField can be specified. # An automatically generated GUI, for example, # could use the encoding to detect strings and # display them correctly. # # Legal values include "NONE", "ASCII", "UTF_8", # "ISO_8859_1" among others (consult the API header # for a complete list). # Floating-point numbers use e.g., "IEEE_754". # # Defaults to NONE. encoding: <int> # optional # The base in which values are saved to a configuration # file. Legal values are 10 (decimal) or 16 (hex). # The only effect of this parameter is increasing # readability of saved configuration files. # # Defaults to 16. configBase: <int> # optional The `wordSwap` parameter can e.g., be used to handle the case of a 64-bit word in 'mixed format': Assume a 32-bit system with little-endian byte-addressing where the most significant word is stored at a lower address, followed by the least significant word: address 0 high_word 4 low_word If the words are stored in little-endian format then the quad '0x0807060504030201' would *normally* be stored (by increasing address): 0x01, 0x02, 0x03, 0x04, 0x04, 0x06, 0x07, 0x08 If, however, (as it sometimes happens in 'real-world' devices) the above 'mixed format' is used then the data would be stored: 0x04, 0x05, 0x06, 0x07, 0x01, 0x02, 0x03, 0x04 In this case an IntField with `wordSwap` set to 4 would read the correct value. ### 2.6 The 'ConstIntField' Class ConstIntField is derived from IntField and is another 'leaf' class. It implements `ScalVal_RO` and `DoubleVal_RO` user-APIs. The purpose of ConstIntField is providing run-time access to `constant` values which are defined in YAML. ConstIntField does never access real hardware. ConstIntField can supply string-, double- or integer values. It inherits all properties from IntField but overrides some of them (`sizeBits`, `lsBit`, `byteOrder`, `wordSwap`, `mode`). The type of the value retrieved from YAML is defined by the `encoding` property. If `encoding` is set to `ASCII` then the value is interpreted as a string; otherwise, if `encoding` equals `IEEE_754` then it is interpreted as a `double` else as a 64-bit integer (honoring `isSigned`). A 'Enum' menu with a single entry which maps a string to the numerical value is always attached to ConstIntField (except if IEEE_754 encoding is used) but it is not necessary to define such a menu in YAML. E.g., in the case of aString: class: ConstIntField # *mandatory* # inherited from IntField encoding: ASCII # The constant value value: "Hello" # *mandatory* aDouble: class: ConstIntField # *mandatory* # set to 'IEEE_754' if the value is to be interpreted # as a double. encoding: IEEE_754 value: 3.141 # *mandatory* `aString`'s menu will map the value 0 to the string "Hello" and `aDouble`'s menu will map the value 3 to the string "3.141". ### 2.7 The 'MMIODev' Class MMIODev is a subclass of Dev and implements a memory-mapped container. The only parameter added to Dev is `byteOrder` which is just for convenience since it allows the user to define a byte-order for the entire container which sets the default for all of its children (each child's Address may override this setting individually for the specific child). myMMIODev: class: MMIODev # *mandatory* # NOTE: An MMIODev must specify a non-zero size size: <int> # Set the default ByteOrder for the container. # As explained under 'Address' - if the ByteOrder # is unknown (default) then this MMIODev inherits # from the Address where it is attached. # # Note: At *some* level the byte-order which # will be used by Fields for which this # is relevant *must* be defined. Either # by the Field itself or any of its parents # in the hierarchy. # Legal values are # # "BE" (big-endian) # "LE" (little-endian) # "UNKNOWN" byteOrder: <ByteOrder> #### 2.7.1 MMIOAddress When a Field is attached to an MMIODev then there are two parameters in addition to `nelms` and `byteOrder` (which are inherited from Address): # The byte offset from the MMIODev's base address # where the child resides (if the child spans more than one # byte then this means the offset of the byte with the # lowest byte-address ). # # Default: <none> offset: <int> # *see note* # The 'stride' specifies by how many bytes the elements # of an array are separated/spaced. # The default (0) assumes 'dense' packing i.e., the stride # is automatically set to the size of an array element. stride: <int> # *optional* Note that when the offset is omitted then an ordinary 'Address' is used which essentially bypasses the MMIODev and forwards all read/write operations to the parent. #### 2.7.2 MMIODev and IntField Example Assume a (memory-mapped) device presents an ASCII string of 40 characters where each character is aligned on a 32-bit boundary. The string starts at offset 0x10. myMMIODev: class : MMIODev byteOrder: LE size : 0x1000 children : myString: class: IntField sizeBits: 8 encoding: ASCII at: offset: 0x10 stride: 4 nelms: 40 ### 2.8 The 'NetIODev' Class NetIODev is -- for most applications -- the 'root' device which handles all communication with a remote endpoint. The main property supported by NetIODev is the IP address of the peer: myNetIODev: class : NetIODev # CPSW does attempt to resolve DNS names. # IP addresses may be provided in 'numbers- # and-dot' notation or as names. # Note: omitting the peer's IP lets # CPSW pick 'INADDR_ANY' which on some systems # causes communication with the local host # (which may be useful for testing only). ipAddr: <string> # *optional* # Connect to 'ipAddr' via an 'rssi bridge' # proxy. This property defines the address # of the proxy (you must make sure such a # bridge is actually running!) rssiBridge: <string> # Connect to a TCP destination (either # a 'native' TCP destination or an 'rssi # bridge') via a SOCKS proxy (the most # common case being an SSH connection # with SOCKS support - see open-ssh's # '-D' option for details). socksProxy: <string> Thus, for each peer with a different IP address a separate NetIODev instance is required. These could be attached to a dummy root Dev: myRoot: class: Dev children: peer_1: class: NetIODev ipAddr: 10.0.0.1 at: # empty 'at' -- no addressing info needed peer_2: class: NetIODev ipAddr: 10.0.0.2 at: # empty 'at' -- no addressing info needed In addition to the peer's address the following settings help with tunneling/proxying connections over TCP and even SSH: myRoot: class: NetIODev rssiBridge: <rssi_bridge_ip_or_name> socksProxy: <socks_proxy_ip_or_name>[:<socks_proxy_port>] If the `rssiBridge` property is set then it points to a computer with direct connectivity to the target. On that computer a properly configured `rssi_bridge` must be running. Such a bridge serves as a proxy for RSSI/UDP communication: CPSW <-- TCP connection --> rssi_bridge <-- RSSI/UDP connection --> target If `rssiBridge` is defined in YAML then all `UDP` modules are automatically converted into `TCP`. In addition to `rssiBridge` the `socksProxy` property may be used for tunneling TCP connection(s) over SSH. Typically, an SSH connection is started with the `-D` option ("dynamic port forwarding") letting the SOCKS protocol (which is supported by CPSW) taking care of the rest. Alternatively to `socksProxy`, the environment-variable `SOCKS_PROXY` may also be used for defining a SOCKS proxy. It is often convenient to start an `rssi_bridge` via ssh: ssh -D1080 -tt user@host <path>/rssi_bridge -a <target_ip> -p 8198 -u 8197 <other ports> In most cases `socksProxy` is simply set to `localhost`. Note that the SOCKS proxy is ignored if no TCP is defined in the YAML (explicitly or implicitly due to `rssiBridge`). #### 2.8.1 NetIODev Address Objects The true power of NetIODev lies in the Address objects it implements. These are the communication endpoints where children are attached. Each endpoint instantiates a protocol stack or a part thereof which is configured with a variety of parameters that shall be listed below. Note that the arrangement of the stack ('stacking order') is configured automatically and not subject to user choices. The various protocol modules are presented in the order in which they are usually stacked: SRP_VirtualChannel -- (De)/Multiplexer of SRP transactions based on a tag SRP -- 'Slac Register Protocol'. Encodes I/O Operations TDEST -- (De)/Multiplexer based on TDEST tag Fragmentation -- AKA 'Packetizer'; assembles/disassembles larger frames from/into MTU-sized ones. RSSI -- Reliability and flow-control UDP -- UDP communication layer communicating with a single port of the peer. All modules above UDP are optional but some depend on each other: The `TDEST` (De)Muxer requires the Packetizer to be present and `SRP_VC` requires SRP. Of course, the setup of these layers must match the peer's configuration exactly. Each protocol module is assigned its own map entry in the `at` map of the attachee. Since these entries are YAML maps the order of the protocol modules in YAML is arbitrary (the decision was made deliberately to use maps so that merge keys work across all levels). UDP: # The UDP port of the peer # NOTE: a port number of 0 *disables* UDP # # Default: 8192 port: <int> # Depth of the queue up to the next module # zero (default) picks a suitable value. outQueueDepth: <int> # Number of RX threads to spawn for handling # zero (default) picks a suitable value. numRxThreads: <int> # # Peers which do not implement ARP rely # on being contacted at regular intervals # so that they can record our UDP/IP/MAC # address. # A special thread periodically sends a short # message to the peer for this purpose. # This parameter defines the frequency of # this polling operation. # # A value of 0 disables polling altogether. # A value less than zero lets CPSW pick # a suitable default. # # Default: -1 pollSecs: <int> # Priority of the UDP RX threads. A number # bigger than zero must be a valid pthread # priority and tries to engage a real-time # scheduler. If this fails, CPSW falls back # to the default scheduler. # # Default: 0 threadPriority: <int> # The presence of this key indicates # that RSSI shall be used. Its absence # that no RSSI is to be configured. RSSI: # Priority of the RSSI thread. A number # bigger than zero must be a valid pthread # priority and tries to engage a real-time # scheduler. If this fails, CPSW falls back # to the default scheduler. # # Default: 0 threadPriority: <int> # log2 of the max. number of unacknowledged # segments the peer may send to us. # (See RSSI/RUDP spec for more information) # # Default: 4 ldMaxUnackedSegs: <int> # Depth or RSS output queue. # # Default: 0 (a suitable value is chosen) outQueueDepth: <int> # Depth or RSS input queue. # # Default: 0 (a suitable value is chosen; # should be >= 2**ldMaxUnackedSegs) inpQueueDepth: <int> # Retransmission timeout (proposed to # negotiation process) # # (See RSSI/RUDP spec for more information) # # Default: 100000us retransmissionTimeoutUS: <int> # Cumulative ACK timeout (proposed to # negotiation process) # # (See RSSI/RUDP spec for more information) # # Default: 50000us (should be less than # retransmissionTimeoutUS) cumulativeAckTimeoutUS: <int> # NULL timeout (proposed to # negotiation process) # # (See RSSI/RUDP spec for more information) # # Default: 3000000us nullTimeoutUS: <int> # Max. number of retransmissions before # the connection is considered dead # (proposed to negotiation process). # # (See RSSI/RUDP spec for more information) # # Default: 15 maxRetransmissions: <int> # Max. number of segments that are accepted # without sending ACK (``cumulative ACK''; # proposed to negotiation process). # # (See RSSI/RUDP spec for more information) # # Default: 2 maxCumulativeAcks: <int> # Max. segment size we can receive # without sending ACK (``cumulative ACK'') # # (See RSSI/RUDP spec for more information) # # Default: suitable value chosen by # MTU discovery. When overriding # the default, MAKE SURE NOT TO # EXCEED YOUR CONNECTION'S MTU! maxSegmentSize: <int> # The presence of this key indicates # that 'depack' shall be used. Its absence # that no 'depack' is to be configured. # Obviously, configuring any parameter # for the depacketizer enables it as well. # The depacketizer is also enabled # automatically if the TDESTMux module # is enabled. depack: # Packetizer protocol version. # Either DEPACKETIZER_V0 or # DEPACKETIZER_V2 # # Note that DEPACKETIZER_V2 which # supports TDEST interleaving is actually # implemented by the TDESTMux module, i.e., # all parameters defined for the depacketizer # (such as threadPriority, queue sizes etc.) # are ignored; the TDESTMux configuration # is used instead! # # Default: DEPACKETIZER_V0 protocolVersion:<DepackProtoVersion> # The output queue depth can in special # cases be tuned - if this module # is at the top of the stack and the # application requires more buffering # capacity inside CPSW. outQueueDepth: <int> # For expert use only ldFrameWinSize: <int> # For expert use only ldFragWinSize: <int> # Priority of the depacketizer thread. A number # bigger than zero must be a valid pthread # priority and tries to engage a real-time # scheduler. If this fails, CPSW falls back # to the default scheduler. # # Default: 0 threadPriority: <int> # The TDEST (De)Muxer is enabled # by the presence of this key. TDESTMux: # TDEST to which the child connects # a value between 0 and 255; # Defaults to 0 TDEST: <int> # Whether to strip the Packetizer header # and footer prior to handing data up # (or adding header/footer in the opposite # direction). # The default (when this key is not present) # does not strip the header except if SRP # is layered on top. stripHeader: <bool> # The output queue depth can in special # cases be tuned - if this module # is at the top of the stack and the # application requires more buffering # capacity inside CPSW. outQueueDepth: <int> # The input queue depth can in special # cases be tuned - if this module # is at the top of the stack and the # application requires more buffering # capacity inside CPSW. # # Note: this parameter is only relevant # if the TDESTMux is operating in DEPACKETIZER_V2 # mode. It is ignored otherwise. inpQueueDepth: <int> # Priority of the demultiplexer thread. A number # bigger than zero must be a valid pthread # priority and tries to engage a real-time # scheduler. If this fails, CPSW falls back # to the default scheduler. # # Default: 0 threadPriority: <int> # The presence of the SRP module is defined # by the value of protocolVersion. # NOTE: the default is SRP_UDP_V2 which enables # SRP. SRP: # SRP Protocol version. To disable set explicitly # to SRP_UDP_NONE. # Legal values: # SRP_UDP_NONE No SRP # SRP_UDP_V1 (early version in network-byte order # with extra header word) # SRP_UDP_V2 first version in wider use # SRP_UDP_V3 Current version # Consult respective confluence documentation for # details about SRP versions. protocolVersion:<SRPProtoVersion> # How long to wait for a SRP transaction to # complete (in micro-seconds) # zero (default) picks a suitable value timeoutUS: <int> # Whether to automatically adjust the # retransmission timeout based on statistics. # Should be disabled if RSSI or TDESTMux # are being used (large frames to a different # TDEST going through the packetizer may stall # SRP) # Default: yes unless TDESTMux is configured dynTimeout: <bool> # How many times to retry a failed SRP transaction retryCount: <int> # The default write mode (might be overridden by # for individual operations if the API offers such # a feature). POSTED or SYNCHRONOUS (defaults to # POSTED). defaultWriteMode: <WriteMode> # The presence of this key enables the # SRP VirtualChannel (De)Muxer. SRPMux: # Virtual Channel number (see note) to use. # A number in the range 0..127. # Default: 0 virtualChannel: <int> # Depth of the queue where asynchronous # replies are posted. # zero (default) picks a suitable value. outQueueDepth: <int> # Priority of the demultiplexer thread. A number # bigger than zero must be a valid pthread # priority and tries to engage a real-time # scheduler. If this fails, CPSW falls back # to the default scheduler. # # Default: 0 threadPriority: <int> ##### 2.8.1.1 TCP Module A TCP protocol module is also available. It is intended to substitute UDP and RSSI. This module is useful when contacting a server implemented in software (as opposed to firmware). In software, TCP is readily available and provides a reliable data stream without the need for RSSI. The TCP module supports the following YAML properties: TCP: # The TCP port of the peer # NOTE: a port number of 0 *disables* TCP # # Default: 8192 port: <int> # Depth of the queue up to the next module # zero (default) picks a suitable value. outQueueDepth: <int> # Priority of the TCP RX thread. A number # bigger than zero must be a valid pthread # priority and tries to engage a real-time # scheduler. If this fails, CPSW falls back # to the default scheduler. # # Default: 0 threadPriority: <int> #### 2.8.2 Protocol Multiplexing There are two layers of protocol-multiplexers available in the stack: messages may be routed based on TDEST information and in addition, SRP transactions may be multiplexed by virtual channel: Items in brackets [] represent protocol modules, (*) are endpoints where children of NetIODev (itself in {}) are attached. { NetIODev Class } [ UDP ] [ RSSI ] [ depack ] [ TDEST Mux-Demux ] / | \ / | \ TDEST_0 TDEST_1 TDEST_2 | | | (*) (*) [ SRP ] Field Field | [ SRP VC Mux] / | \ VC0 VC1 VC2 | | | (*) (*) (*) MMIODev MMIODev MMIODev The diagram shows a configuration with a `TDEST` multiplexer which has three different `TDEST`s configured. `TDEST_0` and `TDEST_1` are used as raw (non-SRP) endpoints, e.g., to attach a 'Stream'ing interface (implemented by 'Field'). `TDEST_2` has an SRP module layered on top, followed by a `SRP_VC` multiplexer which has three endpoints (`VC0`, `VC1` and `VC2`) configured. In total, there are 5 children of the NetIODev. The SRP VC multiplexer uses some bits in the SRP transaction ID for routing transactions. Its operation is entirely transparent to firmware and no extra firmware features are used. The benefit is the following: all SRP transactions to a single VirtualChannel are strictly synchronous in order to guarantee a defined order in which transactions are executed. However, if the user has multiple independent subdevices then these could all be attached to different VC channels and then be accessed in parallel. From the above it is obvious that children of NetIODev can share parts of the protocol stack. It is necessary, however, that the common parts are configured identically. E.g., it is not possible to have two children talking to the same UDP port with one using RSSI and the other child not using RSSI. Here is the YAML file describing the above configuration: commonConfig: &commonConfig UDP: port: 8192 RSSI: ~ TDESTMux: ~ srpConfig: &srpConfig <<: *commonConfig SRP: protocolVersion: SRP_UDP_V3 TDESTMux: TDEST: 2 SRPMux: ~ root: class: NetIODev children: stream0: class: Field at: <<: *commonConfig TDESTMux: TDEST: 0 stream1: class: Field at: <<: *commonConfig TDESTMux: TDEST: 1 mmio0: class: MMIODev size: 0x1000 at: <<: *srpConfig SRPMux: virtualChannel: 0 mmio1: class: MMIODev size: 0x1000 at: <<: *srpConfig SRPMux: virtualChannel: 1 ... (mmio2 not shownN)