Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Running Dual-Core Applications on the RP2040

Tech Oct 28 1

The RP2040 microcontroller integrates a dual‑core Arm Cortex‑M0+ processor alongside 264 KB of on-chip SRAM and a 2 MB external flash. While many projects rely on a single core, leveraging both processors can improve parallelism and responsiveness.

Official SDK and Examples

The pico‑examples repository provides several dual‑core demonstrations inside the multicore folder:

  • hello_multicore
  • multicore_fifo_irqs
  • multicore_runner
  • multicore_runner_queue

Core communication primitives are declared in pico‑sdk/src/rp2_common/pico_multicore/multicore.c. The main API consists of:

void multicore_launch_core1(void (*entry)(void));

// FIFO operations
uint32_t multicore_fifo_pop_blocking(void);
void multicore_fifo_push_blocking(uint32_t data);

// Interrupt management
inline void multicore_fifo_clear_irq(void);
void irq_set_exclusive_handler(uint num, irq_handler_t handler);
void irq_set_enabled(uint num, bool enabled);
inline bool multicore_fifo_rvalid(void);

FreeRTOS Symmetric Multiprocessing

FreeRTOS now includes SMP support through a dedicated branch. The kernel repository can be found at https://github.com/FreeRTOS/FreeRTOS-Kernel (smp branch), with introductory material on the official website at https://www.freertos.org/symmetric-multiprocessing-introduction.html. A page specifically covering the Pico board is available at https://www.freertos.org/smp-demos-for-the-raspberry-pi-pico-board.html.

Demo projects are hosted in the FreeRTOS-SMP-Demos repository, which includes the SMP‑capable kernel as a submodule. Clone recursively to get started:

git clone --recursive https://github.com/FreeRTOS/FreeRTOS-SMP-Demos

The RP2040 project lives under FreeRTOS/Demo/CORTEX_M0+_RP2040. It ships with two example configurations:

  • Standard
  • OnEitherCore

The build uses CMake and the Pico SDK integration file pico_sdk_import.cmake. Compile both examples by runing:

mkdir build && cd build
cmake ..
make

Each example folder will contain the resulting binaries after a successful build.

Understanding the Standard Demo

The Standard example produces two executables: main_blinky and main_full. The vLaunch() function selects between them based on the mainCREATE_SIMPLE_BLINKY_DEMO_ONLY preprocessor constant.

void vLaunch( void )
{
#if( mainCREATE_SIMPLE_BLINKY_DEMO_ONLY == 1 )
    main_blinky();
#else
    main_full();
#endif
}

The main_blinky variant spawns a pair of tasks—prvQueueReceiveTask and prvQueueSendTask. By default the tasks run on whichever core the scheduler assigns, wich may change across reboots. To observe core placement, insert a debug print:

printf("task %s executing on core %d\r\n", __FUNCTION__, portGET_CORE_ID());

Possible output:

task prvQueueSendTask executing on core 1
task prvQueueReceiveTask executing on core 0

Alternatively, a different run could schedule both tasks on core 0 or core 1.

To pin tasks to specific cores, use FreeRTOS's SMP‑specific affinity API. The following code forces the sender to core 0 and the receiver to core 1:

void main_blinky( void )
{
    printf("Booting main_blinky.\n");

    QueueHandle_t sampleQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ) );

    if( sampleQueue != NULL )
    {
        TaskHandle_t consumerHandle = NULL;
        xTaskCreate( prvQueueReceiveTask, "Consumer",
                     configMINIMAL_STACK_SIZE, NULL,
                     mainQUEUE_RECEIVE_TASK_PRIORITY, &consumerHandle );

        UBaseType_t consumerAffinity = (1 << 1);
        vTaskCoreAffinitySet( consumerHandle, consumerAffinity );

        TaskHandle_t producerHandle = NULL;
        xTaskCreate( prvQueueSendTask, "Producer",
                     configMINIMAL_STACK_SIZE, NULL,
                     mainQUEUE_SEND_TASK_PRIORITY, &producerHandle );

        UBaseType_t producerAffinity = (1 << 0);
        vTaskCoreAffinitySet( producerHandle, producerAffinity );

        vTaskStartScheduler();
    }

    for( ;; );
}

With affinity set, the output confirms the fixed assignment:

task Producer executing on core 0
task Consumer executing on core 1

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.