Rust and ThreadX
ThreadX is a real-time operating system (or RTOS) written in C which dates back to 1997. The developer, Express Logic, was acquired by Microsoft in 2019 and the product was renamed Azure RTOS. In November 2023, it was announced that the product was to be transferred to the Eclipse Foundation and renamed to Eclipse ThreadX
So why is this interesting to Embedded Rust developers? Don't we already have RTIC and embassy-rs? It's interesting, because ThreadX/Azure RTOS was previously available as a certified software component.
Certified software components
If you were building a product where functional safety was paramount (such as the anti-lock braking system for a car), you would ultimately need to prove to an assessor, working at a body that is recognized and trusted by your industry, that your product was safe enough to go on the market. This is a difficult process because these assessors do not generally just "take your word for it" and require actual evidence that you did your job properly and in accordance with the various written standards for product development in whatever area your product is going in to.
One way you could lower the burden of this assessment process would be to use qualified tools, such as Ferrocene. Another is to not write all the software from scratch but instead rely on some third-party certified software components. These effectively mean you can say to the auditor, "We don't have to spend a lot of time looking at these tools/components, because I have a certificate here from the developer that says they have been shown to work correctly provided I follow this set of user instructions." You are then free to spend more time convincing the auditor that the stuff you wrote is in accordance with the standards (and that you followed the instructions).
Functional-safety certs and open source
Traditionally, functional-safety certified RTOSes like ThreadX were expensive commercial products that were not open source. After all, producing the evidence necessary for an assessor to issue the certificate is an expensive business, and you hope to recover your costs by selling the software. If you make the software open source, what's to stop someone just copying it and selling their own version?
We pushed back on this idea when we made Ferrocene open source; you can have the source code to inspect and compile for yourself, you can have pre-compiled binaries for a small monthly charge, and if you want our digitally-signed pile of evidence to give to your assessor, there's separate fee to pay (and you should call us to discuss your needs).
We're therefore delighted to see that The Eclipse Foundation has promised to make Eclipse ThreadX open source, but also continue to make it available as a certified software component for functional-safety product developments. How this works in practice is something I'm sure they're working on, and something we're very eager to learn more about!
From C to Rust
In the mean time, we noticed that there don't seem to be many examples online about writing ThreadX applications in Rust. We decided to fix that, and present https://github.com/ferrous-systems/threadx-experiments - an open-source Rust demo which uses ThreadX and runs on the Cortex-M4 based Nordic nRF52840-DK.
To make this work, we started off manually transcribing a couple of ThreadX APIs from C to Rust to get started, and they looked a bit like this:
#[repr(C)]
struct TxThread {
storage: [u32; 128],
}
static mut THREAD_0: MaybeUninit<TxThread> = MaybeUninit::uninit();
extern "C" {
fn _tx_initialize_kernel_enter();
fn _tx_thread_create(
thread_ptr: *mut TxThread,
name_ptr: *const core::ffi::c_char,
entry_function: TxThreadFunc,
entry_input: core::ffi::c_ulong,
stack_start: *mut core::ffi::c_void,
stack_size: core::ffi::c_ulong,
priority: core::ffi::c_uint,
preempt_threshold: core::ffi::c_uint,
time_slice: core::ffi::c_ulong,
auto_start: core::ffi::c_uint,
) -> u32;
}
_tx_thread_create(
THREAD_0.as_mut_ptr(),
"thread 0\0".as_ptr() as *const core::ffi::c_char,
my_thread,
0xAABBCCDD,
pointer,
DEMO_STACK_SIZE,
1,
1,
0,
1,
);
Having done that, it didn't take long to get bindgen to automatically generate a Rust version of the ThreadX API. This gives us the repr(C)
types, and the extern "C"
blocks in a way that is guaranteed to be compatible with what the C header file was declaring. An interesting hiccup was that the actual ThreadX API for creating a thread takes a char*
for the thread name - which maps to a pointer to a mutable null-terminated string. I'm pretty sure it doesn't actually modify the given string, but that's what the API says! Unfortunately a failure to make all things const that should be const is a fact of life when working with C - and one of the reasons Rust has immutability by default!
If there's any interest in this from people, the next step would be to wrap those raw auto-converted APIs in some higher-level Rust types.
If you'd like to sponsor further work on Rust with ThreadX, or if you're interested in writing the software for your next product development in Rust, whether in the functional-safety space or not, please do get in touch!
– Jonathan