176:  implement RFCs 147 and 155, fix #141, etc. r=japaric a=japaric

This PR:

- Implements RFC 147: "all functions must be safe"

- Implements RFC 155: "explicit Context parameter"

- Implements the pending breaking change #141: reject assign syntax in `init`
  (which was used to initialize late resources)

- Refactors code generation to make it more readable -- there are no more random
  identifiers in the output -- and align it with the book description of RTFM
  internals (see PR #175).

- Makes the framework hard depend on `core::mem::MaybeUninit` and thus will
  require nightly until that API is stabilized.

- Fixes a ceiling analysis bug where the priority of the system timer was not
  considered in the analysis (TODO backport this into the v0.4.x branch).

- Shrinks the size of all the internal queues by turning `AtomicUsize` indices
  into `AtomicU8`s.

- Removes the integration with `owned_singleton`.

closes #141
closes #147
closes #155

Additionally:

- This changes CI to push v0.5.x docs to
  https://japaric.github.io/rtfm5/book/en/ -- we need to do this because our
  official docs are hosted on https://japaric.github.io/cortex-m-rtfm and we
  need to keep them on v0.4.x until we release v0.5.0

- I propose that we use the master branch to develop the upcoming v0.5.0.

- I have created a branch v0.4.x for backports; new v0.4.x releases will come
  from that branch.

r? @korken89 @texitoi, sorry for doing all the impl work in a single commit --
I know that makes things harder to review for you.

Suggestions for compile-pass and compile-fail tests are welcome


Co-authored-by: Jorge Aparicio <jorge@japaric.io>
This commit is contained in:
bors[bot] 2019-05-01 19:50:50 +00:00
commit bc024f1979
97 changed files with 3090 additions and 3012 deletions

View file

@ -3,17 +3,17 @@ language: rust
matrix: matrix:
include: include:
# NOTE used to build docs on successful merges to master # NOTE used to build docs on successful merges to master
- env: TARGET=x86_64-unknown-linux-gnu # - env: TARGET=x86_64-unknown-linux-gnu
- env: TARGET=thumbv6m-none-eabi # - env: TARGET=thumbv6m-none-eabi
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- env: TARGET=thumbv7m-none-eabi # - env: TARGET=thumbv7m-none-eabi
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- env: TARGET=x86_64-unknown-linux-gnu - env: TARGET=x86_64-unknown-linux-gnu
rust: nightly rust: nightly
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- env: TARGET=thumbv6m-none-eabi - env: TARGET=thumbv6m-none-eabi
rust: nightly rust: nightly

View file

@ -5,6 +5,35 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased] ## [Unreleased]
## v0.5.0 - 2019-??-?? (ALPHA pre-release)
### Changed
- [breaking-change][] [RFC 155] "explicit `Context` parameter" has been
implemented.
[RFC 155]: https://github.com/japaric/cortex-m-rtfm/issues/155
- [breaking-change][] [RFC 147] "all functions must be safe" has been
implemented.
[RFC 147]: https://github.com/japaric/cortex-m-rtfm/issues/147
- All the queues internally used by the framework now use `AtomicU8` indices
instead of `AtomicUsize`; this reduces the static memory used by the
framework.
### Removed
- [breaking-change] the integration with the `owned_singleton` crate has been
removed. You can use `heapless::Pool` instead of `alloc_singleton`.
- [breaking-change] late resources can no longer be initialized using the assign
syntax. `init::LateResources` is the only method to initialize late resources.
See [PR #140] for more details.
[PR #140]: https://github.com/japaric/cortex-m-rtfm/pull/140
## [v0.4.3] - 2019-04-21 ## [v0.4.3] - 2019-04-21
### Changed ### Changed

View file

@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm" name = "cortex-m-rtfm"
readme = "README.md" readme = "README.md"
repository = "https://github.com/japaric/cortex-m-rtfm" repository = "https://github.com/japaric/cortex-m-rtfm"
version = "0.4.3" version = "0.5.0-alpha.1"
[lib] [lib]
name = "rtfm" name = "rtfm"
@ -25,6 +25,12 @@ required-features = ["timer-queue"]
name = "periodic" name = "periodic"
required-features = ["timer-queue"] required-features = ["timer-queue"]
[[example]]
name = "pool"
# this example doesn't need this feature but only works on ARMv7-M
# specifying the feature here avoids compiling this for ARMv6-M
required-features = ["timer-queue"]
[[example]] [[example]]
name = "schedule" name = "schedule"
required-features = ["timer-queue"] required-features = ["timer-queue"]
@ -36,12 +42,13 @@ required-features = ["timer-queue"]
[dependencies] [dependencies]
cortex-m = "0.5.8" cortex-m = "0.5.8"
cortex-m-rt = "0.6.7" cortex-m-rt = "0.6.7"
cortex-m-rtfm-macros = { path = "macros", version = "0.4.3" } cortex-m-rtfm-macros = { path = "macros", version = "0.5.0-alpha.1" }
heapless = "0.4.1"
owned-singleton = "0.1.0" [dependencies.heapless]
features = ["smaller-atomics", "min-const-fn"]
version = "0.4.3"
[dev-dependencies] [dev-dependencies]
alloc-singleton = "0.1.0"
cortex-m-semihosting = "0.3.2" cortex-m-semihosting = "0.3.2"
lm3s6965 = "0.1.3" lm3s6965 = "0.1.3"
panic-halt = "0.2.0" panic-halt = "0.2.0"

View file

@ -41,13 +41,13 @@ A concurrency framework for building real time systems.
## Requirements ## Requirements
- Rust 1.31.0+ - Rust 1.36.0+
- Applications must be written using the 2018 edition. - Applications must be written using the 2018 edition.
## [User documentation](https://japaric.github.io/cortex-m-rtfm/book/en) ## [User documentation](https://japaric.github.io/rtfm5/book/en)
## [API reference](https://japaric.github.io/cortex-m-rtfm/api/rtfm/index.html) ## [API reference](https://japaric.github.io/rtfm5/api/rtfm/index.html)
## Acknowledgments ## Acknowledgments

View file

@ -6,7 +6,6 @@
- [Resources](./by-example/resources.md) - [Resources](./by-example/resources.md)
- [Tasks](./by-example/tasks.md) - [Tasks](./by-example/tasks.md)
- [Timer queue](./by-example/timer-queue.md) - [Timer queue](./by-example/timer-queue.md)
- [Singletons](./by-example/singletons.md)
- [Types, Send and Sync](./by-example/types-send-sync.md) - [Types, Send and Sync](./by-example/types-send-sync.md)
- [Starting a new project](./by-example/new.md) - [Starting a new project](./by-example/new.md)
- [Tips & tricks](./by-example/tips.md) - [Tips & tricks](./by-example/tips.md)

View file

@ -28,15 +28,14 @@ not required to use the [`cortex_m_rt::entry`] attribute.
Within the pseudo-module the `app` attribute expects to find an initialization Within the pseudo-module the `app` attribute expects to find an initialization
function marked with the `init` attribute. This function must have signature function marked with the `init` attribute. This function must have signature
`[unsafe] fn()`. `fn(init::Context) [-> init::LateResources]`.
This initialization function will be the first part of the application to run. 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 The `init` function will run *with interrupts disabled* and has exclusive access
to Cortex-M and device specific peripherals through the `core` and `device` 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 variables fields of `init::Context`. Not all Cortex-M peripherals are available
all Cortex-M peripherals are available in `core` because the RTFM runtime takes in `core` because the RTFM runtime takes ownership of some of them -- for more
ownership of some of them -- for more details see the [`rtfm::Peripherals`] details see the [`rtfm::Peripherals`] struct.
struct.
`static mut` variables declared at the beginning of `init` will be transformed `static mut` variables declared at the beginning of `init` will be transformed
into `&'static mut` references that are safe to access. into `&'static mut` references that are safe to access.
@ -61,7 +60,7 @@ $ cargo run --example init
A function marked with the `idle` attribute can optionally appear in the 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 pseudo-module. This function is used as the special *idle task* and must have
signature `[unsafe] fn() - > !`. signature `fn(idle::Context) - > !`.
When present, the runtime will execute the `idle` task after `init`. Unlike 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 `init`, `idle` will run *with interrupts enabled* and it's not allowed to return

View file

@ -40,7 +40,7 @@ $ rm memory.x build.rs
`timer-queue` feature. `timer-queue` feature.
``` console ``` console
$ cargo add cortex-m-rtfm $ cargo add cortex-m-rtfm --allow-prerelease
``` ```
4. Write your RTFM application. 4. Write your RTFM application.
@ -49,7 +49,7 @@ Here I'll use the `init` example from the `cortex-m-rtfm` crate.
``` console ``` console
$ curl \ $ curl \
-L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0/examples/init.rs \ -L https://github.com/japaric/cortex-m-rtfm/raw/v0.5.0-alpha.1/examples/init.rs \
> src/main.rs > src/main.rs
``` ```

View file

@ -10,7 +10,7 @@ have enough information to optimize the access to the shared data.
The `app` attribute has a full view of the application thus it can optimize 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 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 declared inside the `app` pseudo-module as *resources*. To access a resource the
context (`init`, `idle`, `interrupt` or `exception`) must first declare the context (`init`, `idle`, `interrupt` or `exception`) one must first declare the
resource in the `resources` argument of its attribute. resource in the `resources` argument of its attribute.
In the example below two interrupt handlers access the same resource. No `Mutex` In the example below two interrupt handlers access the same resource. No `Mutex`
@ -30,7 +30,7 @@ $ cargo run --example resource
The priority of each handler can be declared in the `interrupt` and `exception` 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 attributes. It's not possible to set the priority in any other way because the
runtime takes ownership of the `NVIC` peripheral; it's also not possible to runtime takes ownership of the `NVIC` peripheral thus it's also not possible to
change the priority of a handler / task at runtime. Thanks to this restriction 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 the framework has knowledge about the *static* priorities of all interrupt and
exception handlers. exception handlers.
@ -71,10 +71,10 @@ $ cargo run --example lock
One more note about priorities: choosing a priority higher than what the device One more note about priorities: choosing a priority higher than what the device
supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to
limitations in the language the error is currently far from helpful: it will say limitations in the language the error message is currently far from helpful: it
something along the lines of "evaluation of constant value failed" and the span will say something along the lines of "evaluation of constant value failed" and
of the error will *not* point out to the problematic interrupt value -- we are the span of the error will *not* point out to the problematic interrupt value --
sorry about this! we are sorry about this!
## Late resources ## Late resources

View file

@ -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}}```

View file

@ -24,8 +24,8 @@ of tasks.
You can use conditional compilation (`#[cfg]`) on resources (`static [mut]` You can use conditional compilation (`#[cfg]`) on resources (`static [mut]`
items) and tasks (`fn` items). The effect of using `#[cfg]` attributes is that 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 the resource / task will *not* be available through the corresponding `Context`
them (see `resources`, `spawn` and `schedule`) if the condition doesn't hold. `struct` if the condition doesn't hold.
The example below logs a message whenever the `foo` task is spawned, but only if The example below logs a message whenever the `foo` task is spawned, but only if
the program has been compiled using the `dev` profile. the program has been compiled using the `dev` profile.
@ -37,7 +37,7 @@ the program has been compiled using the `dev` profile.
## Running tasks from RAM ## Running tasks from RAM
The main goal of moving the specification of RTFM applications to attributes in The main goal of moving the specification of RTFM applications to attributes in
RTFM v0.4.x was to allow inter-operation with other attributes. For example, the RTFM v0.4.0 was to allow inter-operation with other attributes. For example, the
`link_section` attribute can be applied to tasks to place them in RAM; this can `link_section` attribute can be applied to tasks to place them in RAM; this can
improve performance in some cases. improve performance in some cases.
@ -78,8 +78,6 @@ $ cargo nm --example ramfunc --release | grep ' bar::'
## `binds` ## `binds`
**NOTE**: Requires RTFM ~0.4.2
You can give hardware tasks more task-like names using the `binds` argument: you You can give hardware tasks more task-like names using the `binds` argument: you
name the function as you wish and specify the name of the interrupt / exception name the function as you wish and specify the name of the interrupt / exception
in the `binds` argument. Types like `Spawn` will be placed in a module named in the `binds` argument. Types like `Spawn` will be placed in a module named
@ -91,3 +89,27 @@ after the function, not the interrupt / exception. Example below:
``` console ``` console
$ cargo run --example binds $ cargo run --example binds
{{#include ../../../../ci/expected/binds.run}}``` {{#include ../../../../ci/expected/binds.run}}```
## Indirection for faster message passing
Message passing always involves copying the payload from the sender into a
static variable and then from the static variable into the receiver. Thus
sending a large buffer, like a `[u8; 128]`, as a message involves two expensive
`memcpy`s. To minimize the message passing overhead one can use indirection:
instead of sending the buffer by value, one can send an owning pointer into the
buffer.
One can use a global allocator to achieve indirection (`alloc::Box`,
`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.34.0,
or one can use a statically allocated memory pool like [`heapless::Pool`].
[`heapless::Pool`]: https://docs.rs/heapless/0.4.3/heapless/pool/index.html
Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes.
``` rust
{{#include ../../../../examples/pool.rs}}
```
``` console
$ cargo run --example binds
{{#include ../../../../ci/expected/pool.run}}```

View file

@ -7,8 +7,7 @@ write plain functions that take them as arguments.
The API reference specifies how these types are generated from the input. You The API reference specifies how these types are generated from the input. You
can also generate documentation for you binary crate (`cargo doc --bin <name>`); can also generate documentation for you binary crate (`cargo doc --bin <name>`);
in the documentation you'll find `Context` structs (e.g. `init::Context` and in the documentation you'll find `Context` structs (e.g. `init::Context` and
`idle::Context`) whose fields represent the variables injected into each `idle::Context`).
function.
The example below shows the different types generates by the `app` attribute. The example below shows the different types generates by the `app` attribute.

View file

@ -11,6 +11,9 @@ There is a translation of this book in [Russian].
[Russian]: ../ru/index.html [Russian]: ../ru/index.html
**HEADS UP** This is an **alpha** pre-release; there may be breaking changes in
the API and semantics before a proper release is made.
{{#include ../../../README.md:5:46}} {{#include ../../../README.md:5:46}}
{{#include ../../../README.md:52:}} {{#include ../../../README.md:52:}}

View file

@ -23,7 +23,8 @@ main() {
./ghp-import/ghp_import.py $td ./ghp-import/ghp_import.py $td
set +x set +x
git push -fq https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git gh-pages && echo OK # NOTE push documentation to a different repository
git push -fq https://$GH_TOKEN@github.com/japaric/rtfm5.git gh-pages && echo OK
rm -rf $td rm -rf $td
} }

2
ci/expected/pool.run Normal file
View file

@ -0,0 +1,2 @@
bar(0x2000008c)
foo(0x20000110)

View file

@ -104,14 +104,13 @@ main() {
message message
capacity capacity
singleton
types types
not-send not-send
not-sync not-sync
shared-with-init shared-with-init
generics generics
pool
ramfunc ramfunc
) )
@ -121,6 +120,31 @@ main() {
continue continue
fi fi
if [ $ex = pool ]; then
if [ $TARGET != thumbv6m-none-eabi ]; then
local td=$(mktemp -d)
local features="$nightly,timer-queue"
cargo run --example $ex --target $TARGET --features $features >\
$td/pool.run
grep 'foo(0x2' $td/pool.run
grep 'bar(0x2' $td/pool.run
arm-none-eabi-objcopy -O ihex target/$TARGET/debug/examples/$ex \
ci/builds/${ex}_${features/,/_}_debug_1.hex
cargo run --example $ex --target $TARGET --features $features --release >\
$td/pool.run
grep 'foo(0x2' $td/pool.run
grep 'bar(0x2' $td/pool.run
arm-none-eabi-objcopy -O ihex target/$TARGET/release/examples/$ex \
ci/builds/${ex}_${features/,/_}_release_1.hex
rm -rf $td
fi
continue
fi
if [ $ex != types ]; then if [ $ex != types ]; then
arm_example "run" $ex "debug" "$nightly" "1" arm_example "run" $ex "debug" "$nightly" "1"
arm_example "run" $ex "release" "$nightly" "1" arm_example "run" $ex "release" "$nightly" "1"
@ -140,13 +164,7 @@ main() {
continue continue
fi fi
if [ $ex = singleton ]; then if [ $ex != types ] && [ $ex != pool ]; then
# singleton build is currently not reproducible due to
# https://github.com/japaric/owned-singleton/issues/2
continue
fi
if [ $ex != types ]; then
arm_example "build" $ex "debug" "$nightly" "2" arm_example "build" $ex "debug" "$nightly" "2"
cmp ci/builds/${ex}_${nightly/nightly/nightly_}debug_1.hex \ cmp ci/builds/${ex}_${nightly/nightly/nightly_}debug_1.hex \
ci/builds/${ex}_${nightly/nightly/nightly_}debug_2.hex ci/builds/${ex}_${nightly/nightly/nightly_}debug_2.hex

View file

@ -9,24 +9,23 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
// NOTE: does NOT properly work on QEMU // NOTE: does NOT properly work on QEMU
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(spawn = [foo])] #[init(spawn = [foo])]
fn init() { fn init(c: init::Context) {
hprintln!("init(baseline = {:?})", start).unwrap(); hprintln!("init(baseline = {:?})", c.start).unwrap();
// `foo` inherits the baseline of `init`: `Instant(0)` // `foo` inherits the baseline of `init`: `Instant(0)`
spawn.foo().unwrap(); c.spawn.foo().unwrap();
} }
#[task(schedule = [foo])] #[task(schedule = [foo])]
fn foo() { fn foo(c: foo::Context) {
static mut ONCE: bool = true; static mut ONCE: bool = true;
hprintln!("foo(baseline = {:?})", scheduled).unwrap(); hprintln!("foo(baseline = {:?})", c.scheduled).unwrap();
if *ONCE { if *ONCE {
*ONCE = false; *ONCE = false;
@ -38,11 +37,11 @@ const APP: () = {
} }
#[interrupt(spawn = [foo])] #[interrupt(spawn = [foo])]
fn UART0() { fn UART0(c: UART0::Context) {
hprintln!("UART0(baseline = {:?})", start).unwrap(); hprintln!("UART0(baseline = {:?})", c.start).unwrap();
// `foo` inherits the baseline of `UART0`: its `start` time // `foo` inherits the baseline of `UART0`: its `start` time
spawn.foo().unwrap(); c.spawn.foo().unwrap();
} }
extern "C" { extern "C" {

View file

@ -9,20 +9,19 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
// `examples/interrupt.rs` rewritten to use `binds` // `examples/interrupt.rs` rewritten to use `binds`
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
hprintln!("init").unwrap(); hprintln!("init").unwrap();
} }
#[idle] #[idle]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
hprintln!("idle").unwrap(); hprintln!("idle").unwrap();
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
@ -33,7 +32,7 @@ const APP: () = {
} }
#[interrupt(binds = UART0)] #[interrupt(binds = UART0)]
fn foo() { fn foo(_: foo::Context) {
static mut TIMES: u32 = 0; static mut TIMES: u32 = 0;
*TIMES += 1; *TIMES += 1;

View file

@ -9,32 +9,31 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
} }
#[interrupt(spawn = [foo, bar])] #[interrupt(spawn = [foo, bar])]
fn UART0() { fn UART0(c: UART0::Context) {
spawn.foo(0).unwrap(); c.spawn.foo(0).unwrap();
spawn.foo(1).unwrap(); c.spawn.foo(1).unwrap();
spawn.foo(2).unwrap(); c.spawn.foo(2).unwrap();
spawn.foo(3).unwrap(); c.spawn.foo(3).unwrap();
spawn.bar().unwrap(); c.spawn.bar().unwrap();
} }
#[task(capacity = 4)] #[task(capacity = 4)]
fn foo(x: u32) { fn foo(_: foo::Context, x: u32) {
hprintln!("foo({})", x).unwrap(); hprintln!("foo({})", x).unwrap();
} }
#[task] #[task]
fn bar() { fn bar(_: bar::Context) {
hprintln!("bar").unwrap(); hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);

View file

@ -9,25 +9,24 @@ extern crate panic_semihosting;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use cortex_m_semihosting::hprintln; use cortex_m_semihosting::hprintln;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[cfg(debug_assertions)] // <- `true` when using the `dev` profile #[cfg(debug_assertions)] // <- `true` when using the `dev` profile
static mut COUNT: u32 = 0; static mut COUNT: u32 = 0;
#[init] #[init]
fn init() { fn init(_: init::Context) {
// .. // ..
} }
#[task(priority = 3, resources = [COUNT], spawn = [log])] #[task(priority = 3, resources = [COUNT], spawn = [log])]
fn foo() { fn foo(c: foo::Context) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
*resources.COUNT += 1; *c.resources.COUNT += 1;
spawn.log(*resources.COUNT).ok(); c.spawn.log(*c.resources.COUNT).ok();
} }
// this wouldn't compile in `release` mode // this wouldn't compile in `release` mode
@ -38,7 +37,7 @@ const APP: () = {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
#[task] #[task]
fn log(n: u32) { fn log(_: log::Context, n: u32) {
hprintln!( hprintln!(
"foo has been called {} time{}", "foo has been called {} time{}",
n, n,

View file

@ -9,25 +9,25 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::{app, Mutex}; use rtfm::Mutex;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut SHARED: u32 = 0; static mut SHARED: u32 = 0;
#[init] #[init]
fn init() { fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1); rtfm::pend(Interrupt::UART1);
} }
#[interrupt(resources = [SHARED])] #[interrupt(resources = [SHARED])]
fn UART0() { fn UART0(c: UART0::Context) {
static mut STATE: u32 = 0; static mut STATE: u32 = 0;
hprintln!("UART0(STATE = {})", *STATE).unwrap(); hprintln!("UART0(STATE = {})", *STATE).unwrap();
advance(STATE, resources.SHARED); advance(STATE, c.resources.SHARED);
rtfm::pend(Interrupt::UART1); rtfm::pend(Interrupt::UART1);
@ -35,17 +35,17 @@ const APP: () = {
} }
#[interrupt(priority = 2, resources = [SHARED])] #[interrupt(priority = 2, resources = [SHARED])]
fn UART1() { fn UART1(mut c: UART1::Context) {
static mut STATE: u32 = 0; static mut STATE: u32 = 0;
hprintln!("UART1(STATE = {})", *STATE).unwrap(); hprintln!("UART1(STATE = {})", *STATE).unwrap();
// just to show that `SHARED` can be accessed directly and .. // just to show that `SHARED` can be accessed directly and ..
*resources.SHARED += 0; *c.resources.SHARED += 0;
// .. also through a (no-op) `lock` // .. also through a (no-op) `lock`
resources.SHARED.lock(|shared| *shared += 0); c.resources.SHARED.lock(|shared| *shared += 0);
advance(STATE, resources.SHARED); advance(STATE, c.resources.SHARED);
} }
}; };

View file

@ -8,17 +8,16 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(_: init::Context) {
hprintln!("init").unwrap(); hprintln!("init").unwrap();
} }
#[idle] #[idle]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
static mut X: u32 = 0; static mut X: u32 = 0;
// Safe access to local `static mut` variable // Safe access to local `static mut` variable

View file

@ -8,19 +8,18 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(c: init::Context) {
static mut X: u32 = 0; static mut X: u32 = 0;
// Cortex-M peripherals // Cortex-M peripherals
let _core: rtfm::Peripherals = core; let _core: rtfm::Peripherals = c.core;
// Device specific peripherals // Device specific peripherals
let _device: lm3s6965::Peripherals = device; let _device: lm3s6965::Peripherals = c.device;
// Safe access to local `static mut` variable // Safe access to local `static mut` variable
let _x: &'static mut u32 = X; let _x: &'static mut u32 = X;

View file

@ -9,12 +9,11 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(_: init::Context) {
// Pends the UART0 interrupt but its handler won't run until *after* // Pends the UART0 interrupt but its handler won't run until *after*
// `init` returns because interrupts are disabled // `init` returns because interrupts are disabled
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
@ -23,7 +22,7 @@ const APP: () = {
} }
#[idle] #[idle]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
// interrupts are enabled again; the `UART0` handler runs at this point // interrupts are enabled again; the `UART0` handler runs at this point
hprintln!("idle").unwrap(); hprintln!("idle").unwrap();
@ -36,7 +35,7 @@ const APP: () = {
} }
#[interrupt] #[interrupt]
fn UART0() { fn UART0(_: UART0::Context) {
static mut TIMES: u32 = 0; static mut TIMES: u32 = 0;
// Safe access to local `static mut` variable // Safe access to local `static mut` variable

View file

@ -13,16 +13,15 @@ use heapless::{
spsc::{Consumer, Producer, Queue}, spsc::{Consumer, Producer, Queue},
}; };
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
// Late resources // Late resources
static mut P: Producer<'static, u32, U4> = (); static mut P: Producer<'static, u32, U4> = ();
static mut C: Consumer<'static, u32, U4> = (); static mut C: Consumer<'static, u32, U4> = ();
#[init] #[init]
fn init() -> init::LateResources { fn init(_: init::Context) -> init::LateResources {
// NOTE: we use `Option` here to work around the lack of // NOTE: we use `Option` here to work around the lack of
// a stable `const` constructor // a stable `const` constructor
static mut Q: Option<Queue<u32, U4>> = None; static mut Q: Option<Queue<u32, U4>> = None;
@ -35,9 +34,9 @@ const APP: () = {
} }
#[idle(resources = [C])] #[idle(resources = [C])]
fn idle() -> ! { fn idle(c: idle::Context) -> ! {
loop { loop {
if let Some(byte) = resources.C.dequeue() { if let Some(byte) = c.resources.C.dequeue() {
hprintln!("received message: {}", byte).unwrap(); hprintln!("received message: {}", byte).unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
@ -48,7 +47,7 @@ const APP: () = {
} }
#[interrupt(resources = [P])] #[interrupt(resources = [P])]
fn UART0() { fn UART0(c: UART0::Context) {
resources.P.enqueue(42).unwrap(); c.resources.P.enqueue(42).unwrap();
} }
}; };

View file

@ -9,24 +9,23 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut SHARED: u32 = 0; static mut SHARED: u32 = 0;
#[init] #[init]
fn init() { fn init(_: init::Context) {
rtfm::pend(Interrupt::GPIOA); rtfm::pend(Interrupt::GPIOA);
} }
// when omitted priority is assumed to be `1` // when omitted priority is assumed to be `1`
#[interrupt(resources = [SHARED])] #[interrupt(resources = [SHARED])]
fn GPIOA() { fn GPIOA(mut c: GPIOA::Context) {
hprintln!("A").unwrap(); hprintln!("A").unwrap();
// the lower priority task requires a critical section to access the data // the lower priority task requires a critical section to access the data
resources.SHARED.lock(|shared| { c.resources.SHARED.lock(|shared| {
// data can only be modified within this critical section (closure) // data can only be modified within this critical section (closure)
*shared += 1; *shared += 1;
@ -47,15 +46,15 @@ const APP: () = {
} }
#[interrupt(priority = 2, resources = [SHARED])] #[interrupt(priority = 2, resources = [SHARED])]
fn GPIOB() { fn GPIOB(mut c: GPIOB::Context) {
// the higher priority task does *not* need a critical section // the higher priority task does *not* need a critical section
*resources.SHARED += 1; *c.resources.SHARED += 1;
hprintln!("D - SHARED = {}", *resources.SHARED).unwrap(); hprintln!("D - SHARED = {}", *c.resources.SHARED).unwrap();
} }
#[interrupt(priority = 3)] #[interrupt(priority = 3)]
fn GPIOC() { fn GPIOC(_: GPIOC::Context) {
hprintln!("C").unwrap(); hprintln!("C").unwrap();
} }
}; };

View file

@ -8,41 +8,40 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(spawn = [foo])] #[init(spawn = [foo])]
fn init() { fn init(c: init::Context) {
spawn.foo(/* no message */).unwrap(); c.spawn.foo(/* no message */).unwrap();
} }
#[task(spawn = [bar])] #[task(spawn = [bar])]
fn foo() { fn foo(c: foo::Context) {
static mut COUNT: u32 = 0; static mut COUNT: u32 = 0;
hprintln!("foo").unwrap(); hprintln!("foo").unwrap();
spawn.bar(*COUNT).unwrap(); c.spawn.bar(*COUNT).unwrap();
*COUNT += 1; *COUNT += 1;
} }
#[task(spawn = [baz])] #[task(spawn = [baz])]
fn bar(x: u32) { fn bar(c: bar::Context, x: u32) {
hprintln!("bar({})", x).unwrap(); hprintln!("bar({})", x).unwrap();
spawn.baz(x + 1, x + 2).unwrap(); c.spawn.baz(x + 1, x + 2).unwrap();
} }
#[task(spawn = [foo])] #[task(spawn = [foo])]
fn baz(x: u32, y: u32) { fn baz(c: baz::Context, x: u32, y: u32) {
hprintln!("baz({}, {})", x, y).unwrap(); hprintln!("baz({}, {})", x, y).unwrap();
if x + y > 4 { if x + y > 4 {
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }
spawn.foo().unwrap(); c.spawn.foo().unwrap();
} }
extern "C" { extern "C" {

View file

@ -21,32 +21,32 @@ const APP: () = {
static mut SHARED: Option<NotSend> = None; static mut SHARED: Option<NotSend> = None;
#[init(spawn = [baz, quux])] #[init(spawn = [baz, quux])]
fn init() { fn init(c: init::Context) {
spawn.baz().unwrap(); c.spawn.baz().unwrap();
spawn.quux().unwrap(); c.spawn.quux().unwrap();
} }
#[task(spawn = [bar])] #[task(spawn = [bar])]
fn foo() { fn foo(c: foo::Context) {
// scenario 1: message passed to task that runs at the same priority // scenario 1: message passed to task that runs at the same priority
spawn.bar(NotSend { _0: PhantomData }).ok(); c.spawn.bar(NotSend { _0: PhantomData }).ok();
} }
#[task] #[task]
fn bar(_x: NotSend) { fn bar(_: bar::Context, _x: NotSend) {
// scenario 1 // scenario 1
} }
#[task(priority = 2, resources = [SHARED])] #[task(priority = 2, resources = [SHARED])]
fn baz() { fn baz(mut c: baz::Context) {
// scenario 2: resource shared between tasks that run at the same priority // scenario 2: resource shared between tasks that run at the same priority
*resources.SHARED = Some(NotSend { _0: PhantomData }); *c.resources.SHARED = Some(NotSend { _0: PhantomData });
} }
#[task(priority = 2, resources = [SHARED])] #[task(priority = 2, resources = [SHARED])]
fn quux() { fn quux(mut c: quux::Context) {
// scenario 2 // scenario 2
let _not_send = resources.SHARED.take().unwrap(); let _not_send = c.resources.SHARED.take().unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }

View file

@ -10,29 +10,28 @@ extern crate panic_halt;
use core::marker::PhantomData; use core::marker::PhantomData;
use cortex_m_semihosting::debug; use cortex_m_semihosting::debug;
use rtfm::app;
pub struct NotSync { pub struct NotSync {
_0: PhantomData<*const ()>, _0: PhantomData<*const ()>,
} }
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static SHARED: NotSync = NotSync { _0: PhantomData }; static SHARED: NotSync = NotSync { _0: PhantomData };
#[init] #[init]
fn init() { fn init(_: init::Context) {
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }
#[task(resources = [SHARED])] #[task(resources = [SHARED])]
fn foo() { fn foo(c: foo::Context) {
let _: &NotSync = resources.SHARED; let _: &NotSync = c.resources.SHARED;
} }
#[task(resources = [SHARED])] #[task(resources = [SHARED])]
fn bar() { fn bar(c: bar::Context) {
let _: &NotSync = resources.SHARED; let _: &NotSync = c.resources.SHARED;
} }
extern "C" { extern "C" {

View file

@ -8,24 +8,24 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::hprintln; use cortex_m_semihosting::hprintln;
use rtfm::{app, Instant}; use rtfm::Instant;
const PERIOD: u32 = 8_000_000; const PERIOD: u32 = 8_000_000;
// NOTE: does NOT work on QEMU! // NOTE: does NOT work on QEMU!
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(schedule = [foo])] #[init(schedule = [foo])]
fn init() { fn init(c: init::Context) {
schedule.foo(Instant::now() + PERIOD.cycles()).unwrap(); c.schedule.foo(Instant::now() + PERIOD.cycles()).unwrap();
} }
#[task(schedule = [foo])] #[task(schedule = [foo])]
fn foo() { fn foo(c: foo::Context) {
let now = Instant::now(); let now = Instant::now();
hprintln!("foo(scheduled = {:?}, now = {:?})", scheduled, now).unwrap(); hprintln!("foo(scheduled = {:?}, now = {:?})", c.scheduled, now).unwrap();
schedule.foo(scheduled + PERIOD.cycles()).unwrap(); c.schedule.foo(c.scheduled + PERIOD.cycles()).unwrap();
} }
extern "C" { extern "C" {

67
examples/pool.rs Normal file
View file

@ -0,0 +1,67 @@
//! examples/pool.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use heapless::{
pool,
pool::singleton::{Box, Pool},
};
use lm3s6965::Interrupt;
use rtfm::app;
// Declare a pool of 128-byte memory blocks
pool!(P: [u8; 128]);
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init(_: init::Context) {
static mut MEMORY: [u8; 512] = [0; 512];
// Increase the capacity of the memory pool by ~4
P::grow(MEMORY);
rtfm::pend(Interrupt::I2C0);
}
#[interrupt(priority = 2, spawn = [foo, bar])]
fn I2C0(c: I2C0::Context) {
// claim a memory block, leave it uninitialized and ..
let x = P::alloc().unwrap().freeze();
// .. send it to the `foo` task
c.spawn.foo(x).ok().unwrap();
// send another block to the task `bar`
c.spawn.bar(P::alloc().unwrap().freeze()).ok().unwrap();
}
#[task]
fn foo(_: foo::Context, x: Box<P>) {
hprintln!("foo({:?})", x.as_ptr()).unwrap();
// explicitly return the block to the pool
drop(x);
debug::exit(debug::EXIT_SUCCESS);
}
#[task(priority = 2)]
fn bar(_: bar::Context, x: Box<P>) {
hprintln!("bar({:?})", x.as_ptr()).unwrap();
// this is done automatically so we can omit the call to `drop`
// drop(x);
}
extern "C" {
fn UART0();
fn UART1();
}
};

View file

@ -8,18 +8,17 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(spawn = [bar])] #[init(spawn = [bar])]
fn init() { fn init(c: init::Context) {
spawn.bar().unwrap(); c.spawn.bar().unwrap();
} }
#[inline(never)] #[inline(never)]
#[task] #[task]
fn foo() { fn foo(_: foo::Context) {
hprintln!("foo").unwrap(); hprintln!("foo").unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
@ -29,8 +28,8 @@ const APP: () = {
#[inline(never)] #[inline(never)]
#[link_section = ".data.bar"] #[link_section = ".data.bar"]
#[task(priority = 2, spawn = [foo])] #[task(priority = 2, spawn = [foo])]
fn bar() { fn bar(c: bar::Context) {
spawn.foo().unwrap(); c.spawn.foo().unwrap();
} }
extern "C" { extern "C" {

View file

@ -9,21 +9,20 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
// A resource // A resource
static mut SHARED: u32 = 0; static mut SHARED: u32 = 0;
#[init] #[init]
fn init() { fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1); rtfm::pend(Interrupt::UART1);
} }
#[idle] #[idle]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
// error: `SHARED` can't be accessed from this context // error: `SHARED` can't be accessed from this context
@ -34,17 +33,17 @@ const APP: () = {
// `SHARED` can be access from this context // `SHARED` can be access from this context
#[interrupt(resources = [SHARED])] #[interrupt(resources = [SHARED])]
fn UART0() { fn UART0(mut c: UART0::Context) {
*resources.SHARED += 1; *c.resources.SHARED += 1;
hprintln!("UART0: SHARED = {}", resources.SHARED).unwrap(); hprintln!("UART0: SHARED = {}", c.resources.SHARED).unwrap();
} }
// `SHARED` can be access from this context // `SHARED` can be access from this context
#[interrupt(resources = [SHARED])] #[interrupt(resources = [SHARED])]
fn UART1() { fn UART1(mut c: UART1::Context) {
*resources.SHARED += 1; *c.resources.SHARED += 1;
hprintln!("UART1: SHARED = {}", resources.SHARED).unwrap(); hprintln!("UART1: SHARED = {}", c.resources.SHARED).unwrap();
} }
}; };

View file

@ -8,31 +8,31 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::hprintln; use cortex_m_semihosting::hprintln;
use rtfm::{app, Instant}; use rtfm::Instant;
// NOTE: does NOT work on QEMU! // NOTE: does NOT work on QEMU!
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(schedule = [foo, bar])] #[init(schedule = [foo, bar])]
fn init() { fn init(c: init::Context) {
let now = Instant::now(); let now = Instant::now();
hprintln!("init @ {:?}", now).unwrap(); hprintln!("init @ {:?}", now).unwrap();
// Schedule `foo` to run 8e6 cycles (clock cycles) in the future // Schedule `foo` to run 8e6 cycles (clock cycles) in the future
schedule.foo(now + 8_000_000.cycles()).unwrap(); c.schedule.foo(now + 8_000_000.cycles()).unwrap();
// Schedule `bar` to run 4e6 cycles in the future // Schedule `bar` to run 4e6 cycles in the future
schedule.bar(now + 4_000_000.cycles()).unwrap(); c.schedule.bar(now + 4_000_000.cycles()).unwrap();
} }
#[task] #[task]
fn foo() { fn foo(_: foo::Context) {
hprintln!("foo @ {:?}", Instant::now()).unwrap(); hprintln!("foo @ {:?}", Instant::now()).unwrap();
} }
#[task] #[task]
fn bar() { fn bar(_: bar::Context) {
hprintln!("bar @ {:?}", Instant::now()).unwrap(); hprintln!("bar @ {:?}", Instant::now()).unwrap();
} }

View file

@ -18,17 +18,17 @@ const APP: () = {
static mut SHARED: Option<MustBeSend> = None; static mut SHARED: Option<MustBeSend> = None;
#[init(resources = [SHARED])] #[init(resources = [SHARED])]
fn init() { fn init(c: init::Context) {
// this `message` will be sent to task `UART0` // this `message` will be sent to task `UART0`
let message = MustBeSend; let message = MustBeSend;
*resources.SHARED = Some(message); *c.resources.SHARED = Some(message);
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
} }
#[interrupt(resources = [SHARED])] #[interrupt(resources = [SHARED])]
fn UART0() { fn UART0(c: UART0::Context) {
if let Some(message) = resources.SHARED.take() { if let Some(message) = c.resources.SHARED.take() {
// `message` has been received // `message` has been received
drop(message); drop(message);

View file

@ -1,61 +0,0 @@
//! examples/singleton.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
extern crate panic_semihosting;
use alloc_singleton::stable::pool::{Box, Pool};
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[Singleton(Send)]
static mut M: [u32; 2] = [0; 2];
static mut P: Pool<M> = ();
#[init(resources = [M])]
fn init() -> init::LateResources {
rtfm::pend(Interrupt::I2C0);
init::LateResources {
P: Pool::new(resources.M),
}
}
#[interrupt(
priority = 2,
resources = [P],
spawn = [foo, bar],
)]
fn I2C0() {
spawn.foo(resources.P.alloc(1).unwrap()).unwrap();
spawn.bar(resources.P.alloc(2).unwrap()).unwrap();
}
#[task(resources = [P])]
fn foo(x: Box<M>) {
hprintln!("foo({})", x).unwrap();
resources.P.lock(|p| p.dealloc(x));
debug::exit(debug::EXIT_SUCCESS);
}
#[task(priority = 2, resources = [P])]
fn bar(x: Box<M>) {
hprintln!("bar({})", x).unwrap();
resources.P.dealloc(x);
}
extern "C" {
fn UART0();
fn UART1();
}
};

View file

@ -13,5 +13,5 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
}; };

View file

@ -9,14 +9,13 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static KEY: u32 = (); static KEY: u32 = ();
#[init] #[init]
fn init() -> init::LateResources { fn init(_: init::Context) -> init::LateResources {
rtfm::pend(Interrupt::UART0); rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1); rtfm::pend(Interrupt::UART1);
@ -24,14 +23,14 @@ const APP: () = {
} }
#[interrupt(resources = [KEY])] #[interrupt(resources = [KEY])]
fn UART0() { fn UART0(c: UART0::Context) {
hprintln!("UART0(KEY = {:#x})", resources.KEY).unwrap(); hprintln!("UART0(KEY = {:#x})", c.resources.KEY).unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }
#[interrupt(priority = 2, resources = [KEY])] #[interrupt(priority = 2, resources = [KEY])]
fn UART1() { fn UART1(c: UART1::Context) {
hprintln!("UART1(KEY = {:#x})", resources.KEY).unwrap(); hprintln!("UART1(KEY = {:#x})", c.resources.KEY).unwrap();
} }
}; };

View file

@ -8,38 +8,37 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use rtfm::app;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(spawn = [foo])] #[init(spawn = [foo])]
fn init() { fn init(c: init::Context) {
spawn.foo().unwrap(); c.spawn.foo().unwrap();
} }
#[task(spawn = [bar, baz])] #[task(spawn = [bar, baz])]
fn foo() { fn foo(c: foo::Context) {
hprintln!("foo").unwrap(); hprintln!("foo").unwrap();
// spawns `bar` onto the task scheduler // spawns `bar` onto the task scheduler
// `foo` and `bar` have the same priority so `bar` will not run until // `foo` and `bar` have the same priority so `bar` will not run until
// after `foo` terminates // after `foo` terminates
spawn.bar().unwrap(); c.spawn.bar().unwrap();
// spawns `baz` onto the task scheduler // spawns `baz` onto the task scheduler
// `baz` has higher priority than `foo` so it immediately preempts `foo` // `baz` has higher priority than `foo` so it immediately preempts `foo`
spawn.baz().unwrap(); c.spawn.baz().unwrap();
} }
#[task] #[task]
fn bar() { fn bar(_: bar::Context) {
hprintln!("bar").unwrap(); hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }
#[task(priority = 2)] #[task(priority = 2)]
fn baz() { fn baz(_: baz::Context) {
hprintln!("baz").unwrap(); hprintln!("baz").unwrap();
} }

View file

@ -8,45 +8,45 @@
extern crate panic_semihosting; extern crate panic_semihosting;
use cortex_m_semihosting::debug; use cortex_m_semihosting::debug;
use rtfm::{app, Exclusive, Instant}; use rtfm::{Exclusive, Instant};
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut SHARED: u32 = 0; static mut SHARED: u32 = 0;
#[init(schedule = [foo], spawn = [foo])] #[init(schedule = [foo], spawn = [foo])]
fn init() { fn init(c: init::Context) {
let _: Instant = start; let _: Instant = c.start;
let _: rtfm::Peripherals = core; let _: rtfm::Peripherals = c.core;
let _: lm3s6965::Peripherals = device; let _: lm3s6965::Peripherals = c.device;
let _: init::Schedule = schedule; let _: init::Schedule = c.schedule;
let _: init::Spawn = spawn; let _: init::Spawn = c.spawn;
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }
#[exception(schedule = [foo], spawn = [foo])] #[exception(schedule = [foo], spawn = [foo])]
fn SVCall() { fn SVCall(c: SVCall::Context) {
let _: Instant = start; let _: Instant = c.start;
let _: SVCall::Schedule = schedule; let _: SVCall::Schedule = c.schedule;
let _: SVCall::Spawn = spawn; let _: SVCall::Spawn = c.spawn;
} }
#[interrupt(resources = [SHARED], schedule = [foo], spawn = [foo])] #[interrupt(resources = [SHARED], schedule = [foo], spawn = [foo])]
fn UART0() { fn UART0(c: UART0::Context) {
let _: Instant = start; let _: Instant = c.start;
let _: resources::SHARED = resources.SHARED; let _: resources::SHARED = c.resources.SHARED;
let _: UART0::Schedule = schedule; let _: UART0::Schedule = c.schedule;
let _: UART0::Spawn = spawn; let _: UART0::Spawn = c.spawn;
} }
#[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])] #[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])]
fn foo() { fn foo(c: foo::Context) {
let _: Instant = scheduled; let _: Instant = c.scheduled;
let _: Exclusive<u32> = resources.SHARED; let _: Exclusive<u32> = c.resources.SHARED;
let _: foo::Resources = resources; let _: foo::Resources = c.resources;
let _: foo::Schedule = schedule; let _: foo::Schedule = c.schedule;
let _: foo::Spawn = spawn; let _: foo::Spawn = c.spawn;
} }
extern "C" { extern "C" {

View file

@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm-macros" name = "cortex-m-rtfm-macros"
readme = "../README.md" readme = "../README.md"
repository = "https://github.com/japaric/cortex-m-rtfm" repository = "https://github.com/japaric/cortex-m-rtfm"
version = "0.4.3" version = "0.5.0-alpha.1"
[lib] [lib]
proc-macro = true proc-macro = true
@ -22,10 +22,6 @@ proc-macro2 = "0.4.24"
features = ["extra-traits", "full"] features = ["extra-traits", "full"]
version = "0.15.23" version = "0.15.23"
[dependencies.rand]
default-features = false
version = "0.5.5"
[features] [features]
timer-queue = [] timer-queue = []
nightly = [] nightly = []

View file

@ -190,19 +190,20 @@ pub fn app(app: &App) -> Analysis {
} }
// Ceiling analysis of free queues (consumer end point) -- first pass // Ceiling analysis of free queues (consumer end point) -- first pass
// Ceiling analysis of ready queues (producer end point) // Ceiling analysis of ready queues (producer end point) -- first pass
// Also compute more Send-ness requirements // Also compute more Send-ness requirements
let mut free_queues: HashMap<_, _> = app.tasks.keys().map(|task| (task.clone(), 0)).collect(); let mut free_queues = HashMap::new();
let mut ready_queues: HashMap<_, _> = dispatchers.keys().map(|level| (*level, 0)).collect(); let mut ready_queues = HashMap::new();
for (priority, task) in app.spawn_calls() { for (priority, task) in app.spawn_calls() {
if let Some(priority) = priority { if let Some(priority) = priority {
// Users of `spawn` contend for the to-be-spawned task FREE_QUEUE // Users of `spawn` contend for the spawnee FREE_QUEUE
let c = free_queues.get_mut(task).expect("BUG: free_queue.get_mut"); let c = free_queues.entry(task.clone()).or_default();
*c = cmp::max(*c, priority); *c = cmp::max(*c, priority);
// Users of `spawn` contend for the spawnee's dispatcher READY_QUEUE
let c = ready_queues let c = ready_queues
.get_mut(&app.tasks[task].args.priority) .entry(app.tasks[task].args.priority)
.expect("BUG: ready_queues.get_mut"); .or_default();
*c = cmp::max(*c, priority); *c = cmp::max(*c, priority);
// Send is required when sending messages from a task whose priority doesn't match the // Send is required when sending messages from a task whose priority doesn't match the
@ -215,16 +216,23 @@ pub fn app(app: &App) -> Analysis {
} }
} }
// Ceiling analysis of ready queues (producer end point) -- second pass
// Ceiling analysis of free queues (consumer end point) -- second pass // Ceiling analysis of free queues (consumer end point) -- second pass
// Ceiling analysis of the timer queue // Ceiling analysis of the timer queue
let mut tq_ceiling = tq_priority; let mut tq_ceiling = tq_priority;
for (priority, task) in app.schedule_calls() { for (priority, task) in app.schedule_calls() {
// the system timer handler contends for the spawnee's dispatcher READY_QUEUE
let c = ready_queues
.entry(app.tasks[task].args.priority)
.or_default();
*c = cmp::max(*c, tq_priority);
if let Some(priority) = priority { if let Some(priority) = priority {
// Users of `schedule` contend for the to-be-spawned task FREE_QUEUE (consumer end point) // Users of `schedule` contend for the spawnee task FREE_QUEUE
let c = free_queues.get_mut(task).expect("BUG: free_queue.get_mut"); let c = free_queues.entry(task.clone()).or_default();
*c = cmp::max(*c, priority); *c = cmp::max(*c, priority);
// Users of `schedule` contend for the timer queu // Users of `schedule` contend for the timer queue
tq_ceiling = cmp::max(tq_ceiling, priority); tq_ceiling = cmp::max(tq_ceiling, priority);
} else { } else {
// spawns from `init` are excluded from the ceiling analysis // spawns from `init` are excluded from the ceiling analysis

View file

@ -35,21 +35,12 @@ pub fn app(app: &App) -> parse::Result<()> {
} }
} }
// Check that all late resources have been initialized in `#[init]` if `init` has signature // Check that `init` returns `LateResources` if there's any declared late resource
// `fn()` if !app.init.returns_late_resources && app.resources.iter().any(|(_, res)| res.expr.is_none()) {
if !app.init.returns_late_resources { return Err(parse::Error::new(
for res in app.init.span,
app.resources "late resources have been specified so `init` must return `init::LateResources`",
.iter() ));
.filter_map(|(name, res)| if res.expr.is_none() { Some(name) } else { None })
{
if app.init.assigns.iter().all(|assign| assign.left != *res) {
return Err(parse::Error::new(
res.span(),
"late resources MUST be initialized at the end of `init`",
));
}
}
} }
// Check that all referenced tasks have been declared // Check that all referenced tasks have been declared
@ -128,7 +119,7 @@ pub fn app(app: &App) -> parse::Result<()> {
} else if app.init.returns_late_resources { } else if app.init.returns_late_resources {
return Err(parse::Error::new( return Err(parse::Error::new(
Span::call_site(), Span::call_site(),
"`init` signature must be `[unsafe] fn()` if there are no late resources", "`init` signature must be `fn(init::Context)` if there are no late resources",
)); ));
} }

File diff suppressed because it is too large Load diff

View file

@ -288,9 +288,9 @@ mod syntax;
pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
// Parse // Parse
let args = parse_macro_input!(args as syntax::AppArgs); let args = parse_macro_input!(args as syntax::AppArgs);
let items = parse_macro_input!(input as syntax::Input).items; let input = parse_macro_input!(input as syntax::Input);
let app = match syntax::App::parse(items, args) { let app = match syntax::App::parse(input.items, args) {
Err(e) => return e.to_compile_error().into(), Err(e) => return e.to_compile_error().into(),
Ok(app) => app, Ok(app) => app,
}; };
@ -304,5 +304,5 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
let analysis = analyze::app(&app); let analysis = analyze::app(&app);
// Code generation // Code generation
codegen::app(&app, &analysis).into() codegen::app(&input.ident, &app, &analysis).into()
} }

View file

@ -11,8 +11,8 @@ use syn::{
spanned::Spanned, spanned::Spanned,
token::Brace, token::Brace,
ArgCaptured, AttrStyle, Attribute, Expr, FnArg, ForeignItem, Ident, IntSuffix, Item, ItemFn, ArgCaptured, AttrStyle, Attribute, Expr, FnArg, ForeignItem, Ident, IntSuffix, Item, ItemFn,
ItemForeignMod, ItemStatic, LitInt, Path, PathArguments, PathSegment, ReturnType, Stmt, Token, ItemForeignMod, ItemStatic, LitInt, Pat, Path, PathArguments, ReturnType, Stmt, Token, Type,
Type, TypeTuple, Visibility, TypeTuple, Visibility,
}; };
pub struct AppArgs { pub struct AppArgs {
@ -70,7 +70,7 @@ impl Parse for AppArgs {
pub struct Input { pub struct Input {
_const_token: Token![const], _const_token: Token![const],
_ident: Ident, pub ident: Ident,
_colon_token: Token![:], _colon_token: Token![:],
_ty: TypeTuple, _ty: TypeTuple,
_eq_token: Token![=], _eq_token: Token![=],
@ -94,7 +94,7 @@ impl Parse for Input {
let content; let content;
Ok(Input { Ok(Input {
_const_token: input.parse()?, _const_token: input.parse()?,
_ident: input.parse()?, ident: input.parse()?,
_colon_token: input.parse()?, _colon_token: input.parse()?,
_ty: input.parse()?, _ty: input.parse()?,
_eq_token: input.parse()?, _eq_token: input.parse()?,
@ -435,7 +435,7 @@ pub type FreeInterrupts = BTreeMap<Ident, FreeInterrupt>;
pub struct Idle { pub struct Idle {
pub args: IdleArgs, pub args: IdleArgs,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>, pub context: Pat,
pub statics: BTreeMap<Ident, Static>, pub statics: BTreeMap<Ident, Static>,
pub stmts: Vec<Stmt>, pub stmts: Vec<Stmt>,
} }
@ -444,34 +444,29 @@ pub type IdleArgs = InitArgs;
impl Idle { impl Idle {
fn check(args: IdleArgs, item: ItemFn) -> parse::Result<Self> { fn check(args: IdleArgs, item: ItemFn) -> parse::Result<Self> {
let valid_signature = item.vis == Visibility::Inherited let valid_signature =
&& item.constness.is_none() check_signature(&item) && item.decl.inputs.len() == 1 && is_bottom(&item.decl.output);
&& item.asyncness.is_none()
&& item.abi.is_none()
&& item.decl.generics.params.is_empty()
&& item.decl.generics.where_clause.is_none()
&& item.decl.inputs.is_empty()
&& item.decl.variadic.is_none()
&& is_bottom(&item.decl.output);
let span = item.span(); let span = item.span();
if !valid_signature { if valid_signature {
return Err(parse::Error::new( if let Some((context, _)) = check_inputs(item.decl.inputs, "idle") {
span, let (statics, stmts) = extract_statics(item.block.stmts);
"`idle` must have type signature `[unsafe] fn() -> !`",
)); return Ok(Idle {
args,
attrs: item.attrs,
context,
statics: Static::parse(statics)?,
stmts,
});
}
} }
let (statics, stmts) = extract_statics(item.block.stmts); Err(parse::Error::new(
span,
Ok(Idle { "`idle` must have type signature `fn(idle::Context) -> !`",
args, ))
attrs: item.attrs,
unsafety: item.unsafety,
statics: Static::parse(statics)?,
stmts,
})
} }
} }
@ -596,34 +591,21 @@ impl Parse for InitArgs {
} }
} }
// TODO remove in v0.5.x
pub struct Assign {
pub attrs: Vec<Attribute>,
pub left: Ident,
pub right: Box<Expr>,
}
pub struct Init { pub struct Init {
pub args: InitArgs, pub args: InitArgs,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>,
pub statics: BTreeMap<Ident, Static>, pub statics: BTreeMap<Ident, Static>,
pub context: Pat,
pub stmts: Vec<Stmt>, pub stmts: Vec<Stmt>,
// TODO remove in v0.5.x
pub assigns: Vec<Assign>,
pub returns_late_resources: bool, pub returns_late_resources: bool,
pub span: Span,
} }
impl Init { impl Init {
fn check(args: InitArgs, item: ItemFn) -> parse::Result<Self> { fn check(args: InitArgs, item: ItemFn) -> parse::Result<Self> {
let mut valid_signature = item.vis == Visibility::Inherited let mut valid_signature = check_signature(&item) && item.decl.inputs.len() == 1;
&& item.constness.is_none()
&& item.asyncness.is_none() const DONT_CARE: bool = false;
&& item.abi.is_none()
&& item.decl.generics.params.is_empty()
&& item.decl.generics.where_clause.is_none()
&& item.decl.inputs.is_empty()
&& item.decl.variadic.is_none();
let returns_late_resources = match &item.decl.output { let returns_late_resources = match &item.decl.output {
ReturnType::Default => false, ReturnType::Default => false,
@ -636,36 +618,25 @@ impl Init {
} else { } else {
valid_signature = false; valid_signature = false;
false // don't care DONT_CARE
} }
} }
Type::Path(p) => { Type::Path(_) => {
let mut segments = p.path.segments.iter(); if is_path(ty, &["init", "LateResources"]) {
if p.qself.is_none()
&& p.path.leading_colon.is_none()
&& p.path.segments.len() == 2
&& segments.next().map(|s| {
s.arguments == PathArguments::None && s.ident.to_string() == "init"
}) == Some(true)
&& segments.next().map(|s| {
s.arguments == PathArguments::None
&& s.ident.to_string() == "LateResources"
}) == Some(true)
{
// -> init::LateResources // -> init::LateResources
true true
} else { } else {
valid_signature = false; valid_signature = false;
false // don't care DONT_CARE
} }
} }
_ => { _ => {
valid_signature = false; valid_signature = false;
false // don't care DONT_CARE
} }
} }
} }
@ -673,29 +644,26 @@ impl Init {
let span = item.span(); let span = item.span();
if !valid_signature { if valid_signature {
return Err(parse::Error::new( if let Some((context, _)) = check_inputs(item.decl.inputs, "init") {
span, let (statics, stmts) = extract_statics(item.block.stmts);
"`init` must have type signature `[unsafe] fn() [-> init::LateResources]`",
)); return Ok(Init {
args,
attrs: item.attrs,
statics: Static::parse(statics)?,
context,
stmts,
returns_late_resources,
span,
});
}
} }
let (statics, stmts) = extract_statics(item.block.stmts); Err(parse::Error::new(
let (stmts, assigns) = if returns_late_resources { span,
(stmts, vec![]) "`init` must have type signature `fn(init::Context) [-> init::LateResources]`",
} else { ))
extract_assignments(stmts)
};
Ok(Init {
args,
attrs: item.attrs,
unsafety: item.unsafety,
statics: Static::parse(statics)?,
stmts,
assigns,
returns_late_resources,
})
} }
} }
@ -725,8 +693,8 @@ impl Default for Args {
pub struct Exception { pub struct Exception {
pub args: ExceptionArgs, pub args: ExceptionArgs,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>,
pub statics: BTreeMap<Ident, Static>, pub statics: BTreeMap<Ident, Static>,
pub context: Pat,
pub stmts: Vec<Stmt>, pub stmts: Vec<Stmt>,
} }
@ -770,61 +738,67 @@ impl Parse for ExceptionArgs {
impl Exception { impl Exception {
fn check(args: ExceptionArgs, item: ItemFn) -> parse::Result<Self> { fn check(args: ExceptionArgs, item: ItemFn) -> parse::Result<Self> {
let valid_signature = item.vis == Visibility::Inherited let valid_signature =
&& item.constness.is_none() check_signature(&item) && item.decl.inputs.len() == 1 && is_unit(&item.decl.output);
&& item.asyncness.is_none()
&& item.abi.is_none()
&& item.decl.generics.params.is_empty()
&& item.decl.generics.where_clause.is_none()
&& item.decl.inputs.is_empty()
&& item.decl.variadic.is_none()
&& is_unit(&item.decl.output);
if !valid_signature { let span = item.span();
return Err(parse::Error::new(
item.span(),
"`exception` handlers must have type signature `[unsafe] fn()`",
));
}
let span = item.ident.span(); let name = item.ident.to_string();
match &*args.binds.as_ref().unwrap_or(&item.ident).to_string() { if valid_signature {
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" if let Some((context, _)) = check_inputs(item.decl.inputs, &name) {
| "DebugMonitor" | "PendSV" => {} // OK let span = item.ident.span();
"SysTick" => { match &*args
if cfg!(feature = "timer-queue") { .binds
return Err(parse::Error::new( .as_ref()
.map(|ident| ident.to_string())
.unwrap_or(name)
{
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
| "DebugMonitor" | "PendSV" => {} // OK
"SysTick" => {
if cfg!(feature = "timer-queue") {
return Err(parse::Error::new(
span,
"the `SysTick` exception can't be used because it's used by \
the runtime when the `timer-queue` feature is enabled",
));
}
}
_ => {
return Err(parse::Error::new(
span, span,
"the `SysTick` exception can't be used because it's used by \ "only exceptions with configurable priority can be used as hardware tasks",
the runtime when the `timer-queue` feature is enabled",
)); ));
}
} }
}
_ => { let (statics, stmts) = extract_statics(item.block.stmts);
return Err(parse::Error::new(
span, return Ok(Exception {
"only exceptions with configurable priority can be used as hardware tasks", args,
)); attrs: item.attrs,
statics: Static::parse(statics)?,
context,
stmts,
});
} }
} }
let (statics, stmts) = extract_statics(item.block.stmts); Err(parse::Error::new(
span,
Ok(Exception { &format!(
args, "this `exception` handler must have type signature `fn({}::Context)`",
attrs: item.attrs, name
unsafety: item.unsafety, ),
statics: Static::parse(statics)?, ))
stmts,
})
} }
} }
pub struct Interrupt { pub struct Interrupt {
pub args: InterruptArgs, pub args: InterruptArgs,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>,
pub statics: BTreeMap<Ident, Static>, pub statics: BTreeMap<Ident, Static>,
pub context: Pat,
pub stmts: Vec<Stmt>, pub stmts: Vec<Stmt>,
} }
@ -832,49 +806,47 @@ pub type InterruptArgs = ExceptionArgs;
impl Interrupt { impl Interrupt {
fn check(args: InterruptArgs, item: ItemFn) -> parse::Result<Self> { fn check(args: InterruptArgs, item: ItemFn) -> parse::Result<Self> {
let valid_signature = item.vis == Visibility::Inherited let valid_signature =
&& item.constness.is_none() check_signature(&item) && item.decl.inputs.len() == 1 && is_unit(&item.decl.output);
&& item.asyncness.is_none()
&& item.abi.is_none()
&& item.decl.generics.params.is_empty()
&& item.decl.generics.where_clause.is_none()
&& item.decl.inputs.is_empty()
&& item.decl.variadic.is_none()
&& is_unit(&item.decl.output);
let span = item.span(); let span = item.span();
if !valid_signature { let name = item.ident.to_string();
return Err(parse::Error::new( if valid_signature {
span, if let Some((context, _)) = check_inputs(item.decl.inputs, &name) {
"`interrupt` handlers must have type signature `[unsafe] fn()`", match &*name {
)); "init" | "idle" | "resources" => {
} return Err(parse::Error::new(
span,
"`interrupt` handlers can NOT be named `idle`, `init` or `resources`",
));
}
_ => {}
}
match &*item.ident.to_string() { let (statics, stmts) = extract_statics(item.block.stmts);
"init" | "idle" | "resources" => {
return Err(parse::Error::new( return Ok(Interrupt {
span, args,
"`interrupt` handlers can NOT be named `idle`, `init` or `resources`", attrs: item.attrs,
)); statics: Static::parse(statics)?,
context,
stmts,
});
} }
_ => {}
} }
let (statics, stmts) = extract_statics(item.block.stmts); Err(parse::Error::new(
span,
Ok(Interrupt { format!(
args, "this `interrupt` handler must have type signature `fn({}::Context)`",
attrs: item.attrs, name
unsafety: item.unsafety, ),
statics: Static::parse(statics)?, ))
stmts,
})
} }
} }
pub struct Resource { pub struct Resource {
pub singleton: bool,
pub cfgs: Vec<Attribute>, pub cfgs: Vec<Attribute>,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub mutability: Option<Token![mut]>, pub mutability: Option<Token![mut]>,
@ -883,7 +855,7 @@ pub struct Resource {
} }
impl Resource { impl Resource {
fn check(mut item: ItemStatic) -> parse::Result<Resource> { fn check(item: ItemStatic) -> parse::Result<Resource> {
if item.vis != Visibility::Inherited { if item.vis != Visibility::Inherited {
return Err(parse::Error::new( return Err(parse::Error::new(
item.span(), item.span(),
@ -896,19 +868,9 @@ impl Resource {
_ => false, _ => false,
}; };
let pos = item.attrs.iter().position(|attr| eq(attr, "Singleton"));
if let Some(pos) = pos {
item.attrs[pos].path.segments.insert(
0,
PathSegment::from(Ident::new("owned_singleton", Span::call_site())),
);
}
let (cfgs, attrs) = extract_cfgs(item.attrs); let (cfgs, attrs) = extract_cfgs(item.attrs);
Ok(Resource { Ok(Resource {
singleton: pos.is_some(),
cfgs, cfgs,
attrs, attrs,
mutability: item.mutability, mutability: item.mutability,
@ -1177,66 +1139,61 @@ pub struct Task {
pub args: TaskArgs, pub args: TaskArgs,
pub cfgs: Vec<Attribute>, pub cfgs: Vec<Attribute>,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>,
pub inputs: Vec<ArgCaptured>, pub inputs: Vec<ArgCaptured>,
pub context: Pat,
pub statics: BTreeMap<Ident, Static>, pub statics: BTreeMap<Ident, Static>,
pub stmts: Vec<Stmt>, pub stmts: Vec<Stmt>,
} }
impl Task { impl Task {
fn check(args: TaskArgs, item: ItemFn) -> parse::Result<Self> { fn check(args: TaskArgs, item: ItemFn) -> parse::Result<Self> {
let valid_signature = item.vis == Visibility::Inherited let valid_signature =
&& item.constness.is_none() check_signature(&item) && !item.decl.inputs.is_empty() && is_unit(&item.decl.output);
&& item.asyncness.is_none()
&& item.abi.is_none()
&& item.decl.generics.params.is_empty()
&& item.decl.generics.where_clause.is_none()
&& item.decl.variadic.is_none()
&& is_unit(&item.decl.output);
let span = item.span(); let span = item.span();
if !valid_signature { let name = item.ident.to_string();
return Err(parse::Error::new( if valid_signature {
span, if let Some((context, rest)) = check_inputs(item.decl.inputs, &name) {
"`task` handlers must have type signature `[unsafe] fn(..)`", let (statics, stmts) = extract_statics(item.block.stmts);
));
}
let (statics, stmts) = extract_statics(item.block.stmts); let inputs = rest.map_err(|arg| {
parse::Error::new(
arg.span(),
"inputs must be named arguments (e.f. `foo: u32`) and not include `self`",
)
})?;
let mut inputs = vec![]; match &*name {
for input in item.decl.inputs { "init" | "idle" | "resources" => {
if let FnArg::Captured(capture) = input { return Err(parse::Error::new(
inputs.push(capture); span,
} else { "`task` handlers can NOT be named `idle`, `init` or `resources`",
return Err(parse::Error::new( ));
span, }
"inputs must be named arguments (e.f. `foo: u32`) and not include `self`", _ => {}
)); }
let (cfgs, attrs) = extract_cfgs(item.attrs);
return Ok(Task {
args,
cfgs,
attrs,
inputs,
context,
statics: Static::parse(statics)?,
stmts,
});
} }
} }
match &*item.ident.to_string() { Err(parse::Error::new(
"init" | "idle" | "resources" => { span,
return Err(parse::Error::new( &format!(
span, "this `task` handler must have type signature `fn({}::Context, ..)`",
"`task` handlers can NOT be named `idle`, `init` or `resources`", name
)); ),
} ))
_ => {}
}
let (cfgs, attrs) = extract_cfgs(item.attrs);
Ok(Task {
args,
cfgs,
attrs,
unsafety: item.unsafety,
inputs,
statics: Static::parse(statics)?,
stmts,
})
} }
} }
@ -1335,38 +1292,69 @@ fn extract_statics(stmts: Vec<Stmt>) -> (Statics, Vec<Stmt>) {
(statics, stmts) (statics, stmts)
} }
// TODO remove in v0.5.x // checks that the list of arguments has the form `#pat: #name::Context, (..)`
fn extract_assignments(stmts: Vec<Stmt>) -> (Vec<Stmt>, Vec<Assign>) { //
let mut istmts = stmts.into_iter().rev(); // if the check succeeds it returns `#pat` plus the remaining arguments
fn check_inputs(
inputs: Punctuated<FnArg, Token![,]>,
name: &str,
) -> Option<(Pat, Result<Vec<ArgCaptured>, FnArg>)> {
let mut inputs = inputs.into_iter();
let mut assigns = vec![]; match inputs.next() {
let mut stmts = vec![]; Some(FnArg::Captured(first)) => {
while let Some(stmt) = istmts.next() { if is_path(&first.ty, &[name, "Context"]) {
match stmt { let rest = inputs
Stmt::Semi(Expr::Assign(assign), semi) => { .map(|arg| match arg {
if let Expr::Path(ref expr) = *assign.left { FnArg::Captured(arg) => Ok(arg),
if expr.path.segments.len() == 1 { _ => Err(arg),
assigns.push(Assign { })
attrs: assign.attrs, .collect::<Result<Vec<_>, _>>();
left: expr.path.segments[0].ident.clone(),
right: assign.right,
});
continue;
}
}
stmts.push(Stmt::Semi(Expr::Assign(assign), semi)); Some((first.pat, rest))
} } else {
_ => { None
stmts.push(stmt);
break;
} }
} }
_ => None,
} }
}
stmts.extend(istmts); /// checks that a function signature
///
/// - has no bounds (like where clauses)
/// - is not `async`
/// - is not `const`
/// - is not `unsafe`
/// - is not generic (has no type parametrs)
/// - is not variadic
/// - uses the Rust ABI (and not e.g. "C")
fn check_signature(item: &ItemFn) -> bool {
item.vis == Visibility::Inherited
&& item.constness.is_none()
&& item.asyncness.is_none()
&& item.abi.is_none()
&& item.unsafety.is_none()
&& item.decl.generics.params.is_empty()
&& item.decl.generics.where_clause.is_none()
&& item.decl.variadic.is_none()
}
(stmts.into_iter().rev().collect(), assigns) fn is_path(ty: &Type, segments: &[&str]) -> bool {
match ty {
Type::Path(tpath) if tpath.qself.is_none() => {
tpath.path.segments.len() == segments.len()
&& tpath
.path
.segments
.iter()
.zip(segments)
.all(|(lhs, rhs)| lhs.ident == **rhs)
}
_ => false,
}
} }
fn is_bottom(ty: &ReturnType) -> bool { fn is_bottom(ty: &ReturnType) -> bool {

View file

@ -1,7 +1,5 @@
//! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE //! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
#[cfg(not(feature = "nightly"))]
use core::ptr;
use core::{cell::Cell, u8}; use core::{cell::Cell, u8};
#[cfg(armv7m)] #[cfg(armv7m)]
@ -14,25 +12,31 @@ pub use heapless::consts;
use heapless::spsc::{Queue, SingleCore}; use heapless::spsc::{Queue, SingleCore};
#[cfg(feature = "timer-queue")] #[cfg(feature = "timer-queue")]
pub use crate::tq::{isr as sys_tick, NotReady, TimerQueue}; pub use crate::tq::{NotReady, TimerQueue};
pub type FreeQueue<N> = Queue<u8, N, usize, SingleCore>; pub type FreeQueue<N> = Queue<u8, N, u8, SingleCore>;
pub type ReadyQueue<T, N> = Queue<(T, u8), N, usize, SingleCore>; pub type ReadyQueue<T, N> = Queue<(T, u8), N, u8, SingleCore>;
#[cfg(armv7m)] #[cfg(armv7m)]
#[inline(always)] #[inline(always)]
pub fn run<F>(f: F) pub fn run<F>(priority: u8, f: F)
where where
F: FnOnce(), F: FnOnce(),
{ {
let initial = basepri::read(); if priority == 1 {
f(); // if the priority of this interrupt is `1` then BASEPRI can only be `0`
unsafe { basepri::write(initial) } f();
unsafe { basepri::write(0) }
} else {
let initial = basepri::read();
f();
unsafe { basepri::write(initial) }
}
} }
#[cfg(not(armv7m))] #[cfg(not(armv7m))]
#[inline(always)] #[inline(always)]
pub fn run<F>(f: F) pub fn run<F>(_priority: u8, f: F)
where where
F: FnOnce(), F: FnOnce(),
{ {
@ -52,7 +56,7 @@ impl Priority {
} }
} }
// these two methods are used by claim (see below) but can't be used from the RTFM application // these two methods are used by `lock` (see below) but can't be used from the RTFM application
#[inline(always)] #[inline(always)]
fn set(&self, value: u8) { fn set(&self, value: u8) {
self.inner.set(value) self.inner.set(value)
@ -64,13 +68,12 @@ impl Priority {
} }
} }
#[cfg(feature = "nightly")] // We newtype `core::mem::MaybeUninit` so the end-user doesn't need `#![feature(maybe_uninit)]` in
// their code
pub struct MaybeUninit<T> { pub struct MaybeUninit<T> {
// we newtype so the end-user doesn't need `#![feature(maybe_uninit)]` in their code
inner: core::mem::MaybeUninit<T>, inner: core::mem::MaybeUninit<T>,
} }
#[cfg(feature = "nightly")]
impl<T> MaybeUninit<T> { impl<T> MaybeUninit<T> {
pub const fn uninit() -> Self { pub const fn uninit() -> Self {
MaybeUninit { MaybeUninit {
@ -86,64 +89,15 @@ impl<T> MaybeUninit<T> {
self.inner.as_mut_ptr() self.inner.as_mut_ptr()
} }
pub unsafe fn read(&self) -> T {
self.inner.read()
}
pub fn write(&mut self, value: T) -> &mut T { pub fn write(&mut self, value: T) -> &mut T {
self.inner.write(value) self.inner.write(value)
} }
} }
#[cfg(not(feature = "nightly"))]
pub struct MaybeUninit<T> {
value: Option<T>,
}
#[cfg(not(feature = "nightly"))]
const MSG: &str =
"you have hit a bug (UB) in RTFM implementation; try enabling this crate 'nightly' feature";
#[cfg(not(feature = "nightly"))]
impl<T> MaybeUninit<T> {
pub const fn uninit() -> Self {
MaybeUninit { value: None }
}
pub fn as_ptr(&self) -> *const T {
if let Some(x) = self.value.as_ref() {
x
} else {
unreachable!(MSG)
}
}
pub fn as_mut_ptr(&mut self) -> *mut T {
if let Some(x) = self.value.as_mut() {
x
} else {
unreachable!(MSG)
}
}
pub unsafe fn get_ref(&self) -> &T {
if let Some(x) = self.value.as_ref() {
x
} else {
unreachable!(MSG)
}
}
pub unsafe fn get_mut(&mut self) -> &mut T {
if let Some(x) = self.value.as_mut() {
x
} else {
unreachable!(MSG)
}
}
pub fn write(&mut self, val: T) {
// NOTE(volatile) we have observed UB when this uses a plain `ptr::write`
unsafe { ptr::write_volatile(&mut self.value, Some(val)) }
}
}
#[inline(always)] #[inline(always)]
pub fn assert_send<T>() pub fn assert_send<T>()
where where
@ -160,19 +114,16 @@ where
#[cfg(armv7m)] #[cfg(armv7m)]
#[inline(always)] #[inline(always)]
pub unsafe fn claim<T, R, F>( pub unsafe fn lock<T, R>(
ptr: *mut T, ptr: *mut T,
priority: &Priority, priority: &Priority,
ceiling: u8, ceiling: u8,
nvic_prio_bits: u8, nvic_prio_bits: u8,
f: F, f: impl FnOnce(&mut T) -> R,
) -> R ) -> R {
where
F: FnOnce(&mut T) -> R,
{
let current = priority.get(); let current = priority.get();
if priority.get() < ceiling { if current < ceiling {
if ceiling == (1 << nvic_prio_bits) { if ceiling == (1 << nvic_prio_bits) {
priority.set(u8::MAX); priority.set(u8::MAX);
let r = interrupt::free(|_| f(&mut *ptr)); let r = interrupt::free(|_| f(&mut *ptr));
@ -193,19 +144,16 @@ where
#[cfg(not(armv7m))] #[cfg(not(armv7m))]
#[inline(always)] #[inline(always)]
pub unsafe fn claim<T, R, F>( pub unsafe fn lock<T, R>(
ptr: *mut T, ptr: *mut T,
priority: &Priority, priority: &Priority,
ceiling: u8, ceiling: u8,
_nvic_prio_bits: u8, _nvic_prio_bits: u8,
f: F, f: impl FnOnce(&mut T) -> R,
) -> R ) -> R {
where
F: FnOnce(&mut T) -> R,
{
let current = priority.get(); let current = priority.get();
if priority.get() < ceiling { if current < ceiling {
priority.set(u8::MAX); priority.set(u8::MAX);
let r = interrupt::free(|_| f(&mut *ptr)); let r = interrupt::free(|_| f(&mut *ptr));
priority.set(current); priority.set(current);
@ -215,8 +163,7 @@ where
} }
} }
#[cfg(armv7m)]
#[inline] #[inline]
fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
} }

View file

@ -1,5 +1,8 @@
//! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers //! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers
//! //!
//! **HEADS UP** This is an **alpha** pre-release; there may be breaking changes in the API and
//! semantics before a proper release is made.
//!
//! **IMPORTANT**: This crate is published as [`cortex-m-rtfm`] on crates.io but the name of the //! **IMPORTANT**: This crate is published as [`cortex-m-rtfm`] on crates.io but the name of the
//! library is `rtfm`. //! library is `rtfm`.
//! //!
@ -7,7 +10,7 @@
//! //!
//! The user level documentation can be found [here]. //! The user level documentation can be found [here].
//! //!
//! [here]: https://japaric.github.io/cortex-m-rtfm/book/en/ //! [here]: https://japaric.github.io/rtfm5/book/en/
//! //!
//! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component //! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component
//! of the framework. //! of the framework.
@ -16,7 +19,7 @@
//! //!
//! # Minimum Supported Rust Version (MSRV) //! # Minimum Supported Rust Version (MSRV)
//! //!
//! This crate is guaranteed to compile on stable Rust 1.31 (2018 edition) and up. It *might* //! This crate is guaranteed to compile on stable Rust 1.36 (2018 edition) and up. It *might*
//! compile on older versions but that may change in any new patch release. //! compile on older versions but that may change in any new patch release.
//! //!
//! # Semantic Versioning //! # Semantic Versioning
@ -36,12 +39,11 @@
//! [`Instant`]: struct.Instant.html //! [`Instant`]: struct.Instant.html
//! [`Duration`]: struct.Duration.html //! [`Duration`]: struct.Duration.html
//! //!
//! - `nightly`. Enabling this opt-in feature makes RTFM internally use the unstable //! - `nightly`. Enabling this opt-in feature makes RTFM internally use the unstable `const_fn`
//! `core::mem::MaybeUninit` API and unstable `const_fn` language feature to reduce static memory //! language feature to reduce static memory usage, runtime overhead and initialization overhead.
//! usage, runtime overhead and initialization overhead. This feature requires a nightly compiler //! This feature requires a nightly compiler and may stop working at any time!
//! and may stop working at any time!
#![cfg_attr(feature = "nightly", feature(maybe_uninit))] #![feature(maybe_uninit)]
#![deny(missing_docs)] #![deny(missing_docs)]
#![deny(warnings)] #![deny(warnings)]
#![no_std] #![no_std]
@ -132,7 +134,7 @@ pub struct Instant(i32);
impl Instant { impl Instant {
/// IMPLEMENTATION DETAIL. DO NOT USE /// IMPLEMENTATION DETAIL. DO NOT USE
#[doc(hidden)] #[doc(hidden)]
pub fn artificial(timestamp: i32) -> Self { pub unsafe fn artificial(timestamp: i32) -> Self {
Instant(timestamp) Instant(timestamp)
} }
@ -290,9 +292,7 @@ pub trait Mutex {
type T; type T;
/// Creates a critical section and grants temporary access to the protected data /// Creates a critical section and grants temporary access to the protected data
fn lock<R, F>(&mut self, f: F) -> R fn lock<R>(&mut self, f: impl FnOnce(&mut Self::T) -> R) -> R;
where
F: FnOnce(&mut Self::T) -> R;
} }
impl<'a, M> Mutex for &'a mut M impl<'a, M> Mutex for &'a mut M
@ -301,10 +301,7 @@ where
{ {
type T = M::T; type T = M::T;
fn lock<R, F>(&mut self, f: F) -> R fn lock<R>(&mut self, f: impl FnOnce(&mut M::T) -> R) -> R {
where
F: FnOnce(&mut Self::T) -> R,
{
(**self).lock(f) (**self).lock(f)
} }
} }
@ -317,10 +314,7 @@ pub struct Exclusive<'a, T>(pub &'a mut T);
impl<'a, T> Mutex for Exclusive<'a, T> { impl<'a, T> Mutex for Exclusive<'a, T> {
type T = T; type T = T;
fn lock<R, F>(&mut self, f: F) -> R fn lock<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
where
F: FnOnce(&mut Self::T) -> R,
{
f(self.0) f(self.0)
} }
} }

View file

@ -3,7 +3,7 @@ use core::cmp::{self, Ordering};
use cortex_m::peripheral::{SCB, SYST}; use cortex_m::peripheral::{SCB, SYST};
use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
use crate::{Instant, Mutex}; use crate::Instant;
pub struct TimerQueue<T, N> pub struct TimerQueue<T, N>
where where
@ -43,11 +43,39 @@ where
} }
// set SysTick pending // set SysTick pending
(*SCB::ptr()).icsr.write(1 << 26); SCB::set_pendst();
} }
self.queue.push_unchecked(nr); self.queue.push_unchecked(nr);
} }
#[inline]
pub fn dequeue(&mut self) -> Option<(T, u8)> {
if let Some(instant) = self.queue.peek().map(|p| p.instant) {
let diff = instant.0.wrapping_sub(Instant::now().0);
if diff < 0 {
// task became ready
let nr = unsafe { self.queue.pop_unchecked() };
Some((nr.task, nr.index))
} else {
// set a new timeout
const MAX: u32 = 0x00ffffff;
self.syst.set_reload(cmp::min(MAX, diff as u32));
// start counting down from the new reload
self.syst.clear_current();
None
}
} else {
// the queue is empty
self.syst.disable_interrupt();
None
}
}
} }
pub struct NotReady<T> pub struct NotReady<T>
@ -87,49 +115,3 @@ where
Some(self.cmp(&other)) Some(self.cmp(&other))
} }
} }
#[inline(always)]
pub fn isr<TQ, T, N, F>(mut tq: TQ, mut f: F)
where
TQ: Mutex<T = TimerQueue<T, N>>,
T: Copy + Send,
N: ArrayLength<NotReady<T>>,
F: FnMut(T, u8),
{
loop {
// XXX does `#[inline(always)]` improve performance or not?
let next = tq.lock(#[inline(always)]
|tq| {
if let Some(instant) = tq.queue.peek().map(|p| p.instant) {
let diff = instant.0.wrapping_sub(Instant::now().0);
if diff < 0 {
// task became ready
let m = unsafe { tq.queue.pop_unchecked() };
Some((m.task, m.index))
} else {
// set a new timeout
const MAX: u32 = 0x00ffffff;
tq.syst.set_reload(cmp::min(MAX, diff as u32));
// start counting down from the new reload
tq.syst.clear_current();
None
}
} else {
// the queue is empty
tq.syst.disable_interrupt();
None
}
});
if let Some((task, index)) = next {
f(task, index)
} else {
return;
}
}
}

View file

@ -30,35 +30,35 @@ const APP: () = {
static S3: u32 = 0; static S3: u32 = 0;
#[init(resources = [O1, O4, O5, O6, S3])] #[init(resources = [O1, O4, O5, O6, S3])]
fn init() { fn init(c: init::Context) {
resources.O1; //~ ERROR no field `O1` c.resources.O1; //~ ERROR no field `O1`
resources.O4; //~ ERROR no field `O4` c.resources.O4; //~ ERROR no field `O4`
resources.O5; //~ ERROR no field `O5` c.resources.O5; //~ ERROR no field `O5`
resources.O6; //~ ERROR no field `O6` c.resources.O6; //~ ERROR no field `O6`
resources.S3; //~ ERROR no field `S3` c.resources.S3; //~ ERROR no field `S3`
} }
#[idle(resources = [O2, O4, S1, S3])] #[idle(resources = [O2, O4, S1, S3])]
fn idle() -> ! { fn idle(c: idle::Context) -> ! {
resources.O2; //~ ERROR no field `O2` c.resources.O2; //~ ERROR no field `O2`
resources.O4; //~ ERROR no field `O4` c.resources.O4; //~ ERROR no field `O4`
resources.S1; //~ ERROR no field `S1` c.resources.S1; //~ ERROR no field `S1`
resources.S3; //~ ERROR no field `S3` c.resources.S3; //~ ERROR no field `S3`
loop {} loop {}
} }
#[interrupt(resources = [O3, S1, S2, S3])] #[interrupt(resources = [O3, S1, S2, S3])]
fn UART0() { fn UART0(c: UART0::Context) {
resources.O3; //~ ERROR no field `O3` c.resources.O3; //~ ERROR no field `O3`
resources.S1; //~ ERROR no field `S1` c.resources.S1; //~ ERROR no field `S1`
resources.S2; //~ ERROR no field `S2` c.resources.S2; //~ ERROR no field `S2`
resources.S3; //~ ERROR no field `S3` c.resources.S3; //~ ERROR no field `S3`
} }
#[interrupt(resources = [S2, O5])] #[interrupt(resources = [S2, O5])]
fn UART1() { fn UART1(c: UART1::Context) {
resources.S2; //~ ERROR no field `S2` c.resources.S2; //~ ERROR no field `S2`
resources.O5; //~ ERROR no field `O5` c.resources.O5; //~ ERROR no field `O5`
} }
}; };

View file

@ -10,7 +10,7 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(_: init::Context) {
#[cfg(never)] #[cfg(never)]
static mut FOO: u32 = 0; static mut FOO: u32 = 0;
@ -18,7 +18,7 @@ const APP: () = {
} }
#[idle] #[idle]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
#[cfg(never)] #[cfg(never)]
static mut FOO: u32 = 0; static mut FOO: u32 = 0;
@ -28,7 +28,7 @@ const APP: () = {
} }
#[exception] #[exception]
fn SVCall() { fn SVCall(_: SVCall::Context) {
#[cfg(never)] #[cfg(never)]
static mut FOO: u32 = 0; static mut FOO: u32 = 0;
@ -36,7 +36,7 @@ const APP: () = {
} }
#[interrupt] #[interrupt]
fn UART0() { fn UART0(_: UART0::Context) {
#[cfg(never)] #[cfg(never)]
static mut FOO: u32 = 0; static mut FOO: u32 = 0;
@ -44,7 +44,7 @@ const APP: () = {
} }
#[task] #[task]
fn foo() { fn foo(_: foo::Context) {
#[cfg(never)] #[cfg(never)]
static mut FOO: u32 = 0; static mut FOO: u32 = 0;

View file

@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[task( #[task(
priority = 1, priority = 1,
priority = 2, //~ ERROR argument appears more than once priority = 2, //~ ERROR argument appears more than once
)] )]
fn foo() {} fn foo(_: foo::Context) {}
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[task( #[task(
capacity = 1, capacity = 1,
capacity = 2, //~ ERROR argument appears more than once capacity = 2, //~ ERROR argument appears more than once
)] )]
fn foo() {} fn foo(_: foo::Context) {}
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -1,29 +0,0 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
static mut UNINITIALIZED: bool = ();
#[init]
fn init() {
if false {
return; //~ ERROR `init` is *not* allowed to early return
}
UNINITIALIZED = true;
}
#[interrupt(resources = [UNINITIALIZED])]
fn UART0() {
if resources.UNINITIALIZED {
// UB
}
}
};

View file

@ -1,32 +0,0 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
static mut UNINITIALIZED: bool = ();
#[init]
fn init() {
let x = || {
// this is OK
return 0;
};
return; //~ ERROR `init` is *not* allowed to early return
UNINITIALIZED = true;
}
#[interrupt(resources = [UNINITIALIZED])]
fn UART0() {
if resources.UNINITIALIZED {
// UB
}
}
};

View file

@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[exception] #[exception]
fn SVCall() -> ! { fn SVCall(_: SVCall::Context) -> ! {
//~^ ERROR `exception` handlers must have type signature `[unsafe] fn()` //~^ ERROR this `exception` handler must have type signature `fn(SVCall::Context)`
loop {} loop {}
} }
}; };

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[exception] #[exception]
fn SVCall(undef: u32) { fn SVCall(_: SVCall::Context, undef: u32) {
//~^ ERROR `exception` handlers must have type signature `[unsafe] fn()` //~^ ERROR this `exception` handler must have type signature `fn(SVCall::Context)`
} }
}; };

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[exception] #[exception]
fn NonMaskableInt() { fn NonMaskableInt(_: NonMaskableInt::Context) {
//~^ ERROR only exceptions with configurable priority can be used as hardware tasks //~^ ERROR only exceptions with configurable priority can be used as hardware tasks
} }
}; };

View file

@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[exception] #[exception]
fn SVCall() -> u32 { fn SVCall(_: SVCall::Context) -> u32 {
//~^ ERROR `exception` handlers must have type signature `[unsafe] fn()` //~^ ERROR this `exception` handler must have type signature `fn(SVCall::Context)`
0 0
} }
}; };

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[exception] #[exception]
fn SysTick() { fn SysTick(_: SysTick::Context) {
//~^ ERROR the `SysTick` exception can't be used because it's used by the runtime //~^ ERROR the `SysTick` exception can't be used because it's used by the runtime
} }
}; };

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[idle] #[idle]
fn idle(undef: u32) { fn idle(_: idle::Context, undef: u32) {
//~^ ERROR `idle` must have type signature `[unsafe] fn() -> !` //~^ ERROR `idle` must have type signature `fn(idle::Context) -> !`
} }
}; };

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[idle] #[idle]
fn idle() { fn idle(_: idle::Context) {
//~^ ERROR `idle` must have type signature `[unsafe] fn() -> !` //~^ ERROR `idle` must have type signature `fn(idle::Context) -> !`
} }
}; };

View file

@ -10,8 +10,8 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() -> ! { fn init(_: init::Context) -> ! {
//~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]` //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
loop {} loop {}
} }
}; };

View file

@ -10,7 +10,7 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init(undef: u32) { fn init(_: init::Context, undef: u32) {
//~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]` //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
} }
}; };

View file

@ -1,6 +1,5 @@
//! This is equivalent to the `late-not-send` cfail test //! This is equivalent to the `late-not-send` cfail test
#![feature(extern_crate_item_prelude)] // ???
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -21,10 +20,10 @@ const APP: () = {
static mut X: Option<NotSend> = None; static mut X: Option<NotSend> = None;
#[init(resources = [X])] #[init(resources = [X])]
fn init() { fn init(c: init::Context) {
*resources.X = Some(NotSend { _0: PhantomData }) *c.resources.X = Some(NotSend { _0: PhantomData })
} }
#[interrupt(resources = [X])] #[interrupt(resources = [X])]
fn UART0() {} fn UART0(_: UART0::Context) {}
}; };

View file

@ -10,8 +10,8 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() -> u32 { fn init(_: init::Context) -> u32 {
//~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]` //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
0 0
} }
}; };

View file

@ -10,8 +10,8 @@ use rtfm::app;
#[app(device = lm3s6965)] //~ ERROR 1 free interrupt (`extern { .. }`) is required #[app(device = lm3s6965)] //~ ERROR 1 free interrupt (`extern { .. }`) is required
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[task] #[task]
fn foo() {} fn foo(_: foo::Context) {}
}; };

View file

@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[interrupt] #[interrupt]
fn UART0() -> ! { fn UART0(_: UART0::Context) -> ! {
//~^ ERROR `interrupt` handlers must have type signature `[unsafe] fn()` //~^ ERROR this `interrupt` handler must have type signature `fn(UART0::Context)`
loop {} loop {}
} }
}; };

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[interrupt] #[interrupt]
fn UART0(undef: u32) { fn UART0(_: UART0::Context, undef: u32) {
//~^ ERROR `interrupt` handlers must have type signature `[unsafe] fn()` //~^ ERROR this `interrupt` handler must have type signature `fn(UART0::Context)`
} }
}; };

View file

@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[interrupt] #[interrupt]
fn UART0() -> u32 { fn UART0(_: UART0::Context) -> u32 {
//~^ ERROR `interrupt` handlers must have type signature `[unsafe] fn()` //~^ ERROR this `interrupt` handler must have type signature `fn(UART0::Context)`
0 0
} }
}; };

View file

@ -12,5 +12,5 @@ const APP: () = {
static mut X: u32 = (); static mut X: u32 = ();
#[init(resources = [X])] //~ ERROR late resources can NOT be assigned to `init` #[init(resources = [X])] //~ ERROR late resources can NOT be assigned to `init`
fn init() {} fn init(_: init::Context) {}
}; };

View file

@ -1,7 +1,6 @@
//! `init` has a static priority of `0`. Initializing resources from it is equivalent to sending a //! `init` has a static priority of `0`. Initializing resources from it is equivalent to sending a
//! message to the task that will own the resource //! message to the task that will own the resource
#![feature(extern_crate_item_prelude)] // ???
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -22,12 +21,12 @@ const APP: () = {
static mut X: NotSend = (); static mut X: NotSend = ();
#[init] #[init]
fn init() -> init::LateResources { fn init(_: init::Context) -> init::LateResources {
init::LateResources { init::LateResources {
X: NotSend { _0: PhantomData }, X: NotSend { _0: PhantomData },
} }
} }
#[interrupt(resources = [X])] #[interrupt(resources = [X])]
fn UART0() {} fn UART0(_: UART0::Context) {}
}; };

View file

@ -1,4 +1,3 @@
#![feature(extern_crate_item_prelude)] // ???
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -19,10 +18,10 @@ unsafe impl Sync for NotSend {}
#[app(device = lm3s6965)] //~ ERROR cannot be sent between threads safely #[app(device = lm3s6965)] //~ ERROR cannot be sent between threads safely
const APP: () = { const APP: () = {
#[init(spawn = [foo])] #[init(spawn = [foo])]
fn init() {} fn init(_: init::Context) {}
#[task] #[task]
fn foo(_x: NotSend) {} fn foo(_: foo::Context, _x: NotSend) {}
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -1,4 +1,3 @@
#![feature(extern_crate_item_prelude)] // ???
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -21,13 +20,13 @@ const APP: () = {
static X: NotSync = NotSync { _0: PhantomData }; static X: NotSync = NotSync { _0: PhantomData };
#[init(spawn = [foo])] #[init(spawn = [foo])]
fn init() {} fn init(_: init::Context) {}
#[task(priority = 1, resources = [X])] #[task(priority = 1, resources = [X])]
fn foo() {} fn foo(_: foo::Context) {}
#[task(priority = 2, resources = [X])] #[task(priority = 2, resources = [X])]
fn bar() {} fn bar(_: bar::Context) {}
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)] //~ error evaluation of constant value failed #[app(device = lm3s6965)] //~ error evaluation of constant value failed
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
// OK, this is the maximum priority supported by the device // OK, this is the maximum priority supported by the device
#[interrupt(priority = 8)] #[interrupt(priority = 8)]
fn UART0() {} fn UART0(_: UART0::Context) {}
// this value is too high! // this value is too high!
#[interrupt(priority = 9)] #[interrupt(priority = 9)]
fn UART1() {} fn UART1(_: UART1::Context) {}
}; };

View file

@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
// OK, this is the minimum priority that tasks can have // OK, this is the minimum priority that tasks can have
#[interrupt(priority = 1)] #[interrupt(priority = 1)]
fn UART0() {} fn UART0(_: UART0::Context) {}
// this value is too low! // this value is too low!
#[interrupt(priority = 0)] //~ error this literal must be in the range 1...255 #[interrupt(priority = 0)] //~ error this literal must be in the range 1...255
fn UART1() {} fn UART1(_: UART1::Context) {}
}; };

View file

@ -10,5 +10,5 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(resources = [X])] //~ ERROR this resource has NOT been declared #[init(resources = [X])] //~ ERROR this resource has NOT been declared
fn init() {} fn init(_: init::Context) {}
}; };

View file

@ -13,5 +13,5 @@ const APP: () = {
//~^ ERROR resources must have inherited / private visibility //~^ ERROR resources must have inherited / private visibility
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
}; };

View file

@ -5,16 +5,14 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::app; #[rtfm::app(device = lm3s6965)]
#[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[task] #[task]
fn foo() -> ! { fn foo(_: foo::Context) -> ! {
//~^ ERROR `task` handlers must have type signature `[unsafe] fn(..)` //~^ ERROR this `task` handler must have type signature `fn(foo::Context, ..)`
loop {} loop {}
} }

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[task] #[task]
fn idle() { fn idle(_: idle::Context) {
//~^ ERROR `task` handlers can NOT be named `idle`, `init` or `resources` //~^ ERROR `task` handlers can NOT be named `idle`, `init` or `resources`
} }

View file

@ -10,5 +10,5 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(spawn = [X])] //~ ERROR this task has NOT been declared #[init(spawn = [X])] //~ ERROR this task has NOT been declared
fn init() {} fn init(_: init::Context) {}
}; };

View file

@ -0,0 +1,18 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init(_: init::Context) {}
#[exception(binds = SVCall)]
unsafe fn foo(_: foo::Context) {}
//~^ ERROR this `exception` handler must have type signature `fn(foo::Context)`
};

View file

@ -0,0 +1,20 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init(_: init::Context) {}
#[idle]
unsafe fn idle(_: idle::Context) -> ! {
//~^ ERROR `idle` must have type signature `fn(idle::Context) -> !`
loop {}
}
};

View file

@ -1,5 +1,3 @@
// TODO remove in v0.5.x
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -11,8 +9,7 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut X: u32 = (); //~ ERROR late resources MUST be initialized at the end of `init`
#[init] #[init]
fn init() {} unsafe fn init(_: init::Context) {}
//~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
}; };

View file

@ -0,0 +1,18 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init(_: init::Context) {}
#[interrupt(binds = UART0)]
unsafe fn foo(_: foo::Context) {}
//~^ ERROR this `interrupt` handler must have type signature `fn(foo::Context)`
};

View file

@ -0,0 +1,22 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
fn init(_: init::Context) {}
#[task]
unsafe fn foo(_: foo::Context) {}
//~^ ERROR this `task` handler must have type signature `fn(foo::Context, ..)`
extern "C" {
fn UART0();
}
};

View file

@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[interrupt(binds = UART0)] //~ ERROR free interrupts (`extern { .. }`) can't be used as interrupt handlers #[interrupt(binds = UART0)] //~ ERROR free interrupts (`extern { .. }`) can't be used as interrupt handlers
fn foo() {} fn foo(_: foo::Context) {}
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -10,10 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)] #[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[interrupt] #[interrupt]
fn UART0() {} //~ ERROR free interrupts (`extern { .. }`) can't be used as interrupt handlers fn UART0(_: UART0::Context) {}
//~^ ERROR free interrupts (`extern { .. }`) can't be used as interrupt handlers
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -1,4 +1,6 @@
//! Check that `binds` works as advertised //! Check that `binds` works as advertised
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -6,18 +8,20 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::app; #[rtfm::app(device = lm3s6965)]
#[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() {} fn init(_: init::Context) {}
#[exception(binds = SVCall)] #[exception(binds = SVCall)]
fn foo() {} fn foo(c: foo::Context) {
foo_trampoline(c)
}
#[interrupt(binds = UART0)] #[interrupt(binds = UART0)]
fn bar() {} fn bar(c: bar::Context) {
bar_trampoline(c)
}
}; };
#[allow(dead_code)] #[allow(dead_code)]

View file

@ -6,24 +6,22 @@
#![no_std] #![no_std]
extern crate lm3s6965; extern crate lm3s6965;
extern crate panic_semihosting; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::app; #[rtfm::app(device = lm3s6965)]
#[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[cfg(never)] #[cfg(never)]
static mut FOO: u32 = 0; static mut FOO: u32 = 0;
#[init] #[init]
fn init() { fn init(_: init::Context) {
#[cfg(never)] #[cfg(never)]
static mut BAR: u32 = 0; static mut BAR: u32 = 0;
} }
#[idle] #[idle]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
#[cfg(never)] #[cfg(never)]
static mut BAR: u32 = 0; static mut BAR: u32 = 0;
@ -31,20 +29,20 @@ const APP: () = {
} }
#[task(resources = [FOO], schedule = [quux], spawn = [quux])] #[task(resources = [FOO], schedule = [quux], spawn = [quux])]
fn foo() { fn foo(_: foo::Context) {
#[cfg(never)] #[cfg(never)]
static mut BAR: u32 = 0; static mut BAR: u32 = 0;
} }
#[task(priority = 3, resources = [FOO], schedule = [quux], spawn = [quux])] #[task(priority = 3, resources = [FOO], schedule = [quux], spawn = [quux])]
fn bar() { fn bar(_: bar::Context) {
#[cfg(never)] #[cfg(never)]
static mut BAR: u32 = 0; static mut BAR: u32 = 0;
} }
#[cfg(never)] #[cfg(never)]
#[task] #[task]
fn quux() {} fn quux(_: quux::Context) {}
extern "C" { extern "C" {
fn UART0(); fn UART0();

View file

@ -1,3 +1,5 @@
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -7,20 +9,18 @@ extern crate rtfm;
use core::marker::PhantomData; use core::marker::PhantomData;
use rtfm::app;
pub struct NotSend { pub struct NotSend {
_0: PhantomData<*const ()>, _0: PhantomData<*const ()>,
} }
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut X: NotSend = (); static mut X: NotSend = ();
static mut Y: Option<NotSend> = None; static mut Y: Option<NotSend> = None;
#[init(resources = [Y])] #[init(resources = [Y])]
fn init() -> init::LateResources { fn init(c: init::Context) -> init::LateResources {
*resources.Y = Some(NotSend { _0: PhantomData }); *c.resources.Y = Some(NotSend { _0: PhantomData });
init::LateResources { init::LateResources {
X: NotSend { _0: PhantomData }, X: NotSend { _0: PhantomData },
@ -28,7 +28,7 @@ const APP: () = {
} }
#[idle(resources = [X, Y])] #[idle(resources = [X, Y])]
fn idle() -> ! { fn idle(_: idle::Context) -> ! {
loop {} loop {}
} }
}; };

View file

@ -1,4 +1,6 @@
//! Runtime initialized resources //! Runtime initialized resources
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -6,15 +8,13 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::app; #[rtfm::app(device = lm3s6965)]
#[app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut X: u32 = (); static mut X: u32 = ();
static Y: u32 = (); static Y: u32 = ();
#[init] #[init]
fn init() -> init::LateResources { fn init(_: init::Context) -> init::LateResources {
init::LateResources { X: 0, Y: 1 } init::LateResources { X: 0, Y: 1 }
} }
}; };

View file

@ -1,4 +1,6 @@
//! Core and device peripherals //! Core and device peripherals
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -6,13 +8,11 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::app; #[rtfm::app(device = lm3s6965)]
#[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init] #[init]
fn init() { fn init(c: init::Context) {
let _: rtfm::Peripherals = core; let _: rtfm::Peripherals = c.core;
let _: lm3s6965::Peripherals = device; let _: lm3s6965::Peripherals = c.device;
} }
}; };

View file

@ -1,5 +1,7 @@
//! Check code generation of resources //! Check code generation of resources
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -7,9 +9,9 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::{app, Exclusive}; use rtfm::Exclusive;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
static mut O1: u32 = 0; // init static mut O1: u32 = 0; // init
static mut O2: u32 = 0; // idle static mut O2: u32 = 0; // idle
@ -23,57 +25,57 @@ const APP: () = {
static S3: u32 = 0; static S3: u32 = 0;
#[init(resources = [O1, O4, O5, O6, S3])] #[init(resources = [O1, O4, O5, O6, S3])]
fn init() { fn init(c: init::Context) {
// owned by `init` == `&'static mut` // owned by `init` == `&'static mut`
let _: &'static mut u32 = resources.O1; let _: &'static mut u32 = c.resources.O1;
// owned by `init` == `&'static` if read-only // owned by `init` == `&'static` if read-only
let _: &'static u32 = resources.O6; let _: &'static u32 = c.resources.O6;
// `init` has exclusive access to all resources // `init` has exclusive access to all resources
let _: &mut u32 = resources.O4; let _: &mut u32 = c.resources.O4;
let _: &mut u32 = resources.O5; let _: &mut u32 = c.resources.O5;
let _: &mut u32 = resources.S3; let _: &mut u32 = c.resources.S3;
} }
#[idle(resources = [O2, O4, S1, S3])] #[idle(resources = [O2, O4, S1, S3])]
fn idle() -> ! { fn idle(mut c: idle::Context) -> ! {
// owned by `idle` == `&'static mut` // owned by `idle` == `&'static mut`
let _: &'static mut u32 = resources.O2; let _: &'static mut u32 = c.resources.O2;
// owned by `idle` == `&'static` if read-only // owned by `idle` == `&'static` if read-only
let _: &'static u32 = resources.O4; let _: &'static u32 = c.resources.O4;
// shared with `idle` == `Mutex` // shared with `idle` == `Mutex`
resources.S1.lock(|_| {}); c.resources.S1.lock(|_| {});
// `&` if read-only // `&` if read-only
let _: &u32 = resources.S3; let _: &u32 = c.resources.S3;
loop {} loop {}
} }
#[interrupt(resources = [O3, S1, S2, S3])] #[interrupt(resources = [O3, S1, S2, S3])]
fn UART0() { fn UART0(c: UART0::Context) {
// owned by interrupt == `&mut` // owned by interrupt == `&mut`
let _: &mut u32 = resources.O3; let _: &mut u32 = c.resources.O3;
// no `Mutex` proxy when access from highest priority task // no `Mutex` proxy when access from highest priority task
let _: Exclusive<u32> = resources.S1; let _: Exclusive<u32> = c.resources.S1;
// no `Mutex` proxy when co-owned by cooperative (same priority) tasks // no `Mutex` proxy when co-owned by cooperative (same priority) tasks
let _: Exclusive<u32> = resources.S2; let _: Exclusive<u32> = c.resources.S2;
// `&` if read-only // `&` if read-only
let _: &u32 = resources.S3; let _: &u32 = c.resources.S3;
} }
#[interrupt(resources = [S2, O5])] #[interrupt(resources = [S2, O5])]
fn UART1() { fn UART1(c: UART1::Context) {
// owned by interrupt == `&` if read-only // owned by interrupt == `&` if read-only
let _: &u32 = resources.O5; let _: &u32 = c.resources.O5;
// no `Mutex` proxy when co-owned by cooperative (same priority) tasks // no `Mutex` proxy when co-owned by cooperative (same priority) tasks
let _: Exclusive<u32> = resources.S2; let _: Exclusive<u32> = c.resources.S2;
} }
}; };

View file

@ -1,3 +1,5 @@
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -5,52 +7,52 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::{app, Instant}; use rtfm::Instant;
#[app(device = lm3s6965)] #[rtfm::app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(schedule = [foo, bar, baz])] #[init(schedule = [foo, bar, baz])]
fn init() { fn init(c: init::Context) {
let _: Result<(), ()> = schedule.foo(start + 10.cycles()); let _: Result<(), ()> = c.schedule.foo(c.start + 10.cycles());
let _: Result<(), u32> = schedule.bar(start + 20.cycles(), 0); let _: Result<(), u32> = c.schedule.bar(c.start + 20.cycles(), 0);
let _: Result<(), (u32, u32)> = schedule.baz(start + 30.cycles(), 0, 1); let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 30.cycles(), 0, 1);
} }
#[idle(schedule = [foo, bar, baz])] #[idle(schedule = [foo, bar, baz])]
fn idle() -> ! { fn idle(c: idle::Context) -> ! {
let _: Result<(), ()> = schedule.foo(Instant::now() + 40.cycles()); let _: Result<(), ()> = c.schedule.foo(Instant::now() + 40.cycles());
let _: Result<(), u32> = schedule.bar(Instant::now() + 50.cycles(), 0); let _: Result<(), u32> = c.schedule.bar(Instant::now() + 50.cycles(), 0);
let _: Result<(), (u32, u32)> = schedule.baz(Instant::now() + 60.cycles(), 0, 1); let _: Result<(), (u32, u32)> = c.schedule.baz(Instant::now() + 60.cycles(), 0, 1);
loop {} loop {}
} }
#[exception(schedule = [foo, bar, baz])] #[exception(schedule = [foo, bar, baz])]
fn SVCall() { fn SVCall(c: SVCall::Context) {
let _: Result<(), ()> = schedule.foo(start + 70.cycles()); let _: Result<(), ()> = c.schedule.foo(c.start + 70.cycles());
let _: Result<(), u32> = schedule.bar(start + 80.cycles(), 0); let _: Result<(), u32> = c.schedule.bar(c.start + 80.cycles(), 0);
let _: Result<(), (u32, u32)> = schedule.baz(start + 90.cycles(), 0, 1); let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 90.cycles(), 0, 1);
} }
#[interrupt(schedule = [foo, bar, baz])] #[interrupt(schedule = [foo, bar, baz])]
fn UART0() { fn UART0(c: UART0::Context) {
let _: Result<(), ()> = schedule.foo(start + 100.cycles()); let _: Result<(), ()> = c.schedule.foo(c.start + 100.cycles());
let _: Result<(), u32> = schedule.bar(start + 110.cycles(), 0); let _: Result<(), u32> = c.schedule.bar(c.start + 110.cycles(), 0);
let _: Result<(), (u32, u32)> = schedule.baz(start + 120.cycles(), 0, 1); let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 120.cycles(), 0, 1);
} }
#[task(schedule = [foo, bar, baz])] #[task(schedule = [foo, bar, baz])]
fn foo() { fn foo(c: foo::Context) {
let _: Result<(), ()> = schedule.foo(scheduled + 130.cycles()); let _: Result<(), ()> = c.schedule.foo(c.scheduled + 130.cycles());
let _: Result<(), u32> = schedule.bar(scheduled + 140.cycles(), 0); let _: Result<(), u32> = c.schedule.bar(c.scheduled + 140.cycles(), 0);
let _: Result<(), (u32, u32)> = schedule.baz(scheduled + 150.cycles(), 0, 1); let _: Result<(), (u32, u32)> = c.schedule.baz(c.scheduled + 150.cycles(), 0, 1);
} }
#[task] #[task]
fn bar(_x: u32) {} fn bar(_: bar::Context, _x: u32) {}
#[task] #[task]
fn baz(_x: u32, _y: u32) {} fn baz(_: baz::Context, _x: u32, _y: u32) {}
extern "C" { extern "C" {
fn UART1(); fn UART1();

View file

@ -1,66 +0,0 @@
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate owned_singleton;
extern crate panic_halt;
extern crate rtfm;
use rtfm::{app, Exclusive};
#[app(device = lm3s6965)]
const APP: () = {
#[Singleton]
static mut O1: u32 = 0;
#[Singleton]
static mut O2: u32 = 0;
#[Singleton]
static mut O3: u32 = 0;
#[Singleton]
static O4: u32 = 0;
#[Singleton]
static O5: u32 = 0;
#[Singleton]
static O6: u32 = 0;
#[Singleton]
static mut S1: u32 = 0;
#[Singleton]
static S2: u32 = 0;
#[init(resources = [O1, O2, O3, O4, O5, O6, S1, S2])]
fn init() {
let _: O1 = resources.O1;
let _: &mut O2 = resources.O2;
let _: &mut O3 = resources.O3;
let _: O4 = resources.O4;
let _: &mut O5 = resources.O5;
let _: &mut O6 = resources.O6;
let _: &mut S1 = resources.S1;
let _: &mut S2 = resources.S2;
}
#[idle(resources = [O2, O5])]
fn idle() -> ! {
let _: O2 = resources.O2;
let _: O5 = resources.O5;
loop {}
}
#[interrupt(resources = [O3, O6, S1, S2])]
fn UART0() {
let _: &mut O3 = resources.O3;
let _: &O6 = resources.O6;
let _: Exclusive<S1> = resources.S1;
let _: &S2 = resources.S2;
}
#[interrupt(resources = [S1, S2])]
fn UART1() {
let _: Exclusive<S1> = resources.S1;
let _: &S2 = resources.S2;
}
};

View file

@ -1,4 +1,6 @@
//! Check code generation of `spawn` //! Check code generation of `spawn`
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main] #![no_main]
#![no_std] #![no_std]
@ -6,52 +8,50 @@ extern crate lm3s6965;
extern crate panic_halt; extern crate panic_halt;
extern crate rtfm; extern crate rtfm;
use rtfm::app; #[rtfm::app(device = lm3s6965)]
#[app(device = lm3s6965)]
const APP: () = { const APP: () = {
#[init(spawn = [foo, bar, baz])] #[init(spawn = [foo, bar, baz])]
fn init() { fn init(c: init::Context) {
let _: Result<(), ()> = spawn.foo(); let _: Result<(), ()> = c.spawn.foo();
let _: Result<(), u32> = spawn.bar(0); let _: Result<(), u32> = c.spawn.bar(0);
let _: Result<(), (u32, u32)> = spawn.baz(0, 1); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
} }
#[idle(spawn = [foo, bar, baz])] #[idle(spawn = [foo, bar, baz])]
fn idle() -> ! { fn idle(c: idle::Context) -> ! {
let _: Result<(), ()> = spawn.foo(); let _: Result<(), ()> = c.spawn.foo();
let _: Result<(), u32> = spawn.bar(0); let _: Result<(), u32> = c.spawn.bar(0);
let _: Result<(), (u32, u32)> = spawn.baz(0, 1); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
loop {} loop {}
} }
#[exception(spawn = [foo, bar, baz])] #[exception(spawn = [foo, bar, baz])]
fn SVCall() { fn SVCall(c: SVCall::Context) {
let _: Result<(), ()> = spawn.foo(); let _: Result<(), ()> = c.spawn.foo();
let _: Result<(), u32> = spawn.bar(0); let _: Result<(), u32> = c.spawn.bar(0);
let _: Result<(), (u32, u32)> = spawn.baz(0, 1); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
} }
#[interrupt(spawn = [foo, bar, baz])] #[interrupt(spawn = [foo, bar, baz])]
fn UART0() { fn UART0(c: UART0::Context) {
let _: Result<(), ()> = spawn.foo(); let _: Result<(), ()> = c.spawn.foo();
let _: Result<(), u32> = spawn.bar(0); let _: Result<(), u32> = c.spawn.bar(0);
let _: Result<(), (u32, u32)> = spawn.baz(0, 1); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
} }
#[task(spawn = [foo, bar, baz])] #[task(spawn = [foo, bar, baz])]
fn foo() { fn foo(c: foo::Context) {
let _: Result<(), ()> = spawn.foo(); let _: Result<(), ()> = c.spawn.foo();
let _: Result<(), u32> = spawn.bar(0); let _: Result<(), u32> = c.spawn.bar(0);
let _: Result<(), (u32, u32)> = spawn.baz(0, 1); let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
} }
#[task] #[task]
fn bar(_x: u32) {} fn bar(_: bar::Context, _x: u32) {}
#[task] #[task]
fn baz(_x: u32, _y: u32) {} fn baz(_: baz::Context, _x: u32, _y: u32) {}
extern "C" { extern "C" {
fn UART1(); fn UART1();

View file

@ -1,45 +0,0 @@
//! Check code generation of `unsafe` `init` / `idle` / `exception` / `interrupt` / `task`
#![no_main]
#![no_std]
extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
use rtfm::app;
unsafe fn foo() {}
#[app(device = lm3s6965)]
const APP: () = {
#[init]
unsafe fn init() {
foo();
}
#[idle]
unsafe fn idle() -> ! {
foo();
loop {}
}
#[exception]
unsafe fn SVCall() {
foo();
}
#[interrupt]
unsafe fn UART0() {
foo();
}
#[task]
unsafe fn bar() {
foo();
}
extern "C" {
fn UART1();
}
};