diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc38d83a3..cc84e15911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed - Attempt to handle docs generation enabling `deny(missing_docs)` +- Book: Editorial review - Use native GHA rustup and cargo - Distinguish between thumbv8m.base and thumbv8m.main for basepri usage. diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 22c4a28ade..5bf6200e1c 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,13 +1,18 @@ # 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, 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. -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`. +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`. ## Example @@ -15,7 +20,7 @@ The example below shows the types of the `core`, `device` and `cs` fields, and s 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 1a92ec846c..8cee7499e1 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -18,8 +18,8 @@ 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 +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 @@ -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 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: ```text diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md index 97160041e3..d83f1ff15a 100644 --- a/book/en/src/by-example/app_task.md +++ b/book/en/src/by-example/app_task.md @@ -4,15 +4,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. -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 7f8d3c6e14..2d405d324d 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,24 +1,26 @@ # 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 tasks except `#[init]` and `#[idle]` +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. -This also means that you can manually bind tasks to 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 +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 +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, +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 diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md index 094bd5df02..3a23681fd9 100644 --- a/book/en/src/by-example/monotonic.md +++ b/book/en/src/by-example/monotonic.md @@ -1,7 +1,7 @@ # Monotonic & spawn_{at/after} 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 */)`. `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. @@ -43,10 +43,14 @@ $ cargo run --target thumbv7m-none-eabi --example schedule {{#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 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. + 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: diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 6349b520b5..30089d34a2 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -30,13 +30,13 @@ 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. -Types of `#[local]` resources must implement [`Send`] trait as they are being sent from `init` -to 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 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 {{#include ../../../../examples/locals.rs}} @@ -49,12 +49,14 @@ $ cargo run --target thumbv7m-none-eabi --example locals {{#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 -A special use-case of local resources are the ones specified directly in the resource claim, -`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be +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]`. -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 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 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. +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 ../../../../examples/lock.rs}} diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 5c03f9140b..8ee185bd15 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -2,29 +2,33 @@ 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 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). -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. -Observe the absence of a `binds = InterruptName` argument to the attribute. -The static method `task_name::spawn()` spawns (starts) a software task and -given that there are no higher priority tasks running the task will start executing directly. +The `#[task]` attributes used on a function determine if it is +software tasks, specifically the absence of a `binds = InterruptName` +argument to the attribute definition. -All software tasks at the same priority level shares an interrupt handler acting as a dispatcher. -What differentiates software and hardware tasks are the dispatcher versus bound interrupt vector. +The static method `task_name::spawn()` spawns (schedules) a software +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. -The interrupt vectors used as dispatchers can not be used by hardware tasks. +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. -A list of “free” (not in use by hardware tasks) and usable interrupts allows the framework -to dispatch software tasks. +The interrupt vectors used as dispatchers cannot be used by hardware tasks. -This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an +Availability of a set of “free” (not in use by hardware tasks) and usable interrupt vectors allows the framework +to dispatch software tasks via dedicated interrupt handlers. + +This set of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute. -Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that -the list of dispatchers need to cover all priority levels used by software tasks. +Each interrupt vector acting as dispatcher gets assigned to a unique priority level meaning that +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 three different priorities for software tasks. diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index ccb0083c07..fe7be57818 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -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 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 - [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) diff --git a/book/en/src/by-example/tips_indirection.md b/book/en/src/by-example/tips_indirection.md index 1a330c5162..567a5e723b 100644 --- a/book/en/src/by-example/tips_indirection.md +++ b/book/en/src/by-example/tips_indirection.md @@ -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 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, 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 +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. ``` rust diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index d97b5839b0..7c3449b2b3 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -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`], 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. The trait documents the requirements for each method, diff --git a/book/en/src/by-example/tips_static_lifetimes.md b/book/en/src/by-example/tips_static_lifetimes.md index 8d3a832c4e..dadd9c9461 100644 --- a/book/en/src/by-example/tips_static_lifetimes.md +++ b/book/en/src/by-example/tips_static_lifetimes.md @@ -1,6 +1,6 @@ # '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 or some other object. diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 731931f013..5a8fabce5b 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -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. See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`. + +This enables breaking apps into multiple files.