rtic/book/en/src/by-example/resources.md

127 lines
5.5 KiB
Markdown
Raw Normal View History

2018-11-03 17:02:41 +01:00
## Resources
One of the limitations of the attributes provided by the `cortex-m-rt` crate is
that sharing data (or peripherals) between interrupts, or between an interrupt
and the `entry` function, requires a `cortex_m::interrupt::Mutex`, which
*always* requires disabling *all* interrupts to access the data. Disabling all
the interrupts is not always required for memory safety but the compiler doesn't
have enough information to optimize the access to the shared data.
The `app` attribute has a full view of the application thus it can optimize
access to `static` variables. In RTFM we refer to the `static` variables
declared inside the `app` pseudo-module as *resources*. To access a resource the
2019-04-21 20:25:59 +02:00
context (`init`, `idle`, `interrupt` or `exception`) one must first declare the
2018-11-03 17:02:41 +01:00
resource in the `resources` argument of its attribute.
In the example below two interrupt handlers access the same resource. No `Mutex`
is required in this case because the two handlers run at the same priority and
no preemption is possible. The `SHARED` resource can only be accessed by these
two handlers.
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/resource.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example resource
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/resource.run}}```
2018-11-03 17:02:41 +01:00
## Priorities
The priority of each handler can be declared in the `interrupt` and `exception`
attributes. It's not possible to set the priority in any other way because the
2019-04-21 20:25:59 +02:00
runtime takes ownership of the `NVIC` peripheral thus it's also not possible to
2018-11-03 17:02:41 +01:00
change the priority of a handler / task at runtime. Thanks to this restriction
the framework has knowledge about the *static* priorities of all interrupt and
exception handlers.
Interrupts and exceptions can have priorities in the range `1..=(1 <<
NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
crate. The `idle` task has a priority of `0`, the lowest priority.
Resources that are shared between handlers that run at different priorities
require critical sections for memory safety. The framework ensures that critical
sections are used but *only where required*: for example, no critical section is
required by the highest priority handler that has access to the resource.
The critical section API provided by the RTFM framework (see [`Mutex`]) is
based on dynamic priorities rather than on disabling interrupts. The consequence
is that these critical sections will prevent *some* handlers, including all the
ones that contend for the resource, from *starting* but will let higher priority
handlers, that don't contend for the resource, run.
[`Mutex`]: ../../api/rtfm/trait.Mutex.html
In the example below we have three interrupt handlers with priorities ranging
from one to three. The two handlers with the lower priorities contend for the
`SHARED` resource. The lowest priority handler needs to [`lock`] the
`SHARED` resource to access its data, whereas the mid priority handler can
directly access its data. The highest priority handler is free to preempt
the critical section created by the lowest priority handler.
[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/lock.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example lock
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/lock.run}}```
2018-11-03 17:02:41 +01:00
One more note about priorities: choosing a priority higher than what the device
supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to
2019-04-21 20:25:59 +02:00
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!
2018-11-03 17:02:41 +01:00
## Late resources
Unlike normal `static` variables, which need to be assigned an initial value
when declared, resources can be initialized at runtime. We refer to these
runtime initialized resources as *late resources*. Late resources are useful for
*moving* (as in transferring ownership) peripherals initialized in `init` into
interrupt and exception handlers.
Late resources are declared like normal resources but that are given an initial
value of `()` (the unit value). `init` must return the initial values of all
late resources packed in a `struct` of type `init::LateResources`.
2018-11-03 17:02:41 +01:00
The example below uses late resources to stablish a lockless, one-way channel
between the `UART0` interrupt handler and the `idle` function. A single producer
single consumer [`Queue`] is used as the channel. The queue is split into
consumer and producer end points in `init` and then each end point is stored
in a different resource; `UART0` owns the producer resource and `idle` owns
the consumer resource.
[`Queue`]: ../../api/heapless/spsc/struct.Queue.html
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/late.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example late
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/late.run}}```
2018-11-03 17:02:41 +01:00
## `static` resources
`static` variables can also be used as resources. Tasks can only get `&`
(shared) references to these resources but locks are never required to access
their data. You can think of `static` resources as plain `static` variables that
can be initialized at runtime and have better scoping rules: you can control
which tasks can access the variable, instead of the variable being visible to
all the functions in the scope it was declared in.
In the example below a key is loaded (or created) at runtime and then used from
two tasks that run at different priorities.
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/static.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example static
2019-02-11 21:40:53 +01:00
{{#include ../../../../ci/expected/static.run}}```