diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-05-23 06:26:28 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-23 06:26:28 +0000 |
| commit | 62162241d4c7d82dfbb310113f7525d134cfde9b (patch) | |
| tree | 4346cbe248835eba381003d8592248102028dac5 /book/en/archive | |
| parent | 21b0d97e17922c023a3b5d8148a414d4277f7b87 (diff) | |
| parent | 9fa073f7936782bddf5d02b7b1949032e84de1bd (diff) | |
Merge #741
741: Docs 2 r=korken89 a=datdenkikniet
Working on the migration guide and other docs
TODO:
- [x] Migration guide
- [x] Hardcoded examples should link to example code that is tested (this was already done, AFAICT)
- [x] Address #699
- [x] Discuss: should we remove references to non-v2, apart from the migration guide and link to the book for v1? (Off-github conclusion: yes)
- [x] RTIC {vs,and} Embassy (important: distinction between embassy runtime & HALs)
- [x] More descriptive docs on how to implement & PR implementations of `Monotonic` to `rtic-monotonics`
Co-authored-by: datdenkikniet <jcdra1@gmail.com>
Diffstat (limited to 'book/en/archive')
| -rw-r--r-- | book/en/archive/by_example/app_task.md | 23 | ||||
| -rw-r--r-- | book/en/archive/by_example/monotonic.md | 64 | ||||
| -rw-r--r-- | book/en/archive/by_example/tips/from_ram.md | 45 | ||||
| -rw-r--r-- | book/en/archive/migration.md | 4 | ||||
| -rw-r--r-- | book/en/archive/migration/migration_rtic.md | 50 | ||||
| -rw-r--r-- | book/en/archive/migration/migration_v4.md | 247 | ||||
| -rw-r--r-- | book/en/archive/migration/migration_v5.md | 372 |
7 files changed, 805 insertions, 0 deletions
diff --git a/book/en/archive/by_example/app_task.md b/book/en/archive/by_example/app_task.md new file mode 100644 index 0000000..b2731f6 --- /dev/null +++ b/book/en/archive/by_example/app_task.md @@ -0,0 +1,23 @@ +<!-- Should probably be removed --> + +# Defining tasks with `#[task]` + +Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. + +Tasks can + +* Be spawned (now or in the future, also by themselves) +* Receive messages (passing messages between tasks) +* Be prioritized, allowing preemptive multitasking +* Optionally bind to a hardware interrupt + +RTIC makes a distinction between “software tasks” and “hardware tasks”. + +*Hardware tasks* are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not. + +This means that if a hardware task is bound to, lets say, a UART RX interrupt, the task will be run every +time that interrupt triggers, usually when a character is received. + +*Software tasks* are explicitly spawned in a task, either immediately or using the Monotonic timer mechanism. + +In the coming pages we will explore both tasks and the different options available. diff --git a/book/en/archive/by_example/monotonic.md b/book/en/archive/by_example/monotonic.md new file mode 100644 index 0000000..0ed4340 --- /dev/null +++ b/book/en/archive/by_example/monotonic.md @@ -0,0 +1,64 @@ +# Monotonic & spawn_{at/after} + +The understanding of time is an important concept in embedded systems, and to be able to run tasks +based on time is essential. The framework provides the static methods +`task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`. +`spawn_after` is more commonly used, but in cases where it's needed to have spawns happen +without drift or to a fixed baseline `spawn_at` is available. + +The `#[monotonic]` attribute, applied to a type alias definition, exists to support this. +This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait. +This is generally some timer which handles the timing of the system. +One or more monotonics can coexist in the same system, for example a slow timer that wakes the +system from sleep and another which purpose is for fine grained scheduling while the +system is awake. + +[`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic + +The attribute has one required parameter and two optional parameters, `binds`, `default` and +`priority` respectively. +The required parameter, `binds = InterruptName`, associates an interrupt vector to the timer's +interrupt, while `default = true` enables a shorthand API when spawning and accessing +time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority +of the interrupt vector. + +> The default `priority` is the **maximum priority** of the system. +> If your system has a high priority task with tight scheduling requirements, +> it might be desirable to demote the `monotonic` task to a lower priority +> to reduce scheduling jitter for the high priority task. +> This however might introduce jitter and delays into scheduling via the `monotonic`, +> making it a trade-off. + +The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple. +This activates the monotonics making it possible to use them. + +See the following example: + +``` rust,noplayground +{{#include ../../../../examples/schedule.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example schedule +{{#include ../../../../ci/expected/schedule.run}} +``` + +A key requirement of a Monotonic is that it must deal gracefully with +hardware timer overruns. + +## Canceling or rescheduling a scheduled task + +Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`, +which allows canceling or rescheduling of the task scheduled to run in the future. + +If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was +too late and that the task is already sent for execution. The following example shows this in action: + +``` rust,noplayground +{{#include ../../../../examples/cancel-reschedule.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example cancel-reschedule +{{#include ../../../../ci/expected/cancel-reschedule.run}} +``` diff --git a/book/en/archive/by_example/tips/from_ram.md b/book/en/archive/by_example/tips/from_ram.md new file mode 100644 index 0000000..a153139 --- /dev/null +++ b/book/en/archive/by_example/tips/from_ram.md @@ -0,0 +1,45 @@ +# Running tasks from RAM + +The main goal of moving the specification of RTIC applications to attributes in RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the `link_section` attribute can be applied to tasks to place them in RAM; this can +improve performance in some cases. + +> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` attributes are powerful but also easy to misuse. Incorrectly using any of these attributes can cause undefined behavior; you should always prefer to use safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and `exception` attributes. +> +> In the particular case of RAM functions there's no safe abstraction for it in `cortex-m-rt` v0.6.5 but there's an [RFC] for adding a `ramfunc` attribute in a future release. + +[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 + +The example below shows how to place the higher priority task, `bar`, in RAM. + +``` rust,noplayground +{{#include ../../../../../rtic/examples/ramfunc.rs}} +``` + +Running this program produces the expected output. + +``` console +$ cargo run --target thumbv7m-none-eabi --example ramfunc +``` + +``` console +{{#include ../../../../../rtic/ci/expected/ramfunc.run}} +``` + +One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM +(`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`). + +``` console +$ cargo nm --example ramfunc --release | grep ' foo::' +``` + +``` console +{{#include ../../../../../rtic/ci/expected/ramfunc.run.grep.foo}} +``` + +``` console +$ cargo nm --example ramfunc --target thumbv7m-none-eabi --release | grep '*bar::' +``` + +``` console +{{#include ../../../../../rtic/ci/expected/ramfunc.run.grep.bar}} +``` diff --git a/book/en/archive/migration.md b/book/en/archive/migration.md new file mode 100644 index 0000000..f52b0a5 --- /dev/null +++ b/book/en/archive/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/archive/migration/migration_rtic.md b/book/en/archive/migration/migration_rtic.md new file mode 100644 index 0000000..e079cbf --- /dev/null +++ b/book/en/archive/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,noplayground +// +// 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/archive/migration/migration_v4.md b/book/en/archive/migration/migration_v4.md new file mode 100644 index 0000000..f28b6d9 --- /dev/null +++ b/book/en/archive/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,noplayground +#[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,noplayground +#[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,noplayground +#[rtfm::app(/* .. */)] +const APP: () = { + #[init] + fn init() { + device.SOME_PERIPHERAL.write(something); + } + + // .. +}; +``` + +Into this: + +``` rust,noplayground +#[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,noplayground +#[rtfm::app(/* .. */)] +const APP: () = { + // hardware tasks + #[exception] + fn SVCall() { /* .. */ } + + #[interrupt] + fn UART0() { /* .. */ } + + // software task + #[task] + fn foo() { /* .. */ } + + // .. +}; +``` + +Into this: + +``` rust,noplayground +#[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,noplayground +use rtfm::{Duration, Instant, U32Ext}; + +#[rtfm::app(/* .. */)] +const APP: () = { + #[task(schedule = [b])] + fn a() { + // .. + } +}; +``` + +Into this: + +``` rust,noplayground +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/archive/migration/migration_v5.md b/book/en/archive/migration/migration_v5.md new file mode 100644 index 0000000..1b4fa0d --- /dev/null +++ b/book/en/archive/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,noplayground +#[rtic::app(/* .. */)] +const APP: () = { + [code here] +}; +``` + +into + +``` rust,noplayground +#[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,noplayground +#[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,noplayground +#[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,noplayground +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,noplayground +#[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,noplayground +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,noplayground +#[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,noplayground +#[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,noplayground +#[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,noplayground +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,noplayground +#[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,noplayground +#[init] +fn init(_: init::Context) { + static mut BUFFER: [u8; 1024] = [0; 1024]; + let buffer: &'static mut [u8; 1024] = BUFFER; +} +``` + +v1.0.0 code: + +``` rust,noplayground +#[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,noplayground +#[rtic::app(device = lm3s6965)] +const APP: () = { + #[init] + fn init(_: init::Context) { + rtic::pend(Interrupt::UART0); + } + + // [more code] +}; +``` + +to this: + +``` rust,noplayground +#[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,noplayground +#[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,noplayground +#[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. |
