Mostly editorial review.

This commit is contained in:
John van der Koijk 2022-02-20 19:21:25 +01:00 committed by Henrik Tjäder
parent 3240fb332a
commit 04189cc684
13 changed files with 81 additions and 53 deletions

View file

@ -15,6 +15,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
### Fixed ### Fixed
- Attempt to handle docs generation enabling `deny(missing_docs)` - Attempt to handle docs generation enabling `deny(missing_docs)`
- Book: Editorial review
- Use native GHA rustup and cargo - Use native GHA rustup and cargo
- Distinguish between thumbv8m.base and thumbv8m.main for basepri usage. - Distinguish between thumbv8m.base and thumbv8m.main for basepri usage.

View file

@ -1,13 +1,18 @@
# App initialization and the `#[init]` task # App initialization and the `#[init]` task
An RTIC application requires an `init` task setting up the system. The corresponding `init` function must have the 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 signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are resource
structures defined by the user. structures defined by the user.
The `init` task executes after system reset (after the optionally defined `pre-init` and internal RTIC The `init` task executes after system reset, [after an optionally defined `pre-init` code section][pre-init] and an always occurring internal RTIC
initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the initialization.
`bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through
the `core` and `device` fields of `init::Context`. [pre-init]: https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.pre_init.html
The `init` and optional `pre-init` tasks runs *with interrupts disabled* and have exclusive access to Cortex-M (the
`bare_metal::CriticalSection` token is available as `cs`).
Device specific peripherals are available through the `core` and `device` fields of `init::Context`.
## Example ## Example
@ -15,7 +20,7 @@ The example below shows the types of the `core`, `device` and `cs` fields, and s
variable with `'static` lifetime. variable with `'static` lifetime.
Such variables can be delegated from the `init` task to other tasks of the RTIC application. Such variables can be delegated from the `init` task to other tasks of the RTIC application.
The `device` field is available when the `peripherals` argument is set to the default value `true`. The `device` field is only 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`. In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`.
``` rust ``` rust

View file

@ -18,8 +18,8 @@ The highest static priority task takes precedence when more than one
task are ready to execute. task are ready to execute.
The following scenario demonstrates task prioritization: The following scenario demonstrates task prioritization:
Spawning a higher priority task A during execution of a lower priority task B pends Spawning a higher priority task A during execution of a lower priority task B suspends
task A. Task A has higher priority thus preempting task B which gets suspended task B. 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. until task A completes execution. Thus, when task A completes task B resumes execution.
```text ```text
@ -53,7 +53,8 @@ when `baz`returns. When `bar` returns `foo` can resume.
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 compilation error. supports will result in a compilation error.
The error is cryptic due to limitations in the language,
The error is cryptic due to limitations in the Rust language
if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like: if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like:
```text ```text

View file

@ -4,15 +4,18 @@ Tasks, defined with `#[task]`, are the main mechanism of getting work done in RT
Tasks can Tasks can
* Be spawned (now or in the future) * Be spawned (now or in the future, also by themselves)
* Receive messages (message passing) * Receive messages (passing messages between tasks)
* Prioritized allowing preemptive multitasking * Be prioritized, allowing preemptive multitasking
* Optionally bind to a hardware interrupt * Optionally bind to a hardware interrupt
RTIC makes a distinction between “software tasks” and “hardware tasks”. 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 *Hardware tasks* are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not.
time this interrupt triggers, usually when a character is received.
This means that if a hardware task is bound to, lets say, a UART RX interrupt, the task will be run every
time that interrupt triggers, usually when a character is received.
*Software tasks* are explicitly spawned in a task, either immediately or using the Monotonic timer mechanism.
In the coming pages we will explore both tasks and the different options available. In the coming pages we will explore both tasks and the different options available.

View file

@ -1,24 +1,26 @@
# Hardware tasks # Hardware tasks
At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) At its core RTIC is using a hardware interrupt controller ([ARM NVIC on cortex-m][NVIC])
to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]` to schedule and start execution of tasks. All tasks except `pre-init`, `#[init]` and `#[idle]`
run as interrupt handlers. run as interrupt handlers.
This also means that you can manually bind tasks to interrupt handlers.
To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`. Hardware tasks are explicitly bound to interrupt handlers.
This task becomes the interrupt handler for this hardware interrupt vector.
All tasks bound to an explicit interrupt are *hardware tasks* since they To bind a task to an interrupt, use the `#[task]` attribute argument `binds = InterruptName`.
This task then becomes the interrupt handler for this hardware interrupt vector.
All tasks bound to an explicit interrupt are called *hardware tasks* since they
start execution in reaction to a hardware event. start execution in reaction to a hardware event.
Specifying a non-existing interrupt name will cause a compilation error. The interrupt names Specifying a non-existing interrupt name will cause a compilation error. The interrupt names
are commonly defined by [PAC or HAL][pacorhal] crates. are commonly defined by [PAC or HAL][pacorhal] crates.
Any available interrupt vector should work, but different hardware might have Any available interrupt vector should work. Specific devices may bind
added special properties to select interrupt priority levels, such as the specific interrupt priorities to specific interrupt vectors outside
user code control. See for example the
[nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434). [nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434).
Beware of re-purposing interrupt vectors used internally by hardware features, Beware of using interrupt vectors that are used internally by hardware features;
RTIC is unaware of such hardware specific details. RTIC is unaware of such hardware specific details.
[pacorhal]: https://docs.rust-embedded.org/book/start/registers.html [pacorhal]: https://docs.rust-embedded.org/book/start/registers.html

View file

@ -1,7 +1,7 @@
# 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 useful. For this use-case the framework provides the static methods based on time is essential. 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 */)`.
`spawn_after` is more commonly used, but in cases where it's needed to have spawns happen `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. without drift or to a fixed baseline `spawn_at` is available.
@ -43,10 +43,14 @@ $ cargo run --target thumbv7m-none-eabi --example schedule
{{#include ../../../../ci/expected/schedule.run}} {{#include ../../../../ci/expected/schedule.run}}
``` ```
A key requirement of a Monotonic is that it must deal gracefully with
hardware timer overruns.
## Canceling or rescheduling a scheduled task ## Canceling or rescheduling a scheduled task
Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`, 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. 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

@ -30,13 +30,13 @@ task.
Thus, a task `#[local]` resource can only be accessed by one singular 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. Attempting to assign the same `#[local]` resource to more than one task is a compile-time error.
Types of `#[local]` resources must implement [`Send`] trait as they are being sent from `init` Types of `#[local]` resources must implement a [`Send`] trait as they are being sent from `init`
to target task and thus crossing the thread boundary. to a target task, crossing a thread boundary.
[`Send`]: https://doc.rust-lang.org/stable/core/marker/trait.Send.html [`Send`]: https://doc.rust-lang.org/stable/core/marker/trait.Send.html
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; the `idle` task has its own `#[local]` as well.
``` rust ``` rust
{{#include ../../../../examples/locals.rs}} {{#include ../../../../examples/locals.rs}}
@ -49,12 +49,14 @@ $ cargo run --target thumbv7m-none-eabi --example locals
{{#include ../../../../ci/expected/locals.run}} {{#include ../../../../ci/expected/locals.run}}
``` ```
Local resources in `#[init]` and `#[idle]` have `'static`
lifetimes. This is safe since both tasks are not re-entrant.
### Task local initialized resources ### Task local initialized resources
A special use-case of local resources are the ones specified directly in the resource claim, Local resources can also be specified directly in the resource claim like so:
`#[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.
Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they
are not crossing any thread boundary. are not crossing any thread boundary.
@ -92,9 +94,9 @@ preempting the critical section. This synchronization protocol is known as the
[srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy [srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy
In the example below we have three interrupt handlers with priorities ranging from one to three. 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 and need to lock the The two handlers with the lower priorities contend for a `shared` resource and need to succeed in locking the
resource for accessing the data. The highest priority handler, which do not access the `shared` resource in order to access its data. The highest priority handler, which does not access the `shared`
resource, is free to preempt the critical section created by the lowest priority handler. resource, is free to preempt a critical section created by the lowest priority handler.
``` rust ``` rust
{{#include ../../../../examples/lock.rs}} {{#include ../../../../examples/lock.rs}}

View file

@ -2,29 +2,33 @@
The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md) The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md)
with the core difference that a software task is not explicitly bound to a specific with the core difference that a software task is not explicitly bound to a specific
interrupt vector, but rather a “dispatcher” interrupt vector running interrupt vector, but rather bound to a “dispatcher” interrupt vector running
at the same priority as the software task. at the intended priority of the software task (see below).
Thus, software tasks are tasks which are not directly assigned to a specific interrupt vector. Thus, software tasks are tasks which are not *directly* bound to an interrupt vector.
The `#[task]` attribute used on a function declare it as a software tasks. The `#[task]` attributes used on a function determine if it is
Observe the absence of a `binds = InterruptName` argument to the attribute. software tasks, specifically the absence of a `binds = InterruptName`
The static method `task_name::spawn()` spawns (starts) a software task and argument to the attribute definition.
given that there are no higher priority tasks running the task will start executing directly.
All software tasks at the same priority level shares an interrupt handler acting as a dispatcher. The static method `task_name::spawn()` spawns (schedules) a software
What differentiates software and hardware tasks are the dispatcher versus bound interrupt vector. task by registering it with a specific dispatcher. If there are no
higher priority tasks available to the scheduler (which serves a set
of dispatchers), the task will start executing directly.
All software tasks at the same priority level share an interrupt handler bound to their dispatcher.
What differentiates software and hardware tasks is the usage of either a dispatcher or a bound interrupt vector.
The interrupt vectors used as dispatchers cannot be used by hardware tasks. The interrupt vectors used as dispatchers cannot be used by hardware tasks.
A list of “free” (not in use by hardware tasks) and usable interrupts allows the framework Availability of a set of “free” (not in use by hardware tasks) and usable interrupt vectors allows the framework
to dispatch software tasks. to dispatch software tasks via dedicated interrupt handlers.
This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an This set of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an
argument to the `#[app]` attribute. argument to the `#[app]` attribute.
Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that Each interrupt vector acting as dispatcher gets assigned to a unique priority level meaning that
the list of dispatchers need to cover all priority levels used by software tasks. the list of dispatchers needs to cover all priority levels used by software tasks.
Example: The `dispatchers =` argument needs to have at least 3 entries for an application using Example: The `dispatchers =` argument needs to have at least 3 entries for an application using
three different priorities for software tasks. three different priorities for software tasks.

View file

@ -8,7 +8,7 @@ If you are targeting ARMv6-M or ARMv8-M-base architecture, check out the section
[`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 a multitude of examples available provided by the community: protection using [`flip-link`]. There is also a multitude of examples provided by the community:
- [`rtic-examples`] - Multiple projects - [`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)

View file

@ -9,12 +9,16 @@ 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.
One can use a global allocator to achieve indirection (`alloc::Box`, One can use a global memory allocator to achieve indirection (`alloc::Box`,
`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.37.0, `alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.37.0,
or one can use a statically allocated memory pool like [`heapless::Pool`]. or one can use a statically allocated memory pool like [`heapless::Pool`].
[`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html [`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html
As this example of approach goes completely outside of RTIC resource
model with shared and local the program would rely on the correctness
of the memory allocator, in this case `heapless::pool`.
Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes.
``` rust ``` rust

View file

@ -11,7 +11,7 @@ Moreover, the relation between time and timers used for scheduling was difficult
For RTIC 1.0 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], For RTIC 1.0 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`. as the basis for all time-based operations when implementing `Monotonic`.
This makes it much easier to correctly implement the `Monotonic` trait allowing the use of These libraries make it much easier to correctly implement the `Monotonic` trait, allowing the use of
almost any timer in the system for scheduling. almost any timer in the system for scheduling.
The trait documents the requirements for each method, The trait documents the requirements for each method,

View file

@ -1,6 +1,6 @@
# 'static super-powers # 'static super-powers
In `#[init]` and `#[idle]` `local` resources has `'static` lifetime. In `#[init]` and `#[idle]` `local` resources have `'static` lifetime.
Useful when pre-allocating and/or splitting resources between tasks, drivers Useful when pre-allocating and/or splitting resources between tasks, drivers
or some other object. or some other object.

View file

@ -368,3 +368,5 @@ 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. 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`.
This enables breaking apps into multiple files.