RP2350 FreeRTOS SMP Port Bugs
Tracked issues with the community FreeRTOS SMP port for RP2350 (Cortex-M33)
that affect picodroid. These are upstream bugs in
third_party/FreeRTOS-Kernel/portable/ThirdParty/Community-Supported-Ports/GCC/RP2350_ARM_NTZ/.
1. configTICK_CORE=1 breaks task scheduling on core 0
Status: Worked around (configTICK_CORE=0 + hardware timer alarm)
When configTICK_CORE=1 (tick on core 1, matching the RP2040 configuration),
core 0 never starts its first task. The PDB task on core 1 runs normally but
the JVM task on core 0 is never scheduled.
Root cause: The RP2350 port uses the generic ARM_CM33_NTZ vPortStartFirstTask
which triggers SVC to load the first task context. Unlike the RP2040 port (which
directly reads pxCurrentTCBs[get_core_num()] from assembly), the SVC-based
approach does not properly select a task for core 0 when it is not the tick core.
Workaround: Use configTICK_CORE=0 (tick on core 0). This means the tick
freezes during each flash erase/program window (with_xip_disabled! disables
core 0 interrupts) — see bug #2.
2. Tick freeze during flash operations stalls the scheduler
Status: Superseded (PDB task moved to core 0; residual freeze handled by busywait reads)
With configTICK_CORE=0, disabling interrupts on core 0 freezes the FreeRTOS
tick, and any tick-dependent operation (timeouts, delays, queue receives)
stops advancing for the duration.
The original design parked core 0 in a RAM spin loop (park_for_flash()) for
the whole install while core 1 wrote flash, compensating with a TIMER0 alarm
ISR on core 1 (timer_alarm.rs) that fired every 1 ms independently of
FreeRTOS. Both mechanisms were retired when the PDB task moved to core 0
(see bug #3): the JVM task now blocks on a FreeRTOS notification with
interrupts enabled, so the tick keeps running between flash operations.
Parking has since swapped cores: a dedicated flashpark parker task pinned
to core 1 blocks on a FreeRTOS task notification (hal/rp/core1_park.rs);
before each erase/program window core 0 notifies it and waits for the parked
ack in SRAM, and core 1 spins in a RAM-resident loop with interrupts masked
until the window closes. (On testbench_rp2350w, core 1 also hosts the cyw43
WiFi task, at a priority below the parker.) The tick still freezes inside
each erase/program window (with_xip_disabled!),
so install-time reads use a hardware-timer busywait
(pdb_usb::queue_read_byte_busywait) instead of tick-based timeouts, and a
successful install ends in a chip reset.
3. Cross-core FreeRTOS IPC is unreliable
Status: Avoided (not used on RP2350 install path)
xTaskNotify and xQueueSend from core 0 to a blocked task on core 1 silently
fail to wake the target task. The notification/data is delivered (the value is
set) but the doorbell interrupt that triggers PendSV on core 1 either isn’t sent
or isn’t processed. This was tested with both task notifications and queues.
Calling xTaskNotify from core 1 to core 0 also causes the FreeRTOS SMP
scheduler to deadlock — vTaskDelay on core 0 stops completing entirely.
Workaround: The install path on RP2350 avoids nearly all cross-core
FreeRTOS API calls. Core 1 signals core 0 via atomic flags + notify_jvm()
(which works because the JVM task checks STOP_JVM at opcode boundaries).
Core 0’s one cross-core call is the FreeRTOS task notification that wakes the
dedicated core-1 parker task (flashpark, hal/rp/core1_park.rs); the
parker acks back through SRAM atomics, not a FreeRTOS wake. That notification
replaced the retired TIMER0-alarm ISR signal, and it is viable because
cross-core doorbell/IPI delivery has since been repaired — the same fix that
exposed the XIP-window lockup the parker exists to prevent (see
core1_park.rs’s design notes).
4. Tight busy-wait on core 1 starves core 0’s scheduler
Status: Avoided (core 1 blocks in FreeRTOS during install wait)
When core 1 runs a tight loop (even with NOP gaps or hardware timer delays),
vTaskDelay on core 0 never completes. This occurs regardless of whether the
loop accesses shared memory or peripherals. The FreeRTOS SMP scheduler appears
to require both cores to periodically enter FreeRTOS-managed blocking states
for tick processing to work correctly.
Workaround: Core 1’s resident tasks block in the scheduler: the
flashpark parker waits on a FreeRTOS task notification (core1_park.rs),
and on testbench_rp2350w the cyw43 task blocks between driver polls. The
only tight loop core 1 ever runs is the deliberate RAM-resident park spin
inside a flash window, entered and exited via the core1_park.rs handshake.
The TIMER0 alarm this workaround once relied on is retired.
Ideal fix
All four bugs stem from configTICK_CORE=0 being forced by bug #1. If the
RP2350 port’s vPortStartFirstTask / SVC handler were fixed to properly select
tasks for non-tick cores, configTICK_CORE=1 would work and bugs #2–#4 would
not apply (the tick would run on core 1, unaffected by core 0 parking).
The RP2040 port handles this correctly by reading
pxCurrentTCBs[get_core_num()] directly in assembly rather than relying on SVC.
Porting that approach to the Cortex-M33 SVC handler (or using a similar direct
context-load) would be the root fix.