Skip to content

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

Description

@zackees

Scope

Build the device-side and host-side AutoResearch validation harness for the LPC845 SPI + DMA0 async driver that just landed in #3453 / PR #3454. This is the natural next step in the #3453 series — Phase 1 source is in but cannot be exercised until the harness exists and the toolchain blockers (#3450 B1/B2) clear.

Background

PR #3454 added src/platforms/arm/lpc/spi_arm_lpc_dma.h against UM11029 §15 (SPI) and §17 (DMA). The header self-gates on FL_IS_ARM_LPC_845 && FASTLED_LPC_SPI_DMA, so default builds are unaffected. The driver is host-compile clean (340/340 tests pass), but every TODO(3453) marker in it points at a register-level assumption that only silicon can confirm — TXCTL persistence semantics, SYSAHBCLKCTRL0 bit 29 layout, BURSTPOWER default of 0, INMUX wiring for channel overrides > 15, SRAMBASE allocation, chunked-stream support for frames > buffer capacity.

The existing AutoResearch driver flags (--parlio, --rmt, --spi, --object-fled, etc.) all follow the same pattern: a per-driver header (e.g. AutoResearchSimd.h) gets pulled into AutoResearch.ino under a build macro, and the host wrapper in ci/autoresearch/phases.py adds the corresponding -D to the compile + a pass/fail phase that calls RPC bindings on the device.

Deliverables

Device-side: examples/AutoResearch/AutoResearchSpiDma.h

A new harness header, gated on #if defined(FL_IS_ARM_LPC_845) && defined(FASTLED_LPC_SPI_DMA), conditionally included from AutoResearch.ino (or AutoResearchLowMemory.h if the 64 KB flash budget pushes back). Wires three RPC handlers on top of the existing remote.bind(...) infrastructure:

  • dmaSpi.transferOnce(N, byte_pattern) — allocates an N-byte buffer of byte_pattern, calls ARMHardwareSPIOutputDMA::kickDmaStream(buf, N), returns {started_us, done_us, cpu_was_busy_during_transfer: bool}. The "cpu_was_busy" probe is the inverse of the async claim — if true, the DMA stream did not actually offload from the CPU.
  • dmaSpi.transferOverlap(N, byte_pattern) — kicks the same stream but also runs a tight beacon-toggle loop on the CPU until done() returns true. Reports {toggle_count, completion_us}. Pass criterion: toggle_count >= floor(completion_us * expected_toggles_per_us). This is the affirmative async proof — non-zero toggle count under DMA load.
  • dmaSpi.measureSck(freq_target, divider) — drives a known waveform; host correlates the SCK pin (loopbacked via jumper to an SCT-capture-capable pin) to measure the actual SCK Hz versus the _SPI_CLOCK_DIVIDER template arg.

Implementation reference: existing examples/AutoResearch/AutoResearchSimd.h for the header layout + remote.bind pattern, ARMHardwareSPIOutputDMA<> for the driver surface.

Host-side: ci/autoresearch/phases.py + wrapper

  • Add --dma-spi flag to the bash autoresearch wrapper, parallel to the existing --spi / --parlio / --object-fled flags.
  • Add the corresponding phase entry in ci/autoresearch/phases.py that:
    • Compiles examples/AutoResearch with -DFASTLED_LPC_SPI_DMA -DFASTLED_LPC_SPI_DMA_CHANNEL=4 added to build_flags.
    • Calls the three RPC handlers above in sequence.
    • Pass criteria:
      • transferOnce: done_us - started_us within ±10% of 8 * N / SCK_Hz.
      • transferOverlap: cpu_was_busy_during_transfer == false AND toggle_count >= 0.95 * theoretical_max.
      • measureSck: measured SCK within ±5% of target.
  • On LPC, document the mutual exclusion: --dma-spi and --pwm-dma-cl (Phase 2 of LPC8xx: async DMA output drivers — Phase 1 SPI, Phase 2 clockless bring-up #3453) cannot be enabled in the same build because both consume DMA0 channels and the LowMemory flash budget doesn't fit both. The wrapper should reject --dma-spi --pwm-dma-cl with a clear error.

Loopback wiring

  • On LPC845-BRK, document a single jumper from the SPI0 MOSI pin to one SCT-capture-capable input pin. Reuses the existing LPC_SCT_CAPTURE plumbing (LPC845-BRK: WS2812 loopback validation via SCT-capture RX channel #3015) — once that backend's begin() body lands (referenced as a closed but bench-validation-pending leaf in src/platforms/arm/lpc/rx_sct_capture.cpp.hpp), dmaSpi.transferOnce can self-capture and byte-compare what it transmitted.

Acceptance criteria

  • examples/AutoResearch/AutoResearchSpiDma.h lands with the three RPC bindings.
  • --dma-spi flag in bash autoresearch lpc845 wraps to the new phase.
  • All three pass criteria above are bench-validated on LPC845-BRK and reported in the closing comment with scope/LA captures (or screenshots from the SCT-capture self-loopback path).
  • At least one TODO(3453) marker in spi_arm_lpc_dma.h is closed out as a result of the bench validation (typically TXCTL persistence is the first to confirm or refute).

Dependencies

Hard-blocked by:

  1. [META] LPC8xx compile + autoresearch + channels API end-to-end restoration — all blockers #3450 B1 — fbuild matcher rejects the hyphenated nxp-lpc GitHub URL form. fix(core): match nxp-lpc (hyphenated GitHub form) in platform parser fbuild#900 open; once it lands + fbuild 2.3.15 ships, the pyproject.toml bump unblocks bash compile lpc845brk.
  2. [META] LPC8xx compile + autoresearch + channels API end-to-end restoration — all blockers #3450 B2framework-arduino-lpc8xx tarball checksum mismatch from the 2026-06-28 org transfer. Needs the checksum re-record alongside the next fbuild release.

Soft-blocked by:

  1. [BLOCKER] LPC845-BRK AutoResearch bring-up echo returns empty RX (suspected HardFault) — blocks #3102 #3300 — LPC845-BRK AutoResearch echo silence. Without RPC round-trip there's no way for the host to call any of the three handlers above.

The device-side header and host-side phase code can both land while these are open — they just won't gate CI on a passing run until the blockers clear.

Out of scope

  • Phase 2 (PWM-DMA clockless) validation harness — AutoResearchPwmDmaClockless.h + --pwm-dma-cl host phase. Will get its own follow-up issue once Phase 1's harness is bench-validated.
  • LPC804 SPI DMA — Phase 1 is LPC845-only; LPC804's 4-channel DMA needs separate allocation review.

Relationships

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions