One cut, no dual path. Closes ALIGNMENT defect 3 ("Arduino-CLI build
machinery is actively hostile") and vision bullet 4.
platformio.ini carries 16 [env:], one per variant, each passing
-DMEMLNAUT_MODE_TYPE; selftest passes -DNISPS_SELFTEST=1 instead. The env list
IS the registry now — the .ino comment-registry and the NISPS_ST_* token-paste
table are deleted rather than migrated. L12 noted that table was already
silently missing the currently-shipped SLPWorkshop variant, which is the whole
argument against having a second list.
Also deleted: the Python/sed machinery that rewrote the COMMITTED .ino on every
build, the sketch symlink forest, the global TFT_eSPI User_Setup.h mutation
(now -D flags — TFT_eSPI's own documented PlatformIO recipe), the UF2
boot-mount detection stack (upload_protocol=picotool talks to the bootloader
directly), and build-firmware-arch.sh entirely. Scripts 683 -> 435 lines.
memllib is vendored at lib/memllib/ from upstream e291192; no submodules
remain. VENDORED.md records provenance and the re-sync procedure.
S9: a firmware-build CI job compiles three representative envs against a cached
toolchain and reports per-variant flash/RAM. Firmware is in an automated gate
for the FIRST time. The old ci.yml comment justified excluding it as "low
verification value" — an assessment that did not survive contact, since the
SelfTest variant sat broken for an unknown period calling a DisplayDriver
method that did not exist at the pinned memllib commit, and nothing noticed
because nothing built it.
Verified: all 16 envs build from an empty cache, each within ~520 bytes of the
arduino-cli binary it replaces, flash and RAM. Measured as .text+.rodata /
.data+.bss+vector+uninitialized — NOT PlatformIO's console line, which
double-counts .data on this board. This does not prove the hardware boots; no
flash+smoke test was possible and that stays an operator chokepoint.
slpworkshop 248232/145028 pafsynth 256880/149716 selftest 216228/17960
(all 16 in the CI log format; none exceeds 2% of a 16 MB flash)
Two traps recorded so nobody rediscovers them: vendoring memllib's subdirs
without a src/ wrapper makes PlatformIO's library builder silently compile
NOTHING while still linking; and project build_flags land BEFORE the
framework's own -std=gnu++17 -Os, so build_unflags is required.
CORRECTION carried in this commit: the firmware sizes in c19d846's message and
the first version of the memllib recon doc were wrong — SLPWorkshop 145348,
PAFSynth 145300, SelfTest 141840. They came from building variants in sequence
through a SHARED incremental arduino-cli build directory, which reused stale
objects and under-reported by ~75 KB. Clean-cache rebuilds of the identical
commit give 216736/18492 for SelfTest. The real cost of the memllib upstream
bump is +216 bytes flash, not +316. Never measure firmware size through a
reused build dir.
HISTORY NOTE: this commit and the docs commit before it were rebuilt (force-push,
2026-07-21) so that each contains only what its message describes. The first
versions had the firmware deletions stranded in the docs commit by a shared-index
race between concurrent agents; content is byte-identical to the originals.
Gates: run-all-tests.sh ALL GREEN (nisps/ untouched by this change beyond
include paths); 16/16 pio envs build.
59 lines
4.2 KiB
Markdown
59 lines
4.2 KiB
Markdown
# `firmware/` — PlatformIO firmware + hardware glue
|
|
|
|
Thin shell around `nisps/`. The platform-agnostic ML / DSP / engines / modes live there; this directory contains:
|
|
|
|
- `MEMLNaut-NISPS/platformio.ini` — the build: one `[env:<alias>]` per firmware variant (the variant list here IS the registry). See the file itself for the full flag/library rationale.
|
|
- `MEMLNaut-NISPS/src/main.cpp` — entry point. Picks one mode at compile time via `MEMLNAUT_MODE_TYPE` (a per-env build flag, not an in-source registry), hosts the dual-core lifecycle (Core 0: ML + UI, Core 1: audio + MIDI), and connects glue to mode.
|
|
- `MEMLNaut-NISPS/glue/audio_driver.hpp` — bridges memllib's `AudioDriver` block callback to `Mode::process(stereosample_t)`. Templated trampoline so no virtual dispatch in audio path.
|
|
- `MEMLNaut-NISPS/glue/peripherals.hpp` — wires MEMLNaut joystick / pots / buttons to `Mode::set_input(idx, value)` and the ML primitives (`draw_weights`, `move_weights`, `train`, `reset`).
|
|
- `MEMLNaut-NISPS/glue/midi_io.hpp` — binds incoming MIDI to `mode.note_on/note_off/update_bpm/set_playing` (where supported); drains outgoing `ControlEvent`s from the mode's ring buffer to the MIDI UART.
|
|
- `MEMLNaut-NISPS/glue/output_router.hpp` — drains engine events + mode events; called from `loop1()`.
|
|
- `MEMLNaut-NISPS/glue/mode_select.hpp` — type aliases mapping `MEMLNautMode<Name>` identifiers to `nisps::modes::<Name>Mode` C++ types. The active alias is chosen per PlatformIO env (`-DMEMLNAUT_MODE_TYPE=...`), not by rewriting a source file.
|
|
- `MEMLNaut-NISPS/glue/selftest.hpp` — the guided hardware self-test rig (`selftest` env, `-DNISPS_SELFTEST=1`).
|
|
- `MEMLNaut-NISPS/lib/memllib/` — **vendored** hardware-abstraction library (audio driver, TFT display, MIDI I/O, peripherals). Not a submodule — see `lib/memllib/VENDORED.md` for provenance and the re-sync procedure.
|
|
- `useq-celium/` — standalone RP2040 firmware for the uSEQ-Celium USB→CV/gate converter (separate PlatformIO project; unrelated to the MEMLNaut-NISPS build described here).
|
|
|
|
## Building
|
|
|
|
```bash
|
|
scripts/build-firmware.sh # interactive variant prompt
|
|
scripts/build-firmware.sh pafsynth # build a specific variant
|
|
scripts/build-firmware.sh --all # build every variant
|
|
scripts/build-and-flash-firmware.sh # build + flash via picotool
|
|
```
|
|
|
|
These wrap `pio` (PlatformIO Core). If `pio` isn't on `PATH`, run through
|
|
`nix-shell -p platformio-core --run '...'` — **not** `nix-shell -p platformio`,
|
|
which wraps the CLI in a bubblewrap FHS sandbox that fails without a working
|
|
user namespace (containers/sandboxes). `platformio-core` is the same CLI,
|
|
unwrapped. First build downloads the RP2350 platform + toolchain + libraries
|
|
into `~/.platformio` (hundreds of MB) — be patient once, fast after.
|
|
|
|
Target board: `solderparty_rp2350_stamp_xl` (RP2350, Cortex-M33), `-O3`,
|
|
`-std=gnu++20`. See `platformio.ini` for the exact flag/version pins.
|
|
|
|
## Include paths
|
|
|
|
- `nisps/` (platform-neutral, shared with the WASM build) is reached via
|
|
`-I${PROJECT_DIR}/../..` (repo root) — never copied or symlinked into
|
|
`firmware/`. Includes look like `#include "nisps/core/perf.hpp"`.
|
|
- `lib/memllib/` is a PlatformIO private library (`library.properties` +
|
|
`src/` — the standard Arduino 1.5 layout, required for PlatformIO to
|
|
recursively compile its nested subdirectories; see `VENDORED.md`). Its
|
|
`src/` becomes the include root: `#include "audio/AudioDriver.hpp"`, no
|
|
`memllib/` or `src/` prefix.
|
|
- `glue/` is reached via `-I${PROJECT_DIR}`: `#include "glue/audio_driver.hpp"`.
|
|
|
|
There is no git submodule anywhere in this tree.
|
|
|
|
## Verification
|
|
|
|
You cannot test on hardware from a sandbox. We verify:
|
|
|
|
1. Every `[env:...]` in `platformio.ini` builds (`scripts/build-firmware.sh --all`).
|
|
2. Flash/RAM sizes are comparable to an arduino-cli build of the same source
|
|
at the same commit — use `arm-none-eabi-size -A` (text+rodata vs.
|
|
data+bss) rather than `pio run`'s own console "Flash: NN%" line, which
|
|
double-counts `.data` for this board (see the note in `platformio.ini`).
|
|
3. Host C++ tests in `nisps/build` still pass — the firmware build system
|
|
must not perturb the platform-agnostic library (`scripts/run-all-tests.sh`).
|