mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-16 21:05:35 +01:00
parent
653338e799
commit
c631049efc
154 changed files with 7538 additions and 3276 deletions
5
book/book.toml
Normal file
5
book/book.toml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[book]
|
||||
authors = ["Jorge Aparicio"]
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Real Time For the Masses"
|
||||
16
book/src/SUMMARY.md
Normal file
16
book/src/SUMMARY.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Summary
|
||||
|
||||
[Preface](./preface.md)
|
||||
- [RTFM by example](./by-example.md)
|
||||
- [The `app` attribute](./by-example/app.md)
|
||||
- [Resources](./by-example/resources.md)
|
||||
- [Tasks](./by-example/tasks.md)
|
||||
- [Timer queue](./by-example/timer-queue.md)
|
||||
- [Singletons](./by-example/singletons.md)
|
||||
- [Types, Send and Sync](./by-example/types-send-sync.md)
|
||||
- [Starting a new project](./by-example/new.md)
|
||||
- [Tips & tricks](./by-example/tips.md)
|
||||
- [Under the hood](./internals.md)
|
||||
- [Ceiling analysis](./internals/ceilings.md)
|
||||
- [Task dispatcher](./internals/tasks.md)
|
||||
- [Timer queue](./internals/timer-queue.md)
|
||||
16
book/src/by-example.md
Normal file
16
book/src/by-example.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# RTFM by example
|
||||
|
||||
This part of the book introduces the Real Time For the Masses (RTFM) framework
|
||||
to new users by walking them through examples of increasing complexity.
|
||||
|
||||
All examples in this part of the book can be found in the GitHub [repository] of
|
||||
the project, and most of the examples can be run on QEMU so no special hardware
|
||||
is required to follow along.
|
||||
|
||||
[repository]: https://github.com/japaric/cortex-m-rtfm
|
||||
|
||||
To run the examples on your laptop / PC you'll need the `qemu-system-arm`
|
||||
program. Check [the embedded Rust book] for instructions on how to set up an
|
||||
embedded development environment that includes QEMU.
|
||||
|
||||
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html
|
||||
105
book/src/by-example/app.md
Normal file
105
book/src/by-example/app.md
Normal file
|
|
@ -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 *device* crate 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.
|
||||
67
book/src/by-example/new.md
Normal file
67
book/src/by-example/new.md
Normal file
|
|
@ -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 device crate that was generated using [`svd2rust`] **v0.14.x**, or a
|
||||
board support crate that depends on one such device crate 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 --allow-prerelease --upgrade=none
|
||||
```
|
||||
|
||||
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-beta.1/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}}```
|
||||
119
book/src/by-example/resources.md
Normal file
119
book/src/by-example/resources.md
Normal file
|
|
@ -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}}```
|
||||
26
book/src/by-example/singletons.md
Normal file
26
book/src/by-example/singletons.md
Normal file
|
|
@ -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}}```
|
||||
63
book/src/by-example/tasks.md
Normal file
63
book/src/by-example/tasks.md
Normal file
|
|
@ -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 `capacicy` 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}}```
|
||||
89
book/src/by-example/timer-queue.md
Normal file
89
book/src/by-example/timer-queue.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# 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 some
|
||||
time in the future.
|
||||
|
||||
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 task 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}}
|
||||
```
|
||||
43
book/src/by-example/tips.md
Normal file
43
book/src/by-example/tips.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Tips & tricks
|
||||
|
||||
## 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}}```
|
||||
60
book/src/by-example/types-send-sync.md
Normal file
60
book/src/by-example/types-send-sync.md
Normal file
|
|
@ -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}}
|
||||
```
|
||||
6
book/src/internals.md
Normal file
6
book/src/internals.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Under the hood
|
||||
|
||||
This section describes the internals of the RTFM framework at a *high level*.
|
||||
Low level details like the parsing and code generation done by the procedural
|
||||
macro (`#[app]`) will not be explained here. The focus will be the analysis of
|
||||
the user specification and the data structures used by the runtime.
|
||||
3
book/src/internals/ceilings.md
Normal file
3
book/src/internals/ceilings.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Ceiling analysis
|
||||
|
||||
**TODO**
|
||||
3
book/src/internals/tasks.md
Normal file
3
book/src/internals/tasks.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Task dispatcher
|
||||
|
||||
**TODO**
|
||||
3
book/src/internals/timer-queue.md
Normal file
3
book/src/internals/timer-queue.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Timer queue
|
||||
|
||||
**TODO**
|
||||
12
book/src/preface.md
Normal file
12
book/src/preface.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<h1 align="center">Real Time For the Masses</h1>
|
||||
|
||||
<p align="center">A concurrency framework for building real time systems</p>
|
||||
|
||||
# Preface
|
||||
|
||||
This book contains user level documentation for the Real Time For the Masses
|
||||
(RTFM) framework. The API reference can be found [here](../api/rtfm/index.html).
|
||||
|
||||
{{#include ../../README.md:5:53}}
|
||||
|
||||
{{#include ../../README.md:59:}}
|
||||
Loading…
Add table
Add a link
Reference in a new issue