Skip to content

Implement dmspi with STM32F4/F7 support - #2

Merged
JohnAmadis merged 13 commits into
mainfrom
feature/second-try
Sep 26, 2026
Merged

JohnAmadis merged 13 commits into
mainfrom
feature/second-try

Conversation

@JaxCalder

Copy link
Copy Markdown
Contributor

Summary

  • implement the dmspi core and STM32F4/F7 hardware ports
  • add board and MCU configurations, dmdevfs integration, and friend-based software chip select
  • add the spitest utility, including single-device MOSI-to-MISO echo mode
  • fix STM32F7 8-bit data-register access and overrun clearing

Testing

  • cmake --build build-hardware -j4
  • flashed and tested on STM32F746G-DISCO
  • ran spitest --echo /dev/dmspix/arduino_spi
  • verified two consecutive 16-byte loopback transfers without reset

JohnAmadis and others added 7 commits September 21, 2026 18:58
Testing on real STM32F746G-DISCO hardware found two bugs:

- The cs_path device path format was wrong: dmdevfs mounts alt-named
  devices under /dev/dmgpio<port_index>/<name>, not a flat /dev/<name>.
  Fixed in all 8 board configs.

- More fundamentally, dmspi_dmdrvi_create() was opening cs_path eagerly,
  which fails whenever dmspi's own config is part of dmdevfs's initial
  boot-time scan (the normal case): dmdevfs's /dev mount isn't marked
  "ready" until that whole scan finishes, so Dmod_FileOpen() on any other
  /dev path fails during that window even when the path is correct. Fix
  the path alone wasn't enough - confirmed the same path opens fine from
  an interactive shell after boot, but not from within the boot scan
  itself.

  Fixed by opening cs_path lazily on first transfer instead of at create
  time, which sidesteps the ordering problem entirely.

Verified end-to-end on hardware: dmspi now creates successfully, the
device appears under /dev, spitest opens it and queries its role, and a
transfer correctly asserts/deasserts the CS pin (confirmed via readback)
before timing out cleanly (bounded, no hang) with nothing wired to MISO.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The cs_path design - Dmod_FileOpen() on a dmdevfs /dev path, opened
lazily, driven via hardcoded dmgpio ioctl constants - had three real
problems: the /dev/dmgpio<port_index>/<name> path format is an
implementation detail the caller had to compute by hand (got it wrong
twice while writing the board configs); the ioctl constants were a
manually-copied guess at dmgpio's ABI; and the lazy-open only existed
to work around dmdevfs's /dev mount not being "ready" during its own
boot-time config scan, so a broken CS pin only surfaced on first
transfer, not at config time.

dmgpio now exposes a pin-lease API (dmgpio_lease.h) built for exactly
this: dmgpio_pin_acquire/_release/_write, a direct Built-in API
dependency resolved by the loader at module-load time, not a runtime
/dev lookup, with real collision detection (dmgpio_port_claim_pins).
cs_pin names the pin directly ("PE3", the same syntax dmgpio's own
pin= already uses) and dmspi claims it eagerly in _create() - a bad or
already-claimed pin now fails at config time instead of silently on
first transfer. The lease also configures the pin itself, so the
separate [xxx_cs] dmgpio board-config section is gone entirely.

Requires a dmgpio release with the pin-lease API and the packaging fix
in choco-technologies/dmgpio (pushed to master there); dmod_link_modules
here will not resolve dmgpio's headers until that release exists.
Verified locally in the meantime by building against the fixed dmgpio
checkout directly (stm32f4, stm32f7, and the x86_64 "core" build all
compile and link cleanly).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bumps every DIF registration to the 2.0 interface version dmdrvi.h now
requires, including _friend_changed (used here for the GPIO chip-select
friend relationship). The functional changes are in _read/_write (offset
-> dmdrvi_offset_t, return type -> dmdrvi_ssize_t, with explicit
-EOVERFLOW/-EINVAL guards) and _stat (size -> dmdrvi_size_t).

dmspi is transaction-like (SPI transfers), not a seekable/addressable
device, so offset is validated (must be non-negative) but otherwise
ignored, same treatment dmclk gives its own non-seekable device. The RX
ring path clamps (rather than silently truncates) a request larger than
dm_sw_ring's own uint32_t capacity type before handing it to that
separate module's interface.

_open already took the full (context, flags, dev_num) parameter list
required by the contract, so no signature-drift fix was needed here.

Part of choco-technologies/dmod-ecosystem#8.
…ation

Migrate to dmdrvi 2.0 (64-bit I/O contract)
JaxCalder and others added 6 commits September 26, 2026 17:10
dmod_link_modules only downloads headers at configure time and records
runtime dependencies into the generated dmspi.dmd - it doesn't stage
the actual .dmf blobs into build/dmf. The "Run tests with dmod_loader"
step called dmod_loader directly without that fetch step, so dmhaman
(a runtime dependency of dmspi) was never present, failing every run
with "Cannot load module by name - module not found: dmhaman".

The ctest target in tests/CMakeLists.txt already does this correctly
(dmf-get install -d test_dmspi-local.dmd before dmod_loader) - this
just brings the CI workflow's manual step in line with it, fetching
dmspi's own dependencies via dmf-get -d build/dmf/dmspi.dmd first.

Confirmed locally: dmhaman.dmf/dmini.dmf/dm_sw_ring.dmf are absent from
build/dmf before this step and present after it.
…dependency

CI: fetch dmspi's runtime dependencies before running dmod_loader
dmspi.dmd lists dmspi_port as a dependency, but this job never needs
the real port (it only builds against dmspi_port_if headers, see the
comment on the build step), and dmspi_port has no published x86_64
package anyway. Under `sh -e`, dmf-get returning non-zero because of
that one irrelevant, expected-to-fail lookup aborted the script before
dmod_loader ever ran - so the previous fix (fetching dmhaman/dmini/
dm_sw_ring) never actually got exercised.

`|| true` on the dmf-get line lets that fail without masking a real
problem: if dmhaman/dmini/dm_sw_ring genuinely failed to fetch,
dmod_loader would still fail loudly and clearly on the missing module.
…nal-in-core-job

CI: don't let dmspi_port's missing x86_64 build abort dmf-get install
test_dmspi's dmod_loader-based test needs the full dmspi module
enabled, which pulls in its declared dmspi_port dependency - but the
real port is STM32 hardware register access with no host build, and
no x86_64 release has ever been published for it. Every CI run failed
with "Cannot load required module 'dmspi_port'" once the earlier
missing-dmhaman/dmspi_port-lookup issues were fixed.

Added src/port/x86_64/port.c: a self-contained software loopback
(bytes written via _transmit/_transfer's tx side are queued into the
same instance's FIFO or RX ring, and drained by _receive/_transfer's
rx side) that doesn't touch real hardware, dmclk_port, or dmosi, so it
has no further unpublished dependencies of its own - confirmed its
generated .dmd only requires dm_sw_ring, which does have an x86_64
release.

Wired into the CI workflow: the core build step now also builds
dmspi_port for DMOD_CPU_FAMILY=x86_64 so the .dmf is already staged
in build/dmf before dmod_loader runs, and the discover-cpu-families /
build-dmspi matrix job will now also produce a real x86_64 release
artifact for it going forward.
Setting DMOD_CPU_FAMILY=x86_64 on the same configure that builds dmspi
itself broke dmgpio's header fetch: dmod_link_modules keys dmgpio's
header download off DMOD_CPU_FAMILY too (not just DMOD_TOOLS_NAME), and
dmgpio has never published an x86_64 package - only stm32f4/stm32f7,
which is also all its own release pipeline ever builds. Previously
DMOD_CPU_FAMILY defaulted to stm32f7 and that fetch worked by accident;
overriding it to x86_64 made it request the (nonexistent)
dmgpio-v1.0-x86_64.zip and 404, breaking the dmspi/test_dmspi build
that already worked in the previous run.

Split it into its own build_dmspi_port_x86_64/ configure that only
builds the dmspi_port target, then copy just its .dmf/.dmd into the
main build/dmf/ before the dmf-get + dmod_loader step. Confirmed
locally: dmspi/test_dmspi build cleanly with the default CPU family,
dmspi_port builds cleanly in the separate x86_64 configure, and the
copied dmspi_port.dmf survives dmf-get's (expected, `|| true`'d)
failed remote lookup for it.

Also fixes the same DMOD_CPU_FAMILY=x86_64 problem in the
discover-cpu-families/build-dmspi matrix job: it now builds only the
dmspi_port target for the x86_64 entry instead of the default "all",
which was hitting the identical dmgpio 404 and getting the whole
matrix cancelled via fail-fast.
@JohnAmadis
JohnAmadis merged commit 0747d91 into main Sep 26, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants