diff --git a/book/en/src/awesome_rtic.md b/book/en/src/awesome_rtic.md index 925cd3fd96..36d38e6252 100644 --- a/book/en/src/awesome_rtic.md +++ b/book/en/src/awesome_rtic.md @@ -1 +1,8 @@ # Awesome RTIC examples + +See the [`rtic-rs/rtic-examples`][rticexamples] repository for community +provided complete examples. + +Pull-requests to this repo are welcome! + +[rticexamples]: https://github.com/rtic-rs/rtic-examples diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md index fef6872e49..84f00193ae 100644 --- a/book/en/src/by-example.md +++ b/book/en/src/by-example.md @@ -3,15 +3,15 @@ This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTIC) framework to new users by walking them through examples of increasing complexity. -All examples in this part of the book can be found in the GitHub [repository] of -the project. The examples can be run on QEMU (emulating a Cortex M3 target) so no special hardware -is required to follow along. +All examples in this part of the book are accessible at the +[GitHub repository][repoexamples]. +The examples are runnable on QEMU (emulating a Cortex M3 target), +thus no special hardware required to follow along. -[repository]: https://github.com/rtic-rs/cortex-m-rtic +[repoexamples]: https://github.com/rtic-rs/cortex-m-rtic/tree/master/examples -To run the examples on your computer you'll need the `qemu-system-arm` -program. Check [the embedded Rust book] for instructions on how to set up an +To run the examples with QEMU you will need the `qemu-system-arm` program. +Check [the embedded Rust book] for instructions on how to set up an embedded development environment that includes QEMU. [the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html - diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 09f3371e26..2c6aca7a2b 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -3,14 +3,14 @@ ## Requirements on the `app` attribute All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute -must be applied to a `mod`-item containing the RTIC application. The `app` -attribute has a mandatory `device` -argument that takes a *path* as a value. This must be a full path pointing to a +only applies to a `mod`-item containing the RTIC application. The `app` +attribute has a mandatory `device` argument that takes a *path* as a value. +This must be a full path pointing to a *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or newer. -The `app` attribute will expand into a suitable entry point so it's not required -to use the [`cortex_m_rt::entry`] attribute. +The `app` attribute will expand into a suitable entry point and thus replaces +the use of the [`cortex_m_rt::entry`] attribute. [`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html [`svd2rust`]: https://crates.io/crates/svd2rust @@ -18,9 +18,9 @@ to use the [`cortex_m_rt::entry`] attribute. ## An RTIC application example -To give a flavor of RTIC, the following example contains commonly used features. In the following sections we will go through each feature in detail. +To give a flavour of RTIC, the following example contains commonly used features. +In the following sections we will go through each feature in detail. ``` rust {{#include ../../../../examples/common.rs}} ``` - diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md index 66f40497fb..537902a442 100644 --- a/book/en/src/by-example/app_idle.md +++ b/book/en/src/by-example/app_idle.md @@ -1,14 +1,18 @@ # The background task `#[idle]` A function marked with the `idle` attribute can optionally appear in the -module. This function is used as the special *idle task* and must have -signature `fn(idle::Context) -> !`. +module. This becomes the special *idle task* and must have signature +`fn(idle::Context) -> !`. When present, the runtime will execute the `idle` task after `init`. Unlike -`init`, `idle` will run *with interrupts enabled* and it's not allowed to return -so it must run forever. +`init`, `idle` will run *with interrupts enabled* and must never return, +as the `-> !` function signature indicates. +[The Rust type `!` means “never”][nevertype]. -Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access. +[nevertype]: https://doc.rust-lang.org/core/primitive.never.html + +Like in `init`, locally declared resources will have `'static` lifetimes that +are safe to access. The example below shows that `idle` runs after `init`. @@ -21,9 +25,9 @@ $ cargo run --target thumbv7m-none-eabi --example idle {{#include ../../../../ci/expected/idle.run}} ``` -By default the RTIC `idle` task does not try to optimise for any specific targets. +By default, the RTIC `idle` task does not try to optimize for any specific targets. -A common useful optimisation is to enable the [SLEEPONEXIT] and allow the MCU +A common useful optimization is to enable the [SLEEPONEXIT] and allow the MCU to enter sleep when reaching `idle`. >**Caution** some hardware unless configured disables the debug unit during sleep mode. diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 3112ccf9e1..615c299102 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,14 +1,22 @@ # App initialization and the `#[init]` task -An RTIC application is required an `init` task setting up the system. The corresponding function must have the signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource structures defined by the user. +An RTIC application requires an `init` task setting up the system. The corresponding `init` function must have the +signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource +structures defined by the user. -On system reset, the `init` task is executed (after the optionally defined `pre-init` and internal RTIC initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the `bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through the `core` and `device` fields of `init::Context`. +The `init` task executes after system reset (after the optionally defined `pre-init` and internal RTIC +initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the +`bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through +the `core` and `device` fields of `init::Context`. ## Example -The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` variable with `'static` lifetime. As we will see later, such variables can later be delegated from `init` to other tasks of the RTIC application. +The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` +variable with `'static` lifetime. +Such variables can be delegated from the `init` task to other tasks of the RTIC application. -The `device` field is only available when the `peripherals` argument is set to `true` (which is the default). In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. +The `device` field is available when the `peripherals` argument is set to the default value `true`. +In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. ``` rust {{#include ../../../../examples/init.rs}} @@ -16,13 +24,13 @@ The `device` field is only available when the `peripherals` argument is set to ` Running the example will print `init` to the console and then exit the QEMU process. -``` console +``` console $ cargo run --target thumbv7m-none-eabi --example init {{#include ../../../../ci/expected/init.run}} ``` > **NOTE**: You can choose target device by passing a target -> triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or +> triple to cargo (e.g. `cargo run --example init --target thumbv7m-none-eabi`) or > configure a default target in `.cargo/config.toml`. > -> For running the examples, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. +> For running the examples, we use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`. diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md index 934359d5d7..1a92ec846c 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -2,26 +2,41 @@ ## Priorities -The static priority of each handler can be declared in the `task` attribute -using the `priority` argument. For Cortex-M, tasks can have priorities in the range `1..=(1 << -NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device` -crate. When the `priority` argument is omitted, the priority is assumed to be -`1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority. +The `priority` argument declares the static priority of each `task`. + +For Cortex-M, tasks can have priorities in the range `1..=(1 << NVIC_PRIO_BITS)` +where `NVIC_PRIO_BITS` is a constant defined in the `device` crate. + +Omitting the `priority` argument the task priority defaults to `1`. +The `idle` task has a non-configurable static priority of `0`, the lowest priority. > A higher number means a higher priority in RTIC, which is the opposite from what > Cortex-M does in the NVIC peripheral. > Explicitly, this means that number `10` has a **higher** priority than number `9`. -When several tasks are ready to be executed the one with highest static -priority will be executed first. Task prioritization can be observed in the -following scenario: during the execution of a low -priority task a higher priority task is spawned; this puts the higher priority task in the pending state. -The difference in priority results in the higher priority task preempting the -lower priority one: the execution of the lower priority task is suspended and -the higher priority task is executed to completion. Once the higher priority -task has terminated the lower priority task is resumed. +The highest static priority task takes precedence when more than one +task are ready to execute. -The following example showcases the priority based scheduling of tasks. +The following scenario demonstrates task prioritization: +Spawning a higher priority task A during execution of a lower priority task B pends +task A. Task A has higher priority thus preempting task B which gets suspended +until task A completes execution. Thus, when task A completes task B resumes execution. + +```text +Task Priority + ┌────────────────────────────────────────────────────────┐ + │ │ + │ │ +3 │ Preempts │ +2 │ A─────────► │ +1 │ B─────────► - - - - B────────► │ +0 │Idle┌─────► Resumes ┌──────────► │ + ├────┴──────────────────────────────────┴────────────────┤ + │ │ + └────────────────────────────────────────────────────────┘Time +``` + +The following example showcases the priority based scheduling of tasks: ``` rust {{#include ../../../../examples/preempt.rs}} @@ -33,13 +48,24 @@ $ cargo run --target thumbv7m-none-eabi --example preempt ``` Note that the task `bar` does *not* preempt task `baz` because its priority -is the *same* as `baz`'s. However, once `baz` returns, the execution of -task `bar` is prioritized over `foo` due to its higher priority. `foo` -is resumed only after `bar` returns. +is the *same* as `baz`'s. The higher priority task `bar` runs before `foo` +when `baz`returns. When `bar` returns `foo` can resume. One more note about priorities: choosing a priority higher than what the device -supports will result in a compile error. Due to -limitations in the language, the error message is currently far from helpful: it -will say something along the lines of "evaluation of constant value failed" and -the span of the error will *not* point out to the problematic interrupt value -- -we are sorry about this! +supports will result in a compilation error. +The error is cryptic due to limitations in the language, +if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like: + +```text + error[E0080]: evaluation of constant value failed + --> examples/common.rs:10:1 + | +10 | #[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `8_usize - 9_usize`, which would overflow + | + = note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info) + +``` + +The error message incorrectly points to the starting point of the macro, but at least the +value subtracted (in this case 9) will suggest which task causes the error. diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md index a5c8b171a2..97160041e3 100644 --- a/book/en/src/by-example/app_task.md +++ b/book/en/src/by-example/app_task.md @@ -1,7 +1,18 @@ # Defining tasks with `#[task]` -Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. Every task can be spawned, now or later, be sent messages (message passing) and be given priorities for preemptive multitasking. +Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. -There are two kinds of tasks, software tasks and hardware tasks, and the difference is that hardware tasks 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 the UART's RX interrupt the task will run every time a character is received. +Tasks can + +* Be spawned (now or in the future) +* Receive messages (message passing) +* 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 an UART RX interrupt the task will run every +time this interrupt triggers, usually when a character is received. In the coming pages we will explore both tasks and the different options available. diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index d5968761dc..30b88d0df8 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,17 +1,21 @@ # Hardware tasks -At its core RTIC is based on using the interrupt controller in the hardware to do scheduling and -run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and -`#[idle]`). This also means that you can directly bind tasks to interrupt handlers. +At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) +to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]` +run as interrupt handlers. +This also means that you can manually bind tasks to interrupt handlers. -To declare interrupt handlers the `#[task]` attribute takes a `binds = InterruptName` argument whose -value is the name of the interrupt to which the handler will be bound to; the -function used with this attribute becomes the interrupt handler. Within the -framework these type of tasks are referred to as *hardware* tasks, because they -start executing in reaction to a hardware event. +To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`. +This task becomes the interrupt handler for this hardware interrupt vector. -Providing an interrupt name that does not exist will cause a compile error to help with accidental -errors. +All tasks bound to an explicit interrupt are *hardware tasks* since they +start execution in reaction to a hardware event. + +Specifying a non-existing interrupt name will cause a compilation error. The interrupt names +are commonly defined by [PAC or HAL][pacorhal] crates. + +[pacorhal]: https://docs.rust-embedded.org/book/start/registers.html +[NVIC]: https://developer.arm.com/documentation/100166/0001/Nested-Vectored-Interrupt-Controller/NVIC-functional-description/NVIC-interrupts The example below demonstrates the use of the `#[task]` attribute to declare an interrupt handler. @@ -24,4 +28,3 @@ interrupt handler. $ cargo run --target thumbv7m-none-eabi --example hardware {{#include ../../../../ci/expected/hardware.run}} ``` - diff --git a/book/en/src/by-example/message_passing.md b/book/en/src/by-example/message_passing.md index b80ae03cde..0dc8f85814 100644 --- a/book/en/src/by-example/message_passing.md +++ b/book/en/src/by-example/message_passing.md @@ -1,8 +1,14 @@ # Message passing & capacity -Software tasks have support for message passing, this means that they can be spawned with an argument -as `foo::spawn(1)` which will run the task `foo` with the argument `1`. The number of arguments is not -limited and is exemplified in the following: +Software tasks support message passing, this means that software tasks can be spawned +with an argument: `foo::spawn(1)` which will run the task `foo` with the argument `1`. + +Capacity sets the size of the spawn queue for the task, if not specified capacity defaults to 1. + +In the example below, the capacity of task `foo` is `3`, allowing three simultaneous +pending spawns of `foo`. Exceeding this capacity is an `Error`. + +The number of arguments to a task is not limited: ``` rust {{#include ../../../../examples/message_passing.rs}} diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md index c2a5d86cb9..0c8e15aed9 100644 --- a/book/en/src/by-example/monotonic.md +++ b/book/en/src/by-example/monotonic.md @@ -1,33 +1,38 @@ # 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 very useful. For this use-case the framework provides the static methods +based on time is useful. For this use-case the framework provides the static methods `task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`. -Mostly one uses `spawn_after`, but in cases where it's needed to have spawns happen without drift or -to a fixed baseline `spawn_at` is available. +`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. -To support this the `#[monotonic]` attribute exists which is applied to a type alias definition. +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 be -used in the same system, for example a slow timer that is used to wake the system from sleep and another -that is used for high granularity scheduling while the system is awake. +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. `binds = InterruptName` defines which interrupt vector is associated to -the timer's interrupt, `default = true` enables a shorthand API when spawning and accessing the -time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority the -interrupt vector has. +`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. -> By default `priority` is set to the **maximum priority** of the system but a lower priority -> can be selected if a high priority task cannot take the jitter introduced by the scheduling. -> This can however introduce jitter and delays into the scheduling, making it a trade-off. +> 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. -Finally, the monotonics must be initialized in `#[init]` and returned in the `init::Monotonic( ... )` tuple. -This moves the monotonics into the active state which makes it possible to use them. +The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple. +This activates the monotonics making it possible to use them. -An example is provided below: +See the following example: ``` rust {{#include ../../../../examples/schedule.rs}} @@ -40,8 +45,8 @@ $ cargo run --target thumbv7m-none-eabi --example message ## Canceling or rescheduling a scheduled task -Tasks spawned using `task::spawn_after` and `task::spawn_at` has as returns a `SpawnHandle`, -where the `SpawnHandle` can be used to cancel or reschedule a task that will run in the future. +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: diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 71092b2fd2..9f2c6c577f 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -1,22 +1,22 @@ # Resource usage -The RTIC framework manages shared and task local resources which allows data to be persistently -stored and safely accessed without the use of unsafe code. +The RTIC framework manages shared and task local resources allowing persistent data +storage and safe accesses without the use of `unsafe` code. RTIC resources are visible only to functions declared within the `#[app]` module and the framework gives the user complete control (on a per-task basis) over resource accessibility. -System wide resources are declared as **two** `struct`'s within the `#[app]` module annotated with -the attribute `#[local]` and `#[shared]` respectively. Each field in these structures corresponds -to a different resource (identified by field name). The difference between these two sets of -resources will be covered below. +Declaration of system-wide resources are by annotating **two** `struct`s within the `#[app]` module +with the attribute `#[local]` and `#[shared]`. +Each field in these structures corresponds to a different resource (identified by field name). +The difference between these two sets of resources will be covered below. Each task must declare the resources it intends to access in its corresponding metadata attribute -using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. The -listed resources are made available to the context under the `local` and `shared` fields of the +using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. +The listed resources are made available to the context under the `local` and `shared` fields of the `Context` structure. -The `init` task returns the initial values for the system wide (`#[shared]` and `#[local]`) +The `init` task returns the initial values for the system-wide (`#[shared]` and `#[local]`) resources, and the set of initialized timers used by the application. The monotonic timers will be further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md). @@ -27,6 +27,9 @@ access the resource and does so without locks or critical sections. This allows commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific task. +Thus, a task `#[local]` resource can only be accessed by one singular task. +Attempting to assign the same `#[local]` resource to more than one task is a compile-time error. + The example application shown below contains two tasks where each task has access to its own `#[local]` resource, plus that the `idle` task has its own `#[local]` as well. @@ -39,15 +42,12 @@ $ cargo run --target thumbv7m-none-eabi --example locals {{#include ../../../../ci/expected/locals.run}} ``` -A `#[local]` resource cannot be accessed from outside the task it was associated to in a `#[task]` attribute. -Assigning the same `#[local]` resource to more than one task is a compile-time error. - ### Task local initialized resources A special use-case of local resources are the ones specified directly in the resource claim, `#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be initialized in `#[init]`. -Moreover local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. +Moreover, local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. In the example below the different uses and lifetimes are shown: @@ -96,7 +96,7 @@ $ cargo run --target thumbv7m-none-eabi --example lock ## Multi-lock As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The -following examples shows this in use: +following examples show this in use: ``` rust {{#include ../../../../examples/multilock.rs}} @@ -109,12 +109,12 @@ $ cargo run --target thumbv7m-none-eabi --example multilock ## Only shared (`&-`) access -By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but -it is possible to specify that a task only requires shared access (`&-`) to a resource using the +By default, the framework assumes that all tasks require exclusive access (`&mut-`) to resources, +but it is possible to specify that a task only requires shared access (`&-`) to a resource using the `&resource_name` syntax in the `shared` list. The advantage of specifying shared access (`&-`) to a resource is that no locks are required to -access the resource even if the resource is contended by several tasks running at different +access the resource even if the resource is contended by more than one task running at different priorities. The downside is that the task only gets a shared reference (`&-`) to the resource, limiting the operations it can perform on it, but where a shared reference is enough this approach reduces the number of required locks. In addition to simple immutable data, this shared access can @@ -142,8 +142,11 @@ $ cargo run --target thumbv7m-none-eabi --example only-shared-access A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). Note that -this is merely a convenience: if you do use the `lock` API, at runtime the framework will -**not** produce a critical section. Also worth noting: using `#[lock_free]` on resources shared by +this is merely a convenience to reduce needless resource locking code, because even if the +`lock` API is used, at runtime the framework will **not** produce a critical section due to how +the underlying resource-ceiling preemption works. + +Also worth noting: using `#[lock_free]` on resources shared by tasks running at different priorities will result in a *compile-time* error -- not using the `lock` API would be a data race in that case. diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index f78efea9c3..370792f841 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,21 +1,31 @@ # Software tasks & spawn -Software tasks, as hardware tasks, are run as interrupt handlers where all software tasks at the -same priority shares a "free" interrupt handler to run from, called a dispatcher. These free -interrupts are interrupt vectors not used by hardware tasks. +Software tasks are tasks which are not directly assigned to a specific interrupt vector. -To declare tasks in the framework the `#[task]` attribute is used on a function. -By default these tasks are referred to as software tasks as they do not have a direct coupling to -an interrupt handler. Software tasks can be spawned (started) using the `task_name::spawn()` static -method which will directly run the task given that there are no higher priority tasks running. +They run as interrupt handlers where all software tasks at the +same priority level shares a "free" interrupt handler acting as a dispatcher. +Thus, what differentiates software and hardware tasks are the dispatcher versus +bound interrupt vector. -To indicate to the framework which interrupts are free for use to dispatch software tasks with the -`#[app]` attribute has a `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` argument. You need -to provide as many dispatchers as there are priority levels used by software tasks, as an -dispatcher is assigned per interrupt level. The framework will also give a compile error if there -are not enough dispatchers provided. +These free interrupts used as dispatchers are interrupt vectors not used by hardware tasks. -This is exemplified in the following: +The `#[task]` attribute used on a function declare it as a software tasks. +The static method `task_name::spawn()` spawn (start) a software task and +given that there are no higher priority tasks running the task will start executing directly. + +A list of “free” and usable interrupts allows the framework to dispatch software tasks. +This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an +argument to the `#[app]` attribute. + +Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that +the list of dispatchers need to cover all priority levels used by software tasks. + +Example: The `dispatchers =` argument needs to have at least 3 entries for an application using +three different priorities for software tasks. + +The framework will give a compilation error if there are not enough dispatchers provided. + +See the following example: ``` rust {{#include ../../../../examples/spawn.rs}} diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index b50ac4a9ef..c916479aa8 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -1,14 +1,16 @@ # Starting a new project -When starting an RTIC project from scratch it is recommended to follow RTIC's [`defmt-app-template`]. +A recommendation when starting a RTIC project from scratch is to follow RTIC's [`defmt-app-template`]. [`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow -protection using [`flip-link`]. There are also an multitude of examples available provided by the community: +protection using [`flip-link`]. There are also a multitude of examples available provided by the community: +- [`rtic-examples`] - Multiple projects - [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) - ... More to come [`defmt`]: https://github.com/knurling-rs/defmt/ [`flip-link`]: https://github.com/knurling-rs/flip-link/ +[`rtic-examples`]: https://github.com/rtic-rs/rtic-examples diff --git a/book/en/src/by-example/tips_destructureing.md b/book/en/src/by-example/tips_destructureing.md index 7b864c4666..4637b48343 100644 --- a/book/en/src/by-example/tips_destructureing.md +++ b/book/en/src/by-example/tips_destructureing.md @@ -1,7 +1,8 @@ # Resource de-structure-ing -When having a task taking multiple resources it can help in readability to split -up the resource struct. Here are two examples on how this can be done: +Destructuring task resources might help readability if a task takes multiple +resources. +Here are two examples on how to split up the resource struct: ``` rust {{#include ../../../../examples/destructure.rs}} diff --git a/book/en/src/by-example/tips_from_ram.md b/book/en/src/by-example/tips_from_ram.md index 6aef2f704e..ecb5dde195 100644 --- a/book/en/src/by-example/tips_from_ram.md +++ b/book/en/src/by-example/tips_from_ram.md @@ -6,7 +6,7 @@ RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the improve performance in some cases. > **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` -> attributes are very powerful but also easy to misuse. Incorrectly using any of +> 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. @@ -42,4 +42,3 @@ $ cargo nm --example ramfunc --release | grep ' foo::' $ cargo nm --example ramfunc --release | grep ' bar::' {{#include ../../../../ci/expected/ramfunc.grep.bar}} ``` - diff --git a/book/en/src/by-example/tips_indirection.md b/book/en/src/by-example/tips_indirection.md index 22c5774630..1a330c5162 100644 --- a/book/en/src/by-example/tips_indirection.md +++ b/book/en/src/by-example/tips_indirection.md @@ -3,7 +3,9 @@ Message passing always involves copying the payload from the sender into a static variable and then from the static variable into the receiver. Thus sending a large buffer, like a `[u8; 128]`, as a message involves two expensive -`memcpy`s. To minimize the message passing overhead one can use indirection: +`memcpy`s. + +Indirection can minimize message passing overhead: instead of sending the buffer by value, one can send an owning pointer into the buffer. @@ -23,4 +25,3 @@ Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. $ cargo run --target thumbv7m-none-eabi --example pool {{#include ../../../../ci/expected/pool.run}} ``` - diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 210a08e669..24df7712c5 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -1,18 +1,21 @@ # Implementing a `Monotonic` timer for scheduling -The framework is very flexible in that it can utilize any timer which has compare-match and (optional) -overflow interrupts for scheduling. The only thing needed to make a timer usable with RTIC is to -implement the [`rtic_monotonic::Monotonic`] trait. +The framework is flexible because it can use any timer which has compare-match and optionally +supporting overflow interrupts for scheduling. +The single requirement to make a timer usable with RTIC is implementing the +[`rtic_monotonic::Monotonic`] trait. -Implementing time that supports a vast range is generally **very** difficult, and in RTIC 0.5 it was a -common problem how to implement time handling and not get stuck in weird special cases. Moreover -it was difficult to understand the relation between time and the timers used for scheduling. For -RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], -as the basis for all time-based operations when implementing `Monotonic`. This is why in RTIC 0.6 -it is almost trivial to implement the `Monotonic` trait and use any timer in a system for scheduling. +Implementing time counting that supports large time spans is generally **difficult**, in RTIC 0.5 +implementing time handling was a common problem. +Moreover, the relation between time and timers used for scheduling was difficult to understand. -The trait documents the requirements for each method, however below you can find a list of -implementations in the wild that can be used as inspiration: +For RTIC 0.6 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], +as the basis for all time-based operations when implementing `Monotonic`. +This makes it almost trivial to implement the `Monotonic` trait allowing the use of any timer in +the system for scheduling. + +The trait documents the requirements for each method, +and for inspiration here is a list of `Monotonic` implementations: - [`STM32F411 series`], implemented for the 32-bit timers - [`Nordic nRF52 series`], implemented for the 32-bit timers @@ -28,4 +31,3 @@ If you know of more implementations feel free to add them to this list. [`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs [`Systick based`]: https://github.com/rtic-rs/systick-monotonic [`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic - diff --git a/book/en/src/by-example/tips_static_lifetimes.md b/book/en/src/by-example/tips_static_lifetimes.md index 3ea08166e4..8d3a832c4e 100644 --- a/book/en/src/by-example/tips_static_lifetimes.md +++ b/book/en/src/by-example/tips_static_lifetimes.md @@ -1,17 +1,17 @@ # 'static super-powers -As discussed earlier `local` resources are given `'static` lifetime in `#[init]` and `#[idle]`, -this can be used to allocate an object and then split it up or give the pre-allocated object to a -task, driver or some other object. -This is very useful when needing to allocate memory for drivers, such as USB drivers, and using -data structures that can be split such as [`heapless::spsc::Queue`]. +In `#[init]` and `#[idle]` `local` resources has `'static` lifetime. -In the following example an [`heapless::spsc::Queue`] is given to two different tasks for lock-free access -to the shared queue. +Useful when pre-allocating and/or splitting resources between tasks, drivers +or some other object. +This comes in handy when drivers, such as USB drivers, need to allocate memory and +when using splittable data structures such as [`heapless::spsc::Queue`]. + +In the following example two different tasks share a [`heapless::spsc::Queue`] +for lock-free access to the shared queue. [`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html - ``` rust {{#include ../../../../examples/static.rs}} ``` diff --git a/book/en/src/by-example/tips_view_code.md b/book/en/src/by-example/tips_view_code.md index 8f0d86b591..736b7ac895 100644 --- a/book/en/src/by-example/tips_view_code.md +++ b/book/en/src/by-example/tips_view_code.md @@ -7,7 +7,7 @@ options: You can inspect the file `rtic-expansion.rs` inside the `target` directory. This file contains the expansion of the `#[rtic::app]` item (not your whole program!) of the *last built* (via `cargo build` or `cargo check`) RTIC application. The -expanded code is not pretty printed by default so you'll want to run `rustfmt` +expanded code is not pretty printed by default, so you'll want to run `rustfmt` on it before you read it. ``` console @@ -15,7 +15,7 @@ $ cargo build --example foo $ rustfmt target/rtic-expansion.rs -$ tail target/rtic-expansion.rs +tail target/rtic-expansion.rs ``` ``` rust @@ -43,6 +43,6 @@ crate and print the output to the console. [`cargo-expand`]: https://crates.io/crates/cargo-expand ``` console -$ # produces the same output as before -$ cargo expand --example smallest | tail +# produces the same output as before +cargo expand --example smallest | tail ``` diff --git a/book/en/src/migration.md b/book/en/src/migration.md index 08feb81e1e..f52b0a51cd 100644 --- a/book/en/src/migration.md +++ b/book/en/src/migration.md @@ -1,4 +1,4 @@ # Migration Guides -This section describes how to migrate between different version of RTIC. +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_v4.md b/book/en/src/migration/migration_v4.md index ac59d8c9fb..d1a7ebeb98 100644 --- a/book/en/src/migration/migration_v4.md +++ b/book/en/src/migration/migration_v4.md @@ -1,19 +1,31 @@ # Migrating from v0.4.x to v0.5.0 -This section covers how to upgrade an application written against RTIC v0.4.x to +This section covers how to upgrade an application written against 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` -First, the version of the `cortex-m-rtic` dependency needs to be updated to -`"0.5.0"`. The `timer-queue` feature needs to be removed. +Change the version of `cortex-m-rtfm` to +`"0.5.0"`, change `rtfm` to `rtic`. +Remove the `timer-queue` feature. ``` toml -[dependencies.cortex-m-rtic] +[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 @@ -23,15 +35,15 @@ features = ["timer-queue"] ## `Context` argument -All functions inside the `#[rtic::app]` item need to take as first argument a +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 `#[rtic::app]` item +fields of the `Context` structure. Each function within the `#[rtfm::app]` item gets a different `Context` type. ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { // change this #[task(resources = [x], spawn = [a], schedule = [b])] @@ -75,11 +87,11 @@ const APP: () = { ## Resources -The syntax used to declare resources has been changed from `static mut` +The syntax used to declare resources has changed from `static mut` variables to a `struct Resources`. ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { // change this static mut X: u32 = 0; @@ -101,13 +113,13 @@ const APP: () = { If your application was accessing the device peripherals in `#[init]` through the `device` variable then you'll need to add `peripherals = true` to the -`#[rtic::app]` attribute to continue to access the device peripherals through +`#[rtfm::app]` attribute to continue to access the device peripherals through the `device` field of the `init::Context` structure. Change this: ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { #[init] fn init() { @@ -121,7 +133,7 @@ const APP: () = { Into this: ``` rust -#[rtic::app(/* .. */, peripherals = true)] +#[rtfm::app(/* .. */, peripherals = true)] // ^^^^^^^^^^^^^^^^^^ const APP: () = { #[init] @@ -137,13 +149,14 @@ const APP: () = { ## `#[interrupt]` and `#[exception]` -The `#[interrupt]` and `#[exception]` attributes have been removed. To declare -hardware tasks in v0.5.x use the `#[task]` attribute with the `binds` argument. +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 -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { // hardware tasks #[exception] @@ -163,7 +176,7 @@ const APP: () = { Into this: ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { #[task(binds = SVCall)] // ^^^^^^^^^^^^^^ @@ -183,25 +196,26 @@ const APP: () = { ## `schedule` -The `schedule` API no longer requires the `timer-queue` cargo feature, which has -been removed. To use the `schedule` API one must -first define the monotonic timer the runtime will use using the `monotonic` -argument of the `#[rtic::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 = rtic::cyccnt::CYCCNT` argument to the `#[rtic::app]` attribute. +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 have been moved -into the `rtic::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, this will need to be enabled by the application -inside `init`. +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 rtic::{Duration, Instant, U32Ext}; +use rtfm::{Duration, Instant, U32Ext}; -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { #[task(schedule = [b])] fn a() { @@ -213,10 +227,10 @@ const APP: () = { Into this: ``` rust -use rtic::cyccnt::{Duration, Instant, U32Ext}; +use rtfm::cyccnt::{Duration, Instant, U32Ext}; // ^^^^^^^^ -#[rtic::app(/* .. */, monotonic = rtic::cyccnt::CYCCNT)] +#[rtfm::app(/* .. */, monotonic = rtfm::cyccnt::CYCCNT)] // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const APP: () = { #[init] diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 24353d2a31..5c0dad193e 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -71,7 +71,7 @@ mod app { } ``` -## Move Dispatchers from `extern "C"` to app arguments. +## Move Dispatchers from `extern "C"` to app arguments Change @@ -171,7 +171,10 @@ 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: +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])] @@ -354,6 +357,7 @@ Note that the attributes `spawn` and `schedule` are no longer needed. ### 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. +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`. diff --git a/book/en/src/preface.md b/book/en/src/preface.md index e81542c997..d9dbc04bf8 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -8,7 +8,7 @@ # Preface This book contains user level documentation for the Real-Time Interrupt-driven Concurrency -(RTIC) framework. The API reference can be found [here](../../api/). +(RTIC) framework. The API reference is available [here](../../api/). Formerly known as Real-Time For the Masses.