diff options
Diffstat (limited to 'book/en/src/by-example')
21 files changed, 120 insertions, 302 deletions
diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 0d977a1..0aeed5b 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -2,7 +2,9 @@ ## Requirements on the `app` attribute -All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute only applies to a `mod`-item containing the RTIC application. The `app` attribute has a mandatory `device` argument that takes a *path* as a value. This must be a full path pointing to a *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or newer. +All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute only applies to a `mod`-item containing the RTIC application. + +The `app` attribute has a mandatory `device` argument that takes a *path* as a value. This must be a full path pointing to a *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or newer. The `app` attribute will expand into a suitable entry point and thus replaces the use of the [`cortex_m_rt::entry`] attribute. @@ -12,21 +14,33 @@ The `app` attribute will expand into a suitable entry point and thus replaces th ## Structure and zero-cost concurrency -An RTIC `app` is an executable system model for single-core applications, declaring a set of `local` and `shared` resources operated on by a set of `init`, `idle`, *hardware* and *software* tasks. In short the `init` task runs before any other task returning the set of `local` and `shared` resources. Tasks run preemptively based on their associated static priority, `idle` has the lowest priority (and can be used for background work, and/or to put the system to sleep until woken by some event). Hardware tasks are bound to underlying hardware interrupts, while software tasks are scheduled by asynchronous executors (one for each software task priority). +An RTIC `app` is an executable system model for single-core applications, declaring a set of `local` and `shared` resources operated on by a set of `init`, `idle`, *hardware* and *software* tasks. + +* `init` runs before any other task, and returns the `local` and `shared` resources. +* Tasks (both hardware and software) run preemptively based on their associated static priority. +* Hardware tasks are bound to underlying hardware interrupts. +* Software tasks are schedulied by an set of asynchronous executors, one for each software task priority. +* `idle` has the lowest priority, and can be used for background work, and/or to put the system to sleep until it is woken by some event. At compile time the task/resource model is analyzed under the Stack Resource Policy (SRP) and executable code generated with the following outstanding properties: -- guaranteed race-free resource access and deadlock-free execution on a single-shared stack - - hardware task scheduling is performed directly by the hardware, and - - software task scheduling is performed by auto generated async executors tailored to the application. +- Guaranteed race-free resource access and deadlock-free execution on a single-shared stack. +- Hardware task scheduling is performed directly by the hardware. +- Software task scheduling is performed by auto generated async executors tailored to the application. Overall, the generated code infers no additional overhead in comparison to a hand-written implementation, thus in Rust terms RTIC offers a zero-cost abstraction to concurrency. +## Priority + +Priorities in RTIC are specified using the `priority = N` (where N is a positive number) argument passed to the `#[task]` attribute. All `#[task]`s can have a priority. If the priority of a task is not specified, it is set to the default value of 1. + +Priorities in RTIC follow a higher value = more important scheme. For examples, a task with priority 2 will preempt a task with priority 1. + ## An RTIC application example -To give a flavour of RTIC, the following example contains commonly used features. +To give a taste of RTIC, the following example contains commonly used features. In the following sections we will go through each feature in detail. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/common.rs}} ``` diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md index cbfd7ba..c0b4139 100644 --- a/book/en/src/by-example/app_idle.md +++ b/book/en/src/by-example/app_idle.md @@ -11,7 +11,7 @@ Like in `init`, locally declared resources will have `'static` lifetimes that ar The example below shows that `idle` runs after `init`. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/idle.rs}} ``` @@ -38,7 +38,7 @@ The following example shows how to enable sleep by setting the [WFI]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/WFI [NOP]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/NOP -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/idle-wfi.rs}} ``` diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index fb37387..e581ef5 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -16,7 +16,7 @@ The example below shows the types of the `core`, `device` and `cs` fields, and s 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 +``` rust,noplayground {{#include ../../../../rtic/examples/init.rs}} ``` diff --git a/book/en/src/by-example/app_minimal.md b/book/en/src/by-example/app_minimal.md index 714f543..2c6f218 100644 --- a/book/en/src/by-example/app_minimal.md +++ b/book/en/src/by-example/app_minimal.md @@ -2,7 +2,7 @@ This is the smallest possible RTIC application: -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/smallest.rs}} ``` diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md index 9d27658..86ff985 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -33,7 +33,7 @@ Task Priority The following example showcases the priority based scheduling of tasks: -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/preempt.rs}} ``` diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md deleted file mode 100644 index b2731f6..0000000 --- a/book/en/src/by-example/app_task.md +++ /dev/null @@ -1,23 +0,0 @@ -<!-- Should probably be removed --> - -# Defining tasks with `#[task]` - -Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. - -Tasks can - -* 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, 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/channel.md b/book/en/src/by-example/channel.md index c020870..75ecbfd 100644 --- a/book/en/src/by-example/channel.md +++ b/book/en/src/by-example/channel.md @@ -1,8 +1,8 @@ # Communication over channels. -Channels can be used to communicate data between running *software* tasks. The channel is essentially a wait queue, allowing tasks with multiple producers and a single receiver. A channel is constructed in the `init` task and backed by statically allocated memory. Send and receive endpoints are distributed to *software* tasks: +Channels can be used to communicate data between running tasks. The channel is essentially a wait queue, allowing tasks with multiple producers and a single receiver. A channel is constructed in the `init` task and backed by statically allocated memory. Send and receive endpoints are distributed to *software* tasks: -``` rust +``` rust,noplayground ... const CAPACITY: usize = 5; #[init] @@ -16,11 +16,13 @@ const CAPACITY: usize = 5; In this case the channel holds data of `u32` type with a capacity of 5 elements. +Channels can also be used from *hardware* tasks, but only in a non-`async` manner using the [Try API](#try-api). + ## Sending data The `send` method post a message on the channel as shown below: -``` rust +``` rust,noplayground #[task] async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { hprintln!("Sender 1 sending: 1"); @@ -32,7 +34,7 @@ async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY The receiver can `await` incoming messages: -``` rust +``` rust,noplayground #[task] async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { while let Ok(val) = receiver.recv().await { @@ -46,7 +48,7 @@ Channels are implemented using a small (global) *Critical Section* (CS) for prot For a complete example: -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-channel.rs}} ``` @@ -62,7 +64,7 @@ Also sender endpoint can be awaited. In case the channel capacity has not yet be In the following example the `CAPACITY` has been reduced to 1, forcing sender tasks to wait until the data in the channel has been received. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-channel-done.rs}} ``` @@ -79,7 +81,7 @@ $ cargo run --target thumbv7m-none-eabi --example async-channel-done --features In case all senders have been dropped `await`-ing on an empty receiver channel results in an error. This allows to gracefully implement different types of shutdown operations. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-channel-no-sender.rs}} ``` @@ -95,7 +97,7 @@ Similarly, `await`-ing on a send channel results in an error in case the receive The resulting error returns the data back to the sender, allowing the sender to take appropriate action (e.g., storing the data to later retry sending it). -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-channel-no-receiver.rs}} ``` @@ -107,13 +109,13 @@ $ cargo run --target thumbv7m-none-eabi --example async-channel-no-receiver --fe {{#include ../../../../rtic/ci/expected/async-channel-no-receiver.run}} ``` - - ## Try API -In cases you wish the sender to proceed even in case the channel is full. To that end, a `try_send` API is provided. +Using the Try API, you can send or receive data from or to a channel without requiring that the operation succeeds, and in non-`async` contexts. + +This API is exposed through `Receiver::try_recv` and `Sender::try_send`. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-channel-try.rs}} ``` diff --git a/book/en/src/by-example/delay.md b/book/en/src/by-example/delay.md index f286363..81f855f 100644 --- a/book/en/src/by-example/delay.md +++ b/book/en/src/by-example/delay.md @@ -1,24 +1,23 @@ # Tasks with delay -A convenient way to express *miniminal* timing requirements is by means of delaying progression. +A convenient way to express miniminal timing requirements is by delaying progression. -This can be achieved by instantiating a monotonic timer: +This can be achieved by instantiating a monotonic timer (for implementations, see [`rtic-monotonics`]): -``` rust -... -rtic_monotonics::make_systick_handler!(); - -#[init] -fn init(cx: init::Context) -> (Shared, Local) { - hprintln!("init"); +[`rtic-monotonics`]: https://github.com/rtic-rs/rtic/tree/master/rtic-monotonics +[`rtic-time`]: https://github.com/rtic-rs/rtic/tree/master/rtic-time +[`Monotonic`]: https://docs.rs/rtic-time/latest/rtic_time/trait.Monotonic.html +[Implementing a `Monotonic`]: ../monotonic_impl.md - Systick::start(cx.core.SYST, 12_000_000); - ... +``` rust,noplayground +... +{{#include ../../../../rtic/examples/async-timeout.rs:init}} + ... ``` A *software* task can `await` the delay to expire: -``` rust +``` rust,noplayground #[task] async fn foo(_cx: foo::Context) { ... @@ -28,13 +27,10 @@ async fn foo(_cx: foo::Context) { ``` -Technically, the timer queue is implemented as a list based priority queue, where list-nodes are statically allocated as part of the underlying task `Future`. Thus, the timer queue is infallible at run-time (its size and allocation is determined at compile time). - -Similarly the channels implementation, the timer-queue implementation relies on a global *Critical Section* (CS) for race protection. For the examples a CS implementation is provided by adding `--features test-critical-section` to the build options. +<details> +<summary>A complete example</summary> -For a complete example: - -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-delay.rs}} ``` @@ -46,75 +42,63 @@ $ cargo run --target thumbv7m-none-eabi --example async-delay --features test-cr {{#include ../../../../rtic/ci/expected/async-delay.run}} ``` +</details> + +> Interested in contributing new implementations of [`Monotonic`], or more information about the inner workings of monotonics? +> Check out the [Implementing a `Monotonic`] chapter! + ## Timeout -Rust `Futures` (underlying Rust `async`/`await`) are composable. This makes it possible to `select` in between `Futures` that have completed. +Rust [`Future`]s (underlying Rust `async`/`await`) are composable. This makes it possible to `select` in between `Futures` that have completed. -A common use case is transactions with associated timeout. In the examples shown below, we introduce a fake HAL device which performs some transaction. We have modelled the time it takes based on the input parameter (`n`) as `350ms + n * 100ms)`. +[`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html + +A common use case is transactions with an associated timeout. In the examples shown below, we introduce a fake HAL device that performs some transaction. We have modelled the time it takes based on the input parameter (`n`) as `350ms + n * 100ms`. Using the `select_biased` macro from the `futures` crate it may look like this: -``` rust -// Call hal with short relative timeout using `select_biased` -select_biased! { - v = hal_get(1).fuse() => hprintln!("hal returned {}", v), - _ = Systick::delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first -} +``` rust,noplayground,noplayground +{{#include ../../../../rtic/examples/async-timeout.rs:select_biased}} ``` -Assuming the `hal_get` will take 450ms to finish, a short timeout of 200ms will expire. +Assuming the `hal_get` will take 450ms to finish, a short timeout of 200ms will expire before `hal_get` can complete. -``` rust -// Call hal with long relative timeout using `select_biased` -select_biased! { - v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first - _ = Systick::delay(1000.millis()).fuse() => hprintln!("timeout", ), -} -``` +Extending the timeout to 1000ms would cause `hal_get` will to complete first. -By extending the timeout to 1000ms, the `hal_get` will finish first. +Using `select_biased` any number of futures can be combined, so its very powerful. However, as the timeout pattern is frequently used, more ergonomic support is baked into RTIC, provided by the [`rtic-monotonics`] and [`rtic-time`] crates. -Using `select_biased` any number of futures can be combined, so its very powerful. However, as the timeout pattern is frequently used, it is directly supported by the RTIC [rtc-monotonics] and [rtic-time] crates. The second example from above using `timeout_after`: +Rewriting the second example from above using `timeout_after` gives: -``` rust -// Call hal with long relative timeout using monotonic `timeout_after` -match Systick::timeout_after(1000.millis(), hal_get(1)).await { - Ok(v) => hprintln!("hal returned {}", v), - _ => hprintln!("timeout"), -} +``` rust,noplayground +{{#include ../../../../rtic/examples/async-timeout.rs:timeout_at_basic}} ``` -In cases you want exact control over time without drift. For this purpose we can use exact points in time using `Instance`, and spans of time using `Duration`. Operations on the `Instance` and `Duration` types are given by the [fugit] crate. +In cases where you want exact control over time without drift we can use exact points in time using `Instant`, and spans of time using `Duration`. Operations on the `Instant` and `Duration` types come from the [`fugit`] crate. [fugit]: https://crates.io/crates/fugit -``` rust -// get the current time instance -let mut instant = Systick::now(); +``` rust,noplayground -// do this 3 times -for n in 0..3 { - // absolute point in time without drift - instant += 1000.millis(); - Systick::delay_until(instant).await; +{{#include ../../../../rtic/examples/async-timeout.rs:timeout_at}} - // absolute point it time for timeout - let timeout = instant + 500.millis(); - hprintln!("now is {:?}, timeout at {:?}", Systick::now(), timeout); - - match Systick::timeout_at(timeout, hal_get(n)).await { - Ok(v) => hprintln!("hal returned {} at time {:?}", v, Systick::now()), - _ => hprintln!("timeout"), - } -} ``` -`instant = Systick::now()` gives the baseline (i.e., the absolute current point in time). We want to call `hal_get` after 1000ms relative to this absolute point in time. This can be accomplished by `Systick::delay_until(instant).await;`. We define the absolute point in time for the `timeout`, and call `Systick::timeout_at(timeout, hal_get(n)).await`. For the first loop iteration `n == 0`, and the `hal_get` will take 350ms (and finishes before the timeout). For the second iteration `n == 1`, and `hal_get` will take 450ms (and again succeeds to finish before the timeout). For the third iteration `n == 2` (`hal_get` will take 5500ms to finish). In this case we will run into a timeout. +`let mut instant = Systick::now()` sets the starting time of execution. + +We want to call `hal_get` after 1000ms relative to this starting time. This can be accomplished by using `Systick::delay_until(instant).await`. + +Then, we define a point in time called `timeout`, and call `Systick::timeout_at(timeout, hal_get(n)).await`. + +For the first iteration of the loop, with `n == 0`, the `hal_get` will take 350ms (and finishes before the timeout). + +For the second iteration, with `n == 1`, the `hal_get` will take 450ms (and again succeeds to finish before the timeout). +For the third iteration, with `n == 2`, `hal_get` will take 550ms to finish, in which case we will run into a timeout. -The complete example: +<details> +<summary>A complete example</summary> -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/async-timeout.rs}} ``` @@ -125,3 +109,4 @@ $ cargo run --target thumbv7m-none-eabi --example async-timeout --features test- ``` console {{#include ../../../../rtic/ci/expected/async-timeout.run}} ``` +</details> diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index 75dd1a4..ded488c 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,8 +1,6 @@ # Hardware tasks -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. - -Hardware tasks are explicitly bound 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` (a hidden "task"), `#[init]` and `#[idle]` run as interrupt handlers. 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. @@ -17,9 +15,11 @@ Beware of using interrupt vectors that are used internally by hardware features; [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 +## Example + The example below demonstrates the use of the `#[task(binds = InterruptName)]` attribute to declare a hardware task bound to an interrupt handler. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/hardware.rs}} ``` diff --git a/book/en/src/by-example/message_passing.md b/book/en/src/by-example/message_passing.md index 0dc8f85..02fd298 100644 --- a/book/en/src/by-example/message_passing.md +++ b/book/en/src/by-example/message_passing.md @@ -3,14 +3,14 @@ Software tasks support message passing, this means that software tasks can be spawned with an argument: `foo::spawn(1)` which will run the task `foo` with the argument `1`. -Capacity sets the size of the spawn queue for the task, if not specified capacity defaults to 1. +Capacity sets the size of the spawn queue for the task. If it is not specified, the capacity defaults to 1. In the example below, the capacity of task `foo` is `3`, allowing three simultaneous pending spawns of `foo`. Exceeding this capacity is an `Error`. The number of arguments to a task is not limited: -``` rust +``` rust,noplayground {{#include ../../../../examples/message_passing.rs}} ``` diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md deleted file mode 100644 index 3a23681..0000000 --- a/book/en/src/by-example/monotonic.md +++ /dev/null @@ -1,64 +0,0 @@ -# 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 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. - -The `#[monotonic]` attribute, applied to a type alias definition, exists to support this. -This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait. -This is generally some timer which handles the timing of the system. -One or more monotonics can coexist in the same system, for example a slow timer that wakes the -system from sleep and another which purpose is for fine grained scheduling while the -system is awake. - -[`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic - -The attribute has one required parameter and two optional parameters, `binds`, `default` and -`priority` respectively. -The required parameter, `binds = InterruptName`, associates an interrupt vector to the timer's -interrupt, while `default = true` enables a shorthand API when spawning and accessing -time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority -of the interrupt vector. - -> The default `priority` is the **maximum priority** of the system. -> If your system has a high priority task with tight scheduling requirements, -> it might be desirable to demote the `monotonic` task to a lower priority -> to reduce scheduling jitter for the high priority task. -> This however might introduce jitter and delays into scheduling via the `monotonic`, -> making it a trade-off. - -The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple. -This activates the monotonics making it possible to use them. - -See the following example: - -``` rust -{{#include ../../../../examples/schedule.rs}} -``` - -``` console -$ 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: - -``` rust -{{#include ../../../../examples/cancel-reschedule.rs}} -``` - -``` console -$ cargo run --target thumbv7m-none-eabi --example cancel-reschedule -{{#include ../../../../ci/expected/cancel-reschedule.run}} -``` diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 0bf5d11..c2472bc 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -25,7 +25,7 @@ Types of `#[local]` resources must implement a [`Send`] trait as they are being The example application shown below contains three tasks `foo`, `bar` and `idle`, each having access to its own `#[local]` resource. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/locals.rs}} ``` @@ -51,7 +51,7 @@ Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Syn In the example below the different uses and lifetimes are shown: -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/declared_locals.rs}} ``` @@ -76,7 +76,7 @@ The critical section created by the `lock` API is based on dynamic priorities: i 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,noplayground {{#include ../../../../rtic/examples/lock.rs}} ``` @@ -94,7 +94,7 @@ Types of `#[shared]` resources have to be [`Send`]. As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The following examples show this in use: -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/multilock.rs}} ``` @@ -116,7 +116,7 @@ Note that in this release of RTIC it is not possible to request both exclusive a In the example below a key (e.g. a cryptographic key) is loaded (or created) at runtime (returned by `init`) and then used from two tasks that run at different priorities without any kind of lock. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/only-shared-access.rs}} ``` @@ -142,7 +142,7 @@ To adhere to the Rust [aliasing] rule, a resource may be either accessed through Using `#[lock_free]` on resources shared by tasks running at different priorities will result in a *compile-time* error -- not using the `lock` API would violate the aforementioned alias rule. Similarly, for each priority there can be only a single *software* task accessing a shared resource (as an `async` task may yield execution to other *software* or *hardware* tasks running at the same priority). However, under this single-task restriction, we make the observation that the resource is in effect no longer `shared` but rather `local`. Thus, using a `#[lock_free]` shared resource will result in a *compile-time* error -- where applicable, use a `#[local]` resource instead. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/lock-free.rs}} ``` diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 0efc57b..444f4a6 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,7 +1,6 @@ # 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 bound to a “dispatcher” interrupt vector running at the intended priority of the software task (see below). +The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md). The core difference is 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*. @@ -9,11 +8,11 @@ The static method `task_name::spawn()` spawns (starts) a software task and given The *software* task itself is given as an `async` Rust function, which allows the user to optionally `await` future events. This allows to blend reactive programming (by means of *hardware* tasks) with sequential programming (by means of *software* tasks). -Whereas, *hardware* tasks are assumed to run-to-completion (and return), *software* tasks may be started (`spawned`) once and run forever, with the side condition that any loop (execution path) is broken by at least one `await` (yielding operation). +While *hardware* tasks are assumed to run-to-completion (and return), *software* tasks may be started (`spawned`) once and run forever, on the condition that any loop (execution path) is broken by at least one `await` (yielding operation). -All *software* tasks at the same priority level shares an interrupt handler acting as an async executor dispatching the software tasks. +## Dispatchers -This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute, where you define the set of free and usable interrupts. +All *software* tasks at the same priority level share an interrupt handler acting as an async executor dispatching the software tasks. This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute, where you define the set of free and usable interrupts. 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. @@ -23,7 +22,7 @@ The framework will give a compilation error if there are not enough dispatchers See the following example: -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/spawn.rs}} ``` @@ -40,7 +39,7 @@ In the below example, we `spawn` the *software* task `foo` from the `idle` task. Technically the async executor will `poll` the `foo` *future* which in this case leaves the *future* in a *completed* state. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/spawn_loop.rs}} ``` @@ -56,7 +55,7 @@ An attempt to `spawn` an already spawned task (running) task will result in an e Technically, a `spawn` to a *future* that is not in *completed* state is considered an error. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/spawn_err.rs}} ``` @@ -71,7 +70,7 @@ $ cargo run --target thumbv7m-none-eabi --example spawn_err ## Passing arguments You can also pass arguments at spawn as follows. -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/spawn_arguments.rs}} ``` @@ -92,7 +91,7 @@ Conceptually, one can see such tasks as running in the `main` thread of the appl [Send]: https://doc.rust-lang.org/nomicon/send-and-sync.html -``` rust +``` rust,noplayground {{#include ../../../../rtic/examples/zero-prio-task.rs}} ``` diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md deleted file mode 100644 index 86d7e71..0000000 --- a/book/en/src/by-example/starting_a_project.md +++ /dev/null @@ -1,21 +0,0 @@ -# Starting a new project - -A recommendation when starting a RTIC project from scratch is to -follow RTIC's [`defmt-app-template`]. - -If you are targeting ARMv6-M or ARMv8-M-base architecture, check out the section [Target Architecture](../internals/targets.md) for more information on hardware limitations to be aware of. - -[`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 is also a multitude of examples provided by the community: - -For inspiration, you may look at the below resources. For now, they cover RTIC v1.x, but will be updated with RTIC v2.x examples over time. - -- [`rtic-examples`] - Multiple projects -- [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) -- ... More to come - -[`defmt`]: https://github.com/knurling-rs/defmt/ -[`flip-link`]: https://github.com/knurling-rs/flip-link/ -[`rtic-examples`]: https://github.com/rtic-rs/rtic-examples diff --git a/book/en/src/by-example/tips_destructureing.md b/book/en/src/by-example/tips/destructureing.md index ab27987..752311d 100644 --- a/book/en/src/by-example/tips_destructureing.md +++ b/book/en/src/by-example/tips/destructureing.md @@ -3,13 +3,13 @@ Destructuring task resources might help readability if a task takes multiple resources. Here are two examples on how to split up the resource struct: -``` rust -{{#include ../../../../rtic/examples/destructure.rs}} +``` rust,noplayground +{{#include ../../../../../rtic/examples/destructure.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example destructure ``` ``` console -{{#include ../../../../rtic/ci/expected/destructure.run}} +{{#include ../../../../../rtic/ci/expected/destructure.run}} ``` diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips/index.md index 18d5991..18d5991 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips/index.md diff --git a/book/en/src/by-example/tips_indirection.md b/book/en/src/by-example/tips/indirection.md index 0de14a6..aa68190 100644 --- a/book/en/src/by-example/tips_indirection.md +++ b/book/en/src/by-example/tips/indirection.md @@ -7,14 +7,14 @@ Indirection can minimize message passing overhead: instead of sending the buffer 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 +[`heapless::Pool`]: https://docs.rs/heapless/latest/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 -{{#include ../../../../rtic/examples/pool.rs}} +``` rust,noplayground +{{#include ../../../../../rtic/examples/pool.rs}} ``` ``` console @@ -22,5 +22,5 @@ $ cargo run --target thumbv7m-none-eabi --example pool ``` ``` console -{{#include ../../../../rtic/ci/expected/pool.run}} +{{#include ../../../../../rtic/ci/expected/pool.run}} ``` diff --git a/book/en/src/by-example/tips_static_lifetimes.md b/book/en/src/by-example/tips/static_lifetimes.md index 0eaa59f..f4e4829 100644 --- a/book/en/src/by-example/tips_static_lifetimes.md +++ b/book/en/src/by-example/tips/static_lifetimes.md @@ -8,8 +8,8 @@ In the following example two different tasks share a [`heapless::spsc::Queue`] f [`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html -``` rust -{{#include ../../../../rtic/examples/static.rs}} +``` rust,noplayground +{{#include ../../../../../rtic/examples/static.rs}} ``` Running this program produces the expected output. @@ -19,5 +19,5 @@ $ cargo run --target thumbv7m-none-eabi --example static ``` ``` console -{{#include ../../../../rtic/ci/expected/static.run}} +{{#include ../../../../../rtic/ci/expected/static.run}} ``` diff --git a/book/en/src/by-example/tips_view_code.md b/book/en/src/by-example/tips/view_code.md index b4a9066..64af7ad 100644 --- a/book/en/src/by-example/tips_view_code.md +++ b/book/en/src/by-example/tips/view_code.md @@ -16,7 +16,7 @@ $ rustfmt target/rtic-expansion.rs $ tail target/rtic-expansion.rs ``` -``` rust +``` rust,noplayground #[doc = r" Implementation details"] mod app { #[doc = r" Always include the device crate which contains the vector table"] diff --git a/book/en/src/by-example/tips_from_ram.md b/book/en/src/by-example/tips_from_ram.md deleted file mode 100644 index f6b2173..0000000 --- a/book/en/src/by-example/tips_from_ram.md +++ /dev/null @@ -1,45 +0,0 @@ -# Running tasks from RAM - -The main goal of moving the specification of RTIC applications to attributes in RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the `link_section` attribute can be applied to tasks to place them in RAM; this can -improve performance in some cases. - -> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` attributes are powerful but also easy to misuse. Incorrectly using any of these attributes can cause undefined behavior; you should always prefer to use safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and `exception` attributes. -> -> In the particular case of RAM functions there's no safe abstraction for it in `cortex-m-rt` v0.6.5 but there's an [RFC] for adding a `ramfunc` attribute in a future release. - -[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 - -The example below shows how to place the higher priority task, `bar`, in RAM. - -``` rust -{{#include ../../../../rtic/examples/ramfunc.rs}} -``` - -Running this program produces the expected output. - -``` console -$ cargo run --target thumbv7m-none-eabi --example ramfunc -``` - -``` console -{{#include ../../../../rtic/ci/expected/ramfunc.run}} -``` - -One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM -(`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`). - -``` console -$ cargo nm --example ramfunc --release | grep ' foo::' -``` - -``` console -{{#include ../../../../rtic/ci/expected/ramfunc.run.grep.foo}} -``` - -``` console -$ cargo nm --example ramfunc --target thumbv7m-none-eabi --release | grep '*bar::' -``` - -``` console -{{#include ../../../../rtic/ci/expected/ramfunc.run.grep.bar}} -``` diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md deleted file mode 100644 index 57b0a01..0000000 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ /dev/null @@ -1,29 +0,0 @@ -# Implementing a `Monotonic` timer for scheduling - -The framework is flexible because it can use any timer which has compare-match and optionally supporting overflow interrupts for scheduling. The single requirement to make a timer usable with RTIC is implementing the [`rtic-time::Monotonic`] trait. - -For RTIC 1.0 and 2.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`. 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, and for inspiration -there is a reference implementation based on the `SysTick` timer available on all ARM Cortex M MCUs. - -- [`Systick based`], runs at a fixed interrupt (tick) rate - with some overhead but simple and provides support for large time spans - -Here is a list of `Monotonic` implementations for RTIC 1.0: - -- [`STM32F411 series`], implemented for the 32-bit timers -- [`Nordic nRF52 series Timer`], implemented for the 32-bit timers -- [`Nordic nRF52 series RTC`], implemented for the RTCs -- [`DWT and Systick based`], a more efficient (tickless) implementation - requires both `SysTick` and `DWT`, supports both high resolution and large time spans - -If you know of more implementations feel free to add them to this list. - -[`rtic_time::Monotonic`]: https://docs.rs/rtic_time/ -[`fugit`]: https://docs.rs/fugit/ -[`embedded_time`]: https://docs.rs/embedded_time/ -[`STM32F411 series`]: https://github.com/kalkyl/f411-rtic/blob/a696fce7d6d19fda2356c37642c4d53547982cca/src/mono.rs -[`Nordic nRF52 series Timer`]: https://github.com/kalkyl/nrf-play/blob/47f4410d4e39374c18ff58dc17c25159085fb526/src/mono.rs -[`Nordic nRF52 series RTC`]: https://gist.github.com/korken89/fe94a475726414dd1bce031c76adc3dd -[`Systick based`]: https://github.com/rtic-monotonics -[`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic |
