Skip to content

arch/arm/src/imxrt: Fix FlexCAN TX timeout aborting live mailboxes. - #19970

Open
dakejahl wants to merge 4 commits into
apache:masterfrom
dakejahl:dakejahl/imxrt-flexcan-tx-fixes
Open

arch/arm/src/imxrt: Fix FlexCAN TX timeout aborting live mailboxes.#19970
dakejahl wants to merge 4 commits into
apache:masterfrom
dakejahl:dakejahl/imxrt-flexcan-tx-fixes

Conversation

@dakejahl

@dakejahl dakejahl commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Three defects in the i.MX RT FlexCAN transmit path, all found while chasing an error-passive storm on an ARK FMU-v6XRT.

imxrt_txtimeout_work() aborts the wrong mailboxes. It aborts mailbox RXMBCOUNT + mbi while the deadline it consulted belongs to RXMBCOUNT + 1 + mbi, so every abort lands one mailbox low; mbi == 0 writes CAN_TXMB_ABORT into the buffer reserved for the ERR005829 workaround and the highest TX mailbox is never aborted at all. Its expiry test is now.tv_sec > d.tv_sec || now.tv_usec > d.tv_usec, which declares any deadline crossing a second boundary expired, and the now it compares against is a struct timespec cast to a struct timeval. The walk runs to TXMBCOUNT, which counts the reserved mailbox too, so the last iteration addresses one mailbox past the ring and mb_address[] one past its end — in bounds today only because txmb[] is never written that far. And imxrt_txdone() cancels the watchdog but leaves txmb[].deadline set, so a retired mailbox looks expired forever and the next expiry on any other mailbox aborts whatever frame has since been loaded there.

Since imxrt_txmb_next() only hands out a mailbox above every pending one, a mailbox left in DATAORREMOTE also pins the allocator at TOTALMBCOUNT and transmit never recovers.

imxrt_transmit() stores the deadline before it knows the frame is going out. The early return for an already-expired deadline then leaves that deadline on a mailbox holding no frame, and the next watchdog expiry counts a transmit timeout that did not happen and aborts a mailbox the allocator may since have handed to a live frame.

MCR[MAXMB] is OR-ed in rather than assigned. MAXMB resets to 0x0f, so the OR can only raise it: every configuration with fewer than 16 mailboxes runs with MAXMB = 15 and FlexCAN arbitrates over mailboxes the driver never initialised — with the classic payload layout MB14 and MB15 hold power-on contents, and with a 64-byte CAN FD layout they are past the end of the mailbox RAM region. s32k1xx_flexcan.c already carries this fix; the same two lines go here.

The same timeval cast, the same OR-ed expiry test and the same unwritten deadline are in kinetis_flexcan.c, s32k1xx_flexcan.c and s32k3xx_flexcan.c, and the MAXMB one in kinetis and s32k3xx. Those drivers index priv->tx[mbi] directly, so the mailbox off-by-one is imxrt's alone. Left untouched here because there is no hardware to test them on — happy to extend the PR if a maintainer prefers.

Impact

  • Is new feature added? NO
  • Is existing feature changed? YES — a transmit deadline now expires when it is actually due, and aborts the mailbox that owns it.
  • Impact on user? YES — SocketCAN on i.MX RT no longer loses the interface to one expired frame. No API change.
  • Impact on build? NO
  • Impact on hardware? YES — i.MX RT boards using SocketCAN.
  • Impact on documentation? NO
  • Impact on security? NO
  • Impact on compatibility? NO

Testing

ARK FMU-v6XRT (i.MX RT1176), ark_fmu-v6xrt_default, arm-none-eabi-gcc 13.2.1. Two 1 Mbit/s DroneCAN buses with a GNSS node on each, driven by the PX4 uavcan driver. ECR, ESR1 and MCR read over SWD without halting the core.

TX timeout fix, one bus at 736 offered frames/s (9% utilisation):

before:  0 frames/s transmitted, ECR[TXERRCNT] pinned at 128,
         ESR1[FLTCONF] error passive, still dead after a reboot
after:   734 frames/s, 0.1% loss, TXERRCNT 0, error active

MAXMB fix, MCR read back on both instances:

before:  0x0063180f   (MAXMB 15, 14 mailboxes configured)
after:   0x0063180d   (MAXMB 13)

No regression at 8-channel ESC RawCommand to both buses at 400 Hz plus RawIMU back — 705 offered frames/s per bus, 31% utilisation, 120 s: 0 error episodes, TXERRCNT 0, 0 transmit timeouts, 0.00% loss on both interfaces.

The deadline-store fix is structural; its window is too narrow to provoke deliberately, and it is covered by the same run showing no timeouts.

imxrt_txtimeout_work() had four defects that together let one expired frame
take the interface down permanently.

It aborted mailbox RXMBCOUNT + mbi while the deadline it consulted belongs to
RXMBCOUNT + 1 + mbi, so every abort landed one mailbox low and mbi == 0 wrote
CAN_TXMB_ABORT into the buffer reserved for the ERR005829 workaround, while
the highest TX mailbox was never aborted at all.

Its expiry test read `now.tv_sec > d.tv_sec || now.tv_usec > d.tv_usec`, which
declares any deadline that crosses a second boundary expired: in that case the
deadline's microsecond field is always the smaller of the two. The `now` it
compared against was a struct timespec cast to a struct timeval, so writing
tv_usec wrote over tv_nsec and tv_sec was whatever the cast happened to line
up with.

imxrt_txdone() cancelled the watchdog but left txmb[].deadline set, so a
retired mailbox looked expired forever and the next watchdog expiry on any
other mailbox aborted whatever frame had since been loaded there.

The walk ran to TXMBCOUNT, which counts the reserved mailbox as well, so its
last iteration addressed mailbox TOTALMBCOUNT - one past the ring, and
mb_address[] one past its end. Only txmb[] never being written that far kept
it in bounds. TXMBRINGSIZE now names the ring size that the rest of the driver
already assumes.

Aborting a frame that is already on the wire raises a bit error, so the
transmit error counter climbs and the node goes error passive. Since
imxrt_txmb_next() only hands out a mailbox above every pending one, a mailbox
left in DATAORREMOTE also pins the allocator at TOTALMBCOUNT and transmit
never recovers.

Measured on an ARK FMU-v6XRT with a DroneCAN GNSS node on the bus, offering
736 frames/s (9% of a 1 Mbit/s bus) from the PX4 uavcan driver: before, the
interface transmitted 0 frames/s with ECR[TXERRCNT] pinned at 128 and
ESR1[FLTCONF] error passive, and stayed dead across a reboot. After, 734
frames/s, 0.1% loss, TXERRCNT 0, error active.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
… is sent.

imxrt_transmit() wrote the caller's deadline into txmb[] before testing
whether it had already passed, and the early return for an expired deadline
then left that deadline behind on a mailbox holding no frame. The next
watchdog expiry finds it, counts a transmit timeout that did not happen, and
writes CAN_TXMB_ABORT into a mailbox the allocator may have handed to a live
frame in the meantime.

Compute the timeout first and store the deadline after the early return.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
MAXMB resets to 0x0f, so OR-ing the intended value into MCR can only ever
raise it. Every configuration with fewer than 16 mailboxes therefore runs with
MAXMB = 15 and FlexCAN arbitrates over mailboxes the driver never initialised:
with the classic payload layout MB14 and MB15 hold power-on contents, and with
a 64-byte CAN FD layout they are past the end of the mailbox RAM region
entirely.

s32k1xx_flexcan.c already does this; carry the same two lines over.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Blank lines only; `git diff --ignore-blank-lines` against the parent is empty.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Size: S The size of the change in this PR is small labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@dakejahl

dakejahl commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

@PetervdPerk-NXP FYI

The same timeval cast, the same OR-ed expiry test and the same unwritten deadline are in kinetis_flexcan.c, s32k1xx_flexcan.c and s32k3xx_flexcan.c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants