CMSIS-RTOS2 as a third Operating System option, on the FreeRTOS bundle
What. ARM's CMSIS-RTOS2 API (cmsis_os2.h: osThreadNew(), osDelay(), message queues, mutexes, semaphores, event and thread flags, timers, memory pools) is available on top of FreeRTOS, through ARM's own wrapper, CMSIS-FreeRTOS.
How it is chosen. A third Tools > Operating System option, FreeRTOS (SMP) + CMSIS-RTOS2. It uses the same bundle as FreeRTOS (SMP) (platform/system/<board>_freertos/) and adds two settings: build.os.flags=-DPHYDUINO_CMSIS_RTOS2=1 and build.os.libs, which links that bundle's lib/libcmsis_os2.a. The bundle's cmsis_os2.h stops the build with a message naming the menu option unless PHYDUINO_CMSIS_RTOS2 is set; ARM's header is under include/cmsis_rtos2/. FreeRTOS's own API stays available in the same sketch.
Kernel settings. The FreeRTOS configuration, shared by both FreeRTOS options, now has 56 priorities (configMAX_PRIORITIES), the number the wrapper requires: CMSIS priority p is FreeRTOS priority p - 1. configCPU_CLOCK_HZ reads clk_sys at run time, so osKernelGetSysTimerFreq() follows Tools > CPU Speed.
The sketch's threads. The scheduler is started by the core before setup(), as with FreeRTOS (ADR 009), so osKernelInitialize() and osKernelStart() are not needed (they return osError). With this option loop() and loop1() run at osPriorityNormal (FreeRTOS 23), the default priority of osThreadNew(); with plain FreeRTOS they stay at 1.
Source. The wrapper (cmsis_os2.c, freertos_os2.h, freertos_mpool.h) from CMSIS-FreeRTOS v11.3.0 and the API headers (cmsis_os2.h, os_tick.h) from CMSIS_6 v6.4.0, copied unchanged into sdk-builder/cmsis-rtos2/, with a short RP2040 file for the OS Tick functions the wrapper uses (osKernelGetSysTimerCount()).
CMSIS-RTOS2. It is the RTOS API of ARM's Cortex-M software standard and of tools such as Keil MDK and STM32Cube, so code and course material written for it on other Cortex-M boards runs here unchanged.
An option, not always on. libpico.a is linked whole (every object is kept), so the wrapper cannot go in it without giving every FreeRTOS sketch its weak hook functions and its kernel-state bookkeeping. A separate archive that only this option links keeps plain FreeRTOS exactly as before, and the menu makes the choice visible, as for FreeRTOS.
The same bundle, not a third one. The wrapper needs no change to the SDK or the kernel beyond configuration values both FreeRTOS options can share, so it costs a 125 KB archive (mostly debug information, not flash) instead of another 10 MB bundle. 56 priorities instead of 8 costs about 1 KB of RAM (one list per priority).
Copied, not submodules. The CMSIS-FreeRTOS repository also carries a full FreeRTOS kernel, with paths longer than a default Git checkout on Windows accepts, so cloning this repository with submodules would fail there. Only five files are needed.
loop() at osPriorityNormal. CMSIS-RTOS2 code expects its main thread and new threads to share osPriorityNormal. At FreeRTOS priority 1, any default thread that never waits would stop loop() from running at all.
The copied files are updated by hand (sdk-builder/cmsis-rtos2/README.md). The FreeRTOS priority range in the documentation changes from 0-7 to 0-55. A wrapper call made from setup() sees the kernel already running, which differs from ordinary CMSIS-RTOS2 start-up code (osKernelInitialize(), create threads, osKernelStart()). osKernelGetSysTimerCount() has clk_sys resolution on core 0 but steps once per tick (1 ms) on core 1, because the tick timer (SysTick) runs only on core 0 and each core's SysTick is private. A thread's stack_size is in bytes, while FreeRTOS's xTaskCreate() takes words. CMSIS-RTOS2 behaviour needs hardware tests (checklist 35-36).