Skip to content
Merged
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
47 changes: 45 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,55 @@ jobs:
# dmspi links against dmspi_port_if (headers only),
# not dmspi_port itself, so building only these two targets
# never touches src/port/*/port.c - safe to build natively here
# regardless of which CPU family the port defaults to.
# regardless of which CPU family the port defaults to. Leave
# DMOD_CPU_FAMILY at its default (stm32f7): dmod_link_modules'
# header fetch for dmgpio keys off of it too, and dmgpio has never
# published an x86_64 package - only stm32f4/stm32f7, which is
# also all its release pipeline ever builds. Overriding it to
# x86_64 here 404s that fetch, since dmgpio's headers don't come
# from a family-independent package.
cmake .. -DDMOD_MODE=DMOD_MODULE -DDMOD_TOOLS_NAME=arch/x86_64
cmake --build . --target dmspi test_dmspi

- name: Build x86_64 dmspi_port loopback stub
run: |
# Separate build dir/configure so DMOD_CPU_FAMILY=x86_64 here can't
# affect the dmgpio header fetch above. dmspi_port itself has no
# x86_64 release (it's real STM32 register access) - this builds
# src/port/x86_64/port.c, a software loopback stub that exists
# purely so dmod_loader has something to satisfy dmspi's runtime
# dmspi_port dependency with below.
mkdir -p build_dmspi_port_x86_64
cd build_dmspi_port_x86_64
cmake .. -DDMOD_MODE=DMOD_MODULE -DDMOD_TOOLS_NAME=arch/x86_64 -DDMOD_CPU_FAMILY=x86_64
cmake --build . --target dmspi_port
cp dmf/dmspi_port.dmf dmf/dmspi_port.dmd ../build/dmf/

- name: Verify module files
run: |
echo "Checking for module files..."
ls -lh build/dmf/
test -f build/dmf/dmspi.dmf
test -f build/dmf/dmspi_version.txt
test -f build/dmf/test_dmspi.dmf
test -f build/dmf/dmspi_port.dmf
echo "Module files present"

- name: Run tests with dmod_loader
run: |
export DMOD_DMF_DIR=$(pwd)/build/dmf
# dmod_link_modules only fetches headers at configure time; dmspi's
# own runtime dependencies (e.g. dmhaman) still need to be staged
# into build/dmf before dmod_loader can resolve them, same as the
# ctest target defined in tests/CMakeLists.txt already does.
# dmspi.dmd also lists dmspi_port, which has no published x86_64
# release (only the real stm32f4/stm32f7 hardware ports are
# published) - the build step above already placed our own
# x86_64 dmspi_port.dmf (the loopback stub) in build/dmf, so
# `|| true` just lets dmf-get's redundant remote lookup for it
# fail without masking a real failure to fetch dmhaman/dmini/
# dm_sw_ring.
dmf-get install -d build/dmf/dmspi.dmd -y || true
dmod_loader build/dmf/test_dmspi.dmf

discover-cpu-families:
Expand Down Expand Up @@ -92,7 +125,17 @@ jobs:
cd build_$CPU_FAMILY

cmake .. -DDMOD_CPU_FAMILY="${CPU_FAMILY}"
cmake --build .
if [ "$CPU_FAMILY" = "x86_64" ]; then
# x86_64 only exists as a software-loopback stand-in for the
# real (hardware-only) dmspi_port - it has no dmspi release of
# its own to build, and building the full "all" target here
# would also re-fetch dmgpio's headers keyed off this same
# DMOD_CPU_FAMILY, which 404s since dmgpio never published an
# x86_64 package (see the core job's comment on this).
cmake --build . --target dmspi_port
else
cmake --build .
fi

echo "Build completed for CPU family: ${CPU_FAMILY}"
ls -lh dmf/
Expand Down
22 changes: 17 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION}
src/dmspi.c
)

dmod_link_modules(${DMOD_MODULE_NAME}
dmdrvi
dmini
dmhaman
dm_sw_ring
dmgpio
)

target_include_directories(${DMOD_MODULE_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
Expand All @@ -75,12 +83,16 @@ target_include_directories(${DMOD_MODULE_NAME}_if INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)

# To depend on another module's headers, use:
# dmod_link_modules(${DMOD_MODULE_NAME}
# other_module@1.0
# )

add_subdirectory(src/port)
target_link_libraries(${DMOD_MODULE_NAME} ${DMOD_MODULE_NAME}_port_if)
enable_testing()
add_subdirectory(tests)

# ======================================================================
# spitest Tool
# ======================================================================
# Manual device test tool - flashed onto a real board to exercise a real
# master<->slave SPI link against an already-dmdevfs-configured device (see
# tools/spitest/README.md). Not part of the ctest suite: it needs real
# hardware, a dmdevfs config, and a human to wire it up.
add_subdirectory(tools/spitest)
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ DMOD_CXXSOURCES=
DMOD_INC_DIRS=include

# The list of libraries to link
DMOD_LIBS=
DMOD_LIBS=dmdrvi dmini dmhaman dm_sw_ring dmspi_port

# The list of definitions
DMOD_DEFINITIONS=
Expand Down
113 changes: 86 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,33 @@
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/choco-technologies/dmspi/actions/workflows/ci.yml/badge.svg)](https://github.com/choco-technologies/dmspi/actions/workflows/ci.yml)

dmspi DMOD library module.
SPI driver module for the DMOD ecosystem, supporting both master and slave
roles.

## Description

TODO: describe what this module does.
dmspi is a hardware driver (like `dmuart`/`dmgpio`): it registers with
`dmdrvi` and is used through the generic device API (create/open/read/
write/ioctl/close), configured from an INI file via `dmini`. It supports:

- Master and slave roles
- Configurable clock polarity/phase (CPOL/CPHA, or a `mode=0..3` shorthand)
- MSB-first or LSB-first bit order
- Software or hardware NSS (chip-select) management
- Polling or interrupt-driven operation, with an optional RX ring buffer
(useful for slave mode, where reception isn't driven by the local CPU)
- True full-duplex transfers via `dmspi_ioctl_cmd_transfer`

Like `dmuart`/`dmgpio`, dmspi only configures the SPI peripheral itself.
Pin muxing is done externally via `dmgpio`; in `nss_mode=soft`, an optional
GPIO in the same `friends_group` with `friend_role=chip_select` is discovered
through `dmdevfs` and toggled with the public GPIO ioctl through its device
path around each transaction.

See [docs/configuration.md](docs/configuration.md) for the full
configuration reference and a usage example, and
[configs/](configs/README.md) for ready-made per-board/per-MCU INI files
(the same `dmdevfs`-loadable format `dmuart`/`dmgpio`/`dmclk` use).

## Building

Expand Down Expand Up @@ -48,54 +70,76 @@ dmf-get install -d ${DMOD_DMF_DIR}/test_dmspi-local.dmd -y
dmod_loader build/dmf/test_dmspi.dmf
```

## Usage

<TBD>

This library module provides functions that can be used by other modules:

```c
#include "dmspi.h"
```
This exercises hardware-independent logic only (safe to run on the host).
For a real master<->slave SPI link on actual hardware, see
[tools/spitest/](tools/spitest/) - a small CLI tool, flashed onto a board
and run from the `dmell` shell, that opens an already-`dmdevfs`-configured
device by path (see [configs/](configs/README.md)) and exercises it. It
supports both a single-board loopback (two devices wired together) and a
two-board test (one board master, one board slave).

## API

| Function | Description |
|----------|-------------|
| `dmspi_create()` | Create a new `dmspi_t` instance. |
| `dmspi_destroy()` | Destroy an instance created by `_create()`. |
| `dmspi_is_valid()` | Check whether a handle is a valid instance. |
## Usage

See [include/dmspi.h](include/dmspi.h) for the full
declarations and [docs/api-reference.md](docs/api-reference.md) for the
complete reference.
The normal way to use a dmspi device is through `dmdevfs`, like any other
file: open the path `dmdevfs` created for it
(`Dmod_FileOpen("/dev/dmspi1", "r+")`), then `Dmod_Ioctl`/`Dmod_FileClose`
(portable SAL wrappers declared in `dmod.h`, no extra module to link) - see
[tools/spitest/main.c](tools/spitest/main.c) for a complete, build-tested
example.

Writing your own driver-adjacent code that talks to dmspi *without* going
through a mounted path is also possible, but lower-level: dmspi is a
separate loadable module, so its `dmdrvi` entry points
(`dmdrvi_dmspi_create`, `_open`, `_ioctl`, ...) must be resolved dynamically
via `Dmod_GetDifFunction()` rather than called by name - see
[docs/configuration.md](docs/configuration.md) for why and how. See
[docs/api-reference.md](docs/api-reference.md) for the complete API
reference.

## Documentation

See the `docs/` directory:

- **[api-reference.md](docs/api-reference.md)** - Complete API documentation
- **[configuration.md](docs/configuration.md)** - INI configuration reference and usage example
- **[port-implementation.md](docs/port-implementation.md)** - How the port is structured and how to add another architecture

View documentation using `dmf-man dmspi`.

## Configuration Files

[configs/](configs/README.md) has ready-made, `dmdevfs`-loadable INI files
per board (SPI pins muxed via `dmgpio` + the `dmspi` section itself) and per
MCU (pin-agnostic defaults) - the same layout and mechanism `dmuart`,
`dmgpio`, and `dmclk` use.

## Hardware Port

This module ships two DMOD modules: the architecture-independent
`dmspi` and `dmspi_port`, which contains the
architecture-specific implementation. The active architecture is selected via
architecture-specific implementation. `stm32f4` and `stm32f7` are supported
today, sharing a common SPI register implementation (both families use the
same SPI IP block). The active architecture is selected via
`DMOD_CPU_FAMILY` (default: `stm32f7`):

```bash
cmake .. -DDMOD_CPU_FAMILY=stm32f7
cmake .. -DDMOD_CPU_FAMILY=stm32f7 # or stm32f4
```

See [docs/port-implementation.md](docs/port-implementation.md) for how to add
another architecture. Port-specific files:
See [docs/port-implementation.md](docs/port-implementation.md) for how the
common/per-family split works and how to add another architecture.
Port-specific files:

```
├── include/dmspi_port.h
├── src/port/
│ ├── CMakeLists.txt
│ ├── stm32_common/ # shared STM32 SPI register implementation
│ │ ├── stm32_common.h
│ │ └── stm32_common.c
│ ├── stm32f4/
│ │ ├── config.cmake
│ │ └── port.c
│ └── stm32f7/
│ ├── config.cmake
│ └── port.c
Expand All @@ -105,17 +149,32 @@ another architecture. Port-specific files:

```
dmspi/
├── configs/ # per-board/per-MCU INI files - see "Configuration Files" above
│ ├── board/
│ └── mcu/
├── docs/ # Documentation (markdown format)
├── examples/
│ └── config.ini # minimal, board-agnostic sample
├── include/ # Public headers
│ └── dmspi.h
│ ├── dmspi.h
│ ├── dmspi_port.h
│ └── dmspi_types.h
├── src/
│ └── dmspi.c
│ ├── dmspi.c
│ └── port/ # see "Hardware Port" above
├── tests/
│ ├── CMakeLists.txt
│ └── dmspi_test.c
├── tools/
│ └── spitest/ # hardware self-test - see "Testing" above
│ ├── CMakeLists.txt
│ ├── main.c
│ ├── README.md
│ └── spitest.dmr
├── CMakeLists.txt
├── Makefile
├── dmspi.dmr
├── dmspi_port.dmr
└── manifest.dmm
```

Expand Down
Loading
Loading