Introduction to RTOS Part 12 - Multicore Systems | Digi-Key Electronics

Introduction to RTOS Part 12 - Multicore Systems | Digi-Key Electronics

Understanding Multi-Core Microcontrollers

Introduction to Multi-Core Microcontrollers

  • Microcontrollers with multiple cores are increasingly popular for parallelizing tasks, particularly in applications like machine learning and Wi-Fi stacks.
  • The discussion begins with the distinction between asymmetric multiprocessing (AMP) and symmetric multiprocessing (SMP).

Asymmetric vs. Symmetric Multiprocessing

  • AMP involves one core managing the operating system and distributing tasks to other cores, which may have their own OS.
  • In contrast, SMP treats all cores equally, sharing a single OS and allowing each core to access a common task list.
  • SMP typically uses identical architectures for its cores to ensure tight coupling for resource sharing.

ESP32 Architecture Overview

  • The ESP32 features two cores: the protocol core (CPU0) for communication protocols and the application core (CPU1).
  • Each core has its own cache memory; however, they share instruction RAM which affects determinism in SMP programming.

Task Management in FreeRTOS on ESP32

  • FreeRTOS does not natively support multi-core execution; insights shared are specific to the ESP-IDF port of FreeRTOS.
  • Each core operates an independent scheduler with separate tick timers, meaning tasks on different cores may not execute synchronously.

Practical Application Example

  • Tasks can be pinned to specific cores or allowed to run on either core using task affinity constants.
  • An example is provided where a low-priority task and a high-priority task are created; both initially assigned to Core 0 lead to scheduling issues due to lack of yielding.

Observations from Code Execution

  • Running both tasks on Core 0 results in one task monopolizing CPU time until a watchdog timer resets it after several seconds.
  • Solutions include pinning tasks across both cores or utilizing task affinity constants for better load distribution.

Scheduler Behavior Insights

  • After implementing task affinity constants, both tasks run periodically across available cores without conflict.

Understanding Task Scheduling on ESP32

Core Affinity and Task Scheduling

  • The core affinity field determines if a task can run on specific cores (core 0, core 1) or has no affinity. If the calling core matches this field or is set to no affinity, the task can be executed.
  • Mixing pinned and unpinned tasks complicates predictions about which core will execute each task. While technically deterministic, calculating exact timing of events becomes nearly impossible due to this complexity.
  • Pinning a task to a specific core allows it to utilize that core's cache effectively. Conversely, switching cores without affinity leads to cache misses, increasing data fetching time from main memory.
  • On the ESP32 architecture, each core has its own set of interrupts. External events can trigger these interrupts, but knowing which task was halted becomes challenging when schedulers choose tasks arbitrarily.
  • Internal peripherals in each core can also trigger interrupts; however, they cannot affect other cores. Understanding these configurations is crucial for effective programming.

Benefits and Drawbacks of Task Pinning

  • Deciding whether to pin tasks or let schedulers manage them involves weighing benefits like ease of use against potential drawbacks such as loss of load balancing.
  • Allowing schedulers to decide on task execution generally optimizes CPU utilization by minimizing idle time across cores when tasks are ready.
  • Pinning tasks provides more control over interrupt locations and reduces cache misses but requires significant effort in optimal scheduling and placement.
  • Execution determinism improves with pinned tasks since you know exactly which core runs which task—important for applications requiring precise timing like medical devices or space missions.
  • The ESP32 pins Wi-Fi and Bluetooth tasks to core zero for better performance while maintaining shared memory access for kernel objects.

Practical Demonstration: Semaphore Usage

  • A demonstration shows how Task 0 runs on Core 0 while Task 1 operates on Core 1 using a binary semaphore created in setup. This illustrates inter-core communication effectively.
  • Critical sections in FreeRTOS are marked using task enter and exit, preventing scheduler activity during execution. This method is stricter than mutexes as it disables interrupts up to a certain priority level.

Complexity with Spin Locks

  • In ESP-IDF, entering critical sections requires spin locks—a global variable that forces waiting cores into a loop until the lock is available, differing from traditional mutex behavior.
  • An example demonstrates long delays within critical sections affecting scheduling; despite halting one task for an extended period, another continues executing due to running on different cores.

Understanding Critical Sections in ESP32

Core Behavior During Critical Sections

  • When Task 1 is inside the critical section, Core 0 spins idly waiting for the lock to be released. This behavior leads to unexpected timing issues with Core 1.
  • The expected behavior of pin 12 remaining stable while Core 1 is active does not occur; instead, it toggles unexpectedly due to scheduling interruptions caused by critical sections.
  • The ESP-IDF attempts to compensate for lost ticks during long critical sections, which can disrupt task timing and lead to inaccuracies in task management.
  • To mitigate these issues, it's recommended to keep critical sections as short as possible or avoid them entirely when feasible.

Modifying Code for Dual-Core Support

  • A challenge is presented: convert the existing project to utilize both cores of the ESP32 effectively. Task A (averaging samples) should run on Core 0, while Task B (echoing characters) runs on Core 1.
  • The application should maintain its functionality—responding correctly when "avg" is entered in the serial terminal and echoing characters back accurately.
  • Utilizing kernel objects for data synchronization between tasks will require minimal code changes to achieve dual-core support.

Conclusion and Future Directions

  • The series concludes with a note of gratitude for following along and encouragement for further exploration into real-time operating systems (RTOS).
Video description

Running tasks in an RTOS on a multicore system can seem daunting at first. Many processors have unique architectures that you must keep in mind when writing programs that use more than one core. We examine the FreeRTOS port for the ESP32 (known as ESP-IDF) and how to synchronize tasks on both cores. The solution to the challenge can be found here: https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-12-multicore-systems/369936f5671d4207a2c954c0637e7d50 Code for this video series (including demonstrations, challenges, and solutions) can be found here: https://github.com/ShawnHymel/introduction-to-rtos Programming a multicore system can be separated into two paradigms: asymmetric multiprocessing (AMP) and symmetric multiprocessing (SMP). AMP requires a primary core running an operating system to schedule tasks in other (secondary) cores. In SMP, the OS runs on all cores, and the scheduler is free to pull tasks from a shared list. ESP-IDF is the port of FreeRTOS for the ESP32, and it supports SMP. The ESP32 is a dual-core system (although, single-core variants exist). We look at how the ESP32 architecture is configured and how each core can access memory, resources, and interrupts. In ESP-IDF, we have the option of pinning tasks to a core (so that only that core may run a particular task) or setting a task to “no affinity” (so that the task can run in either core). Using “no affinity” is easier to implement and optimizes CPU utilization and load balancing without much effort. However, it becomes difficult to determine when certain tasks will run, which makes predicting deadlines tougher. As a result, many embedded developers prefer to pin tasks to cores, as it allows for a higher level of determinism. We show how to run tasks on either core in the ESP32 as well as set them to “no affinity.” Kernel objects (e.g. queues, mutexes, and semaphores) work across cores without any modification, as the ESP32’s cores share memory. Product Links: https://www.digikey.com/en/products/detail/adafruit-industries-llc/3405/7244967 Related Videos: Introduction to RTOS Part 1 - What is a Real-Time Operating System (RTOS)? - https://youtu.be/F321087yYy4​ Introduction to RTOS Part 2 - Getting Started with FreeRTOS - https://youtu.be/JIr7Xm_riRs​ Introduction to RTOS Part 3 - Task Scheduling - https://youtu.be/95yUbClyf3E​ Introduction to RTOS Part 4 - Memory Management - https://youtu.be/Qske3yZRW5I​ Introduction to RTOS Part 5 - Queue - https://youtu.be/pHJ3lxOoWeI​ Introduction to RTOS Part 6 - Mutex - https://youtu.be/I55auRpbiTs​ Introduction to RTOS Part 7 - https://youtu.be/5JcMtbA9QEE​ Introduction to RTOS Part 8 - https://youtu.be/b1f1Iex0Tso Introduction to RTOS Part 9 - https://youtu.be/qsflCf6ahXU Introduction to RTOS Part 10 - https://youtu.be/hRsWi4HIENc Introduction to RTOS Part 11 - https://youtu.be/C2xKhxROmhA Introduction to RTOS Part 12 - https://youtu.be/LPSHUcH5aQc Related Project Links: https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-11-priority-inversion/abf4b8f7cd4a4c70bece35678d178321 Related Articles: https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-12-multicore-systems/369936f5671d4207a2c954c0637e7d50 Learn more: Maker.io - https://www.digikey.com/en/maker Digi-Key’s Blog – TheCircuit https://www.digikey.com/en/blog Connect with Digi-Key on Facebook https://www.facebook.com/digikey.electronics/ And follow us on Twitter https://twitter.com/digikey