From 3d97c9e431d7fcb20b1b30bc15c34ccffee03c79 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 22 Apr 2023 21:27:26 +0200 Subject: Move deprecated migration guides to deprecated folder --- book/en/deprecated/migration.md | 4 + book/en/deprecated/migration/migration_rtic.md | 50 +++ book/en/deprecated/migration/migration_v4.md | 247 ++++++++++++++ book/en/deprecated/migration/migration_v5.md | 372 +++++++++++++++++++++ book/en/src/SUMMARY.md | 22 +- book/en/src/migration.md | 4 - book/en/src/migration/migration_rtic.md | 50 --- book/en/src/migration/migration_v2.md | 18 - book/en/src/migration/migration_v2/async_tasks.md | 55 --- .../src/migration/migration_v2/complete_example.md | 169 ---------- book/en/src/migration/migration_v2/monotonics.md | 13 - book/en/src/migration/migration_v2/nightly.md | 5 - book/en/src/migration/migration_v2/rtic-sync.md | 9 - book/en/src/migration/migration_v4.md | 247 -------------- book/en/src/migration/migration_v5.md | 372 --------------------- book/en/src/migration_v1_v2.md | 18 + book/en/src/migration_v1_v2/async_tasks.md | 55 +++ book/en/src/migration_v1_v2/complete_example.md | 169 ++++++++++ book/en/src/migration_v1_v2/monotonics.md | 13 + book/en/src/migration_v1_v2/nightly.md | 5 + book/en/src/migration_v1_v2/rtic-sync.md | 9 + 21 files changed, 949 insertions(+), 957 deletions(-) create mode 100644 book/en/deprecated/migration.md create mode 100644 book/en/deprecated/migration/migration_rtic.md create mode 100644 book/en/deprecated/migration/migration_v4.md create mode 100644 book/en/deprecated/migration/migration_v5.md delete mode 100644 book/en/src/migration.md delete mode 100644 book/en/src/migration/migration_rtic.md delete mode 100644 book/en/src/migration/migration_v2.md delete mode 100644 book/en/src/migration/migration_v2/async_tasks.md delete mode 100644 book/en/src/migration/migration_v2/complete_example.md delete mode 100644 book/en/src/migration/migration_v2/monotonics.md delete mode 100644 book/en/src/migration/migration_v2/nightly.md delete mode 100644 book/en/src/migration/migration_v2/rtic-sync.md delete mode 100644 book/en/src/migration/migration_v4.md delete mode 100644 book/en/src/migration/migration_v5.md create mode 100644 book/en/src/migration_v1_v2.md create mode 100644 book/en/src/migration_v1_v2/async_tasks.md create mode 100644 book/en/src/migration_v1_v2/complete_example.md create mode 100644 book/en/src/migration_v1_v2/monotonics.md create mode 100644 book/en/src/migration_v1_v2/nightly.md create mode 100644 book/en/src/migration_v1_v2/rtic-sync.md diff --git a/book/en/deprecated/migration.md b/book/en/deprecated/migration.md new file mode 100644 index 0000000..f52b0a5 --- /dev/null +++ b/book/en/deprecated/migration.md @@ -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. diff --git a/book/en/deprecated/migration/migration_rtic.md b/book/en/deprecated/migration/migration_rtic.md new file mode 100644 index 0000000..c027da3 --- /dev/null +++ b/book/en/deprecated/migration/migration_rtic.md @@ -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: () = { + // ... + +}; +``` diff --git a/book/en/deprecated/migration/migration_v4.md b/book/en/deprecated/migration/migration_v4.md new file mode 100644 index 0000000..d1a7ebe --- /dev/null +++ b/book/en/deprecated/migration/migration_v4.md @@ -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) { + // .. + } +}; +``` diff --git a/book/en/deprecated/migration/migration_v5.md b/book/en/deprecated/migration/migration_v5.md new file mode 100644 index 0000000..5a8fabc --- /dev/null +++ b/book/en/deprecated/migration/migration_v5.md @@ -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. diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index ceed24e..8b19bf1 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -23,11 +23,12 @@ - [RTIC vs. the world](./rtic_vs.md) - [Awesome RTIC examples](./awesome_rtic.md) - +- [Migrating from v1.0.x to v2.0.0](./migration_v1_v2.md) + - [Rust Nightly & features](./migration_v1_v2/nightly.md) + - [Migrating to `rtic-monotonics`](./migration_v1_v2/monotonics.md) + - [Software tasks must now be `async`](./migration_v1_v2/async_tasks.md) + - [Using and understanding `rtic-sync`](./migration_v1_v2/rtic-sync.md) + - [A code example on migration](./migration_v1_v2/complete_example.md) - [Under the hood](./internals.md) - [Cortex-M architectures](./internals/targets.md) @@ -44,13 +45,4 @@ - [Message passing & `capacity`](./by-example/message_passing.md) - [Task priorities](./by-example/app_priorities.md) - [Monotonic & `spawn_{at/after}`](./by-example/monotonic.md) - --> - ---- - -- [Migrating from v1.0.x to v2.0.0](./migration/migration_v2.md) - - [Rust Nightly & features](./migration/migration_v2/nightly.md) - - [Migrating to `rtic-monotonics`](./migration/migration_v2/monotonics.md) - - [Software tasks must now be `async`](./migration/migration_v2/async_tasks.md) - - [Using and understanding `rtic-sync`](./migration/migration_v2/rtic-sync.md) - - [A code example on migration](./migration/migration_v2/complete_example.md) \ No newline at end of file + --> \ No newline at end of file diff --git a/book/en/src/migration.md b/book/en/src/migration.md deleted file mode 100644 index f52b0a5..0000000 --- a/book/en/src/migration.md +++ /dev/null @@ -1,4 +0,0 @@ -# Migration Guides - -This section describes how to migrate between different versions of RTIC. -It also acts as a comparing reference between versions. diff --git a/book/en/src/migration/migration_rtic.md b/book/en/src/migration/migration_rtic.md deleted file mode 100644 index c027da3..0000000 --- a/book/en/src/migration/migration_rtic.md +++ /dev/null @@ -1,50 +0,0 @@ -# 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: () = { - // ... - -}; -``` diff --git a/book/en/src/migration/migration_v2.md b/book/en/src/migration/migration_v2.md deleted file mode 100644 index 96fa231..0000000 --- a/book/en/src/migration/migration_v2.md +++ /dev/null @@ -1,18 +0,0 @@ -# Migrating from v1.0.x to v2.0.0 - -Migrating a project from RTIC `v1.0.x` to `v2.0.0` involves the following steps: - -1. `v2.0.0` requires [`#![type_alias_impl_trait]`](https://github.com/rust-lang/rust/issues/63063) and Rust Nightly. -2. Migrating from the monotonics included in `v1.0.x` to `rtic-time` and `rtic-monotonics`, replacing `spawn_after`, `spawn_at`. -3. Software tasks are now required to be `async`, and using them correctly. -4. Understanding and using data types provided by `rtic-sync`. - -For a detailed description of the changes, refer to the subchapters. - -If you wish to see a code example of changes required, you can check out [the full example migration page](./migration_v2/complete_example.md). - -#### TL;DR (Too Long; Didn't Read) -1. Add `#![type_alias_impl_trait]` to your crate, and use `cargo +nightly`. -2. Instead of `spawn_after` and `spawn_at`, you now use the `async` functions `delay`, `delay_until` (and related) with impls provided by `rtic-monotonics`. -3. Software tasks _must_ be `async fn`s now. Not returning from a task is allowed so long as there is an `await` in the task. You can still `lock` shared resources. -4. Use `rtic_sync::Arbiter` to `await` access to a shared resource, and `rtic-channel` to communicate between tasks instead of `spawn`-ing new ones. \ No newline at end of file diff --git a/book/en/src/migration/migration_v2/async_tasks.md b/book/en/src/migration/migration_v2/async_tasks.md deleted file mode 100644 index 54e0893..0000000 --- a/book/en/src/migration/migration_v2/async_tasks.md +++ /dev/null @@ -1,55 +0,0 @@ -# Using `async` softare tasks. - -There have been a few changes to software tasks. They are outlined below. - -### Software tasks must now be `async`. - -All software tasks are now required to be `async`. - -#### Required changes. - -All of the tasks in your project that do not bind to an interrupt must now be an `async fn`. For example: - -``` rust -#[task( - local = [ some_resource ], - shared = [ my_shared_resource ], - priority = 2 -)] -fn my_task(cx: my_task::Context) { - cx.local.some_resource.do_trick(); - cx.shared.my_shared_resource.lock(|s| s.do_shared_thing()); -} -``` - -becomes - -``` rust -#[task( - local = [ some_resource ], - shared = [ my_shared_resource ], - priority = 2 -)] -async fn my_task(cx: my_task::Context) { - cx.local.some_resource.do_trick(); - cx.shared.my_shared_resource.lock(|s| s.do_shared_thing()); -} -``` - -## Software tasks may now run forever - -The new `async` software tasks are allowed to run forever, on one precondition: **there must be an `await` within the infinite loop of the task**. An example of such a task: - -``` rust -#[task(local = [ my_channel ] )] -async fn my_task_that_runs_forever(cx: my_task_that_runs_forever::Context) { - loop { - let value = cx.local.my_channel.recv().await; - do_something_with_value(value); - } -} -``` - -## `spawn_after` and `spawn_at` have been removed. - -As discussed in the [Migrating to `rtic-monotonics`](./monotonics.md) chapter, `spawn_after` and `spawn_at` are no longer available. \ No newline at end of file diff --git a/book/en/src/migration/migration_v2/complete_example.md b/book/en/src/migration/migration_v2/complete_example.md deleted file mode 100644 index b68f1ef..0000000 --- a/book/en/src/migration/migration_v2/complete_example.md +++ /dev/null @@ -1,169 +0,0 @@ -# A complete example of migration - -Below you can find the code for the implementation of the `stm32f3_blinky` example for v1.0.x and for v2.0.0. Further down, a diff is displayed. - -# v1.0.X - -```rust -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_rtt_target as _; -use rtic::app; -use rtt_target::{rprintln, rtt_init_print}; -use stm32f3xx_hal::gpio::{Output, PushPull, PA5}; -use stm32f3xx_hal::prelude::*; -use systick_monotonic::{fugit::Duration, Systick}; - -#[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])] -mod app { - use super::*; - - #[shared] - struct Shared {} - - #[local] - struct Local { - led: PA5>, - state: bool, - } - - #[monotonic(binds = SysTick, default = true)] - type MonoTimer = Systick<1000>; - - #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - // Setup clocks - let mut flash = cx.device.FLASH.constrain(); - let mut rcc = cx.device.RCC.constrain(); - - let mono = Systick::new(cx.core.SYST, 36_000_000); - - rtt_init_print!(); - rprintln!("init"); - - let _clocks = rcc - .cfgr - .use_hse(8.MHz()) - .sysclk(36.MHz()) - .pclk1(36.MHz()) - .freeze(&mut flash.acr); - - // Setup LED - let mut gpioa = cx.device.GPIOA.split(&mut rcc.ahb); - let mut led = gpioa - .pa5 - .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper); - led.set_high().unwrap(); - - // Schedule the blinking task - blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); - - ( - Shared {}, - Local { led, state: false }, - init::Monotonics(mono), - ) - } - - #[task(local = [led, state])] - fn blink(cx: blink::Context) { - rprintln!("blink"); - if *cx.local.state { - cx.local.led.set_high().unwrap(); - *cx.local.state = false; - } else { - cx.local.led.set_low().unwrap(); - *cx.local.state = true; - } - blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); - } -} - -``` - -# V2.0.0 - -``` rust -{{ #include ../../../../../examples/stm32f3_blinky/src/main.rs }} -``` - -## A diff between the two projects - -_Note_: This diff may not be 100% accurate, but it displays the important changes. - -``` diff -#![no_main] - #![no_std] -+#![feature(type_alias_impl_trait)] - - use panic_rtt_target as _; - use rtic::app; - use stm32f3xx_hal::gpio::{Output, PushPull, PA5}; - use stm32f3xx_hal::prelude::*; --use systick_monotonic::{fugit::Duration, Systick}; -+use rtic_monotonics::Systick; - - #[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])] - mod app { -@@ -20,16 +21,14 @@ mod app { - state: bool, - } - -- #[monotonic(binds = SysTick, default = true)] -- type MonoTimer = Systick<1000>; -- - #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - // Setup clocks - let mut flash = cx.device.FLASH.constrain(); - let mut rcc = cx.device.RCC.constrain(); - -- let mono = Systick::new(cx.core.SYST, 36_000_000); -+ let mono_token = rtic_monotonics::create_systick_token!(); -+ let mono = Systick::new(cx.core.SYST, 36_000_000, mono_token); - - let _clocks = rcc - .cfgr -@@ -46,7 +45,7 @@ mod app { - led.set_high().unwrap(); - - // Schedule the blinking task -- blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); -+ blink::spawn().unwrap(); - - ( - Shared {}, -@@ -56,14 +55,18 @@ mod app { - } - - #[task(local = [led, state])] -- fn blink(cx: blink::Context) { -- rprintln!("blink"); -- if *cx.local.state { -- cx.local.led.set_high().unwrap(); -- *cx.local.state = false; -- } else { -- cx.local.led.set_low().unwrap(); -- *cx.local.state = true; -- blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); -- } -+ async fn blink(cx: blink::Context) { -+ loop { -+ // A task is now allowed to run forever, provided that -+ // there is an `await` somewhere in the loop. -+ SysTick::delay(1000.millis()).await; -+ rprintln!("blink"); -+ if *cx.local.state { -+ cx.local.led.set_high().unwrap(); -+ *cx.local.state = false; -+ } else { -+ cx.local.led.set_low().unwrap(); -+ *cx.local.state = true; -+ } -+ } -+ } - } -``` \ No newline at end of file diff --git a/book/en/src/migration/migration_v2/monotonics.md b/book/en/src/migration/migration_v2/monotonics.md deleted file mode 100644 index a8b0dba..0000000 --- a/book/en/src/migration/migration_v2/monotonics.md +++ /dev/null @@ -1,13 +0,0 @@ -# Migrating to `rtic-monotonics` - -In previous versions of `rtic`, monotonics were an integral, tightly coupled part of the `#[rtic::app]`. In this new version, [`rtic-monotonics`] provides them in a more decoupled way. - -The `#[monotonic]` attribute is no longer used. Instead, you use a `create_X_token` from [`rtic-monotonics`]. An invocation of this macro returns an interrupt registration token, which can be used to construct an instance of your desired monotonic. - -`spawn_after` and `spawn_at` are no longer available. Instead, you use the async functions `delay` and `delay_until` provided by ipmlementations of the `rtic_time::Monotonic` trait, available through [`rtic-monotonics`]. - -Check out the [code example](./complete_example.md) for an overview of the required changes. - -For more information on current monotonic implementations, see [the `rtic-monotonics` documentation](https://docs.rs/rtic-monotonics), and [the examples](https://github.com/rtic-rs/rtic/tree/master/examples). - -[`rtic-monotonics`]: ghttps://github.com/rtic/rtic-monotonics \ No newline at end of file diff --git a/book/en/src/migration/migration_v2/nightly.md b/book/en/src/migration/migration_v2/nightly.md deleted file mode 100644 index 09f6e33..0000000 --- a/book/en/src/migration/migration_v2/nightly.md +++ /dev/null @@ -1,5 +0,0 @@ -# RTIC now requires Rust Nightly - -The new `async` features require that you use a nightly compiler, and that the feature `type_alias_impl_trait` is enabled for your applications. - -To enable this feature, you must add the line `#![type_alias_impl_trait]` to the root file of your project, on the lines below or above where `#![no_std]` and `#![no_main]` are defined. \ No newline at end of file diff --git a/book/en/src/migration/migration_v2/rtic-sync.md b/book/en/src/migration/migration_v2/rtic-sync.md deleted file mode 100644 index fefde03..0000000 --- a/book/en/src/migration/migration_v2/rtic-sync.md +++ /dev/null @@ -1,9 +0,0 @@ -# Using `rtic-sync` - -`rtic-sync` provides primitives that can be used for message passing and resource sharing in async context. - -The important structs are: -* The `Arbiter`, which allows you to await access to a shared resource in async contexts without using `lock`. -* `Channel`, which allows you to communicate between tasks (both `async` and non-`async`). - -For more information on these structs, see the [`rtic-sync` docs](https://docs.rs/rtic-sync) \ No newline at end of file diff --git a/book/en/src/migration/migration_v4.md b/book/en/src/migration/migration_v4.md deleted file mode 100644 index d1a7ebe..0000000 --- a/book/en/src/migration/migration_v4.md +++ /dev/null @@ -1,247 +0,0 @@ -# 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) { - // .. - } -}; -``` diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md deleted file mode 100644 index 5a8fabc..0000000 --- a/book/en/src/migration/migration_v5.md +++ /dev/null @@ -1,372 +0,0 @@ -# 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. diff --git a/book/en/src/migration_v1_v2.md b/book/en/src/migration_v1_v2.md new file mode 100644 index 0000000..96fa231 --- /dev/null +++ b/book/en/src/migration_v1_v2.md @@ -0,0 +1,18 @@ +# Migrating from v1.0.x to v2.0.0 + +Migrating a project from RTIC `v1.0.x` to `v2.0.0` involves the following steps: + +1. `v2.0.0` requires [`#![type_alias_impl_trait]`](https://github.com/rust-lang/rust/issues/63063) and Rust Nightly. +2. Migrating from the monotonics included in `v1.0.x` to `rtic-time` and `rtic-monotonics`, replacing `spawn_after`, `spawn_at`. +3. Software tasks are now required to be `async`, and using them correctly. +4. Understanding and using data types provided by `rtic-sync`. + +For a detailed description of the changes, refer to the subchapters. + +If you wish to see a code example of changes required, you can check out [the full example migration page](./migration_v2/complete_example.md). + +#### TL;DR (Too Long; Didn't Read) +1. Add `#![type_alias_impl_trait]` to your crate, and use `cargo +nightly`. +2. Instead of `spawn_after` and `spawn_at`, you now use the `async` functions `delay`, `delay_until` (and related) with impls provided by `rtic-monotonics`. +3. Software tasks _must_ be `async fn`s now. Not returning from a task is allowed so long as there is an `await` in the task. You can still `lock` shared resources. +4. Use `rtic_sync::Arbiter` to `await` access to a shared resource, and `rtic-channel` to communicate between tasks instead of `spawn`-ing new ones. \ No newline at end of file diff --git a/book/en/src/migration_v1_v2/async_tasks.md b/book/en/src/migration_v1_v2/async_tasks.md new file mode 100644 index 0000000..54e0893 --- /dev/null +++ b/book/en/src/migration_v1_v2/async_tasks.md @@ -0,0 +1,55 @@ +# Using `async` softare tasks. + +There have been a few changes to software tasks. They are outlined below. + +### Software tasks must now be `async`. + +All software tasks are now required to be `async`. + +#### Required changes. + +All of the tasks in your project that do not bind to an interrupt must now be an `async fn`. For example: + +``` rust +#[task( + local = [ some_resource ], + shared = [ my_shared_resource ], + priority = 2 +)] +fn my_task(cx: my_task::Context) { + cx.local.some_resource.do_trick(); + cx.shared.my_shared_resource.lock(|s| s.do_shared_thing()); +} +``` + +becomes + +``` rust +#[task( + local = [ some_resource ], + shared = [ my_shared_resource ], + priority = 2 +)] +async fn my_task(cx: my_task::Context) { + cx.local.some_resource.do_trick(); + cx.shared.my_shared_resource.lock(|s| s.do_shared_thing()); +} +``` + +## Software tasks may now run forever + +The new `async` software tasks are allowed to run forever, on one precondition: **there must be an `await` within the infinite loop of the task**. An example of such a task: + +``` rust +#[task(local = [ my_channel ] )] +async fn my_task_that_runs_forever(cx: my_task_that_runs_forever::Context) { + loop { + let value = cx.local.my_channel.recv().await; + do_something_with_value(value); + } +} +``` + +## `spawn_after` and `spawn_at` have been removed. + +As discussed in the [Migrating to `rtic-monotonics`](./monotonics.md) chapter, `spawn_after` and `spawn_at` are no longer available. \ No newline at end of file diff --git a/book/en/src/migration_v1_v2/complete_example.md b/book/en/src/migration_v1_v2/complete_example.md new file mode 100644 index 0000000..dc29b25 --- /dev/null +++ b/book/en/src/migration_v1_v2/complete_example.md @@ -0,0 +1,169 @@ +# A complete example of migration + +Below you can find the code for the implementation of the `stm32f3_blinky` example for v1.0.x and for v2.0.0. Further down, a diff is displayed. + +# v1.0.X + +```rust +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_rtt_target as _; +use rtic::app; +use rtt_target::{rprintln, rtt_init_print}; +use stm32f3xx_hal::gpio::{Output, PushPull, PA5}; +use stm32f3xx_hal::prelude::*; +use systick_monotonic::{fugit::Duration, Systick}; + +#[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])] +mod app { + use super::*; + + #[shared] + struct Shared {} + + #[local] + struct Local { + led: PA5>, + state: bool, + } + + #[monotonic(binds = SysTick, default = true)] + type MonoTimer = Systick<1000>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + // Setup clocks + let mut flash = cx.device.FLASH.constrain(); + let mut rcc = cx.device.RCC.constrain(); + + let mono = Systick::new(cx.core.SYST, 36_000_000); + + rtt_init_print!(); + rprintln!("init"); + + let _clocks = rcc + .cfgr + .use_hse(8.MHz()) + .sysclk(36.MHz()) + .pclk1(36.MHz()) + .freeze(&mut flash.acr); + + // Setup LED + let mut gpioa = cx.device.GPIOA.split(&mut rcc.ahb); + let mut led = gpioa + .pa5 + .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper); + led.set_high().unwrap(); + + // Schedule the blinking task + blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); + + ( + Shared {}, + Local { led, state: false }, + init::Monotonics(mono), + ) + } + + #[task(local = [led, state])] + fn blink(cx: blink::Context) { + rprintln!("blink"); + if *cx.local.state { + cx.local.led.set_high().unwrap(); + *cx.local.state = false; + } else { + cx.local.led.set_low().unwrap(); + *cx.local.state = true; + } + blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); + } +} + +``` + +# V2.0.0 + +``` rust +{{ #include ../../../../examples/stm32f3_blinky/src/main.rs }} +``` + +## A diff between the two projects + +_Note_: This diff may not be 100% accurate, but it displays the important changes. + +``` diff +#![no_main] + #![no_std] ++#![feature(type_alias_impl_trait)] + + use panic_rtt_target as _; + use rtic::app; + use stm32f3xx_hal::gpio::{Output, PushPull, PA5}; + use stm32f3xx_hal::prelude::*; +-use systick_monotonic::{fugit::Duration, Systick}; ++use rtic_monotonics::Systick; + + #[app(device = stm32f3xx_hal::pac, peripherals = true, dispatchers = [SPI1])] + mod app { +@@ -20,16 +21,14 @@ mod app { + state: bool, + } + +- #[monotonic(binds = SysTick, default = true)] +- type MonoTimer = Systick<1000>; +- + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + // Setup clocks + let mut flash = cx.device.FLASH.constrain(); + let mut rcc = cx.device.RCC.constrain(); + +- let mono = Systick::new(cx.core.SYST, 36_000_000); ++ let mono_token = rtic_monotonics::create_systick_token!(); ++ let mono = Systick::new(cx.core.SYST, 36_000_000, mono_token); + + let _clocks = rcc + .cfgr +@@ -46,7 +45,7 @@ mod app { + led.set_high().unwrap(); + + // Schedule the blinking task +- blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); ++ blink::spawn().unwrap(); + + ( + Shared {}, +@@ -56,14 +55,18 @@ mod app { + } + + #[task(local = [led, state])] +- fn blink(cx: blink::Context) { +- rprintln!("blink"); +- if *cx.local.state { +- cx.local.led.set_high().unwrap(); +- *cx.local.state = false; +- } else { +- cx.local.led.set_low().unwrap(); +- *cx.local.state = true; +- blink::spawn_after(Duration::::from_ticks(1000)).unwrap(); +- } ++ async fn blink(cx: blink::Context) { ++ loop { ++ // A task is now allowed to run forever, provided that ++ // there is an `await` somewhere in the loop. ++ SysTick::delay(1000.millis()).await; ++ rprintln!("blink"); ++ if *cx.local.state { ++ cx.local.led.set_high().unwrap(); ++ *cx.local.state = false; ++ } else { ++ cx.local.led.set_low().unwrap(); ++ *cx.local.state = true; ++ } ++ } ++ } + } +``` \ No newline at end of file diff --git a/book/en/src/migration_v1_v2/monotonics.md b/book/en/src/migration_v1_v2/monotonics.md new file mode 100644 index 0000000..a8b0dba --- /dev/null +++ b/book/en/src/migration_v1_v2/monotonics.md @@ -0,0 +1,13 @@ +# Migrating to `rtic-monotonics` + +In previous versions of `rtic`, monotonics were an integral, tightly coupled part of the `#[rtic::app]`. In this new version, [`rtic-monotonics`] provides them in a more decoupled way. + +The `#[monotonic]` attribute is no longer used. Instead, you use a `create_X_token` from [`rtic-monotonics`]. An invocation of this macro returns an interrupt registration token, which can be used to construct an instance of your desired monotonic. + +`spawn_after` and `spawn_at` are no longer available. Instead, you use the async functions `delay` and `delay_until` provided by ipmlementations of the `rtic_time::Monotonic` trait, available through [`rtic-monotonics`]. + +Check out the [code example](./complete_example.md) for an overview of the required changes. + +For more information on current monotonic implementations, see [the `rtic-monotonics` documentation](https://docs.rs/rtic-monotonics), and [the examples](https://github.com/rtic-rs/rtic/tree/master/examples). + +[`rtic-monotonics`]: ghttps://github.com/rtic/rtic-monotonics \ No newline at end of file diff --git a/book/en/src/migration_v1_v2/nightly.md b/book/en/src/migration_v1_v2/nightly.md new file mode 100644 index 0000000..09f6e33 --- /dev/null +++ b/book/en/src/migration_v1_v2/nightly.md @@ -0,0 +1,5 @@ +# RTIC now requires Rust Nightly + +The new `async` features require that you use a nightly compiler, and that the feature `type_alias_impl_trait` is enabled for your applications. + +To enable this feature, you must add the line `#![type_alias_impl_trait]` to the root file of your project, on the lines below or above where `#![no_std]` and `#![no_main]` are defined. \ No newline at end of file diff --git a/book/en/src/migration_v1_v2/rtic-sync.md b/book/en/src/migration_v1_v2/rtic-sync.md new file mode 100644 index 0000000..fefde03 --- /dev/null +++ b/book/en/src/migration_v1_v2/rtic-sync.md @@ -0,0 +1,9 @@ +# Using `rtic-sync` + +`rtic-sync` provides primitives that can be used for message passing and resource sharing in async context. + +The important structs are: +* The `Arbiter`, which allows you to await access to a shared resource in async contexts without using `lock`. +* `Channel`, which allows you to communicate between tasks (both `async` and non-`async`). + +For more information on these structs, see the [`rtic-sync` docs](https://docs.rs/rtic-sync) \ No newline at end of file -- cgit v1.2.3