Migrating from v0.4.x to v0.5.0

This section covers how to upgrade an application written against RTFM v0.4.x to the version v0.5.0 of the framework.

Project name change RTFM -> RTIC

With release v0.5.2 the name was change to Real-Time Interrupt-driven Concurrency

All occurrences of RTFM needs to change to RTIC.

See migration guide RTFM to RTIC

Cargo.toml

Change the version of cortex-m-rtfm to "0.5.0", change rtfm to rtic. Remove the timer-queue feature.

[dependencies.cortex-m-rtfm]
# change this
version = "0.4.3"

# into this
[dependencies.cortex-m-rtic]
version = "0.5.0"

# and remove this Cargo feature
features = ["timer-queue"]
#           ^^^^^^^^^^^^^

Context argument

All functions inside the #[rtfm::app] item need to take as first argument a Context structure. This Context type will contain the variables that were magically injected into the scope of the function by version v0.4.x of the framework: resources, spawn, schedule -- these variables will become fields of the Context structure. Each function within the #[rtfm::app] item gets a different Context type.

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    // change this
    #[task(resources = [x], spawn = [a], schedule = [b])]
    fn foo() {
        resources.x.lock(|x| /* .. */);
        spawn.a(message);
        schedule.b(baseline);
    }

    // into this
    #[task(resources = [x], spawn = [a], schedule = [b])]
    fn foo(mut cx: foo::Context) {
        // ^^^^^^^^^^^^^^^^^^^^

        cx.resources.x.lock(|x| /* .. */);
    //  ^^^

        cx.spawn.a(message);
    //  ^^^

        cx.schedule.b(message, baseline);
    //  ^^^
    }

    // change this
    #[init]
    fn init() {
        // ..
    }

    // into this
    #[init]
    fn init(cx: init::Context) {
        //  ^^^^^^^^^^^^^^^^^
        // ..
    }

    // ..
};
}

Resources

The syntax used to declare resources has changed from static mut variables to a struct Resources.

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    // change this
    static mut X: u32 = 0;
    static mut Y: u32 = (); // late resource

    // into this
    struct Resources {
        #[init(0)] // <- initial value
        X: u32, // NOTE: we suggest changing the naming style to `snake_case`

        Y: u32, // late resource
    }

    // ..
};
}

Device peripherals

If your application was accessing the device peripherals in #[init] through the device variable then you'll need to add peripherals = true to the #[rtfm::app] attribute to continue to access the device peripherals through the device field of the init::Context structure.

Change this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    #[init]
    fn init() {
        device.SOME_PERIPHERAL.write(something);
    }

    // ..
};
}

Into this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */, peripherals = true)]
//                    ^^^^^^^^^^^^^^^^^^
const APP: () = {
    #[init]
    fn init(cx: init::Context) {
        //  ^^^^^^^^^^^^^^^^^
        cx.device.SOME_PERIPHERAL.write(something);
    //  ^^^
    }

    // ..
};
}

#[interrupt] and #[exception]

Remove the attributes #[interrupt] and #[exception]. To declare hardware tasks in v0.5.x use the #[task] attribute with the binds argument instead.

Change this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    // hardware tasks
    #[exception]
    fn SVCall() { /* .. */ }

    #[interrupt]
    fn UART0() { /* .. */ }

    // software task
    #[task]
    fn foo() { /* .. */ }

    // ..
};
}

Into this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    #[task(binds = SVCall)]
    //     ^^^^^^^^^^^^^^
    fn svcall(cx: svcall::Context) { /* .. */ }
    // ^^^^^^ we suggest you use a `snake_case` name here

    #[task(binds = UART0)]
    //     ^^^^^^^^^^^^^
    fn uart0(cx: uart0::Context) { /* .. */ }

    #[task]
    fn foo(cx: foo::Context) { /* .. */ }

    // ..
};
}

schedule

The schedule API no longer requires the timer-queue cargo feature. To use the schedule API one must first define the monotonic timer the runtime will use using the monotonic argument of the #[rtfm::app] attribute. To continue using the cycle counter (CYCCNT) as the monotonic timer, and match the behavior of version v0.4.x, add the monotonic = rtfm::cyccnt::CYCCNT argument to the #[rtfm::app] attribute.

Also, the Duration and Instant types and the U32Ext trait moved into the rtfm::cyccnt module. This module is only available on ARMv7-M+ devices. The removal of the timer-queue also brings back the DWT peripheral inside the core peripherals struct, if DWT is required, ensure it is enabled by the application inside init.

Change this:

#![allow(unused)]
fn main() {
use rtfm::{Duration, Instant, U32Ext};

#[rtfm::app(/* .. */)]
const APP: () = {
    #[task(schedule = [b])]
    fn a() {
        // ..
    }
};
}

Into this:

#![allow(unused)]
fn main() {
use rtfm::cyccnt::{Duration, Instant, U32Ext};
//        ^^^^^^^^

#[rtfm::app(/* .. */, monotonic = rtfm::cyccnt::CYCCNT)]
//                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const APP: () = {
    #[init]
    fn init(cx: init::Context) {
        cx.core.DWT.enable_cycle_counter();
        // optional, configure the DWT run without a debugger connected
        cx.core.DCB.enable_trace();
    }
    #[task(schedule = [b])]
    fn a(cx: a::Context) {
        // ..
    }
};
}