aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/by-example
diff options
context:
space:
mode:
Diffstat (limited to 'book/en/src/by-example')
-rw-r--r--book/en/src/by-example/app.md105
-rw-r--r--book/en/src/by-example/new.md67
-rw-r--r--book/en/src/by-example/resources.md119
-rw-r--r--book/en/src/by-example/singletons.md26
-rw-r--r--book/en/src/by-example/tasks.md63
-rw-r--r--book/en/src/by-example/timer-queue.md97
-rw-r--r--book/en/src/by-example/tips.md77
-rw-r--r--book/en/src/by-example/types-send-sync.md60
8 files changed, 614 insertions, 0 deletions
diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md
new file mode 100644
index 0000000..996b8c1
--- /dev/null
+++ b/book/en/src/by-example/app.md
@@ -0,0 +1,105 @@
+# The `app` attribute
+
+This is the smallest possible RTFM application:
+
+``` rust
+{{#include ../../../../examples/smallest.rs}}
+```
+
+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.
+
+[`app`]: ../../api/cortex_m_rtfm_macros/attr.app.html
+[`svd2rust`]: https://crates.io/crates/svd2rust
+[`cortex_m_rt::entry`]: ../../api/cortex_m_rt_macros/attr.entry.html
+
+> **ASIDE**: Some of you may be wondering why we are using a `const` item as a
+> module and not a proper `mod` item. The reason is that using attributes on
+> modules requires a feature gate, which requires a nightly toolchain. To make
+> RTFM work on stable we use the `const` item instead. When more parts of macros
+> 1.2 are stabilized we'll move from a `const` item to a `mod` item and
+> eventually to a crate level attribute (`#![app]`).
+
+## `init`
+
+Within the pseudo-module the `app` attribute expects to find an initialization
+function marked with the `init` attribute. This function must have signature
+`[unsafe] fn()`.
+
+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, which are injected in the scope of `init` by the `app` attribute. 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.
+
+`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.
+
+``` rust
+{{#include ../../../../examples/init.rs}}
+```
+
+Running the example will print `init` to the console and then exit the QEMU
+process.
+
+``` console
+$ cargo run --example init
+{{#include ../../../../ci/expected/init.run}}```
+
+## `idle`
+
+A function marked with the `idle` attribute can optionally appear in the
+pseudo-module. This function is used as the special *idle task* and must have
+signature `[unsafe] fn() - > !`.
+
+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.
+
+When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and
+then sends the microcontroller to sleep after running `init`.
+
+[SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
+
+Like in `init`, `static mut` variables will be transformed into `&'static mut`
+references that are safe to access.
+
+The example below shows that `idle` runs after `init`.
+
+``` rust
+{{#include ../../../../examples/idle.rs}}
+```
+
+``` console
+$ cargo run --example idle
+{{#include ../../../../ci/expected/idle.run}}```
+
+## `interrupt` / `exception`
+
+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.
+
+``` rust
+{{#include ../../../../examples/interrupt.rs}}
+```
+
+``` console
+$ cargo run --example interrupt
+{{#include ../../../../ci/expected/interrupt.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.
diff --git a/book/en/src/by-example/new.md b/book/en/src/by-example/new.md
new file mode 100644
index 0000000..ae49ef2
--- /dev/null
+++ b/book/en/src/by-example/new.md
@@ -0,0 +1,67 @@
+# Starting a new project
+
+Now that you have learned about the main features of the RTFM framework you can
+try it out on your hardware by following these instructions.
+
+1. Instantiate the [`cortex-m-quickstart`] template.
+
+[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart#cortex-m-quickstart
+
+``` console
+$ # for example using `cargo-generate`
+$ cargo generate \
+ --git https://github.com/rust-embedded/cortex-m-quickstart \
+ --name app
+
+$ # follow the rest of the instructions
+```
+
+2. Add a peripheral access crate (PAC) that was generated using [`svd2rust`]
+ **v0.14.x**, or a board support crate that depends on one such PAC as a
+ dependency. Make sure that the `rt` feature of the crate is enabled.
+
+[`svd2rust`]: https://crates.io/crates/svd2rust
+
+In this example, I'll use the [`lm3s6965`] device crate. This device crate
+doesn't have an `rt` Cargo feature; that feature is always enabled.
+
+[`lm3s6965`]: https://crates.io/crates/lm3s6965
+
+This device crate provides a linker script with the memory layout of the target
+device so `memory.x` and `build.rs` need to be removed.
+
+``` console
+$ cargo add lm3s6965 --vers 0.1.3
+
+$ rm memory.x build.rs
+```
+
+3. Add the `cortex-m-rtfm` crate as a dependency and, if you need it, enable the
+ `timer-queue` feature.
+
+``` console
+$ cargo add cortex-m-rtfm
+```
+
+4. Write your RTFM application.
+
+Here I'll use the `init` example from the `cortex-m-rtfm` crate.
+
+``` console
+$ curl \
+ -L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0/examples/init.rs \
+ > src/main.rs
+```
+
+That example depends on the `panic-semihosting` crate:
+
+``` console
+$ cargo add panic-semihosting
+```
+
+5. Build it, flash it and run it.
+
+``` console
+$ # NOTE: I have uncommented the `runner` option in `.cargo/config`
+$ cargo run
+{{#include ../../../../ci/expected/init.run}}```
diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md
new file mode 100644
index 0000000..efdeaa2
--- /dev/null
+++ b/book/en/src/by-example/resources.md
@@ -0,0 +1,119 @@
+## Resources
+
+One of the limitations of the attributes provided by the `cortex-m-rt` crate is
+that sharing data (or peripherals) between interrupts, or between an interrupt
+and the `entry` function, requires a `cortex_m::interrupt::Mutex`, which
+*always* requires disabling *all* interrupts to access the data. Disabling all
+the interrupts is not always required for memory safety but the compiler doesn't
+have enough information to optimize the access to the shared data.
+
+The `app` attribute has a full view of the application thus it can optimize
+access to `static` variables. In RTFM we refer to the `static` variables
+declared inside the `app` pseudo-module as *resources*. To access a resource the
+context (`init`, `idle`, `interrupt` or `exception`) must first declare the
+resource in the `resources` argument of its attribute.
+
+In the example below two interrupt handlers access the same resource. No `Mutex`
+is required in this case because the two handlers run at the same priority and
+no preemption is possible. The `SHARED` resource can only be accessed by these
+two handlers.
+
+``` rust
+{{#include ../../../../examples/resource.rs}}
+```
+
+``` console
+$ cargo run --example resource
+{{#include ../../../../ci/expected/resource.run}}```
+
+## Priorities
+
+The priority of each handler can be declared in the `interrupt` and `exception`
+attributes. It's not possible to set the priority in any other way because the
+runtime takes ownership of the `NVIC` peripheral; it's also not possible to
+change the priority of a handler / task at runtime. Thanks to this restriction
+the framework has knowledge about the *static* priorities of all interrupt and
+exception handlers.
+
+Interrupts and exceptions can have priorities in the range `1..=(1 <<
+NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
+crate. The `idle` task has a priority of `0`, the lowest priority.
+
+Resources that are shared between handlers that run at different priorities
+require critical sections for memory safety. The framework ensures that critical
+sections are used but *only where required*: for example, no critical section is
+required by the highest priority handler that has access to the resource.
+
+The critical section API provided by the RTFM framework (see [`Mutex`]) is
+based on dynamic priorities rather than on disabling interrupts. The consequence
+is that these critical sections will prevent *some* handlers, including all the
+ones that contend for the resource, from *starting* but will let higher priority
+handlers, that don't contend for the resource, run.
+
+[`Mutex`]: ../../api/rtfm/trait.Mutex.html
+
+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. The lowest priority handler needs to [`lock`] the
+`SHARED` resource to access its data, whereas the mid priority handler can
+directly access its data. The highest priority handler is free to preempt
+the critical section created by the lowest priority handler.
+
+[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
+
+``` rust
+{{#include ../../../../examples/lock.rs}}
+```
+
+``` console
+$ cargo run --example lock
+{{#include ../../../../ci/expected/lock.run}}```
+
+## Late resources
+
+Unlike normal `static` variables, which need to be assigned an initial value
+when declared, resources can be initialized at runtime. We refer to these
+runtime initialized resources as *late resources*. Late resources are useful for
+*moving* (as in transferring ownership) peripherals initialized in `init` into
+interrupt and exception handlers.
+
+Late resources are declared like normal resources but that are given an initial
+value of `()` (the unit value). Late resources must be initialized at the end of
+the `init` function using plain assignments (e.g. `FOO = 1`).
+
+The example below uses late resources to stablish a lockless, one-way channel
+between the `UART0` interrupt handler and the `idle` function. A single producer
+single consumer [`Queue`] is used as the channel. The queue is split into
+consumer and producer end points in `init` and then each end point is stored
+in a different resource; `UART0` owns the producer resource and `idle` owns
+the consumer resource.
+
+[`Queue`]: ../../api/heapless/spsc/struct.Queue.html
+
+``` rust
+{{#include ../../../../examples/late.rs}}
+```
+
+``` console
+$ cargo run --example late
+{{#include ../../../../ci/expected/late.run}}```
+
+## `static` resources
+
+`static` variables can also be used as resources. Tasks can only get `&`
+(shared) references to these resources but locks are never required to access
+their data. You can think of `static` resources as plain `static` variables that
+can be initialized at runtime and have better scoping rules: you can control
+which tasks can access the variable, instead of the variable being visible to
+all the functions in the scope it was declared in.
+
+In the example below a key is loaded (or created) at runtime and then used from
+two tasks that run at different priorities.
+
+``` rust
+{{#include ../../../../examples/static.rs}}
+```
+
+``` console
+$ cargo run --example static
+{{#include ../../../../ci/expected/static.run}}```
diff --git a/book/en/src/by-example/singletons.md b/book/en/src/by-example/singletons.md
new file mode 100644
index 0000000..0823f05
--- /dev/null
+++ b/book/en/src/by-example/singletons.md
@@ -0,0 +1,26 @@
+# Singletons
+
+The `app` attribute is aware of [`owned-singleton`] crate and its [`Singleton`]
+attribute. When this attribute is applied to one of the resources the runtime
+will perform the `unsafe` initialization of the singleton for you, ensuring that
+only a single instance of the singleton is ever created.
+
+[`owned-singleton`]: ../../api/owned_singleton/index.html
+[`Singleton`]: ../../api/owned_singleton_macros/attr.Singleton.html
+
+Note that when using the `Singleton` attribute you'll need to have the
+`owned_singleton` in your dependencies.
+
+Below is an example that uses the `Singleton` attribute on a chunk of memory
+and then uses the singleton instance as a fixed-size memory pool using one of
+the [`alloc-singleton`] abstractions.
+
+[`alloc-singleton`]: https://crates.io/crates/alloc-singleton
+
+``` rust
+{{#include ../../../../examples/singleton.rs}}
+```
+
+``` console
+$ cargo run --example singleton
+{{#include ../../../../ci/expected/singleton.run}}```
diff --git a/book/en/src/by-example/tasks.md b/book/en/src/by-example/tasks.md
new file mode 100644
index 0000000..edcdbed
--- /dev/null
+++ b/book/en/src/by-example/tasks.md
@@ -0,0 +1,63 @@
+# Software tasks
+
+RTFM treats interrupt and exception handlers as *hardware* tasks. Hardware tasks
+are invoked by the hardware in response to events, like pressing a button. RTFM
+also supports *software* tasks which can be spawned by the software from any
+execution context.
+
+Software tasks can also be assigned priorities and are dispatched from interrupt
+handlers. RTFM requires that free interrupts are declared in an `extern` block
+when using software tasks; these free interrupts will be used to dispatch the
+software tasks. An advantage of software tasks over hardware tasks is that many
+tasks can be mapped to a single interrupt handler.
+
+Software tasks are declared by applying the `task` attribute to functions. To be
+able to spawn a software task the name of the task must appear in the `spawn`
+argument of the context attribute (`init`, `idle`, `interrupt`, etc.).
+
+The example below showcases three software tasks that run at 2 different
+priorities. The three tasks map to 2 interrupts handlers.
+
+``` rust
+{{#include ../../../../examples/task.rs}}
+```
+
+``` console
+$ cargo run --example task
+{{#include ../../../../ci/expected/task.run}}```
+
+## Message passing
+
+The other advantage of software tasks is that messages can be passed to these
+tasks when spawning them. The type of the message payload must be specified in
+the signature of the task handler.
+
+The example below showcases three tasks, two of them expect a message.
+
+``` rust
+{{#include ../../../../examples/message.rs}}
+```
+
+``` console
+$ cargo run --example message
+{{#include ../../../../ci/expected/message.run}}```
+
+## Capacity
+
+Task dispatchers do *not* use any dynamic memory allocation. The memory required
+to store messages is statically reserved. The framework will reserve enough
+space for every context to be able to spawn each task at most once. This is a
+sensible default but the "inbox" capacity of each task can be controlled using
+the `capacity` argument of the `task` attribute.
+
+The example below sets the capacity of the software task `foo` to 4. If the
+capacity is not specified then the second `spawn.foo` call in `UART0` would
+fail.
+
+``` rust
+{{#include ../../../../examples/capacity.rs}}
+```
+
+``` console
+$ cargo run --example capacity
+{{#include ../../../../ci/expected/capacity.run}}```
diff --git a/book/en/src/by-example/timer-queue.md b/book/en/src/by-example/timer-queue.md
new file mode 100644
index 0000000..167939c
--- /dev/null
+++ b/book/en/src/by-example/timer-queue.md
@@ -0,0 +1,97 @@
+# Timer queue
+
+When the `timer-queue` feature is enabled the RTFM framework includes a *global
+timer queue* that applications can use to *schedule* software tasks to run at
+some time in the future.
+
+> **NOTE**: The timer-queue feature can't be enabled when the target is
+> `thumbv6m-none-eabi` because there's no timer queue support for ARMv6-M. This
+> may change in the future.
+
+> **NOTE**: When the `timer-queue` feature is enabled you will *not* be able to
+> use the `SysTick` exception as a hardware task because the runtime uses it to
+> implement the global timer queue.
+
+To be able to schedule a software task the name of the task must appear in the
+`schedule` argument of the context attribute. When scheduling a task the
+[`Instant`] at which the task should be executed must be passed as the first
+argument of the `schedule` invocation.
+
+[`Instant`]: ../../api/rtfm/struct.Instant.html
+
+The RTFM runtime includes a monotonic, non-decreasing, 32-bit timer which can be
+queried using the `Instant::now` constructor. A [`Duration`] can be added to
+`Instant::now()` to obtain an `Instant` into the future. The monotonic timer is
+disabled while `init` runs so `Instant::now()` always returns the value
+`Instant(0 /* clock cycles */)`; the timer is enabled right before the
+interrupts are re-enabled and `idle` is executed.
+
+[`Duration`]: ../../api/rtfm/struct.Duration.html
+
+The example below schedules two tasks from `init`: `foo` and `bar`. `foo` is
+scheduled to run 8 million clock cycles in the future. Next, `bar` is scheduled
+to run 4 million clock cycles in the future. `bar` runs before `foo` since it
+was scheduled to run first.
+
+> **IMPORTANT**: The examples that use the `schedule` API or the `Instant`
+> abstraction will **not** properly work on QEMU because the Cortex-M cycle
+> counter functionality has not been implemented in `qemu-system-arm`.
+
+``` rust
+{{#include ../../../../examples/schedule.rs}}
+```
+
+Running the program on real hardware produces the following output in the console:
+
+``` text
+{{#include ../../../../ci/expected/schedule.run}}
+```
+
+## Periodic tasks
+
+Software tasks have access to the `Instant` at which they were scheduled to run
+through the `scheduled` variable. This information and the `schedule` API can be
+used to implement periodic tasks as shown in the example below.
+
+``` rust
+{{#include ../../../../examples/periodic.rs}}
+```
+
+This is the output produced by the example. Note that there is zero drift /
+jitter even though `schedule.foo` was invoked at the *end* of `foo`. Using
+`Instant::now` instead of `scheduled` would have resulted in drift / jitter.
+
+``` text
+{{#include ../../../../ci/expected/periodic.run}}
+```
+
+## Baseline
+
+For the tasks scheduled from `init` we have exact information about their
+`scheduled` time. For hardware tasks there's no `scheduled` time because these
+tasks are asynchronous in nature. For hardware tasks the runtime provides a
+`start` time, which indicates the time at which the task handler started
+executing.
+
+Note that `start` is **not** equal to the arrival time of the event that fired
+the task. Depending on the priority of the task and the load of the system the
+`start` time could be very far off from the event arrival time.
+
+What do you think will be the value of `scheduled` for software tasks that are
+*spawned* instead of scheduled? The answer is that spawned tasks inherit the
+*baseline* time of the context that spawned it. The baseline of hardware tasks
+is `start`, the baseline of software tasks is `scheduled` and the baseline of
+`init` is `start = Instant(0)`. `idle` doesn't really have a baseline but tasks
+spawned from it will use `Instant::now()` as their baseline time.
+
+The example below showcases the different meanings of the *baseline*.
+
+``` rust
+{{#include ../../../../examples/baseline.rs}}
+```
+
+Running the program on real hardware produces the following output in the console:
+
+``` text
+{{#include ../../../../ci/expected/baseline.run}}
+```
diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md
new file mode 100644
index 0000000..8f71599
--- /dev/null
+++ b/book/en/src/by-example/tips.md
@@ -0,0 +1,77 @@
+# Tips & tricks
+
+## Generics
+
+Resources shared between two or more tasks implement the `Mutex` trait in *all*
+contexts, even on those where a critical section is not required to access the
+data. This lets you easily write generic code that operates on resources and can
+be called from different tasks. Here's one such example:
+
+``` rust
+{{#include ../../../../examples/generics.rs}}
+```
+
+``` console
+$ cargo run --example generics
+{{#include ../../../../ci/expected/generics.run}}```
+
+This also lets you change the static priorities of tasks without having to
+rewrite code. If you consistently use `lock`s to access the data behind shared
+resources then your code will continue to compile when you change the priority
+of tasks.
+
+## Conditional compilation
+
+You can use conditional compilation (`#[cfg]`) on resources (`static [mut]`
+items) and tasks (`fn` items). The effect of using `#[cfg]` attributes is that
+the resource / task will *not* be injected into the prelude of tasks that use
+them (see `resources`, `spawn` and `schedule`) if the condition doesn't hold.
+
+The example below logs a message whenever the `foo` task is spawned, but only if
+the program has been compiled using the `dev` profile.
+
+``` rust
+{{#include ../../../../examples/cfg.rs}}
+```
+
+## Running tasks from RAM
+
+The main goal of moving the specification of RTFM applications to attributes in
+RTFM v0.4.x 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 very 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 ../../../../examples/ramfunc.rs}}
+```
+
+Running this program produces the expected output.
+
+``` console
+$ cargo run --example ramfunc
+{{#include ../../../../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::'
+{{#include ../../../../ci/expected/ramfunc.grep.foo}}```
+
+``` console
+$ cargo nm --example ramfunc --release | grep ' bar::'
+{{#include ../../../../ci/expected/ramfunc.grep.bar}}```
diff --git a/book/en/src/by-example/types-send-sync.md b/book/en/src/by-example/types-send-sync.md
new file mode 100644
index 0000000..da53cf9
--- /dev/null
+++ b/book/en/src/by-example/types-send-sync.md
@@ -0,0 +1,60 @@
+# Types, Send and Sync
+
+The `app` attribute injects a context, a collection of variables, into every
+function. All these variables have predictable, non-anonymous types so you can
+write plain functions that take them as arguments.
+
+The API reference specifies how these types are generated from the input. You
+can also generate documentation for you binary crate (`cargo doc --bin <name>`);
+in the documentation you'll find `Context` structs (e.g. `init::Context` and
+`idle::Context`) whose fields represent the variables injected into each
+function.
+
+The example below shows the different types generates by the `app` attribute.
+
+``` rust
+{{#include ../../../../examples/types.rs}}
+```
+
+## `Send`
+
+[`Send`] is a marker trait for "types that can be transferred across thread
+boundaries", according to its definition in `core`. In the context of RTFM the
+`Send` trait is only required where it's possible to transfer a value between
+tasks that run at *different* priorities. This occurs in a few places: in message
+passing, in shared `static mut` resources and in the initialization of late
+resources.
+
+[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
+
+The `app` attribute will enforce that `Send` is implemented where required so
+you don't need to worry much about it. It's more important to know where you do
+*not* need the `Send` trait: on types that are transferred between tasks that
+run at the *same* priority. This occurs in two places: in message passing and in
+shared `static mut` resources.
+
+The example below shows where a type that doesn't implement `Send` can be used.
+
+``` rust
+{{#include ../../../../examples/not-send.rs}}
+```
+
+## `Sync`
+
+Similarly, [`Sync`] is a marker trait for "types for which it is safe to share
+references between threads", according to its definition in `core`. In the
+context of RTFM the `Sync` trait is only required where it's possible for two,
+or more, tasks that run at different priority to hold a shared reference to a
+resource. This only occurs with shared `static` resources.
+
+[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
+
+The `app` attribute will enforce that `Sync` is implemented where required but
+it's important to know where the `Sync` bound is not required: in `static`
+resources shared between tasks that run at the *same* priority.
+
+The example below shows where a type that doesn't implement `Sync` can be used.
+
+``` rust
+{{#include ../../../../examples/not-sync.rs}}
+```