Skip to main content

Crate consortium_ipc_transport_fdcan

Crate consortium_ipc_transport_fdcan 

Source
Expand description

Segmenting FDCAN IPC transport.

This is the second wired, Type-B transport in the workspace, after consortium-ipc-transport-uart. Type-B means the link itself is the notification: a frame arriving is the wakeup, so there is no Doorbell and no Side — unlike shared memory, where both are needed to coordinate two cores over one window.

§Wire format

A CAN controller already delimits every frame and protects it with a CRC, so there is nothing here to frame or checksum — the UART transport’s COBS and CRC32 layers have no counterpart. What CAN lacks is a way to carry a message longer than one frame, so that is all this crate adds:

byte 0: [ kind:2 | low:6 ]
  00 Single   : low = payload length; the whole message follows
  01 First    : low = 0; bytes 1..3 = total length u16 LE, then the head
  10 Continue : low = sequence number, 1,2,3,… mod 64; then the body
  11 reserved

§Sizing

One payload byte per frame goes to the header, and a First frame spends two more on the total length:

Frame kindClassic (8 B)FD (64 B)
Single7 B63 B
First5 B61 B
Continue7 B63 B

frames_for reports what a full-MTU message costs in frames, which is the number to reach for when budgeting bus time. A 128-byte message is 19 classic frames — about 2.5 ms at 500 kbit/s — or 3 FD frames.

Segments are padded up to a length the medium can describe (padded_len). On classic CAN that is a no-op, since every length 0..=8 is exact. On FD the DLC jumps 8, 12, 16, 20, 24, 32, 48, 64, so a 10-byte segment goes out as 12. The receiver ignores the difference — a Single carries its own length and a Continue is bounded by the declared total.

Only the receive half takes a buffer, sized by rx_buf_len. CAN frames are fixed-size, so the transmit path stages each one in a stack array and slices the caller’s payload directly; there is no tx_buf_len to match the UART crate’s.

§Identifiers

Chan does not go on the wire. CAN frames already carry an identifier, so each half is bound to one: the transmit half stamps it into every frame, and the receive half drops anything else. The two directions must use different identifiers, since both halves see every frame on the bus and an endpoint must not reassemble its own traffic. FdcanTransport::new rejects equality with Error::SameIdentifier rather than leaving it to be discovered at runtime — a controller running with loopback or self-reception, as bring-up self-tests use, echoes every frame straight back.

Filtering is currently done in software, because neither chip HAL programs hardware acceptance filters yet. Frames for other identifiers therefore cost a wakeup; a hardware filter is the natural optimization and needs no change here.

§Example

ⓘ
use consortium_ipc_transport_fdcan::{FdcanTransport, rx_buf_len};
use embedded_can::{Id, StandardId};

const MTU: usize = 128;
static RX: StaticCell<[u8; rx_buf_len(MTU)]> = StaticCell::new([0; rx_buf_len(MTU)]);

let (tx, rx) = can.split();
let mut transport = FdcanTransport::new(
    chan,
    Id::Standard(StandardId::new(0x101).unwrap()), // we send here
    Id::Standard(StandardId::new(0x102).unwrap()), // the peer sends here
    tx,
    rx,
    RX.init([0; rx_buf_len(MTU)]),
    MTU,
)?;
transport.connect(()).await?;
let (tx, rx) = transport.split();

§Backends

CrateController
consortium-ipc-transport-fdcan-embeddedChip HAL drivers for i.MX9 FlexCAN and STM32MP2 FDCAN
this crate, no backendAny type implementing CanTx/CanRx

§Limitations

  • Classic 8-byte frames. The seam is FD-ready — a frame type reporting MAX_PAYLOAD = 64 needs no change here — but neither chip HAL drives FD yet.
  • No flow control. A receiver that is not already parked in recv can miss frames from a burst, and the segmentation layer will report the resulting sequence gap and drop that message. The i.MX9 driver’s single receive mailbox makes this tighter than the STM32MP2’s eight-element FIFO.
  • One Chan per transport; no multiplexing.
  • Losing exactly a multiple of 64 consecutive frames is invisible to the sequence counter. The codec’s decode is the backstop.

§Features

FeatureEffect
stdDefault. Widens MaybeSend to Send and routes logging to tracing.
allocImplied by std; enables consortium-ipc’s allocating paths.
defmtRoutes logging to defmt and derives Format on Error.

std does not select a second set of impls: the CanTx/CanRx seam belongs to this crate, so its futures are declared MaybeSend once and one impl family serves both firmware and host.

§Testing

cargo test -p consortium-ipc-transport-fdcan
cargo test -p consortium-ipc-transport-fdcan --no-default-features

Both run the same sources, including the same loopback integration test — which is the evidence for the single-impl-family claim above.

Re-exports§

pub use embedded_can;

Structs§

FdcanTransport
Full-duplex FDCAN transport pairing a send and a receive half.
FdcanTransportRx
Receive half of an FDCAN transport: filters by identifier and reassembles segmented messages. Pass it to Channel::<Rx, …>.
FdcanTransportTx
Send half of an FDCAN transport: segments payloads and transmits them under this endpoint’s identifier. Pass it to Channel::<Tx, …>.

Enums§

Error
Errors surfaced by FdcanTransportTx and FdcanTransportRx.

Constants§

CLASSIC_PAYLOAD
Data bytes a classic CAN frame carries.
FD_PAYLOAD
Data bytes a CAN FD frame carries.
FIRST_EXTRA
Additional bytes a First frame spends on the total-length field.
HEADER_LEN
Bytes of frame payload spent on the header.
MAX_MTU
Largest MTU the total-length field can describe.
MIN_FRAME_PAYLOAD
Smallest frame payload that can carry a segmented message: a header, the length field, and at least one data byte.

Traits§

CanErrorType
The error a CAN half reports. Split out so CanTx and CanRx can be implemented separately for the two halves of a split driver while agreeing on one error type.
CanFrame
A CAN data frame this transport can carry a payload in.
CanRx
Receive half of a CAN controller.
CanTx
Transmit half of a CAN controller.

Functions§

continue_max
Message bytes a Continue frame carries.
first_max
Message bytes a First frame carries alongside the total-length field.
frames_for
CAN frames a message of exactly mtu bytes occupies.
padded_len
Round len up to a length a CAN frame can actually describe.
rx_buf_len
Reassembly buffer size for an MTU: one full message.
single_max
Largest message a single unsegmented frame carries.