Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
Arm Community blogs
Arm Community blogs
Tools, Software and IDEs blog CMSIS++: a proposal for a future CMSIS, written in C++
  • Blogs
  • Mentions
  • Sub-Groups
  • Tags
  • Jump...
  • Cancel
More blogs in Arm Community blogs
  • AI blog

  • Announcements

  • Architectures and Processors blog

  • Automotive blog

  • Embedded and Microcontrollers blog

  • Internet of Things (IoT) blog

  • Laptops and Desktops blog

  • Mobile, Graphics, and Gaming blog

  • Operating Systems blog

  • Servers and Cloud Computing blog

  • SoC Design and Simulation blog

  • Tools, Software and IDEs blog

Tags
  • C++
  • RTX
  • api
  • CMSIS
  • CMSIS RTOS
Actions
  • RSS
  • More
  • Cancel
Related blog posts
Related forum threads

CMSIS++: a proposal for a future CMSIS, written in C++

Liviu Ionescu
Liviu Ionescu
March 11, 2016
8 minute read time.

Overview

CMSIS++ is a portable, vendor-independent hardware abstraction layer intended for C++/C embedded applications, designed with special consideration for the industry standard Arm Cortex-M processor series.

Major features and benefits

Written in C++ but with C wrappers for full C support

The original Arm/Keil name stands for Cortex Microcontroller Software Interface Standard, and the CMSIS++ design inherits the good things from Arm CMSIS, but goes one step further and ventures into the world of C++; as such, CMSIS++ is not a C++ wrapper running on top of the Arm CMSIS APIs, but a set of newly designed C++ APIs, with C APIs supported as wrappers on top of the native C++ APIs.

Close adherence to standards (POSIX and ISO)

The first iteration of CMSIS++ was a direct rewrite in C++ of Arm CMSIS, but later most of the definitions were adjusted to match the POSIX IEEE Std 1003.1, 2013 Edition and the ISO/IEC 14882:2011 (E) – Programming Language C++ standards.

As such, CMSIS++ RTOS API is no longer a wrapper over Keil RTX (as Arm CMSIS unfortunately was), but a wrapper over standard threads and synchronisation objects.

Compatibility with existing Arm CMSIS

Although fully written in C++, the CMSIS++ RTOS API, initially implemented on top of the FreeRTOS scheduler, and accessible via the C wrapper, was the first non-Keil RTOS that passed the recently released CMSIS RTOS validation suite.

The CMSIS++ RTOS APIs

There are many components in the original CMSIS, but the major ones that benefit from C++ are RTOS and Drivers. Since everything revolves around the RTOS API, the C++ RTOS API was the first CMSIS++ API defined and is presented here in more detail.

Under the CMSIS++ RTOS APIs umbrella there are actually several interfaces, two in C++, two in C and one internal, in C++. The relationships between them is presented below:

CMSIS++ RTOS APIs diagram

The native RTOS C++ API

This is the native RTOS interface, implemented in C++, and providing access to the entire RTOS functionality.

The classes are grouped under the os::rtos namespace, and, to access them, C++ applications need to include the <cmsis-plus/rtos/os.h> header.

Objects can be instantiated from native classes in the usual C++ way, and can be allocated statically, dynamically on the caller stack or dynamically on the heap.

Inspired by the POSIX threads usage model, all CMSIS++ native objects can be instantiated in two ways:

  • a simple, minimalistic, default way, with a default constructor, or, if not possible, a constructor with a minimum number of arguments.
  • a fully configurable, maximal way, by using a set of specific attributes, passed as the first argument to a separate constructor.

For example, to create a thread with default settings, only the pointer to the thread function and a pointer to the function arguments need to be specified, while a thread with custom settings can also have a custom priority, a static stack, and possibly other custom settings.

Here is a short example with a thread that counts 5 seconds and quits:

#include <cmsis-plus/rtos/os.h>
#include <cmsis-plus/diag/trace.h>

using namespace os;

// Define the thread function.
// Native threads can have only one pointer parameter.
void*
func(void* args)
{
  for (int i = 0; i < 5; i++).
    {
      trace::printf("%d sec\n", i);

      // Sleep for one second.
      rtos::sysclock::sleep_for(rtos::clock_systick::frequency_hz);
    }
  return nullptr;
}

// In CMSIS++, os_main() is called from main()
// after initialising and starting the scheduler.
int
os_main(int argc, char* argv[])
{
  // Create a new native thread, with pointer to function and no arguments.
  // The thread is automatically destroyed at the end of the os_main() function.
  rtos::thread th { func };

  // Wait for the thread to terminate.
  th.join();

  trace::puts("done.");
  return 0;
}

The native CMSIS++ thread is basically a POSIX thread, with some additional functionality.

Similarly, synchronisation objects can be created with the usual C++ approach; for example a piece of code that uses a mutex to protects a counter looks like this:

#include <cmsis-plus/rtos/os.h>

// Protected resource (a counter).
typedef struct {
  int count;
} resource_t;

// Alloc the resource statically.
resource_t resource;

// Define a native mutex to protect the resource.
rtos::mutex mx;

void
count(void)
{
  mx.lock();
  // Not much here, real applications are more complicated.
  resource.count++;
  mx.unlock();
}

The ISO C++ Threads API

The CMSIS++ ISO C++ Threads API is an accurate implementation of the ISO C++ 11 standard threads specifications.

With the ISO standard threads defined as wrappers over POSIX threads, and with the CMSIS++ native threads functionally compatible with POSIX threads, the implementation of the CMSIS++ ISO threads was quite straightforward.

The classes are grouped under the os::estd namespace, and, to access them, C++ applications have to include headers from the cmsis-plus/estd folder, like <cmsis-plus/estd/thread>. The namespace std:: and the standard header names (like <thread>) could not be used, to avoid clashes with system definitions when building CMSIS++ applications on POSIX host systems. The e in estd stands for embedded, so the namespace is dedicated to embedded standard definitions.

A similar example using the standard C++ threads:

#include <cmsis-plus/estd/thread>
#include <cmsis-plus/estd/chrono>
#include <cmsis-plus/diag/trace.h>

using namespace os;
using namespace os::estd; // Use the embedded version of 'std::'.

// Define the thread function.
// Thanks to the magic of C++ tuples, standard threads
// can have any number of arguments, of any type.
void*
func(int max_count, const char* msg)
{
  for (int i = 0; i < max_count; i++).
    {
      trace::printf("%d sec, %s\n", i, msg);

      // Sleep for one second. <chrono> is very convenient,
      // notice the duration syntax.
      this_thread::sleep_for (1s);
    }
  return nullptr;
}

// In CMSIS++, os_main() is called from main()
// after initialising and starting the scheduler.
int
os_main(int argc, char* argv[])
{
  // Create a new standard thread, and pass two arguments.
  // The thread is automatically destroyed at the end of the os_main() function.
  thread th { func, 5, "bing" };

  // Wait for the thread to terminate.
  th.join();

  trace::puts("done.");
  return 0;
}

Most of the goodies of the C++ 11 standard can be used, for example RAII mutex locks, condition variables, lambdas:

#include <cmsis-plus/estd/mutex>
#include <cmsis-plus/estd/condition_variable>

using namespace os;
using namespace os::estd;

// Protected resource (a counter and a limit).
typedef struct {
  int count;
  int limit;
} resource_t;

// Alloc the resource statically.
resource_t resource { 0, 10 };

// Define a standard mutex to protect the resource.
mutex mx;
// Define a condition variable to notify listeners and detect limits.
condition_variable cv;

// Increment count and notify possible listeners.
void
count(void)
{
  unique_lock<mutex> lck(mx); // Enter the locked region.

  resource.count++;
  cv.notify_one();

  // No need to explicitly unlock, done automatically.
}

// Return only when count reaches the limit.
void
wait_for_limit()
{
  unique_lock<mutex> lck(mx); // Enter the locked region.

  cv.wait(lck,
          []{ return (res.count >= res.limit); }
  );
}

The new CMSIS++ RTOS C API

Although fully written in C++, CMSIS++ also provides a C API, to be used by C applications. Yes, that's correct, plain C applications can use CMSIS++ without any problems. Only that function names are a bit longer and some of the C++ magic (like running the constructors and the destructors) needs to be done by hand, but otherwise the entire functionality is available.

The C API is defined in the <cmsis-plus/rtos/os-c-api.h> header.

The same simple example that counts 5 seconds and quits, in C would look like:

#include <cmsis-plus/rtos/os-c-api.h>
#include <cmsis-plus/diag/trace.h>

// Define the thread function.
// Native threads can have only one pointer parameter.
void*
func(void* args)
{
  for (int i = 0; i < 5; i++).
    {
      trace_printf("%d sec\n", i);

      // Sleep for one second.
      os_sysclock_sleep_for(OS_INTEGER_SYSTICK_FREQUENCY_HZ);
    }
  return NULL;
}

// In CMSIS++, os_main() is called from main()
// after initialising and starting the scheduler.
int
os_main(int argc, char* argv[])
{
  // Manually allocate space for the thread.
  os_thread_t th;

  // Initialise a new native thread, with function and no arguments.
  os_thread_create(&th, NULL, func, NULL, NULL);

  // Wait for the thread to terminate.
  os_thread_join(&th, NULL);

  // Manually destroy the thread.
  os_thread_destroy(&th);

  trace_puts("done.");
  return 0;
}

The Arm CMSIS RTOS C API (compatibility layer)

Even more, the CMSIS++ C wrapper also implements the original Arm CMSIS API. This is a full and accurate implementation, since this API already passed the Arm CMSIS RTOS validation test.

To access this API, include the <cmsis_os.h> header provided in the CMSIS++ package.

#include <cmsis_os.h>
#include <cmsis-plus/diag/trace.h>

// Define the thread function.
// Arm CMSIS threads can have only one pointer parameter.
void
func(void* args)
{
  for (int i = 0; i < 5; i++).
    {
      trace_printf("%d sec\n", i);

      // Sleep for one second.
      osDelay(osKernelSysTickFrequency);
    }
  // Arm CMSIS threads can return, but there is
  // no way to know when this happens.
}

// The unusual way of defining a thread, specific to CMSIS RTOS API.
// It looks like a function, but it is not, it is a macro that defines
// some internal structures.
osThreadDef(func, 0, 1, 0);

// In CMSIS++, os_main() is called from main()
// after initialising and starting the scheduler.
int
os_main(int argc, char* argv[])
{
  // Initialise a new Arm CMSIS thread, with function and no arguments.
  osThreadCreate(osThread(func), NULL);

  // Since Arm CMSIS has no mechanism to wait for a thread to terminate,
  // a more complicated synchronisation scheme must be used.
  // In this test just sleep for a little longer.
  osDelay(6 * osKernelSysTickFrequency);

  trace_puts("done.");
  return 0;
}

The CMSIS++ RTOS Reference

The entire CMSIS++ RTOS interface is fully documented in the separate site, available in the project web:

CMSIS++ RTOS reference screenshot

More CMSIS++ components

In addition to the RTOS APIs, CMSIS++ also includes:

  • CMSIS++ Drivers - a C++ rewrite of CMSIS Drivers, with extensions;
  • CMSIS++ POSIX I/O - a layer bringing together access to terminal devices, files and sockets, via a unified and standard API, using open(), close(), read(), write() as main functions;
  • CMSIS++ Startup - a portable startup code, replacing non-portable vendor assembly code;
  • CMSIS++ Core - C++ API for the Arm Cortex-M processors core and peripherals;
  • CMSIS++ Diagnostics - a C++/C API providing support for diagnostics and instrumentation.

Conclusions

CMSIS++ is still a young project, and many things need to be addressed, but the core component, the RTOS API, is pretty well defined and awaiting for comments.

For now it may not be perfect (as it tries to be), but it definitely provides a more standard set of primitives, closer to POSIX, and a wider set of APIs, covering both C++ and C applications; at the same time it does its best to preserve compatibility with the original Arm CMSIS APIs.

Any contributions to improve CMSIS++ will be highly appreciated.

More info

CMSIS++ is an open source project, maintained by Liviu Ionescu.

The main source of information for CMSIS++ is the project web.

The Git repositories and all public releases are available from GitHub.

For questions and discussions, please use the CMSIS++ section of the GNU Arm Eclipse forum.

For bugs and feature requests, please use the GitHub issues.

Follow-ups

  • the license was changed to MIT
  • CMSIS RTOS API: Criticism, comments and CMSIS++ suggestions
  • CMSIS++ RTOS: fully functional reference implementation
Anonymous
  • Liviu Ionescu
    Liviu Ionescu over 8 years ago

    mbed OS is a C++ wrapper on top of a very unfortunate design, which is Keil RTX.

    CMSIS++ is a new design, POSIX based, with the native APIs in C++.

    You can check the API yourself at:

    µOS++ IIIe / CMSIS++ Reference: CMSIS++ RTOS C++ API

    I was pleasantly surprised to discover that many of the CMSIS++ suggestions were already included in the new CMSIS 5 beta specifications (CMSIS RTOS API: Criticism, comments and CMSIS++ suggestions ).

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
  • moyanming2013
    moyanming2013 over 8 years ago

    The mbed is written by c++, what is the difference with the mbed and CMSIS++?

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
  • Liviu Ionescu
    Liviu Ionescu over 9 years ago

    I posted a new CMSIS related article. 

    If you would like to express your support for POSIX compatibility, or generally to have a better CMSIS RTOS API, please go to GitHub Issues and comment on any of the existing issues (especially those marked with Help Wanted), or open new tickets with your own suggestions.

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
  • Liviu Ionescu
    Liviu Ionescu over 9 years ago

    vae, here it is: CMSIS++: License changed from LGPL to MIT License

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
  • Ilija Kocho
    Ilija Kocho over 9 years ago

    Liviu Ionescu wrote:

    with GPL being too extreme, my favourite remains MIT; vae, how about this:

    MIT license for the µOS++ project. · GitHub

    MIT is permissive and simple, so I think that If you are happy, than all should be happy.

    Ilija

    • Cancel
    • Up 0 Down
    • Reply
    • More
    • Cancel
>
Tools, Software and IDEs blog
  • GCC 15: Continuously Improving

    Tamar Christina
    Tamar Christina
    GCC 15 brings major Arm optimizations: enhanced vectorization, FP8 support, Neoverse tuning, and 3–5% performance gains on SPEC CPU 2017.
    • June 26, 2025
  • GitHub and Arm are transforming development on Windows for developers

    Pareena Verma
    Pareena Verma
    Develop, test, and deploy natively on Windows on Arm with GitHub-hosted Arm runners—faster CI/CD, AI tooling, and full dev stack, no emulation needed.
    • May 20, 2025
  • What is new in LLVM 20?

    Volodymyr Turanskyy
    Volodymyr Turanskyy
    Discover what's new in LLVM 20, including Armv9.6-A support, SVE2.1 features, and key performance and code generation improvements.
    • April 29, 2025