Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codespellrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ exclude-file = .codespell-ignore-lines
skip =
LICENSE,
*/CODEOWNERS,
*/system/zbus/images/*,

# Ignore seemingly misspelled words.
# lowercase: case insensitive
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions Documentation/applications/system/zbus/images/zbus_operations.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
209 changes: 209 additions & 0 deletions Documentation/applications/system/zbus/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
==========================
``zbus`` ZBus message bus
==========================

Port of the `zbus <https://docs.zephyrproject.org/latest/services/zbus/index.html>`_
message bus to NuttX, built entirely on native NuttX primitives. ZBus
implements many-to-many communication through **channels** (typed shared
messages) observed by **observers**, keeping publishers and consumers
fully decoupled.

.. figure:: images/zbus_overview.svg
:alt: zbus usage overview
:width: 75%

A typical zbus application architecture.

Channels and observers are defined statically, in any source file, with
declarative macros. The definitions are collected at link time through
iterable sections (see the Iterable Sections component documentation) --
there is no runtime registration and no central list to maintain.

.. figure:: images/zbus_anatomy.svg
:alt: zbus anatomy
:width: 70%

ZBus anatomy: channels, observers and observations.

Observer types
==============

.. figure:: images/zbus_type_of_observers.svg
:alt: zbus observer types
:width: 70%

The four observer types.

======================== ===================================================
Type Behavior
======================== ===================================================
Listener Callback executed synchronously in the publisher
context (``ZBUS_LISTENER_DEFINE``).
Subscriber Receives channel references through a message
queue; waits with ``zbus_sub_wait()``
(``ZBUS_SUBSCRIBER_DEFINE``).
Message subscriber Receives a *copy* of every published message, in
order; waits with ``zbus_sub_wait_msg()``
(``ZBUS_MSG_SUBSCRIBER_DEFINE``,
``CONFIG_ZBUS_MSG_SUBSCRIBER``).
Async listener Callback executed on a dedicated task with a
copy of the message
(``ZBUS_ASYNC_LISTENER_DEFINE``,
``CONFIG_ZBUS_ASYNC_LISTENER``).
======================== ===================================================

Runtime observers (``zbus_chan_add_obs()``/``zbus_chan_rm_obs()``,
``CONFIG_ZBUS_RUNTIME_OBSERVERS``), per-observation notification masks,
observer enable/disable, message validators and channel user data are
also supported.

.. figure:: images/zbus_observation_mask.svg
:alt: zbus observation mask
:width: 75%

Observer enable/disable and per-observation masks: disabling the
observer (b) silences every channel; masking observations (c, d)
silences individual channels.

Example
=======

The figure below shows the kind of decoupled architecture zbus enables:
every block only talks to channels, so each one can be replaced without
touching the others.

.. figure:: images/zbus_operations.svg
:alt: zbus sensor-based application
:width: 85%

A sensor-based application built on zbus.

.. code-block:: c

#include <system/zbus.h>

struct acc_msg
{
int x;
int y;
int z;
};

static void listener_cb(const struct zbus_channel *chan)
{
const struct acc_msg *msg = zbus_chan_const_msg(chan);
printf("x=%d y=%d z=%d\n", msg->x, msg->y, msg->z);
}

ZBUS_LISTENER_DEFINE(acc_listener, listener_cb);
ZBUS_SUBSCRIBER_DEFINE(acc_subscriber, 4);

ZBUS_CHAN_DEFINE(acc_chan, /* Name */
struct acc_msg, /* Message type */
NULL, /* Validator */
NULL, /* User data */
ZBUS_OBSERVERS(acc_listener, /* Observers, in */
acc_subscriber),/* priority order */
ZBUS_MSG_INIT(.x = 0, .y = 0, .z = 0));

/* Publisher: */

struct acc_msg msg = { 1, 10, 100 };
zbus_chan_pub(&acc_chan, &msg, 1000);

/* Subscriber thread: */

const struct zbus_channel *chan;
if (zbus_sub_wait(&acc_subscriber, &chan, ZBUS_FOREVER) == 0)
{
zbus_chan_read(chan, &msg, 500);
}

A complete runnable example is available in ``apps/examples/zbus``
(``CONFIG_EXAMPLES_ZBUS``), and a cmocka test suite covering the whole
API in ``apps/testing/zbus`` (``CONFIG_TESTING_ZBUS``).

Not ported
==========

The following Zephyr zbus features are **not available** in this port:

* **Multi-domain proxy agent** (``CONFIG_ZBUS_PROXY_AGENT``): bridges
channels between domains/cores over IPC. Experimental upstream and
tied to the Zephyr IPC service; a NuttX equivalent would be built on
rpmsg and is left as future work.
* **Publishing from interrupt handlers**: the Zephyr original allows
``zbus_chan_pub()`` from ISRs with ``K_NO_WAIT``. This port is a
userspace library and its primitives (semaphores, message queues, lazy
initialization) are not ISR-safe: interrupt handling belongs to the
driver, which should hand the data to a thread (the usual NuttX
pattern) that then publishes it. ``test_timer_driven_publisher`` in
``apps/testing/zbus`` shows the pattern with a kernel timer interrupt
delivering a signal to a sampling thread.
* **Priority boost (Highest Locker Protocol)**
(``CONFIG_ZBUS_PRIORITY_BOOST``): the Zephyr hand-rolled protection
against priority inversion during the notification process. Not
needed: enable the native ``CONFIG_PRIORITY_INHERITANCE`` so the
channel semaphores get equivalent protection from the kernel.
* **net_buf pools and pool isolation**
(``CONFIG_ZBUS_MSG_SUBSCRIBER_BUF_*``): obsolete by design in this
port -- message queues copy the payload on ``mq_send``, so no shared
reference-counted buffers exist at all.
* **Static/user-provided runtime observer nodes**
(``CONFIG_ZBUS_RUNTIME_OBSERVERS_NODE_ALLOC_STATIC/NONE``): runtime
observer nodes are always heap-allocated in this port.

Differences from the Zephyr original
====================================

* Timeouts are plain milliseconds (``int32_t``): ``ZBUS_NO_WAIT`` (0) and
``ZBUS_FOREVER`` (-1) replace ``K_NO_WAIT``/``K_FOREVER``.
* Subscriber queues are POSIX message queues opened lazily on first API
use through the kernel ``file_mq_*`` interface, making them usable from
any task (a ``mqd_t`` descriptor would die with the opening task).
* Initialization is lazy (``pthread_once`` on the first API call)
replacing the Zephyr ``SYS_INIT`` hook; no explicit init call is
needed.
* Async listeners run on a dedicated task per listener (spawned on
first use; priority and stack size are configurable) instead of the
Zephyr system work queue.

Configuration
=============

Requirements:

* The board linker script must provide the zbus iterable sections, either
by including ``<nuttx/linker/common-rom.ld>`` inside ``.text`` (see the
``linum-stm32h753bi`` board) or through ``CONFIG_ZBUS_LINKER_INSERT``
(zero-touch mode; see its help text for the MEMORY-layout constraint).
* ``CONFIG_MQ_MAXMSGSIZE`` must be at least
``CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE`` plus the size of a pointer
when message subscribers or async listeners are used, otherwise their
queues fail to open with ``-EINVAL``.
* FLAT build (the library uses the kernel ``file_mq_*`` interface
directly).

Main options:

* ``CONFIG_ZBUS`` -- enable the library.
* ``CONFIG_ZBUS_CHANNEL_NAME`` / ``CONFIG_ZBUS_OBSERVER_NAME`` -- name
fields and lookup by name.
* ``CONFIG_ZBUS_CHANNEL_ID`` -- numeric channel identifiers
(``ZBUS_CHAN_DEFINE_WITH_ID``, ``zbus_chan_from_id()``).
* ``CONFIG_ZBUS_MSG_SUBSCRIBER`` -- message subscribers
(+ ``_MAX_MSG_SIZE``, ``_QUEUE_SIZE``).
* ``CONFIG_ZBUS_ASYNC_LISTENER`` -- async listeners (+ ``_PRIORITY``,
``_STACKSIZE`` of their tasks).
* ``CONFIG_ZBUS_RUNTIME_OBSERVERS`` -- runtime observers.
* ``CONFIG_ZBUS_CHANNEL_PUBLISH_STATS`` -- publish timestamp/count.
* ``CONFIG_ZBUS_ASSERT_MOCK`` -- invalid parameters return ``-EFAULT``
instead of asserting (for tests).

Credits
=======

The zbus design and the diagrams in this page come from the upstream
`Zephyr zbus documentation
<https://docs.zephyrproject.org/latest/services/zbus/index.html>`_
by Rodrigo Peixoto and contributors (Apache License 2.0).
Original file line number Diff line number Diff line change
Expand Up @@ -1298,3 +1298,28 @@ This example demonstrates how to use the CAN-FD peripherals can0 and can1 with t
can0 051 [8] 00 11 22 33 44 55 66 77
can0 051 [16] 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF


zbus
----

Enables the zbus message bus library (``apps/system/zbus``, a port of the
Zephyr zbus — see :doc:`its documentation
</applications/system/zbus/index>`) with all observer types (listeners,
subscribers, message subscribers, async listeners, ISR publisher and
runtime observers), together with its example application::

nsh> zbus
zbus: publishing 5 messages to acc_chan
zbus: listener: x=1 y=10 z=100
zbus: subscriber: x=1 y=10 z=100
...
zbus: done

The cmocka test suite from ``apps/testing/zbus`` is also included and can
be used to validate the whole zbus API on the board::

nsh> cmocka_zbus_test
[==========] tests: Running 16 test(s).
...
[==========] tests: 16 test(s) run.
[ PASSED ] 16 test(s).
74 changes: 74 additions & 0 deletions boards/arm/stm32h7/linum-stm32h753bi/configs/zbus/defconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_STANDARD_SERIAL is not set
CONFIG_ALLOW_MIT_COMPONENTS=y
CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD="linum-stm32h753bi"
CONFIG_ARCH_BOARD_LINUM_STM32H753BI=y
CONFIG_ARCH_CHIP="stm32h7"
CONFIG_ARCH_CHIP_STM32=y
CONFIG_ARCH_CHIP_STM32H753BI=y
CONFIG_ARCH_CHIP_STM32H7=y
CONFIG_ARCH_CHIP_STM32H7_CORTEXM7=y
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_SETJMP_H=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_ARMV7M_DCACHE=y
CONFIG_ARMV7M_DCACHE_WRITETHROUGH=y
CONFIG_ARMV7M_DTCM=y
CONFIG_ARMV7M_ICACHE=y
CONFIG_BOARD_LOOPSPERMSEC=43103
CONFIG_BUILTIN=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_EXAMPLES_ALARM=y
CONFIG_EXAMPLES_ZBUS=y
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=4096
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBM=y
CONFIG_LINE_MAX=64
CONFIG_MM_REGIONS=4
CONFIG_MQ_MAXMSGSIZE=96
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_DISABLE_IFUPDOWN=y
CONFIG_NSH_DISABLE_VCONFIG=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=245760
CONFIG_RAM_START=0x20010000
CONFIG_RAW_BINARY=y
CONFIG_RR_INTERVAL=200
CONFIG_RTC_ALARM=y
CONFIG_RTC_DATETIME=y
CONFIG_RTC_DRIVER=y
CONFIG_SCHED_CPULOAD_SYSCLK=y
CONFIG_SCHED_WAITPID=y
CONFIG_STACK_COLORATION=y
CONFIG_START_DAY=6
CONFIG_START_MONTH=12
CONFIG_START_YEAR=2011
CONFIG_STM32_PWR=y
CONFIG_STM32_RTC=y
CONFIG_STM32_USART1=y
CONFIG_SYSTEM_NSH=y
CONFIG_TASK_NAME_SIZE=20
CONFIG_TESTING_CMOCKA=y
CONFIG_TESTING_ZBUS=y
CONFIG_USART1_SERIAL_CONSOLE=y
CONFIG_ZBUS=y
CONFIG_ZBUS_ASYNC_LISTENER=y
CONFIG_ZBUS_CHANNEL_ID=y
CONFIG_ZBUS_CHANNEL_NAME=y
CONFIG_ZBUS_CHANNEL_PUBLISH_STATS=y
CONFIG_ZBUS_MSG_SUBSCRIBER=y
CONFIG_ZBUS_OBSERVER_NAME=y
CONFIG_ZBUS_RUNTIME_OBSERVERS=y
2 changes: 2 additions & 0 deletions boards/arm/stm32h7/linum-stm32h753bi/scripts/flash.ld
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ SECTIONS
*(.got)
*(.gcc_except_table)
*(.gnu.linkonce.r.*)
#include <nuttx/linker/common-rom.ld>
_etext = ABSOLUTE(.);
} > flash

Expand Down Expand Up @@ -156,6 +157,7 @@ SECTIONS
*(.data .data.*)
*(.gnu.linkonce.d.*)
CONSTRUCTORS
#include <nuttx/linker/common-ram.ld>
. = ALIGN(4);
_edata = ABSOLUTE(.);
} > sram AT > flash
Expand Down
Loading
Loading