Skip to content

feat(autoresearch): LPC845 SPI+DMA async driver validation harness (#3456) - #3503

Merged
zackees merged 1 commit into
masterfrom
feat/lpc-spi-dma-harness
Jul 1, 2026
Merged

feat(autoresearch): LPC845 SPI+DMA async driver validation harness (#3456)#3503
zackees merged 1 commit into
masterfrom
feat/lpc-spi-dma-harness

Conversation

@zackees

@zackees zackees commented Jul 1, 2026

Copy link
Copy Markdown
Member

Closes #3456.

Summary

Phase 1 of the #3453 bench bring-up series. Adds the device-side + host-side plumbing to exercise ARMHardwareSPIOutputDMA<> (from #3454) on real LPC845 silicon via the AutoResearch RPC contract, mirroring the pattern established by --pwm-dma-cl (#3468).

Device-side

examples/AutoResearch/AutoResearchSpiDma.h, self-gated on FL_IS_ARM_LPC_845 && FASTLED_LPC_SPI_DMA, exposes three RPC handlers:

  • dmaSpiTransferOnce(byte_count, byte_pattern) — single-shot DMA transfer with CPU-poll wait; reports timing.
  • dmaSpiTransferOverlap(byte_count, byte_pattern) — kicks the stream then runs a volatile beacon-toggle counter until done(). Non-zero counter under DMA load is the affirmative async claim (proves the CPU was free to run application code while DMA drove SPI TX).
  • dmaSpiMeasureSck(divider_hint) — 512-byte burst; host computes effective SCK Hz via wall-clock. No SCT input capture on SCK (that's LPC845-BRK: WS2812 loopback validation via SCT-capture RX channel #3015 Phase 3 territory).

Wired into AutoResearchLowMemory.h with a compile-time #error if the user tries to combine FASTLED_LPC_SPI_DMA with FASTLED_LPC_PWM_DMA — both claim DMA0 channels and the LowMemory flash budget doesn't fit both.

Host-side

  • --dma-spi flag on bash autoresearch, LPC845-only, mutually exclusive with --pwm-dma-cl (both would fight over DMA0 channels).
  • New _run_lpc_dma_spi_tests phase entry in ci/autoresearch/phases.py.
  • ci/autoresearch/test_lpc_dma_spi.py test runner: 4 cases —
    • transferOnce@32B — small transfer sanity
    • transferOnce@256B — crosses the driver's polled-vs-DMA fast-path threshold at 16B
    • transferOverlap@512B — affirmative async proof (requires non-zero beacon-toggle count)
    • measureSck@512B — ±25% band vs. compile-time divider

Compile-time contract

The user compiles with -DFASTLED_LPC_SPI_DMA=1 (add to platformio.ini or fbuild config) before running the wrapper. Optional overrides:

  • -DFASTLED_LPC_SPI_DMA_HARNESS_DATA_PIN=<N> (default 17)
  • -DFASTLED_LPC_SPI_DMA_HARNESS_CLOCK_PIN=<N> (default 13)
  • -DFASTLED_LPC_SPI_DMA_HARNESS_DIVIDER=<N> (default 6 → 4 MHz SCK on the 24 MHz LPC845 FRO)
  • -DFASTLED_LPC_SPI_DMA_CHANNEL=4 (SPI1 default per driver)

Silicon status

Silicon validation deferred to the next hardware bring-up run — the harness compiles clean on host and gates out on all non-LPC targets. This PR delivers the plumbing that #3456 requested; the pass/fail bands themselves are only meaningful when run against real LPC845 hardware.

Test plan

  • bash compile wasm --examples AutoResearch — WASM compiles clean (gates correctly compile out on non-LPC)
  • bash test --cpp --clean — 344/344 tests pass
  • bash lint — clean
  • bash autoresearch lpc845 --dma-spi — pending silicon access

Related

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new LPC845 SPI+DMA async AutoResearch option for hardware validation.
    • Introduced a serial-based test flow that checks transfer completion, async overlap, and effective clock timing.
    • Expanded the LowMemory example to support the new SPI+DMA harness and RPC handlers.
  • Bug Fixes

    • Prevents incompatible LPC DMA configurations from being selected together.
    • Adds input clamping and safer setup for transfer tests to avoid oversized requests.

…3456)

Phase 1 of the #3453 bench bring-up series. Adds the device-side + host-side
plumbing to exercise `ARMHardwareSPIOutputDMA<>` (from #3454) on real LPC845
silicon via the AutoResearch RPC contract, following the same pattern as
`--pwm-dma-cl` (#3468).

Device-side (`examples/AutoResearch/AutoResearchSpiDma.h`, self-gated on
`FL_IS_ARM_LPC_845 && FASTLED_LPC_SPI_DMA`) exposes three RPC handlers:

- `dmaSpiTransferOnce(byte_count, byte_pattern)` — single-shot DMA transfer
  with CPU-poll wait; reports timing.
- `dmaSpiTransferOverlap(byte_count, byte_pattern)` — kicks the stream then
  runs a `volatile` beacon-toggle counter until `done()`. Non-zero counter
  under DMA load is the affirmative async claim.
- `dmaSpiMeasureSck(divider_hint)` — 512-byte burst; host computes effective
  SCK Hz via wall-clock (no SCT capture — that's #3015 territory).

Wired into `AutoResearchLowMemory.h` with a compile-time `#error` if the
user tries to combine `FASTLED_LPC_SPI_DMA` with `FASTLED_LPC_PWM_DMA`
(both claim DMA0 and the LowMemory flash budget doesn't fit both).

Host-side:

- `--dma-spi` flag on `bash autoresearch`, LPC845-only, mutually exclusive
  with `--pwm-dma-cl` (both would fight over DMA0 channels).
- New `_run_lpc_dma_spi_tests` phase entry in `ci/autoresearch/phases.py`.
- `ci/autoresearch/test_lpc_dma_spi.py` test runner: 4 cases —
  transferOnce@32B, transferOnce@256B (crosses driver's DMA-vs-polled
  fast-path threshold at 16B), transferOverlap@512B (async proof),
  measureSck@512B (±25 % band vs. compile-time divider).

The user compiles with `-DFASTLED_LPC_SPI_DMA=1` (add to platformio.ini or
fbuild config) before running the wrapper. Silicon validation deferred to
next hardware bring-up run — the harness compiles clean on host (WASM +
`bash test --cpp` 344/344 green) and gates out on all non-LPC targets.

Closes #3456 (harness deliverable). #3453 Phase 1 device-side plumbing done;
Phase 2 (clockless SCT+DMA) was already delivered via `--pwm-dma-cl`
(#3468).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zackees
zackees merged commit 43ef4e7 into master Jul 1, 2026
7 of 11 checks passed
@fastled-project-sync fastled-project-sync Bot moved this to Triage in FastLED Tracker Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 563331ba-2d5a-432f-87aa-6a26218520c1

📥 Commits

Reviewing files that changed from the base of the PR and between f62ccbd and de6fbdd.

📒 Files selected for processing (5)
  • ci/autoresearch/args.py
  • ci/autoresearch/phases.py
  • ci/autoresearch/test_lpc_dma_spi.py
  • examples/AutoResearch/AutoResearchLowMemory.h
  • examples/AutoResearch/AutoResearchSpiDma.h

📝 Walkthrough

Walkthrough

This PR adds a Phase 1 SPI+DMA async driver validation harness for LPC845. It introduces a --dma-spi CLI flag with mutual exclusivity against --pwm-dma-cl, a CI phase runner, a Python bench script using JSON-RPC over serial, and a device-side AutoResearch header exposing three RPC handlers backed by a DMA SPI driver.

Changes

SPI+DMA Bench Harness

Layer / File(s) Summary
CLI flag and phase dispatch
ci/autoresearch/args.py, ci/autoresearch/phases.py
Adds --dma-spi flag/field, enforces mutual exclusion with --pwm-dma-cl, and dispatches to a new _run_lpc_dma_spi_tests handler that runs the bench script via uv run.
Host bench script RPC test cases
ci/autoresearch/test_lpc_dma_spi.py
New standalone script implementing send_rpc, CSV result parsing, and three test functions (run_transfer_once, run_transfer_overlap, run_measure_sck) driven via serial JSON-RPC, plus CLI main() wiring and exit codes.
Device-side AutoResearch SPI+DMA header and wiring
examples/AutoResearch/AutoResearchLowMemory.h, examples/AutoResearch/AutoResearchSpiDma.h
Adds FASTLED_LPC_SPI_DMA build gate with compile-time mutual exclusion against FASTLED_LPC_PWM_DMA, binds autoresearch::dma_spi::bind(remote), and implements the DMA harness driver plus three RPC handlers (transfer once, overlap, measure SCK).

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as autoresearch CLI
  participant Phases as phases.py
  participant Bench as test_lpc_dma_spi.py
  participant Device as LPC845 (AutoResearchSpiDma.h)
  CLI->>Phases: --dma-spi flag
  Phases->>Phases: validate mutual exclusion with --pwm-dma-cl
  Phases->>Bench: uv run test_lpc_dma_spi.py --port <port>
  Bench->>Device: send_rpc dmaSpiTransferOnce
  Device-->>Bench: CSV result (timing)
  Bench->>Device: send_rpc dmaSpiTransferOverlap
  Device-->>Bench: CSV result (toggle count)
  Bench->>Device: send_rpc dmaSpiMeasureSck
  Device-->>Bench: CSV result (byte count, time)
  Bench-->>Phases: exit code 0/1
  Phases-->>CLI: return status
Loading

Possibly related issues

Possibly related PRs

  • FastLED/FastLED#3031: Both modify the same args.py/phases.py LPC bring-up dispatcher plumbing with a new boolean flag routed to a dedicated phase runner.
  • FastLED/FastLED#3454: Introduces the ARMHardwareSPIOutputDMA/spi_arm_lpc_dma.h driver that this PR's HarnessDriver and bench directly validate.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/lpc-spi-dma-harness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

AutoResearch: SPI-DMA validation harness for LPC845 (Phase 1 of #3453 bench bring-up)

1 participant