496: update the 0.5.x -> 0.6.0 migration guide r=AfoHT a=japaric

to use the new resources syntax
I also reordered the sections to cover all the resource API first before covering the spawn API
I've also added a section about the old `static mut` variable transform

Co-authored-by: Jorge Aparicio <jorge.aparicio@ferrous-systems.com>
This commit is contained in:
bors[bot] 2021-07-21 08:40:21 +00:00 committed by GitHub
commit 2f3b5cba80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 144 additions and 73 deletions

View file

@ -102,6 +102,134 @@ mod app {
This works also for ram functions, see examples/ramfunc.rs 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 v0.6.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 v0.6.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 v0.6.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.
## 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;
}
```
v0.6.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 ## Init always returns late resources
In order to make the API more symmetric the #[init]-task always returns a late resource. In order to make the API more symmetric the #[init]-task always returns a late resource.
@ -125,48 +253,23 @@ to this:
``` rust ``` rust
#[rtic::app(device = lm3s6965)] #[rtic::app(device = lm3s6965)]
mod app { mod app {
#[shared]
struct MySharedResources {}
#[local]
struct MyLocalResources {}
#[init] #[init]
fn init(_: init::Context) -> (init::LateResources, init::Monotonics) { fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
rtic::pend(Interrupt::UART0); rtic::pend(Interrupt::UART0);
(init::LateResources {}, init::Monotonics()) (MySharedResources, MyLocalResources, init::Monotonics())
} }
// [more code] // [more code]
} }
``` ```
## Resources struct - `#[resources]`
Previously the RTIC resources had to be in in a struct named exactly "Resources":
``` rust
struct Resources {
// Resources defined in here
}
```
With RTIC v0.6.0 the resources struct is annotated similarly like
`#[task]`, `#[init]`, `#[idle]`: with an attribute `#[resources]`
``` rust
#[resources]
struct Resources {
// Resources defined in here
}
```
In fact, the name of the struct is now up to the developer:
``` rust
#[resources]
struct Whateveryouwant {
// Resources defined in here
}
```
would work equally well.
## Spawn/schedule from anywhere ## Spawn/schedule from anywhere
With the new "spawn/schedule from anywhere", old code such as: With the new "spawn/schedule from anywhere", old code such as:
@ -201,37 +304,6 @@ fn bar(_c: bar::Context) {
Note that the attributes `spawn` and `schedule` are no longer needed. Note that the attributes `spawn` and `schedule` are no longer needed.
## Symmetric locks
Now RTIC utilizes symmetric locks, this means that the `lock` method need to be used for all 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, resources = [r])]
fn foo(cx: foo::Context) {
cx.resources.r.lock(|r| r = /* ... */);
}
#[task(resources = [r])]
fn bar(cx: bar::Context) {
cx.resources.r.lock(|r| r = /* ... */);
}
```
Note that the performance does not change thanks to LLVM's optimizations which optimizes away unnecessary locks.
--- ---
@ -242,4 +314,3 @@ Note that the performance does not change thanks to LLVM's optimizations which o
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. 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`. See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`.

View file

@ -16,6 +16,6 @@ mod app {
#[init] #[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
} }

View file

@ -21,7 +21,7 @@ mod app {
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
foo::spawn(1, 2).unwrap(); foo::spawn(1, 2).unwrap();
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task()] #[task()]

View file

@ -21,7 +21,7 @@ mod app {
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
foo::spawn(1, 2).unwrap(); foo::spawn(1, 2).unwrap();
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task] #[task]

View file

@ -18,7 +18,7 @@ fn analyze() {
#[init] #[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task(priority = 1)] #[task(priority = 1)]

View file

@ -10,7 +10,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task(binds = NonMaskableInt)] #[task(binds = NonMaskableInt)]

View file

@ -10,7 +10,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task] #[task]

View file

@ -10,7 +10,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task(binds = UART0)] #[task(binds = UART0)]

View file

@ -10,7 +10,7 @@ mod app {
#[init] #[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
(Shared {}, Local {}, init::Monotonics {}) (Shared {}, Local {}, init::Monotonics())
} }
#[task(binds = GPIOA, priority = 1)] #[task(binds = GPIOA, priority = 1)]