Add migration to 0.6 along with updated documentation

This commit is contained in:
Henrik Tjäder 2020-10-02 09:33:28 +00:00
parent 163edd7579
commit 9ca10b0d8c
14 changed files with 140 additions and 73 deletions

View file

@ -9,7 +9,7 @@ is required to follow along.
[repository]: https://github.com/rtic-rs/cortex-m-rtic [repository]: https://github.com/rtic-rs/cortex-m-rtic
To run the examples on your laptop / PC you'll need the `qemu-system-arm` To run the examples on your computer you'll need the `qemu-system-arm`
program. Check [the embedded Rust book] for instructions on how to set up an program. Check [the embedded Rust book] for instructions on how to set up an
embedded development environment that includes QEMU. embedded development environment that includes QEMU.

View file

@ -4,11 +4,11 @@ The framework provides an abstraction to share data between any of the contexts
we saw in the previous section (task handlers, `init` and `idle`): resources. we saw in the previous section (task handlers, `init` and `idle`): resources.
Resources are data visible only to functions declared within the `#[app]` Resources are data visible only to functions declared within the `#[app]`
pseudo-module. The framework gives the user complete control over which context module. The framework gives the user complete control over which context
can access which resource. can access which resource.
All resources are declared as a single `struct` within the `#[app]` All resources are declared as a single `struct` within the `#[app]`
pseudo-module. Each field in the structure corresponds to a different resource. module. Each field in the structure corresponds to a different resource.
Resources can optionally be given an initial value using the `#[init]` Resources can optionally be given an initial value using the `#[init]`
attribute. Resources that are not given an initial value are referred to as attribute. Resources that are not given an initial value are referred to as
*late* resources and are covered in more detail in a follow-up section in this *late* resources and are covered in more detail in a follow-up section in this

View file

@ -92,7 +92,7 @@ following snippet:
``` rust ``` rust
#[rtic::app(..)] #[rtic::app(..)]
const APP: () = { mod app {
#[init(spawn = [foo, bar])] #[init(spawn = [foo, bar])]
fn init(cx: init::Context) { fn init(cx: init::Context) {
cx.spawn.foo().unwrap(); cx.spawn.foo().unwrap();
@ -113,5 +113,5 @@ const APP: () = {
fn bar(cx: bar::Context, payload: i32) { fn bar(cx: bar::Context, payload: i32) {
// .. // ..
} }
}; }
``` ```

View file

@ -139,7 +139,7 @@ $ tail target/rtic-expansion.rs
``` rust ``` rust
#[doc = r" Implementation details"] #[doc = r" Implementation details"]
const APP: () = { mod app {
#[doc = r" Always include the device crate which contains the vector table"] #[doc = r" Always include the device crate which contains the vector table"]
use lm3s6965 as _; use lm3s6965 as _;
#[no_mangle] #[no_mangle]
@ -152,7 +152,7 @@ const APP: () = {
rtic::export::wfi() rtic::export::wfi()
} }
} }
}; }
``` ```
Or, you can use the [`cargo-expand`] sub-command. This sub-command will expand Or, you can use the [`cargo-expand`] sub-command. This sub-command will expand

View file

@ -1,6 +1,6 @@
# Types, Send and Sync # Types, Send and Sync
Every function within the `APP` pseudo-module has a `Context` structure as its Every function within the `app` module has a `Context` structure as its
first parameter. All the fields of these structures have predictable, first parameter. All the fields of these structures have predictable,
non-anonymous types so you can write plain functions that take them as arguments. non-anonymous types so you can write plain functions that take them as arguments.

View file

@ -15,7 +15,7 @@ To achieve the fine-grained access control where tasks can only access the
static variables (resources) that they have specified in their RTIC attribute static variables (resources) that they have specified in their RTIC attribute
the RTIC framework performs a source code level transformation. This the RTIC framework performs a source code level transformation. This
transformation consists of placing the resources (static variables) specified by transformation consists of placing the resources (static variables) specified by
the user *inside* a `const` item and the user code *outside* the `const` item. the user *inside* a module and the user code *outside* the module.
This makes it impossible for the user code to refer to these static variables. This makes it impossible for the user code to refer to these static variables.
Access to the resources is then given to each task using a `Resources` struct Access to the resources is then given to each task using a `Resources` struct
@ -29,7 +29,7 @@ happens behind the scenes:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
static mut X: u64: 0; static mut X: u64: 0;
static mut Y: bool: 0; static mut Y: bool: 0;
@ -49,7 +49,7 @@ const APP: () = {
} }
// .. // ..
}; }
``` ```
The framework produces codes like this: The framework produces codes like this:
@ -103,8 +103,8 @@ pub mod bar {
} }
/// Implementation details /// Implementation details
const APP: () = { mod app {
// everything inside this `const` item is hidden from user code // everything inside this module is hidden from user code
static mut X: u64 = 0; static mut X: u64 = 0;
static mut Y: bool = 0; static mut Y: bool = 0;
@ -154,5 +154,5 @@ const APP: () = {
// .. // ..
}); });
} }
}; }
``` ```

View file

@ -28,7 +28,7 @@ An example to illustrate the ceiling analysis:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
struct Resources { struct Resources {
// accessed by `foo` (prio = 1) and `bar` (prio = 2) // accessed by `foo` (prio = 1) and `bar` (prio = 2)
// -> CEILING = 2 // -> CEILING = 2
@ -80,5 +80,5 @@ const APP: () = {
} }
// .. // ..
}; }
``` ```

View file

@ -32,7 +32,7 @@ The example below shows the different types handed out to each task:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mut app {
struct Resources { struct Resources {
#[init(0)] #[init(0)]
x: u64, x: u64,
@ -57,7 +57,7 @@ const APP: () = {
} }
// .. // ..
}; }
``` ```
Now let's see how these types are created by the framework. Now let's see how these types are created by the framework.
@ -99,7 +99,7 @@ pub mod bar {
} }
} }
const APP: () = { mod app {
static mut x: u64 = 0; static mut x: u64 = 0;
impl rtic::Mutex for resources::x { impl rtic::Mutex for resources::x {
@ -129,7 +129,7 @@ const APP: () = {
// .. // ..
}) })
} }
}; }
``` ```
## `lock` ## `lock`
@ -225,7 +225,7 @@ Consider this program:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
struct Resources { struct Resources {
#[init(0)] #[init(0)]
x: u64, x: u64,
@ -277,7 +277,7 @@ const APP: () = {
} }
// .. // ..
}; }
``` ```
The code generated by the framework looks like this: The code generated by the framework looks like this:
@ -315,7 +315,7 @@ pub mod foo {
} }
} }
const APP: () = { mod app {
use cortex_m::register::basepri; use cortex_m::register::basepri;
#[no_mangle] #[no_mangle]
@ -368,7 +368,7 @@ const APP: () = {
} }
// repeat for resource `y` // repeat for resource `y`
}; }
``` ```
At the end the compiler will optimize the function `foo` into something like At the end the compiler will optimize the function `foo` into something like
@ -430,7 +430,7 @@ handler through preemption. This is best observed in the following example:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
struct Resources { struct Resources {
#[init(0)] #[init(0)]
x: u64, x: u64,
@ -484,7 +484,7 @@ const APP: () = {
// .. // ..
} }
}; }
``` ```
IMPORTANT: let's say we *forget* to roll back `BASEPRI` in `UART1` -- this would IMPORTANT: let's say we *forget* to roll back `BASEPRI` in `UART1` -- this would
@ -493,7 +493,7 @@ be a bug in the RTIC code generator.
``` rust ``` rust
// code generated by RTIC // code generated by RTIC
const APP: () = { mod app {
// .. // ..
#[no_mangle] #[no_mangle]
@ -513,7 +513,7 @@ const APP: () = {
// BUG: FORGOT to roll back the BASEPRI to the snapshot value we took before // BUG: FORGOT to roll back the BASEPRI to the snapshot value we took before
basepri::write(initial); basepri::write(initial);
} }
}; }
``` ```
The consequence is that `idle` will run at a dynamic priority of `2` and in fact The consequence is that `idle` will run at a dynamic priority of `2` and in fact

View file

@ -13,7 +13,7 @@ This example gives you an idea of the code that the RTIC framework runs:
``` rust ``` rust
#[rtic::app(device = lm3s6965)] #[rtic::app(device = lm3s6965)]
const APP: () = { mod app {
#[init] #[init]
fn init(c: init::Context) { fn init(c: init::Context) {
// .. user code .. // .. user code ..
@ -28,7 +28,7 @@ const APP: () = {
fn foo(c: foo::Context) { fn foo(c: foo::Context) {
// .. user code .. // .. user code ..
} }
}; }
``` ```
The framework generates an entry point that looks like this: The framework generates an entry point that looks like this:

View file

@ -10,7 +10,7 @@ initialize late resources.
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
struct Resources { struct Resources {
x: Thing, x: Thing,
} }
@ -34,7 +34,7 @@ const APP: () = {
} }
// .. // ..
}; }
``` ```
The code generated by the framework looks like this: The code generated by the framework looks like this:
@ -69,7 +69,7 @@ pub mod foo {
} }
/// Implementation details /// Implementation details
const APP: () = { mod app {
// uninitialized static // uninitialized static
static mut x: MaybeUninit<Thing> = MaybeUninit::uninit(); static mut x: MaybeUninit<Thing> = MaybeUninit::uninit();
@ -101,7 +101,7 @@ const APP: () = {
// .. // ..
}) })
} }
}; }
``` ```
An important detail here is that `interrupt::enable` behaves like a *compiler An important detail here is that `interrupt::enable` behaves like a *compiler

View file

@ -12,7 +12,7 @@ are discouraged from directly invoking an interrupt handler.
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
#[init] #[init]
fn init(c: init::Context) { .. } fn init(c: init::Context) { .. }
@ -39,7 +39,7 @@ const APP: () = {
// in aliasing of the static variable `X` // in aliasing of the static variable `X`
unsafe { UART0() } unsafe { UART0() }
} }
}; }
``` ```
The RTIC framework must generate the interrupt handler code that calls the user The RTIC framework must generate the interrupt handler code that calls the user
@ -57,7 +57,7 @@ fn bar(c: bar::Context) {
// .. user code .. // .. user code ..
} }
const APP: () = { mod app {
// everything in this block is not visible to user code // everything in this block is not visible to user code
#[no_mangle] #[no_mangle]
@ -69,7 +69,7 @@ const APP: () = {
unsafe fn USART1() { unsafe fn USART1() {
bar(..); bar(..);
} }
}; }
``` ```
## By hardware ## By hardware

View file

@ -28,7 +28,7 @@ Consider this example:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
// .. // ..
#[interrupt(binds = UART0, priority = 2, spawn = [bar, baz])] #[interrupt(binds = UART0, priority = 2, spawn = [bar, baz])]
@ -51,7 +51,7 @@ const APP: () = {
extern "C" { extern "C" {
fn UART1(); fn UART1();
} }
}; }
``` ```
The framework produces the following task dispatcher which consists of an The framework produces the following task dispatcher which consists of an
@ -62,7 +62,7 @@ fn bar(c: bar::Context) {
// .. user code .. // .. user code ..
} }
const APP: () = { mod app {
use heapless::spsc::Queue; use heapless::spsc::Queue;
use cortex_m::register::basepri; use cortex_m::register::basepri;
@ -110,7 +110,7 @@ const APP: () = {
// BASEPRI invariant // BASEPRI invariant
basepri::write(snapshot); basepri::write(snapshot);
} }
}; }
``` ```
## Spawning a task ## Spawning a task
@ -144,7 +144,7 @@ mod foo {
} }
} }
const APP: () = { mod app {
// .. // ..
// Priority ceiling for the producer endpoint of the `RQ1` // Priority ceiling for the producer endpoint of the `RQ1`
@ -194,7 +194,7 @@ const APP: () = {
} }
} }
} }
}; }
``` ```
Using `bar_FQ` to limit the number of `bar` tasks that can be spawned may seem Using `bar_FQ` to limit the number of `bar` tasks that can be spawned may seem
@ -211,7 +211,7 @@ fn baz(c: baz::Context, input: u64) {
// .. user code .. // .. user code ..
} }
const APP: () = { mod app {
// .. // ..
// Now we show the full contents of the `Ready` struct // Now we show the full contents of the `Ready` struct
@ -263,13 +263,13 @@ const APP: () = {
} }
} }
} }
}; }
``` ```
And now let's look at the real implementation of the task dispatcher: And now let's look at the real implementation of the task dispatcher:
``` rust ``` rust
const APP: () = { mod app {
// .. // ..
#[no_mangle] #[no_mangle]
@ -304,7 +304,7 @@ const APP: () = {
// BASEPRI invariant // BASEPRI invariant
basepri::write(snapshot); basepri::write(snapshot);
} }
}; }
``` ```
`INPUTS` plus `FQ`, the free queue, is effectively a memory pool. However, `INPUTS` plus `FQ`, the free queue, is effectively a memory pool. However,
@ -357,7 +357,7 @@ Consider the following example:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
#[idle(spawn = [foo, bar])] #[idle(spawn = [foo, bar])]
fn idle(c: idle::Context) -> ! { fn idle(c: idle::Context) -> ! {
// .. // ..
@ -382,7 +382,7 @@ const APP: () = {
fn quux(c: quux::Context) { fn quux(c: quux::Context) {
// .. // ..
} }
}; }
``` ```
This is how the ceiling analysis would go: This is how the ceiling analysis would go:

View file

@ -12,7 +12,7 @@ Let's see how this in implemented in code. Consider the following program:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
// .. // ..
#[task(capacity = 2, schedule = [foo])] #[task(capacity = 2, schedule = [foo])]
@ -24,7 +24,7 @@ const APP: () = {
extern "C" { extern "C" {
fn UART0(); fn UART0();
} }
}; }
``` ```
## `schedule` ## `schedule`
@ -46,7 +46,7 @@ mod foo {
} }
} }
const APP: () = { mod app {
type Instant = <path::to::user::monotonic::timer as rtic::Monotonic>::Instant; type Instant = <path::to::user::monotonic::timer as rtic::Monotonic>::Instant;
// all tasks that can be `schedule`-d // all tasks that can be `schedule`-d
@ -100,7 +100,7 @@ const APP: () = {
} }
} }
} }
}; }
``` ```
This looks very similar to the `Spawn` implementation. In fact, the same This looks very similar to the `Spawn` implementation. In fact, the same
@ -123,7 +123,7 @@ is up.
Let's see the associated code. Let's see the associated code.
``` rust ``` rust
const APP: () = { mod app {
#[no_mangle] #[no_mangle]
fn SysTick() { fn SysTick() {
const PRIORITY: u8 = 1; const PRIORITY: u8 = 1;
@ -146,7 +146,7 @@ const APP: () = {
} }
} }
} }
}; }
``` ```
This looks similar to a task dispatcher except that instead of running the This looks similar to a task dispatcher except that instead of running the
@ -197,7 +197,7 @@ able to insert the task in the timer queue; this lets us omit runtime checks.
## System timer priority ## System timer priority
The priority of the system timer can't set by the user; it is chosen by the The priority of the system timer can't be set by the user; it is chosen by the
framework. To ensure that lower priority tasks don't prevent higher priority framework. To ensure that lower priority tasks don't prevent higher priority
tasks from running we choose the priority of the system timer to be the maximum tasks from running we choose the priority of the system timer to be the maximum
of all the `schedule`-able tasks. of all the `schedule`-able tasks.
@ -222,7 +222,7 @@ To illustrate, consider the following example:
``` rust ``` rust
#[rtic::app(device = ..)] #[rtic::app(device = ..)]
const APP: () = { mod app {
#[task(priority = 3, spawn = [baz])] #[task(priority = 3, spawn = [baz])]
fn foo(c: foo::Context) { fn foo(c: foo::Context) {
// .. // ..
@ -237,7 +237,7 @@ const APP: () = {
fn baz(c: baz::Context) { fn baz(c: baz::Context) {
// .. // ..
} }
}; }
``` ```
The ceiling analysis would go like this: The ceiling analysis would go like this:
@ -246,7 +246,7 @@ The ceiling analysis would go like this:
`SysTick` must run at the highest priority between these two, that is `3`. `SysTick` must run at the highest priority between these two, that is `3`.
- `foo::Spawn` (prio = 3) and `bar::Schedule` (prio = 2) contend over the - `foo::Spawn` (prio = 3) and `bar::Schedule` (prio = 2) contend over the
consumer endpoind of `baz_FQ`; this leads to a priority ceiling of `3`. consumer endpoint of `baz_FQ`; this leads to a priority ceiling of `3`.
- `bar::Schedule` (prio = 2) has exclusive access over the consumer endpoint of - `bar::Schedule` (prio = 2) has exclusive access over the consumer endpoint of
`foo_FQ`; thus the priority ceiling of `foo_FQ` is effectively `2`. `foo_FQ`; thus the priority ceiling of `foo_FQ` is effectively `2`.
@ -270,7 +270,7 @@ run; this `Instant` is read in the task dispatcher and passed to the user code
as part of the task context. as part of the task context.
``` rust ``` rust
const APP: () = { mod app {
// .. // ..
#[no_mangle] #[no_mangle]
@ -303,7 +303,7 @@ const APP: () = {
// BASEPRI invariant // BASEPRI invariant
basepri::write(snapshot); basepri::write(snapshot);
} }
}; }
``` ```
Conversely, the `spawn` implementation needs to write a value to the `INSTANTS` Conversely, the `spawn` implementation needs to write a value to the `INSTANTS`
@ -333,7 +333,7 @@ mod foo {
} }
} }
const APP: () = { mod app {
impl<'a> foo::Spawn<'a> { impl<'a> foo::Spawn<'a> {
/// Spawns the `baz` task /// Spawns the `baz` task
pub fn baz(&self, message: u64) -> Result<(), u64> { pub fn baz(&self, message: u64) -> Result<(), u64> {
@ -364,5 +364,5 @@ const APP: () = {
} }
} }
} }
}; }
``` ```

View file

@ -1,14 +1,81 @@
# Migrating from v0.4.x to v0.5.0 # Migration of RTIC
## Migrating from v0.5.x to v0.6.0
This section describes how to upgrade from v0.5.x to v0.6.0 of the RTIC framework.
### `Cargo.toml` - version bump
Change the version of `cortex-m-rtic` to `"0.6.0"`.
### Module instead of Const
With the support of attributes on modules the `const APP` workaround is not needed.
Change
``` rust
#[rtic::app(/* .. */)]
const APP: () = {
[code here]
};
```
into
``` rust
#[rtic::app(/* .. */)]
mod app {
[code here]
}
```
Now that a regular Rust module is used it means it is possible to have custom
user code within that module.
Additionally, it means that `use`-statements for resources etc may be required.
### Init always returns late resources
In order to make the API more symmetric the #[init]-task always returns a late resource.
From this:
``` rust
#[rtic::app(device = lm3s6965)]
mod app {
#[init]
fn init(_: init::Context) {
rtic::pend(Interrupt::UART0);
}
[more code]
}
```
to this:
``` rust
#[rtic::app(device = lm3s6965)]
mod app {
#[init]
fn init(_: init::Context) -> init::LateResources {
rtic::pend(Interrupt::UART0);
init::LateResources {}
}
[more code]
}
```
## Migrating from v0.4.x to v0.5.0
This section covers how to upgrade an application written against RTIC v0.4.x to This section covers how to upgrade an application written against RTIC v0.4.x to
the version v0.5.0 of the framework. the version v0.5.0 of the framework.
## `Cargo.toml` ### `Cargo.toml`
First, the version of the `cortex-m-rtic` dependency needs to be updated to First, the version of the `cortex-m-rtic` dependency needs to be updated to
`"0.5.0"`. The `timer-queue` feature needs to be removed. `"0.5.0"`. The `timer-queue` feature needs to be removed.
``` toml ``` toml
[dependencies.cortex-m-rtic] [dependencies.cortex-m-rtic]
# change this # change this
@ -22,7 +89,7 @@ features = ["timer-queue"]
# ^^^^^^^^^^^^^ # ^^^^^^^^^^^^^
``` ```
## `Context` argument ### `Context` argument
All functions inside the `#[rtic::app]` item need to take as first argument a All functions inside the `#[rtic::app]` item need to take as first argument a
`Context` structure. This `Context` type will contain the variables that were `Context` structure. This `Context` type will contain the variables that were
@ -74,7 +141,7 @@ const APP: () = {
}; };
``` ```
## Resources ### Resources
The syntax used to declare resources has been changed from `static mut` The syntax used to declare resources has been changed from `static mut`
variables to a `struct Resources`. variables to a `struct Resources`.
@ -98,7 +165,7 @@ const APP: () = {
}; };
``` ```
## Device peripherals ### Device peripherals
If your application was accessing the device peripherals in `#[init]` through If your application was accessing the device peripherals in `#[init]` through
the `device` variable then you'll need to add `peripherals = true` to the the `device` variable then you'll need to add `peripherals = true` to the
@ -136,7 +203,7 @@ const APP: () = {
}; };
``` ```
## `#[interrupt]` and `#[exception]` ### `#[interrupt]` and `#[exception]`
The `#[interrupt]` and `#[exception]` attributes have been removed. To declare The `#[interrupt]` and `#[exception]` attributes have been removed. To declare
hardware tasks in v0.5.x use the `#[task]` attribute with the `binds` argument. hardware tasks in v0.5.x use the `#[task]` attribute with the `binds` argument.
@ -182,7 +249,7 @@ const APP: () = {
}; };
``` ```
## `schedule` ### `schedule`
The `timer-queue` feature has been removed. To use the `schedule` API one must The `timer-queue` feature has been removed. To use the `schedule` API one must
first define the monotonic timer the runtime will use using the `monotonic` first define the monotonic timer the runtime will use using the `monotonic`