ADR-005: Embedded platform choices and C/C++ conventions

Status

Accepted

Context

The “from math to metal” pipeline requires embedded C/C++ implementations alongside the Python prototypes. Three topics already have embedded.qmd pages targeting STM32F4 and ESP32. We need to standardize on specific, purchasable hardware and establish conventions before scaling to more topics.

Key constraints: - The STM32F4-Discovery board (used in the original DSBC practicum source material) is discontinued. Students can no longer buy it new. - ADR-002 establishes Python as the primary language. C/C++ is a companion for embedded.qmd pages, not a replacement. - Target general-purpose MCUs, not dedicated DSP chips (per CLAUDE.md).

Decision

1. Two-platform strategy

Platform Board MCU Clock FPU DSP library
ESP32-S3 ESP32-S3-DevKitC Xtensa LX7 dual-core 240 MHz Single-precision ESP-DSP
STM32F4 NUCLEO-F446RE Cortex-M4F 180 MHz Single-precision CMSIS-DSP
  • ESP32-S3 for accessible prototyping: cheap (~EUR 8), built-in WiFi/BT and I2S, ESP-DSP library with optimized FIR/FFT.
  • NUCLEO-F446RE for professional embedded DSP: CMSIS-DSP (the industry-standard ARM DSP library), SIMD for Q15/Q31 fixed-point, deterministic real-time behavior.
  • RP2350 / Pico 2 (Cortex-M33, $5) is referenced in platform comparison tables as a budget alternative but does not get its own code sections.
  • Teensy, dedicated DSP chips (TI C6x, SHARC) are excluded. FPGAs were excluded here originally but are admitted in a bounded way by amendment 1b below.

Audio I/O: pair either board with INMP441 I2S MEMS mic (~EUR 2) and PCM5102/MAX98357A I2S DAC (~EUR 3).

1a. Extended capability ladder (amended 2026-06-15)

The two-platform pair above remains the default for embedded pages. But where an algorithm’s portability and feasibility across capability tiers is itself the lesson, a page may use a wider capability ladder instead, drawn from the HAN Embedded Machine Learning (EML) course board set (Arends and Veen, gitlab.han.nl/aea/ese/eml). The ladder spans, from bottom to top:

Tier Board Core Clock FPU RAM
8-bit, no FPU ATmega328P Xplained Mini AVR 16 MHz none 2 KB
M0+, no FPU FRDM-KL25Z Cortex-M0+ 48 MHz none 16 KB
M4F NUCLEO-F411RE Cortex-M4F 100 MHz single-prec 128 KB
M33 + NPU FRDM-MCXN947 Cortex-M33 ×2 + eIQ Neutron NPU 150 MHz single-prec 512 KB

Rationale: the same portable C runs on every rung, but memory and arithmetic differ by more than an order of magnitude, so “same algorithm, different feasibility” becomes a teachable result rather than an aside. First applied in topics/gabor-filters/embedded.qmd (2-D convolution). The shared-lib/, per-target-glue, multi-toolchain (PlatformIO / Keil / MCUXpresso / Microchip Studio) structure is also borrowed from the EML course. Conventions 2-6 below (float-first, write-your-own-then-library, inline code, negated IIR coefficients) still apply on whichever boards a page uses.

1b. Gateware / HDL exception (amended 2026-07-10)

FPGAs were excluded above to keep the platform set small and buyable. That still holds as the default: embedded pages target MCUs. But a few DSP structures exist because of the gateware they map to, and for those an HDL companion is the honest “to metal” endpoint, not scope creep. Such a page may add a single VHDL block (the primary HDL taught here; Verilog optional) showing the structure as registers and adders.

Bounds, so this does not become a second platform track: - Only where the hardware mapping is the lesson. The CIC filter (basics/10-multirate/embedded.qmd) is the reference case: multiplier-free, register-growth/overflow behaviour that only makes sense in gateware. Do not add HDL to a page just because it could be synthesised. - Illustrative, not a maintained flow. The workshop has no HDL simulator in CI. HDL blocks are verified by inspection and by an equivalent, tested Python model (e.g. the bit-accurate overflow check in test_multirate.py), and are labelled as not-CI-simulated. No vendor project files, no board commitment. - Inline, self-contained, like the C blocks (convention 5), and cross-linked to the Python that validates them.

Conventions 2–6 (companion-not-replacement, write-your-own-first, inline code) apply to HDL as they do to C.

2. C/C++ is companion, not replacement

ADR-002 is not superseded. Python remains the primary language for theory, prototyping, and analysis. C/C++ code appears only in embedded.qmd pages within topic directories. Each embedded.qmd links back to its topic’s index.qmd for the mathematical derivation and Python implementation.

3. Float first, fixed-point as optimization

All implementations use float (single-precision) by default. Fixed-point (Q15/Q31) is shown as an optimization section within the same page, not as a separate implementation. Both target MCUs have hardware single-precision FPU; fixed-point is relevant for power-constrained or FPU-less targets.

4. Write your own first, then benchmark against library

Each embedded page shows a hand-written C implementation first (teaches the algorithm), then the CMSIS-DSP or ESP-DSP equivalent, followed by a performance comparison. This mirrors the existing pattern in the biquad, adaptive-filtering, and matched-filtering pages.

5. Code inline, no shared directory

C/C++ code lives inline in embedded.qmd files as fenced code blocks. There is no shared embedded/ source directory. Each code block is self-contained with #include statements, type definitions, and function signatures so a reader can paste it into a PlatformIO or CubeIDE project.

6. IIR coefficient convention

All IIR filter code uses negated denominator coefficients (CMSIS-DSP convention): w[0] = x + a1*w[1] where the stored a1 is \(-a_1\) from the transfer function. When porting from SciPy, negate a1 and a2. This matches the existing biquad embedded.qmd and avoids a subtraction in the inner loop.

Consequences

  • Positive: Students can buy the recommended hardware today. Both boards are actively manufactured and widely available.
  • Positive: Two platforms cover complementary use cases (IoT/prototyping vs. professional/industrial) without overwhelming contributors with three or more toolchains.
  • Positive: Float-first approach means students write their first filter without learning Q-format notation.
  • Negative: NUCLEO-F446RE lacks built-in audio hardware (unlike the old Discovery board’s CS43L22 codec). External I2S breakouts add ~EUR 5 to the BOM.
  • Negative: Excluding RP2350 code sections means Pico 2 users must adapt STM32 CMSIS-DSP code themselves (both are ARM Cortex-M, so this is straightforward).