요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0-or-later
CTU CAN FD Driver
=================
Author: Martin Jerabek <martin.jerabek01@gmail.com>
About CTU CAN FD IP Core
------------------------
`CTU CAN FD <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
is an open source soft core written in VHDL.
It originated in 2015 as Ondrej Ille's project
at the `Department of Measurement <https://meas.fel.cvut.cz/>`_
of `FEE <http://www.fel.cvut.cz/en/>`_ at `CTU <https://www.cvut.cz/en>`_.
The SocketCAN driver for Xilinx Zynq SoC based MicroZed board
`Vivado integration <https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top>`_
and Intel Cyclone V 5CSEMA4U23C6 based DE0-Nano-SoC Terasic board
`QSys integration <https://gitlab.fel.cvut.cz/canbus/intel-soc-ctucanfd>`_
has been developed as well as support for
`PCIe integration <https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd>`_ of the core.
In the case of Zynq, the core is connected via the APB system bus, which does
not have enumeration support, and the device must be specified in Device Tree.
This kind of devices is called platform device in the kernel and is
handled by a platform device driver.
The basic functional model of the CTU CAN FD peripheral has been
accepted into QEMU mainline. See QEMU `CAN emulation support <https://www.qemu.org/docs/master/system/devices/can.html>`_
for CAN FD buses, host connection and CTU CAN FD core emulation. The development
version of emulation support can be cloned from ctu-canfd branch of QEMU local
development `repository <https://gitlab.fel.cvut.cz/canbus/qemu-canbus>`_.
About SocketCAN
---------------
SocketCAN is a standard common interface for CAN devices in the Linux
kernel. As the name suggests, the bus is accessed via sockets, similarly
to common network devices. The reasoning behind this is in depth
described in `Linux SocketCAN <https://www.kernel.org/doc/html/latest/networking/can.html>`_.
In short, it offers a
natural way to implement and work with higher layer protocols over CAN,
in the same way as, e.g., UDP/IP over Ethernet.
Device probe
~~~~~~~~~~~~
Before going into detail about the structure of a CAN bus device driver,
let's reiterate how the kernel gets to know about the device at all.
Some buses, like PCI or PCIe, support device enumeration. That is, when
the system boots, it discovers all the devices on the bus and reads
their configuration. The kernel identifies the device via its vendor ID
and device ID, and if there is a driver registered for this identifier
combination, its probe method is invoked to populate the driver's
instance for the given hardware. A similar situation goes with USB, only
it allows for device hot-plug.
The situation is different for peripherals which are directly embedded
in the SoC and connected to an internal system bus (AXI, APB, Avalon,
and others). These buses do not support enumeration, and thus the kernel
has to learn about the devices from elsewhere. This is exactly what the
Device Tree was made for.
Device tree
~~~~~~~~~~~
An entry in device tree states that a device exists in the system, how
it is reachable (on which bus it resides) and its configuration –
registers address, interrupts and so on. An example of such a device
tree is given in .
::
/ {
/* ... */
amba: amba {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
CTU_CAN_FD_0: CTU_CAN_FD@43c30000 {
compatible = "ctu,ctucanfd";
interrupt-parent = <&intc>;
interrupts = <0 30 4>;
clocks = <&clkc 15>;
reg = <0x43c30000 0x10000>;
};
};
};
.. _sec:socketcan:drv:
Driver structure
~~~~~~~~~~~~~~~~
The driver can be divided into two parts – platform-dependent device
discovery and set up, and platform-independent CAN network device
implementation.
.. _sec:socketcan:platdev:
Platform device driver
^^^^^^^^^^^^^^^^^^^^^^
In the case of Zynq, the core is connected via the AXI system bus, which
does not have enumeration support, and the device must be specified in
Device Tree. This kind of devices is called *platform device* in the
kernel and is handled by a *platform device driver*\ [1]_.
A platform device driver provides the following things:
- A *probe* function
- A *remove* function
- A table of *compatible* devices that the driver can handle
The *probe* function is called exactly once when the device appears (or
the driver is loaded, whichever happens later). If there are more
devices handled by the same driver, the *probe* function is called for
each one of them. Its role is to allocate and initialize resources
required for handling the device, as well as set up low-level functions
for the platform-independent layer, e.g., *read_reg* and *write_reg*.
After that, the driver registers the device to a higher layer, in our
case as a *network device*.
The *remove* function is called when the device disappears, or the
driver is about to be unloaded. It serves to free the resources
allocated in *probe* and to unregister the device from higher layers.
Finally, the table of *compatible* devices states which devices the
driver can handle. The Device Tree entry ``compatible`` is matched
against the tables of all *platform drivers*.
.. code:: c
/* Match table for OF platform binding */
static const struct of_device_id ctucan_of_match[] = {
{ .compatible = "ctu,canfd-2", },
{ .compatible = "ctu,ctucanfd", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, ctucan_of_match);
static int ctucan_probe(struct platform_device *pdev);
static int ctucan_remove(struct platform_device *pdev);
static struct platform_driver ctucanfd_driver = {
.probe = ctucan_probe,
.remove = ctucan_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = ctucan_of_match,
},
};
module_platform_driver(ctucanfd_driver);
.. _sec:socketcan:netdev:
Network device driver
^^^^^^^^^^^^^^^^^^^^^
Each network device must support at least these operations:
- Bring the device up: ``ndo_open``
- Bring the device down: ``ndo_close``
- Submit TX frames to the device: ``ndo_start_xmit``
- Signal TX completion and errors to the network subsystem: ISR
- Submit RX frames to the network subsystem: ISR and NAPI
There are two possible event sources: the device and the network
subsystem. Device events are usually signaled via an interrupt, handled
in an Interrupt Service Routine (ISR). Handlers for the events
originating in the network subsystem are then specified in
``struct net_device_ops``.
When the device is brought up, e.g., by calling ``ip link set can0 up``,
the driver’s function ``ndo_open`` is called. It should validate the
interface configuration and configure and enable the device. The
analogous opposite is ``ndo_close``, called when the device is being
brought down, be it explicitly or implicitly.
When the system should transmit a frame, it does so by calling
``ndo_start_xmit``, which enqueues the frame into the device. If the
device HW queue (FIFO, mailboxes or whatever the implementation is)
becomes full, the ``ndo_start_xmit`` implementation informs the network
subsystem that it should stop the TX queue (via ``netif_stop_queue``).
It is then re-enabled later in ISR when the device has some space
available again and is able to enqueue another frame.
All the device events are handled in ISR, namely:
#. **TX completion**. When the device successfully finishes transmitting
a frame, the frame is echoed locally. On error, an informative error
frame [2]_ is sent to the network subsystem instead. In both cases,
the software TX queue is resumed so that more frames may be sent.
#. **Error condition**. If something goes wrong (e.g., the device goes
bus-off or RX overrun happens), error counters are updated, and
informative error frames are enqueued to SW RX queue.
#. **RX buffer not empty**. In this case, read the RX frames and enqueue
them to SW RX queue. Usually NAPI is used as a middle layer (see ).
.. _sec:socketcan:napi:
NAPI
~~~~
The frequency of incoming frames can be high and the overhead to invoke
the interrupt service routine for each frame can cause significant
system load. There are multiple mechanisms in the Linux kernel to deal
with this situation. They evolved over the years of Linux kernel
development and enhancements. For network devices, the current standard
is NAPI – *the New API*. It is similar to classical top-half/bottom-half
interrupt handling in that it only acknowledges the interrupt in the ISR
and signals that the rest of the processing should be done in softirq
context. On top of that, it offers the possibility to *poll* for new
frames for a while. This has a potential to avoid the costly round of
enabling interrupts, handling an incoming IRQ in ISR, re-enabling the
softirq and switching context back to softirq.
See :ref:`Documentation/networking/napi.rst <napi>` for more information.
Integrating the core to Xilinx Zynq
-----------------------------------
The core interfaces a simple subset of the Avalon
(search for Intel **Avalon Interface Specifications**)
bus as it was originally used on
Alterra FPGA chips, yet Xilinx natively interfaces with AXI
(search for ARM **AMBA AXI and ACE Protocol Specification AXI3,
AXI4, and AXI4-Lite, ACE and ACE-Lite**).
The most obvious solution would be to use
an Avalon/AXI bridge or implement some simple conversion entity.
However, the core’s interface is half-duplex with no handshake
signaling, whereas AXI is full duplex with two-way signaling. Moreover,
even AXI-Lite slave interface is quite resource-intensive, and the
flexibility and speed of AXI are not required for a CAN core.
Thus a much simpler bus was chosen – APB (Advanced Peripheral Bus)
(search for ARM **AMBA APB Protocol Specification**).
APB-AXI bridge is directly available in
Xilinx Vivado, and the interface adaptor entity is just a few simple
combinatorial assignments.
Finally, to be able to include the core in a block diagram as a custom
IP, the core, together with the APB interface, has been packaged as a
Vivado component.
CTU CAN FD Driver design
------------------------
The general structure of a CAN device driver has already been examined
in . The next paragraphs provide a more detailed description of the CTU
CAN FD core driver in particular.
Low-level driver
~~~~~~~~~~~~~~~~
The core is not intended to be used solely with SocketCAN, and thus it
is desirable to have an OS-independent low-level driver. This low-level
driver can then be used in implementations of OS driver or directly
either on bare metal or in a user-space application. Another advantage
is that if the hardware slightly changes, only the low-level driver
needs to be modified.
The code [3]_ is in part automatically generated and in part written
manually by the core author, with contributions of the thesis’ author.
The low-level driver supports operations such as: set bit timing, set
controller mode, enable/disable, read RX frame, write TX frame, and so
on.
Configuring bit timing
~~~~~~~~~~~~~~~~~~~~~~
On CAN, each bit is divided into four segments: SYNC, PROP, PHASE1, and
PHASE2. Their duration is expressed in multiples of a Time Quantum
(details in `CAN Specification, Version 2.0 <http://esd.cs.ucr.edu/webres/can20.pdf>`_, chapter 8).
When configuring
bitrate, the durations of all the segments (and time quantum) must be
computed from the bitrate and Sample Point. This is performed
independently for both the Nominal bitrate and Data bitrate for CAN FD.
SocketCAN is fairly flexible and offers either highly customized
configuration by setting all the segment durations manually, or a
convenient configuration by setting just the bitrate and sample point
(and even that is chosen automatically per Bosch recommendation if not
specified). However, each CAN controller may have different base clock
frequency and different width of segment duration registers. The
algorithm thus needs the minimum and maximum values for the durations
(and clock prescaler) and tries to optimize the numbers to fit both the
constraints and the requested parameters.
.. code:: c
struct can_bittiming_const {
char name[16]; /* Name of the CAN controller hardware */
__u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
__u32 tseg1_max;
__u32 tseg2_min; /* Time segment 2 = phase_seg2 */
__u32 tseg2_max;
__u32 sjw_max; /* Synchronisation jump width */
__u32 brp_min; /* Bit-rate prescaler */
__u32 brp_max;
__u32 brp_inc;
};
[lst:can_bittiming_const]
A curious reader will notice that the durations of the segments PROP_SEG
and PHASE_SEG1 are not determined separately but rather combined and
then, by default, the resulting TSEG1 is evenly divided between PROP_SEG
and PHASE_SEG1. In practice, this has virtually no consequences as the
sample point is between PHASE_SEG1 and PHASE_SEG2. In CTU CAN FD,
however, the duration registers ``PROP`` and ``PH1`` have different
widths (6 and 7 bits, respectively), so the auto-computed values might
overflow the shorter register and must thus be redistributed among the
two [4]_.
Handling RX
~~~~~~~~~~~
Frame reception is handled in NAPI queue, which is enabled from ISR when
the RXNE (RX FIFO Not Empty) bit is set. Frames are read one by one
until either no frame is left in the RX FIFO or the maximum work quota
has been reached for the NAPI poll run (see ). Each frame is then passed
to the network interface RX queue.
An incoming frame may be either a CAN 2.0 frame or a CAN FD frame. The
way to distinguish between these two in the kernel is to allocate either
``struct can_frame`` or ``struct canfd_frame``, the two having different
sizes. In the controller, the information about the frame type is stored
in the first word of RX FIFO.
This brings us a chicken-egg problem: we want to allocate the ``skb``
for the frame, and only if it succeeds, fetch the frame from FIFO;
otherwise keep it there for later. But to be able to allocate the
correct ``skb``, we have to fetch the first work of FIFO. There are
several possible solutions:
#. Read the word, then allocate. If it fails, discard the rest of the
frame. When the system is low on memory, the situation is bad anyway.
#. Always allocate ``skb`` big enough for an FD frame beforehand. Then
tweak the ``skb`` internals to look like it has been allocated for
the smaller CAN 2.0 frame.
#. Add option to peek into the FIFO instead of consuming the word.
#. If the allocation fails, store the read word into driver’s data. On
the next try, use the stored word instead of reading it again.
Option 1 is simple enough, but not very satisfying if we could do
better. Option 2 is not acceptable, as it would require modifying the
private state of an integral kernel structure. The slightly higher
memory consumption is just a virtual cherry on top of the “cake”. Option
3 requires non-trivial HW changes and is not ideal from the HW point of
view.
Option 4 seems like a good compromise, with its disadvantage being that
a partial frame may stay in the FIFO for a prolonged time. Nonetheless,
there may be just one owner of the RX FIFO, and thus no one else should
see the partial frame (disregarding some exotic debugging scenarios).
Basides, the driver resets the core on its initialization, so the
partial frame cannot be “adopted” either. In the end, option 4 was
selected [5]_.
.. _subsec:ctucanfd:rxtimestamp:
Timestamping RX frames
^^^^^^^^^^^^^^^^^^^^^^
The CTU CAN FD core reports the exact timestamp when the frame has been
received. The timestamp is by default captured at the sample point of
the last bit of EOF but is configurable to be captured at the SOF bit.
The timestamp source is external to the core and may be up to 64 bits
wide. At the time of writing, passing the timestamp from kernel to
userspace is not yet implemented, but is planned in the future.
Handling TX
~~~~~~~~~~~
The CTU CAN FD core has 4 independent TX buffers, each with its own
state and priority. When the core wants to transmit, a TX buffer in
Ready state with the highest priority is selected.
The priorities are 3bit numbers in register TX_PRIORITY
(nibble-aligned). This should be flexible enough for most use cases.
SocketCAN, however, supports only one FIFO queue for outgoing
frames [6]_. The buffer priorities may be used to simulate the FIFO
behavior by assigning each buffer a distinct priority and *rotating* the
priorities after a frame transmission is completed.
In addition to priority rotation, the SW must maintain head and tail
pointers into the FIFO formed by the TX buffers to be able to determine
which buffer should be used for next frame (``txb_head``) and which
should be the first completed one (``txb_tail``). The actual buffer
indices are (obviously) modulo 4 (number of TX buffers), but the
pointers must be at least one bit wider to be able to distinguish
between FIFO full and FIFO empty – in this situation,
:math:`txb\_head \equiv txb\_tail\ (\textrm{mod}\ 4)`. An example of how
the FIFO is maintained, together with priority rotation, is depicted in
|
+------+---+---+---+---+
| TXB# | 0 | 1 | 2 | 3 |
+======+===+===+===+===+
| Seq | A | B | C | |
+------+---+---+---+---+
| Prio | 7 | 6 | 5 | 4 |
+------+---+---+---+---+
| | | T | | H |
+------+---+---+---+---+
|
+------+---+---+---+---+
| TXB# | 0 | 1 | 2 | 3 |
+======+===+===+===+===+
| Seq | | B | C | |
+------+---+---+---+---+
| Prio | 4 | 7 | 6 | 5 |
+------+---+---+---+---+
| | | T | | H |
+------+---+---+---+---+
|
+------+---+---+---+---+----+
| TXB# | 0 | 1 | 2 | 3 | 0’ |
+======+===+===+===+===+====+
| Seq | E | B | C | D | |
+------+---+---+---+---+----+
| Prio | 4 | 7 | 6 | 5 | |
+------+---+---+---+---+----+
| | | T | | | H |
+------+---+---+---+---+----+
|
.. kernel-figure:: fsm_txt_buffer_user.svg
TX Buffer states with possible transitions
.. _subsec:ctucanfd:txtimestamp:
Timestamping TX frames
^^^^^^^^^^^^^^^^^^^^^^
When submitting a frame to a TX buffer, one may specify the timestamp at
which the frame should be transmitted. The frame transmission may start
later, but not sooner. Note that the timestamp does not participate in
buffer prioritization – that is decided solely by the mechanism
described above.
Support for time-based packet transmission was recently merged to Linux
v4.19 `Time-based packet transmission <https://lwn.net/Articles/748879/>`_,
but it remains yet to be researched
whether this functionality will be practical for CAN.
Also similarly to retrieving the timestamp of RX frames, the core
supports retrieving the timestamp of TX frames – that is the time when
the frame was successfully delivered. The particulars are very similar
to timestamping RX frames and are described in .
Handling RX buffer overrun
~~~~~~~~~~~~~~~~~~~~~~~~~~
When a received frame does no more fit into the hardware RX FIFO in its
entirety, RX FIFO overrun flag (STATUS[DOR]) is set and Data Overrun
Interrupt (DOI) is triggered. When servicing the interrupt, care must be
taken first to clear the DOR flag (via COMMAND[CDO]) and after that
clear the DOI interrupt flag. Otherwise, the interrupt would be
immediately [7]_ rearmed.
**Note**: During development, it was discussed whether the internal HW
pipelining cannot disrupt this clear sequence and whether an additional
dummy cycle is necessary between clearing the flag and the interrupt. On
the Avalon interface, it indeed proved to be the case, but APB being
safe because it uses 2-cycle transactions. Essentially, the DOR flag
would be cleared, but DOI register’s Preset input would still be high
the cycle when the DOI clear request would also be applied (by setting
the register’s Reset input high). As Set had higher priority than Reset,
the DOI flag would not be reset. This has been already fixed by swapping
the Set/Reset priority (see issue #187).
Reporting Error Passive and Bus Off conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It may be desirable to report when the node reaches *Error Passive*,
*Error Warning*, and *Bus Off* conditions. The driver is notified about
error state change by an interrupt (EPI, EWLI), and then proceeds to
determine the core’s error state by reading its error counters.
There is, however, a slight race condition here – there is a delay
between the time when the state transition occurs (and the interrupt is
triggered) and when the error counters are read. When EPI is received,
the node may be either *Error Passive* or *Bus Off*. If the node goes
*Bus Off*, it obviously remains in the state until it is reset.
Otherwise, the node is *or was* *Error Passive*. However, it may happen
that the read state is *Error Warning* or even *Error Active*. It may be
unclear whether and what exactly to report in that case, but I
personally entertain the idea that the past error condition should still
be reported. Similarly, when EWLI is received but the state is later
detected to be *Error Passive*, *Error Passive* should be reported.
CTU CAN FD Driver Sources Reference
-----------------------------------
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd.h
:internal:
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_base.c
:internal:
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_pci.c
:internal:
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_platform.c
:internal:
CTU CAN FD IP Core and Driver Development Acknowledgment
---------------------------------------------------------
* Odrej Ille <ondrej.ille@gmail.com>
* started the project as student at Department of Measurement, FEE, CTU
* invested great amount of personal time and enthusiasm to the project over years
* worked on more funded tasks
* `Department of Measurement <https://meas.fel.cvut.cz/>`_,
`Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_,
`Czech Technical University <https://www.cvut.cz/en>`_
* is the main investor into the project over many years
* uses project in their CAN/CAN FD diagnostics framework for `Skoda Auto <https://www.skoda-auto.cz/>`_
* `Digiteq Automotive <https://www.digiteqautomotive.com/en>`_
* funding of the project CAN FD Open Cores Support Linux Kernel Based Systems
* negotiated and paid CTU to allow public access to the project
* provided additional funding of the work
* `Department of Control Engineering <https://control.fel.cvut.cz/en>`_,
`Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_,
`Czech Technical University <https://www.cvut.cz/en>`_
* solving the project CAN FD Open Cores Support Linux Kernel Based Systems
* providing GitLab management
* virtual servers and computational power for continuous integration
* providing hardware for HIL continuous integration tests
* `PiKRON Ltd. <http://pikron.com/>`_
* minor funding to initiate preparation of the project open-sourcing
* Petr Porazil <porazil@pikron.com>
* design of PCIe transceiver addon board and assembly of boards
* design and assembly of MZ_APO baseboard for MicroZed/Zynq based system
* Martin Jerabek <martin.jerabek01@gmail.com>
* Linux driver development
* continuous integration platform architect and GHDL updates
* thesis `Open-source and Open-hardware CAN FD Protocol Support <https://dspace.cvut.cz/bitstream/handle/10467/80366/F3-DP-2019-Jerabek-Martin-Jerabek-thesis-2019-canfd.pdf>`_
* Jiri Novak <jnovak@fel.cvut.cz>
* project initiation, management and use at Department of Measurement, FEE, CTU
* Pavel Pisa <pisa@cmp.felk.cvut.cz>
* initiate open-sourcing, project coordination, management at Department of Control Engineering, FEE, CTU
* Jaroslav Beran<jara.beran@gmail.com>
* system integration for Intel SoC, core and driver testing and updates
* Carsten Emde (`OSADL <https://www.osadl.org/>`_)
* provided OSADL expertise to discuss IP core licensing
* pointed to possible deadlock for LGPL and CAN bus possible patent case which lead to relicense IP core design to BSD like license
* Reiner Zitzmann and Holger Zeltwanger (`CAN in Automation <https://www.can-cia.org/>`_)
* provided suggestions and help to inform community about the project and invited us to events focused on CAN bus future development directions
* Jan Charvat
* implemented CTU CAN FD functional model for QEMU which has been integrated into QEMU mainline (`docs/system/devices/can.rst <https://www.qemu.org/docs/master/system/devices/can.html>`_)
* Bachelor thesis Model of CAN FD Communication Controller for QEMU Emulator
Notes
-----
.. [1]
Other buses have their own specific driver interface to set up the
device.
.. [2]
Not to be mistaken with CAN Error Frame. This is a ``can_frame`` with
``CAN_ERR_FLAG`` set and some error info in its ``data`` field.
.. [3]
Available in CTU CAN FD repository
`<https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
.. [4]
As is done in the low-level driver functions
``ctucan_hw_set_nom_bittiming`` and
``ctucan_hw_set_data_bittiming``.
.. [5]
At the time of writing this thesis, option 1 is still being used and
the modification is queued in gitlab issue #222
.. [6]
Strictly speaking, multiple CAN TX queues are supported since v4.19
`can: enable multi-queue for SocketCAN devices <https://lore.kernel.org/patchwork/patch/913526/>`_ but no mainline driver is using
them yet.
.. [7]
Or rather in the next clock cycle
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
CTU CAN FD IP core와 SocketCAN
1-46CTU CAN FD 드라이버
저자: Martin Jerabek `<martin.jerabek01@gmail.com>`
CTU CAN FD IP core 소개
`CTU CAN FD <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_는 VHDL로 작성된 오픈 소스 soft core입니다. 2015년에 CTU FEE의 `Department of Measurement <https://meas.fel.cvut.cz/>`_에서 Ondrej Ille의 프로젝트로 시작되었습니다. 관련 기관은 `FEE <http://www.fel.cvut.cz/en/>`_와 `CTU <https://www.cvut.cz/en>`_입니다.
Xilinx Zynq SoC 기반 MicroZed 보드를 위한 SocketCAN 드라이버와 `Vivado integration <https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top>`_, Intel Cyclone V 5CSEMA4U23C6 기반 DE0-Nano-SoC Terasic 보드의 `QSys integration <https://gitlab.fel.cvut.cz/canbus/intel-soc-ctucanfd>`_, core의 `PCIe integration <https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd>`_ 지원도 함께 개발되었습니다.
Zynq에서는 core가 enumeration 기능이 없는 APB system bus에 연결되므로 Device Tree에 device를 명시해야 합니다. 커널은 이런 device를 platform device라고 부르며 platform device driver로 처리합니다.
CTU CAN FD peripheral의 기본 functional model은 QEMU mainline에 포함되었습니다. CAN FD bus, host 연결, CTU CAN FD core emulation은 QEMU의 `CAN emulation support <https://www.qemu.org/docs/master/system/devices/can.html>`_를 참고하십시오. 개발 버전은 QEMU 로컬 개발 `repository <https://gitlab.fel.cvut.cz/canbus/qemu-canbus>`_의 `ctu-canfd` branch에서 받을 수 있습니다.
SocketCAN 소개
SocketCAN은 Linux kernel에서 CAN device에 제공하는 표준 공통 interface입니다. 이름처럼 일반 network device와 비슷하게 socket으로 bus에 접근합니다. 자세한 설계 이유는 `Linux SocketCAN <https://www.kernel.org/doc/html/latest/networking/can.html>`_에 설명되어 있습니다. 요약하면 Ethernet 위에서 UDP/IP를 다루듯 CAN 위에서 상위 계층 protocol을 자연스럽게 구현하고 사용할 수 있게 합니다.
.. SPDX-License-Identifier: GPL-2.0-or-later
CTU CAN FD Driver
=================
Author: Martin Jerabek <martin.jerabek01@gmail.com>
About CTU CAN FD IP Core
------------------------
`CTU CAN FD <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
is an open source soft core written in VHDL.
It originated in 2015 as Ondrej Ille's project
at the `Department of Measurement <https://meas.fel.cvut.cz/>`_
of `FEE <http://www.fel.cvut.cz/en/>`_ at `CTU <https://www.cvut.cz/en>`_.
The SocketCAN driver for Xilinx Zynq SoC based MicroZed board
`Vivado integration <https://gitlab.fel.cvut.cz/canbus/zynq/zynq-can-sja1000-top>`_
and Intel Cyclone V 5CSEMA4U23C6 based DE0-Nano-SoC Terasic board
`QSys integration <https://gitlab.fel.cvut.cz/canbus/intel-soc-ctucanfd>`_
has been developed as well as support for
`PCIe integration <https://gitlab.fel.cvut.cz/canbus/pcie-ctucanfd>`_ of the core.
In the case of Zynq, the core is connected via the APB system bus, which does
not have enumeration support, and the device must be specified in Device Tree.
This kind of devices is called platform device in the kernel and is
handled by a platform device driver.
The basic functional model of the CTU CAN FD peripheral has been
accepted into QEMU mainline. See QEMU `CAN emulation support <https://www.qemu.org/docs/master/system/devices/can.html>`_
for CAN FD buses, host connection and CTU CAN FD core emulation. The development
version of emulation support can be cloned from ctu-canfd branch of QEMU local
development `repository <https://gitlab.fel.cvut.cz/canbus/qemu-canbus>`_.
About SocketCAN
---------------
SocketCAN is a standard common interface for CAN devices in the Linux
kernel. As the name suggests, the bus is accessed via sockets, similarly
to common network devices. The reasoning behind this is in depth
described in `Linux SocketCAN <https://www.kernel.org/doc/html/latest/networking/can.html>`_.
In short, it offers a
natural way to implement and work with higher layer protocols over CAN,
in the same way as, e.g., UDP/IP over Ethernet.
Device probe, Device Tree와 platform driver
47-137Device probe
CAN bus device driver의 구조를 살펴보기 전에 커널이 device의 존재를 알아내는 과정을 정리합니다. PCI와 PCIe 같은 bus는 device enumeration을 지원합니다. 부팅할 때 bus의 모든 device를 발견하고 configuration을 읽으며, vendor ID와 device ID 조합에 등록된 driver가 있으면 해당 hardware용 driver instance를 구성하는 `probe` method를 호출합니다. USB도 비슷하지만 hot-plug를 지원합니다.
SoC에 직접 내장되어 AXI, APB, Avalon 같은 내부 system bus에 연결된 peripheral은 다릅니다. 이 bus들은 enumeration을 지원하지 않으므로 커널은 다른 곳에서 device 정보를 얻어야 하며, 바로 이 목적으로 Device Tree를 사용합니다.
Device Tree
Device Tree entry는 system에 device가 존재한다는 사실, 어느 bus를 통해 접근하는지, register address와 interrupt를 비롯한 configuration을 기술합니다. 다음 예는 `ctu,ctucanfd` device의 register 영역, interrupt, clock을 선언합니다.
/
{
/* ... */
amba: amba {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
CTU_CAN_FD_0: CTU_CAN_FD@43c30000 {
compatible = "ctu,ctucanfd";
interrupt-parent = <&intc>;
interrupts = <0 30 4>;
clocks = <&clkc 15>;
reg = <0x43c30000 0x10000>;
};
};
};
드라이버 구조
드라이버는 platform에 의존하는 device discovery·setup 부분과, platform에 독립적인 CAN network device 구현 부분으로 나눌 수 있습니다.
Platform device driver
Zynq에서는 core가 enumeration을 지원하지 않는 AXI system bus에 연결되므로 Device Tree에 지정됩니다. 커널은 이를 *platform device*로 취급하고 *platform device driver*가 처리합니다. 다른 bus에는 device를 설정하기 위한 고유한 driver interface가 있습니다.
Platform device driver가 제공해야 하는 항목은 다음과 같습니다.
- *probe* function
- *remove* function
- driver가 처리할 수 있는 *compatible* device 표
*probe* function은 device가 나타나거나 driver가 load되는 시점 중 더 늦은 때에 device마다 정확히 한 번 호출됩니다. 같은 driver가 여러 device를 처리하면 각각에 대해 호출됩니다. 이 함수는 device 처리에 필요한 resource를 할당·초기화하고, platform 독립 계층이 사용할 `read_reg`, `write_reg` 같은 저수준 function을 설정합니다. 이후 이 사례에서는 device를 *network device*로 상위 계층에 등록합니다.
*remove* function은 device가 사라지거나 driver가 unload되기 직전에 호출됩니다. `probe`에서 할당한 resource를 해제하고 상위 계층에서 device 등록을 해제합니다.
*compatible* 표는 driver가 처리할 device를 선언합니다. Device Tree entry의 `compatible` 값은 모든 *platform driver*의 표와 대조됩니다.
Device probe
~~~~~~~~~~~~
Before going into detail about the structure of a CAN bus device driver,
let's reiterate how the kernel gets to know about the device at all.
Some buses, like PCI or PCIe, support device enumeration. That is, when
the system boots, it discovers all the devices on the bus and reads
their configuration. The kernel identifies the device via its vendor ID
and device ID, and if there is a driver registered for this identifier
combination, its probe method is invoked to populate the driver's
instance for the given hardware. A similar situation goes with USB, only
it allows for device hot-plug.
The situation is different for peripherals which are directly embedded
in the SoC and connected to an internal system bus (AXI, APB, Avalon,
and others). These buses do not support enumeration, and thus the kernel
has to learn about the devices from elsewhere. This is exactly what the
Device Tree was made for.
Device tree
~~~~~~~~~~~
An entry in device tree states that a device exists in the system, how
it is reachable (on which bus it resides) and its configuration –
registers address, interrupts and so on. An example of such a device
tree is given in .
::
/ {
/* ... */
amba: amba {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
CTU_CAN_FD_0: CTU_CAN_FD@43c30000 {
compatible = "ctu,ctucanfd";
interrupt-parent = <&intc>;
interrupts = <0 30 4>;
clocks = <&clkc 15>;
reg = <0x43c30000 0x10000>;
};
};
};
.. _sec:socketcan:drv:
Driver structure
~~~~~~~~~~~~~~~~
The driver can be divided into two parts – platform-dependent device
discovery and set up, and platform-independent CAN network device
implementation.
.. _sec:socketcan:platdev:
Platform device driver
^^^^^^^^^^^^^^^^^^^^^^
In the case of Zynq, the core is connected via the AXI system bus, which
does not have enumeration support, and the device must be specified in
Device Tree. This kind of devices is called *platform device* in the
kernel and is handled by a *platform device driver*\ [1]_.
A platform device driver provides the following things:
- A *probe* function
- A *remove* function
- A table of *compatible* devices that the driver can handle
The *probe* function is called exactly once when the device appears (or
the driver is loaded, whichever happens later). If there are more
devices handled by the same driver, the *probe* function is called for
each one of them. Its role is to allocate and initialize resources
required for handling the device, as well as set up low-level functions
for the platform-independent layer, e.g., *read_reg* and *write_reg*.
After that, the driver registers the device to a higher layer, in our
case as a *network device*.
The *remove* function is called when the device disappears, or the
driver is about to be unloaded. It serves to free the resources
allocated in *probe* and to unregister the device from higher layers.
Finally, the table of *compatible* devices states which devices the
driver can handle. The Device Tree entry ``compatible`` is matched
against the tables of all *platform drivers*.
Network device operation, ISR와 NAPI
138-232다음 OF platform binding match table은 `ctu,canfd-2`와 `ctu,ctucanfd`를 받아들이고, `ctucan_probe`와 `ctucan_remove`를 `platform_driver`에 연결합니다.
/* Match table for OF platform binding */
static const struct of_device_id ctucan_of_match[] = {
{ .compatible = "ctu,canfd-2", },
{ .compatible = "ctu,ctucanfd", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, ctucan_of_match);
static int ctucan_probe(struct platform_device *pdev);
static int ctucan_remove(struct platform_device *pdev);
static struct platform_driver ctucanfd_driver = {
.probe = ctucan_probe,
.remove = ctucan_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = ctucan_of_match,
},
};
module_platform_driver(ctucanfd_driver);
Network device driver
각 network device는 적어도 다음 operation을 지원해야 합니다.
- device 시작: `ndo_open`
- device 중지: `ndo_close`
- TX frame을 device에 제출: `ndo_start_xmit`
- TX 완료와 error를 network subsystem에 알림: ISR
- RX frame을 network subsystem에 제출: ISR과 NAPI
Event source는 device와 network subsystem 두 가지입니다. Device event는 보통 interrupt로 전달되어 ISR(Interrupt Service Routine)에서 처리합니다. Network subsystem에서 시작되는 event handler는 `struct net_device_ops`에 지정합니다.
`ip link set can0 up`처럼 device를 올리면 driver의 `ndo_open`이 호출됩니다. 이 함수는 interface configuration을 검증하고 device를 구성·활성화해야 합니다. 반대 동작인 `ndo_close`는 명시적이든 암시적이든 device를 내릴 때 호출됩니다.
System이 frame을 전송할 때 `ndo_start_xmit`을 호출해 frame을 hardware queue에 넣습니다. FIFO나 mailbox 같은 HW queue가 가득 차면 구현은 `netif_stop_queue`로 network subsystem에 TX queue를 멈추라고 알립니다. 나중에 공간이 생기면 ISR에서 queue를 다시 활성화하여 다음 frame을 받을 수 있게 합니다.
ISR은 다음 device event를 처리합니다.
- **TX 완료**: 전송 성공 시 frame을 local echo합니다. 실패하면 설명 정보를 담은 error frame을 network subsystem으로 보냅니다. 두 경우 모두 software TX queue를 재개합니다.
- **Error condition**: bus-off나 RX overrun 같은 문제가 생기면 error counter를 갱신하고 설명 정보를 담은 error frame을 software RX queue에 넣습니다.
- **RX buffer not empty**: RX frame을 읽어 software RX queue에 넣습니다. 일반적으로 NAPI가 중간 계층을 담당합니다.
여기서 말하는 설명용 error frame은 CAN Error Frame과 다릅니다. `CAN_ERR_FLAG`가 설정되고 `data` field에 error 정보가 든 `can_frame`입니다.
NAPI
Frame 유입 빈도가 높으면 frame마다 ISR을 호출하는 비용이 system load를 크게 높일 수 있습니다. Linux kernel에는 이를 다루기 위해 발전해 온 여러 mechanism이 있으며, network device의 현재 표준은 NAPI, 즉 *New API*입니다.
NAPI는 고전적인 top-half/bottom-half interrupt 처리와 비슷합니다. ISR에서는 interrupt만 acknowledge하고 나머지 처리를 softirq context에 맡깁니다. 여기에 일정 시간 새 frame을 *poll*하는 기능을 더합니다. 덕분에 interrupt 재활성화, 새 IRQ의 ISR 처리, softirq 재활성화, softirq context로의 전환이라는 비싼 반복을 피할 수 있습니다.
자세한 내용은 :ref:`Documentation/networking/napi.rst <napi>`를 참고하십시오.
.. code:: c
/* Match table for OF platform binding */
static const struct of_device_id ctucan_of_match[] = {
{ .compatible = "ctu,canfd-2", },
{ .compatible = "ctu,ctucanfd", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, ctucan_of_match);
static int ctucan_probe(struct platform_device *pdev);
static int ctucan_remove(struct platform_device *pdev);
static struct platform_driver ctucanfd_driver = {
.probe = ctucan_probe,
.remove = ctucan_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = ctucan_of_match,
},
};
module_platform_driver(ctucanfd_driver);
.. _sec:socketcan:netdev:
Network device driver
^^^^^^^^^^^^^^^^^^^^^
Each network device must support at least these operations:
- Bring the device up: ``ndo_open``
- Bring the device down: ``ndo_close``
- Submit TX frames to the device: ``ndo_start_xmit``
- Signal TX completion and errors to the network subsystem: ISR
- Submit RX frames to the network subsystem: ISR and NAPI
There are two possible event sources: the device and the network
subsystem. Device events are usually signaled via an interrupt, handled
in an Interrupt Service Routine (ISR). Handlers for the events
originating in the network subsystem are then specified in
``struct net_device_ops``.
When the device is brought up, e.g., by calling ``ip link set can0 up``,
the driver’s function ``ndo_open`` is called. It should validate the
interface configuration and configure and enable the device. The
analogous opposite is ``ndo_close``, called when the device is being
brought down, be it explicitly or implicitly.
When the system should transmit a frame, it does so by calling
``ndo_start_xmit``, which enqueues the frame into the device. If the
device HW queue (FIFO, mailboxes or whatever the implementation is)
becomes full, the ``ndo_start_xmit`` implementation informs the network
subsystem that it should stop the TX queue (via ``netif_stop_queue``).
It is then re-enabled later in ISR when the device has some space
available again and is able to enqueue another frame.
All the device events are handled in ISR, namely:
#. **TX completion**. When the device successfully finishes transmitting
a frame, the frame is echoed locally. On error, an informative error
frame [2]_ is sent to the network subsystem instead. In both cases,
the software TX queue is resumed so that more frames may be sent.
#. **Error condition**. If something goes wrong (e.g., the device goes
bus-off or RX overrun happens), error counters are updated, and
informative error frames are enqueued to SW RX queue.
#. **RX buffer not empty**. In this case, read the RX frames and enqueue
them to SW RX queue. Usually NAPI is used as a middle layer (see ).
.. _sec:socketcan:napi:
NAPI
~~~~
The frequency of incoming frames can be high and the overhead to invoke
the interrupt service routine for each frame can cause significant
system load. There are multiple mechanisms in the Linux kernel to deal
with this situation. They evolved over the years of Linux kernel
development and enhancements. For network devices, the current standard
is NAPI – *the New API*. It is similar to classical top-half/bottom-half
interrupt handling in that it only acknowledges the interrupt in the ISR
and signals that the rest of the processing should be done in softirq
context. On top of that, it offers the possibility to *poll* for new
frames for a while. This has a potential to avoid the costly round of
enabling interrupts, handling an incoming IRQ in ISR, re-enabling the
softirq and switching context back to softirq.
See :ref:`Documentation/networking/napi.rst <napi>` for more information.
Zynq integration, 저수준 driver와 bit timing
233-330Xilinx Zynq에 core 통합
Core는 원래 Altera FPGA에서 사용하던 Avalon bus의 단순한 subset을 interface로 제공합니다. Intel의 **Avalon Interface Specifications**를 찾아볼 수 있습니다. 반면 Xilinx는 AXI를 기본으로 사용하며 ARM의 **AMBA AXI and ACE Protocol Specification AXI3, AXI4, and AXI4-Lite, ACE and ACE-Lite**가 관련 규격입니다.
가장 직접적인 해법은 Avalon/AXI bridge를 사용하거나 간단한 conversion entity를 구현하는 것입니다. 그러나 core interface는 handshake signal이 없는 half-duplex인 반면 AXI는 양방향 signaling을 갖는 full-duplex입니다. AXI-Lite slave interface조차 resource 소모가 크고, CAN core에는 AXI의 유연성과 속도가 필요하지 않습니다.
따라서 훨씬 단순한 APB(Advanced Peripheral Bus)를 선택했습니다. ARM의 **AMBA APB Protocol Specification**이 관련 규격입니다. APB-AXI bridge는 Xilinx Vivado에서 바로 제공되며 interface adaptor entity는 몇 개의 단순한 combinatorial assignment로 구성됩니다.
마지막으로 block diagram에서 custom IP로 core를 넣을 수 있도록 core와 APB interface를 함께 Vivado component로 package했습니다.
CTU CAN FD driver 설계
앞에서 일반적인 CAN device driver 구조를 살펴보았습니다. 다음은 CTU CAN FD core driver에 특화된 세부 설계입니다.
저수준 driver
Core는 SocketCAN 전용이 아니므로 OS 독립적인 저수준 driver가 바람직합니다. 이 계층은 OS driver 구현, bare metal, user-space application에서 직접 사용할 수 있습니다. Hardware가 조금 바뀌더라도 저수준 driver만 수정하면 된다는 장점도 있습니다.
저수준 code는 일부 자동 생성되고 일부는 core 작성자가 수동 작성했으며 논문 저자도 기여했습니다. CTU CAN FD repository `<https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_에서 구할 수 있습니다. Bit timing 설정, controller mode 설정, enable·disable, RX frame 읽기, TX frame 쓰기 등을 지원합니다.
Bit timing 구성
CAN의 각 bit는 SYNC, PROP, PHASE1, PHASE2의 네 segment로 나뉘며 각 길이는 Time Quantum의 배수로 나타냅니다. 자세한 내용은 `CAN Specification, Version 2.0 <http://esd.cs.ucr.edu/webres/can20.pdf>`_ 8장을 참고하십시오. Bitrate를 설정할 때 bitrate와 Sample Point에서 모든 segment와 time quantum의 길이를 계산해야 합니다. CAN FD에서는 Nominal bitrate와 Data bitrate에 대해 이를 각각 수행합니다.
SocketCAN은 모든 segment 길이를 직접 지정하는 세밀한 방식과 bitrate·sample point만 지정하는 간편한 방식을 모두 제공합니다. Sample point를 생략하면 Bosch 권고에 따라 자동 선택됩니다. Controller마다 base clock frequency와 segment duration register 폭이 다르므로 algorithm은 duration과 clock prescaler의 최소·최대 값을 받아 제약과 요청 parameter를 동시에 만족하도록 수치를 최적화합니다.
struct can_bittiming_const {
char name[16]; /* Name of the CAN controller hardware */
__u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
__u32 tseg1_max;
__u32 tseg2_min; /* Time segment 2 = phase_seg2 */
__u32 tseg2_max;
__u32 sjw_max; /* Synchronisation jump width */
__u32 brp_min; /* Bit-rate prescaler */
__u32 brp_max;
__u32 brp_inc;
};
`PROP_SEG`와 `PHASE_SEG1` 길이는 별도로 구하지 않고 합친 뒤 기본적으로 `TSEG1`을 두 부분에 균등 배분합니다. Sample point가 `PHASE_SEG1`과 `PHASE_SEG2` 사이에 있어 실무 영향은 거의 없습니다. 그러나 CTU CAN FD의 `PROP`와 `PH1` register 폭은 각각 6 bit와 7 bit로 다르므로 자동 계산 값이 짧은 register를 넘을 수 있고 두 값 사이에 재분배해야 합니다. 저수준 function `ctucan_hw_set_nom_bittiming`과 `ctucan_hw_set_data_bittiming`이 이 처리를 수행합니다.
Integrating the core to Xilinx Zynq
-----------------------------------
The core interfaces a simple subset of the Avalon
(search for Intel **Avalon Interface Specifications**)
bus as it was originally used on
Alterra FPGA chips, yet Xilinx natively interfaces with AXI
(search for ARM **AMBA AXI and ACE Protocol Specification AXI3,
AXI4, and AXI4-Lite, ACE and ACE-Lite**).
The most obvious solution would be to use
an Avalon/AXI bridge or implement some simple conversion entity.
However, the core’s interface is half-duplex with no handshake
signaling, whereas AXI is full duplex with two-way signaling. Moreover,
even AXI-Lite slave interface is quite resource-intensive, and the
flexibility and speed of AXI are not required for a CAN core.
Thus a much simpler bus was chosen – APB (Advanced Peripheral Bus)
(search for ARM **AMBA APB Protocol Specification**).
APB-AXI bridge is directly available in
Xilinx Vivado, and the interface adaptor entity is just a few simple
combinatorial assignments.
Finally, to be able to include the core in a block diagram as a custom
IP, the core, together with the APB interface, has been packaged as a
Vivado component.
CTU CAN FD Driver design
------------------------
The general structure of a CAN device driver has already been examined
in . The next paragraphs provide a more detailed description of the CTU
CAN FD core driver in particular.
Low-level driver
~~~~~~~~~~~~~~~~
The core is not intended to be used solely with SocketCAN, and thus it
is desirable to have an OS-independent low-level driver. This low-level
driver can then be used in implementations of OS driver or directly
either on bare metal or in a user-space application. Another advantage
is that if the hardware slightly changes, only the low-level driver
needs to be modified.
The code [3]_ is in part automatically generated and in part written
manually by the core author, with contributions of the thesis’ author.
The low-level driver supports operations such as: set bit timing, set
controller mode, enable/disable, read RX frame, write TX frame, and so
on.
Configuring bit timing
~~~~~~~~~~~~~~~~~~~~~~
On CAN, each bit is divided into four segments: SYNC, PROP, PHASE1, and
PHASE2. Their duration is expressed in multiples of a Time Quantum
(details in `CAN Specification, Version 2.0 <http://esd.cs.ucr.edu/webres/can20.pdf>`_, chapter 8).
When configuring
bitrate, the durations of all the segments (and time quantum) must be
computed from the bitrate and Sample Point. This is performed
independently for both the Nominal bitrate and Data bitrate for CAN FD.
SocketCAN is fairly flexible and offers either highly customized
configuration by setting all the segment durations manually, or a
convenient configuration by setting just the bitrate and sample point
(and even that is chosen automatically per Bosch recommendation if not
specified). However, each CAN controller may have different base clock
frequency and different width of segment duration registers. The
algorithm thus needs the minimum and maximum values for the durations
(and clock prescaler) and tries to optimize the numbers to fit both the
constraints and the requested parameters.
.. code:: c
struct can_bittiming_const {
char name[16]; /* Name of the CAN controller hardware */
__u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
__u32 tseg1_max;
__u32 tseg2_min; /* Time segment 2 = phase_seg2 */
__u32 tseg2_max;
__u32 sjw_max; /* Synchronisation jump width */
__u32 brp_min; /* Bit-rate prescaler */
__u32 brp_max;
__u32 brp_inc;
};
[lst:can_bittiming_const]
A curious reader will notice that the durations of the segments PROP_SEG
and PHASE_SEG1 are not determined separately but rather combined and
then, by default, the resulting TSEG1 is evenly divided between PROP_SEG
and PHASE_SEG1. In practice, this has virtually no consequences as the
sample point is between PHASE_SEG1 and PHASE_SEG2. In CTU CAN FD,
however, the duration registers ``PROP`` and ``PH1`` have different
widths (6 and 7 bits, respectively), so the auto-computed values might
overflow the shorter register and must thus be redistributed among the
two [4]_.
RX 처리와 timestamp
331-390RX 처리
Frame 수신은 NAPI queue에서 처리합니다. ISR에서 `RXNE`(RX FIFO Not Empty) bit가 설정되면 NAPI를 활성화합니다. RX FIFO가 비거나 NAPI poll 실행의 최대 work quota에 도달할 때까지 frame을 하나씩 읽고, 각 frame을 network interface RX queue에 넘깁니다.
들어온 frame은 CAN 2.0 또는 CAN FD일 수 있습니다. 커널에서는 크기가 서로 다른 `struct can_frame`과 `struct canfd_frame` 중 하나를 할당해 두 종류를 구분합니다. Controller는 RX FIFO의 첫 word에 frame type 정보를 저장합니다.
여기서 닭과 달걀 문제가 생깁니다. Frame용 `skb`를 먼저 할당하고 성공한 경우에만 FIFO에서 frame을 가져오고 싶지만, 올바른 크기의 `skb`를 고르려면 먼저 FIFO의 첫 word를 읽어야 합니다. 가능한 해법은 다음과 같습니다.
- 1. Word를 읽은 뒤 할당합니다. 실패하면 frame의 나머지를 버립니다. Memory 부족 상태는 어차피 좋지 않다는 판단입니다.
- 2. 항상 CAN FD frame 크기의 `skb`를 미리 할당하고, 더 작은 CAN 2.0 frame용으로 할당된 것처럼 `skb` 내부를 조정합니다.
- 3. Word를 소비하지 않고 FIFO를 들여다보는 peek 기능을 hardware에 추가합니다.
- 4. 할당 실패 시 읽은 word를 driver data에 저장하고, 다음 시도에서는 다시 읽는 대신 저장한 word를 사용합니다.
1번은 단순하지만 더 나은 방법이 있다면 만족스럽지 않습니다. 2번은 kernel 핵심 structure의 private state를 바꿔야 하므로 허용할 수 없고 memory 소비도 늘어납니다. 3번은 상당한 hardware 변경이 필요하며 hardware 관점에서도 이상적이지 않습니다.
4번은 합리적인 절충안입니다. 일부만 읽은 frame이 FIFO에 오래 남을 수 있다는 단점이 있지만 RX FIFO의 owner는 하나뿐이므로 특수한 debugging 상황 외에는 다른 주체가 이를 볼 수 없습니다. Driver 초기화 때 core를 reset하므로 부분 frame이 다음 instance에 이어지는 문제도 없습니다. 최종적으로 4번을 선택했습니다. 다만 이 논문 작성 당시 구현은 아직 1번이었고 변경은 GitLab issue #222에 대기 중이었습니다.
RX frame timestamp
CTU CAN FD core는 frame을 수신한 정확한 timestamp를 보고합니다. 기본적으로 EOF 마지막 bit의 sample point에서 capture하지만 SOF bit에서 capture하도록 설정할 수도 있습니다. Timestamp source는 core 외부에 있으며 최대 64 bit 폭일 수 있습니다. 문서 작성 시점에는 kernel에서 userspace로 timestamp를 전달하는 기능이 아직 구현되지 않았지만 향후 구현할 계획입니다.
Handling RX
~~~~~~~~~~~
Frame reception is handled in NAPI queue, which is enabled from ISR when
the RXNE (RX FIFO Not Empty) bit is set. Frames are read one by one
until either no frame is left in the RX FIFO or the maximum work quota
has been reached for the NAPI poll run (see ). Each frame is then passed
to the network interface RX queue.
An incoming frame may be either a CAN 2.0 frame or a CAN FD frame. The
way to distinguish between these two in the kernel is to allocate either
``struct can_frame`` or ``struct canfd_frame``, the two having different
sizes. In the controller, the information about the frame type is stored
in the first word of RX FIFO.
This brings us a chicken-egg problem: we want to allocate the ``skb``
for the frame, and only if it succeeds, fetch the frame from FIFO;
otherwise keep it there for later. But to be able to allocate the
correct ``skb``, we have to fetch the first work of FIFO. There are
several possible solutions:
#. Read the word, then allocate. If it fails, discard the rest of the
frame. When the system is low on memory, the situation is bad anyway.
#. Always allocate ``skb`` big enough for an FD frame beforehand. Then
tweak the ``skb`` internals to look like it has been allocated for
the smaller CAN 2.0 frame.
#. Add option to peek into the FIFO instead of consuming the word.
#. If the allocation fails, store the read word into driver’s data. On
the next try, use the stored word instead of reading it again.
Option 1 is simple enough, but not very satisfying if we could do
better. Option 2 is not acceptable, as it would require modifying the
private state of an integral kernel structure. The slightly higher
memory consumption is just a virtual cherry on top of the “cake”. Option
3 requires non-trivial HW changes and is not ideal from the HW point of
view.
Option 4 seems like a good compromise, with its disadvantage being that
a partial frame may stay in the FIFO for a prolonged time. Nonetheless,
there may be just one owner of the RX FIFO, and thus no one else should
see the partial frame (disregarding some exotic debugging scenarios).
Basides, the driver resets the core on its initialization, so the
partial frame cannot be “adopted” either. In the end, option 4 was
selected [5]_.
.. _subsec:ctucanfd:rxtimestamp:
Timestamping RX frames
^^^^^^^^^^^^^^^^^^^^^^
The CTU CAN FD core reports the exact timestamp when the frame has been
received. The timestamp is by default captured at the sample point of
the last bit of EOF but is configurable to be captured at the SOF bit.
The timestamp source is external to the core and may be up to 64 bits
wide. At the time of writing, passing the timestamp from kernel to
userspace is not yet implemented, but is planned in the future.
TX buffer FIFO, priority 회전과 timestamp
391-477TX 처리
CTU CAN FD core에는 독립된 TX buffer 4개가 있고 각각 state와 priority를 가집니다. Core가 전송할 때 Ready state인 buffer 가운데 priority가 가장 높은 것을 선택합니다.
Priority는 `TX_PRIORITY` register에 nibble 정렬된 3 bit 값으로 저장되어 대부분의 용도에 충분합니다. 그러나 SocketCAN은 outgoing frame용 FIFO queue 하나만 지원합니다. Buffer마다 서로 다른 priority를 부여하고 frame 전송이 끝날 때마다 priority를 *회전*시키면 네 buffer로 FIFO 동작을 모사할 수 있습니다.
Software는 priority 회전과 함께 네 TX buffer가 이루는 FIFO의 head·tail pointer를 유지해야 합니다. `txb_head`는 다음 frame을 넣을 buffer, `txb_tail`은 가장 먼저 완료되어야 할 buffer를 가리킵니다. 실제 index는 TX buffer 수인 4를 modulus로 삼지만, FIFO full과 empty를 구분하려면 pointer는 최소 한 bit 더 넓어야 합니다. 두 상태 모두 `txb_head ≡ txb_tail (mod 4)`일 수 있기 때문입니다.
첫 상태에서는 buffer 0·1·2에 A·B·C가 있고 priority는 7·6·5·4이며 tail은 1, head는 3입니다. A가 완료된 뒤 priority를 회전하면 buffer 0은 비고 priority는 4·7·6·5가 됩니다. 이어 D를 buffer 3에 넣고 E를 wrap-around한 buffer 0에 넣으면 sequence는 E·B·C·D이고 tail은 1, head는 다음 주기의 0을 가리킵니다.
`fsm_txt_buffer_user.svg`는 TX buffer state와 가능한 transition을 보여 줍니다.
TX frame timestamp
TX buffer에 frame을 제출할 때 전송을 시작해도 되는 timestamp를 지정할 수 있습니다. 전송은 그 시각보다 늦게 시작할 수 있지만 더 일찍 시작할 수는 없습니다. Timestamp는 buffer priority 결정에는 참여하지 않으며, priority는 앞에서 설명한 mechanism만으로 정합니다.
시간 기반 packet transmission 지원은 Linux v4.19에 `Time-based packet transmission <https://lwn.net/Articles/748879/>`_으로 merge되었지만 CAN에서 이 기능이 실용적인지는 추가 연구가 필요합니다.
RX frame timestamp를 가져오는 것과 마찬가지로 core는 TX frame이 성공적으로 전달된 시각도 제공합니다. 세부 사항은 RX timestamp와 거의 같습니다.
엄밀히 말하면 Linux v4.19부터 `can: enable multi-queue for SocketCAN devices <https://lore.kernel.org/patchwork/patch/913526/>`_를 통해 여러 CAN TX queue가 지원되지만, 문서 작성 시점에는 이를 쓰는 mainline driver가 없었습니다.
Handling TX
~~~~~~~~~~~
The CTU CAN FD core has 4 independent TX buffers, each with its own
state and priority. When the core wants to transmit, a TX buffer in
Ready state with the highest priority is selected.
The priorities are 3bit numbers in register TX_PRIORITY
(nibble-aligned). This should be flexible enough for most use cases.
SocketCAN, however, supports only one FIFO queue for outgoing
frames [6]_. The buffer priorities may be used to simulate the FIFO
behavior by assigning each buffer a distinct priority and *rotating* the
priorities after a frame transmission is completed.
In addition to priority rotation, the SW must maintain head and tail
pointers into the FIFO formed by the TX buffers to be able to determine
which buffer should be used for next frame (``txb_head``) and which
should be the first completed one (``txb_tail``). The actual buffer
indices are (obviously) modulo 4 (number of TX buffers), but the
pointers must be at least one bit wider to be able to distinguish
between FIFO full and FIFO empty – in this situation,
:math:`txb\_head \equiv txb\_tail\ (\textrm{mod}\ 4)`. An example of how
the FIFO is maintained, together with priority rotation, is depicted in
|
+------+---+---+---+---+
| TXB# | 0 | 1 | 2 | 3 |
+======+===+===+===+===+
| Seq | A | B | C | |
+------+---+---+---+---+
| Prio | 7 | 6 | 5 | 4 |
+------+---+---+---+---+
| | | T | | H |
+------+---+---+---+---+
|
+------+---+---+---+---+
| TXB# | 0 | 1 | 2 | 3 |
+======+===+===+===+===+
| Seq | | B | C | |
+------+---+---+---+---+
| Prio | 4 | 7 | 6 | 5 |
+------+---+---+---+---+
| | | T | | H |
+------+---+---+---+---+
|
+------+---+---+---+---+----+
| TXB# | 0 | 1 | 2 | 3 | 0’ |
+======+===+===+===+===+====+
| Seq | E | B | C | D | |
+------+---+---+---+---+----+
| Prio | 4 | 7 | 6 | 5 | |
+------+---+---+---+---+----+
| | | T | | | H |
+------+---+---+---+---+----+
|
.. kernel-figure:: fsm_txt_buffer_user.svg
TX Buffer states with possible transitions
.. _subsec:ctucanfd:txtimestamp:
Timestamping TX frames
^^^^^^^^^^^^^^^^^^^^^^
When submitting a frame to a TX buffer, one may specify the timestamp at
which the frame should be transmitted. The frame transmission may start
later, but not sooner. Note that the timestamp does not participate in
buffer prioritization – that is decided solely by the mechanism
described above.
Support for time-based packet transmission was recently merged to Linux
v4.19 `Time-based packet transmission <https://lwn.net/Articles/748879/>`_,
but it remains yet to be researched
whether this functionality will be practical for CAN.
Also similarly to retrieving the timestamp of RX frames, the core
supports retrieving the timestamp of TX frames – that is the time when
the frame was successfully delivered. The particulars are very similar
to timestamping RX frames and are described in .
RX overrun, error state 보고와 source reference
478-534RX buffer overrun 처리
수신 frame 전체가 hardware RX FIFO에 더 이상 들어가지 않으면 RX FIFO overrun flag `STATUS[DOR]`가 설정되고 Data Overrun Interrupt `DOI`가 발생합니다. Interrupt 처리 시 먼저 `COMMAND[CDO]`로 DOR flag를 지운 뒤 DOI interrupt flag를 지워야 합니다. 순서를 바꾸면 다음 clock cycle에 interrupt가 즉시 다시 armed됩니다.
개발 중에는 내부 HW pipelining 때문에 이 clear sequence가 깨질 수 있어 flag와 interrupt를 지우는 사이에 dummy cycle이 필요한지 논의했습니다. Avalon interface에서는 실제로 필요했지만 APB는 2-cycle transaction을 사용하므로 안전했습니다.
문제 상황에서는 DOR flag는 지워졌지만 DOI clear request가 적용되는 cycle에도 DOI register의 Preset input이 여전히 high였습니다. Register의 Set 우선순위가 Reset보다 높아 DOI flag가 reset되지 않았습니다. Issue #187에서 Set/Reset priority를 바꾸어 이미 수정했습니다.
Error Passive와 Bus Off condition 보고
Node가 *Error Passive*, *Error Warning*, *Bus Off* 상태에 도달한 시점을 보고하는 것이 유용할 수 있습니다. Driver는 EPI 또는 EWLI interrupt로 error state 변경을 통지받고 error counter를 읽어 core의 현재 error state를 판단합니다.
상태 전환과 interrupt 발생 뒤 error counter를 읽기까지 지연이 있어 작은 race condition이 존재합니다. EPI를 받았을 때 node는 *Error Passive* 또는 *Bus Off*일 수 있습니다. *Bus Off*가 되면 reset할 때까지 그 상태에 남지만, 그 외에는 현재 또는 과거에 *Error Passive*였다는 뜻입니다. Counter를 읽는 순간에는 이미 *Error Warning*이나 *Error Active*로 회복했을 수도 있습니다.
이 경우 무엇을 보고할지는 명확하지 않지만 저자는 지나간 error condition도 보고해야 한다고 봅니다. 마찬가지로 EWLI를 받았으나 확인 시점에 *Error Passive*라면 더 심한 상태인 *Error Passive*를 보고해야 합니다.
CTU CAN FD driver source reference
- `drivers/net/can/ctucanfd/ctucanfd.h`의 internal kernel-doc
- `drivers/net/can/ctucanfd/ctucanfd_base.c`의 internal kernel-doc
- `drivers/net/can/ctucanfd/ctucanfd_pci.c`의 internal kernel-doc
- `drivers/net/can/ctucanfd/ctucanfd_platform.c`의 internal kernel-doc
Handling RX buffer overrun
~~~~~~~~~~~~~~~~~~~~~~~~~~
When a received frame does no more fit into the hardware RX FIFO in its
entirety, RX FIFO overrun flag (STATUS[DOR]) is set and Data Overrun
Interrupt (DOI) is triggered. When servicing the interrupt, care must be
taken first to clear the DOR flag (via COMMAND[CDO]) and after that
clear the DOI interrupt flag. Otherwise, the interrupt would be
immediately [7]_ rearmed.
**Note**: During development, it was discussed whether the internal HW
pipelining cannot disrupt this clear sequence and whether an additional
dummy cycle is necessary between clearing the flag and the interrupt. On
the Avalon interface, it indeed proved to be the case, but APB being
safe because it uses 2-cycle transactions. Essentially, the DOR flag
would be cleared, but DOI register’s Preset input would still be high
the cycle when the DOI clear request would also be applied (by setting
the register’s Reset input high). As Set had higher priority than Reset,
the DOI flag would not be reset. This has been already fixed by swapping
the Set/Reset priority (see issue #187).
Reporting Error Passive and Bus Off conditions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It may be desirable to report when the node reaches *Error Passive*,
*Error Warning*, and *Bus Off* conditions. The driver is notified about
error state change by an interrupt (EPI, EWLI), and then proceeds to
determine the core’s error state by reading its error counters.
There is, however, a slight race condition here – there is a delay
between the time when the state transition occurs (and the interrupt is
triggered) and when the error counters are read. When EPI is received,
the node may be either *Error Passive* or *Bus Off*. If the node goes
*Bus Off*, it obviously remains in the state until it is reset.
Otherwise, the node is *or was* *Error Passive*. However, it may happen
that the read state is *Error Warning* or even *Error Active*. It may be
unclear whether and what exactly to report in that case, but I
personally entertain the idea that the past error condition should still
be reported. Similarly, when EWLI is received but the state is later
detected to be *Error Passive*, *Error Passive* should be reported.
CTU CAN FD Driver Sources Reference
-----------------------------------
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd.h
:internal:
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_base.c
:internal:
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_pci.c
:internal:
.. kernel-doc:: drivers/net/can/ctucanfd/ctucanfd_platform.c
:internal:
개발 기여와 주석
535-638CTU CAN FD IP core와 driver 개발 감사의 글
- Odrej Ille `<ondrej.ille@gmail.com>`: CTU FEE Department of Measurement의 학생으로 project를 시작했고, 여러 해 동안 많은 개인 시간과 열정을 투자했으며 추가 지원 과제에도 참여했습니다.
- `Department of Measurement <https://meas.fel.cvut.cz/>`_, `Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_, `Czech Technical University <https://www.cvut.cz/en>`_: 여러 해 동안 project의 주 투자자였고 `Skoda Auto <https://www.skoda-auto.cz/>`_용 CAN/CAN FD diagnostics framework에 project를 사용합니다.
- `Digiteq Automotive <https://www.digiteqautomotive.com/en>`_: CAN FD Open Cores Support Linux Kernel Based Systems project를 지원했고, project 공개 접근을 허용하도록 CTU와 협상하고 비용을 지불했으며 추가 작업도 지원했습니다.
- `Department of Control Engineering <https://control.fel.cvut.cz/en>`_, FEE, CTU: CAN FD Open Cores Support Linux Kernel Based Systems project를 수행하고 GitLab 관리, continuous integration용 virtual server와 계산 자원, HIL continuous integration test hardware를 제공했습니다.
- `PiKRON Ltd. <http://pikron.com/>`_: project 공개 준비를 시작할 수 있도록 소규모 지원을 제공했습니다.
- Petr Porazil `<porazil@pikron.com>`: PCIe transceiver addon board를 설계·조립하고 MicroZed/Zynq system용 MZ_APO baseboard를 설계·조립했습니다.
- Martin Jerabek `<martin.jerabek01@gmail.com>`: Linux driver 개발, continuous integration platform 설계, GHDL update를 담당했고 `Open-source and Open-hardware CAN FD Protocol Support <https://dspace.cvut.cz/bitstream/handle/10467/80366/F3-DP-2019-Jerabek-Martin-Jerabek-thesis-2019-canfd.pdf>`_ 논문을 작성했습니다.
- Jiri Novak `<jnovak@fel.cvut.cz>`: CTU FEE Department of Measurement에서 project 시작, 관리, 활용을 담당했습니다.
- Pavel Pisa `<pisa@cmp.felk.cvut.cz>`: 공개화를 시작하고 project를 조정했으며 CTU FEE Department of Control Engineering에서 관리했습니다.
- Jaroslav Beran `<jara.beran@gmail.com>`: Intel SoC system integration, core·driver test와 update를 담당했습니다.
- Carsten Emde와 `OSADL <https://www.osadl.org/>`_: IP core license 논의를 위한 전문 지식을 제공했습니다. LGPL과 CAN bus 관련 특허 가능성의 deadlock을 지적해 IP core 설계를 BSD 계열 license로 다시 허가하는 계기가 되었습니다.
- Reiner Zitzmann, Holger Zeltwanger와 `CAN in Automation <https://www.can-cia.org/>`_: community에 project를 알리는 데 조언과 도움을 주고 CAN bus 미래 개발 방향 행사에 초대했습니다.
- Jan Charvat: QEMU mainline에 통합된 CTU CAN FD functional model을 구현했습니다. 관련 문서는 `docs/system/devices/can.rst <https://www.qemu.org/docs/master/system/devices/can.html>`_이며 학사 논문은 Model of CAN FD Communication Controller for QEMU Emulator입니다.
주석
- [1] 다른 bus에는 device를 설정하는 고유 driver interface가 있습니다.
- [2] 여기의 error frame은 CAN Error Frame과 혼동하면 안 됩니다. `CAN_ERR_FLAG`가 설정되고 `data` field에 error 정보가 든 `can_frame`입니다.
- [3] 저수준 code는 CTU CAN FD repository `<https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_에서 구할 수 있습니다.
- [4] `PROP`와 `PH1` 재분배는 `ctucan_hw_set_nom_bittiming`, `ctucan_hw_set_data_bittiming`에서 수행합니다.
- [5] 논문 작성 시점에는 RX allocation 해법 1을 사용 중이었고 변경은 GitLab issue #222에 대기 중이었습니다.
- [6] Linux v4.19부터 SocketCAN multi-queue가 지원되지만 당시 이를 사용하는 mainline driver는 없었습니다.
- [7] DOR와 DOI를 잘못된 순서로 지우면 정확히는 다음 clock cycle에 interrupt가 다시 armed됩니다.
CTU CAN FD IP Core and Driver Development Acknowledgment
---------------------------------------------------------
* Odrej Ille <ondrej.ille@gmail.com>
* started the project as student at Department of Measurement, FEE, CTU
* invested great amount of personal time and enthusiasm to the project over years
* worked on more funded tasks
* `Department of Measurement <https://meas.fel.cvut.cz/>`_,
`Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_,
`Czech Technical University <https://www.cvut.cz/en>`_
* is the main investor into the project over many years
* uses project in their CAN/CAN FD diagnostics framework for `Skoda Auto <https://www.skoda-auto.cz/>`_
* `Digiteq Automotive <https://www.digiteqautomotive.com/en>`_
* funding of the project CAN FD Open Cores Support Linux Kernel Based Systems
* negotiated and paid CTU to allow public access to the project
* provided additional funding of the work
* `Department of Control Engineering <https://control.fel.cvut.cz/en>`_,
`Faculty of Electrical Engineering <http://www.fel.cvut.cz/en/>`_,
`Czech Technical University <https://www.cvut.cz/en>`_
* solving the project CAN FD Open Cores Support Linux Kernel Based Systems
* providing GitLab management
* virtual servers and computational power for continuous integration
* providing hardware for HIL continuous integration tests
* `PiKRON Ltd. <http://pikron.com/>`_
* minor funding to initiate preparation of the project open-sourcing
* Petr Porazil <porazil@pikron.com>
* design of PCIe transceiver addon board and assembly of boards
* design and assembly of MZ_APO baseboard for MicroZed/Zynq based system
* Martin Jerabek <martin.jerabek01@gmail.com>
* Linux driver development
* continuous integration platform architect and GHDL updates
* thesis `Open-source and Open-hardware CAN FD Protocol Support <https://dspace.cvut.cz/bitstream/handle/10467/80366/F3-DP-2019-Jerabek-Martin-Jerabek-thesis-2019-canfd.pdf>`_
* Jiri Novak <jnovak@fel.cvut.cz>
* project initiation, management and use at Department of Measurement, FEE, CTU
* Pavel Pisa <pisa@cmp.felk.cvut.cz>
* initiate open-sourcing, project coordination, management at Department of Control Engineering, FEE, CTU
* Jaroslav Beran<jara.beran@gmail.com>
* system integration for Intel SoC, core and driver testing and updates
* Carsten Emde (`OSADL <https://www.osadl.org/>`_)
* provided OSADL expertise to discuss IP core licensing
* pointed to possible deadlock for LGPL and CAN bus possible patent case which lead to relicense IP core design to BSD like license
* Reiner Zitzmann and Holger Zeltwanger (`CAN in Automation <https://www.can-cia.org/>`_)
* provided suggestions and help to inform community about the project and invited us to events focused on CAN bus future development directions
* Jan Charvat
* implemented CTU CAN FD functional model for QEMU which has been integrated into QEMU mainline (`docs/system/devices/can.rst <https://www.qemu.org/docs/master/system/devices/can.html>`_)
* Bachelor thesis Model of CAN FD Communication Controller for QEMU Emulator
Notes
-----
.. [1]
Other buses have their own specific driver interface to set up the
device.
.. [2]
Not to be mistaken with CAN Error Frame. This is a ``can_frame`` with
``CAN_ERR_FLAG`` set and some error info in its ``data`` field.
.. [3]
Available in CTU CAN FD repository
`<https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
.. [4]
As is done in the low-level driver functions
``ctucan_hw_set_nom_bittiming`` and
``ctucan_hw_set_data_bittiming``.
.. [5]
At the time of writing this thesis, option 1 is still being used and
the modification is queued in gitlab issue #222
.. [6]
Strictly speaking, multiple CAN TX queues are supported since v4.19
`can: enable multi-queue for SocketCAN devices <https://lore.kernel.org/patchwork/patch/913526/>`_ but no mainline driver is using
them yet.
.. [7]
Or rather in the next clock cycle
요약·해설
ctucanfd-driver.rst:1-638CTU CAN FD는 VHDL로 구현된 공개 CAN FD soft core이며 APB·PCIe 등으로 통합할 수 있습니다. Linux driver는 platform별 probe 계층과 공통 SocketCAN network 계층을 분리하고, NAPI RX poll, bit timing 보정, 네 TX buffer의 priority 회전, timestamp와 error state 보고를 담당합니다.
동일한 core를 여러 hardware와 software 환경에 연결합니다.
Bus의 enumeration 지원 여부가 probe 경로를 결정합니다.
Platform 자원 관리와 CAN network 동작을 분리합니다.
Network subsystem과 hardware에서 들어온 event가 각 handler로 향합니다.
고빈도 frame을 interrupt마다 끝까지 처리하지 않고 poll 구간으로 묶습니다.
CAN core에 필요한 단순 transaction만 APB adaptor로 제공합니다.
Nominal bitrate와 Data bitrate 각각에 같은 계산을 적용합니다.
FIFO 첫 word를 소비해야 frame type을 아는 문제에 저장 방식으로 대응합니다.
Core 외부 timestamp source를 frame event와 함께 사용합니다.
원문의 세 상태를 구조화해 head·tail과 priority 변화를 보존합니다.
DOR을 먼저 지우지 않으면 DOI가 다음 clock cycle에 다시 설정됩니다.
Interrupt와 counter 읽기 사이의 지연을 고려해 더 심한 과거 상태도 보고합니다.