Skip to content

Add Rigs for Zephyr documentation - #526

Merged
tobiaskaestner merged 12 commits into
mainfrom
fer-510/documentation
Sep 22, 2026
Merged

tobiaskaestner merged 12 commits into
mainfrom
fer-510/documentation

Conversation

@tobiaskaestner

@tobiaskaestner tobiaskaestner commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

This is the current as-is documentation for rigc ported to the Bridle documentation ecosystem. It contains

  • tutorials
  • howtos
  • explanations
  • references

following the diataxis framework for technical documentation.

In addition this PR includes some improvements for shields that carry SPI devices:
Specifically, the computation inside rigc now honors

when the cs-gpios property has already been set by the board
when the board itself has already registered devices on the SPI a socket exposes to shields.

tobiaskaestner and others added 7 commits September 20, 2026 19:39
board/project.py's projection already holds the board's own SPI
controller node at BoardSocket construction time but never reads its
cs-gpios property or its existing child device nodes -- the two facts
a chip-select-preservation fix needs. model.BusRef gains
existing_cs_gpios (the board's own cs-gpios array, as (gpio controller
label, pin, flags) tuples in array order) and existing_child_regs (the
reg values already held by the controller's own pre-existing children,
independent of the array's own length, since a board may legally
declare a child whose reg indexes past its cs-gpios array). Both are
SPI-only: board/project.py::_project_existing_spi gates on
is_bus_kind(qualified, "spi") and is never called for an i2c/uart bus.

Both are read straight off edtlib's already-resolved graph (a
cs-gpios phandle-array's controller/pin/flags, a child node's own
reg), never reconstructed from raw DTS source text. A gpio phandle-array
whose specifier cells are not named pin/flags (not the universal
<pin> <flags> shape every gpio-controller binding in this tree
declares) raises rather than silently misreading a cell.

Nothing downstream reads these two fields yet -- analyzer/cs.py and
emitter/overlay.py, next commits -- so this is a read-only projection
step, exercised here at the projection boundary alone: three new
test_board_project.py tests (existing_cs_gpios, existing_child_regs,
and the SPI-only gate proven against the SAME socket's i2c bus, which
gets a real child of its own in the fixture so the gate is actually
exercised rather than vacuously true). The fixture SPI controller
(spi_ctrl0) reproduces the mikroe_quail.dts/nucleo_f401re.dts shape
synthetically: a 2-entry board-authored cs-gpios array plus one
pre-existing child at reg <0>, described by a new fixture binding,
tests/fixtures/dts/bindings/fixturetest-spi-ctrl.yaml.

mypy clean; tests/unit/board/test_board_project.py: 38 passed (35
pre-existing + 3 new).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
emitter/overlay.py's _spi_scopes built a controller's cs-gpios property
purely from the rig's own chip-select placements and wrote it as a
plain DT property assignment -- which REPLACES the whole array. A
board that already authors its own cs-gpios on that controller, or its
own children directly under it, had both silently destroyed the
moment a rig placed anything on the same bus: an existing child's own
`reg` survives unchanged into a truncated array, so
SPI_CS_GPIOS_DT_SPEC_GET's out-of-range index silently yields an empty
gpio_dt_spec -- the board's own chip select simply never asserts, no
build error, no warning.

The board's own array entries (BusRef.existing_cs_gpios/
existing_child_regs, previous commit) are now preserved rather than
replaced:

- analyzer/cs.py::_fold_cs_placements numbers a rig-placed device's
  own reg/array-index REUSE-then-APPEND: when a placement's resolved
  SoC pin (its net key, the same identity soc_net/check_nets compare
  two claims by) already names one of the board's own entries, the
  device's `reg` becomes THAT entry's index and nothing is appended --
  appending a second entry for a pin the board's array already
  carries would name the same physical pin twice. Only a placement
  whose pin is genuinely absent from the board's array gets a fresh
  index, numbered after the board's own array length. A reused entry
  keeps the board's own flags: reuse contributes nothing to the
  emitted rig-placed list, so the only text ever rendered for that
  array position is the board's own verbatim tuple.
- A rig-allocated index (reused or appended) that collides with a
  `reg` an existing child of that controller already holds is refused
  with `phys-cs`, naming the controller, the colliding reg, and which
  rig device would have landed there -- a board may legally declare a
  child whose reg indexes past its own cs-gpios array, which the
  reuse/offset numbering alone cannot see.
- Two rig devices reusing the same board entry is not special-cased
  here: both already register a `dedicated` NetClaim on the same net
  key, and the existing check_nets exclusive-conflict check (run by
  the composer after this pass) refuses it the same way it refuses any
  other two-exclusive-claims-one-pin conflict.
- analyzer/sockets.py::_compose_buses carries existing_cs_gpios/
  existing_child_regs through carrier pass-through composition
  unchanged, the same as label/path -- they are facts of the physical
  controller, not of the exposing carrier.
- analyzer/__init__.py threads CsAllocation.cs_gpios_existing through
  the composer onto Solved.
- emitter/overlay.py::_spi_scopes renders the board's own array
  entries first, verbatim (controller label/pin/flags exactly as
  edtlib resolved them), followed by the rig's own (already
  reuse/offset-numbered) placements.

No new diagnostic code: the collision check reuses `phys-cs`, an
existing chip-select-pool finding code.

New unit coverage: 8 tests in test_cs_pass.py (board-authored array
offset-and-preserve, the negative control, the in-array vs.
past-the-array collision check on both the append and reuse paths, the
mixed reuse-and-append scope, the flags sub-decision, and the
two-devices-one-entry exclusive-net-conflict case reproduced against
check_nets by hand) and 2 in test_overlay.py (no existing entries
renders unprefixed; existing entries render first, verbatim, ahead of
an offset rig placement).

Bridle carries no SPI rig today (its two rigs, lotus_buttons and
lotus_pwm_led, are neither SPI), so this unit coverage is the entire
proof at this destination -- no golden or corpus rig exercises the
fix here the way btr-shields' mikroe_quail/nucleo_f401re rigs do.
Confirmed no churn: the full existing unit+integration suite (846
tests) passes unchanged.

Mutation-checked (production code broken, confirmed the relevant new
test(s) fail, restored, confirmed green again, __pycache__ purged
after every restore):
- reuse lookup forced to always miss (`reusing = False` unconditionally)
  -> the 4 reuse tests fail (reuses..., one_device_reuses...,
  reused_flags..., reused_index_still_collides...)
- the entries-append guard forced to always fire regardless of
  `reusing` -> the append-negative-control and mixed-scope tests fail
- `existing_child_regs` collision check disabled -> both the
  append-path and reuse-path collision tests fail
- append offset reset to always start at 0 -> the offset-preserve test
  and the append/mixed-scope tests fail
- `_spi_scopes`'s existing-entries prefix removed -> the preservation
  test fails; its sibling is a negative control with no existing
  entries to lose, so it passes vacuously and pins nothing here
- check_nets's own exclusive-conflict threshold weakened -> the
  two-placements-share-an-entry net-conflict test fails with no
  diagnostic instead of one

mypy clean; scripts/rigc/tests/unit (846... see full count below) and
tests/integration all green, zero golden churn (bridle has none of
this shape in its own corpus).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
Port the rig documentation from btr-shields into a new
doc/bridle/rigs4zephyr/ section, alongside a top-level
doc/bridle/rigs4zephyr.rst landing page
and its toctree entry in doc/bridle/index.rst.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
Port test_api_reference_drift.py, test_cli_reference_drift.py and
test_diagnostics_reference_drift.py from btr-shields'
scripts/rigc/tests/integration/, re-pointed at doc/bridle/rigs4zephyr/
REPO_ROOT itself needed no change: harness.py derives it by walking up
from the test file, so it already resolves to this repo's root.

test_cli_reference_drift.py also walks the whole of doc/ for "west
<cmd>" mentions. Scoped it to doc/bridle/rigs4zephyr/ rather than all
of doc/ -- bridle's doc/ also mirrors Zephyr's own documentation,
which is not that guard's concern.

Mutation-checked test_cli_reference_drift.py and
test_diagnostics_reference_drift.py: broke a documented option and a
documented diagnostic code in turn, confirmed both directions of each
guard failed, restored. (git diff confirms a clean restore.)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
Verified every documented `west rigs`/`west build ... -DRIG=` invocation
against bridle's own two rigs (lotus_buttons, lotus_pwm_led) -- `west
rigs`, `-n`, `-f` with each documented key, `--explain`, `--boards-for`,
and a real `west build -b seeeduino_lotus samples/helloshell -- \
-DRIG=lotus_buttons`, which links clean and emits the expected
rig-gen.overlay (grove_d2/grove_d6/grove_a0 wired to btn_start/btn_stop/
led_status exactly as lotus_buttons.yml declares); `-DSHIELD=` alongside
`-DRIG=` fails configure with the documented error. All of it holds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
The tutorial series follows btr-shields' own corpus throughout (nucleo
boards, an ACME-branded module set that does not exist here). Rewriting
all of it is out of this PR's size; leaving all of it as-is risks a
first tutorial nobody in this repository can actually follow along
with. Policy: rewrite the two tutorials a reader hits FIRST --
build-a-rig-that-exists (the "see it work once" tour) and
build-a-rig-on-the-fly (the ad-hoc single-module shortcut) -- against
bridle's own seeeduino_lotus board and its two real rigs (lotus_buttons,
lotus_pwm_led) and two real shields (grove_btn, grove_led). Leave the
rest (give-a-board-a-socket, add-a-second-socket,
make-the-rig-permanent,
write-a-shield-template) as written, each now carrying a note that its
examples come from btr-shields, the harness repository the rig model
comes from -- read them for the mechanism, not as copy-paste bridle
recipes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
The other half of the tutorial-policy decision from the previous commit:
give-a-board-a-socket, add-a-second-socket, make-the-rig-permanent and
write-a-shield-template stay as written (nucleo_f411re, ACME-branded
modules), each now carrying a short note pointing at
build-a-rig-that-exists's own explanation and naming bridle's real
boards/shields (seeeduino_lotus, grove_btn, grove_led) as the substitute
for anyone wanting to follow along here instead of just reading for the
mechanism.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
@tobiaskaestner
tobiaskaestner changed the base branch from fer-510/cs-preservation to main September 20, 2026 17:43
Shrunk rather than updated in place: the full worked example, the
"missing" argument and the standalone-usage walkthrough are gone
because doc/bridle/rigs4zephyr/ now does that job in more depth and
correctly, not summarized. What is kept is what still needs to live in
this directory for a reader with no docs build at hand -- the one-line
gate command, what check.sh actually runs (now including the drift
guards), the hermeticity note, and the module layout table -- plus one
paragraph up top pointing at the real documentation and stating plainly
that the build integration this file used to argue for has since
landed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>

@rexut rexut left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check all points in documentation. That's why the build fails currently.

Comment thread doc/bridle/rigs4zephyr/explanation/architecture.rst Outdated
tobiaskaestner and others added 2 commits September 20, 2026 21:43
Repointed rather than made relative, to match bridle's existing
house style: doc/bridle/releases/release-notes-3.3.1.rst already
uses absolute :doc: targets rooted at doc/bridle/ (e.g. :doc:`/
tests/shields/grove/dts_bindings/README`). Absolute targets are
easier to grep for and consistent with that precedent; the cost
is that a future move of the rigs4zephyr/ subtree would need the
same rewrite again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
Verifying every documented invocation against this workspace turned
up two more worked examples that a6ee749 didn't reach, both in the
--boards-for surface:

- commands.rst's own reference example runs `west rigs --boards-for
  adafruit_data_logger`. Bridle already carries a real, unrelated
  `adafruit_data_logger` shield (ordinary Zephyr hardware, no
  `template: true`), so the exact command fails here with a
  misleading, unrelated error rather than the shown output.

- give-a-board-a-socket.rst (one of the four tutorials 0c08bbc left
  as btr-shields prose) had one paragraph slip into real bridle
  names: `west rigs --boards-for lotus_buttons` claimed to print
  `seeeduino_lotus/samd21g18a/rig`. Running it here prints nothing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
@github-actions

Copy link
Copy Markdown

You can find the documentation preview for this PR at this link. It will be updated about 10 minutes after the documentation build succeeds.

Note: This comment is automatically posted by the Documentation Publishing GitHub Action.

tobiaskaestner and others added 2 commits September 21, 2026 09:19
Add an explicit ".. _label:" immediately before each page's title,
so the following commit can retarget the :doc: roles at :ref:
without changing what they render.

No autosectionlabel extension is configured in doc/bridle/conf.py,
so labels must be explicit; nothing else supplies them.

Labels mirror the existing "rigs4zephyr" root label and are derived
mechanically from each file's path below doc/bridle/, joined with
underscores (e.g. rigs4zephyr_reference_api_loader). This matches
bridle's own dominant convention of path-derived, underscore-joined
labels (introduction, doc_structure_search, dm_code_base, and so
on) and keeps every label unique in Sphinx's single global label
namespace, which also holds Zephyr's own docs (existing bare names
"glossary" and "index" already collide there).

Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Convert every :doc: role under doc/bridle/rigs4zephyr/ (bare and
the one with explicit text) to :ref: at the target page's new
label, per the house rule that :doc: is discouraged in favour of
:ref:. toctree entries are untouched: they are document paths, not
roles.

Absolute and relative targets are both resolved to the file they
point at first, then to that file's label, so every converted role
renders exactly the title text the bare :doc: role rendered before.

explanation/documentation-guidelines.rst uses :doc: twice as plain
markup in its own prose; its guidance does not itself recommend the
:doc: role, so only the markup needed to change, not the wording.

Signed-off-by: Tobias Kaestner <tobias.kaestner@tiac-systems.net>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@rexut rexut left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Lgtm, many thanks.

@tobiaskaestner
tobiaskaestner merged commit 9112346 into main Sep 22, 2026
6 checks passed
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.

3 participants