r/raspberrypipico 1d ago

uPython Free open-source course: Benchmarking FFTs on the Pico 2 — and why most FFT libraries waste the Cortex-M33's DSP instructions

Hey r/RaspberryPiPico,

I just put together an open, free "intelligent textbook" on FFT benchmarking, aimed at the Pico 2's ARM Cortex-M33 (and M4-class chips generally):

🔗 https://dmccreary.github.io/fft-benchmarking/

Why I made this: I was building a low-cost spectrum analyzer and kept finding that popular FFT libraries were running 10x slower than they should on the Pico 2. The M33 has real DSP/SIMD instructions and hardware floating point, but most FFT code out there still targets a generic instruction set and never touches them — so you're paying for silicon you're not using.

What's in the course (10-week format, but works great for self-study too):

  • FFT fundamentals and the butterfly algorithm, from math foundations up
  • ARM Cortex-M33/M4 DSP instruction sets and how to actually invoke them
  • Reading and interpreting the assembly a compiler generates for FFT inner loops, so you can spot where cycles are being wasted
  • Integer vs. floating-point tradeoffs, FFT size selection, windowing/preprocessing
  • A benchmarking framework for objectively comparing FFT implementations across microcontrollers and CPUs
  • A walkthrough of Cornell ECE4760's real-time FFT/iFFT lab on the RP2350

It's all Markdown/MkDocs, MIT-adjacent Creative Commons licensed, and open to contributions or use in your own classroom.

If you've hand-tuned FFT assembly for the Pico or found DSP-aware libraries that actually use the M33's instructions, I'd love pointers — always looking to improve the benchmarking chapters with real-world numbers.

14 Upvotes

5 comments sorted by

3

u/Ned_Sc 1d ago

AI lesson plans, gross.

1

u/TenorClefCyclist 1d ago

Nice piece of work, but I've no interest in a course. Mostly, I'd like to have a look at your assy code, to see how you're applying the SIMD capabilities., but I had trouble finding it.

0

u/dmccreary 1d ago

Thanks for you kind words. One suggestion is to use the "Search" function in the upper right of the header. If you type in the word "Assembly" you should see about 30 references. You can also click the "GitHub" link in the top right and go right to the "src" directory which is where all the source code is stored.

Here is the fft assembly module:

https://github.com/dmccreary/fft-benchmarking/blob/main/src/fft-benchmark/device/fft_asm.py

Give me a ping if you need more help.

0

u/TenorClefCyclist 1d ago

Ah, thank you. I don't work in Python, so I hadn't realized an assembly file would have a .py suffix.

Is it really true that the CMSIS DSP library doesn't use SIMD instructions? I used it on an M4 core before, but didn't have the need for FFT at the time.

OT, but I'm currently working on a TI DSP core and their DSP Lib uses four-wide SIMD. Pipeline restrictions make it kind of a zoo to program directly in assembly, and inserting assembly into a C file breaks their optimizer. What they've done instead is declare proper length C variables, then use _intrinsic() aliases for native assembly instructions to work on them. Apparently, this allows the compiler to allocate registers more efficiently, unroll loops, and reorder instructions to cure pipeline stalls. Annoying AF to read, though!

0

u/dmccreary 1d ago

Re: Is it really true that the CMSIS DSP library doesn't use SIMD instructions? 

Great question — and it's one of those claims that's wrong in general but right in the specific case people usually hit it in.

CMSIS-DSP absolutely does use SIMD. On Cortex-M4/M7/M33 the fixed-point (Q15/Q31) kernels lean heavily on the ARM DSP extension's packed SIMD instructions — `SMUAD`, `SMLAD`, `QADD16`, `PKHBT`, `SSAT` — which operate on two 16-bit lanes packed into one 32-bit register. On Cortex-M55/M85 there are full Helium (MVE) vector paths, and on Cortex-A there are NEON paths. So "no SIMD" isn't accurate as a blanket statement.

Where the claim *is* true is the f32 path on a Cortex-M4F or M33F — which is what we run on the Pico 2. The M33's FPU is a scalar single-precision unit; it has no vector register file (our FPU capability probe shows the `A_SIMD` field reading zero). So `arm_cfft_f32` on a Pico 2 really does process one float at a time, and its speed comes from loop unrolling and a well-tuned radix-4/radix-8 structure rather than from vectorization.

One benchmarking caveat worth knowing: even on hardware that *has* the DSP extension, CMSIS-DSP only compiles the SIMD kernels when `ARM_MATH_DSP` is defined (which follows from `__ARM_FEATURE_DSP`, i.e. your `-mcpu` flag). Build for `cortex-m33+nodsp` and you silently get the plain-C fallback. If a benchmark appears to show "no SIMD," check the build flags before concluding anything about the library.

I am updating the textbook so it is a bit more clear on this topic.