RTOS CAN API requirements

Some time ago we discussed that I should put together a list of requirements for a CAN API that has to be implemented in NuttX to make it usable with UAVCAN implementations without the need to access the underlying CAN hardware bypassing the operating system’s abstractions. These requirements are actually specific neither to UAVCAN nor to NuttX – any high-level CAN protocol is likely to have similar requirements or to make similar assumptions regardless of the underlying RTOS. Most CAN APIs provided by RTOSes that I’ve seen tend to be quite limited in their capabilities, sometimes even defective, which often makes them unsuitable for use in highly deterministic applications, driving application designers to write their own CAN drivers instead of relying on the services provided by RTOS.

The following list sets out basic requirements that need to be met by the RTOS API to make it useful for real-time CAN applications. The list derives from @jonathan.challinger’s post about the CAN API in ChibiOS because its objectives are similar.

  • Avoidance of the inner priority inversion problem. If the transmit mailboxes are filled with low-priority frames and the bus is saturated, a higher-priority frame cannot transmit. The solution is described in the UAVCAN Specification, section 4.2.4.3 “Inner priority inversion”. A compliant implementation is available in the Libcanard driver for STM32 (also see Libuavcan’s drivers and API).

  • CAN frames that share the same CAN ID field shall be transmitted in the FIFO order. This is usually the default behavior but the driver shall provide a hard guarantee.

  • All received frames shall be accurately timestamped. The required accuracy is application-defined.

UAVCAN timestamps are in real time (i.e. SI seconds or a submultiple thereof) as the UAVCAN transport layer has timing requirements specified in duration/seconds (i.e. A real-time clock is not required). Many CAN peripherals default to can bit-timing for hardware timestamps. As such, the integrator should evaluate a peripheral’s ability to provide timestamps that are SI seconds or a submultiple of when selecting hardware for a UAVCAN system.

  • Some transmitted frames shall be accurately timestamped. This feature is usually implemented via loopback: if the application needs a frame to be timestamped, it requests the driver to send it back after it is transmitted. Returned loopback frames are differentiated from regular received frames with the help of a dedicated flag. The reception timestamp set for a loopback frame represents the time when it was transmitted. This logic is implemented in SocketCAN, Libuavcan, PyUAVCAN, and many other CAN stacks.

  • Transmit frames that could not be transmitted by the specified deadline shall be automatically discarded by the driver. The deadline shall be specified individually per CAN frame.

As “transmitted" means “put on-bus and acknowledged” there are limitations on some platforms which impede this. For example, SocketCAN doesn’t guarantee that the message was put on bus when you write to the socket and doesn’t provide a way to cancel a message. For these platforms the expected performance of UAVCAN will be degraded and defects are possible where a multi-part message is transmitted with a significant enough delay that the transfer ID collides with a newer multi-part message on the same port (the expectation is that the CRC check for the multi-part message will fail in this case but integrity guarantees are weakened none-the-less).

Page @iain.galloway @noxuz @scottdixon. I recommend bringing these requirements to the attention of people who are maintaining the CAN stack in NuttX.

Transmit frames that could not be transmitted by the specified deadline shall be automatically discarded by the driver. The deadline shall be specified individually per CAN frame.

I believe this requirement can be limited by some platforms depending on the definition of “transmitted.” If this means, as it should, put on-bus and acknowledged then there are limitations on some platforms that can prevent this. For example, if you are using SocketCAN you aren’t guaranteed that the message was put on bus when you write to the socket but you can’t cancel it after you do.

All received frames shall be accurately timestamped

We don’t discuss capacity or units here. For example; 16-bit, microsecond timestamps are useless but 16-bit, second timestamps are usable if the overflow is properly handled. Similarly we expect timestamps in units relative to seconds (to calculate the transfer timeout). Given that a lot of MCUs default to 16-bit CAN bit time timestamps it’s an important point to call out.

These are good points. I just turned my original post into a wiki. Can you please edit your suggestions into it?

done