Software tasks & spawn

The RTIC concept of a software task shares a lot with that of hardware tasks with the core difference that a software task is not explicitly bound to a specific interrupt vector, but rather a “dispatcher” interrupt vector running at the same priority as the software task.

Thus, software tasks are tasks which are not directly assigned to a specific interrupt vector.

The #[task] attribute used on a function declare it as a software tasks. Observe the absence of a binds = InterruptName argument to the attribute. The static method task_name::spawn() spawns (starts) a software task and given that there are no higher priority tasks running the task will start executing directly.

All software tasks at the same priority level shares an interrupt handler acting as a dispatcher. What differentiates software and hardware tasks are the dispatcher versus bound interrupt vector.

The interrupt vectors used as dispatchers can not be used by hardware tasks.

A list of “free” (not in use by hardware tasks) and usable interrupts allows the framework to dispatch software tasks.

This list of dispatchers, dispatchers = [FreeInterrupt1, FreeInterrupt2, ...] is an argument to the #[app] attribute.

Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that the list of dispatchers need to cover all priority levels used by software tasks.

Example: The dispatchers = argument needs to have at least 3 entries for an application using three different priorities for software tasks.

The framework will give a compilation error if there are not enough dispatchers provided.

See the following example:


#![allow(unused)]
fn main() {
//! examples/spawn.rs

#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]

use panic_semihosting as _;

#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
mod app {
    use cortex_m_semihosting::{debug, hprintln};

    #[shared]
    struct Shared {}

    #[local]
    struct Local {}

    #[init]
    fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
        hprintln!("init").unwrap();
        foo::spawn().unwrap();

        (Shared {}, Local {}, init::Monotonics())
    }

    #[task]
    fn foo(_: foo::Context) {
        hprintln!("foo").unwrap();

        debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
    }
}
}
$ cargo run --target thumbv7m-none-eabi --example spawn
init
foo