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 --- .gitignore | 3 +- book/book.toml | 5 -- book/en/book.toml | 5 ++ book/en/src/SUMMARY.md | 16 ++++ book/en/src/by-example.md | 16 ++++ book/en/src/by-example/app.md | 105 +++++++++++++++++++++++++ book/en/src/by-example/new.md | 67 ++++++++++++++++ book/en/src/by-example/resources.md | 119 +++++++++++++++++++++++++++++ book/en/src/by-example/singletons.md | 26 +++++++ book/en/src/by-example/tasks.md | 63 +++++++++++++++ book/en/src/by-example/timer-queue.md | 97 ++++++++++++++++++++++++ book/en/src/by-example/tips.md | 77 +++++++++++++++++++ book/en/src/by-example/types-send-sync.md | 60 +++++++++++++++ book/en/src/internals.md | 6 ++ book/en/src/internals/ceilings.md | 3 + book/en/src/internals/tasks.md | 3 + book/en/src/internals/timer-queue.md | 3 + book/en/src/preface.md | 16 ++++ book/ru/book.toml | 5 ++ book/ru/src/README_RU.md | 94 +++++++++++++++++++++++ book/ru/src/SUMMARY.md | 16 ++++ book/ru/src/by-example.md | 16 ++++ book/ru/src/by-example/app.md | 101 +++++++++++++++++++++++++ book/ru/src/by-example/new.md | 67 ++++++++++++++++ book/ru/src/by-example/resources.md | 122 ++++++++++++++++++++++++++++++ book/ru/src/by-example/singletons.md | 26 +++++++ book/ru/src/by-example/tasks.md | 63 +++++++++++++++ book/ru/src/by-example/timer-queue.md | 90 ++++++++++++++++++++++ book/ru/src/by-example/tips.md | 63 +++++++++++++++ book/ru/src/by-example/types-send-sync.md | 59 +++++++++++++++ book/ru/src/internals.md | 7 ++ book/ru/src/internals/ceilings.md | 3 + book/ru/src/internals/tasks.md | 3 + book/ru/src/internals/timer-queue.md | 3 + book/ru/src/preface.md | 12 +++ book/src/SUMMARY.md | 16 ---- book/src/by-example.md | 16 ---- 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 --------------- book/src/internals.md | 6 -- book/src/internals/ceilings.md | 3 - book/src/internals/tasks.md | 3 - book/src/internals/timer-queue.md | 3 - book/src/preface.md | 16 ---- ci/script.sh | 16 ++-- ru/book.toml | 5 -- ru/src/README_RU.md | 94 ----------------------- ru/src/SUMMARY.md | 16 ---- ru/src/by-example.md | 16 ---- ru/src/by-example/app.md | 101 ------------------------- ru/src/by-example/new.md | 67 ---------------- ru/src/by-example/resources.md | 122 ------------------------------ ru/src/by-example/singletons.md | 26 ------- ru/src/by-example/tasks.md | 63 --------------- ru/src/by-example/timer-queue.md | 90 ---------------------- ru/src/by-example/tips.md | 63 --------------- ru/src/by-example/types-send-sync.md | 59 --------------- ru/src/internals.md | 7 -- ru/src/internals/ceilings.md | 3 - ru/src/internals/tasks.md | 3 - ru/src/internals/timer-queue.md | 3 - ru/src/preface.md | 12 --- src/lib.rs | 2 +- 69 files changed, 1444 insertions(+), 1441 deletions(-) delete mode 100644 book/book.toml create mode 100644 book/en/book.toml create mode 100644 book/en/src/SUMMARY.md create mode 100644 book/en/src/by-example.md create mode 100644 book/en/src/by-example/app.md create mode 100644 book/en/src/by-example/new.md create mode 100644 book/en/src/by-example/resources.md create mode 100644 book/en/src/by-example/singletons.md create mode 100644 book/en/src/by-example/tasks.md create mode 100644 book/en/src/by-example/timer-queue.md create mode 100644 book/en/src/by-example/tips.md create mode 100644 book/en/src/by-example/types-send-sync.md create mode 100644 book/en/src/internals.md create mode 100644 book/en/src/internals/ceilings.md create mode 100644 book/en/src/internals/tasks.md create mode 100644 book/en/src/internals/timer-queue.md create mode 100644 book/en/src/preface.md create mode 100644 book/ru/book.toml create mode 100644 book/ru/src/README_RU.md create mode 100644 book/ru/src/SUMMARY.md create mode 100644 book/ru/src/by-example.md create mode 100644 book/ru/src/by-example/app.md create mode 100644 book/ru/src/by-example/new.md create mode 100644 book/ru/src/by-example/resources.md create mode 100644 book/ru/src/by-example/singletons.md create mode 100644 book/ru/src/by-example/tasks.md create mode 100644 book/ru/src/by-example/timer-queue.md create mode 100644 book/ru/src/by-example/tips.md create mode 100644 book/ru/src/by-example/types-send-sync.md create mode 100644 book/ru/src/internals.md create mode 100644 book/ru/src/internals/ceilings.md create mode 100644 book/ru/src/internals/tasks.md create mode 100644 book/ru/src/internals/timer-queue.md create mode 100644 book/ru/src/preface.md delete mode 100644 book/src/SUMMARY.md delete mode 100644 book/src/by-example.md 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 delete mode 100644 book/src/internals.md delete mode 100644 book/src/internals/ceilings.md delete mode 100644 book/src/internals/tasks.md delete mode 100644 book/src/internals/timer-queue.md delete mode 100644 book/src/preface.md delete mode 100644 ru/book.toml delete mode 100644 ru/src/README_RU.md delete mode 100644 ru/src/SUMMARY.md delete mode 100644 ru/src/by-example.md delete mode 100644 ru/src/by-example/app.md delete mode 100644 ru/src/by-example/new.md delete mode 100644 ru/src/by-example/resources.md delete mode 100644 ru/src/by-example/singletons.md delete mode 100644 ru/src/by-example/tasks.md delete mode 100644 ru/src/by-example/timer-queue.md delete mode 100644 ru/src/by-example/tips.md delete mode 100644 ru/src/by-example/types-send-sync.md delete mode 100644 ru/src/internals.md delete mode 100644 ru/src/internals/ceilings.md delete mode 100644 ru/src/internals/tasks.md delete mode 100644 ru/src/internals/timer-queue.md delete mode 100644 ru/src/preface.md diff --git a/.gitignore b/.gitignore index 9a7ec5f..0c0491e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ **/*.rs.bk .#* .gdb_history -/book/book -/ru/book +/book/*/book /target Cargo.lock diff --git a/book/book.toml b/book/book.toml deleted file mode 100644 index c611ce0..0000000 --- a/book/book.toml +++ /dev/null @@ -1,5 +0,0 @@ -[book] -authors = ["Jorge Aparicio"] -multilingual = false -src = "src" -title = "Real Time For the Masses" diff --git a/book/en/book.toml b/book/en/book.toml new file mode 100644 index 0000000..c611ce0 --- /dev/null +++ b/book/en/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["Jorge Aparicio"] +multilingual = false +src = "src" +title = "Real Time For the Masses" diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md new file mode 100644 index 0000000..051d1ac --- /dev/null +++ b/book/en/src/SUMMARY.md @@ -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) diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md new file mode 100644 index 0000000..0e09b03 --- /dev/null +++ b/book/en/src/by-example.md @@ -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 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 `); +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}} +``` diff --git a/book/en/src/internals.md b/book/en/src/internals.md new file mode 100644 index 0000000..0ef55e6 --- /dev/null +++ b/book/en/src/internals.md @@ -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. diff --git a/book/en/src/internals/ceilings.md b/book/en/src/internals/ceilings.md new file mode 100644 index 0000000..2c645a4 --- /dev/null +++ b/book/en/src/internals/ceilings.md @@ -0,0 +1,3 @@ +# Ceiling analysis + +**TODO** diff --git a/book/en/src/internals/tasks.md b/book/en/src/internals/tasks.md new file mode 100644 index 0000000..85f783f --- /dev/null +++ b/book/en/src/internals/tasks.md @@ -0,0 +1,3 @@ +# Task dispatcher + +**TODO** diff --git a/book/en/src/internals/timer-queue.md b/book/en/src/internals/timer-queue.md new file mode 100644 index 0000000..7059285 --- /dev/null +++ b/book/en/src/internals/timer-queue.md @@ -0,0 +1,3 @@ +# Timer queue + +**TODO** diff --git a/book/en/src/preface.md b/book/en/src/preface.md new file mode 100644 index 0000000..d8f64fd --- /dev/null +++ b/book/en/src/preface.md @@ -0,0 +1,16 @@ +

Real Time For the Masses

+ +

A concurrency framework for building real time systems

+ +# 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). + +There is a translation of this book in [Russian]. + +[Russian]: ../ru/index.html + +{{#include ../../../README.md:5:46}} + +{{#include ../../../README.md:52:}} diff --git a/book/ru/book.toml b/book/ru/book.toml new file mode 100644 index 0000000..c611ce0 --- /dev/null +++ b/book/ru/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["Jorge Aparicio"] +multilingual = false +src = "src" +title = "Real Time For the Masses" diff --git a/book/ru/src/README_RU.md b/book/ru/src/README_RU.md new file mode 100644 index 0000000..921837a --- /dev/null +++ b/book/ru/src/README_RU.md @@ -0,0 +1,94 @@ +# Real Time For the Masses + +Конкурентный фреймворк для создания систем реального времени. + +## Возможности + +- **Задачи** - единица конкуренции [^1]. Задачи могут *запускаться по событию* + (в ответ на асинхронный стимул) или вызываться программно по желанию. + +- **Передача сообщений** между задачами. А именно, сообщения можно передавать + программным задачам в момент вызова. + +- **Очередь таймера** [^2]. Программные задачи можно планировать на запуск в + определенный момент в будущем. Это свойство можно использовать, чтобы + реализовывать периодические задачи. + +- Поддержка приоритетов задач, и таким образом, **вытесняющей многозадачности**. + +- **Эффективное, свободное от гонок данных разделение памяти** через хорошо + разграниченные критические секции на *основе приоритетов* [^1]. + +- **Выполнение без взаимной блокировки задач**, гарантированное на этапе + компиляции. Это более сильная гарантия, чем предоставляемая + [стандартной абстракцией `Mutex`][std-mutex]. + +[std-mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html + +- **Минимальные затраты на диспетчеризацию**. Диспетчер задач имеет + минимальный след; основная часть работы по диспетчеризации делается аппаратно. + +- **Высокоэффективное использование памяти**: Все задачи используют общий стек + вызовов и нет сильной зависимости от динамического распределителя памяти. + +- **Все устройства Cortex-M полностью поддерживаются**. + +- Эта модель задач поддается известному анализу методом WCET (наихудшего + времени исполнения) и техникам анализа диспетчеризации. (Хотя мы еще не + разработали для дружественных инструментов для этого). + +## Требования + +- Rust 1.31.0+ + +- Программы нужно писать используя 2018 edition. + +## [User documentation](https://japaric.github.io/cortex-m-rtfm/book) + +## [API reference](https://japaric.github.io/cortex-m-rtfm/api/rtfm/index.html) + +## Благодарности + +Эта библиотека основана на [языке RTFM][rtfm-lang], созданном Embedded +Systems group в [Техническом Университете Luleå][ltu], под рук. +[Prof. Per Lindgren][per]. + +[rtfm-lang]: http://www.rtfm-lang.org/ +[ltu]: https://www.ltu.se/?l=en +[per]: https://www.ltu.se/staff/p/pln-1.11258?l=en + +## Ссылки + +[^1]: Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P. + (2013, June). Real-time for the masses, step 1: Programming API and static + priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013 + 8th IEEE International Symposium on (pp. 110-113). IEEE. + +[^2]: Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho, + L. M. (2016). Abstract timers and their implementation onto the arm cortex-m + family of mcus. ACM SIGBED Review, 13(1), 48-53. + +## Лицензия + +Все исходные тексты (включая примеры кода) лицензированы либо под: + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) или + [https://www.apache.org/licenses/LICENSE-2.0][L1]) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or + [https://opensource.org/licenses/MIT][L2]) + +[L1]: https://www.apache.org/licenses/LICENSE-2.0 +[L2]: https://opensource.org/licenses/MIT + +на Ваше усмотрение. + +Текст книги лицензирован по условиям лицензий +Creative Commons CC-BY-SA v4.0 ([LICENSE-CC-BY-SA](LICENSE-CC-BY-SA) или +[https://creativecommons.org/licenses/by-sa/4.0/legalcode][L3]). + +[L3]: https://creativecommons.org/licenses/by-sa/4.0/legalcode + +### Contribution + +Если вы явно не заявляете иначе, любой взнос, преднамеренно представленный +для включения в эту работу, как определено в лицензии Apache-2.0, лицензируется, как указано выше, без каких-либо дополнительных условий. diff --git a/book/ru/src/SUMMARY.md b/book/ru/src/SUMMARY.md new file mode 100644 index 0000000..7831e41 --- /dev/null +++ b/book/ru/src/SUMMARY.md @@ -0,0 +1,16 @@ +# Summary + +[Введение](./preface.md) +- [RTFM в примерах](./by-example.md) + - [Атрибут `app`](./by-example/app.md) + - [Ресурсы](./by-example/resources.md) + - [Задачи](./by-example/tasks.md) + - [Очередь таймера](./by-example/timer-queue.md) + - [Одиночки](./by-example/singletons.md) + - [Типы, Send и Sync](./by-example/types-send-sync.md) + - [Создание нового проекта](./by-example/new.md) + - [Советы и хитрости](./by-example/tips.md) +- [Под капотом](./internals.md) + - [Ceiling analysis](./internals/ceilings.md) + - [Диспетчер задач](./internals/tasks.md) + - [Очередь таймера](./internals/timer-queue.md) diff --git a/book/ru/src/by-example.md b/book/ru/src/by-example.md new file mode 100644 index 0000000..c7a2a4a --- /dev/null +++ b/book/ru/src/by-example.md @@ -0,0 +1,16 @@ +# RTFM в примерах + +Эта часть книги представляет фреймворк Real Time For the Masses (RTFM) +новым пользователям через примеры с растущей сложностью. + +Все примеры в этой книге можно найти в [репозитории] проекта на GitHub, +и большинство примеров можно запустить на эмуляторе QEMU, поэтому никакого +специального оборудования не требуется их выполнять. + +[репозитории]: https://github.com/japaric/cortex-m-rtfm + +Чтобы запустить примеры на Вашем ноутбуке / ПК, Вам нужна программа +`qemu-system-arm`. Инструкции по настройке окружения для разработки +встраиваемых устройств, в том числе QEMU, Вы можете найти в [the embedded Rust book]. + +[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html diff --git a/book/ru/src/by-example/app.md b/book/ru/src/by-example/app.md new file mode 100644 index 0000000..18147dc --- /dev/null +++ b/book/ru/src/by-example/app.md @@ -0,0 +1,101 @@ +# The `app` attribute + +Это наименьшая возможная программа на RTFM: + +``` rust +{{#include ../../../../examples/smallest.rs}} +``` + +Все программы на RTFM используют атрибут [`app`] (`#[app(..)]`). Этот атрибут +нужно применять к `const`-элементам, содержащим элементы. Атрибут `app` имеет +обязательный аргумент `device`, в качестве значения которому передается *путь*. +Этот путь должен указывать на библиотеку *устройства*, сгенерированную с помощью +[`svd2rust`] **v0.14.x**. Атрибут `app` развернется в удобную точку входа, +поэтому нет необходимости использовать атрибут [`cortex_m_rt::entry`]. + +[`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 + +> **ОТСТУПЛЕНИЕ**: Некоторые из вас удивятся, почему мы используем ключевое слово `const` как +> модуль, а не правильное `mod`. Причина в том, что использование атрибутов на +> модулях требует feature gate, который требует ночную сборку. Чтобы заставить +> RTFM работать на стабильной сборке, мы используем вместо него слово `const`. +> Когда большая часть макросов 1.2 стабилизируются, мы прейдем от `const` к `mod` и в конце концов в атрибуту уровне приложения (`#![app]`). + +## `init` + +Внутри псевдо-модуля атрибут `app` ожидает найти функцию инициализации, обозначенную +атрибутом `init`. Эта функция должна иметь сигнатуру `[unsafe] fn()`. + +Эта функция инициализации будет первой частью запускаемого приложения. +Функция `init` запустится *с отключенными прерываниями* и будет иметь эксклюзивный +доступ к периферии Cortex-M и специфичной для устройства периферии через переменные +`core` and `device`, которые внедряются в область видимости `init` атрибутом `app`. +Не вся периферия Cortex-M доступна в `core`, потому что рантайм RTFM принимает владение +частью из неё -- более подробно см. структуру [`rtfm::Peripherals`]. + +Переменные `static mut`, определённые в начале `init` будут преобразованы +в ссылки `&'static mut` с безопасным доступом. + +[`rtfm::Peripherals`]: ../../api/rtfm/struct.Peripherals.html + +Пример ниже показывает типы переменных `core` и `device` и +демонстрирует безопасный доступ к переменной `static mut`. + +``` rust +{{#include ../../../../examples/init.rs}} +``` + +Запуск примера напечатает `init` в консоли и завершит процесс QEMU. + +``` console +$ cargo run --example init +{{#include ../../../../ci/expected/init.run}}``` + +## `idle` + +Функция, помеченная атрибутом `idle` может присутствовать в псевдо-модуле +опционально. Эта функция используется как специальная *задача ожидания* и должна иметь +сигнатуру `[unsafe] fn() - > !`. + +Когда она присутствует, рантайм запустит задачу `idle` после `init`. В отличие от +`init`, `idle` запустится *с включенными прерываниями* и не может завершиться, +поэтому будет работать бесконечно. + +Когда функция `idle` определена, рантайм устанавливает бит [SLEEPONEXIT], после чего +отправляет микроконтроллер в состояние сна после выполнения `init`. + +[SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit + +Как и в `init`, переменные `static mut`будут преобразованы в ссылки `&'static mut` +с безопасным доступом. + +В примере ниже показан запуск `idle` после `init`. + +``` rust +{{#include ../../../../examples/idle.rs}} +``` + +``` console +$ cargo run --example idle +{{#include ../../../../ci/expected/idle.run}}``` + +## `interrupt` / `exception` + +Как Вы бы сделали с помощью библиотеки `cortex-m-rt`, Вы можете использовать атрибуты +`interrupt` и `exception` внутри псевдо-модуля `app`, чтобы определить обработчики +прерываний и исключений. В RTFM, мы называем обработчики прерываний и исключений +*аппаратными* задачами. + +``` rust +{{#include ../../../../examples/interrupt.rs}} +``` + +``` console +$ cargo run --example interrupt +{{#include ../../../../ci/expected/interrupt.run}}``` + +До сих пор программы RTFM, которые мы видели не отличались от программ, которые +можно написать, используя только библиотеку `cortex-m-rt`. В следующем разделе +мы начнем знакомиться с функционалом, присущим только RTFM. diff --git a/book/ru/src/by-example/new.md b/book/ru/src/by-example/new.md new file mode 100644 index 0000000..688935e --- /dev/null +++ b/book/ru/src/by-example/new.md @@ -0,0 +1,67 @@ +# Создание нового проекта + +Теперь, когда Вы изучили основные возможности фреймворка RTFM, Вы можете +попробовать его использовать на Вашем оборудовании следуя этим инструкциям. + +1. Создайте экземпляр из шаблона [`cortex-m-quickstart`]. + +[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart#cortex-m-quickstart + +``` console +$ # например используя `cargo-generate` +$ cargo generate \ + --git https://github.com/rust-embedded/cortex-m-quickstart \ + --name app + +$ # следуйте остальным инструкциям +``` + +2. Добавьте крейт устройства, сгенерированный с помощью [`svd2rust`] **v0.14.x**, +или библиотеку отладочной платы, у которой в зависимостях одно из устройств. + Убедитесь, что опция `rt` крейта включена. + +[`svd2rust`]: https://crates.io/crates/svd2rust + +В этом примере я покажу использование крейта устройства [`lm3s6965`]. +Эта библиотека не имеет Cargo-опции `rt`; эта опция всегда включена. + +[`lm3s6965`]: https://crates.io/crates/lm3s6965 + +Этот крейт устройства предоставляет линковочный скрипт с макетом памяти +целевого устройства, поэтому `memory.x` и `build.rs` не нужно удалять. + +``` console +$ cargo add lm3s6965 --vers 0.1.3 + +$ rm memory.x build.rs +``` + +3. Добавьте библиотеку `cortex-m-rtfm` как зависимость, и если необходимо, +включите опцию `timer-queue`. + +``` console +$ cargo add cortex-m-rtfm --allow-prerelease --upgrade=none +``` + +4. Напишите программу RTFM. + +Здесь я буду использовать пример `init` из библиотеки `cortex-m-rtfm`. + +``` console +$ curl \ + -L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0-beta.1/examples/init.rs \ + > src/main.rs +``` + +Этот пример зависит от библиотеки `panic-semihosting`: + +``` console +$ cargo add panic-semihosting +``` + +5. Соберите его, загрузите в микроконтроллер и запустите. + +``` console +$ # ПРИМЕЧАНИЕ: Я раскомментировал опцию `runner` в `.cargo/config` +$ cargo run +{{#include ../../../../ci/expected/init.run}}``` diff --git a/book/ru/src/by-example/resources.md b/book/ru/src/by-example/resources.md new file mode 100644 index 0000000..3e8a606 --- /dev/null +++ b/book/ru/src/by-example/resources.md @@ -0,0 +1,122 @@ +## Ресурсы + +Одно из ограничений атрибутов, предоставляемых библиотекой `cortex-m-rt` является +то, что совместное использование данных (или периферии) между прерываниями, +или прерыванием и функцией `init`, требуют `cortex_m::interrupt::Mutex`, который +*всегда* требует отключения *всех* прерываний для доступа к данным. Отключение всех +прерываний не всегда необходимо для безопасности памяти, но компилятор не имеет +достаточно информации, чтобы оптимизировать доступ к разделяемым данным. + +Атрибут `app` имеет полную картину приложения, поэтому может оптимизировать доступ к +`static`-переменным. В RTFM мы обращаемся к `static`-переменным, объявленным внутри +псевдо-модуля `app` как к *ресурсам*. Чтобы получить доступ к ресурсу, контекст +(`init`, `idle`, `interrupt` или `exception`) должен сначала определить +аргумент `resources` в соответствующем атрибуте. + +В примере ниже два обработчика прерываний имеют доступ к одному и тому же ресурсу. +Никакого `Mutex` в этом случае не требуется, потому что оба обработчика запускаются +с одним приоритетом и никакого вытеснения быть не может. +К ресурсу `SHARED` можно получить доступ только из этих двух прерываний. + +``` rust +{{#include ../../../../examples/resource.rs}} +``` + +``` console +$ cargo run --example resource +{{#include ../../../../ci/expected/resource.run}} +``` + +## Приоритеты + +Приоритет каждого прерывания можно определить в атрибутах `interrupt` и `exception`. +Невозможно установить приоритет любым другим способом, потому что рантайм +забирает владение прерыванием `NVIC`; также невозможно изменить приоритет +обработчика / задачи в рантайме. Благодаря этому ограничению у фреймворка +есть знание о *статических* приоритетах всех обработчиков прерываний и исключений. + +Прерывания и исключения могут иметь приоритеты в интервале `1..=(1 << NVIC_PRIO_BITS)`, +где `NVIC_PRIO_BITS` - константа, определённая в библиотеке `device`. +Задача `idle` имеет приоритет `0`, наименьший. + +Ресурсы, совместно используемые обработчиками, работающими на разных приоритетах, +требуют критических секций для безопасности памяти. Фреймворк проверяет, что +критические секции используются, но *только где необходимы*: например, +критические секции не нужны для обработчика с наивысшим приоритетом, имеющим +доступ к ресурсу. + +API критической секции, предоставляемое фреймворком RTFM (см. [`Mutex`]), +основано на динамических приоритетах вместо отключения прерываний. Из этого следует, +что критические секции не будут допускать *запуск некоторых* обработчиков, +включая все соперничающие за ресурс, но будут позволять запуск обработчиков с +большим приоритетом не соперничащих за ресурс. + +[`Mutex`]: ../../api/rtfm/trait.Mutex.html + +В примере ниже у нас есть 3 обработчика прерываний с приоритетами от одного +до трех. Два обработчика с низким приоритетом соперничают за ресурс `SHARED`. +Обработчик с низшим приоритетом должен заблокировать ([`lock`]) ресурс +`SHARED`, чтобы получить доступ к его данным, в то время как обработчик со +средним приоритетом может напрямую получать доступ к его данным. Обработчик +с наивысшим приоритетом может свободно вытеснять критическую секцию, +созданную обработчиком с низшим приоритетом. + +[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock + +``` rust +{{#include ../../../../examples/lock.rs}} +``` + +``` console +$ cargo run --example lock +{{#include ../../../../ci/expected/lock.run}}``` + +## Поздние ресурсы + +В отличие от обычных `static`-переменных, к которым должно быть присвоено +начальное значение, ресурсы можно инициализировать в рантайме. +Мы называем ресурсы, инициализируемые в рантайме *поздними*. Поздние ресурсы +полезны для *переноса* (как при передаче владения) периферии из `init` в +обработчики прерываний и исключений. + +Поздние ресурсы определяются как обычные ресурсы, но им присваивается начальное +значение `()` (the unit value). Поздние ресурсы необходимо инициализировать в конце +функции `init` используя обычное присвоение (например `FOO = 1`). + +В примере ниже использованы поздние ресурсы, чтобы установить неблокированный, +односторонний канал между обработчиком прерывания `UART0` и функцией `idle`. +Очередь типа один производитель-один потребитель [`Queue`] использована как канал. +Очередь разделена на элементы потребителя и поизводителя в `init` и каждый элемент +расположен в отдельном ресурсе; `UART0` владеет ресурсом произодителя, а `idle` +владеет ресурсом потребителя. + +[`Queue`]: ../../api/heapless/spsc/struct.Queue.html + +``` rust +{{#include ../../../../examples/late.rs}} +``` + +``` console +$ cargo run --example late +{{#include ../../../../ci/expected/late.run}}``` + +## `static`-ресурсы + +Переменные типа `static` также можно использовать в качестве ресурсов. Задачи +могут получать только (разделяемые) `&` ссылки на ресурсы, но блокировки не +нужны для доступа к данным. Вы можете думать о `static`-ресурсах как о простых +`static`-переменных, которые можно инициализировать в рантайме и иметь лучшие +правила видимости: Вы можете контролировать, какие задачи получают доступ к +переменной, чтобы переменная не была видна всем фунциям в область видимости, +где она была объявлена. + +В примере ниже ключ загружен (или создан) в рантайме, а затем использован в двух +задачах, запущенных на разных приоритетах. + +``` rust +{{#include ../../../../examples/static.rs}} +``` + +``` console +$ cargo run --example static +{{#include ../../../../ci/expected/static.run}}``` diff --git a/book/ru/src/by-example/singletons.md b/book/ru/src/by-example/singletons.md new file mode 100644 index 0000000..d6d60ef --- /dev/null +++ b/book/ru/src/by-example/singletons.md @@ -0,0 +1,26 @@ +# Одиночки + +Атрибут `app` знает о библиотеке [`owned-singleton`] и её атрибуте [`Singleton`]. +Когда этот атрибут применяется к одному из ресурсов, рантайм производит для Вас +`unsafe` инициализацию одиночки, проверяя, что только один экземпляр одиночки +когда-либо создан. + +[`owned-singleton`]: ../../api/owned_singleton/index.html +[`Singleton`]: ../../api/owned_singleton_macros/attr.Singleton.html + +Заметьте, что когда Вы используете атрибут `Singleton`, Вым нужно иметь +`owned_singleton` в зависимостях. + +В примере ниже атрибутом `Singleton` аннотирован массив памяти, +а экземпляр одиночки использован как фиксированный по размеру пул памяти +с помощью одной из абстракций [`alloc-singleton`]. + +[`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/ru/src/by-example/tasks.md b/book/ru/src/by-example/tasks.md new file mode 100644 index 0000000..355bd4e --- /dev/null +++ b/book/ru/src/by-example/tasks.md @@ -0,0 +1,63 @@ +# Программные задачи + +RTFM обрабатывает прерывания и исключения как *аппаратные* задачи. Аппаратные +задачи могут вызываться устройством в ответ на события, такие как нажатие кнопки. +RTFM также поддерживает *программные* задачи, порождаемые программой из любого +контекста выполнения. + +Программным задачам также можно назначать приоритет и диспетчеризовать из +обработчиков прерываний. RTFM требует определения свободных прерываний в блоке +`extern`, когда используются программные задачи; эти свободные прерывания будут использованы, чтобы диспетчеризовать программные задачи. Преимущество программных +задач перед аппаратными в том, что на один обработчик прерывания можно назначить +множество задач. + +Программные задачи определяются заданием функциям атрибута `task`. Чтобы было +возможно вызывать программные задачи, имя задачи нужно передать в аргументе +`spawn` контекста атрибута (`init`, `idle`, `interrupt`, etc.). + +В примере ниже продемонстрированы три программных задачи, запускаемые на 2-х +разных приоритетах. Трем задачам назначены 2 обработчика прерываний. + +``` rust +{{#include ../../../../examples/task.rs}} +``` + +``` console +$ cargo run --example task +{{#include ../../../../ci/expected/task.run}}``` + +## Передача сообщений + +Другое преимущество программных задач - возможность передавать сообщения задачам +во время их вызова. Тип полезной нагрузки сообщения должен быть определен в +сигнатуре обработчика задачи. + +Пример ниже демонстрирует три задачи, две из которых ожидают сообщения. + +``` rust +{{#include ../../../../examples/message.rs}} +``` + +``` console +$ cargo run --example message +{{#include ../../../../ci/expected/message.run}}``` + +## Ёмкость + +Диспетчеры задач *не* используют динамическое выделение памяти. Память +необходимая для размещения сообщений, резервируется статически. Фреймворк +зарезервирует достаточно памяти для каждого контекста, чтобы можно было вызвать +каждую задачу как минимум единожды. Это разумно по умолчанию, но +"внутреннюю" ёмкость каждой задачи можно контролировать используя аргумент +`capacity` атрибута `task`. + +В примере ниже установлена ёмкость программной задачи `foo` на 4. Если ёмкость +не определена, тогда второй вызов `spawn.foo` в `UART0` вызовет ошибку. + +``` rust +{{#include ../../../../examples/capacity.rs}} +``` + +``` console +$ cargo run --example capacity +{{#include ../../../../ci/expected/capacity.run}}``` diff --git a/book/ru/src/by-example/timer-queue.md b/book/ru/src/by-example/timer-queue.md new file mode 100644 index 0000000..8995bd0 --- /dev/null +++ b/book/ru/src/by-example/timer-queue.md @@ -0,0 +1,90 @@ +# Очередь таймера + +Когда включена опция `timer-queue`, фреймворк RTFM включает +*глобальную очередь таймера*, которую приложения могут использовать, чтобы +*планировать* программные задачи на запуск через некоторое время в будущем. + +Чтобы была возможность планировать программную задачу, имя задачи должно +присутствовать в аргументе `schedule` контекста атрибута. Когда задача +планируется, момент ([`Instant`]), в который задачу нужно запустить, нужно передать +как первый аргумент вызова `schedule`. + +[`Instant`]: ../../api/rtfm/struct.Instant.html + +Рантайм RTFM включает монотонный, растущий только вверх, 32-битный таймер, +значение которого можно запросить конструктором `Instant::now`. Время ([`Duration`]) +можно передать в `Instant::now()`, чтобы получить `Instant` в будущем. Монотонный +таймер отключен пока запущен `init`, поэтому `Instant::now()` всегда возвращает +значение `Instant(0 /* циклов тактовой частоты */)`; таймер включается сразу перед +включением прерываний и запуском `idle`. + +[`Duration`]: ../../api/rtfm/struct.Duration.html + +В примере ниже две задачи планируются из `init`: `foo` и `bar`. `foo` - +запланирована на запуск через 8 миллионов тактов в будущем. Кроме того, `bar` +запланирован на запуск через 4 миллиона тактов в будущем. `bar` запустится раньше +`foo`, т.к. он запланирован на запуск первым. + +> **ВАЖНО**: Примеры, использующие API `schedule` или абстракцию `Instant` +> **не** будут правильно работать на QEMU, потому что функциональность счетчика +> тактов Cortex-M не реализована в `qemu-system-arm`. + +``` rust +{{#include ../../../../examples/schedule.rs}} +``` + +Запуск программы на реальном оборудовании производит следующий вывод в консоли: + +``` text +{{#include ../../../../ci/expected/schedule.run}} +``` + +## Периодические задачи + +Программные задачи имеют доступ к `Instant` в момент, когда были запланированы +на запуск через переменную `scheduled`. Эта информация и API `schedule` могут +быть использованы для реализации периодических задач, как показано в примере ниже. + +``` rust +{{#include ../../../../examples/periodic.rs}} +``` + +Это вывод, произведенный примером. Заметьте, что есть смещение / колебание нуля +даже если `schedule.foo` была вызвана в *конце* `foo`. Использование +`Instant::now` вместо `scheduled` имело бы влияние на смещение / колебание. + +``` text +{{#include ../../../../ci/expected/periodic.run}} +``` + +## Базовое время + +Для задач, планируемых из `init` мы имеем точную информацию о их планируемом +(`scheduled`) времени. Для аппаратных задач нет `scheduled` времени, потому +что эти задачи асинхронны по природе. Для аппаратных задач рантайм предоставляет +время старта (`start`), которе отражает время, в которое обработчик прерывания +был запущен. + +Заметьте, что `start` **не** равен времени возникновения события, вызвавшего +задачу. В зависимости от приоритета задачи и загрузки системы время +`start` может быть сильно отдалено от времени возникновения события. + +Какое по Вашему мнению будет значение `scheduled` для программных задач которые +*вызываются*, вместо того чтобы планироваться? Ответ в том, что вызываемые +задачи наследуют *базовое* время контекста, в котором вызваны. Бызовым для +аппаратных задач является `start`, базовым для программных задач - `scheduled` +и базовым для `init` - `start = Instant(0)`. `idle` на сомом деле не имеет +базового времени но задачи, вызванные из него будут использовать `Instant::now()` +как их базовое время. + +Пример ниже демонстрирует разное значение *базового времени*. + +``` rust +{{#include ../../../../examples/baseline.rs}} +``` + +Запуск программы на реальном оборудовании произведет следующий вывод в консоли: + +``` text +{{#include ../../../../ci/expected/baseline.run}} +``` diff --git a/book/ru/src/by-example/tips.md b/book/ru/src/by-example/tips.md new file mode 100644 index 0000000..0294512 --- /dev/null +++ b/book/ru/src/by-example/tips.md @@ -0,0 +1,63 @@ +# Советы и хитрости + +## Обобщенное программирование (Generics) + +Ресурсы, совместно используемые двумя или более задачами, реализуют трейт `Mutex` +во *всех* контекстах, даже в тех, где для доступа к данным не требуются +критические секции. Это позволяет легко писать обобщенный код оперирующий +ресурсами, который можно вызывать из различных задач. Вот такой пример: + +``` rust +{{#include ../../../../examples/generics.rs}} +``` + +``` console +$ cargo run --example generics +{{#include ../../../../ci/expected/generics.run}}``` + +Это также позволяет Вам изменять статические приоритеты задач без +переписывания кода. Если Вы единообразно используете `lock`-и для доступа +к данным в разделяемых ресурсах, тогда Ваш код продолжит компилироваться, +когда Вы измените приоритет задач. + +## Запуск задач из ОЗУ + +Главной целью переноса описания программы на RTFM в атрибуты в +RTFM v0.4.x была возможность взаимодействия с другими атрибутами. +Напримерe, атрибут `link_section` можно применять к задачам, чтобы разместить +их в ОЗУ; это может улучшить производительность в некоторых случаях. + +> **ВАЖНО**: Обычно атрибуты `link_section`, `export_name` и `no_mangle` +> очень мощные, но их легко использовать неправильно. Неверное использование +> любого из этих атрибутов может вызвать неопределенное поведение; +> Вам следует всегда предпочитать использование безопасных, высокоуровневых +> атрибутов вокруг них, таких как атрибуты `interrupt` и `exception` +> из `cortex-m-rt`. +> +> В особых случаях функций RAM нет безопасной абстракции в `cortex-m-rt` +> v0.6.5 но создано [RFC] для добавления атрибута `ramfunc` в будущем релизе. + +[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 + +В примере ниже показано как разместить высокоприоритетную задачу `bar` в ОЗУ. + +``` rust +{{#include ../../../../examples/ramfunc.rs}} +``` + +Запуск этой программы произведет ожидаемый вывод. + +``` console +$ cargo run --example ramfunc +{{#include ../../../../ci/expected/ramfunc.run}}``` + +Можно посмотреть на вывод `cargo-nm`, чтобы убедиться, что `bar` расположен в ОЗУ +(`0x2000_0000`), тогда как `foo` расположен во 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/ru/src/by-example/types-send-sync.md b/book/ru/src/by-example/types-send-sync.md new file mode 100644 index 0000000..77c9af0 --- /dev/null +++ b/book/ru/src/by-example/types-send-sync.md @@ -0,0 +1,59 @@ +# Типы, Send и Sync + +Атрибут `app` вводит контекст, коллекцию переменных в каждую из функций. +Все эти переменные имеют предсказуемые, неанонимные типы, поэтому Вы можете +писать простые функции, получающие их как аргументы. + +Описание API определяет как эти типы эти типы генерируются из входных данных. +Вы можете также сгенерировать документацию для Вашей бинарной библиотеки +(`cargo doc --bin `); в документации Вы найдете структуры `Context` +(например `init::Context` и `idle::Context`), чьи поля представляют переменные +включенные в каждую функцию. + +В примере ниже сгенерированы разные типы с помощью атрибута `app`. + +``` rust +{{#include ../../../../examples/types.rs}} +``` + +## `Send` + +[`Send`] - маркерный типаж (trait) для "типов, которые можно передавать через границы +потоков", как это определено в `core`. В контексте RTFM типаж `Send` необходим +только там, где возможна передача значения между задачами, запускаемыми на +*разных* приоритетах. Это возникает в нескольких случаях: при передаче сообщений, +в совместно используемых `static mut` ресурсах и инициализации поздних ресурсов. + +[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html + +Атрибут `app` проверит, что `Send` реализован, где необходимо, поэтому Вам не +стоит волноваться об этом. Более важно знать, где Вам *не* нужен типаж `Send`: +в типах, передаваемых между задачами с *одинаковым* приоритетом. Это возникает +в двух случаях: при передаче сообщений и в совместно используемых `static mut` +ресурсах. + +В примере ниже показано, где можно использовать типы, не реализующие `Send`. + +``` rust +{{#include ../../../../examples/not-send.rs}} +``` + +## `Sync` + +Похожая ситуация, [`Sync`] - маркерный типаж для "типов, на которых можно +ссылаться в разных потоках", как это определено в `core`. В контексте RTFM +типаж `Sync` необходим только там, где возможны две или более задачи, +запускаемые на разных приоритетах, чтобы захватить разделяемую ссылку на +ресурс. Это возникает только совместно используемых `static`-ресурсах. + +[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html + +Атрибут `app` проверит, что `Sync` реализован, где необходимо, но важно знать, +где ограничение `Sync` не требуется: в `static`-ресурсах, разделяемых между +задачами с *одинаковым* приоритетом. + +В примере ниже показано, где можно использовать типы, не реализующие `Sync`. + +``` rust +{{#include ../../../../examples/not-sync.rs}} +``` diff --git a/book/ru/src/internals.md b/book/ru/src/internals.md new file mode 100644 index 0000000..4a47e77 --- /dev/null +++ b/book/ru/src/internals.md @@ -0,0 +1,7 @@ +# Под капотом + +В этом разделе описывабтся внутренности фркймворка на *высоком уровне*. +Низкоуровневые тонкости, такие как парсинг и кодогенерация производимые +процедурным макросом (`#[app]`) здесь объясняться не будут. Мы сосредоточимся +на анализе пользовательской спецификации и структурах данных, используемых +рантаймом. diff --git a/book/ru/src/internals/ceilings.md b/book/ru/src/internals/ceilings.md new file mode 100644 index 0000000..2c645a4 --- /dev/null +++ b/book/ru/src/internals/ceilings.md @@ -0,0 +1,3 @@ +# Ceiling analysis + +**TODO** diff --git a/book/ru/src/internals/tasks.md b/book/ru/src/internals/tasks.md new file mode 100644 index 0000000..85f783f --- /dev/null +++ b/book/ru/src/internals/tasks.md @@ -0,0 +1,3 @@ +# Task dispatcher + +**TODO** diff --git a/book/ru/src/internals/timer-queue.md b/book/ru/src/internals/timer-queue.md new file mode 100644 index 0000000..7059285 --- /dev/null +++ b/book/ru/src/internals/timer-queue.md @@ -0,0 +1,3 @@ +# Timer queue + +**TODO** diff --git a/book/ru/src/preface.md b/book/ru/src/preface.md new file mode 100644 index 0000000..ee01d10 --- /dev/null +++ b/book/ru/src/preface.md @@ -0,0 +1,12 @@ +

Real Time For the Masses

+ +

Конкурентный фреймворк для создания систем реального времени

+ +# Введение + +Эта книга содержит документацию уровня пользователя фреймворком Real Time For the Masses +(RTFM). Описание API можно найти [здесь](../api/rtfm/index.html). + +{{#include ../..ADME_RU.md:5:54}} + +{{#include ../..ADME_RU.md:60:}} diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md deleted file mode 100644 index 051d1ac..0000000 --- a/book/src/SUMMARY.md +++ /dev/null @@ -1,16 +0,0 @@ -# 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) diff --git a/book/src/by-example.md b/book/src/by-example.md deleted file mode 100644 index 0e09b03..0000000 --- a/book/src/by-example.md +++ /dev/null @@ -1,16 +0,0 @@ -# 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 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}} -``` diff --git a/book/src/internals.md b/book/src/internals.md deleted file mode 100644 index 0ef55e6..0000000 --- a/book/src/internals.md +++ /dev/null @@ -1,6 +0,0 @@ -# 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. diff --git a/book/src/internals/ceilings.md b/book/src/internals/ceilings.md deleted file mode 100644 index 2c645a4..0000000 --- a/book/src/internals/ceilings.md +++ /dev/null @@ -1,3 +0,0 @@ -# Ceiling analysis - -**TODO** diff --git a/book/src/internals/tasks.md b/book/src/internals/tasks.md deleted file mode 100644 index 85f783f..0000000 --- a/book/src/internals/tasks.md +++ /dev/null @@ -1,3 +0,0 @@ -# Task dispatcher - -**TODO** diff --git a/book/src/internals/timer-queue.md b/book/src/internals/timer-queue.md deleted file mode 100644 index 7059285..0000000 --- a/book/src/internals/timer-queue.md +++ /dev/null @@ -1,3 +0,0 @@ -# Timer queue - -**TODO** diff --git a/book/src/preface.md b/book/src/preface.md deleted file mode 100644 index 9bc0401..0000000 --- a/book/src/preface.md +++ /dev/null @@ -1,16 +0,0 @@ -

Real Time For the Masses

- -

A concurrency framework for building real time systems

- -# 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). - -There is a translation of this book in [Russian]. - -[Russian]: ru/index.html - -{{#include ../../README.md:5:46}} - -{{#include ../../README.md:52:}} diff --git a/ci/script.sh b/ci/script.sh index 8878d92..645db3a 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -23,15 +23,19 @@ main() { else cargo doc fi - ( cd book && mdbook build ) - ( cd ru && mdbook build ) + ( cd book/en && mdbook build ) + ( cd book/ru && mdbook build ) local td=$(mktemp -d) cp -r target/doc $td/api - cp -r book/book $td/ - cp LICENSE-* $td/book/ - - linkchecker $td/book/ + mkdir $td/book + cp -r book/en/book $td/book/en + cp -r book/ru/book $td/book/ru + cp LICENSE-* $td/book/en + cp LICENSE-* $td/book/ru + + linkchecker $td/book/en/ + linkchecker $td/book/ru/ linkchecker $td/api/rtfm/ linkchecker $td/api/cortex_m_rtfm_macros/ fi diff --git a/ru/book.toml b/ru/book.toml deleted file mode 100644 index c611ce0..0000000 --- a/ru/book.toml +++ /dev/null @@ -1,5 +0,0 @@ -[book] -authors = ["Jorge Aparicio"] -multilingual = false -src = "src" -title = "Real Time For the Masses" diff --git a/ru/src/README_RU.md b/ru/src/README_RU.md deleted file mode 100644 index 921837a..0000000 --- a/ru/src/README_RU.md +++ /dev/null @@ -1,94 +0,0 @@ -# Real Time For the Masses - -Конкурентный фреймворк для создания систем реального времени. - -## Возможности - -- **Задачи** - единица конкуренции [^1]. Задачи могут *запускаться по событию* - (в ответ на асинхронный стимул) или вызываться программно по желанию. - -- **Передача сообщений** между задачами. А именно, сообщения можно передавать - программным задачам в момент вызова. - -- **Очередь таймера** [^2]. Программные задачи можно планировать на запуск в - определенный момент в будущем. Это свойство можно использовать, чтобы - реализовывать периодические задачи. - -- Поддержка приоритетов задач, и таким образом, **вытесняющей многозадачности**. - -- **Эффективное, свободное от гонок данных разделение памяти** через хорошо - разграниченные критические секции на *основе приоритетов* [^1]. - -- **Выполнение без взаимной блокировки задач**, гарантированное на этапе - компиляции. Это более сильная гарантия, чем предоставляемая - [стандартной абстракцией `Mutex`][std-mutex]. - -[std-mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html - -- **Минимальные затраты на диспетчеризацию**. Диспетчер задач имеет - минимальный след; основная часть работы по диспетчеризации делается аппаратно. - -- **Высокоэффективное использование памяти**: Все задачи используют общий стек - вызовов и нет сильной зависимости от динамического распределителя памяти. - -- **Все устройства Cortex-M полностью поддерживаются**. - -- Эта модель задач поддается известному анализу методом WCET (наихудшего - времени исполнения) и техникам анализа диспетчеризации. (Хотя мы еще не - разработали для дружественных инструментов для этого). - -## Требования - -- Rust 1.31.0+ - -- Программы нужно писать используя 2018 edition. - -## [User documentation](https://japaric.github.io/cortex-m-rtfm/book) - -## [API reference](https://japaric.github.io/cortex-m-rtfm/api/rtfm/index.html) - -## Благодарности - -Эта библиотека основана на [языке RTFM][rtfm-lang], созданном Embedded -Systems group в [Техническом Университете Luleå][ltu], под рук. -[Prof. Per Lindgren][per]. - -[rtfm-lang]: http://www.rtfm-lang.org/ -[ltu]: https://www.ltu.se/?l=en -[per]: https://www.ltu.se/staff/p/pln-1.11258?l=en - -## Ссылки - -[^1]: Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P. - (2013, June). Real-time for the masses, step 1: Programming API and static - priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013 - 8th IEEE International Symposium on (pp. 110-113). IEEE. - -[^2]: Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho, - L. M. (2016). Abstract timers and their implementation onto the arm cortex-m - family of mcus. ACM SIGBED Review, 13(1), 48-53. - -## Лицензия - -Все исходные тексты (включая примеры кода) лицензированы либо под: - -- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) или - [https://www.apache.org/licenses/LICENSE-2.0][L1]) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or - [https://opensource.org/licenses/MIT][L2]) - -[L1]: https://www.apache.org/licenses/LICENSE-2.0 -[L2]: https://opensource.org/licenses/MIT - -на Ваше усмотрение. - -Текст книги лицензирован по условиям лицензий -Creative Commons CC-BY-SA v4.0 ([LICENSE-CC-BY-SA](LICENSE-CC-BY-SA) или -[https://creativecommons.org/licenses/by-sa/4.0/legalcode][L3]). - -[L3]: https://creativecommons.org/licenses/by-sa/4.0/legalcode - -### Contribution - -Если вы явно не заявляете иначе, любой взнос, преднамеренно представленный -для включения в эту работу, как определено в лицензии Apache-2.0, лицензируется, как указано выше, без каких-либо дополнительных условий. diff --git a/ru/src/SUMMARY.md b/ru/src/SUMMARY.md deleted file mode 100644 index 7831e41..0000000 --- a/ru/src/SUMMARY.md +++ /dev/null @@ -1,16 +0,0 @@ -# Summary - -[Введение](./preface.md) -- [RTFM в примерах](./by-example.md) - - [Атрибут `app`](./by-example/app.md) - - [Ресурсы](./by-example/resources.md) - - [Задачи](./by-example/tasks.md) - - [Очередь таймера](./by-example/timer-queue.md) - - [Одиночки](./by-example/singletons.md) - - [Типы, Send и Sync](./by-example/types-send-sync.md) - - [Создание нового проекта](./by-example/new.md) - - [Советы и хитрости](./by-example/tips.md) -- [Под капотом](./internals.md) - - [Ceiling analysis](./internals/ceilings.md) - - [Диспетчер задач](./internals/tasks.md) - - [Очередь таймера](./internals/timer-queue.md) diff --git a/ru/src/by-example.md b/ru/src/by-example.md deleted file mode 100644 index c7a2a4a..0000000 --- a/ru/src/by-example.md +++ /dev/null @@ -1,16 +0,0 @@ -# RTFM в примерах - -Эта часть книги представляет фреймворк Real Time For the Masses (RTFM) -новым пользователям через примеры с растущей сложностью. - -Все примеры в этой книге можно найти в [репозитории] проекта на GitHub, -и большинство примеров можно запустить на эмуляторе QEMU, поэтому никакого -специального оборудования не требуется их выполнять. - -[репозитории]: https://github.com/japaric/cortex-m-rtfm - -Чтобы запустить примеры на Вашем ноутбуке / ПК, Вам нужна программа -`qemu-system-arm`. Инструкции по настройке окружения для разработки -встраиваемых устройств, в том числе QEMU, Вы можете найти в [the embedded Rust book]. - -[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html diff --git a/ru/src/by-example/app.md b/ru/src/by-example/app.md deleted file mode 100644 index bcd778d..0000000 --- a/ru/src/by-example/app.md +++ /dev/null @@ -1,101 +0,0 @@ -# The `app` attribute - -Это наименьшая возможная программа на RTFM: - -``` rust -{{#include ../../../examples/smallest.rs}} -``` - -Все программы на RTFM используют атрибут [`app`] (`#[app(..)]`). Этот атрибут -нужно применять к `const`-элементам, содержащим элементы. Атрибут `app` имеет -обязательный аргумент `device`, в качестве значения которому передается *путь*. -Этот путь должен указывать на библиотеку *устройства*, сгенерированную с помощью -[`svd2rust`] **v0.14.x**. Атрибут `app` развернется в удобную точку входа, -поэтому нет необходимости использовать атрибут [`cortex_m_rt::entry`]. - -[`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 - -> **ОТСТУПЛЕНИЕ**: Некоторые из вас удивятся, почему мы используем ключевое слово `const` как -> модуль, а не правильное `mod`. Причина в том, что использование атрибутов на -> модулях требует feature gate, который требует ночную сборку. Чтобы заставить -> RTFM работать на стабильной сборке, мы используем вместо него слово `const`. -> Когда большая часть макросов 1.2 стабилизируются, мы прейдем от `const` к `mod` и в конце концов в атрибуту уровне приложения (`#![app]`). - -## `init` - -Внутри псевдо-модуля атрибут `app` ожидает найти функцию инициализации, обозначенную -атрибутом `init`. Эта функция должна иметь сигнатуру `[unsafe] fn()`. - -Эта функция инициализации будет первой частью запускаемого приложения. -Функция `init` запустится *с отключенными прерываниями* и будет иметь эксклюзивный -доступ к периферии Cortex-M и специфичной для устройства периферии через переменные -`core` and `device`, которые внедряются в область видимости `init` атрибутом `app`. -Не вся периферия Cortex-M доступна в `core`, потому что рантайм RTFM принимает владение -частью из неё -- более подробно см. структуру [`rtfm::Peripherals`]. - -Переменные `static mut`, определённые в начале `init` будут преобразованы -в ссылки `&'static mut` с безопасным доступом. - -[`rtfm::Peripherals`]: ../../api/rtfm/struct.Peripherals.html - -Пример ниже показывает типы переменных `core` и `device` и -демонстрирует безопасный доступ к переменной `static mut`. - -``` rust -{{#include ../../../examples/init.rs}} -``` - -Запуск примера напечатает `init` в консоли и завершит процесс QEMU. - -``` console -$ cargo run --example init -{{#include ../../../ci/expected/init.run}}``` - -## `idle` - -Функция, помеченная атрибутом `idle` может присутствовать в псевдо-модуле -опционально. Эта функция используется как специальная *задача ожидания* и должна иметь -сигнатуру `[unsafe] fn() - > !`. - -Когда она присутствует, рантайм запустит задачу `idle` после `init`. В отличие от -`init`, `idle` запустится *с включенными прерываниями* и не может завершиться, -поэтому будет работать бесконечно. - -Когда функция `idle` определена, рантайм устанавливает бит [SLEEPONEXIT], после чего -отправляет микроконтроллер в состояние сна после выполнения `init`. - -[SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit - -Как и в `init`, переменные `static mut`будут преобразованы в ссылки `&'static mut` -с безопасным доступом. - -В примере ниже показан запуск `idle` после `init`. - -``` rust -{{#include ../../../examples/idle.rs}} -``` - -``` console -$ cargo run --example idle -{{#include ../../../ci/expected/idle.run}}``` - -## `interrupt` / `exception` - -Как Вы бы сделали с помощью библиотеки `cortex-m-rt`, Вы можете использовать атрибуты -`interrupt` и `exception` внутри псевдо-модуля `app`, чтобы определить обработчики -прерываний и исключений. В RTFM, мы называем обработчики прерываний и исключений -*аппаратными* задачами. - -``` rust -{{#include ../../../examples/interrupt.rs}} -``` - -``` console -$ cargo run --example interrupt -{{#include ../../../ci/expected/interrupt.run}}``` - -До сих пор программы RTFM, которые мы видели не отличались от программ, которые -можно написать, используя только библиотеку `cortex-m-rt`. В следующем разделе -мы начнем знакомиться с функционалом, присущим только RTFM. diff --git a/ru/src/by-example/new.md b/ru/src/by-example/new.md deleted file mode 100644 index f3aa455..0000000 --- a/ru/src/by-example/new.md +++ /dev/null @@ -1,67 +0,0 @@ -# Создание нового проекта - -Теперь, когда Вы изучили основные возможности фреймворка RTFM, Вы можете -попробовать его использовать на Вашем оборудовании следуя этим инструкциям. - -1. Создайте экземпляр из шаблона [`cortex-m-quickstart`]. - -[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart#cortex-m-quickstart - -``` console -$ # например используя `cargo-generate` -$ cargo generate \ - --git https://github.com/rust-embedded/cortex-m-quickstart \ - --name app - -$ # следуйте остальным инструкциям -``` - -2. Добавьте крейт устройства, сгенерированный с помощью [`svd2rust`] **v0.14.x**, -или библиотеку отладочной платы, у которой в зависимостях одно из устройств. - Убедитесь, что опция `rt` крейта включена. - -[`svd2rust`]: https://crates.io/crates/svd2rust - -В этом примере я покажу использование крейта устройства [`lm3s6965`]. -Эта библиотека не имеет Cargo-опции `rt`; эта опция всегда включена. - -[`lm3s6965`]: https://crates.io/crates/lm3s6965 - -Этот крейт устройства предоставляет линковочный скрипт с макетом памяти -целевого устройства, поэтому `memory.x` и `build.rs` не нужно удалять. - -``` console -$ cargo add lm3s6965 --vers 0.1.3 - -$ rm memory.x build.rs -``` - -3. Добавьте библиотеку `cortex-m-rtfm` как зависимость, и если необходимо, -включите опцию `timer-queue`. - -``` console -$ cargo add cortex-m-rtfm --allow-prerelease --upgrade=none -``` - -4. Напишите программу RTFM. - -Здесь я буду использовать пример `init` из библиотеки `cortex-m-rtfm`. - -``` console -$ curl \ - -L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0-beta.1/examples/init.rs \ - > src/main.rs -``` - -Этот пример зависит от библиотеки `panic-semihosting`: - -``` console -$ cargo add panic-semihosting -``` - -5. Соберите его, загрузите в микроконтроллер и запустите. - -``` console -$ # ПРИМЕЧАНИЕ: Я раскомментировал опцию `runner` в `.cargo/config` -$ cargo run -{{#include ../../../ci/expected/init.run}}``` diff --git a/ru/src/by-example/resources.md b/ru/src/by-example/resources.md deleted file mode 100644 index 096c803..0000000 --- a/ru/src/by-example/resources.md +++ /dev/null @@ -1,122 +0,0 @@ -## Ресурсы - -Одно из ограничений атрибутов, предоставляемых библиотекой `cortex-m-rt` является -то, что совместное использование данных (или периферии) между прерываниями, -или прерыванием и функцией `init`, требуют `cortex_m::interrupt::Mutex`, который -*всегда* требует отключения *всех* прерываний для доступа к данным. Отключение всех -прерываний не всегда необходимо для безопасности памяти, но компилятор не имеет -достаточно информации, чтобы оптимизировать доступ к разделяемым данным. - -Атрибут `app` имеет полную картину приложения, поэтому может оптимизировать доступ к -`static`-переменным. В RTFM мы обращаемся к `static`-переменным, объявленным внутри -псевдо-модуля `app` как к *ресурсам*. Чтобы получить доступ к ресурсу, контекст -(`init`, `idle`, `interrupt` или `exception`) должен сначала определить -аргумент `resources` в соответствующем атрибуте. - -В примере ниже два обработчика прерываний имеют доступ к одному и тому же ресурсу. -Никакого `Mutex` в этом случае не требуется, потому что оба обработчика запускаются -с одним приоритетом и никакого вытеснения быть не может. -К ресурсу `SHARED` можно получить доступ только из этих двух прерываний. - -``` rust -{{#include ../../../examples/resource.rs}} -``` - -``` console -$ cargo run --example resource -{{#include ../../../ci/expected/resource.run}} -``` - -## Приоритеты - -Приоритет каждого прерывания можно определить в атрибутах `interrupt` и `exception`. -Невозможно установить приоритет любым другим способом, потому что рантайм -забирает владение прерыванием `NVIC`; также невозможно изменить приоритет -обработчика / задачи в рантайме. Благодаря этому ограничению у фреймворка -есть знание о *статических* приоритетах всех обработчиков прерываний и исключений. - -Прерывания и исключения могут иметь приоритеты в интервале `1..=(1 << NVIC_PRIO_BITS)`, -где `NVIC_PRIO_BITS` - константа, определённая в библиотеке `device`. -Задача `idle` имеет приоритет `0`, наименьший. - -Ресурсы, совместно используемые обработчиками, работающими на разных приоритетах, -требуют критических секций для безопасности памяти. Фреймворк проверяет, что -критические секции используются, но *только где необходимы*: например, -критические секции не нужны для обработчика с наивысшим приоритетом, имеющим -доступ к ресурсу. - -API критической секции, предоставляемое фреймворком RTFM (см. [`Mutex`]), -основано на динамических приоритетах вместо отключения прерываний. Из этого следует, -что критические секции не будут допускать *запуск некоторых* обработчиков, -включая все соперничающие за ресурс, но будут позволять запуск обработчиков с -большим приоритетом не соперничащих за ресурс. - -[`Mutex`]: ../../api/rtfm/trait.Mutex.html - -В примере ниже у нас есть 3 обработчика прерываний с приоритетами от одного -до трех. Два обработчика с низким приоритетом соперничают за ресурс `SHARED`. -Обработчик с низшим приоритетом должен заблокировать ([`lock`]) ресурс -`SHARED`, чтобы получить доступ к его данным, в то время как обработчик со -средним приоритетом может напрямую получать доступ к его данным. Обработчик -с наивысшим приоритетом может свободно вытеснять критическую секцию, -созданную обработчиком с низшим приоритетом. - -[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock - -``` rust -{{#include ../../../examples/lock.rs}} -``` - -``` console -$ cargo run --example lock -{{#include ../../../ci/expected/lock.run}}``` - -## Поздние ресурсы - -В отличие от обычных `static`-переменных, к которым должно быть присвоено -начальное значение, ресурсы можно инициализировать в рантайме. -Мы называем ресурсы, инициализируемые в рантайме *поздними*. Поздние ресурсы -полезны для *переноса* (как при передаче владения) периферии из `init` в -обработчики прерываний и исключений. - -Поздние ресурсы определяются как обычные ресурсы, но им присваивается начальное -значение `()` (the unit value). Поздние ресурсы необходимо инициализировать в конце -функции `init` используя обычное присвоение (например `FOO = 1`). - -В примере ниже использованы поздние ресурсы, чтобы установить неблокированный, -односторонний канал между обработчиком прерывания `UART0` и функцией `idle`. -Очередь типа один производитель-один потребитель [`Queue`] использована как канал. -Очередь разделена на элементы потребителя и поизводителя в `init` и каждый элемент -расположен в отдельном ресурсе; `UART0` владеет ресурсом произодителя, а `idle` -владеет ресурсом потребителя. - -[`Queue`]: ../../api/heapless/spsc/struct.Queue.html - -``` rust -{{#include ../../../examples/late.rs}} -``` - -``` console -$ cargo run --example late -{{#include ../../../ci/expected/late.run}}``` - -## `static`-ресурсы - -Переменные типа `static` также можно использовать в качестве ресурсов. Задачи -могут получать только (разделяемые) `&` ссылки на ресурсы, но блокировки не -нужны для доступа к данным. Вы можете думать о `static`-ресурсах как о простых -`static`-переменных, которые можно инициализировать в рантайме и иметь лучшие -правила видимости: Вы можете контролировать, какие задачи получают доступ к -переменной, чтобы переменная не была видна всем фунциям в область видимости, -где она была объявлена. - -В примере ниже ключ загружен (или создан) в рантайме, а затем использован в двух -задачах, запущенных на разных приоритетах. - -``` rust -{{#include ../../../examples/static.rs}} -``` - -``` console -$ cargo run --example static -{{#include ../../../ci/expected/static.run}}``` diff --git a/ru/src/by-example/singletons.md b/ru/src/by-example/singletons.md deleted file mode 100644 index 23ed167..0000000 --- a/ru/src/by-example/singletons.md +++ /dev/null @@ -1,26 +0,0 @@ -# Одиночки - -Атрибут `app` знает о библиотеке [`owned-singleton`] и её атрибуте [`Singleton`]. -Когда этот атрибут применяется к одному из ресурсов, рантайм производит для Вас -`unsafe` инициализацию одиночки, проверяя, что только один экземпляр одиночки -когда-либо создан. - -[`owned-singleton`]: ../../api/owned_singleton/index.html -[`Singleton`]: ../../api/owned_singleton_macros/attr.Singleton.html - -Заметьте, что когда Вы используете атрибут `Singleton`, Вым нужно иметь -`owned_singleton` в зависимостях. - -В примере ниже атрибутом `Singleton` аннотирован массив памяти, -а экземпляр одиночки использован как фиксированный по размеру пул памяти -с помощью одной из абстракций [`alloc-singleton`]. - -[`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/ru/src/by-example/tasks.md b/ru/src/by-example/tasks.md deleted file mode 100644 index f0596d3..0000000 --- a/ru/src/by-example/tasks.md +++ /dev/null @@ -1,63 +0,0 @@ -# Программные задачи - -RTFM обрабатывает прерывания и исключения как *аппаратные* задачи. Аппаратные -задачи могут вызываться устройством в ответ на события, такие как нажатие кнопки. -RTFM также поддерживает *программные* задачи, порождаемые программой из любого -контекста выполнения. - -Программным задачам также можно назначать приоритет и диспетчеризовать из -обработчиков прерываний. RTFM требует определения свободных прерываний в блоке -`extern`, когда используются программные задачи; эти свободные прерывания будут использованы, чтобы диспетчеризовать программные задачи. Преимущество программных -задач перед аппаратными в том, что на один обработчик прерывания можно назначить -множество задач. - -Программные задачи определяются заданием функциям атрибута `task`. Чтобы было -возможно вызывать программные задачи, имя задачи нужно передать в аргументе -`spawn` контекста атрибута (`init`, `idle`, `interrupt`, etc.). - -В примере ниже продемонстрированы три программных задачи, запускаемые на 2-х -разных приоритетах. Трем задачам назначены 2 обработчика прерываний. - -``` rust -{{#include ../../../examples/task.rs}} -``` - -``` console -$ cargo run --example task -{{#include ../../../ci/expected/task.run}}``` - -## Передача сообщений - -Другое преимущество программных задач - возможность передавать сообщения задачам -во время их вызова. Тип полезной нагрузки сообщения должен быть определен в -сигнатуре обработчика задачи. - -Пример ниже демонстрирует три задачи, две из которых ожидают сообщения. - -``` rust -{{#include ../../../examples/message.rs}} -``` - -``` console -$ cargo run --example message -{{#include ../../../ci/expected/message.run}}``` - -## Ёмкость - -Диспетчеры задач *не* используют динамическое выделение памяти. Память -необходимая для размещения сообщений, резервируется статически. Фреймворк -зарезервирует достаточно памяти для каждого контекста, чтобы можно было вызвать -каждую задачу как минимум единожды. Это разумно по умолчанию, но -"внутреннюю" ёмкость каждой задачи можно контролировать используя аргумент -`capacity` атрибута `task`. - -В примере ниже установлена ёмкость программной задачи `foo` на 4. Если ёмкость -не определена, тогда второй вызов `spawn.foo` в `UART0` вызовет ошибку. - -``` rust -{{#include ../../../examples/capacity.rs}} -``` - -``` console -$ cargo run --example capacity -{{#include ../../../ci/expected/capacity.run}}``` diff --git a/ru/src/by-example/timer-queue.md b/ru/src/by-example/timer-queue.md deleted file mode 100644 index 939192f..0000000 --- a/ru/src/by-example/timer-queue.md +++ /dev/null @@ -1,90 +0,0 @@ -# Очередь таймера - -Когда включена опция `timer-queue`, фреймворк RTFM включает -*глобальную очередь таймера*, которую приложения могут использовать, чтобы -*планировать* программные задачи на запуск через некоторое время в будущем. - -Чтобы была возможность планировать программную задачу, имя задачи должно -присутствовать в аргументе `schedule` контекста атрибута. Когда задача -планируется, момент ([`Instant`]), в который задачу нужно запустить, нужно передать -как первый аргумент вызова `schedule`. - -[`Instant`]: ../../api/rtfm/struct.Instant.html - -Рантайм RTFM включает монотонный, растущий только вверх, 32-битный таймер, -значение которого можно запросить конструктором `Instant::now`. Время ([`Duration`]) -можно передать в `Instant::now()`, чтобы получить `Instant` в будущем. Монотонный -таймер отключен пока запущен `init`, поэтому `Instant::now()` всегда возвращает -значение `Instant(0 /* циклов тактовой частоты */)`; таймер включается сразу перед -включением прерываний и запуском `idle`. - -[`Duration`]: ../../api/rtfm/struct.Duration.html - -В примере ниже две задачи планируются из `init`: `foo` и `bar`. `foo` - -запланирована на запуск через 8 миллионов тактов в будущем. Кроме того, `bar` -запланирован на запуск через 4 миллиона тактов в будущем. `bar` запустится раньше -`foo`, т.к. он запланирован на запуск первым. - -> **ВАЖНО**: Примеры, использующие API `schedule` или абстракцию `Instant` -> **не** будут правильно работать на QEMU, потому что функциональность счетчика -> тактов Cortex-M не реализована в `qemu-system-arm`. - -``` rust -{{#include ../../../examples/schedule.rs}} -``` - -Запуск программы на реальном оборудовании производит следующий вывод в консоли: - -``` text -{{#include ../../../ci/expected/schedule.run}} -``` - -## Периодические задачи - -Программные задачи имеют доступ к `Instant` в момент, когда были запланированы -на запуск через переменную `scheduled`. Эта информация и API `schedule` могут -быть использованы для реализации периодических задач, как показано в примере ниже. - -``` rust -{{#include ../../../examples/periodic.rs}} -``` - -Это вывод, произведенный примером. Заметьте, что есть смещение / колебание нуля -даже если `schedule.foo` была вызвана в *конце* `foo`. Использование -`Instant::now` вместо `scheduled` имело бы влияние на смещение / колебание. - -``` text -{{#include ../../../ci/expected/periodic.run}} -``` - -## Базовое время - -Для задач, планируемых из `init` мы имеем точную информацию о их планируемом -(`scheduled`) времени. Для аппаратных задач нет `scheduled` времени, потому -что эти задачи асинхронны по природе. Для аппаратных задач рантайм предоставляет -время старта (`start`), которе отражает время, в которое обработчик прерывания -был запущен. - -Заметьте, что `start` **не** равен времени возникновения события, вызвавшего -задачу. В зависимости от приоритета задачи и загрузки системы время -`start` может быть сильно отдалено от времени возникновения события. - -Какое по Вашему мнению будет значение `scheduled` для программных задач которые -*вызываются*, вместо того чтобы планироваться? Ответ в том, что вызываемые -задачи наследуют *базовое* время контекста, в котором вызваны. Бызовым для -аппаратных задач является `start`, базовым для программных задач - `scheduled` -и базовым для `init` - `start = Instant(0)`. `idle` на сомом деле не имеет -базового времени но задачи, вызванные из него будут использовать `Instant::now()` -как их базовое время. - -Пример ниже демонстрирует разное значение *базового времени*. - -``` rust -{{#include ../../../examples/baseline.rs}} -``` - -Запуск программы на реальном оборудовании произведет следующий вывод в консоли: - -``` text -{{#include ../../../ci/expected/baseline.run}} -``` diff --git a/ru/src/by-example/tips.md b/ru/src/by-example/tips.md deleted file mode 100644 index ab0f1b5..0000000 --- a/ru/src/by-example/tips.md +++ /dev/null @@ -1,63 +0,0 @@ -# Советы и хитрости - -## Обобщенное программирование (Generics) - -Ресурсы, совместно используемые двумя или более задачами, реализуют трейт `Mutex` -во *всех* контекстах, даже в тех, где для доступа к данным не требуются -критические секции. Это позволяет легко писать обобщенный код оперирующий -ресурсами, который можно вызывать из различных задач. Вот такой пример: - -``` rust -{{#include ../../../examples/generics.rs}} -``` - -``` console -$ cargo run --example generics -{{#include ../../../ci/expected/generics.run}}``` - -Это также позволяет Вам изменять статические приоритеты задач без -переписывания кода. Если Вы единообразно используете `lock`-и для доступа -к данным в разделяемых ресурсах, тогда Ваш код продолжит компилироваться, -когда Вы измените приоритет задач. - -## Запуск задач из ОЗУ - -Главной целью переноса описания программы на RTFM в атрибуты в -RTFM v0.4.x была возможность взаимодействия с другими атрибутами. -Напримерe, атрибут `link_section` можно применять к задачам, чтобы разместить -их в ОЗУ; это может улучшить производительность в некоторых случаях. - -> **ВАЖНО**: Обычно атрибуты `link_section`, `export_name` и `no_mangle` -> очень мощные, но их легко использовать неправильно. Неверное использование -> любого из этих атрибутов может вызвать неопределенное поведение; -> Вам следует всегда предпочитать использование безопасных, высокоуровневых -> атрибутов вокруг них, таких как атрибуты `interrupt` и `exception` -> из `cortex-m-rt`. -> -> В особых случаях функций RAM нет безопасной абстракции в `cortex-m-rt` -> v0.6.5 но создано [RFC] для добавления атрибута `ramfunc` в будущем релизе. - -[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 - -В примере ниже показано как разместить высокоприоритетную задачу `bar` в ОЗУ. - -``` rust -{{#include ../../../examples/ramfunc.rs}} -``` - -Запуск этой программы произведет ожидаемый вывод. - -``` console -$ cargo run --example ramfunc -{{#include ../../../ci/expected/ramfunc.run}}``` - -Можно посмотреть на вывод `cargo-nm`, чтобы убедиться, что `bar` расположен в ОЗУ -(`0x2000_0000`), тогда как `foo` расположен во 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/ru/src/by-example/types-send-sync.md b/ru/src/by-example/types-send-sync.md deleted file mode 100644 index e470adf..0000000 --- a/ru/src/by-example/types-send-sync.md +++ /dev/null @@ -1,59 +0,0 @@ -# Типы, Send и Sync - -Атрибут `app` вводит контекст, коллекцию переменных в каждую из функций. -Все эти переменные имеют предсказуемые, неанонимные типы, поэтому Вы можете -писать простые функции, получающие их как аргументы. - -Описание API определяет как эти типы эти типы генерируются из входных данных. -Вы можете также сгенерировать документацию для Вашей бинарной библиотеки -(`cargo doc --bin `); в документации Вы найдете структуры `Context` -(например `init::Context` и `idle::Context`), чьи поля представляют переменные -включенные в каждую функцию. - -В примере ниже сгенерированы разные типы с помощью атрибута `app`. - -``` rust -{{#include ../../../examples/types.rs}} -``` - -## `Send` - -[`Send`] - маркерный типаж (trait) для "типов, которые можно передавать через границы -потоков", как это определено в `core`. В контексте RTFM типаж `Send` необходим -только там, где возможна передача значения между задачами, запускаемыми на -*разных* приоритетах. Это возникает в нескольких случаях: при передаче сообщений, -в совместно используемых `static mut` ресурсах и инициализации поздних ресурсов. - -[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html - -Атрибут `app` проверит, что `Send` реализован, где необходимо, поэтому Вам не -стоит волноваться об этом. Более важно знать, где Вам *не* нужен типаж `Send`: -в типах, передаваемых между задачами с *одинаковым* приоритетом. Это возникает -в двух случаях: при передаче сообщений и в совместно используемых `static mut` -ресурсах. - -В примере ниже показано, где можно использовать типы, не реализующие `Send`. - -``` rust -{{#include ../../../examples/not-send.rs}} -``` - -## `Sync` - -Похожая ситуация, [`Sync`] - маркерный типаж для "типов, на которых можно -ссылаться в разных потоках", как это определено в `core`. В контексте RTFM -типаж `Sync` необходим только там, где возможны две или более задачи, -запускаемые на разных приоритетах, чтобы захватить разделяемую ссылку на -ресурс. Это возникает только совместно используемых `static`-ресурсах. - -[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html - -Атрибут `app` проверит, что `Sync` реализован, где необходимо, но важно знать, -где ограничение `Sync` не требуется: в `static`-ресурсах, разделяемых между -задачами с *одинаковым* приоритетом. - -В примере ниже показано, где можно использовать типы, не реализующие `Sync`. - -``` rust -{{#include ../../../examples/not-sync.rs}} -``` diff --git a/ru/src/internals.md b/ru/src/internals.md deleted file mode 100644 index 4a47e77..0000000 --- a/ru/src/internals.md +++ /dev/null @@ -1,7 +0,0 @@ -# Под капотом - -В этом разделе описывабтся внутренности фркймворка на *высоком уровне*. -Низкоуровневые тонкости, такие как парсинг и кодогенерация производимые -процедурным макросом (`#[app]`) здесь объясняться не будут. Мы сосредоточимся -на анализе пользовательской спецификации и структурах данных, используемых -рантаймом. diff --git a/ru/src/internals/ceilings.md b/ru/src/internals/ceilings.md deleted file mode 100644 index 2c645a4..0000000 --- a/ru/src/internals/ceilings.md +++ /dev/null @@ -1,3 +0,0 @@ -# Ceiling analysis - -**TODO** diff --git a/ru/src/internals/tasks.md b/ru/src/internals/tasks.md deleted file mode 100644 index 85f783f..0000000 --- a/ru/src/internals/tasks.md +++ /dev/null @@ -1,3 +0,0 @@ -# Task dispatcher - -**TODO** diff --git a/ru/src/internals/timer-queue.md b/ru/src/internals/timer-queue.md deleted file mode 100644 index 7059285..0000000 --- a/ru/src/internals/timer-queue.md +++ /dev/null @@ -1,3 +0,0 @@ -# Timer queue - -**TODO** diff --git a/ru/src/preface.md b/ru/src/preface.md deleted file mode 100644 index 8aa53ad..0000000 --- a/ru/src/preface.md +++ /dev/null @@ -1,12 +0,0 @@ -

Real Time For the Masses

- -

Конкурентный фреймворк для создания систем реального времени

- -# Введение - -Эта книга содержит документацию уровня пользователя фреймворком Real Time For the Masses -(RTFM). Описание API можно найти [здесь](../api/rtfm/index.html). - -{{#include README_RU.md:5:54}} - -{{#include README_RU.md:60:}} diff --git a/src/lib.rs b/src/lib.rs index 4756702..9dc5175 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ //! //! The user level documentation can be found [here]. //! -//! [here]: https://japaric.github.io/cortex-m-rtfm/book/ +//! [here]: https://japaric.github.io/cortex-m-rtfm/book/en/ //! //! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component //! of the framework. -- cgit v1.2.3