From 3adc9c31f3fd46af7e3a42a5adba7471452f06e1 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 21 Apr 2019 20:25:59 +0200 Subject: update the book --- book/en/src/SUMMARY.md | 1 - book/en/src/by-example/app.md | 11 +++++------ book/en/src/by-example/new.md | 4 ++-- book/en/src/by-example/resources.md | 12 ++++++------ book/en/src/by-example/singletons.md | 26 -------------------------- book/en/src/by-example/tips.md | 8 +++----- book/en/src/by-example/types-send-sync.md | 3 +-- book/en/src/preface.md | 3 +++ 8 files changed, 20 insertions(+), 48 deletions(-) delete mode 100644 book/en/src/by-example/singletons.md (limited to 'book/en') diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index 051d1ac..bde2b8d 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -6,7 +6,6 @@ - [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) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 996b8c1..d595570 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -28,15 +28,14 @@ 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 -`[unsafe] fn()`. +`fn(init::Context) [-> init::LateResources]`. 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. +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. `static mut` variables declared at the beginning of `init` will be transformed into `&'static mut` references that are safe to access. @@ -61,7 +60,7 @@ $ cargo run --example init 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() - > !`. +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 diff --git a/book/en/src/by-example/new.md b/book/en/src/by-example/new.md index ae49ef2..91b31bb 100644 --- a/book/en/src/by-example/new.md +++ b/book/en/src/by-example/new.md @@ -40,7 +40,7 @@ $ rm memory.x build.rs `timer-queue` feature. ``` console -$ cargo add cortex-m-rtfm +$ cargo add cortex-m-rtfm --allow-prerelease ``` 4. Write your RTFM application. @@ -49,7 +49,7 @@ 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 \ + -L https://github.com/japaric/cortex-m-rtfm/raw/v0.5.0-alpha.1/examples/init.rs \ > src/main.rs ``` diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 17f4d13..06f2f06 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -10,7 +10,7 @@ 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 +context (`init`, `idle`, `interrupt` or `exception`) one 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` @@ -30,7 +30,7 @@ $ cargo run --example resource 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 +runtime takes ownership of the `NVIC` peripheral thus 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. @@ -71,10 +71,10 @@ $ cargo run --example lock 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 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! +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! ## Late resources diff --git a/book/en/src/by-example/singletons.md b/book/en/src/by-example/singletons.md deleted file mode 100644 index 0823f05..0000000 --- a/book/en/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/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index c0bfc56..07a5c0b 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -24,8 +24,8 @@ of tasks. 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 resource / task will *not* be available through the corresponding `Context` +`struct` 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. @@ -37,7 +37,7 @@ the program has been compiled using the `dev` profile. ## 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 +RTFM 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. @@ -78,8 +78,6 @@ $ cargo nm --example ramfunc --release | grep ' bar::' ## `binds` -**NOTE**: Requires RTFM ~0.4.2 - You can give hardware tasks more task-like names using the `binds` argument: you name the function as you wish and specify the name of the interrupt / exception in the `binds` argument. Types like `Spawn` will be placed in a module named diff --git a/book/en/src/by-example/types-send-sync.md b/book/en/src/by-example/types-send-sync.md index 632946b..99f9f19 100644 --- a/book/en/src/by-example/types-send-sync.md +++ b/book/en/src/by-example/types-send-sync.md @@ -7,8 +7,7 @@ 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. +`idle::Context`). The example below shows the different types generates by the `app` attribute. diff --git a/book/en/src/preface.md b/book/en/src/preface.md index d8f64fd..e6a52b7 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -11,6 +11,9 @@ There is a translation of this book in [Russian]. [Russian]: ../ru/index.html +**HEADS UP** This is an **alpha** pre-release; there may be breaking changes in +the API and semantics before a proper release is made. + {{#include ../../../README.md:5:46}} {{#include ../../../README.md:52:}} -- cgit v1.2.3 From ccd7f4586b63841c4bac51f24dc38570c9f89726 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 22 Apr 2019 22:21:46 +0200 Subject: book: indirection for faster message passing --- Cargo.toml | 10 +++++-- book/en/src/by-example/tips.md | 24 +++++++++++++++ ci/expected/pool.run | 2 ++ ci/script.sh | 28 +++++++++++++++++- examples/pool.rs | 67 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 ci/expected/pool.run create mode 100644 examples/pool.rs (limited to 'book/en') diff --git a/Cargo.toml b/Cargo.toml index 236f309..b0df048 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,12 @@ required-features = ["timer-queue"] name = "periodic" required-features = ["timer-queue"] +[[example]] +name = "pool" +# this example doesn't need this feature but only works on ARMv7-M +# specifying the feature here avoids compiling this for ARMv6-M +required-features = ["timer-queue"] + [[example]] name = "schedule" required-features = ["timer-queue"] @@ -39,8 +45,8 @@ cortex-m-rt = "0.6.7" cortex-m-rtfm-macros = { path = "macros", version = "0.5.0-alpha.1" } [dependencies.heapless] -features = ["smaller-atomics"] -version = "0.4.1" +features = ["smaller-atomics", "min-const-fn"] +version = "0.4.3" [dev-dependencies] cortex-m-semihosting = "0.3.2" diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index 07a5c0b..79b9d71 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -89,3 +89,27 @@ after the function, not the interrupt / exception. Example below: ``` console $ cargo run --example binds {{#include ../../../../ci/expected/binds.run}}``` + +## Indirection for faster message passing + +Message passing always involves copying the payload from the sender into a +static variable and then from the static variable into the receiver. Thus +sending a large buffer, like a `[u8; 128]`, as a message involves two expensive +`memcpy`s. To minimize the message passing overhead one can use indirection: +instead of sending the buffer by value, one can send an owning pointer into the +buffer. + +One can use a global allocator to achieve indirection (`alloc::Box`, +`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.34.0, +or one can use a statically allocated memory pool like [`heapless::Pool`]. + +[`heapless::Pool`]: https://docs.rs/heapless/0.4.3/heapless/pool/index.html + +Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. + +``` rust +{{#include ../../../../examples/pool.rs}} +``` +``` console +$ cargo run --example binds +{{#include ../../../../ci/expected/pool.run}}``` diff --git a/ci/expected/pool.run b/ci/expected/pool.run new file mode 100644 index 0000000..040dcee --- /dev/null +++ b/ci/expected/pool.run @@ -0,0 +1,2 @@ +bar(0x2000008c) +foo(0x20000110) diff --git a/ci/script.sh b/ci/script.sh index 00162eb..b64617d 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -110,6 +110,7 @@ main() { shared-with-init generics + pool ramfunc ) @@ -119,6 +120,31 @@ main() { continue fi + if [ $ex = pool ]; then + if [ $TARGET != thumbv6m-none-eabi ]; then + local td=$(mktemp -d) + + local features="$nightly,timer-queue" + cargo run --example $ex --target $TARGET --features $features >\ + $td/pool.run + grep 'foo(0x2' $td/pool.run + grep 'bar(0x2' $td/pool.run + arm-none-eabi-objcopy -O ihex target/$TARGET/debug/examples/$ex \ + ci/builds/${ex}_${features/,/_}_debug_1.hex + + cargo run --example $ex --target $TARGET --features $features --release >\ + $td/pool.run + grep 'foo(0x2' $td/pool.run + grep 'bar(0x2' $td/pool.run + arm-none-eabi-objcopy -O ihex target/$TARGET/release/examples/$ex \ + ci/builds/${ex}_${features/,/_}_release_1.hex + + rm -rf $td + fi + + continue + fi + if [ $ex != types ]; then arm_example "run" $ex "debug" "$nightly" "1" arm_example "run" $ex "release" "$nightly" "1" @@ -138,7 +164,7 @@ main() { continue fi - if [ $ex != types ]; then + if [ $ex != types ] && [ $ex != pool ]; then arm_example "build" $ex "debug" "$nightly" "2" cmp ci/builds/${ex}_${nightly/nightly/nightly_}debug_1.hex \ ci/builds/${ex}_${nightly/nightly/nightly_}debug_2.hex diff --git a/examples/pool.rs b/examples/pool.rs new file mode 100644 index 0000000..0b594b1 --- /dev/null +++ b/examples/pool.rs @@ -0,0 +1,67 @@ +//! examples/pool.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::{debug, hprintln}; +use heapless::{ + pool, + pool::singleton::{Box, Pool}, +}; +use lm3s6965::Interrupt; +use rtfm::app; + +// Declare a pool of 128-byte memory blocks +pool!(P: [u8; 128]); + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init(_: init::Context) { + static mut MEMORY: [u8; 512] = [0; 512]; + + // Increase the capacity of the memory pool by ~4 + P::grow(MEMORY); + + rtfm::pend(Interrupt::I2C0); + } + + #[interrupt(priority = 2, spawn = [foo, bar])] + fn I2C0(c: I2C0::Context) { + // claim a memory block, leave it uninitialized and .. + let x = P::alloc().unwrap().freeze(); + + // .. send it to the `foo` task + c.spawn.foo(x).ok().unwrap(); + + // send another block to the task `bar` + c.spawn.bar(P::alloc().unwrap().freeze()).ok().unwrap(); + } + + #[task] + fn foo(_: foo::Context, x: Box

) { + hprintln!("foo({:?})", x.as_ptr()).unwrap(); + + // explicitly return the block to the pool + drop(x); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2)] + fn bar(_: bar::Context, x: Box

) { + hprintln!("bar({:?})", x.as_ptr()).unwrap(); + + // this is done automatically so we can omit the call to `drop` + // drop(x); + } + + extern "C" { + fn UART0(); + fn UART1(); + } +}; -- cgit v1.2.3