Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify CAN frame transmit order #221

Closed
jboynes opened this issue Nov 2, 2023 · 1 comment · Fixed by #222
Closed

Clarify CAN frame transmit order #221

jboynes opened this issue Nov 2, 2023 · 1 comment · Fixed by #222

Comments

@jboynes
Copy link
Contributor

jboynes commented Nov 2, 2023

API component

CAN

Description

Clarify the order in which CAN.write() will send frames with the same priority. Specifically, define whether buffered frames will be sent in transmit order, ordered by priority, or without an ordering guarantee.

if the frames are not buffered by the API implementation then there is no issue. However, if they are the behaviour is currently undefined. I'm raising this as an issue for the API as it's part of the contract between the user and an implementation.

This affects protocols such as RV-C or NMEA2000 where messages may require multiple frames to transmit. Mechanisms such as ISO-TP or NMEA's Fast Packet transmit message segments using the same CAN ID with sequencing information contained in the frame's data. Some devices are known to require segments be received in order, effectively requiring the sender to ensure they are transmitted in order. CAN's inherent prioritization by ID does not help as the ID is the same for all message segment frames.

There are a few ways this could be addressed:

  1. Define the transmit API to be unbuflfered placing the application in control.
  2. Only support in-order transmission
  3. Extend the API to allow the transmit ordering to be configured
  4. Extend the API to allow the application to manage the state of buffered messages to control ordering

The first is the simplest option and matches the current behaviour of the Renesas core. The API change would simply be to define that behaviour in documentation so application code can rely on it.

The second would allow an implementation to use buffering providing the transmission order can be guaranteed. This could be achieved with a software ring buffer or hardware transmit FIFO. The downside is that it would not support pre-emption, allowing a higher priority CAN message to be transmitted ahead of those that are buffered. However, an application that needs that could still implement it by handling the queues itself.

The third and fourth add complexity to this API that probably can't be justified as it would be closely coupled to hardware capabilities anyway; an application that needs it would be better served using the underlying hardware APIs directly.

Proposal

Add documentation to the API class that states:

  • CAN.write() will always send messages in the order in which is called.

  • CAN.write() may buffer messages providing it ensures that they are transmitted in order (e.g. using a hardware FIFO). The number of messages that can be buffered is implementation defined.

Is this a breaking change?

No. Any API changes would be additive.

The current Renesas cores are unbuffered using a single mailbox for transmission and return an error if that mailbox is busy.

I don't know of other cores using this part of the API especially as it's a fairly recent addition.

Additional information

Many CAN controllers allow multiple frames to be buffered for transmission, for example, by providing:

  • transmit mailboxes
  • transmit FIFOs
  • transmit priority queues

Problems can occur when an implementation uses multiple transmit mailboxes and where the hardware implicitly prioritizes transmission by mailbox number. For example, if the hardware has three mailboxes and the message requires more than three frames, the following sequence can occur

  1. First frame is queued in mailbox 0 and transmission is started
  2. Second frame is queued in mailbox 1
  3. Third frame is queued in mailbox 2
  4. First frame send completes, freeing mailbox 0
  5. Hardware starts send of mailbox 1 as that is the highest priority one with pending data
  6. Fourth frame is queued in mailbox 0
  7. Second frame send completes, freeing mailbox 1
  8. Hardware starts send of mailbox 0 (now containing the fourth frame) as that is higher priority than mailbox 2

As a result segment 4 will be send ahead of segment 3.

This would not occur if the mailboxes were used in FIFO mode as the hardware would then send mailbox 2 ahead of mailbox 0.

@aentinger
Copy link
Contributor

Hi @jboynes ☕ 👋

Thank you very much for your detailed issue report. You are right in that the current implementation of CAN across all supported cores emulate a FIFO and not a priority queue (or another mechanism).

I'm very much in favour of your proposal and would ask you if you could provide a PR adding this current state of affairs as comment to the ArduinoCore-API repository? The FIFO approach makes more sense to how a beginner would expect the system to work, unexpected frame order inversion due to a priority queue is much more likely to cause headache than if you can match each call to can.write() to a subsequent transmission of a CAN frame.

@per1234 per1234 linked a pull request Nov 3, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants