From cd102632011e36ad434bf1f3e4c1f19c78e73b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 1 Feb 2023 01:15:56 +0100 Subject: [PATCH] Revert accidental removal of editorial changes --- book/en/src/by-example/app_init.md | 16 ++++++++-------- book/en/src/by-example/app_priorities.md | 4 +++- book/en/src/by-example/app_task.md | 15 ++++++++++----- book/en/src/by-example/hardware_tasks.md | 14 ++++++++------ book/en/src/by-example/resources.md | 12 +++++++----- book/en/src/by-example/software_tasks.md | 7 ++----- 6 files changed, 38 insertions(+), 30 deletions(-) diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 62fb55b837..3767bd7616 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,19 +1,19 @@ # App initialization and the `#[init]` task 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 -structures defined by the user. +signature `fn(init::Context) -> (Shared, Local)`, where `Shared` and `Local` are resource 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 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`. ``` rust diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md index f03ebf7390..9d27658362 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -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 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 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: +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 error[E0080]: evaluation of constant value failed --> examples/common.rs:10:1 diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md index e0c67ad2c2..b2731f62a0 100644 --- a/book/en/src/by-example/app_task.md +++ b/book/en/src/by-example/app_task.md @@ -6,13 +6,18 @@ Tasks, defined with `#[task]`, are the main mechanism of getting work done in RT Tasks can -* Be spawned (now or in the future) -* Receive messages (message passing) -* Prioritized allowing preemptive multitasking +* Be spawned (now or in the future, also by themselves) +* Receive messages (passing messages between tasks) +* Be 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. +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. diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index e3e51acc59..cb20a7ccd4 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,21 +1,23 @@ # 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. -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 [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 {{#include ../../../../rtic/examples/hardware.rs}} diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index ea67b2661b..2dd7cb7a4b 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -15,11 +15,11 @@ further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md). --> ## `#[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 @@ -36,9 +36,11 @@ $ cargo run --target thumbv7m-none-eabi --example locals {{#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 -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. @@ -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 [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 {{#include ../../../../rtic/examples/lock.rs}} diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 27527078d3..828c3fd8ce 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,7 +1,7 @@ # 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 -interrupt vector, but rather to a “dispatcher” interrupt vector running at the same priority as the software task. +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 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*. @@ -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. - - -