FFT on Pico 2
Prompt
Are there any MicroPython FFT libraries available yet that leverage the powerful new DSP assembly instructions on the Raspberry Pi Pico 2? All the old MicroPython FFT libraries only used the older assembly language code written for the original Raspberry Pi Pico. Please find sample code and benchmarks for 512 and 1024 point FFT functions.
This appendix summarizes the state of MicroPython FFT libraries for the Pico 2 (RP2350) as of August 2026, and how they relate to the assembler FFT and DSP instructions lessons used elsewhere in this book.
Short answer
There is no mature, drop-in MicroPython package yet that hand-exploits the RP2350's Cortex-M33 integer DSP-SIMD extension (the SMLAD/SXTB16-style instructions) for FFT. What actually exists is more interesting: the community jumped past that and is leaning on the M33's hardware FPU instead, because it turns out to be fast enough on its own.
FPU vs. DSP-SIMD extension
These are two separate hardware blocks, and it matters which one a library actually uses:
| Feature | Original Pico (RP2040) | Pico 2 (RP2350) |
|---|---|---|
| Core | Cortex-M0+ | Cortex-M33 |
| Instruction set | ARMv6-M, Thumb-1 | ARMv8-M, Thumb-2 |
| Hardware FPU | None (software float) | Yes, single-precision (FPv5-SP) |
| Integer DSP-SIMD extension | None | Yes (SMLAD, SXTB16, saturating arithmetic, etc.) |
The original Pico has neither an FPU nor the DSP-SIMD extension, which is why the older MicroPython FFT libraries (including the version of Peter Hinch's library used in the assembler FFT lesson) lean on hand-rolled Thumb-1 integer assembly. The Pico 2 has both, and existing library authors report that the FPU alone already removes the old bottleneck — from Peter Hinch's own README:
The ARM FPU is so fast that integer code offers no speed advantage.
Libraries available today
peterhinch/micropython-fourier
github.com/peterhinch/micropython-fourier is the library already in use in this book's lessons, and the only one found that explicitly names Pico 2 / RP2350 as a supported target. It is an in-place radix-2 Cooley-Tukey FFT (power-of-2 sizes only) with the butterfly/twiddle inner loop hand-written in Thumb-2 assembler that calls the FPU — not the integer DSP-SIMD instructions. A few files still reference pyb.ADC; swapping in machine.ADC is a small, documented change to run it standalone on Pico 2 without a Pyboard.
ulab (numpy.fft)
micropython-ulab is buildable into RP2350 MicroPython firmware via dpgeorge's rp2-add-rp2350 branch, now merged upstream. Its numpy.fft.fft is a portable radix-2 C implementation, not a CMSIS-DSP call — it benefits from GCC targeting the hardware FPU (-mfpu=fpv5-sp-d16 -mfloat-abi=hard) when the firmware is built for RP2350, but it doesn't hand-exploit the DSP-SIMD instructions either. No published Pico-2-specific 512/1024-point benchmark was found.
CMSIS-DSP (the actual path to the SIMD ceiling)
ARM ships libCMSISDSP_cortex-m33.a with arm_rfft_fast_f32 / arm_rfft_q15, and these kernels do use the DSP-SIMD extension. It is not wired into MicroPython for the rp2 port by anyone publicly yet:
- A Raspberry Pi forum thread shows someone linking CMSIS-DSP against RP2350 C code and hitting a hard-float/soft-float ABI mismatch (
"uses VFP register arguments, Project.elf does not") — fixable by matching-mfloat-abi=hard -mfpu=fpv5-sp-d16 -DARM_MATH_CM33=1across the entire build, but it shows nobody has packaged this cleanly for rp2 yet. - A MicroPython discussion thread has someone building a full CMSIS-DSP usermod wrapper (70+ functions including FFT, matrix ops, IIR filters, using
uctypesfor structs) — but for STM32, notrp2. On their STM32 Cortex-M33 target, a 1024-point FFT through that wrapper measured ~182 µs — that's the ceiling a properly-wired CMSIS-DSP module gets you, roughly 38x faster than the pure-Python FPU path below. Nobody has published anrp2port of this wrapper yet.
Ruled out
muFFT-pico is a C/C++ library (not MicroPython) whose "optimizations" are SSE/AVX/NEON — none of which exist on Cortex-M33 (no NEON on M-profile cores). Despite the name, it isn't targeting the Pico's DSP hardware.
Benchmarks
| Library | Platform | Core | Size | Time |
|---|---|---|---|---|
| micropython-fourier | Pico 2 | Cortex-M33 @150MHz | 1024-pt complex | 6.97 ms |
| micropython-fourier | Pyboard 1.x | Cortex-M4 @168MHz | 1024-pt complex | 12.9 ms |
| micropython-fourier | Pyboard D SF2W/SF6W | Cortex-M7 | 1024-pt complex | 3.6 ms |
| CMSIS-DSP (C) | RP2040 | Cortex-M0+ (no FPU) | 512-pt f32 | 9.1 ms |
| CMSIS-DSP (C) | RP2040 | Cortex-M0+ (no FPU) | 1024-pt f32 | 18.6 ms |
| CMSIS-DSP usermod | STM32 | Cortex-M33 | 1024-pt | ~0.18 ms |
Note
Only the first row (peterhinch's library, 1024-pt, Pico 2) is a directly published MicroPython-on-Pico-2 number — sourced from the library's README. No 512-point number is published there; for a radix-2 FFT that scales as N·log(N), expect roughly 3–3.3 ms, but this should be verified on real hardware rather than trusted as an extrapolation. The RP2040 rows come from jptrainor/cmsis-sandbox (plain C, not MicroPython) and are included only for scale — they show Pico 2's FPU-backed MicroPython FFT already beats the old chip's raw C float FFT by roughly 2.7x, without touching the DSP-SIMD extension at all. The STM32 row comes from the MicroPython CMSIS-DSP discussion and is the best available estimate of what a properly wired CMSIS-DSP path could achieve on Pico 2's own Cortex-M33 — nobody has published that port or its numbers yet.
Sample code
Benchmarking the FPU-based library on-device
This script needs no external hardware — it generates synthetic data and times the transform. Copy dft.py, dftclass.py, polar.py, and window.py from micropython-fourier to /lib first, and build firmware with ulab included if you want the second half to run.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
Building CMSIS-DSP for RP2350 (C, for a future usermod)
The compiler/link flags that resolved the hard-float ABI mismatch on the forum thread above — every object in the final link, not just the CMSIS-DSP library, must use these flags:
1 | |
linked against libCMSISDSP_cortex-m33.a, calling arm_rfft_fast_f32() / arm_rfft_fast_init_f32(). This is C, not MicroPython — turning it into a usable Pico 2 library means wrapping it as a MicroPython usermod (micropython.mk + a small C shim exposing fft(buffer) to Python), following the pattern the STM32 wrapper in the MicroPython discussion thread already proved out. This has not been done for rp2 publicly yet — it's the open gap.
Practical recommendation
- Today: use micropython-fourier (already in use in this book) or
ulab'snumpy.fft— run the benchmark script above on real hardware to fill in the missing 512-point and ulab numbers. - To chase the ~180 µs-class ceiling: write a small C usermod wrapping
arm_rfft_fast_f32/arm_cfft_f32from CMSIS-DSP, matching the hard-float ABI flags throughout the whole build. No one has published this forrp2yet, so this would be new work rather than a drop-in install.
Sources
- peterhinch/micropython-fourier
- RP2350 dsp instructions — Raspberry Pi Forums
- RP2350 + CMSIS-DSP + FPU help — Raspberry Pi Forums
- Unable to build RP2350/Pico 2 MicroPython firmware — Raspberry Pi Forums
- jptrainor/cmsis-sandbox
- j-sass/muFFT-pico
- Adding CMSIS DSP functions — MicroPython discussion #16130
- ARM-software/CMSIS-DSP
- Cornell ECE4760 RP2350 arithmetic benchmarks
- RP2350 — Wikipedia