Dear RTEMS community, the core developers team,
Michal Lenc works on a new generic CAN/CAN FD subsystem for RTEMS under my
supervision. The project has reached a phase where we will be very grateful
for the review and pointing to required changes to start a discussion of
possible inclusion into the RTEMS mainline. The project is currently being
developed as a separate RTEMS application based on the OMK build
https://gitlab.fel.cvut.cz/otrees/rtems/rtems-canfd
But long-term intention is to move
https://gitlab.fel.cvut.cz/otrees/rtems/rtems-canfd/-/tree/master/lib/candrv
directory into RTEMS cpukit/dev/can directory and corresponding include files
into cpukit/include/dev/can directory.
We have reached a state where the virtual interface has been tested on an
MZ_APO Xilinx/AMD Zynq-based board and an LX_CPU/LX_RoCoN NXP LPC4088-based
board. The communication with a complete CAN interface has been successfully
tested on the MZ_APO Xilinx/AMD Zynq board with CTU CAN FD IP cores
implemented in FPGA.
Building on other RTEMS BSP should be possible by changing a single definition
in the config.target file
RTEMS_MAKEFILE_PATH=/opt/rtems/6/arm-rtems6/xilinx_zynq_zedboard
To test with the controller, we can provide FPGA bitstream, which should fit
Zed/MicroZed or other XilinX Zynq 7Z010 based boards, and thanks to the
source availability of CTU CAN FD core, it can be ported to other FPGA+SoC
platforms. We have QEMU CTU CAN FD PCI integration emulation so that
can be other target for continuous integration testing even on x86, but
PCI/PCIe mapping has to be added to the RTEMS driver code.
The API to the driver is provided in the form of the POSIX character device.
Each CAN controller (chip) is registered as a node into the "/dev" namespace
by name (i.e., "can0", "can1", ... ). It can be opened multiple times, and
each instance of the open can be accessed from multiple threads. Written CAN
frames are posted into the input end of the queue (FIFO), which is connected
to the CAN controller (chip) or other frame processor on the output side. The
processor takes frames from FIFOs according to their priority class and
transmits them to the network. When the frame is successfully sent, it is
echoed back to all queues back to open file instances except the sending one
(that filtering is configurable). The filtering is implemented in
canque_filter_frame2edges() function. Received frames are filtered to all
queues to applications ends of the queues, which filter matches the CAN
identifier and frame type.
The design is based on FIFOs designed closely to support needs to provide
service optimal to CAN frames delivery. On the controller side,
multiple slots, each with one CAN frame, can be taken from FIFO and kept
until the transmission is finished. Then, the frame from the slot is
distributed to inform even local clients about the frame that was sent.
The framework has a unique feature to allow pushback slots (frames) when some
later scheduled low-priority frame occupies the hardware Tx buffer, which is
urgently demanded for a higher priority pending frame to be sent. Then the
frame push back to the FIFO head is delivered for Tx processing again.
We present (in the prepared article) how our design resolves bus arbitration
priority inversion, which is often a problem for generic CAN drivers and
subsystems, which allows access from more applications than is the fixed
Tx buffers count. With a well-behaved controller, we are able to keep
all available Tx hardware buffers filled by the right frames instantly.
When higher priority class frame is released by the application into
queues, the controller replaces the latest lower priority frames
with higher priority one and places it in the sequence after all
frames with the same priority class but before all with lower priority.
We will present the idea in the prepared article at the
international CAN Conference in May
https://www.can-cia.org/icc/
The design implementation choice is to use CAN-specific FIFOS queues.
The consistency is protected by rtems_interrupt_lock_acquire /
rtems_interrupt_lock_release, and thanks to reference counting
of users iterating with given queue from both its ends, the locks
are released during most of the operations and are held
for a really short time. We consider rtems_interrupt_lock
appropriate thanks to low overhead when we keep it
for limited code sequences. But switching to semaphores
or some POSIX mutexes is possible. But we also want to keep
code usable on RTEMS build even without POSIX API enabled,
and classical API semaphores have significant size and execution
time overhead.
We have switched to BSD Tail Queues (TAILQ) to keep lists
of the queues (FIFOs) coming to/from a given frame processor node
because they are provided even by NewLib and make code portable
#include
The API is much harder to use than Linux struct list_head
and supporting functions. My preferred alternative is my uLUt