CMSIS-RTOS v2 wrapper implementation

August 14, 2023 · View on GitHub

The CMSIS-RTOS v2 (CMSIS-RTOS2) provides generic RTOS interfaces for Arm® Cortex® processor-based devices. It provides a standardized API for software components that require RTOS functionality and gives therefore serious benefits to the users and the software industry:

  • CMSIS-RTOS2 provides basic features that are required in many applications.
  • The unified feature set of the CMSIS-RTOS2 reduces learning efforts and simplifies sharing of software components.
  • Middleware components that use the CMSIS-RTOS2 are RTOS agnostic and are easier to adapt.
  • Standard project templates of the CMSIS-RTOS2 may be shipped with freely available CMSIS-RTOS2 implementations.

This CMSIS-RTOS v2 represents a wrapper layer for CMSIS RTOS v2 APIs implementation based on threadX RTOS APIs. The list of features supported by the current implementation are as below:

FeatureSupportedShort Description
Kernel Information and ControlYIt provide version/system information and starts/controls the RTOS Kernel. More...
Thread ManagementYIt define, create, and control thread functions.. More...
Thread FlagsNIt synchronize threads using flags. More...
Event FlagsYIt synchronize threads using event flags. More...
Generic Wait FunctionsYIt wait for a certain period of time.. More...
Timer ManagementYIt create and control timer and timer callback functions. More...
Mutex ManagementYIt synchronize resource access using Mutual Exclusion (Mutex). More...
SemaphoresYIt access shared resources simultaneously from different threads. More...
Memory PoolYIt manage thread-safe fixed-size blocks of dynamic memory. More...
Message QueueYIt exchange messages between threads in a FIFO-like operation. More...

For more information about CMSIS-RTOS v2 APIs, please refer to the ARM manual: CMSIS-RTOS API v2

CMSIS-RTOS v2 design

Kernel Initialize and Start

In threadX RTOS only one interface is used to start the kernel (tx_kernel_enter). This function will:

  • _tx_initialize_low_level: invoke the low-level initialization to handle all processor specific initialization issues
  • _tx_initialize_high_level: invoke the high-level initialization to exercise all of the ThreadX components and the application's initialization function
  • tx_application_define: call the application provided initialization function. Pass the first available memory address to it
  • _tx_thread_schedule: enter the scheduling loop to start executing threads.

For ARM CMSIS solution, its mandatory to separate the kernel initialization from the kernel start to allow the user to create threads, timers, semaphores,... in between. For that we design the CMSIS-RTOS2 wrapper as below:

  • osKernelInitialize: will initialize the low level and high level layers by calling the function "_tx_initialize_kernel_setup"
  • osKernelGetState: will call the application provided initialization function start the kernel.

Dynamic Memory Management

CMSIS-RTOS v2 APIs such as osThreadNew, give the possibilities to the user, when implementing his application, to:

  • Pass the block memory and stack address (declared or allocated in the application level)
  • Let the low layers (wrapper or RTOS) allocate the memory

However, the dynamic memory allocation is not a supported feature for threadX RTOS. In fact, its mandatory to implement it in CMSIS RTOS v2 wrapper level.

The global idea for dynamic memory allocation solution is to use two threadX BytePools:

  • HeapBytePool: used for thread, timer, mutex, semaphore, event flags and message queue object block memory allocation
  • StackBytePool: used for thread and message queue stack memory allocation.

In fact, three internal functions are added as following:

Function NameShort Description
MemInitcreates the HeapBytePool and StackBytePool BytePools
MemAllocallocate the needed memory for object block or stack
MemFreefree the memory for object block or stack

Notes:

  • The sizes of HeapBytePool and StackBytePool are user defined using respectively the macro defines RTOS2_BYTE_POOL_HEAP_SIZE and RTOS2_BYTE_POOL_STACK_SIZE
  • The minimum size of HeapBytePool and StackBytePool is defined by the threadX macro define TX_BYTE_POOL_MIN
  • The HeapBytePool and StackBytePool are allocated from the first free memory area defined by the threadX variable _tx_initialize_unused_memory.

Static Memory Management

CMSIS-RTOS v2 gives the possibilities to the user, when implementing his application, to statically allocate the memory. In fact, static buffers will be created and allocated based on user defines RTOS2_BYTE_POOL_HEAP_SIZE and RTOS2_BYTE_POOL_STACK_SIZE. The minimum size of HeapBytePool and StackBytePool is defined by the threadX macro define TX_BYTE_POOL_MIN.

CMSIS-RTOS v2 Modules description

Kernel Information and Control

The kernel Information and Control function group allows to:

  • Obtain information about the system and the underlying kernel.
  • Obtain version information about the CMSIS-RTOS API.
  • Initialize of the RTOS kernel for creating objects.
  • Start the RTOS kernel and thread switching.
  • Check the execution status of the RTOS kernel.
API NameSupportedShort DescriptionLimitation
osKernelInitializeYKernel Initialize...No limitations
osKernelGetInfoYKernel Get Info...No limitations
osKernelGetStateYKernel Get State...Only osKernelInactive, osKernelReady and osKernelRunning states are supported
osKernelStartYKernel Start...No limitations
osKernelLockNKernel Lock...This API is not supported due to threadX limitation
osKernelUnlockNKernel Unlock...This API is not supported due to threadX limitation
osKernelRestoreLockNKernel Restore Lock...This API is not supported due to threadX limitation
osKernelSuspendNKernel Suspend...This API is not supported due to threadX limitation
osKernelResumeNKernel Resume...This API is not supported due to threadX limitation
osKernelGetTickCountYKernel Get Tick Count...No limitations
osKernelGetTickFreqYKernel Get Tick Freq...No limitations
osKernelGetSysTimerCountYKernel Get SysTimer Count...No limitations
osKernelGetSysTimerFreqYKernel Get SysTimer Freq...No limitations

Notes:

  • Due to threadX RTOS limitation (no lock or suspend feature are supported), all kernel lock, suspend and resume APIs are not supported.

Thread Management

The Thread Management function group allows defining, creating, and controlling thread functions in the system.

API NameSupportedShort DescriptionLimitation
osThreadNewYThread New...If argument is given as input this will be considered as entry_input for threadX
osThreadGetNameYThread Get Name...No limitations
osThreadGetIdYThread Get Id...No limitations
osThreadGetStateYThread Get State...Only osThreadRunning, osThreadReady, osThreadTerminated and osThreadBlocked thread states are supported
osThreadSetPriorityYThread Set Priority...No limitations
osThreadGetPriorityYThread Get Priority...No limitations
osThreadYieldYThread Yield...No limitations
osThreadSuspendYThread Suspend...No limitations
osThreadResumeYThread Resume...No limitations
osThreadDetachYThread Detach...TX_THREAD_USER_EXTENSION must be defined
osThreadJoinYThread Join...TX_THREAD_USER_EXTENSION must be defined
osThreadExitYThread Exit...No limitations
osThreadTerminateYThread Terminate...No limitations
osThreadGetStackSizeYThread Get Stack Size...No limitations
osThreadGetStackSpaceYThread Get Stack Space...No limitations
osThreadGetCountYThread Get Count...No limitations
osThreadEnumerateYThread Enumerate...No limitations

Notes:

  • Thread management functions cannot be called from Interrupt Service Routines.
  • To support osThreadDetach and osThreadJoin APIs, the TX_THREAD_USER_EXTENSION must be defined as tx_thread_detached_joinable (ULONG) in tx_user.h file

Thread Flags

Thread Flags are a more specialized version of the Event Flags. See Event Flags. While Event Flags can be used to globally signal a number of threads, thread flags are only send to a single specific thread. Every thread instance can receive thread flags without any additional allocation of a thread flags object.

API NameSupportedShort DescriptionLimitation
osThreadFlagsSetNThread Flags Set...Not yet implemented
osThreadFlagsClearNThread Flags Clear...Not yet implemented
osThreadFlagsGetNThread Flags Get...Not yet implemented
osThreadFlagsWaitNThread Flags Wait...Not yet implemented

Notes:

  • Thread flag management functions cannot be called from Interrupt Service Routines, except for osThreadFlagsSet.
  • The Thread flag management functions are not supported in the current CMSIS RTOS v2 implementation and will be implemented in the future version.

Event Flags

The event flags management functions in CMSIS-RTOS allow you to control or wait for event flags. Each signal has up to 31 event flags.

A thread :

  • Can wait for event flags to be set (using osEventFlagsWait). Using this function, it enters the BLOCKED state.
  • May set one or more flags in any other given thread (using osEventFlagsSet).
  • May clear its own signals or the signals of other threads (using osEventFlagsClear).
  • When a thread wakes up and resumes execution, its signal flags are automatically cleared (unless event flags option osFlagsNoClear is specified).
API NameSupportedShort DescriptionLimitation
osEventFlagsNewYThread Flags Set...No limitations
osEventFlagsSetYThread Flags Clear...No limitations
osEventFlagsClearYThread Flags Get...No limitations
osEventFlagsGetYThread Flags Wait...No limitations
osEventFlagsWaitYThread Flags Clear...No limitations
osEventFlagsDeleteYThread Flags Get...No limitations
osEventFlagsGetNameYThread Flags Wait...No limitations

Notes:

  • The functions osEventFlagsSet, osEventFlagsClear, osEventFlagsGet, and osEventFlagsWait can be called from Interrupt Service Routines
  • If a thread is blocked on waiting an event flag, the osEventFlagsSet will unblock it.

Generic Wait Functions

The generic wait functions provide means for a time delay.

API NameSupportedShort DescriptionLimitation
osDelayYDelay...No limitations
osDelayUntilYDelay Until...No limitations

Notes:

  • Generic wait functions cannot be called from Interrupt Service Routines.

Timer Management

In addition to the Generic Wait Functions CMSIS-RTOS also supports virtual timer objects. These timer objects can trigger the execution of a function (not threads). When a timer expires, a callback function is executed to run associated code with the timer. Each timer can be configured as a one-shot or a periodic timer. A periodic timer repeats its operation until it is deleted or stopped. All timers can be started, restarted, or stopped.

API NameSupportedShort DescriptionLimitation
osTimerNewYTimer New...No limitations
osTimerGetNameYTimer Get Name...No limitations
osTimerStartYTimer Start...No limitations
osTimerStopYTimer Stop...No limitations
osTimerIsRunningYTimer Is Running...No limitations
osTimerDeleteYTimer Delete...No limitations

Notes:

  • Timer management functions cannot be called from Interrupt Service Routines.

Mutex Management

Mutual exclusion (widely known as Mutex) is used in various operating systems for resource management. Many resources in a microcontroller device can be used repeatedly, but only by one thread at a time (for example communication channels, memory, and files). Mutexes are used to protect access to a shared resource. A mutex is created and then passed between the threads (they can acquire and release the mutex).

API NameSupportedShort DescriptionLimitation
osMutexNewYMutex New...osMutexRobust and osMutexRecursive mutex types are not supported
osMutexGetNameYMutex Get Name...No limitations
osMutexAcquireYMutex Acquire...No limitations
osMutexReleaseYMutex Release...No limitations
osMutexGetOwnerYMutex Get Owner...No limitations
osMutexDeleteYMutex Delete...No limitations

Notes:

  • Mutex management functions cannot be called from Interrupt Service Routines (ISR), unlike a binary semaphore that can be released from an ISR.
  • If a thread is blocked on acquiring or releasing the mutex, the osMutexAcquire and osMutexRelease will unblock it. Therefore, acquiring or releasing the mutex will not change the mutex status except from the number of thread blocked on it.

Semaphores

Semaphores are used to manage and protect access to shared resources. Semaphores are very similar to Mutexes. Whereas a Mutex permits just one thread to access a shared resource at a time, a semaphore can be used to permit a fixed number of threads/ISRs to access a pool of shared resources. Using semaphores, access to a group of identical peripherals can be managed (for example multiple DMA channels).

API NameSupportedShort DescriptionLimitation
osSemaphoreNewYSemaphore New...The parameter max_count is not supported
osSemaphoreGetNameYSemaphore Get Name...No limitations
osSemaphoreAcquireYSemaphore Acquire...No limitations
osSemaphoreReleaseYSemaphore Release...No limitations
osSemaphoreGetCountNSemaphore Get Count...This API is not supported due to max_count limitation
osSemaphoreDeleteYSemaphore Delete...No limitations

Notes:

  • The functions osSemaphoreAcquire, osSemaphoreGetCount, and osSemaphoreRelease can be called from Interrupt Service Routines.
  • If a thread is blocked on acquiring or releasing the semaphore, the osSemaphoreAcquire and osSemaphoreRelease will unblock it. Therefore, acquiring or releasing the semaphore will not change the semaphore status except from the number of thread blocked on it
  • Due to max_count limitation, the binary semaphore is not supported.

Memory Pool

Memory Pools are fixed-size blocks of memory that are thread-safe. They operate much faster than the dynamically allocated heap and do not suffer from fragmentation. Being thread-safe, they can be accessed from threads and ISRs alike. A Memory Pool can be seen as a linked list of available (unused) memory blocks of fixed and equal size. Allocating memory from a pool (using osMemoryPoolAlloc) simply unchains a block from the list and hands over control to the user. Freeing memory to the pool (using osMemoryPoolFree) simply rechains the block into the list.

API NameSupportedShort DescriptionLimitation
osMemoryPoolNewYMemoryPool New...No limitations
osMemoryPoolGetNameYMemoryPool Get Name...No limitations
osMemoryPoolAllocYMemoryPool Alloc...No limitations
osMemoryPoolFreeYMemoryPool Free...No limitations
osMemoryPoolGetCapacityYMemoryPool Get Capacity...No limitations
osMemoryPoolGetBlockSizeYMemoryPool Get Block Size...No limitations
osMemoryPoolGetCountYMemoryPool Get Count...No limitations
osMemoryPoolGetSpaceYMemoryPool Get Count...No limitations
osMemoryPoolDeleteYMemoryPool Delete...No limitations

Notes:

  • The functions osMemoryPoolAlloc, osMemoryPoolFree, osMemoryPoolGetCapacity, osMemoryPoolGetBlockSize, osMemoryPoolGetCount, osMemoryPoolGetSpace can be called from Interrupt Service Routines.

Message Queue

Message passing is another basic communication model between threads. In the message passing model, one thread sends data explicitly, while another thread receives it. The operation is more like some kind of I/O rather than a direct access to information to be shared. In CMSIS-RTOS, this mechanism is called s message queue. The data is passed from one thread to another in a FIFO-like operation. Using message queue functions, you can control, send, receive, or wait for messages. The data to be passed can be of integer or pointer type.

API NameSupportedShort DescriptionLimitation
osMessageQueueNewYMessageQueue New...No limitations
osMessageQueueGetNameYMessageQueue Get Name...No limitations
osMessageQueuePutYMessageQueue Put...Message priority is not supported
osMessageQueueGetYMessageQueue Get...Message priority is not supported
osMessageQueueGetCapacityYMessageQueue Get Capacity...No limitations
osMessageQueueGetMsgSizeYMessageQueue Get Msg Size...No limitations
osMessageQueueGetCountYMessageQueue Get Count...No limitations
osMessageQueueGetSpaceYMessageQueue Get Space...No limitations
osMessageQueueResetYMessageQueue Reset...No limitations
osMessageQueueDeleteYMessageQueue Delete...No limitations

Notes:

  • The functions osMessageQueuePut, osMessageQueueGet, osMessageQueueGetCapacity, osMessageQueueGetMsgSize, osMessageQueueGetCount, osMessageQueueGetSpace can be called from Interrupt Service Routines.
  • If a thread is blocked on getting or putting a message on the queue, the osMessageQueuePut and osMessageQueueGet will unblock it. Therefore, putting or getting a message will not change the queue status except from the number of thread blocked on it.