mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
Merge #686
686: Book: Editorial review r=korken89 a=AfoHT Continuation of https://github.com/rtic-rs/cortex-m-rtic/pull/618 Better late than never... A big thanks to `@jvanderk` ! Co-authored-by: John van der Koijk <33966414+jvanderk@users.noreply.github.com>
This commit is contained in:
commit
a5e18cd529
13 changed files with 81 additions and 53 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue