Homepage GitHub

On CAN adapters and stateless interfaces

(Pavel Kirienko) #1

As a practical experiment on the applicability of DSDL, I have designed a namespace defining the interface to an abstract UAVCAN-interfaced CAN adapter. It is not supposed to be included in the standard namespace since it’s too application-specific; rather, suppose that these definitions are contained in a vendor-specific namespace. The exact use I have in mind for these definitions is irrelevant as of now so I am not going to discuss that; think that the business requirements are to make a generic bridge.

The interface is designed to be fully stateless (one side does not make assumptions about the state of the other side) and is based on the continuous exchange of two messages between the “master” node (the user of the interface bridge) and the “bridge”/“adapter” node itself:

  • Idempotent command message type Configuration, published periodically by the “master”.
  • Informative status message type Status, published periodically by the bridge.

Such message-based idempotent interface is desirable because it is self-recovering against loss of state on the bridges (e.g., due to power cycling) and scales automatically to any number of bridges working in lockstep (modular redundancy).

The proposed types define the management interface only. The application data is transferred via separate topics of type uavcan.metatransport.can.Frame.1.0, which is a very new experimental message type that I’m planning to merge into the standard namespace soon as a part of the ongoing work on the logging feature (the link points to PyUAVCAN but the feature is obviously supposed to be implementation-agnostic).

One thing I noticed is that having complex data type definitions with a deep level of nesting in one namespace directory gets messy fast. When you’re looking at the directory, the structure looks flat and it’s hard to grasp the purpose of the namespace at a glance:

Perhaps in the future, we should look at having some means of defining simple (anonymous?) types in-place or expressing the structure at the directory level.


# Generic CAN adapter configuration message.
# This is a top-level type in this namespace.
# The adapter configuration interface is designed to be fully idempotent. The "master" (a node that controls the
# adapter) publishes this configuration message periodically; additional out-of-band ad-hoc messages may be
# inserted as needed. The controlled adapter(s) subscribes to the configuration messages, and whenever a
# new message is received, it ensures that its current configuration matches that of the message, otherwise
# the adapter updates its configuration accordingly. Shall an error occur, it is reported via the Status
# message asynchronously.
# In this description, "adapter" and "bridge" are used interchangeably. The imperative "open the channel" means
# that the adapter is to enter the active mode where it is bridging the CAN frames between the CAN bus and this
# UAVCAN interface; likewise, "close the channel" means the return to the idle state, which is the default state.
# While idle, the adapter is required to avoid any activities on the bus.
# If the channel is already open and the new message commands another non-idle configuration with the
# same bit rate, the adapter shall establish the new configuration without losing any incoming frames
# or interrupting the transmission of outgoing frames. Practically, this means that the channel should
# not be closed while the reconfiguration is in progress. If the new bit rate value is different, the
# adapter is allowed to close the channel before establishing the new configuration. If the new configuration
# cannot be applied, the adapter should close the channel and update the result code accordingly.
# The adapter shall be able to process a continuous stream of such messages without any adverse effects on its
# data throughput. A viable tactic is to check each received message against the current configuration early
# and drop (ignore) messages which command the configuration that is already established.
# A CAN adapter compatible with this subprotocol will have the following subjects:
# Subscriptions:
#   - Outgoing frame subject of type uavcan.metatransport.can.Frame.
#     The adapter emits every frame received from this subject to the CAN bus.
#     The timestamp of each frame contains the transmission deadline.
#     If the timestamp is UNKNOWN (zero), the behavior is implementation-defined.
#     Transmission of frames whose deadline is exceeded shall be aborted.
#     The accuracy of deadline tracking is implementation-defined.
#     Technically, the edge case where no deadline tracking is performed at all also fits this requirement
#     (infinitely low accuracy).
#     Unsupported frames (e.g. CAN FD on a classic CAN adapter) shall be dropped and reported via the feedback
#     interface (see the dedicated section).
#     The handling of CAN FD frames where the number of data bytes cannot be represented by a valid DLC value
#     is implementation-defined.
#   - Standard time synchronization service uavcan.time.Synchronization. The adapter is a time sync slave.
#     This feature ensures consistent timing information across the system.
#     The behavior of the adapter when the time synchronization data is not available is implementation-defined.
# Publications:
#   - Incoming frame and error subject of type uavcan.metatransport.can.Frame.
#     Every frame received from the bus is published via this subject; also, this subject carries
#     bus error notifications in the form of a CAN frame message where the manifestation is set to "Error".
#   - Feedback frame subject of type uavcan.metatransport.can.Frame.
#     Every successfully transmitted frame is republished here with the timestamp value indicating
#     the moment of time when the frame was delivered to the bus.
#     Every expired and unsupported frame is also republished here with timestamp value being UNKNOWN (zero).
#   - Bridge status message of type Status defined in this namespace. Read its description for more info.

# Physical layer configuration parameters.
PhysicalConfig.1.0 physical_config

@assert _offset_ % 8 == {0}


# Empty array instructs the adapter to reject all frames.
# This field should be ignored if the physical layer configuration commands the channel to be closed.
# Silent mode with an empty filter configuration should not be used; otherwise, the behavior is implementation-defined.
# If the underlying hardware does not implement the required number of filters, the missing filters (or all of them)
# shall be implemented by the adapter's software/firmware.
uint6 MAX_FILTERS = 32
FilterConfig.1.0[<=MAX_FILTERS] filter_config

@assert _offset_ % 8 == {0}
@assert _offset_.max / 8 <= 313


# Generic CAN adapter status message.
# This is a top-level type in this namespace.
# This message is to be published periodically; the recommended frequency is 1 Hz or higher.
# It is recommended to additionally publish this message out-of-band (perhaps at a lower priority value)
# immediately after each reconfiguration.

# This is intended for adapter diagnostics. If the time is not synchronized, this value should be UNKNOWN (zero).
uavcan.time.SynchronizedTimestamp.1.0 timestamp

# The error state per the CAN specification obtained directly from the CAN controller hardware.
FaultConfinementStatus.1.0 fault_confinement_status

# The counters shall be reset back to zero when the channel is opened.
FrameCounters.1.0    frame_counters
BusErrorCounters.1.0 bus_error_counters

@assert _offset_ % 8 == {0}

# Incremented by one whenever the adapter receives a new configuration message where the commanded
# configuration is different from the current configuration. Never reset while the adapter is running.
uint32 reconfiguration_count

# Approximate mean CAN bus utilization, in percent, over the last second. The accuracy is implementation-defined.
# The estimate shall reflect the true state of the medium regardless of the acceptance filter configuration.
# The first value is the total bus utilization.
# The second value is the contribution to the total utilization by the bridge itself.
# The following invariants apply: 100% >= total >= tx >= 0%. Values above 100% shall be attributed to an
# estimation error and to be treated as 100%.
uint7 bus_load_percent
uint7 bus_load_contributed_percent

@assert _offset_ % 8 == {0}

# Physical layer configuration parameters.
# These are not the commanded values but the actual properties configured by the adapter.
# For example, if the requested sample point location is 80% but the adapter could only configure 83%,
# the latter value will be reported here.
PhysicalConfig.1.0 physical_config

@assert _offset_ % 8 == {0}


# The result of the last reconfiguration.
# Normally, if the channel is open, the result should be SUCCESS, since the channel should be closed
# on bad configuration.
# If the channel has been commanded to be closed, the result would be SUCCESS, since closing can't fail.
ConfigResult.1.0 config_result

@assert _offset_ % 8 == {0}
@assert _offset_.count == 1     # All preceding fields are fixed-size.
@assert _offset_.max / 8 <= 124


# CAN acceptance filter configuration.
# In order to accept both extended and base frames, use two filters.
# The interface does not differentiate between RTR and data frames - both accepted if the ID matches.


BaseFilterConfig.1.0     base
ExtendedFilterConfig.1.0 extended

@assert _offset_ == {16 * 2, 32 * 2}


# 11-bit acceptance filter configuration.

uint11 identifier

uint11 mask


# 29-bit acceptance filter configuration.

uint29 identifier

uint29 mask


# Low-level CAN bus errors detected by the CAN controller hardware. One increment per error.

# Bad frame format.
uint48 form

# A transmitted frame has not been acknowledged by anyone.
uint48 ack

# The CAN controller tried to send a dominant bit, but the monitored value was recessive.
uint48 bit_dominant

# The CAN controller tried to send a recessive bit, but the monitored value was dominant.
# When the described condition occurs during the arbitration phase, this counter shall not be incremented.
uint48 bit_recessive

# The CRC value of a received frame did not match.
uint48 crc



# The outcome of the last configuration change.

uint8 value

# The new configuration has been applied successfully.
uint8 SUCCESS = 0

# The specified bit rate value cannot be configured or is invalid (e.g., zero).
# Observe that there is no counterpart for the bit sample point location because the adapter shall silently
# round the configured value to the nearest reachable value; i.e., can't fail.

# The initialization timeout is implementation-defined.
# Per the CAN bus specification, a failed initialization may indicate that the bit rate is too low.

# Any other error not listed here.
uint8 OTHER_ERROR = 255


# Controller fault confinement status per the CAN (FD) specification.
# The values are obtained directly from the CAN controller hardware.

# This value is redundant because it can be deduced from the error counters;
# however, it is provided nevertheless to relieve the subscribers from implementing CAN-specific
# error handling logic.
uint2 state
uint2 STATE_ERROR_ACTIVE  = 0   # The normal state.
uint2 STATE_ERROR_PASSIVE = 2   # Limited transmission capabilities.
uint2 STATE_BUS_OFF       = 3   # Can't transmit at all.


# Transmit/receive error counters for nominal bit rate (classic CAN, non-data segments of CAN FD).
uint9 tec
uint8 rec


# Transmit/receive error counters for the data segment of CAN FD frames where the BRS flag is active.
uint9 tec_fd
uint8 rec_fd

@assert _offset_ % 8 == {0}


# CAN frame statistics.

uint48 in_success       # Valid CAN frames received from the bus (data or RTR).
uint48 in_overrun       # Received frames that had to be dropped due to buffer overruns.

uint48 out_success      # CAN frames successfully emitted onto the bus before the deadline has expired.
uint48 out_expired      # CAN frames that could not be transmitted before the deadline and have been aborted.
uint48 out_overrun      # Outgoing frames that had to be dropped due to buffer overruns.
uint48 out_unsupported  # Frames that could not be transmitted because they are not supported by the hardware.


# CAN physical layer configuration.
# This type is used both in the configuration message and in the status message; that is, it is used
# both to command a new configuration and to report the current configuration.


# The CAN controller mode, commanded or current.
uint2 mode

# Disconnect from the bus and put the CAN controller into the sleep mode. No frame forwarding will take place.
# The timing configs shall be zero/ignored.
uint2 MODE_IDLE = 0

# In the silent mode the CAN controller will never modulate dominant levels.
# Data transmission is impossible in the silent mode;
# frames submitted for transmission shall be assumed to have timed out on arrival.
uint2 MODE_SILENT = 1

# The mode value 2 is reserved for future use. If such value is encountered, the behavior is implementation-defined.

# In the normal mode, the bridge will perform regular bidirectional forwarding.
uint2 MODE_NORMAL = 3

# The timing configuration used for the nominal segment (classic CAN and non-data segments of CAN FD).
# This setting shall be valid unless the mode is idle.
TimingConfig.1.0 nominal_timing

# The timing configuration used for the data segments of CAN FD frames.
# If the bit rate is less than or equal the nominal timing, only the nominal bit rate is (to be) be used
# for all frames (the BRS flag will never be activated).
# Adapters that do not support CAN FD shall set this field to zero/ignore.
TimingConfig.1.0 fd_timing

@assert _offset_ % 8 == {0}


# CAN timing configuration.

# In bits per second.
uint24 bit_rate


# Desired location of the bit sample point, in percent of the bit period.
# The bridge should strive to configure the closest reachable value. In the extreme case, that would be only
# one pre-programmed value, and this setting would be ignored.
# If the value exceeds 100%, the behavior is implementation-defined.
uint7 sample_point_location_percent

# The recommended sample point location for the nominal bit rate per the UAVCAN specification.

@assert _offset_ % 8 == {0}

(Pavel Kirienko) #2

The pull request is up: