aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/by-example/app.md
diff options
context:
space:
mode:
Diffstat (limited to 'book/en/src/by-example/app.md')
-rw-r--r--book/en/src/by-example/app.md85
1 files changed, 66 insertions, 19 deletions
diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md
index d595570..ebb71f1 100644
--- a/book/en/src/by-example/app.md
+++ b/book/en/src/by-example/app.md
@@ -10,8 +10,8 @@ All RTFM applications use the [`app`] attribute (`#[app(..)]`). This attribute
must be applied to a `const` item that contains items. The `app` attribute has
a mandatory `device` argument that takes a *path* as a value. This path must
point to a *peripheral access crate* (PAC) generated using [`svd2rust`]
-**v0.14.x**. The `app` attribute will expand into a suitable entry point so it's
-not required to use the [`cortex_m_rt::entry`] attribute.
+**v0.14.x** or newer. The `app` attribute will expand into a suitable entry
+point so it's not required to use the [`cortex_m_rt::entry`] attribute.
[`app`]: ../../api/cortex_m_rtfm_macros/attr.app.html
[`svd2rust`]: https://crates.io/crates/svd2rust
@@ -28,22 +28,23 @@ not required to use the [`cortex_m_rt::entry`] attribute.
Within the pseudo-module the `app` attribute expects to find an initialization
function marked with the `init` attribute. This function must have signature
-`fn(init::Context) [-> init::LateResources]`.
+`fn(init::Context) [-> init::LateResources]` (the return type is not always
+required).
This initialization function will be the first part of the application to run.
The `init` function will run *with interrupts disabled* and has exclusive access
-to Cortex-M and device specific peripherals through the `core` and `device`
-variables fields of `init::Context`. Not all Cortex-M peripherals are available
-in `core` because the RTFM runtime takes ownership of some of them -- for more
-details see the [`rtfm::Peripherals`] struct.
+to Cortex-M and, optionally, device specific peripherals through the `core` and
+`device` fields of `init::Context`.
`static mut` variables declared at the beginning of `init` will be transformed
into `&'static mut` references that are safe to access.
[`rtfm::Peripherals`]: ../../api/rtfm/struct.Peripherals.html
-The example below shows the types of the `core` and `device` variables and
-showcases safe access to a `static mut` variable.
+The example below shows the types of the `core` and `device` fields and
+showcases safe access to a `static mut` variable. The `device` field is only
+available when the `peripherals` argument is set to `true` (it defaults to
+`false`).
``` rust
{{#include ../../../../examples/init.rs}}
@@ -64,7 +65,7 @@ signature `fn(idle::Context) - > !`.
When present, the runtime will execute the `idle` task after `init`. Unlike
`init`, `idle` will run *with interrupts enabled* and it's not allowed to return
-so it runs forever.
+so it must run forever.
When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and
then sends the microcontroller to sleep after running `init`.
@@ -84,21 +85,67 @@ The example below shows that `idle` runs after `init`.
$ cargo run --example idle
{{#include ../../../../ci/expected/idle.run}}```
-## `interrupt` / `exception`
+## Hardware tasks
-Just like you would do with the `cortex-m-rt` crate you can use the `interrupt`
-and `exception` attributes within the `app` pseudo-module to declare interrupt
-and exception handlers. In RTFM, we refer to interrupt and exception handlers as
-*hardware* tasks.
+To declare interrupt handlers the framework provides a `#[task]` attribute that
+can be attached to functions. This attribute takes a `binds` argument whose
+value is the name of the interrupt to which the handler will be bound to; the
+function adornated with this attribute becomes the interrupt handler. Within the
+framework these type of tasks are referred to as *hardware* tasks, because they
+start executing in reaction to a hardware event.
+
+The example below demonstrates the use of the `#[task]` attribute to declare an
+interrupt handler. Like in the case of `#[init]` and `#[idle]` local `static
+mut` variables are safe to use within a hardware task.
``` rust
-{{#include ../../../../examples/interrupt.rs}}
+{{#include ../../../../examples/hardware.rs}}
```
``` console
$ cargo run --example interrupt
-{{#include ../../../../ci/expected/interrupt.run}}```
+{{#include ../../../../ci/expected/hardware.run}}```
So far all the RTFM applications we have seen look no different that the
-applications one can write using only the `cortex-m-rt` crate. In the next
-section we start introducing features unique to RTFM.
+applications one can write using only the `cortex-m-rt` crate. From this point
+we start introducing features unique to RTFM.
+
+## Priorities
+
+The static priority of each handler can be declared in the `task` attribute
+using the `priority` argument. Tasks can have priorities in the range `1..=(1 <<
+NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
+crate. When the `priority` argument is omitted the priority is assumed to be
+`1`. The `idle` task has a non-configurable static priority of `0`, the lowest
+priority.
+
+When several tasks are ready to be executed the one with *highest* static
+priority will be executed first. Task prioritization can be observed in the
+following scenario: an interrupt signal arrives during the execution of a low
+priority task; the signal puts the higher priority task in the pending state.
+The difference in priority results in the higher priority task preempting the
+lower priority one: the execution of the lower priority task is suspended and
+the higher priority task is executed to completion. Once the higher priority
+task has terminated the lower priority task is resumed.
+
+The following example showcases the priority based scheduling of tasks.
+
+``` rust
+{{#include ../../../../examples/preempt.rs}}
+```
+
+``` console
+$ cargo run --example interrupt
+{{#include ../../../../ci/expected/preempt.run}}```
+
+Note that the task `gpiob` does *not* preempt task `gpioc` because its priority
+is the *same* as `gpioc`'s. However, once `gpioc` terminates the execution of
+task `gpiob` is prioritized over `gpioa`'s due to its higher priority. `gpioa`
+is resumed only after `gpiob` terminates.
+
+One more note about priorities: choosing a priority higher than what the device
+supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to
+limitations in the language the error message is currently far from helpful: it
+will say something along the lines of "evaluation of constant value failed" and
+the span of the error will *not* point out to the problematic interrupt value --
+we are sorry about this!