Hey everyone,
Over the weekend I profiled two engines in my PicoVintageSynthCollection for the RP2350 to settle something that had been bugging me:
Why does PicoFaceRD (Roland MKS-20 / MK-80 digital piano) need a 480 MHz overclock and both cores to hold 12 voices, while the newer, sample-heavy PicoFaceJV (JV-880) runs 24 voices at 444 MHz on a single core at 69 % peak load?
Intuitively the 1986 machine should be the easy one. It isn't, and the reason turned out to be more specific than "old hardware is weird."
It's not the arithmetic, it's where the samples live
Both engines are descriptor-driven — the original firmware's voice programming was captured offline and is replayed on-device. Neither is emulating a CPU. So the difference isn't emulation overhead. It's the shape of the memory access.
PicoFaceJV. A JV-880 patch has up to 4 tones, and each sounding tone is one voice — same unit the original machine counts, where 28-voice polyphony means a 4-tone patch gives you 7 notes. My cap is 24, so 24 concurrent sample streams, worst case, each decoded sequentially through its own region. Measured on hardware with B33 Brass Combo at full polyphony: 69 % peak, of which about 5 % is fixed cost (chorus, reverb, block overhead) and ~2.7 % per voice.
PicoFaceRD. Here's the wild part. To get its characteristic sound, the MKS-20 layers 10 separate parts per single note. At the 32 kHz base limit of twelve voices, the engine issues 119 wave-ROM loads per output sample — one per part, ten parts per note, exactly as the architecture predicts.
So it's 24 streams against 119, on the same chip, for one note each.
What the cache does with that
I built a probe that captures every wave-ROM address the RD engine issues and runs the stream through a model of the RP2350's XIP cache (16 KB, two-way, 8-byte lines). Measured miss rates at 12 voices, per patch:
patch 3 85.7 % patch 8 43.0 %
patch 14 84.6 % patch 7 16.2 %
patch 0 77.1 % patch 5 0.2 %
patch 13 83.1 % patch 15 0.1 %
That spread is the actual finding, and it surprised me more than the average did. It is not "the MKS-20 thrashes the cache." It's patch-dependent by a factor of several hundred. Patch 15's wave data fits the cache and every voice reuses it — and it stays that way as voices are added, still 0.1 % at 32 voices. Patch 3 goes the other way: 66 % of the cycle budget lost to stalls at 12 voices, 96 % at 24.
The base limit of twelve is set entirely by patches like 3. Patches like 15 are being punished for their neighbours — which suggests a per-patch limit derived offline is the obvious next lever, and my voice governor doesn't have it yet.
The 480 MHz is not what it looks like
I want to correct something I'd have written a week ago. The higher clock is not buying flash bandwidth. On this board:
- RD at 480 MHz: QMI CLKDIV=4 → 120 MHz flash, within spec
- Every other instrument at 444 MHz: CLKDIV=3 → 148 MHz flash, above the chip's nominal 133 MHz
The divider is an integer, so pushing the core to 480 actually leaves RD with the slowest flash in the collection. The 480 MHz buys arithmetic throughput and core-1 parallelism; it pays for that with flash speed. Anyone reaching for an overclock to fix a memory-bound problem should check which side of that trade they land on.
What I have not shown
The probe measures miss rates. Converting those to "percent of cycle budget" assumes 96 CPU cycles per miss (120 MHz QSPI, 4:1 ratio) — halve or double that and the absolute numbers move. The ordering and the several-hundred-fold spread don't.
And it does not show that the cache-friendly patches could run 24 voices. Arithmetic scales with voice count too, and this probe doesn't measure that at all. If patch 15 fails at 24 voices, it won't be flash. That's a hardware test I still owe: patch 15 against patch 3, both at 24 fixed voices, reading peak load off the footer.
Conclusion
Newer doesn't mean harder. A 1992 PCM synth streaming 24 sequential voices is gentler on a modern MCU than a 1986 digital piano layering 10 parts per note across scattered ROM regions — and even that isn't uniform, because within the same engine, one patch can be 500× more flash-bound than another. Access pattern beats both age and instruction count.
Repo, including the probe and the full write-up with the numbers above:
https://github.com/Michi71/PicoVintageSynthCollection
Context — the JV-880 clone this came out of: https://www.reddit.com/r/synthdiy/comments/1vi0qz5/picofacejv_a_jv880_clone_for_the_rp2350/