From 0007a35a274ab2d07eb937e41971ea5e2c1cb5ff Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 11 Feb 2019 21:40:53 +0100 Subject: change layout of books --- book/src/by-example/app.md | 105 ----------------------------- book/src/by-example/new.md | 67 ------------------- book/src/by-example/resources.md | 119 --------------------------------- book/src/by-example/singletons.md | 26 ------- book/src/by-example/tasks.md | 63 ----------------- book/src/by-example/timer-queue.md | 97 --------------------------- book/src/by-example/tips.md | 77 --------------------- book/src/by-example/types-send-sync.md | 60 ----------------- 8 files changed, 614 deletions(-) delete mode 100644 book/src/by-example/app.md delete mode 100644 book/src/by-example/new.md delete mode 100644 book/src/by-example/resources.md delete mode 100644 book/src/by-example/singletons.md delete mode 100644 book/src/by-example/tasks.md delete mode 100644 book/src/by-example/timer-queue.md delete mode 100644 book/src/by-example/tips.md delete mode 100644 book/src/by-example/types-send-sync.md (limited to 'book/src/by-example') diff --git a/book/src/by-example/app.md b/book/src/by-example/app.md deleted file mode 100644 index 26aa03f..0000000 --- a/book/src/by-example/app.md +++ /dev/null @@ -1,105 +0,0 @@ -# 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/src/by-example/new.md b/book/src/by-example/new.md deleted file mode 100644 index b7d18a7..0000000 --- a/book/src/by-example/new.md +++ /dev/null @@ -1,67 +0,0 @@ -# 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/src/by-example/resources.md b/book/src/by-example/resources.md deleted file mode 100644 index 93332f0..0000000 --- a/book/src/by-example/resources.md +++ /dev/null @@ -1,119 +0,0 @@ -## 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/src/by-example/singletons.md b/book/src/by-example/singletons.md deleted file mode 100644 index d83cf1c..0000000 --- a/book/src/by-example/singletons.md +++ /dev/null @@ -1,26 +0,0 @@ -# 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/src/by-example/tasks.md b/book/src/by-example/tasks.md deleted file mode 100644 index b950f70..0000000 --- a/book/src/by-example/tasks.md +++ /dev/null @@ -1,63 +0,0 @@ -# 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/src/by-example/timer-queue.md b/book/src/by-example/timer-queue.md deleted file mode 100644 index 2e3decd..0000000 --- a/book/src/by-example/timer-queue.md +++ /dev/null @@ -1,97 +0,0 @@ -# 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/src/by-example/tips.md b/book/src/by-example/tips.md deleted file mode 100644 index 5057c80..0000000 --- a/book/src/by-example/tips.md +++ /dev/null @@ -1,77 +0,0 @@ -# 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/src/by-example/types-send-sync.md b/book/src/by-example/types-send-sync.md deleted file mode 100644 index 6433060..0000000 --- a/book/src/by-example/types-send-sync.md +++ /dev/null @@ -1,60 +0,0 @@ -# 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 `); -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}} -``` -- cgit v1.2.3