Arm Community
Arm Community
  • Site
  • User
  • Site
  • Search
  • User
Arm Community blogs
Arm Community blogs
Tools, Software and IDEs blog Part 2: Integrating Rust with Real-Time Operating Systems on Arm
  • 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
  • Real Time Operating Systems (RTOS)
  • Ferrous Systems
  • Cortex-M
Actions
  • RSS
  • More
  • Cancel
Related blog posts
Related forum threads

Part 2: Integrating Rust with Real-Time Operating Systems on Arm

Jonathan Pallant
Jonathan Pallant
November 13, 2024
4 minute read time.
Jonathan Pallant, Senior Engineer and Trainer at Ferrous Systems.

In Part 1 of this blog series, we explored building bare-metal applications on Arm microcontrollers using Rust. In Part 2, we shift our focus to integrating Rust with Real-Time Operating Systems (RTOS) on microcontrollers and medium-sized microprocessors.

Most existing RTOS’s are written in C, and so any Rust program running on top will need to interact with an existing C API. Examples of an RTOS include, but are certainly not limited to, Eclipse ThreadX, FreeRTOS or Zephyr. On Arm, these systems are typically executing A32 instructions in AArch32 mode, on processors such as the Cortex-R52; although the concepts here would apply equally on a Cortex-M4, or Cortex-M55 or similar. 

Diagram that shows the approaches to building a Rust application


Figure 1: Approaches to writing Rust applications

Advanced Rust integration with RTOS on Arm

Rust supports importing and exporting C-compatible functions, raw pointers, volatile memory accesses, and inline assembly for low-level hardware interactions. A full demonstration is beyond what can be pasted into a blog post, and so Ferrous Systems have published an open-source example application, which uses the Eclipse ThreadX RTOS and targets an Arm Cortex-R5 on an Arm Versatile Application Board (alongside an Arm PL011 UART, Arm PL190 Vector Interrupt Controller and Arm SP804 Dual Timer). The example compiles ThreadX as a static C library and then links it into a binary that is written in a mixture of Rust and Arm Assembly. This example can be compiled with either Ferrocene or with the standard Rust toolchain. 

As with the bare-metal microcontrollers from the first section, on these real-time systems, the full Rust Standard Library is typically unavailable. Instead, the user is limited to a more basic subset that is known as libcore. It is not that it cannot be done: There are Rust Standard Library ports available for FreeRTOS and NuttX and others. However, these systems usually care a great deal about resource allocation and performance and so it makes sense to create some high-performance bindings to the parts of the RTOS that you require, instead of trying to abstract the RTOS behind an API that is more suited to the Application Processors that the next section will focus on. This approach is also beneficial for Functional Safety systems, where certifying a small custom RTOS shim in Rust is more practical than certifying the full Rust Standard Library. 

In the ThreadX example, after the assembly language startup code has set up the stack pointer and enabled the Floating-Point Unit (FPU), execution is handed over to a main function written in Rust. The Rust code initializes drivers’ peripherals and then hands over execution to the ThreadX scheduler. Part of the ThreadX set-up involves calling back into the Rust firmare through a function called tx_application_define which has been written in Rust but declared as having a “C compatible” interface. This function is used to create a byte pool for the task stacks and various spawn tasks. A snippet from that function is shown in Figure 2, showing how easily Rust can call C APIs. 

let byte_pool = { 
    static BYTE_POOL: StaticCell<threadx_sys::TX_BYTE_POOL> = StaticCell::new(); 
    static BYTE_POOL_STORAGE: StaticCell<[u8; DEMO_POOL_SIZE]> = StaticCell::new(); 
    let byte_pool = BYTE_POOL.uninit(); 
    let byte_pool_storage = BYTE_POOL_STORAGE.uninit(); 
    unsafe { 
        threadx_sys::_tx_byte_pool_create( 
            byte_pool.as_mut_ptr(), 
            c"byte-pool0".as_ptr().cast(), 
            byte_pool_storage.as_mut_ptr().cast(), 
            DEMO_POOL_SIZE as u32, 
        ); 
        byte_pool.assume_init_mut() 
    } 
};

Figure 2: An example of creating a ThreadX byte pool, using Rust. The threadx_sys crate contains auto-generated bindings that are based on the RTOS’s C header files.

Instead of converting the various ThreadX header files into Rust manually, the example uses the bindgen tool to automatically generate Rust bindings for ThreadX. This tool, originally developed by Mozilla and currently being supported by Ferrous Systems, can be applied to almost any library with a standard C header file, for example those provided by ThreadX. The example uses the automatically generated bindings from bindgen, allowing the Rust code to call into any ThreadX function, and the RTOS can call back into any Rust function marked as having extern "C" linkage. 

ThreadX source code must be compiled with a standard C compiler, which the examples handle automatically. Rust is then told to link the resulting libthreadx.a to the compiled Rust code, producing the final binary.  

In our example, the start-up code was written in Rust, but you might prefer the RTOS to handle the start-up and driver initialization from C, with just the tasks written in Rust. Alternatively, you could use an RTOS that was written entirely in Rust, like OxidOS. The general steps remain the same:  Compile the library code you need into static libraries, then compile and link a binary using those static libraries. Whether the RTOS is a library, or the binary does not really change much, except the order in which things must be compiled. 

See Part 3 where we explore using Rust with full-blown operating systems like Linux, Windows, and macOS on Arm processors.

Example of chipboard used in RTOS
Figure 3: Real-Time OSes are often used in industrial and automotive applications.

Read Part 3

Anonymous
Tools, Software and IDEs blog
  • Python on Arm: 2025 Update

    Diego Russo
    Diego Russo
    Python powers applications across Machine Learning (ML), automation, data science, DevOps, web development, and developer tooling.
    • August 21, 2025
  • Product update: Arm Development Studio 2025.0 now available

    Stephen Theobald
    Stephen Theobald
    Arm Development Studio 2025.0 now available with Arm Toolchain for Embedded Professional.
    • July 18, 2025
  • 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