Raspberry Pi Pico and Zephyr USB networking: fixing the flash cache problem
In the previous post I found why the Raspberry Pi Pico answers USB network echoes slowly. The Zephyr network and USB code runs from the external flash chip through a 16 KB cache, and the code that one echo needs does not fit in that cache. I promised to try a better arrangement of the code in flash. This post gives the result.
Short answer: layout alone halves the time per ping, but it cannot fix the problem, because one echo uses more code than the cache holds. A combination works very well: put about 150 well-chosen functions (12–16 KB) in SRAM, and arrange the rest of the hot code in flash so that it does not collide in the cache. With that, the Pico at 125 MHz answers about 2,250 UDP echoes per second, against 322 before. That is more than a Teensy 4.1 at 600 MHz (2,170) and more than moving whole libraries to SRAM, at a third of the RAM.
Read the full report for the method, all measurements, the experiments that failed, and the limits.
The code does not fit
I flushed the cache and counted the cache misses for one echo. A ping touches 27 KB of different flash lines; a UDP echo through a socket touches 39 KB. The cache holds 16 KB. When the working set is larger than the cache, a good layout can only reduce conflicts. It cannot remove capacity misses.
The best layout I found places each hot function at a chosen position in the cache (“cache colouring”), with padding between functions; flash space is cheap. For ping, the time inside the board falls from 1.75 ms to 0.88 ms per echo, with no extra RAM. For UDP echo the gain is smaller: 322 to 412 echoes per second, or 650 with link-time optimization.
A little SRAM goes a long way
Moving whole libraries to SRAM costs 48 KB. Choosing single functions is much more economical. I sample the program counter over the debug port while the board works, rank functions by samples per byte, and move the densest ones to SRAM.

Two results surprised me. First, a layout helps mainly the workload it was measured on: a layout made from ping traffic hardly helps UDP. Second, a plain list of function names keeps most of the gain: without the build-specific colouring, the 16 KB list still gives 81–89 % of the improvement, and it continued to work after I changed the network code. That makes a list of names a realistic candidate for an upstream Zephyr change.
Limits
One board, one Zephyr version, one simple echo application at 125 MHz. A real application has its own code competing for the same cache, so its layout must be measured with that application running. Above about 1,900 echoes per second some single test steps collapse in one of three passes; I have not analysed that yet.
Evidence: measured on a Raspberry Pi Pico (RP2040), Zephyr main of 31 October 2026, USB CDC-ECM, 1 November 2026. Every number is the mean of 3–9 independent runs; the spread is below 2 %.