563: Docs touchup r=korken89 a=AfoHT

Unleashed some language linters on the book

Co-authored-by: Henrik Tjäder <henrik@grepit.se>
Co-authored-by: perlindgren <per.lindgren@ltu.se>
This commit is contained in:
bors[bot] 2021-12-21 19:02:49 +00:00 committed by GitHub
commit c78177c37e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 291 additions and 185 deletions

View file

@ -1 +1,8 @@
# Awesome RTIC examples # 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

View file

@ -3,15 +3,15 @@
This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTIC) framework 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. 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 All examples in this part of the book are accessible at the
the project. The examples can be run on QEMU (emulating a Cortex M3 target) so no special hardware [GitHub repository][repoexamples].
is required to follow along. 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` To run the examples with QEMU you will need the `qemu-system-arm` program.
program. Check [the embedded Rust book] for instructions on how to set up an Check [the embedded Rust book] for instructions on how to set up an
embedded development environment that includes QEMU. embedded development environment that includes QEMU.
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html [the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html

View file

@ -3,14 +3,14 @@
## Requirements on the `app` attribute ## Requirements on the `app` attribute
All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute
must be applied to a `mod`-item containing the RTIC application. The `app` only applies to a `mod`-item containing the RTIC application. The `app`
attribute has a mandatory `device` attribute has a mandatory `device` argument that takes a *path* as a value.
argument that takes a *path* as a value. This must be a full path pointing to a This must be a full path pointing to a
*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or
newer. newer.
The `app` attribute will expand into a suitable entry point so it's not required The `app` attribute will expand into a suitable entry point and thus replaces
to use the [`cortex_m_rt::entry`] attribute. the use of the [`cortex_m_rt::entry`] attribute.
[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html [`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html
[`svd2rust`]: https://crates.io/crates/svd2rust [`svd2rust`]: https://crates.io/crates/svd2rust
@ -18,9 +18,9 @@ to use the [`cortex_m_rt::entry`] attribute.
## An RTIC application example ## 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 ``` rust
{{#include ../../../../examples/common.rs}} {{#include ../../../../examples/common.rs}}
``` ```

View file

@ -1,14 +1,18 @@
# The background task `#[idle]` # The background task `#[idle]`
A function marked with the `idle` attribute can optionally appear in the 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 module. This becomes the special *idle task* and must have signature
signature `fn(idle::Context) -> !`. `fn(idle::Context) -> !`.
When present, the runtime will execute the `idle` task after `init`. Unlike 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 `init`, `idle` will run *with interrupts enabled* and must never return,
so it must run forever. 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`. 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}} {{#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`. to enter sleep when reaching `idle`.
>**Caution** some hardware unless configured disables the debug unit during sleep mode. >**Caution** some hardware unless configured disables the debug unit during sleep mode.

View file

@ -1,14 +1,22 @@
# App initialization and the `#[init]` task # 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 ## 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 ``` rust
{{#include ../../../../examples/init.rs}} {{#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. 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 $ cargo run --target thumbv7m-none-eabi --example init
{{#include ../../../../ci/expected/init.run}} {{#include ../../../../ci/expected/init.run}}
``` ```
> **NOTE**: You can choose target device by passing a target > **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`. > 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`.

View file

@ -2,26 +2,41 @@
## Priorities ## Priorities
The static priority of each handler can be declared in the `task` attribute The `priority` argument declares the static priority of each `task`.
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` For Cortex-M, tasks can have priorities in the range `1..=(1 << NVIC_PRIO_BITS)`
crate. When the `priority` argument is omitted, the priority is assumed to be where `NVIC_PRIO_BITS` is a constant defined in the `device` crate.
`1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority.
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 > A higher number means a higher priority in RTIC, which is the opposite from what
> Cortex-M does in the NVIC peripheral. > Cortex-M does in the NVIC peripheral.
> Explicitly, this means that number `10` has a **higher** priority than number `9`. > 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 The highest static priority task takes precedence when more than one
priority will be executed first. Task prioritization can be observed in the task are ready to execute.
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 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 ``` rust
{{#include ../../../../examples/preempt.rs}} {{#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 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 is the *same* as `baz`'s. The higher priority task `bar` runs before `foo`
task `bar` is prioritized over `foo` due to its higher priority. `foo` when `baz`returns. When `bar` returns `foo` can resume.
is resumed only after `bar` returns.
One more note about priorities: choosing a priority higher than what the device One more note about priorities: choosing a priority higher than what the device
supports will result in a compile error. Due to supports will result in a compilation error.
limitations in the language, the error message is currently far from helpful: it The error is cryptic due to limitations in the language,
will say something along the lines of "evaluation of constant value failed" and if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like:
the span of the error will *not* point out to the problematic interrupt value --
we are sorry about this! ```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.

View file

@ -1,7 +1,18 @@
# Defining tasks with `#[task]` # 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. In the coming pages we will explore both tasks and the different options available.

View file

@ -1,17 +1,21 @@
# Hardware tasks # Hardware tasks
At its core RTIC is based on using the interrupt controller in the hardware to do scheduling and At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC])
run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]`
`#[idle]`). This also means that you can directly bind tasks to interrupt handlers. 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 To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`.
value is the name of the interrupt to which the handler will be bound to; the This task becomes the interrupt handler for this hardware interrupt vector.
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.
Providing an interrupt name that does not exist will cause a compile error to help with accidental All tasks bound to an explicit interrupt are *hardware tasks* since they
errors. 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 The example below demonstrates the use of the `#[task]` attribute to declare an
interrupt handler. interrupt handler.
@ -24,4 +28,3 @@ interrupt handler.
$ cargo run --target thumbv7m-none-eabi --example hardware $ cargo run --target thumbv7m-none-eabi --example hardware
{{#include ../../../../ci/expected/hardware.run}} {{#include ../../../../ci/expected/hardware.run}}
``` ```

View file

@ -1,8 +1,14 @@
# Message passing & capacity # Message passing & capacity
Software tasks have support for message passing, this means that they can be spawned with an argument Software tasks support message passing, this means that software tasks can be spawned
as `foo::spawn(1)` which will run the task `foo` with the argument `1`. The number of arguments is not with an argument: `foo::spawn(1)` which will run the task `foo` with the argument `1`.
limited and is exemplified in the following:
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 ``` rust
{{#include ../../../../examples/message_passing.rs}} {{#include ../../../../examples/message_passing.rs}}

View file

@ -1,33 +1,38 @@
# Monotonic & spawn_{at/after} # Monotonic & spawn_{at/after}
The understanding of time is an important concept in embedded systems, and to be able to run tasks 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 */)`. `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 `spawn_after` is more commonly used, but in cases where it's needed to have spawns happen
to a fixed baseline `spawn_at` is available. 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 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 This is generally some timer which handles the timing of the system.
used in the same system, for example a slow timer that is used to wake the system from sleep and another One or more monotonics can coexist in the same system, for example a slow timer that wakes the
that is used for high granularity scheduling while the system is awake. 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 [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic
The attribute has one required parameter and two optional parameters, `binds`, `default` and The attribute has one required parameter and two optional parameters, `binds`, `default` and
`priority` respectively. `binds = InterruptName` defines which interrupt vector is associated to `priority` respectively.
the timer's interrupt, `default = true` enables a shorthand API when spawning and accessing the The required parameter, `binds = InterruptName`, associates an interrupt vector to the timer's
time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority the interrupt, while `default = true` enables a shorthand API when spawning and accessing
interrupt vector has. 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 > The default `priority` is the **maximum priority** of the system.
> can be selected if a high priority task cannot take the jitter introduced by the scheduling. > If your system has a high priority task with tight scheduling requirements,
> This can however introduce jitter and delays into the scheduling, making it a trade-off. > 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. The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple.
This moves the monotonics into the active state which makes it possible to use them. This activates the monotonics making it possible to use them.
An example is provided below: See the following example:
``` rust ``` rust
{{#include ../../../../examples/schedule.rs}} {{#include ../../../../examples/schedule.rs}}
@ -40,8 +45,8 @@ $ cargo run --target thumbv7m-none-eabi --example message
## Canceling or rescheduling a scheduled task ## Canceling or rescheduling a scheduled task
Tasks spawned using `task::spawn_after` and `task::spawn_at` has as returns a `SpawnHandle`, Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`,
where the `SpawnHandle` can be used to cancel or reschedule a task that will run in the future. 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 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: too late and that the task is already sent for execution. The following example shows this in action:

View file

@ -1,22 +1,22 @@
# Resource usage # Resource usage
The RTIC framework manages shared and task local resources which allows data to be persistently The RTIC framework manages shared and task local resources allowing persistent data
stored and safely accessed without the use of unsafe code. 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 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. 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 Declaration of system-wide resources are by annotating **two** `struct`s within the `#[app]` module
the attribute `#[local]` and `#[shared]` respectively. Each field in these structures corresponds with the attribute `#[local]` and `#[shared]`.
to a different resource (identified by field name). The difference between these two sets of Each field in these structures corresponds to a different resource (identified by field name).
resources will be covered below. 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 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 using the `local` and `shared` arguments. Each argument takes a list of resource identifiers.
listed resources are made available to the context under the `local` and `shared` fields of the The listed resources are made available to the context under the `local` and `shared` fields of the
`Context` structure. `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 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). 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 commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific
task. 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 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. `#[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}} {{#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 ### Task local initialized resources
A special use-case of local resources are the ones specified directly in the resource claim, 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 `#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be
initialized in `#[init]`. 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: 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 ## Multi-lock
As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The 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 ``` rust
{{#include ../../../../examples/multilock.rs}} {{#include ../../../../examples/multilock.rs}}
@ -109,12 +109,12 @@ $ cargo run --target thumbv7m-none-eabi --example multilock
## Only shared (`&-`) access ## Only shared (`&-`) access
By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but By default, the framework assumes that all tasks require exclusive access (`&mut-`) to resources,
it is possible to specify that a task only requires shared access (`&-`) to a resource using the 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. `&resource_name` syntax in the `shared` list.
The advantage of specifying shared access (`&-`) to a resource is that no locks are required to 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, 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 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 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 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 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 `#[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 this is merely a convenience to reduce needless resource locking code, because even if the
**not** produce a critical section. Also worth noting: using `#[lock_free]` on resources shared by `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` 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. API would be a data race in that case.

View file

@ -1,21 +1,31 @@
# Software tasks & spawn # Software tasks & spawn
Software tasks, as hardware tasks, are run as interrupt handlers where all software tasks at the Software tasks are tasks which are not directly assigned to a specific interrupt vector.
same priority shares a "free" interrupt handler to run from, called a dispatcher. These free
interrupts are interrupt vectors not used by hardware tasks.
To declare tasks in the framework the `#[task]` attribute is used on a function. They run as interrupt handlers where all software tasks at the
By default these tasks are referred to as software tasks as they do not have a direct coupling to same priority level shares a "free" interrupt handler acting as a dispatcher.
an interrupt handler. Software tasks can be spawned (started) using the `task_name::spawn()` static Thus, what differentiates software and hardware tasks are the dispatcher versus
method which will directly run the task given that there are no higher priority tasks running. bound interrupt vector.
To indicate to the framework which interrupts are free for use to dispatch software tasks with the These free interrupts used as dispatchers are interrupt vectors not used by hardware tasks.
`#[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.
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 ``` rust
{{#include ../../../../examples/spawn.rs}} {{#include ../../../../examples/spawn.rs}}

View file

@ -1,14 +1,16 @@
# Starting a new project # 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 [`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 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) - [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic)
- ... More to come - ... More to come
[`defmt`]: https://github.com/knurling-rs/defmt/ [`defmt`]: https://github.com/knurling-rs/defmt/
[`flip-link`]: https://github.com/knurling-rs/flip-link/ [`flip-link`]: https://github.com/knurling-rs/flip-link/
[`rtic-examples`]: https://github.com/rtic-rs/rtic-examples

View file

@ -1,7 +1,8 @@
# Resource de-structure-ing # Resource de-structure-ing
When having a task taking multiple resources it can help in readability to split Destructuring task resources might help readability if a task takes multiple
up the resource struct. Here are two examples on how this can be done: resources.
Here are two examples on how to split up the resource struct:
``` rust ``` rust
{{#include ../../../../examples/destructure.rs}} {{#include ../../../../examples/destructure.rs}}

View file

@ -6,7 +6,7 @@ RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the
improve performance in some cases. improve performance in some cases.
> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` > **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 > 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 > safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and
> `exception` attributes. > `exception` attributes.
@ -42,4 +42,3 @@ $ cargo nm --example ramfunc --release | grep ' foo::'
$ cargo nm --example ramfunc --release | grep ' bar::' $ cargo nm --example ramfunc --release | grep ' bar::'
{{#include ../../../../ci/expected/ramfunc.grep.bar}} {{#include ../../../../ci/expected/ramfunc.grep.bar}}
``` ```

View file

@ -3,7 +3,9 @@
Message passing always involves copying the payload from the sender into a Message passing always involves copying the payload from the sender into a
static variable and then from the static variable into the receiver. Thus 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 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 instead of sending the buffer by value, one can send an owning pointer into the
buffer. 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 $ cargo run --target thumbv7m-none-eabi --example pool
{{#include ../../../../ci/expected/pool.run}} {{#include ../../../../ci/expected/pool.run}}
``` ```

View file

@ -1,18 +1,21 @@
# Implementing a `Monotonic` timer for scheduling # Implementing a `Monotonic` timer for scheduling
The framework is very flexible in that it can utilize any timer which has compare-match and (optional) The framework is flexible because it can use any timer which has compare-match and optionally
overflow interrupts for scheduling. The only thing needed to make a timer usable with RTIC is to supporting overflow interrupts for scheduling.
implement the [`rtic_monotonic::Monotonic`] trait. 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 Implementing time counting that supports large time spans is generally **difficult**, in RTIC 0.5
common problem how to implement time handling and not get stuck in weird special cases. Moreover implementing time handling was a common problem.
it was difficult to understand the relation between time and the timers used for scheduling. For Moreover, the relation between time and timers used for scheduling was difficult to understand.
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.
The trait documents the requirements for each method, however below you can find a list of For RTIC 0.6 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`],
implementations in the wild that can be used as inspiration: 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 - [`STM32F411 series`], implemented for the 32-bit timers
- [`Nordic nRF52 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 [`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs
[`Systick based`]: https://github.com/rtic-rs/systick-monotonic [`Systick based`]: https://github.com/rtic-rs/systick-monotonic
[`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic [`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic

View file

@ -1,17 +1,17 @@
# 'static super-powers # 'static super-powers
As discussed earlier `local` resources are given `'static` lifetime in `#[init]` and `#[idle]`, In `#[init]` and `#[idle]` `local` resources has `'static` lifetime.
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 the following example an [`heapless::spsc::Queue`] is given to two different tasks for lock-free access Useful when pre-allocating and/or splitting resources between tasks, drivers
to the shared queue. 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 [`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html
``` rust ``` rust
{{#include ../../../../examples/static.rs}} {{#include ../../../../examples/static.rs}}
``` ```

View file

@ -7,7 +7,7 @@ options:
You can inspect the file `rtic-expansion.rs` inside the `target` directory. This 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!) 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 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. on it before you read it.
``` console ``` console
@ -15,7 +15,7 @@ $ cargo build --example foo
$ rustfmt target/rtic-expansion.rs $ rustfmt target/rtic-expansion.rs
$ tail target/rtic-expansion.rs tail target/rtic-expansion.rs
``` ```
``` rust ``` rust
@ -43,6 +43,6 @@ crate and print the output to the console.
[`cargo-expand`]: https://crates.io/crates/cargo-expand [`cargo-expand`]: https://crates.io/crates/cargo-expand
``` console ``` console
$ # produces the same output as before # produces the same output as before
$ cargo expand --example smallest | tail cargo expand --example smallest | tail
``` ```

View file

@ -1,4 +1,4 @@
# Migration Guides # 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. It also acts as a comparing reference between versions.

View file

@ -1,19 +1,31 @@
# Migrating from v0.4.x to v0.5.0 # 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. 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` ## `Cargo.toml`
First, the version of the `cortex-m-rtic` dependency needs to be updated to Change the version of `cortex-m-rtfm` to
`"0.5.0"`. The `timer-queue` feature needs to be removed. `"0.5.0"`, change `rtfm` to `rtic`.
Remove the `timer-queue` feature.
``` toml ``` toml
[dependencies.cortex-m-rtic] [dependencies.cortex-m-rtfm]
# change this # change this
version = "0.4.3" version = "0.4.3"
# into this # into this
[dependencies.cortex-m-rtic]
version = "0.5.0" version = "0.5.0"
# and remove this Cargo feature # and remove this Cargo feature
@ -23,15 +35,15 @@ features = ["timer-queue"]
## `Context` argument ## `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 `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 magically injected into the scope of the function by version v0.4.x of the
framework: `resources`, `spawn`, `schedule` -- these variables will become 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. gets a different `Context` type.
``` rust ``` rust
#[rtic::app(/* .. */)] #[rtfm::app(/* .. */)]
const APP: () = { const APP: () = {
// change this // change this
#[task(resources = [x], spawn = [a], schedule = [b])] #[task(resources = [x], spawn = [a], schedule = [b])]
@ -75,11 +87,11 @@ const APP: () = {
## Resources ## 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`. variables to a `struct Resources`.
``` rust ``` rust
#[rtic::app(/* .. */)] #[rtfm::app(/* .. */)]
const APP: () = { const APP: () = {
// change this // change this
static mut X: u32 = 0; static mut X: u32 = 0;
@ -101,13 +113,13 @@ const APP: () = {
If your application was accessing the device peripherals in `#[init]` through If your application was accessing the device peripherals in `#[init]` through
the `device` variable then you'll need to add `peripherals = true` to the 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. the `device` field of the `init::Context` structure.
Change this: Change this:
``` rust ``` rust
#[rtic::app(/* .. */)] #[rtfm::app(/* .. */)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init() {
@ -121,7 +133,7 @@ const APP: () = {
Into this: Into this:
``` rust ``` rust
#[rtic::app(/* .. */, peripherals = true)] #[rtfm::app(/* .. */, peripherals = true)]
// ^^^^^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^^^^^
const APP: () = { const APP: () = {
#[init] #[init]
@ -137,13 +149,14 @@ const APP: () = {
## `#[interrupt]` and `#[exception]` ## `#[interrupt]` and `#[exception]`
The `#[interrupt]` and `#[exception]` attributes have been removed. To declare Remove the attributes `#[interrupt]` and `#[exception]`.
hardware tasks in v0.5.x use the `#[task]` attribute with the `binds` argument. To declare hardware tasks in v0.5.x use the `#[task]`
attribute with the `binds` argument instead.
Change this: Change this:
``` rust ``` rust
#[rtic::app(/* .. */)] #[rtfm::app(/* .. */)]
const APP: () = { const APP: () = {
// hardware tasks // hardware tasks
#[exception] #[exception]
@ -163,7 +176,7 @@ const APP: () = {
Into this: Into this:
``` rust ``` rust
#[rtic::app(/* .. */)] #[rtfm::app(/* .. */)]
const APP: () = { const APP: () = {
#[task(binds = SVCall)] #[task(binds = SVCall)]
// ^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^
@ -183,25 +196,26 @@ const APP: () = {
## `schedule` ## `schedule`
The `schedule` API no longer requires the `timer-queue` cargo feature, which has The `schedule` API no longer requires the `timer-queue` cargo feature.
been removed. To use the `schedule` API one must To use the `schedule` API one must first define the monotonic timer the
first define the monotonic timer the runtime will use using the `monotonic` runtime will use using the `monotonic` argument of the `#[rtfm::app]` attribute.
argument of the `#[rtic::app]` attribute. To continue using the cycle counter To continue using the cycle counter (CYCCNT) as the monotonic timer,
(CYCCNT) as the monotonic timer, and match the behavior of version v0.4.x, add and match the behavior of version v0.4.x, add the `monotonic = rtfm::cyccnt::CYCCNT`
the `monotonic = rtic::cyccnt::CYCCNT` argument to the `#[rtic::app]` attribute. argument to the `#[rtfm::app]` attribute.
Also, the `Duration` and `Instant` types and the `U32Ext` trait have been moved Also, the `Duration` and `Instant` types and the `U32Ext` trait moved
into the `rtic::cyccnt` module. This module is only available on ARMv7-M+ into the `rtfm::cyccnt` module.
devices. The removal of the `timer-queue` also brings back the `DWT` peripheral This module is only available on ARMv7-M+ devices.
inside the core peripherals struct, this will need to be enabled by the application The removal of the `timer-queue` also brings back the `DWT` peripheral
inside `init`. inside the core peripherals struct, if `DWT` is required,
ensure it is enabled by the application inside `init`.
Change this: Change this:
``` rust ``` rust
use rtic::{Duration, Instant, U32Ext}; use rtfm::{Duration, Instant, U32Ext};
#[rtic::app(/* .. */)] #[rtfm::app(/* .. */)]
const APP: () = { const APP: () = {
#[task(schedule = [b])] #[task(schedule = [b])]
fn a() { fn a() {
@ -213,10 +227,10 @@ const APP: () = {
Into this: Into this:
``` rust ``` 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: () = { const APP: () = {
#[init] #[init]

View file

@ -71,7 +71,7 @@ mod app {
} }
``` ```
## Move Dispatchers from `extern "C"` to app arguments. ## Move Dispatchers from `extern "C"` to app arguments
Change Change
@ -171,7 +171,10 @@ fn b(_: b::Context) {}
## Symmetric locks ## 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 ``` rust
#[task(priority = 2, resources = [r])] #[task(priority = 2, resources = [r])]
@ -354,6 +357,7 @@ Note that the attributes `spawn` and `schedule` are no longer needed.
### Extern tasks ### 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`. See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`.

View file

@ -8,7 +8,7 @@
# Preface # Preface
This book contains user level documentation for the Real-Time Interrupt-driven Concurrency 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. Formerly known as Real-Time For the Masses.