Move deprecated migration guides to deprecated folder

This commit is contained in:
datdenkikniet 2023-04-22 21:27:26 +02:00
parent ed465b0c3b
commit 3d97c9e431
11 changed files with 8 additions and 16 deletions

View file

@ -0,0 +1,4 @@
# Migration Guides
This section describes how to migrate between different versions of RTIC.
It also acts as a comparing reference between versions.

View file

@ -0,0 +1,50 @@
# Migrating from RTFM to RTIC
This section covers how to upgrade an application written against RTFM v0.5.x to
the same version of RTIC. This applies since the renaming of the framework as per [RFC #33].
**Note:** There are no code differences between RTFM v0.5.3 and RTIC v0.5.3, it is purely a name
change.
[RFC #33]: https://github.com/rtic-rs/rfcs/pull/33
## `Cargo.toml`
First, the `cortex-m-rtfm` dependency needs to be updated to
`cortex-m-rtic`.
``` toml
[dependencies]
# change this
cortex-m-rtfm = "0.5.3"
# into this
cortex-m-rtic = "0.5.3"
```
## Code changes
The only code change that needs to be made is that any reference to `rtfm` before now need to point
to `rtic` as follows:
``` rust
//
// Change this
//
#[rtfm::app(/* .. */, monotonic = rtfm::cyccnt::CYCCNT)]
const APP: () = {
// ...
};
//
// Into this
//
#[rtic::app(/* .. */, monotonic = rtic::cyccnt::CYCCNT)]
const APP: () = {
// ...
};
```

View file

@ -0,0 +1,247 @@
# 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][rtic0.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](./migration_rtic.md)
[rtic0.5.2]: https://crates.io/crates/cortex-m-rtic/0.5.2
## `Cargo.toml`
Change the version of `cortex-m-rtfm` to
`"0.5.0"`, change `rtfm` to `rtic`.
Remove the `timer-queue` feature.
``` toml
[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.
``` rust
#[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`.
``` rust
#[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:
``` rust
#[rtfm::app(/* .. */)]
const APP: () = {
#[init]
fn init() {
device.SOME_PERIPHERAL.write(something);
}
// ..
};
```
Into this:
``` rust
#[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:
``` rust
#[rtfm::app(/* .. */)]
const APP: () = {
// hardware tasks
#[exception]
fn SVCall() { /* .. */ }
#[interrupt]
fn UART0() { /* .. */ }
// software task
#[task]
fn foo() { /* .. */ }
// ..
};
```
Into this:
``` rust
#[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:
``` rust
use rtfm::{Duration, Instant, U32Ext};
#[rtfm::app(/* .. */)]
const APP: () = {
#[task(schedule = [b])]
fn a() {
// ..
}
};
```
Into this:
``` rust
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) {
// ..
}
};
```

View file

@ -0,0 +1,372 @@
# Migrating from v0.5.x to v1.0.0
This section describes how to upgrade from v0.5.x to v1.0.0 of the RTIC framework.
## `Cargo.toml` - version bump
Change the version of `cortex-m-rtic` to `"1.0.0"`.
## `mod` 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 used in user
code must be moved inside `mod app`, or be referred to with `super`. For
example, change:
```rust
use some_crate::some_func;
#[rtic::app(/* .. */)]
const APP: () = {
fn func() {
some_crate::some_func();
}
};
```
into
```rust
#[rtic::app(/* .. */)]
mod app {
use some_crate::some_func;
fn func() {
some_crate::some_func();
}
}
```
or
```rust
use some_crate::some_func;
#[rtic::app(/* .. */)]
mod app {
fn func() {
super::some_crate::some_func();
}
}
```
## Move Dispatchers from `extern "C"` to app arguments
Change
``` rust
#[rtic::app(/* .. */)]
const APP: () = {
[code here]
// RTIC requires that unused interrupts are declared in an extern block when
// using software tasks; these free interrupts will be used to dispatch the
// software tasks.
extern "C" {
fn SSI0();
fn QEI0();
}
};
```
into
``` rust
#[rtic::app(/* .. */, dispatchers = [SSI0, QEI0])]
mod app {
[code here]
}
```
This works also for ram functions, see examples/ramfunc.rs
## Resources structs - `#[shared]`, `#[local]`
Previously the RTIC resources had to be in in a struct named exactly "Resources":
``` rust
struct Resources {
// Resources defined in here
}
```
With RTIC v1.0.0 the resources structs are annotated similarly like
`#[task]`, `#[init]`, `#[idle]`: with the attributes `#[shared]` and `#[local]`
``` rust
#[shared]
struct MySharedResources {
// Resources shared between tasks are defined here
}
#[local]
struct MyLocalResources {
// Resources defined here cannot be shared between tasks; each one is local to a single task
}
```
These structs can be freely named by the developer.
## `shared` and `local` arguments in `#[task]`s
In v1.0.0 resources are split between `shared` resources and `local` resources.
`#[task]`, `#[init]` and `#[idle]` no longer have a `resources` argument; they must now use the `shared` and `local` arguments.
In v0.5.x:
``` rust
struct Resources {
local_to_b: i64,
shared_by_a_and_b: i64,
}
#[task(resources = [shared_by_a_and_b])]
fn a(_: a::Context) {}
#[task(resources = [shared_by_a_and_b, local_to_b])]
fn b(_: b::Context) {}
```
In v1.0.0:
``` rust
#[shared]
struct Shared {
shared_by_a_and_b: i64,
}
#[local]
struct Local {
local_to_b: i64,
}
#[task(shared = [shared_by_a_and_b])]
fn a(_: a::Context) {}
#[task(shared = [shared_by_a_and_b], local = [local_to_b])]
fn b(_: b::Context) {}
```
## Symmetric locks
Now RTIC utilizes symmetric locks, this means that the `lock` method need
to be used for all `shared` resource access.
In old code one could do the following as the high priority
task has exclusive access to the resource:
``` rust
#[task(priority = 2, resources = [r])]
fn foo(cx: foo::Context) {
cx.resources.r = /* ... */;
}
#[task(resources = [r])]
fn bar(cx: bar::Context) {
cx.resources.r.lock(|r| r = /* ... */);
}
```
And with symmetric locks one needs to use locks in both tasks:
``` rust
#[task(priority = 2, shared = [r])]
fn foo(cx: foo::Context) {
cx.shared.r.lock(|r| r = /* ... */);
}
#[task(shared = [r])]
fn bar(cx: bar::Context) {
cx.shared.r.lock(|r| r = /* ... */);
}
```
Note that the performance does not change thanks to LLVM's optimizations which optimizes away unnecessary locks.
## Lock-free resource access
In RTIC 0.5 resources shared by tasks running at the same priority could be accessed *without* the `lock` API.
This is still possible in 1.0: the `#[shared]` resource must be annotated with the field-level `#[lock_free]` attribute.
v0.5 code:
``` rust
struct Resources {
counter: u64,
}
#[task(resources = [counter])]
fn a(cx: a::Context) {
*cx.resources.counter += 1;
}
#[task(resources = [counter])]
fn b(cx: b::Context) {
*cx.resources.counter += 1;
}
```
v1.0 code:
``` rust
#[shared]
struct Shared {
#[lock_free]
counter: u64,
}
#[task(shared = [counter])]
fn a(cx: a::Context) {
*cx.shared.counter += 1;
}
#[task(shared = [counter])]
fn b(cx: b::Context) {
*cx.shared.counter += 1;
}
```
## no `static mut` transform
`static mut` variables are no longer transformed to safe `&'static mut` references.
Instead of that syntax, use the `local` argument in `#[init]`.
v0.5.x code:
``` rust
#[init]
fn init(_: init::Context) {
static mut BUFFER: [u8; 1024] = [0; 1024];
let buffer: &'static mut [u8; 1024] = BUFFER;
}
```
v1.0.0 code:
``` rust
#[init(local = [
buffer: [u8; 1024] = [0; 1024]
// type ^^^^^^^^^^^^ ^^^^^^^^^ initial value
])]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let buffer: &'static mut [u8; 1024] = cx.local.buffer;
(Shared {}, Local {}, init::Monotonics())
}
```
## 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)]
const APP: () = {
#[init]
fn init(_: init::Context) {
rtic::pend(Interrupt::UART0);
}
// [more code]
};
```
to this:
``` rust
#[rtic::app(device = lm3s6965)]
mod app {
#[shared]
struct MySharedResources {}
#[local]
struct MyLocalResources {}
#[init]
fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
rtic::pend(Interrupt::UART0);
(MySharedResources, MyLocalResources, init::Monotonics())
}
// [more code]
}
```
## Spawn from anywhere
With the new spawn/spawn_after/spawn_at interface,
old code requiring the context `cx` for spawning such as:
``` rust
#[task(spawn = [bar])]
fn foo(cx: foo::Context) {
cx.spawn.bar().unwrap();
}
#[task(schedule = [bar])]
fn bar(cx: bar::Context) {
cx.schedule.foo(/* ... */).unwrap();
}
```
Will now be written as:
``` rust
#[task]
fn foo(_c: foo::Context) {
bar::spawn().unwrap();
}
#[task]
fn bar(_c: bar::Context) {
// Takes a Duration, relative to “now”
let spawn_handle = foo::spawn_after(/* ... */);
}
#[task]
fn bar(_c: bar::Context) {
// Takes an Instant
let spawn_handle = foo::spawn_at(/* ... */);
}
```
Thus the requirement of having access to the context is dropped.
Note that the attributes `spawn`/`schedule` in the task definition are no longer needed.
---
## Additions
### Extern tasks
Both software and hardware tasks can now be defined external to the `mod app`.
Previously this was possible only by implementing a trampoline calling out the task implementation.
See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`.
This enables breaking apps into multiple files.