diff --git a/.vscode/settings.json b/.vscode/settings.json index 523aead..d0c0df4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -63,8 +63,10 @@ "attrib", "Autoconfigured", "autodetect", + "AUTOSAR", "AWG", "Backus-Naur", + "baremetal", "baz", "BAZ", "BCI", @@ -79,11 +81,14 @@ "bootloaders", "bootup", "broadcasted", + "bytearray", + "bytewise", "canadensis", "CANaerospace", "CANopen", "Cardinality", "cassert", + "Castagnoli", "CCITT", "cfg", "CLI", @@ -106,11 +111,14 @@ "cstdint", "cyber", "Cyphal", + "datagram", + "datagrams", "Datasheet", "DCD", "dd", "de", "dec", + "Deconflicts", "deconstructed", "deduplicated", "deduplication", @@ -126,6 +134,7 @@ "dq", "driverless", "Dronecode", + "DSCP", "DSDL", "DTH", "DTID", @@ -145,6 +154,8 @@ "evolvable", "extensibility", "extensionpack", + "FFFFU", + "FFFFUL", "fn", "foo", "FOO", @@ -180,6 +191,7 @@ "IEC", "IETF", "iface", + "IGMP", "ijk", "imp-lem-en-ta-ti-on-de-fin-ed", "Implementers", @@ -187,6 +199,7 @@ "Inkscape", "intdd", "integrators", + "internetwork", "interoperate", "Intra", "intravehicular", @@ -195,6 +208,7 @@ "invariants", "iostream", "IP", + "ISCSI", "JST", "JTAG", "kbit", @@ -220,6 +234,7 @@ "Mbit", "Mbps", "MDL", + "memoryview", "merchantability", "metadata", "metaserializable", @@ -235,6 +250,7 @@ "MTTF", "MTU", "mul", + "multicasting", "mycorrhizal", "NACK", "NAK", @@ -248,6 +264,7 @@ "NMEA", "NMI", "nodiscard", + "noexcept", "nq", "ns", "nul", diff --git a/specification/Cyphal_Specification.tex b/specification/Cyphal_Specification.tex index 2536cc5..b5d19e8 100644 --- a/specification/Cyphal_Specification.tex +++ b/specification/Cyphal_Specification.tex @@ -118,5 +118,6 @@ \section*{Limitation of liability} \input{transport/transport.tex} \input{application/application.tex} \input{sdt/sdt.tex} +\input{appendices/appendices.tex} \end{document} diff --git a/specification/appendices/appendices.tex b/specification/appendices/appendices.tex new file mode 100644 index 0000000..4c29fbd --- /dev/null +++ b/specification/appendices/appendices.tex @@ -0,0 +1,2 @@ +\appendix +\input{appendices/crc.tex} diff --git a/specification/appendices/crc.tex b/specification/appendices/crc.tex new file mode 100644 index 0000000..1f93b87 --- /dev/null +++ b/specification/appendices/crc.tex @@ -0,0 +1,258 @@ +\chapter{CRC algorithm implementations} + +\section{CRC-16/CCITT-FALSE}\label{sec:appendix_crc16ccitt_false} + +This algorithm is also known as CRC-16/AUTOSAR or CRC-16/IBM-3740. +Not to be confused with CRC-16/KERMIT. + +This algorithm has the following parameters: +\begin{itemize} + \item width: 16 bits; + \item polynomial: $\mathrm{1021}_{16}$; + \item initial value: $\mathrm{FFFF}_{16}$; + \item input not reflected; + \item output not reflected; + \item no output XOR; + \item the native byte order is big endian. +\end{itemize} + +The value for the input sequence $\left(49, 50, \ldots, 56, 57\right)$ is $\mathrm{29B1}_{16}$. + +\subsection{C++, bitwise} + +\begin{samepage} +\begin{minted}{cpp} +#include +#include +#include + +class CRC16_CCITT_False final +{ +public: + void add(const std::uint8_t byte) + { + value_ ^= static_cast(byte) << 8U; + for (std::uint8_t bit = 8; bit > 0; --bit) + { + if ((value_ & 0x8000U) != 0) + { + value_ = (value_ << 1U) ^ 0x1021U; + } + else + { + value_ = value_ << 1U; + } + } + } + + void add(const std::uint8_t* bytes, std::size_t length) + { + while (length --> 0) + { + add(*bytes++); + } + } + + [[nodiscard]] std::uint16_t get() const { return value_; } + + [[nodiscard]] std::array getBytes() const noexcept + { + const auto x = get(); + return {static_cast(x >> 8U), static_cast(x & 0xFFU)}; + } + +private: + std::uint16_t value_ = 0xFFFFU; +}; +\end{minted} +\end{samepage} + +\subsection{Python, bytewise} + +\begin{samepage} +\begin{minted}{python} +class CRC16CCITT: + def __init__(self) -> None: + self._value = 0xFFFF + + def add(self, data: bytes | bytearray | memoryview) -> None: + val = self._value + for x in data: + val = ((val << 8) & 0xFFFF) ^ self._TABLE[(val >> 8) ^ x] + self._value = val + + def check_residue(self) -> bool: + return self._value == 0 + + @property + def value(self) -> int: + return self._value + + @property + def value_as_bytes(self) -> bytes: + return self.value.to_bytes(2, "big") + + _TABLE = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, + ] +\end{minted} +\end{samepage} + +\newpage +\section{CRC-32C}\label{sec:appendix_crc32c} + +This algorithm is also known as CRC-32/ISCSI, CRC-32/CASTAGNOLI, CRC-32/BASE91-C, or CRC-32/INTERLAKEN. + +This algorithm has the following parameters: +\begin{itemize} + \item width: 32 bits; + \item polynomial: $\mathrm{1EDC6F41}_{16}$; + \item initial value: $\mathrm{FFFFFFFF}_{16}$; + \item input reflected; + \item output reflected; + \item output XOR: $\mathrm{FFFFFFFF}_{16}$; + \item residue: $\mathrm{B798B438}_{16}$ before output XOR, $\mathrm{48674BC7}_{16}$ after output XOR; + \item the native byte order is little endian. +\end{itemize} + +The value for the input sequence $\left(49, 50, \ldots, 56, 57\right)$ is $\mathrm{E3069283}_{16}$. + +\subsection{C++, bitwise} + +\begin{samepage} +\begin{minted}{cpp} +#include +#include +#include + +class CRC32C final +{ +public: + static constexpr std::size_t Size = 4; + + void update(const std::uint8_t b) noexcept + { + value_ ^= static_cast(b); + for (auto i = 0U; i < 8U; i++) + { + value_ = ((value_ & 1U) != 0) ? ((value_ >> 1U) ^ ReflectedPoly) : (value_ >> 1U); + } + } + + [[nodiscard]] std::uint32_t get() const noexcept { return value_ ^ Xor; } + + [[nodiscard]] std::array getBytes() const noexcept + { + const auto x = get(); + return { + static_cast(x >> (8U * 0U)), + static_cast(x >> (8U * 1U)), + static_cast(x >> (8U * 2U)), + static_cast(x >> (8U * 3U)), + }; + } + + [[nodiscard]] auto isResidueCorrect() const noexcept { return value_ == Residue; } + +private: + static constexpr std::uint32_t Xor = 0xFFFF'FFFFUL; + static constexpr std::uint32_t ReflectedPoly = 0x82F6'3B78UL; + static constexpr std::uint32_t Residue = 0xB798'B438UL; + + std::uint32_t value_ = Xor; +}; +\end{minted} +\end{samepage} + +\subsection{Python, bytewise} + +\begin{samepage} +\begin{minted}{python} +class CRC32C: + def __init__(self) -> None: + self._value = 0xFFFFFFFF + + def add(self, data: bytes | bytearray | memoryview) -> None: + val = self._value + for x in data: + val = (val >> 8) ^ self._TABLE[x ^ (val & 0xFF)] + self._value = val + + def check_residue(self) -> bool: + return self._value == 0xB798B438 # Checked before the output XOR is applied. + + @property + def value(self) -> int: + return self._value ^ 0xFFFFFFFF + + @property + def value_as_bytes(self) -> bytes: + return self.value.to_bytes(4, "little") + + _TABLE = [ + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, + ] +\end{minted} +\end{samepage} diff --git a/specification/application/functions.tex b/specification/application/functions.tex index fd02724..a906aac 100644 --- a/specification/application/functions.tex +++ b/specification/application/functions.tex @@ -302,7 +302,7 @@ \subsection{Internet/LAN forwarding interface} \DSDL{uavcan.internet.* --index-only} -\subsection{Meta-transport} +\subsection{Meta-transport}\label{sec:application_functions_metatransport} Data types defined in the namespace \DSDLReference{uavcan.metatransport} (see table~\ref{table:dsdl:uavcan.metatransport}) diff --git a/specification/cyphaldoc.cls b/specification/cyphaldoc.cls index 75cfe73..107bf82 100644 --- a/specification/cyphaldoc.cls +++ b/specification/cyphaldoc.cls @@ -23,7 +23,7 @@ \RequirePackage{etoolbox} \RequirePackage{amstext} \RequirePackage[detect-all]{siunitx} -\RequirePackage[font={bf}]{caption} +\RequirePackage[font={bf},hypcap=false]{caption} \RequirePackage{threeparttablex} \RequirePackage{tabu} \RequirePackage{makecell} @@ -297,10 +297,6 @@ } \begin{ThreePartTable} }{ - % If hypcap is enabled, the package "caption" yields a warning saying that the option will be ignored, - % ostensibly because we don't have the hypcap package loaded (we don't need this behavior). - % RTFM https://www.texlive.info/CTAN/macros/latex/contrib/caption/caption-rus.pdf. - \captionsetup{hypcap=false} % https://tex.stackexchange.com/questions/3243/why-should-a-table-caption-be-placed-above-the-table \captionof{table}{#2} \end{ThreePartTable} diff --git a/specification/introduction/introduction.tex b/specification/introduction/introduction.tex index 1cd6420..fed1c74 100644 --- a/specification/introduction/introduction.tex +++ b/specification/introduction/introduction.tex @@ -204,7 +204,15 @@ \section{Referenced sources} \item IEEE Std 1003.1 --- IEEE Standard for Information Technology -- Portable Operating System Interface (POSIX) Base Specifications. - \item IETF RFC2119 --- Key words for use in RFCs to Indicate Requirement Levels. + \item IETF RFC~768 --- User Datagram Protocol. + \item IETF RFC~791 --- Internet Protocol. + \item IETF RFC~1112 --- Host extensions for IP multicasting. + \item IETF RFC~2119 --- Key words for use in RFCs to Indicate Requirement Levels. + \item IETF RFC~2365 --- Administratively Scoped IP Multicast. + \item IETF RFC~2474 --- Definition of the Differentiated Services Field (DS Field) in the IPv4 and IPv6 Headers. + \item IETF RFC~8085 --- UDP Usage Guidelines. + \item IETF RFC~8900 --- IP Fragmentation Considered Fragile. + \item IETF RFC~8837 --- Differentiated Services Code Point (DSCP) Packet Markings for WebRTC QoS. \item ISO 11898-1 --- Controller area network (CAN) --- Part 1: Data link layer and physical signaling. \item ISO 11898-2 --- Controller area network (CAN) --- Part 2: High-speed medium access unit. @@ -230,6 +238,8 @@ \subsection{v1.0 -- work in progress} \item The constraint on DSDL namespaces being defined in a single folder was removed. Namespaces can be hosted across multiple repositories and code can be generated from a union of said folders. + + \item Cyphal/UDP transport specification has been introduced. \end{itemize} \subsection{v1.0-beta -- Sep 2020} diff --git a/specification/sdt/sdt.tex b/specification/sdt/sdt.tex index bb12c73..3890401 100644 --- a/specification/sdt/sdt.tex +++ b/specification/sdt/sdt.tex @@ -4,7 +4,7 @@ \chapter{List of standard data types}\label{sec:sdt} The standard root namespace is named \texttt{uavcan}, not \texttt{cyphal}, for historical reasons. }. The source text of the DSDL data type definitions provided here is also available via the -official project website at \href{http://uavcan.org}{uavcan.org}. +official project website at \mbox{\url{https://opencyphal.org}}. Regulated non-standard definitions\footnote{% I.e., public definitions contributed by vendors and other users diff --git a/specification/transport/can/can.tex b/specification/transport/can/can.tex index 3123ccb..b30151f 100644 --- a/specification/transport/can/can.tex +++ b/specification/transport/can/can.tex @@ -370,52 +370,7 @@ \subsubsection{Transfer CRC}\label{sec:transport_can_transfer_crc} This is the native byte order for this CRC function. }. -The CRC function is the standard CRC-16-CCITT: -initial value $\mathrm{FFFF}_{16}$, polynomial $\mathrm{1021}_{16}$, -not reversed, no output XOR, big endian. -The value for an input sequence $\left(49, 50, \ldots, 56, 57\right)$ is $\mathrm{29B1}_{16}$. -The following code snippet provides a basic implementation of the transfer CRC algorithm in C++ -(LUT-based alternatives exist). - -\begin{samepage} -\begin{minted}{cpp} -#include -#include - -/// Cyphal/CAN transfer CRC function implementation. License: CC0, no copyright reserved. -class CANTransferCRC -{ - std::uint16_t value_ = 0xFFFFU; - -public: - void add(const std::uint8_t byte) - { - value_ ^= static_cast(byte) << 8U; - for (std::uint8_t bit = 8; bit > 0; --bit) - { - if ((value_ & 0x8000U) != 0) - { - value_ = (value_ << 1U) ^ 0x1021U; - } - else - { - value_ = value_ << 1U; - } - } - } - - void add(const std::uint8_t* bytes, std::size_t length) - { - while (length-- > 0) - { - add(*bytes++); - } - } - - [[nodiscard]] std::uint16_t get() const { return value_; } -}; -\end{minted} -\end{samepage} +The transfer CRC function is \textbf{CRC-16/CCITT-FALSE} (section~\ref{sec:appendix_crc16ccitt_false}). \subsection{Examples} diff --git a/specification/transport/transport.tex b/specification/transport/transport.tex index 0ab0f0d..b0e7c01 100644 --- a/specification/transport/transport.tex +++ b/specification/transport/transport.tex @@ -14,6 +14,11 @@ \chapter{Transport layer}\label{sec:transport} if they are found to map poorly onto the newly supported protocols. Such changes are guaranteed to preserve full backward compatibility of the existing concrete transports. +This chapter defines first-class transports only. +For matters related to tunneling transport frames over other transports, +refer to section~\ref{sec:application_functions_metatransport}. + % Please keep \clearpage in front of every section to enforce clear separation! \clearpage\input{transport/abstract.tex} \clearpage\input{transport/can/can.tex} +\clearpage\input{transport/udp/udp.tex} diff --git a/specification/transport/udp/udp.tex b/specification/transport/udp/udp.tex new file mode 100644 index 0000000..38b1633 --- /dev/null +++ b/specification/transport/udp/udp.tex @@ -0,0 +1,390 @@ +\section{Cyphal/UDP (experimental)}\label{sec:transport_udp} + +\hyphenation{Cyphal/UDP} % Disable hyphenation. + +\subsection{Overview} + +This section specifies a concrete transport based on the UDP/IPv4 protocol\footnote{% + Support for IPv6 may appear in future versions of this specification. +}, as specified in IETF RFC~768. +\textbf{ + As of this version, the Cyphal/UDP specification remains experimental. + Breaking changes affecting wire compatibility are possible. +} + +Cyphal/UDP is a first-class transport intended for low-latency, +high-throughput intravehicular Ethernet networks with complex topologies, +which may be switched, multi-drop, or mixed. +A network utilizing Cyphal/UDP can be built with standards-compliant commercial off-the-shelf +networking equipment and software. + +Cyphal/UDP relies exclusively on IP multicast traffic defined in IETF RFC~1112 for all communication\footnote{% + For rationale, refer to \url{https://forum.opencyphal.org/t/1765}. +}. +The entirety of the session specifier (section~\ref{sec:transport_session_specifier}) +is reified through the multicast group address. +The transfer-ID, transfer priority, and the multi-frame transfer reassembly metadata are allocated in the +Cyphal-specific fixed-size UDP datagram header. +In this transport, a UDP datagram represents a single Cyphal transport frame. +All UDP datagrams are addressed to the same, fixed, destination port, +while the source port and the source address bear no relevance for the protocol and thus can be arbitrary. + +\begin{CyphalSimpleTable}{Cyphal/UDP transport capabilities\label{table:transport_udp_capabilities}}{|l X l|} + Parameter & Value & References \\ + + Maximum node-ID value & + 65534 (16 bits wide). & + \ref{sec:basic} \\ + + Transfer-ID mode & + Monotonic, 64 bits wide. & + \ref{sec:transport_transfer_id} \\ + + Number of transfer priority levels & + 8 (no additional levels). & + \ref{sec:transport_transfer_priority} \\ + + Largest single-frame transfer payload & + % 480 bytes = 508 bytes minus 24 bytes for the Cyphal/UDP header minus 4 bytes for the transfer CRC. + % 65479 bytes = 65507 bytes minus 24 bytes for the Cyphal/UDP header minus 4 bytes for the transfer CRC. + Implementation-defined, but not less than 480~bytes and not greater than 65479~bytes. & + \ref{sec:transport_transfer_payload} \\ + + Anonymous transfers & + Available. & + \ref{sec:transport_route_specifier} \\ +\end{CyphalSimpleTable} + +\subsection{UDP/IP endpoints and routing} + +\subsubsection{Endpoints} + +Transmission of a Cyphal/UDP transport frame is performed by sending a suitably constructed UDP datagram +to the destination IP multicast group address computed from the session specifier +(section~\ref{sec:transport_session_specifier}) +as shown in figure~\ref{fig:transport_udp_multicast_group_address} +with the fixed destination port number \textbf{9382}\footnote{% + % Update this footnote when the EXPERIMENTAL status is lifted. + The port number may change if/when the protocol is registered with IANA, + unless it is stabilized in its current form. +}. + +\begin{figure}[H] + \centering + $$ + \overbrace{ + \underbrace{ + \texttt{\huge{1110}} + }_{\substack{\text{RFC~1112} \\ \text{multicast} \\ \text{prefix}}}% + \underbrace{ + \texttt{\huge{1111}} + }_{\substack{\text{RFC~2365} \\ \text{administrative} \\ \text{scope}}}% + }^{\text{Most significant octet}}% + \texttt{\huge{.}}% ---------------------------------------- + \overbrace{ + \underbrace{ + \texttt{\huge{0}} + }_{\substack{\text{RFC~2365} \\ \text{reserved} \\ \text{range}}}% + \underbrace{ + \texttt{\huge{0}} + }_{\substack{\text{address} \\ \text{version}}}% + \underbrace{ + \texttt{\huge{00000}} + }_{\substack{\text{reserved} \\ \text{keep zero}}}% + \underbrace{ + \texttt{\huge{Z}} + }_{\substack{\text{service,} \\ \text{not} \\ \text{message}}}% + }^{\text{3rd octet}}% + \texttt{\huge{.}}% ---------------------------------------- + \underbrace{ + \overbrace{\texttt{\huge{XXXXXXXX}}}^{\text{2nd octet}} + \texttt{\huge{.}} + \overbrace{\texttt{\huge{XXXXXXXX}}}^{\text{Least significant octet}} + }_{\substack{\text{\textbf{if Z:} destination node-ID} \\ \text{\textbf{else:} subject-ID + reserved}}}% + $$ + Numbers given in base-2. + \caption{IP multicast group address structure\label{fig:transport_udp_multicast_group_address}} +\end{figure} + +\begin{CyphalSimpleTable}[wide]{ + IP multicast group address bit fields\label{table:transport_udp_multicast_group_address} +}{|l l l l X|} + Field & Offset & Width & Value & Description \\ + + RFC~1112 multicast prefix & + 28 & 4 & $1110_2$ & + \\ + + RFC~2365 scope & + 24 & 4 & $1111_2$ & + Selects the administratively scoped range 239.0.0.0/8 per RFC~2365 + to avoid collisions with well-known multicast groups. \\ + + RFC~2365 reserved range & + 23 & 1 & $0$ & + Selects the ad-hoc defined range 239.0.0.0/9 per RFC~2365. \\ + + Cyphal/UDP address version & + 22 & 1 & $0$ & + Deconflicts this layout with future revisions. \\ + + Reserved & + 17 & 5 & $00000_2$ & + May be used for domain-ID segregation in future versions. \\ + + Z: service, not message & + 16 & 1 & any & + Set for service transfers, cleared for message transfers. \\ + + X if Z: destination node-ID & + 0 & 16 & $[0, 65534]$ & + The destination node-ID of the current service transfer. \\ + + X if not Z: reserved & + 13 & 3 & $0$ & + May be used to enlarge the subject-ID field in future versions. \\ + + X if not Z: subject-ID & + 0 & 13 & any & + The subject-ID of the current message transfer. \\ +\end{CyphalSimpleTable} + +\begin{remark} + Freezing (at least) the 9 most significant bits of the multicast group address ensures that + the variability is confined to the 23 least significant bits of the address only, + which is desirable because the IPv4 Ethernet MAC layer does not differentiate beyond the + 23 least significant bits of the multicast group address. + That is, addresses that differ only in the 9 MSb collide at the MAC layer, + which is unacceptable in a real-time system; see RFC~1112 section 6.4. + Without this limitation, an engineer designing a network might inadvertently create a configuration + that causes MAC-layer collisions which may be difficult to detect. +\end{remark} + +A subscriber to certain Cyphal subjects will join the IP multicast groups corresponding to said subjects\footnote{% + For example, the multicast group address for subject 42 is 239.0.0.42. +}. +Likewise, a node that provides at least one RPC-service will join the IP multicast group corresponding to +its own node-ID\footnote{% + For example, the multicast group address for a service transfer with the destination node-ID of 42 is 239.1.0.42. + Observe that multicast groups are not differentiated by service-ID. +}. + +The IP address of a node bears no relevance for the protocol --- +multiple nodes may share the same IP address; likewise, a node may have more than one IP address. +Nodes on a Cyphal/UDP network are identified exclusively by their node-ID value\footnote{% + A node that is registered on an IP network (e.g., via DHCP) + still needs to obtain a node-ID value to participate in a Cyphal/UDP network. + This may be done either through manual assignment or by using the plug-and-play node-ID allocation service + (section~\ref{sec:application_functions_pnp}). +}. +The set of valid node-ID values for Cyphal/UDP is $[0, 65534]$. +Value 65535 is reserved to represent both the broadcast and anonymous node-ID, depending on context. + +\begin{remark} + Per RFC~1112, in order to emit multicast traffic, + a limited level-1 implementation without the full support of IGMP and multicast-specific packet handling policies + is sufficient. + Thus, trivial nodes that are only required to publish messages on the network may be implemented + without the need for a full IGMP stack. + + The reliance on IP multicasting exclusively allows baremetal implementations to omit ARP support. +\end{remark} + +\begin{remark} + Due to the dynamic nature of the IGMP protocol, + a newly configured subscriber may not immediately receive data from the subject --- + a brief subscription initialization delay may occur + because the underlying IGMP stack needs to inform the router about its interest + in the specified multicast group by sending an IGMP membership report first. + Certain high-integrity applications may choose to rely on static switch configurations + to eliminate the subscription initialization delay. +\end{remark} + +\subsubsection{TTL} + +Sources of Cyphal/UDP traffic should set the packet TTL to 16 or higher. + +\begin{remark} + RFC~1112 prescribes a default TTL of 1, + but this is not sufficient as Cyphal/UDP networks may often exceed the diameter of 1 hop. +\end{remark} + +\subsubsection{QoS} + +The DSCP\footnote{RFC~2474} field of outgoing IP packets \emph{should} +be populated based on the Cyphal transfer priority level (section~\ref{sec:transport_transfer_priority}) +as specified\footnote{% + The recommended DSCP mapping is derived from RFC~8837. + The implementation of suitable network policies is outside of the scope of this document. + RFC~4594 provides a starting point for the design of such policies. +} in table \ref{table:transport_udp_priority}. +Implementations \emph{should} provide a means to override the default DSCP mapping per node\footnote{ + E.g., via configuration registers described in section~\ref{sec:application_functions_register}. +} by the system integrator (user). +All nodes in a Cyphal/UDP network \emph{shall} be configured such that the applied DSCP mapping +is consistent with the QoS policies implemented in the network\footnote{% + This requirement is intended to prevent inconsistent QoS treatment of Cyphal/UDP traffic and priority inversion. +}. + +\begin{CyphalSimpleTable}{ + Default mapping from Cyphal priority level to DSCP\label{table:transport_udp_priority} +}{|l l l l|} + Cyphal priority & Header priority value (sec. \ref{sec:transport_udp_payload}) & DSCP class & DSCP value \\ + Exceptional & 0 & DF & 0 \\ + Immediate & 1 & DF & 0 \\ + Fast & 2 & DF & 0 \\ + High & 3 & DF & 0 \\ + Nominal & 4 & DF & 0 \\ + Low & 5 & DF & 0 \\ + Slow & 6 & DF & 0 \\ + Optional & 7 & DF & 0 \\ +\end{CyphalSimpleTable} + +\subsection{UDP datagram payload format}\label{sec:transport_udp_payload} + +The layout of the Cyphal/UDP datagram payload header is shown in the following snippet in DSDL notation +(section~\ref{sec:dsdl}). +The payload header is followed by the payload data, which is opaque to the protocol. + +\begin{samepage} +\begin{minted}{python} +# This 24-byte header can be aliased as a C structure with each field being naturally aligned: +# +# uint8_t version; +# uint8_t priority; +# uint16_t source_node_id; +# uint16_t destination_node_id; +# uint16_t data_specifier_snm; +# uint64_t transfer_id; +# uint32_t frame_index_eot; +# uint16_t user_data; +# uint8_t header_crc16_big_endian[2]; + +uint4 version +# The version of the header format. This document specifies version 1. +# Packets with an unknown version number must be ignored. + +void4 + +uint3 priority +# The values are assigned from 0 (HIGHEST priority) to 7 (LOWEST priority). +# The numerical priority identifiers are chosen to be consistent with Cyphal/CAN. +# The mapping from this priority value to the DSCP value should be configurable; +# otherwise, see the recommended default mapping. + +void5 + +uint16 source_node_id +# The node-ID of the source node. +# Value 65535 represents anonymous transfers. + +uint16 destination_node_id +# The node-ID of the destination node. +# Value 65535 represents broadcast transfers. + +uint15 data_specifier +# If this is a message transfer, this value equals the subject-ID. +# If this is a service response transfer, this value equals the service-ID. +# If this is a service request transfer, this value equals 16384 + service-ID. + +bool service_not_message +# If true, this is a service transfer. If false, this is a message transfer. + +@assert _offset_ == {64} +uint64 transfer_id +# The monotonic transfer-ID value of the current transfer (never overflows). + +uint31 frame_index +# Zero for a single-frame transfer and for the first frame of a multi-frame transfer. +# Incremented by one for each subsequent frame of a multi-frame transfer. + +bool end_of_transfer +# True if this is the last frame of a multi-frame transfer, or a single-frame transfer. + +uint16 user_data +# Opaque application-specific data with user-defined semantics. +# Generic implementations should emit zero and ignore this field upon reception. + +uint8[2] header_crc16_big_endian +# CRC-16/CCITT-FALSE of the preceding serialized header data in the big endian byte order. +# Application of the CRC function to the entire header shall yield zero, otherwise the header is malformed. + +@assert _offset_ / 8 == {24} +@sealed # The payload data follows. +\end{minted} +\end{samepage} + +The header CRC function is \textbf{CRC-16/CCITT-FALSE}; +refer to section~\ref{sec:appendix_crc16ccitt_false} for further information. + +\begin{remark} + Certain states provided in the header duplicate information that is already available in the IP header + or the multicast group address. + This is done for reasons of unification of the header format with other standard transport layer definitions, + and to simplify the access to the transfer parameters that otherwise would be hard to reach above the + network layer, such as the DSCP value. + The latter consideration is particularly important for forwarding nodes. +\end{remark} + +\subsection{Transfer payload} + +After the transfer payload is constructed but before it is scheduled for transmission over the network, +it is appended with the transfer CRC field. +The transfer CRC function is \textbf{CRC-32C} (section~\ref{sec:appendix_crc32c}), +and its value is serialized in the little-endian byte order. +The transfer CRC function is applied to the entire transfer payload and only transfer payload. + +The transfer CRC is provided for all transfers, +including single-frame transfers and transfers with an empty payload\footnote{% + This provides end-to-end integrity protection for the transfer payload. +}. +An implementation receiving a transfer should verify the correctness of its transfer CRC. + +\begin{remark} + From the perspective of the multi-frame segmentation logic, the transfer CRC field is part of the transfer payload. + From the definition of the header format it follows that the transfer CRC can only be found at the end of + the packet if the \verb|end_of_transfer| bit is set, + unless the transfer CRC field has spilled over to the next frame + (in which case the frame would contain only the transfer CRC itself or the tail thereof). +\end{remark} + +\subsection{Maximum transmission unit} + +In this section, the maximum transmission unit (MTU) is defined as the maximum size of a UDP/IP datagram payload. + +This specification does not restrict the MTU of the underlying transport. +It is recommended, however, to avoid MTU values less than 508~bytes, +allowing applications to exchange up to $508 - 24 - 4 = 480$ bytes of payload in a single-frame transfer. +Limiting the MTU at this value allows nodes that do not transmit and/or receive transfers larger than 480~bytes +to omit support for multi-frame transfer decomposition and/or reassembly. + +As Cyphal provides native means of multi-frame transfer decomposition and reassembly +(section~\ref{sec:transport_transfer_payload}), +nodes emitting Cyphal/UDP traffic \emph{shall not} rely on IP fragmentation by +limiting the size of the UDP payload accordingly\footnote{ + This requirement is consistent with RFC~8085 and RFC~8900. + Transfers larger than the MTU limit will be transmitted as multi-frame Cyphal transfers. + The preference towards Cyphal fragmentation over IP fragmentation is to remove the limitation on the + maximum transfer size imposed by the UDP protocol and + to permit preemption of long transfers by higher-priority transfers. +}. +Support for IP fragmentation is optional for Cyphal/UDP receivers\footnote{% + Network equipment such as routers may perform IP fragmentation, + which is allowed as long as such behavior is opaque to the Cyphal/UDP end systems. +}. + +\subsection{Real-time and resource-constrained systems} + +It is anticipated that real-time and/or resource-constrained systems may implement Cyphal/UDP +based on custom UDP/IP and IGMP protocol stacks with a reduced feature set. +In particular, such systems may not support IP fragmentation, +ARP\footnote{Being multicast-based, Cyphal/UDP does not require ARP.}, +and ICMP. + +The networking equipment that such systems are connected to is recommended to not emit ICMP messages because: + +\begin{enumerate} + \item This increases the network load which can modify the timing of the system + and which may be considered an attack vector for some systems. + \item Cyphal/UDP nodes are not required to support ICMP and may therefore not be able to process ICMP messages. +\end{enumerate}