mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
Revert accidental removal of editorial changes
This commit is contained in:
parent
8d46fb9cf9
commit
63f3d784fe
6 changed files with 38 additions and 30 deletions
|
@ -1,19 +1,19 @@
|
||||||
# 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)`, where `Shared` and `Local` are the resource
|
signature `fn(init::Context) -> (Shared, Local)`, where `Shared` and `Local` are resource structures defined by the user.
|
||||||
structures defined by the user.
|
|
||||||
|
The `init` task executes after system reset, [after an optionally defined `pre-init` code section][pre-init] and an always occurring internal RTIC initialization. [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`.
|
||||||
|
|
||||||
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`.
|
|
||||||
[pre-init]: https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.pre_init.html
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` variable with `'static` lifetime. Such variables can be delegated from the `init` task 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 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
|
||||||
|
|
|
@ -15,7 +15,7 @@ Omitting the `priority` argument the task priority defaults to `1`. The `idle` t
|
||||||
The highest static priority task takes precedence when more than one task are ready to execute.
|
The highest static priority task takes precedence when more than one 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 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.
|
Spawning a higher priority task A during execution of a lower priority task B suspends 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.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Task Priority
|
Task Priority
|
||||||
|
@ -46,6 +46,8 @@ Note that the task `bar` does *not* preempt task `baz` because its priority is t
|
||||||
|
|
||||||
One more note about priorities: choosing a priority higher than what the device supports will result in a compilation error. 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:
|
One more note about priorities: choosing a priority higher than what the device supports will result in a compilation error. 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:
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
error[E0080]: evaluation of constant value failed
|
error[E0080]: evaluation of constant value failed
|
||||||
--> examples/common.rs:10:1
|
--> examples/common.rs:10:1
|
||||||
|
|
|
@ -6,13 +6,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”. Hardware tasks are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not.
|
RTIC makes a distinction between “software tasks” and “hardware tasks”.
|
||||||
|
|
||||||
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.
|
*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, 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,21 +1,23 @@
|
||||||
# Hardware tasks
|
# Hardware tasks
|
||||||
|
|
||||||
At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) to perform scheduling and executing tasks, and all (*hardware*) tasks except `#[init]` and `#[idle]` run as interrupt handlers. This also means that you can manually bind tasks to interrupt handlers.
|
At its core RTIC is using a hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) to schedule and start execution of tasks. All tasks except `pre-init`, `#[init]` and `#[idle]` run as interrupt handlers.
|
||||||
|
|
||||||
To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`. This task becomes the interrupt handler for this hardware interrupt vector.
|
Hardware tasks are explicitly bound to interrupt handlers.
|
||||||
|
|
||||||
All tasks bound to an explicit interrupt are *hardware tasks* since they start execution in reaction to a hardware event (interrupt).
|
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.
|
||||||
|
|
||||||
Specifying a non-existing interrupt name will cause a compilation error. The interrupt names are commonly defined by [PAC or HAL][pacorhal] crates.
|
Specifying a non-existing interrupt name will cause a compilation error. The interrupt names are commonly defined by [PAC or HAL][pacorhal] crates.
|
||||||
|
|
||||||
Any available interrupt vector should work, but different hardware might have added special properties to select interrupt priority levels, such as the [nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434).
|
Any available interrupt vector should work. Specific devices may bind 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).
|
||||||
|
|
||||||
Beware of re-purposing interrupt vectors used internally by hardware features, RTIC is unaware of such hardware specific details.
|
Beware of using interrupt vectors that are used internally by hardware features; 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
|
||||||
[NVIC]: https://developer.arm.com/documentation/100166/0001/Nested-Vectored-Interrupt-Controller/NVIC-functional-description/NVIC-interrupts
|
[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(binds = InterruptName)]` attribute to declare a hardware task bound to an interrupt handler. In the example the interrupt triggering task execution is manually pended (`rtic::pend(Interrupt::UART0)`). However, in the typical case, interrupts are pended by the hardware peripheral. RTIC does not interfere with mechanisms for clearing peripheral interrupts, so any hardware specific implementation is completely up to the implementer.
|
The example below demonstrates the use of the `#[task(binds = InterruptName)]` attribute to declare a hardware task bound to an interrupt handler.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../rtic/examples/hardware.rs}}
|
{{#include ../../../../rtic/examples/hardware.rs}}
|
||||||
|
|
|
@ -15,11 +15,11 @@ further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md). -->
|
||||||
|
|
||||||
## `#[local]` resources
|
## `#[local]` resources
|
||||||
|
|
||||||
`#[local]` resources accessible only to a single task. This task is given unique access to the resource without the use of locks or critical sections.
|
`#[local]` resources are locally accessible to a specific task, meaning that only that task can access the resource and does so without locks or critical sections. This allows for the resources, commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific task.
|
||||||
|
|
||||||
This allows for the resources, 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 single task.) Attempting to assign the same `#[local]` resource to more than one task is a compile-time error.
|
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.
|
||||||
|
|
||||||
Types of `#[local]` resources must implement [`Send`] trait as they are being sent from `init` to the target task and thus crossing the *thread* boundary.
|
Types of `#[local]` resources must implement a [`Send`] trait as they are being sent from `init` 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
|
||||||
|
|
||||||
|
@ -36,9 +36,11 @@ $ cargo run --target thumbv7m-none-eabi --example locals
|
||||||
{{#include ../../../../rtic/ci/expected/locals.run}}
|
{{#include ../../../../rtic/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 task declaration, `#[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.
|
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 initialized in `#[init]`.
|
||||||
|
|
||||||
Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they are not crossing any thread boundary.
|
Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they are not crossing any thread boundary.
|
||||||
|
|
||||||
|
@ -69,7 +71,7 @@ The critical section created by the `lock` API is based on dynamic priorities: i
|
||||||
[icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol
|
[icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol
|
||||||
[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. The two handlers with the lower priorities contend for the `shared` resource and need to lock the resource for accessing the data. The highest priority handler, which do not access the `shared` resource, is free to preempt the critical section created by the lowest priority handler.
|
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 a `shared` resource and need to succeed in locking the resource in order to access its data. The highest priority handler, which does not access the `shared` resource, is free to preempt a critical section created by the lowest priority handler.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../rtic/examples/lock.rs}}
|
{{#include ../../../../rtic/examples/lock.rs}}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Software tasks & spawn
|
# Software tasks & spawn
|
||||||
|
|
||||||
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
|
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
|
||||||
interrupt vector, but rather to a “dispatcher” interrupt vector running at the same priority as the software task.
|
interrupt vector, but rather bound to a “dispatcher” interrupt vector running at the intended priority of the software task (see below).
|
||||||
|
|
||||||
Similarly to *hardware* tasks, the `#[task]` attribute used on a function declare it as a task. The absence of a `binds = InterruptName` argument to the attribute declares the function as a *software task*.
|
Similarly to *hardware* tasks, the `#[task]` attribute used on a function declare it as a task. The absence of a `binds = InterruptName` argument to the attribute declares the function as a *software task*.
|
||||||
|
|
||||||
|
@ -94,6 +94,3 @@ $ cargo run --target thumbv7m-none-eabi --example zero-prio-task
|
||||||
---
|
---
|
||||||
|
|
||||||
Application side safety: Technically, the RTIC framework ensures that `poll` is never executed on any *software* task with *completed* future, thus adhering to the soundness rules of async Rust.
|
Application side safety: Technically, the RTIC framework ensures that `poll` is never executed on any *software* task with *completed* future, thus adhering to the soundness rules of async Rust.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue