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
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
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

View file

@ -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}}
```

View file

@ -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.

View file

@ -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`.

View file

@ -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.

View file

@ -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.

View file

@ -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}}
```

View file

@ -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}}

View file

@ -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:

View file

@ -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.

View file

@ -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}}

View file

@ -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

View file

@ -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}}

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.
> **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}}
```

View file

@ -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}}
```

View file

@ -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

View file

@ -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}}
```

View file

@ -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
```

View file

@ -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.

View file

@ -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]

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
@ -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`.

View file

@ -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.