Skip to content

system/zbus: Port the Zephyr zbus message bus to NuttX - #3743

Open
JorgeGzm wants to merge 1 commit into
apache:masterfrom
JorgeGzm:port_zbus
Open

system/zbus: Port the Zephyr zbus message bus to NuttX#3743
JorgeGzm wants to merge 1 commit into
apache:masterfrom
JorgeGzm:port_zbus

Conversation

@JorgeGzm

Copy link
Copy Markdown
Contributor

Summary

This PR ports the Zephyr RTOS zbus (a many-to-many message bus with
typed channels and decoupled observers) to NuttX, preserving the
original declarative API (ZBUS_CHAN_DEFINE, ZBUS_LISTENER_DEFINE,
ZBUS_SUBSCRIBER_DEFINE, zbus_chan_pub/read/notify, ...) so that
existing Zephyr application code and documentation translate directly.
Original zbus by Rodrigo Peixoto (Apache-2.0); copyright preserved in
the derived files.

Ported features: listeners (synchronous callbacks), subscribers
(queue of channel references), message subscribers (ordered message
copies), async listeners (callback on the LP work queue), a deferred
ISR-safe publisher (ZBUS_ISR_PUBLISHER_DEFINE / zbus_isr_pub),
runtime observers, per-observation notification masks, observer
enable/disable, message validators, channel user data, publish
statistics, lookup by name/id and channel/observer iteration.

Everything is built on native NuttX primitives, with no compatibility
shim layer:

Zephyr NuttX
k_sem channel lock + priority boost (HLP) sem_t + native CONFIG_PRIORITY_INHERITANCE
k_msgq / k_fifo + net_buf pools kernel message queues (file_mq_*), lazy-opened; mq payload copy replaces net_buf entirely
k_work work_queue() (LP queue)
SYS_INIT lazy init via pthread_once()
iterable sections include/nuttx/iterable_sections.h (companion PR)
k_timeout_t milliseconds, CLOCK_MONOTONIC deadlines (ZBUS_NO_WAIT / ZBUS_FOREVER)

Board integration (why one linker-script line matters here)

In Zephyr, zbus works on every board out of the box because boards have
no linker scripts: a single common per-arch linker template already
includes the shared common-rom.ld/common-ram.ld fragments where the
zbus iterable sections are collected. In NuttX each board owns its .ld,
so an adopting board needs one of:

  • two #include <nuttx/linker/common-{rom,ram}.ld> lines in its board
    script (done for linum-stm32h753bi, the first adopter, in the
    companion PR), or
  • CONFIG_ZBUS_LINKER_INSERT=y (zero-touch INSERT AFTER mode, with
    the MEMORY-region constraint documented in the companion PR).

This is documented in the Kconfig help, in README.rst and in the
Sphinx page added by the companion PR.

Also included:

  • examples/zbus (CONFIG_EXAMPLES_ZBUS): a runnable demo with one channel,
    one listener, one subscriber, runtime masking.
  • testing/zbus (CONFIG_TESTING_ZBUS): a cmocka suite, 16 tests
    covering the full API surface: pub/read/listener/subscriber,
    multi-channel index isolation, validators, message subscribers
    (ordered copies), bit-exact float/double payload delivery
    (sensor-style message with a float-math validator rejecting
    non-finite samples), claim/finish/notify, masks, enable/disable,
    runtime observers (error paths included), async listeners (bursts),
    ISR publisher, from_name/from_id, iteration, accessors, and
    timeout/overflow semantics.

Not ported (documented in the "Not ported" section of the docs):
multi-domain proxy agent (experimental upstream), direct publishing from
ISRs (the deferred zbus_isr_pub helper covers the use case), the
priority-boost/HLP scheme (superseded by native priority inheritance)
and the net_buf pool machinery (unnecessary with mq payload copies).

Impact

  • New optional application (CONFIG_ZBUS, default n); no impact
    when disabled.
  • Requirements: FLAT build (uses kernel-side file_mq_* so queues
    survive the creating task, since mqd_t is a per-task fd in NuttX);
    CONFIG_MQ_MAXMSGSIZE >= sizeof(pointer) + CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE when message subscribers are
    enabled (checked and documented in Kconfig); board linker integration
    as described above.
  • Build process: Make and CMake supported.
  • Documentation: README.rst here; full Sphinx documentation (with
    the upstream zbus diagrams, Apache-2.0) lands with the companion
    nuttx PR.
  • License: Apache-2.0, derived from Zephyr zbus; original copyright
    retained.

Testing

Host: Ubuntu 24.04.4 x86_64, Arm GNU Toolchain 13.2.rel1
(arm-none-eabi-gcc 13.2.1).

Target: linum-stm32h753bi:zbus (STM32H753BI; board configuration
added by the companion nuttx PR): CONFIG_ZBUS with all options
enabled (message/async/ISR observers, runtime observers),
CONFIG_EXAMPLES_ZBUS, CONFIG_TESTING_ZBUS. Flashed via ST-LINK V3;
console on the ST-LINK VCP.

nsh> uname -a
NuttX 13.0.1-RC0 a1b0941bec Aug 20 2026 21:17:15 arm linum-stm32h753bi

Example (zbus): 5 messages published; listener correctly skips the
masked message #4; listener notified before the subscriber (observer
priority order):

zbus: publishing 5 messages to acc_chan
zbus:  listener: x=1 y=10 z=100
zbus:  subscriber: x=1 y=10 z=100
...
zbus: listener masked
zbus:  subscriber: x=3 y=30 z=300
zbus: listener unmasked
zbus:  subscriber: x=4 y=40 z=400
zbus:  listener: x=5 y=50 z=500
zbus:  subscriber: x=5 y=50 z=500
zbus: done

cmocka suite: 16/16 passing, run twice in the same boot (guards
against the lazy-init/fd-lifetime regression class):

nsh> cmocka_zbus_test
[==========] tests: Running 16 test(s).
...
[ RUN      ] test_float_payload
[       OK ] test_float_payload
...
[==========] tests: 16 test(s) run.
[  PASSED  ] 16 test(s).

(The could not notify observer: -42 line during test_timeouts is the
expected -ENOMSG queue-overflow path being exercised.)

Builds: Make (make -j) and CMake (cmake -GNinja + ninja) both
OK for the target config; _zbus_* iterable-section symbols verified in
the map on both. Stock build with CONFIG_ZBUS disabled: no zbus
sections/symbols (no-op). tools/checkpatch.sh -g on the commit: all
checks pass.

The companion nuttx PR adds a linum-stm32h753bi:zbus board
configuration reproducing this exact run
(./tools/configure.sh linum-stm32h753bi:zbus); since that defconfig
enables Kconfig symbols introduced here, the two PRs should land
together.

Port of the Zephyr RTOS zbus (many-to-many message bus with typed
channels and decoupled observers), built entirely on native NuttX
primitives and preserving the original declarative API
(ZBUS_CHAN_DEFINE, ZBUS_LISTENER_DEFINE, ZBUS_SUBSCRIBER_DEFINE, ...).

Features: listeners (synchronous callbacks), subscribers (queue of
channel references), message subscribers (ordered message copies),
async listeners (callback on the LP work queue), a deferred ISR-safe
publisher (ZBUS_ISR_PUBLISHER_DEFINE/zbus_isr_pub), runtime observers,
per-observation notification masks, observer enable/disable, message
validators, channel user data, publish statistics, lookup by
name/numeric id and channel/observer iteration.

Mapping to NuttX primitives:
- Channel/observer registration: link-time iterable sections
  (include/nuttx/iterable_sections.h, added to nuttx in a companion
  commit); notification masks live in .bss with their initial value
  preserved in ROM and applied on lazy init.
- Channel lock: sem_t (enable CONFIG_PRIORITY_INHERITANCE instead of
  the Zephyr priority-boost/HLP).
- Subscriber queues: kernel message queues (file_mq_*) opened lazily
  via pthread_once, usable from any task; mq payload copying replaces
  the Zephyr net_buf machinery entirely.
- Async listeners: work_queue() on the LP work queue.
- Timeouts: milliseconds with CLOCK_MONOTONIC deadlines
  (ZBUS_NO_WAIT/ZBUS_FOREVER).

Includes a runnable example (examples/zbus, CONFIG_EXAMPLES_ZBUS) and a
cmocka test suite (testing/zbus, CONFIG_TESTING_ZBUS) covering the full
API: 16/16 tests passing on linum-stm32h753bi hardware, including
multi-channel index grouping, mask semantics, runtime observer error
paths, queue overflow/timeout semantics, async listener bursts and
bit-exact float/double payload delivery across every observer type
(sensor-style messages with a float-math validator).

Requirements: FLAT build; CONFIG_MQ_MAXMSGSIZE >= pointer size +
CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE for message subscribers; board
linker script including <nuttx/linker/common-rom.ld> or
CONFIG_ZBUS_LINKER_INSERT.

Not ported: multi-domain proxy agent (experimental upstream); direct
ISR publishing (use the deferred zbus_isr_pub helper instead).

Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant