aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2019-05-01 19:50:50 +0000
committerGravatar bors[bot] <bors[bot]@users.noreply.github.com> 2019-05-01 19:50:50 +0000
commitbc024f197929be1ce7dac9e6cbf6672c3980437e (patch)
treec0839773ab356bac429cbc69e4f6b5654d162d6e
parente6fb2f216fccc09d8e996525dcef3ffb2004f1ec (diff)
parentccd7f4586b63841c4bac51f24dc38570c9f89726 (diff)
downloadrtic-bc024f197929be1ce7dac9e6cbf6672c3980437e.tar.gz
rtic-bc024f197929be1ce7dac9e6cbf6672c3980437e.tar.zst
rtic-bc024f197929be1ce7dac9e6cbf6672c3980437e.zip
Merge #176
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>
Diffstat (limited to '')
-rw-r--r--.travis.yml12
-rw-r--r--CHANGELOG.md29
-rw-r--r--Cargo.toml17
-rw-r--r--README.md6
-rw-r--r--book/en/src/SUMMARY.md1
-rw-r--r--book/en/src/by-example/app.md11
-rw-r--r--book/en/src/by-example/new.md4
-rw-r--r--book/en/src/by-example/resources.md12
-rw-r--r--book/en/src/by-example/singletons.md26
-rw-r--r--book/en/src/by-example/tips.md32
-rw-r--r--book/en/src/by-example/types-send-sync.md3
-rw-r--r--book/en/src/preface.md3
-rw-r--r--ci/after-success.sh3
-rw-r--r--ci/expected/pool.run2
-rw-r--r--ci/script.sh36
-rw-r--r--examples/baseline.rs19
-rw-r--r--examples/binds.rs9
-rw-r--r--examples/capacity.rs21
-rw-r--r--examples/cfg.rs13
-rw-r--r--examples/generics.rs18
-rw-r--r--examples/idle.rs7
-rw-r--r--examples/init.rs9
-rw-r--r--examples/interrupt.rs9
-rw-r--r--examples/late.rs13
-rw-r--r--examples/lock.rs17
-rw-r--r--examples/message.rs19
-rw-r--r--examples/not-send.rs20
-rw-r--r--examples/not-sync.rs13
-rw-r--r--examples/periodic.rs14
-rw-r--r--examples/pool.rs67
-rw-r--r--examples/ramfunc.rs13
-rw-r--r--examples/resource.rs19
-rw-r--r--examples/schedule.rs14
-rw-r--r--examples/shared-with-init.rs8
-rw-r--r--examples/singleton.rs61
-rw-r--r--examples/smallest.rs2
-rw-r--r--examples/static.rs13
-rw-r--r--examples/task.rs17
-rw-r--r--examples/types.rs46
-rw-r--r--macros/Cargo.toml6
-rw-r--r--macros/src/analyze.rs28
-rw-r--r--macros/src/check.rs23
-rw-r--r--macros/src/codegen.rs3876
-rw-r--r--macros/src/lib.rs6
-rw-r--r--macros/src/syntax.rs464
-rw-r--r--src/export.rs113
-rw-r--r--src/lib.rs32
-rw-r--r--src/tq.rs78
-rw-r--r--tests/cfail/cfg-resources.rs38
-rw-r--r--tests/cfail/cfg-static.rs10
-rw-r--r--tests/cfail/duplicate-args-2.rs4
-rw-r--r--tests/cfail/duplicate-args.rs4
-rw-r--r--tests/cfail/early-return-2.rs29
-rw-r--r--tests/cfail/early-return.rs32
-rw-r--r--tests/cfail/exception-divergent.rs6
-rw-r--r--tests/cfail/exception-input.rs6
-rw-r--r--tests/cfail/exception-invalid.rs4
-rw-r--r--tests/cfail/exception-output.rs6
-rw-r--r--tests/cfail/exception-sys-tick.rs4
-rw-r--r--tests/cfail/idle-input.rs6
-rw-r--r--tests/cfail/idle-not-divergent.rs6
-rw-r--r--tests/cfail/init-divergent.rs4
-rw-r--r--tests/cfail/init-input.rs4
-rw-r--r--tests/cfail/init-not-send.rs7
-rw-r--r--tests/cfail/init-output.rs4
-rw-r--r--tests/cfail/insufficient-free-interrupts.rs4
-rw-r--r--tests/cfail/interrupt-divergent.rs6
-rw-r--r--tests/cfail/interrupt-input.rs6
-rw-r--r--tests/cfail/interrupt-output.rs6
-rw-r--r--tests/cfail/late-assigned-to-init.rs2
-rw-r--r--tests/cfail/late-not-send.rs5
-rw-r--r--tests/cfail/needs-send.rs5
-rw-r--r--tests/cfail/needs-sync.rs7
-rw-r--r--tests/cfail/priority-too-high.rs6
-rw-r--r--tests/cfail/priority-too-low.rs6
-rw-r--r--tests/cfail/resource-not-declared.rs2
-rw-r--r--tests/cfail/resource-pub.rs2
-rw-r--r--tests/cfail/task-divergent.rs10
-rw-r--r--tests/cfail/task-idle.rs4
-rw-r--r--tests/cfail/task-not-declared.rs2
-rw-r--r--tests/cfail/unsafe-exception.rs18
-rw-r--r--tests/cfail/unsafe-idle.rs20
-rw-r--r--tests/cfail/unsafe-init.rs (renamed from tests/cfail/late-uninit.rs)7
-rw-r--r--tests/cfail/unsafe-interrupt.rs18
-rw-r--r--tests/cfail/unsafe-task.rs22
-rw-r--r--tests/cfail/used-free-interrupt-2.rs4
-rw-r--r--tests/cfail/used-free-interrupt.rs5
-rw-r--r--tests/cpass/binds.rs16
-rw-r--r--tests/cpass/cfg.rs16
-rw-r--r--tests/cpass/late-not-send.rs12
-rw-r--r--tests/cpass/late-resource.rs8
-rw-r--r--tests/cpass/peripheral.rs12
-rw-r--r--tests/cpass/resource.rs44
-rw-r--r--tests/cpass/schedule.rs50
-rw-r--r--tests/cpass/singleton.rs66
-rw-r--r--tests/cpass/spawn.rs50
-rw-r--r--tests/cpass/unsafe.rs45
97 files changed, 3011 insertions, 2933 deletions
diff --git a/.travis.yml b/.travis.yml
index 31d10e84..2c5fc7e6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,17 +3,17 @@ language: rust
matrix:
include:
# 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
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+ # - env: TARGET=thumbv6m-none-eabi
+ # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- - env: TARGET=thumbv7m-none-eabi
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+ # - env: TARGET=thumbv7m-none-eabi
+ # if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- env: TARGET=x86_64-unknown-linux-gnu
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
rust: nightly
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df4c674d..fb1102c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,35 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [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
### Changed
diff --git a/Cargo.toml b/Cargo.toml
index b102ce78..b0df0483 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm"
readme = "README.md"
repository = "https://github.com/japaric/cortex-m-rtfm"
-version = "0.4.3"
+version = "0.5.0-alpha.1"
[lib]
name = "rtfm"
@@ -26,6 +26,12 @@ name = "periodic"
required-features = ["timer-queue"]
[[example]]
+name = "pool"
+# this example doesn't need this feature but only works on ARMv7-M
+# specifying the feature here avoids compiling this for ARMv6-M
+required-features = ["timer-queue"]
+
+[[example]]
name = "schedule"
required-features = ["timer-queue"]
@@ -36,12 +42,13 @@ required-features = ["timer-queue"]
[dependencies]
cortex-m = "0.5.8"
cortex-m-rt = "0.6.7"
-cortex-m-rtfm-macros = { path = "macros", version = "0.4.3" }
-heapless = "0.4.1"
-owned-singleton = "0.1.0"
+cortex-m-rtfm-macros = { path = "macros", version = "0.5.0-alpha.1" }
+
+[dependencies.heapless]
+features = ["smaller-atomics", "min-const-fn"]
+version = "0.4.3"
[dev-dependencies]
-alloc-singleton = "0.1.0"
cortex-m-semihosting = "0.3.2"
lm3s6965 = "0.1.3"
panic-halt = "0.2.0"
diff --git a/README.md b/README.md
index b8cbd00f..db23d2b4 100644
--- a/README.md
+++ b/README.md
@@ -41,13 +41,13 @@ A concurrency framework for building real time systems.
## Requirements
-- Rust 1.31.0+
+- Rust 1.36.0+
- 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
diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md
index 051d1acc..bde2b8d2 100644
--- a/book/en/src/SUMMARY.md
+++ b/book/en/src/SUMMARY.md
@@ -6,7 +6,6 @@
- [Resources](./by-example/resources.md)
- [Tasks](./by-example/tasks.md)
- [Timer queue](./by-example/timer-queue.md)
- - [Singletons](./by-example/singletons.md)
- [Types, Send and Sync](./by-example/types-send-sync.md)
- [Starting a new project](./by-example/new.md)
- [Tips & tricks](./by-example/tips.md)
diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md
index 996b8c16..d595570c 100644
--- a/book/en/src/by-example/app.md
+++ b/book/en/src/by-example/app.md
@@ -28,15 +28,14 @@ not required to use the [`cortex_m_rt::entry`] attribute.
Within the pseudo-module the `app` attribute expects to find an initialization
function marked with the `init` attribute. This function must have signature
-`[unsafe] fn()`.
+`fn(init::Context) [-> init::LateResources]`.
This initialization function will be the first part of the application to run.
The `init` function will run *with interrupts disabled* and has exclusive access
to Cortex-M and device specific peripherals through the `core` and `device`
-variables, which are injected in the scope of `init` by the `app` attribute. Not
-all Cortex-M peripherals are available in `core` because the RTFM runtime takes
-ownership of some of them -- for more details see the [`rtfm::Peripherals`]
-struct.
+variables fields of `init::Context`. Not all Cortex-M peripherals are available
+in `core` because the RTFM runtime takes ownership of some of them -- for more
+details see the [`rtfm::Peripherals`] struct.
`static mut` variables declared at the beginning of `init` will be transformed
into `&'static mut` references that are safe to access.
@@ -61,7 +60,7 @@ $ cargo run --example init
A function marked with the `idle` attribute can optionally appear in the
pseudo-module. This function is used as the special *idle task* and must have
-signature `[unsafe] fn() - > !`.
+signature `fn(idle::Context) - > !`.
When present, the runtime will execute the `idle` task after `init`. Unlike
`init`, `idle` will run *with interrupts enabled* and it's not allowed to return
diff --git a/book/en/src/by-example/new.md b/book/en/src/by-example/new.md
index ae49ef21..91b31bb8 100644
--- a/book/en/src/by-example/new.md
+++ b/book/en/src/by-example/new.md
@@ -40,7 +40,7 @@ $ rm memory.x build.rs
`timer-queue` feature.
``` console
-$ cargo add cortex-m-rtfm
+$ cargo add cortex-m-rtfm --allow-prerelease
```
4. Write your RTFM application.
@@ -49,7 +49,7 @@ Here I'll use the `init` example from the `cortex-m-rtfm` crate.
``` console
$ curl \
- -L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0/examples/init.rs \
+ -L https://github.com/japaric/cortex-m-rtfm/raw/v0.5.0-alpha.1/examples/init.rs \
> src/main.rs
```
diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md
index 17f4d139..06f2f06c 100644
--- a/book/en/src/by-example/resources.md
+++ b/book/en/src/by-example/resources.md
@@ -10,7 +10,7 @@ have enough information to optimize the access to the shared data.
The `app` attribute has a full view of the application thus it can optimize
access to `static` variables. In RTFM we refer to the `static` variables
declared inside the `app` pseudo-module as *resources*. To access a resource the
-context (`init`, `idle`, `interrupt` or `exception`) must first declare the
+context (`init`, `idle`, `interrupt` or `exception`) one must first declare the
resource in the `resources` argument of its attribute.
In the example below two interrupt handlers access the same resource. No `Mutex`
@@ -30,7 +30,7 @@ $ cargo run --example resource
The priority of each handler can be declared in the `interrupt` and `exception`
attributes. It's not possible to set the priority in any other way because the
-runtime takes ownership of the `NVIC` peripheral; it's also not possible to
+runtime takes ownership of the `NVIC` peripheral thus it's also not possible to
change the priority of a handler / task at runtime. Thanks to this restriction
the framework has knowledge about the *static* priorities of all interrupt and
exception handlers.
@@ -71,10 +71,10 @@ $ cargo run --example lock
One more note about priorities: choosing a priority higher than what the device
supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to
-limitations in the language the error is currently far from helpful: it will say
-something along the lines of "evaluation of constant value failed" and the span
-of the error will *not* point out to the problematic interrupt value -- we are
-sorry about this!
+limitations in the language the error message is currently far from helpful: it
+will say something along the lines of "evaluation of constant value failed" and
+the span of the error will *not* point out to the problematic interrupt value --
+we are sorry about this!
## Late resources
diff --git a/book/en/src/by-example/singletons.md b/book/en/src/by-example/singletons.md
deleted file mode 100644
index 0823f057..00000000
--- a/book/en/src/by-example/singletons.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Singletons
-
-The `app` attribute is aware of [`owned-singleton`] crate and its [`Singleton`]
-attribute. When this attribute is applied to one of the resources the runtime
-will perform the `unsafe` initialization of the singleton for you, ensuring that
-only a single instance of the singleton is ever created.
-
-[`owned-singleton`]: ../../api/owned_singleton/index.html
-[`Singleton`]: ../../api/owned_singleton_macros/attr.Singleton.html
-
-Note that when using the `Singleton` attribute you'll need to have the
-`owned_singleton` in your dependencies.
-
-Below is an example that uses the `Singleton` attribute on a chunk of memory
-and then uses the singleton instance as a fixed-size memory pool using one of
-the [`alloc-singleton`] abstractions.
-
-[`alloc-singleton`]: https://crates.io/crates/alloc-singleton
-
-``` rust
-{{#include ../../../../examples/singleton.rs}}
-```
-
-``` console
-$ cargo run --example singleton
-{{#include ../../../../ci/expected/singleton.run}}```
diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md
index c0bfc56e..79b9d71e 100644
--- a/book/en/src/by-example/tips.md
+++ b/book/en/src/by-example/tips.md
@@ -24,8 +24,8 @@ of tasks.
You can use conditional compilation (`#[cfg]`) on resources (`static [mut]`
items) and tasks (`fn` items). The effect of using `#[cfg]` attributes is that
-the resource / task will *not* be injected into the prelude of tasks that use
-them (see `resources`, `spawn` and `schedule`) if the condition doesn't hold.
+the resource / task will *not* be available through the corresponding `Context`
+`struct` if the condition doesn't hold.
The example below logs a message whenever the `foo` task is spawned, but only if
the program has been compiled using the `dev` profile.
@@ -37,7 +37,7 @@ the program has been compiled using the `dev` profile.
## Running tasks from RAM
The main goal of moving the specification of RTFM applications to attributes in
-RTFM v0.4.x was to allow inter-operation with other attributes. For example, the
+RTFM v0.4.0 was to allow inter-operation with other attributes. For example, the
`link_section` attribute can be applied to tasks to place them in RAM; this can
improve performance in some cases.
@@ -78,8 +78,6 @@ $ cargo nm --example ramfunc --release | grep ' bar::'
## `binds`
-**NOTE**: Requires RTFM ~0.4.2
-
You can give hardware tasks more task-like names using the `binds` argument: you
name the function as you wish and specify the name of the interrupt / exception
in the `binds` argument. Types like `Spawn` will be placed in a module named
@@ -91,3 +89,27 @@ after the function, not the interrupt / exception. Example below:
``` console
$ cargo run --example binds
{{#include ../../../../ci/expected/binds.run}}```
+
+## Indirection for faster message passing
+
+Message passing always involves copying the payload from the sender into a
+static variable and then from the static variable into the receiver. Thus
+sending a large buffer, like a `[u8; 128]`, as a message involves two expensive
+`memcpy`s. To minimize the message passing overhead one can use indirection:
+instead of sending the buffer by value, one can send an owning pointer into the
+buffer.
+
+One can use a global allocator to achieve indirection (`alloc::Box`,
+`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.34.0,
+or one can use a statically allocated memory pool like [`heapless::Pool`].
+
+[`heapless::Pool`]: https://docs.rs/heapless/0.4.3/heapless/pool/index.html
+
+Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes.
+
+``` rust
+{{#include ../../../../examples/pool.rs}}
+```
+``` console
+$ cargo run --example binds
+{{#include ../../../../ci/expected/pool.run}}```
diff --git a/book/en/src/by-example/types-send-sync.md b/book/en/src/by-example/types-send-sync.md
index 632946b9..99f9f190 100644
--- a/book/en/src/by-example/types-send-sync.md
+++ b/book/en/src/by-example/types-send-sync.md
@@ -7,8 +7,7 @@ write plain functions that take them as arguments.
The API reference specifies how these types are generated from the input. You
can also generate documentation for you binary crate (`cargo doc --bin <name>`);
in the documentation you'll find `Context` structs (e.g. `init::Context` and
-`idle::Context`) whose fields represent the variables injected into each
-function.
+`idle::Context`).
The example below shows the different types generates by the `app` attribute.
diff --git a/book/en/src/preface.md b/book/en/src/preface.md
index d8f64fd4..e6a52b7b 100644
--- a/book/en/src/preface.md
+++ b/book/en/src/preface.md
@@ -11,6 +11,9 @@ There is a translation of this book in [Russian].
[Russian]: ../ru/index.html
+**HEADS UP** This is an **alpha** pre-release; there may be breaking changes in
+the API and semantics before a proper release is made.
+
{{#include ../../../README.md:5:46}}
{{#include ../../../README.md:52:}}
diff --git a/ci/after-success.sh b/ci/after-success.sh
index 65ddb904..6c696937 100644
--- a/ci/after-success.sh
+++ b/ci/after-success.sh
@@ -23,7 +23,8 @@ main() {
./ghp-import/ghp_import.py $td
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
}
diff --git a/ci/expected/pool.run b/ci/expected/pool.run
new file mode 100644
index 00000000..040dcee8
--- /dev/null
+++ b/ci/expected/pool.run
@@ -0,0 +1,2 @@
+bar(0x2000008c)
+foo(0x20000110)
diff --git a/ci/script.sh b/ci/script.sh
index 0e350d1f..b64617d0 100644
--- a/ci/script.sh
+++ b/ci/script.sh
@@ -104,14 +104,13 @@ main() {
message
capacity
- singleton
-
types
not-send
not-sync
shared-with-init
generics
+ pool
ramfunc
)
@@ -121,6 +120,31 @@ main() {
continue
fi
+ if [ $ex = pool ]; then
+ if [ $TARGET != thumbv6m-none-eabi ]; then
+ local td=$(mktemp -d)
+
+ local features="$nightly,timer-queue"
+ cargo run --example $ex --target $TARGET --features $features >\
+ $td/pool.run
+ grep 'foo(0x2' $td/pool.run
+ grep 'bar(0x2' $td/pool.run
+ arm-none-eabi-objcopy -O ihex target/$TARGET/debug/examples/$ex \
+ ci/builds/${ex}_${features/,/_}_debug_1.hex
+
+ cargo run --example $ex --target $TARGET --features $features --release >\
+ $td/pool.run
+ grep 'foo(0x2' $td/pool.run
+ grep 'bar(0x2' $td/pool.run
+ arm-none-eabi-objcopy -O ihex target/$TARGET/release/examples/$ex \
+ ci/builds/${ex}_${features/,/_}_release_1.hex
+
+ rm -rf $td
+ fi
+
+ continue
+ fi
+
if [ $ex != types ]; then
arm_example "run" $ex "debug" "$nightly" "1"
arm_example "run" $ex "release" "$nightly" "1"
@@ -140,13 +164,7 @@ main() {
continue
fi
- if [ $ex = singleton ]; then
- # singleton build is currently not reproducible due to
- # https://github.com/japaric/owned-singleton/issues/2
- continue
- fi
-
- if [ $ex != types ]; then
+ if [ $ex != types ] && [ $ex != pool ]; then
arm_example "build" $ex "debug" "$nightly" "2"
cmp ci/builds/${ex}_${nightly/nightly/nightly_}debug_1.hex \
ci/builds/${ex}_${nightly/nightly/nightly_}debug_2.hex
diff --git a/examples/baseline.rs b/examples/baseline.rs
index fdf36838..d743107d 100644
--- a/examples/baseline.rs
+++ b/examples/baseline.rs
@@ -9,24 +9,23 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
// NOTE: does NOT properly work on QEMU
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
- fn init() {
- hprintln!("init(baseline = {:?})", start).unwrap();
+ fn init(c: init::Context) {
+ hprintln!("init(baseline = {:?})", c.start).unwrap();
// `foo` inherits the baseline of `init`: `Instant(0)`
- spawn.foo().unwrap();
+ c.spawn.foo().unwrap();
}
#[task(schedule = [foo])]
- fn foo() {
+ fn foo(c: foo::Context) {
static mut ONCE: bool = true;
- hprintln!("foo(baseline = {:?})", scheduled).unwrap();
+ hprintln!("foo(baseline = {:?})", c.scheduled).unwrap();
if *ONCE {
*ONCE = false;
@@ -38,11 +37,11 @@ const APP: () = {
}
#[interrupt(spawn = [foo])]
- fn UART0() {
- hprintln!("UART0(baseline = {:?})", start).unwrap();
+ fn UART0(c: UART0::Context) {
+ hprintln!("UART0(baseline = {:?})", c.start).unwrap();
// `foo` inherits the baseline of `UART0`: its `start` time
- spawn.foo().unwrap();
+ c.spawn.foo().unwrap();
}
extern "C" {
diff --git a/examples/binds.rs b/examples/binds.rs
index a8b386fb..3d2d9b54 100644
--- a/examples/binds.rs
+++ b/examples/binds.rs
@@ -9,20 +9,19 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
// `examples/interrupt.rs` rewritten to use `binds`
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
+ fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0);
hprintln!("init").unwrap();
}
#[idle]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
hprintln!("idle").unwrap();
rtfm::pend(Interrupt::UART0);
@@ -33,7 +32,7 @@ const APP: () = {
}
#[interrupt(binds = UART0)]
- fn foo() {
+ fn foo(_: foo::Context) {
static mut TIMES: u32 = 0;
*TIMES += 1;
diff --git a/examples/capacity.rs b/examples/capacity.rs
index a7132ba0..07edd9b8 100644
--- a/examples/capacity.rs
+++ b/examples/capacity.rs
@@ -9,32 +9,31 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
+ fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0);
}
#[interrupt(spawn = [foo, bar])]
- fn UART0() {
- spawn.foo(0).unwrap();
- spawn.foo(1).unwrap();
- spawn.foo(2).unwrap();
- spawn.foo(3).unwrap();
+ fn UART0(c: UART0::Context) {
+ c.spawn.foo(0).unwrap();
+ c.spawn.foo(1).unwrap();
+ c.spawn.foo(2).unwrap();
+ c.spawn.foo(3).unwrap();
- spawn.bar().unwrap();
+ c.spawn.bar().unwrap();
}
#[task(capacity = 4)]
- fn foo(x: u32) {
+ fn foo(_: foo::Context, x: u32) {
hprintln!("foo({})", x).unwrap();
}
#[task]
- fn bar() {
+ fn bar(_: bar::Context) {
hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS);
diff --git a/examples/cfg.rs b/examples/cfg.rs
index 3f4ca904..03f9dbdc 100644
--- a/examples/cfg.rs
+++ b/examples/cfg.rs
@@ -9,25 +9,24 @@ extern crate panic_semihosting;
#[cfg(debug_assertions)]
use cortex_m_semihosting::hprintln;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[cfg(debug_assertions)] // <- `true` when using the `dev` profile
static mut COUNT: u32 = 0;
#[init]
- fn init() {
+ fn init(_: init::Context) {
// ..
}
#[task(priority = 3, resources = [COUNT], spawn = [log])]
- fn foo() {
+ fn foo(c: foo::Context) {
#[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
@@ -38,7 +37,7 @@ const APP: () = {
#[cfg(debug_assertions)]
#[task]
- fn log(n: u32) {
+ fn log(_: log::Context, n: u32) {
hprintln!(
"foo has been called {} time{}",
n,
diff --git a/examples/generics.rs b/examples/generics.rs
index c8ce8393..e624da39 100644
--- a/examples/generics.rs
+++ b/examples/generics.rs
@@ -9,25 +9,25 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::{app, Mutex};
+use rtfm::Mutex;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static mut SHARED: u32 = 0;
#[init]
- fn init() {
+ fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1);
}
#[interrupt(resources = [SHARED])]
- fn UART0() {
+ fn UART0(c: UART0::Context) {
static mut STATE: u32 = 0;
hprintln!("UART0(STATE = {})", *STATE).unwrap();
- advance(STATE, resources.SHARED);
+ advance(STATE, c.resources.SHARED);
rtfm::pend(Interrupt::UART1);
@@ -35,17 +35,17 @@ const APP: () = {
}
#[interrupt(priority = 2, resources = [SHARED])]
- fn UART1() {
+ fn UART1(mut c: UART1::Context) {
static mut STATE: u32 = 0;
hprintln!("UART1(STATE = {})", *STATE).unwrap();
// just to show that `SHARED` can be accessed directly and ..
- *resources.SHARED += 0;
+ *c.resources.SHARED += 0;
// .. 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);
}
};
diff --git a/examples/idle.rs b/examples/idle.rs
index 1f21a37f..d10cc43e 100644
--- a/examples/idle.rs
+++ b/examples/idle.rs
@@ -8,17 +8,16 @@
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
+ fn init(_: init::Context) {
hprintln!("init").unwrap();
}
#[idle]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
static mut X: u32 = 0;
// Safe access to local `static mut` variable
diff --git a/examples/init.rs b/examples/init.rs
index be6cfe3e..df687794 100644
--- a/examples/init.rs
+++ b/examples/init.rs
@@ -8,19 +8,18 @@
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
+ fn init(c: init::Context) {
static mut X: u32 = 0;
// Cortex-M peripherals
- let _core: rtfm::Peripherals = core;
+ let _core: rtfm::Peripherals = c.core;
// Device specific peripherals
- let _device: lm3s6965::Peripherals = device;
+ let _device: lm3s6965::Peripherals = c.device;
// Safe access to local `static mut` variable
let _x: &'static mut u32 = X;
diff --git a/examples/interrupt.rs b/examples/interrupt.rs
index 3c669d9e..dd6efa0d 100644
--- a/examples/interrupt.rs
+++ b/examples/interrupt.rs
@@ -9,12 +9,11 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
+ fn init(_: init::Context) {
// Pends the UART0 interrupt but its handler won't run until *after*
// `init` returns because interrupts are disabled
rtfm::pend(Interrupt::UART0);
@@ -23,7 +22,7 @@ const APP: () = {
}
#[idle]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
// interrupts are enabled again; the `UART0` handler runs at this point
hprintln!("idle").unwrap();
@@ -36,7 +35,7 @@ const APP: () = {
}
#[interrupt]
- fn UART0() {
+ fn UART0(_: UART0::Context) {
static mut TIMES: u32 = 0;
// Safe access to local `static mut` variable
diff --git a/examples/late.rs b/examples/late.rs
index 622008a7..0074fb32 100644
--- a/examples/late.rs
+++ b/examples/late.rs
@@ -13,16 +13,15 @@ use heapless::{
spsc::{Consumer, Producer, Queue},
};
use lm3s6965::Interrupt;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
// Late resources
static mut P: Producer<'static, u32, U4> = ();
static mut C: Consumer<'static, u32, U4> = ();
#[init]
- fn init() -> init::LateResources {
+ fn init(_: init::Context) -> init::LateResources {
// NOTE: we use `Option` here to work around the lack of
// a stable `const` constructor
static mut Q: Option<Queue<u32, U4>> = None;
@@ -35,9 +34,9 @@ const APP: () = {
}
#[idle(resources = [C])]
- fn idle() -> ! {
+ fn idle(c: idle::Context) -> ! {
loop {
- if let Some(byte) = resources.C.dequeue() {
+ if let Some(byte) = c.resources.C.dequeue() {
hprintln!("received message: {}", byte).unwrap();
debug::exit(debug::EXIT_SUCCESS);
@@ -48,7 +47,7 @@ const APP: () = {
}
#[interrupt(resources = [P])]
- fn UART0() {
- resources.P.enqueue(42).unwrap();
+ fn UART0(c: UART0::Context) {
+ c.resources.P.enqueue(42).unwrap();
}
};
diff --git a/examples/lock.rs b/examples/lock.rs
index 4ca862e3..814c7364 100644
--- a/examples/lock.rs
+++ b/examples/lock.rs
@@ -9,24 +9,23 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static mut SHARED: u32 = 0;
#[init]
- fn init() {
+ fn init(_: init::Context) {
rtfm::pend(Interrupt::GPIOA);
}
// when omitted priority is assumed to be `1`
#[interrupt(resources = [SHARED])]
- fn GPIOA() {
+ fn GPIOA(mut c: GPIOA::Context) {
hprintln!("A").unwrap();
// 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)
*shared += 1;
@@ -47,15 +46,15 @@ const APP: () = {
}
#[interrupt(priority = 2, resources = [SHARED])]
- fn GPIOB() {
+ fn GPIOB(mut c: GPIOB::Context) {
// 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)]
- fn GPIOC() {
+ fn GPIOC(_: GPIOC::Context) {
hprintln!("C").unwrap();
}
};
diff --git a/examples/message.rs b/examples/message.rs
index b5d68a60..1fd3b9d4 100644
--- a/examples/message.rs
+++ b/examples/message.rs
@@ -8,41 +8,40 @@
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
- fn init() {
- spawn.foo(/* no message */).unwrap();
+ fn init(c: init::Context) {
+ c.spawn.foo(/* no message */).unwrap();
}
#[task(spawn = [bar])]
- fn foo() {
+ fn foo(c: foo::Context) {
static mut COUNT: u32 = 0;
hprintln!("foo").unwrap();
- spawn.bar(*COUNT).unwrap();
+ c.spawn.bar(*COUNT).unwrap();
*COUNT += 1;
}
#[task(spawn = [baz])]
- fn bar(x: u32) {
+ fn bar(c: bar::Context, x: u32) {
hprintln!("bar({})", x).unwrap();
- spawn.baz(x + 1, x + 2).unwrap();
+ c.spawn.baz(x + 1, x + 2).unwrap();
}
#[task(spawn = [foo])]
- fn baz(x: u32, y: u32) {
+ fn baz(c: baz::Context, x: u32, y: u32) {
hprintln!("baz({}, {})", x, y).unwrap();
if x + y > 4 {
debug::exit(debug::EXIT_SUCCESS);
}
- spawn.foo().unwrap();
+ c.spawn.foo().unwrap();
}
extern "C" {
diff --git a/examples/not-send.rs b/examples/not-send.rs
index be78c332..c1b6bcdd 100644
--- a/examples/not-send.rs
+++ b/examples/not-send.rs
@@ -21,32 +21,32 @@ const APP: () = {
static mut SHARED: Option<NotSend> = None;
#[init(spawn = [baz, quux])]
- fn init() {
- spawn.baz().unwrap();
- spawn.quux().unwrap();
+ fn init(c: init::Context) {
+ c.spawn.baz().unwrap();
+ c.spawn.quux().unwrap();
}
#[task(spawn = [bar])]
- fn foo() {
+ fn foo(c: foo::Context) {
// 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]
- fn bar(_x: NotSend) {
+ fn bar(_: bar::Context, _x: NotSend) {
// scenario 1
}
#[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
- *resources.SHARED = Some(NotSend { _0: PhantomData });
+ *c.resources.SHARED = Some(NotSend { _0: PhantomData });
}
#[task(priority = 2, resources = [SHARED])]
- fn quux() {
+ fn quux(mut c: quux::Context) {
// scenario 2
- let _not_send = resources.SHARED.take().unwrap();
+ let _not_send = c.resources.SHARED.take().unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
diff --git a/examples/not-sync.rs b/examples/not-sync.rs
index d94e0a04..bc714065 100644
--- a/examples/not-sync.rs
+++ b/examples/not-sync.rs
@@ -10,29 +10,28 @@ extern crate panic_halt;
use core::marker::PhantomData;
use cortex_m_semihosting::debug;
-use rtfm::app;
pub struct NotSync {
_0: PhantomData<*const ()>,
}
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static SHARED: NotSync = NotSync { _0: PhantomData };
#[init]
- fn init() {
+ fn init(_: init::Context) {
debug::exit(debug::EXIT_SUCCESS);
}
#[task(resources = [SHARED])]
- fn foo() {
- let _: &NotSync = resources.SHARED;
+ fn foo(c: foo::Context) {
+ let _: &NotSync = c.resources.SHARED;
}
#[task(resources = [SHARED])]
- fn bar() {
- let _: &NotSync = resources.SHARED;
+ fn bar(c: bar::Context) {
+ let _: &NotSync = c.resources.SHARED;
}
extern "C" {
diff --git a/examples/periodic.rs b/examples/periodic.rs
index ba2b4933..f7841183 100644
--- a/examples/periodic.rs
+++ b/examples/periodic.rs
@@ -8,24 +8,24 @@
extern crate panic_semihosting;
use cortex_m_semihosting::hprintln;
-use rtfm::{app, Instant};
+use rtfm::Instant;
const PERIOD: u32 = 8_000_000;
// NOTE: does NOT work on QEMU!
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(schedule = [foo])]
- fn init() {
- schedule.foo(Instant::now() + PERIOD.cycles()).unwrap();
+ fn init(c: init::Context) {
+ c.schedule.foo(Instant::now() + PERIOD.cycles()).unwrap();
}
#[task(schedule = [foo])]
- fn foo() {
+ fn foo(c: foo::Context) {
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" {
diff --git a/examples/pool.rs b/examples/pool.rs
new file mode 100644
index 00000000..0b594b19
--- /dev/null
+++ b/examples/pool.rs
@@ -0,0 +1,67 @@
+//! examples/pool.rs
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+extern crate panic_semihosting;
+
+use cortex_m_semihosting::{debug, hprintln};
+use heapless::{
+ pool,
+ pool::singleton::{Box, Pool},
+};
+use lm3s6965::Interrupt;
+use rtfm::app;
+
+// Declare a pool of 128-byte memory blocks
+pool!(P: [u8; 128]);
+
+#[app(device = lm3s6965)]
+const APP: () = {
+ #[init]
+ fn init(_: init::Context) {
+ static mut MEMORY: [u8; 512] = [0; 512];
+
+ // Increase the capacity of the memory pool by ~4
+ P::grow(MEMORY);
+
+ rtfm::pend(Interrupt::I2C0);
+ }
+
+ #[interrupt(priority = 2, spawn = [foo, bar])]
+ fn I2C0(c: I2C0::Context) {
+ // claim a memory block, leave it uninitialized and ..
+ let x = P::alloc().unwrap().freeze();
+
+ // .. send it to the `foo` task
+ c.spawn.foo(x).ok().unwrap();
+
+ // send another block to the task `bar`
+ c.spawn.bar(P::alloc().unwrap().freeze()).ok().unwrap();
+ }
+
+ #[task]
+ fn foo(_: foo::Context, x: Box<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();
+ }
+};
diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs
index 37ea82a7..4b0d69c7 100644
--- a/examples/ramfunc.rs
+++ b/examples/ramfunc.rs
@@ -8,18 +8,17 @@
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [bar])]
- fn init() {
- spawn.bar().unwrap();
+ fn init(c: init::Context) {
+ c.spawn.bar().unwrap();
}
#[inline(never)]
#[task]
- fn foo() {
+ fn foo(_: foo::Context) {
hprintln!("foo").unwrap();
debug::exit(debug::EXIT_SUCCESS);
@@ -29,8 +28,8 @@ const APP: () = {
#[inline(never)]
#[link_section = ".data.bar"]
#[task(priority = 2, spawn = [foo])]
- fn bar() {
- spawn.foo().unwrap();
+ fn bar(c: bar::Context) {
+ c.spawn.foo().unwrap();
}
extern "C" {
diff --git a/examples/resource.rs b/examples/resource.rs
index 5ddab9e8..06bdf395 100644
--- a/examples/resource.rs
+++ b/examples/resource.rs
@@ -9,21 +9,20 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
// A resource
static mut SHARED: u32 = 0;
#[init]
- fn init() {
+ fn init(_: init::Context) {
rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1);
}
#[idle]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
debug::exit(debug::EXIT_SUCCESS);
// error: `SHARED` can't be accessed from this context
@@ -34,17 +33,17 @@ const APP: () = {
// `SHARED` can be access from this context
#[interrupt(resources = [SHARED])]
- fn UART0() {
- *resources.SHARED += 1;
+ fn UART0(mut c: UART0::Context) {
+ *c.resources.SHARED += 1;
- hprintln!("UART0: SHARED = {}", resources.SHARED).unwrap();
+ hprintln!("UART0: SHARED = {}", c.resources.SHARED).unwrap();
}
// `SHARED` can be access from this context
#[interrupt(resources = [SHARED])]
- fn UART1() {
- *resources.SHARED += 1;
+ fn UART1(mut c: UART1::Context) {
+ *c.resources.SHARED += 1;
- hprintln!("UART1: SHARED = {}", resources.SHARED).unwrap();
+ hprintln!("UART1: SHARED = {}", c.resources.SHARED).unwrap();
}
};
diff --git a/examples/schedule.rs b/examples/schedule.rs
index fd633473..eaafb4c9 100644
--- a/examples/schedule.rs
+++ b/examples/schedule.rs
@@ -8,31 +8,31 @@
extern crate panic_semihosting;
use cortex_m_semihosting::hprintln;
-use rtfm::{app, Instant};
+use rtfm::Instant;
// NOTE: does NOT work on QEMU!
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(schedule = [foo, bar])]
- fn init() {
+ fn init(c: init::Context) {
let now = Instant::now();
hprintln!("init @ {:?}", now).unwrap();
// 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(now + 4_000_000.cycles()).unwrap();
+ c.schedule.bar(now + 4_000_000.cycles()).unwrap();
}
#[task]
- fn foo() {
+ fn foo(_: foo::Context) {
hprintln!("foo @ {:?}", Instant::now()).unwrap();
}
#[task]
- fn bar() {
+ fn bar(_: bar::Context) {
hprintln!("bar @ {:?}", Instant::now()).unwrap();
}
diff --git a/examples/shared-with-init.rs b/examples/shared-with-init.rs
index 5ddd2cc3..0fb9191c 100644
--- a/examples/shared-with-init.rs
+++ b/examples/shared-with-init.rs
@@ -18,17 +18,17 @@ const APP: () = {
static mut SHARED: Option<MustBeSend> = None;
#[init(resources = [SHARED])]
- fn init() {
+ fn init(c: init::Context) {
// this `message` will be sent to task `UART0`
let message = MustBeSend;
- *resources.SHARED = Some(message);
+ *c.resources.SHARED = Some(message);
rtfm::pend(Interrupt::UART0);
}
#[interrupt(resources = [SHARED])]
- fn UART0() {
- if let Some(message) = resources.SHARED.take() {
+ fn UART0(c: UART0::Context) {
+ if let Some(message) = c.resources.SHARED.take() {
// `message` has been received
drop(message);
diff --git a/examples/singleton.rs b/examples/singleton.rs
deleted file mode 100644
index 9e48e541..00000000
--- a/examples/singleton.rs
+++ /dev/null
@@ -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();
- }
-};
diff --git a/examples/smallest.rs b/examples/smallest.rs
index e4d86be9..c1537168 100644
--- a/examples/smallest.rs
+++ b/examples/smallest.rs
@@ -13,5 +13,5 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
};
diff --git a/examples/static.rs b/examples/static.rs
index 0309b681..2e3b5b41 100644
--- a/examples/static.rs
+++ b/examples/static.rs
@@ -9,14 +9,13 @@ extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static KEY: u32 = ();
#[init]
- fn init() -> init::LateResources {
+ fn init(_: init::Context) -> init::LateResources {
rtfm::pend(Interrupt::UART0);
rtfm::pend(Interrupt::UART1);
@@ -24,14 +23,14 @@ const APP: () = {
}
#[interrupt(resources = [KEY])]
- fn UART0() {
- hprintln!("UART0(KEY = {:#x})", resources.KEY).unwrap();
+ fn UART0(c: UART0::Context) {
+ hprintln!("UART0(KEY = {:#x})", c.resources.KEY).unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
#[interrupt(priority = 2, resources = [KEY])]
- fn UART1() {
- hprintln!("UART1(KEY = {:#x})", resources.KEY).unwrap();
+ fn UART1(c: UART1::Context) {
+ hprintln!("UART1(KEY = {:#x})", c.resources.KEY).unwrap();
}
};
diff --git a/examples/task.rs b/examples/task.rs
index 4f168bb8..5bb32acb 100644
--- a/examples/task.rs
+++ b/examples/task.rs
@@ -8,38 +8,37 @@
extern crate panic_semihosting;
use cortex_m_semihosting::{debug, hprintln};
-use rtfm::app;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo])]
- fn init() {
- spawn.foo().unwrap();
+ fn init(c: init::Context) {
+ c.spawn.foo().unwrap();
}
#[task(spawn = [bar, baz])]
- fn foo() {
+ fn foo(c: foo::Context) {
hprintln!("foo").unwrap();
// spawns `bar` onto the task scheduler
// `foo` and `bar` have the same priority so `bar` will not run until
// after `foo` terminates
- spawn.bar().unwrap();
+ c.spawn.bar().unwrap();
// spawns `baz` onto the task scheduler
// `baz` has higher priority than `foo` so it immediately preempts `foo`
- spawn.baz().unwrap();
+ c.spawn.baz().unwrap();
}
#[task]
- fn bar() {
+ fn bar(_: bar::Context) {
hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
#[task(priority = 2)]
- fn baz() {
+ fn baz(_: baz::Context) {
hprintln!("baz").unwrap();
}
diff --git a/examples/types.rs b/examples/types.rs
index c1b8cd69..c3dd89ca 100644
--- a/examples/types.rs
+++ b/examples/types.rs
@@ -8,45 +8,45 @@
extern crate panic_semihosting;
use cortex_m_semihosting::debug;
-use rtfm::{app, Exclusive, Instant};
+use rtfm::{Exclusive, Instant};
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static mut SHARED: u32 = 0;
#[init(schedule = [foo], spawn = [foo])]
- fn init() {
- let _: Instant = start;
- let _: rtfm::Peripherals = core;
- let _: lm3s6965::Peripherals = device;
- let _: init::Schedule = schedule;
- let _: init::Spawn = spawn;
+ fn init(c: init::Context) {
+ let _: Instant = c.start;
+ let _: rtfm::Peripherals = c.core;
+ let _: lm3s6965::Peripherals = c.device;
+ let _: init::Schedule = c.schedule;
+ let _: init::Spawn = c.spawn;
debug::exit(debug::EXIT_SUCCESS);
}
#[exception(schedule = [foo], spawn = [foo])]
- fn SVCall() {
- let _: Instant = start;
- let _: SVCall::Schedule = schedule;
- let _: SVCall::Spawn = spawn;
+ fn SVCall(c: SVCall::Context) {
+ let _: Instant = c.start;
+ let _: SVCall::Schedule = c.schedule;
+ let _: SVCall::Spawn = c.spawn;
}
#[interrupt(resources = [SHARED], schedule = [foo], spawn = [foo])]
- fn UART0() {
- let _: Instant = start;
- let _: resources::SHARED = resources.SHARED;
- let _: UART0::Schedule = schedule;
- let _: UART0::Spawn = spawn;
+ fn UART0(c: UART0::Context) {
+ let _: Instant = c.start;
+ let _: resources::SHARED = c.resources.SHARED;
+ let _: UART0::Schedule = c.schedule;
+ let _: UART0::Spawn = c.spawn;
}
#[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])]
- fn foo() {
- let _: Instant = scheduled;
- let _: Exclusive<u32> = resources.SHARED;
- let _: foo::Resources = resources;
- let _: foo::Schedule = schedule;
- let _: foo::Spawn = spawn;
+ fn foo(c: foo::Context) {
+ let _: Instant = c.scheduled;
+ let _: Exclusive<u32> = c.resources.SHARED;
+ let _: foo::Resources = c.resources;
+ let _: foo::Schedule = c.schedule;
+ let _: foo::Spawn = c.spawn;
}
extern "C" {
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index d891ba99..3771869c 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm-macros"
readme = "../README.md"
repository = "https://github.com/japaric/cortex-m-rtfm"
-version = "0.4.3"
+version = "0.5.0-alpha.1"
[lib]
proc-macro = true
@@ -22,10 +22,6 @@ proc-macro2 = "0.4.24"
features = ["extra-traits", "full"]
version = "0.15.23"
-[dependencies.rand]
-default-features = false
-version = "0.5.5"
-
[features]
timer-queue = []
nightly = [] \ No newline at end of file
diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs
index cfd8ebc9..a47be779 100644
--- a/macros/src/analyze.rs
+++ b/macros/src/analyze.rs
@@ -190,19 +190,20 @@ pub fn app(app: &App) -> Analysis {
}
// 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
- let mut free_queues: HashMap<_, _> = app.tasks.keys().map(|task| (task.clone(), 0)).collect();
- let mut ready_queues: HashMap<_, _> = dispatchers.keys().map(|level| (*level, 0)).collect();
+ let mut free_queues = HashMap::new();
+ let mut ready_queues = HashMap::new();
for (priority, task) in app.spawn_calls() {
if let Some(priority) = priority {
- // Users of `spawn` contend for the to-be-spawned task FREE_QUEUE
- let c = free_queues.get_mut(task).expect("BUG: free_queue.get_mut");
+ // Users of `spawn` contend for the spawnee FREE_QUEUE
+ let c = free_queues.entry(task.clone()).or_default();
*c = cmp::max(*c, priority);
+ // Users of `spawn` contend for the spawnee's dispatcher READY_QUEUE
let c = ready_queues
- .get_mut(&app.tasks[task].args.priority)
- .expect("BUG: ready_queues.get_mut");
+ .entry(app.tasks[task].args.priority)
+ .or_default();
*c = cmp::max(*c, priority);
// 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 the timer queue
let mut tq_ceiling = tq_priority;
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 {
- // Users of `schedule` contend for the to-be-spawned task FREE_QUEUE (consumer end point)
- let c = free_queues.get_mut(task).expect("BUG: free_queue.get_mut");
+ // Users of `schedule` contend for the spawnee task FREE_QUEUE
+ let c = free_queues.entry(task.clone()).or_default();
*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);
} else {
// spawns from `init` are excluded from the ceiling analysis
diff --git a/macros/src/check.rs b/macros/src/check.rs
index 4adc2c17..4471e96a 100644
--- a/macros/src/check.rs
+++ b/macros/src/check.rs
@@ -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
- // `fn()`
- if !app.init.returns_late_resources {
- for res in
- app.resources
- .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 `init` returns `LateResources` if there's any declared late resource
+ if !app.init.returns_late_resources && app.resources.iter().any(|(_, res)| res.expr.is_none()) {
+ return Err(parse::Error::new(
+ app.init.span,
+ "late resources have been specified so `init` must return `init::LateResources`",
+ ));
}
// 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 {
return Err(parse::Error::new(
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",
));
}
diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs
index 4468216f..3fae75b7 100644
--- a/macros/src/codegen.rs
+++ b/macros/src/codegen.rs
@@ -1,1454 +1,741 @@
-#![deny(warnings)]
-
use proc_macro::TokenStream;
-use std::{
- collections::{BTreeMap, HashMap},
- time::{SystemTime, UNIX_EPOCH},
-};
+use std::collections::{BTreeMap, BTreeSet};
use proc_macro2::Span;
use quote::quote;
-use rand::{Rng, SeedableRng};
-use syn::{parse_quote, ArgCaptured, Attribute, Ident, IntSuffix, LitInt};
+use syn::{ArgCaptured, Attribute, Ident, IntSuffix, LitInt};
use crate::{
analyze::{Analysis, Ownership},
- syntax::{App, Idents, Static},
+ syntax::{App, Static},
};
-// NOTE to avoid polluting the user namespaces we map some identifiers to pseudo-hygienic names.
-// In some instances we also use the pseudo-hygienic names for safety, for example the user should
-// not modify the priority field of resources.
-type Aliases = BTreeMap<Ident, Ident>;
-
-struct Context {
- // Alias
- #[cfg(feature = "timer-queue")]
- baseline: Ident,
- dispatchers: BTreeMap<u8, Dispatcher>,
- // Alias (`fn`)
- idle: Ident,
- // Alias (`fn`)
- init: Ident,
- // Alias
- priority: Ident,
- // For non-singletons this maps the resource name to its `static mut` variable name
- statics: Aliases,
- /// Task -> Alias (`struct`)
- resources: HashMap<Kind, Resources>,
- // Alias (`enum`)
- schedule_enum: Ident,
- // Task -> Alias (`fn`)
- schedule_fn: Aliases,
- tasks: BTreeMap<Ident, Task>,
- // Alias (`struct` / `static mut`)
- timer_queue: Ident,
- // Generator of Ident names or suffixes
- ident_gen: IdentGenerator,
-}
-
-struct Dispatcher {
- enum_: Ident,
- ready_queue: Ident,
-}
-
-struct Task {
- alias: Ident,
- free_queue: Ident,
- inputs: Ident,
- spawn_fn: Ident,
-
- #[cfg(feature = "timer-queue")]
- scheduleds: Ident,
-}
-
-impl Default for Context {
- fn default() -> Self {
- let mut ident_gen = IdentGenerator::new();
-
- Context {
- #[cfg(feature = "timer-queue")]
- baseline: ident_gen.mk_ident(None, false),
- dispatchers: BTreeMap::new(),
- idle: ident_gen.mk_ident(Some("idle"), false),
- init: ident_gen.mk_ident(Some("init"), false),
- priority: ident_gen.mk_ident(None, false),
- statics: Aliases::new(),
- resources: HashMap::new(),
- schedule_enum: ident_gen.mk_ident(None, false),
- schedule_fn: Aliases::new(),
- tasks: BTreeMap::new(),
- timer_queue: ident_gen.mk_ident(None, false),
- ident_gen,
- }
- }
-}
-
-struct Resources {
- alias: Ident,
- decl: proc_macro2::TokenStream,
-}
-
-pub fn app(app: &App, analysis: &Analysis) -> TokenStream {
- let mut ctxt = Context::default();
-
- let resources = resources(&mut ctxt, &app, analysis);
-
- let tasks = tasks(&mut ctxt, &app, analysis);
-
- let (dispatchers_data, dispatchers) = dispatchers(&mut ctxt, &app, analysis);
-
- let (init_fn, has_late_resources) = init(&mut ctxt, &app, analysis);
- let init_arg = if cfg!(feature = "timer-queue") {
- quote!(rtfm::Peripherals {
- CBP: p.CBP,
- CPUID: p.CPUID,
- DCB: &mut p.DCB,
- FPB: p.FPB,
- FPU: p.FPU,
- ITM: p.ITM,
- MPU: p.MPU,
- SCB: &mut p.SCB,
- TPIU: p.TPIU,
- })
- } else {
- quote!(rtfm::Peripherals {
- CBP: p.CBP,
- CPUID: p.CPUID,
- DCB: p.DCB,
- DWT: p.DWT,
- FPB: p.FPB,
- FPU: p.FPU,
- ITM: p.ITM,
- MPU: p.MPU,
- SCB: &mut p.SCB,
- SYST: p.SYST,
- TPIU: p.TPIU,
- })
- };
+pub fn app(name: &Ident, app: &App, analysis: &Analysis) -> TokenStream {
+ let (const_app_resources, mod_resources) = resources(app, analysis);
- let init = &ctxt.init;
- let init_phase = if has_late_resources {
- let assigns = app
- .resources
- .iter()
- .filter_map(|(name, res)| {
- if res.expr.is_none() {
- let alias = &ctxt.statics[name];
+ let (
+ const_app_exceptions,
+ exception_mods,
+ exception_locals,
+ exception_resources,
+ user_exceptions,
+ ) = exceptions(app, analysis);
- Some(quote!(#alias.write(res.#name);))
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
+ let (
+ const_app_interrupts,
+ interrupt_mods,
+ interrupt_locals,
+ interrupt_resources,
+ user_interrupts,
+ ) = interrupts(app, analysis);
- quote!(
- let res = #init(#init_arg);
- #(#assigns)*
- )
- } else {
- quote!(#init(#init_arg);)
- };
+ let (const_app_tasks, task_mods, task_locals, task_resources, user_tasks) =
+ tasks(app, analysis);
- let post_init = post_init(&ctxt, &app, analysis);
+ let const_app_dispatchers = dispatchers(&app, analysis);
- let (idle_fn, idle_expr) = idle(&mut ctxt, &app, analysis);
+ let const_app_spawn = spawn(app, analysis);
- let exceptions = exceptions(&mut ctxt, app, analysis);
+ let const_app_tq = timer_queue(app, analysis);
- let (root_interrupts, scoped_interrupts) = interrupts(&mut ctxt, app, analysis);
+ let const_app_schedule = schedule(app);
- let spawn = spawn(&mut ctxt, app, analysis);
+ let assertion_stmts = assertions(app, analysis);
- let schedule = match () {
- #[cfg(feature = "timer-queue")]
- () => schedule(&ctxt, app),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
+ let pre_init_stmts = pre_init(&app, analysis);
- let timer_queue = timer_queue(&mut ctxt, app, analysis);
+ let (
+ const_app_init,
+ mod_init,
+ init_locals,
+ init_resources,
+ init_late_resources,
+ user_init,
+ call_init,
+ ) = init(app, analysis);
- let pre_init = pre_init(&ctxt, &app, analysis);
+ let post_init_stmts = post_init(&app, analysis);
- let assertions = assertions(app, analysis);
+ let (const_app_idle, mod_idle, idle_locals, idle_resources, user_idle, call_idle) =
+ idle(app, analysis);
- let main = ctxt.ident_gen.mk_ident(None, false);
+ let device = &app.args.device;
quote!(
- #resources
+ #user_init
- #spawn
+ #user_idle
- #timer_queue
+ #(#user_exceptions)*
- #schedule
+ #(#user_interrupts)*
- #dispatchers_data
+ #(#user_tasks)*
- #(#exceptions)*
+ #mod_resources
- #root_interrupts
+ #init_locals
- const APP: () = {
- #scoped_interrupts
+ #init_resources
- #(#dispatchers)*
- };
+ #init_late_resources
- #(#tasks)*
+ #mod_init
- #init_fn
+ #idle_locals
- #idle_fn
+ #idle_resources
- #[export_name = "main"]
- #[allow(unsafe_code)]
- #[doc(hidden)]
- unsafe fn #main() -> ! {
- #assertions
+ #mod_idle
- rtfm::export::interrupt::disable();
+ #(#exception_locals)*
- #pre_init
+ #(#exception_resources)*
- #init_phase
+ #(#exception_mods)*
- #post_init
+ #(#interrupt_locals)*
- rtfm::export::interrupt::enable();
+ #(#interrupt_resources)*
- #idle_expr
- }
- )
- .into()
-}
+ #(#interrupt_mods)*
-fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
- let mut items = vec![];
- let mut module = vec![];
- for (name, res) in &app.resources {
- let cfgs = &res.cfgs;
- let attrs = &res.attrs;
- let mut_ = &res.mutability;
- let ty = &res.ty;
- let expr = &res.expr;
+ #(#task_locals)*
- if res.singleton {
- items.push(quote!(
- #(#attrs)*
- pub static #mut_ #name: #ty = #expr;
- ));
+ #(#task_resources)*
- let alias = ctxt.ident_gen.mk_ident(None, true); // XXX is randomness required?
- if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) {
- items.push(mk_resource(
- ctxt,
- cfgs,
- name,
- quote!(#name),
- *ceiling,
- quote!(&mut <#name as owned_singleton::Singleton>::new()),
- app,
- Some(&mut module),
- ))
- }
+ #(#task_mods)*
- ctxt.statics.insert(name.clone(), alias);
- } else {
- let alias = ctxt.ident_gen.mk_ident(None, false);
- let symbol = format!("{}::{}", name, alias);
-
- items.push(
- expr.as_ref()
- .map(|expr| {
- quote!(
- #(#attrs)*
- #(#cfgs)*
- #[doc = #symbol]
- static mut #alias: #ty = #expr;
- )
- })
- .unwrap_or_else(|| {
- quote!(
- #(#attrs)*
- #(#cfgs)*
- #[doc = #symbol]
- static mut #alias: rtfm::export::MaybeUninit<#ty> =
- rtfm::export::MaybeUninit::uninit();
- )
- }),
- );
+ /// Implementation details
+ const #name: () = {
+ // always include the device crate, which contains the vector table
+ use #device as _;
- if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) {
- if res.mutability.is_some() {
- let ptr = if res.expr.is_none() {
- quote!(unsafe { &mut *#alias.as_mut_ptr() })
- } else {
- quote!(unsafe { &mut #alias })
- };
+ #(#const_app_resources)*
- items.push(mk_resource(
- ctxt,
- cfgs,
- name,
- quote!(#ty),
- *ceiling,
- ptr,
- app,
- Some(&mut module),
- ));
- }
- }
+ #const_app_init
- ctxt.statics.insert(name.clone(), alias);
- }
- }
+ #const_app_idle
- if !module.is_empty() {
- items.push(quote!(
- /// Resource proxies
- pub mod resources {
- #(#module)*
- }
- ));
- }
+ #(#const_app_exceptions)*
- quote!(#(#items)*)
-}
+ #(#const_app_interrupts)*
-fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::TokenStream, bool) {
- let attrs = &app.init.attrs;
- let locals = mk_locals(&app.init.statics, true);
- let stmts = &app.init.stmts;
- // TODO remove in v0.5.x
- let assigns = app
- .init
- .assigns
- .iter()
- .map(|assign| {
- let attrs = &assign.attrs;
- if app
- .resources
- .get(&assign.left)
- .map(|r| r.expr.is_none())
- .unwrap_or(false)
- {
- let alias = &ctxt.statics[&assign.left];
- let expr = &assign.right;
- quote!(
- #(#attrs)*
- unsafe { #alias.write(#expr); }
- )
- } else {
- let left = &assign.left;
- let right = &assign.right;
- quote!(
- #(#attrs)*
- #left = #right;
- )
- }
- })
- .collect::<Vec<_>>();
+ #(#const_app_dispatchers)*
- let prelude = prelude(
- ctxt,
- Kind::Init,
- &app.init.args.resources,
- &app.init.args.spawn,
- &app.init.args.schedule,
- app,
- 255,
- analysis,
- );
+ #(#const_app_tasks)*
- let (late_resources, late_resources_ident, ret) = if app.init.returns_late_resources {
- // create `LateResources` struct in the root of the crate
- let ident = ctxt.ident_gen.mk_ident(None, false);
+ #(#const_app_spawn)*
- let fields = app
- .resources
- .iter()
- .filter_map(|(name, res)| {
- if res.expr.is_none() {
- let ty = &res.ty;
- Some(quote!(pub #name: #ty))
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
+ #(#const_app_tq)*
- let late_resources = quote!(
- #[allow(non_snake_case)]
- pub struct #ident {
- #(#fields),*
- }
- );
+ #(#const_app_schedule)*
- (
- Some(late_resources),
- Some(ident),
- Some(quote!(-> init::LateResources)),
- )
- } else {
- (None, None, None)
- };
- let has_late_resources = late_resources.is_some();
-
- let module = module(
- ctxt,
- Kind::Init,
- !app.init.args.schedule.is_empty(),
- !app.init.args.spawn.is_empty(),
- app,
- late_resources_ident,
- );
+ #[no_mangle]
+ unsafe fn main() -> ! {
+ #(#assertion_stmts)*
- #[cfg(feature = "timer-queue")]
- let baseline = &ctxt.baseline;
- let baseline_let = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(let ref #baseline = rtfm::Instant::artificial(0);),
+ #(#pre_init_stmts)*
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
+ #call_init
- let start_let = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(
- #[allow(unused_variables)]
- let start = *#baseline;
- ),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
+ #(#post_init_stmts)*
- let unsafety = &app.init.unsafety;
- let device = &app.args.device;
- let init = &ctxt.init;
- (
- quote!(
- #late_resources
-
- #module
-
- // unsafe trampoline to deter end-users from calling this non-reentrant function
- #(#attrs)*
- unsafe fn #init(core: rtfm::Peripherals) #ret {
- #[inline(always)]
- #unsafety fn init(mut core: rtfm::Peripherals) #ret {
- #(#locals)*
-
- #baseline_let
-
- #prelude
-
- let mut device = unsafe { #device::Peripherals::steal() };
-
- #start_let
-
- #(#stmts)*
-
- #(#assigns)*
- }
-
- init(core)
+ #call_idle
}
- ),
- has_late_resources,
+ };
)
+ .into()
}
-fn post_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
- let mut exprs = vec![];
-
- let device = &app.args.device;
- let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
- for (handler, exception) in &app.exceptions {
- let name = exception.args.binds(handler);
- let priority = exception.args.priority;
-
- // compile time assert that the priority is supported by the device
- exprs.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
-
- exprs.push(quote!(p.SCB.set_priority(
- rtfm::export::SystemHandler::#name,
- ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits),
- )));
- }
-
- if !analysis.timer_queue.tasks.is_empty() {
- let priority = analysis.timer_queue.priority;
-
- // compile time assert that the priority is supported by the device
- exprs.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
-
- exprs.push(quote!(p.SCB.set_priority(
- rtfm::export::SystemHandler::SysTick,
- ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits),
- )));
- }
-
- if app.idle.is_none() {
- // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR
- exprs.push(quote!(p.SCB.scr.modify(|r| r | 1 << 1)));
- }
-
- // Enable and start the system timer
- if !analysis.timer_queue.tasks.is_empty() {
- let tq = &ctxt.timer_queue;
- exprs.push(
- quote!((*#tq.as_mut_ptr()).syst.set_clock_source(rtfm::export::SystClkSource::Core)),
- );
- exprs.push(quote!((*#tq.as_mut_ptr()).syst.enable_counter()));
- }
-
- // Enable cycle counter
- if cfg!(feature = "timer-queue") {
- exprs.push(quote!(p.DCB.enable_trace()));
- exprs.push(quote!(p.DWT.enable_cycle_counter()));
- }
-
- quote!(#(#exprs;)*)
-}
-
-/// This function creates creates a module for `init` / `idle` / a `task` (see `kind` argument)
-fn module(
- ctxt: &mut Context,
- kind: Kind,
- schedule: bool,
- spawn: bool,
+/* Main functions */
+/// In this pass we generate a static variable and a resource proxy for each resource
+///
+/// If the user specified a resource like this:
+///
+/// ```
+/// #[rtfm::app(device = ..)]
+/// const APP: () = {
+/// static mut X: UserDefinedStruct = ();
+/// static mut Y: u64 = 0;
+/// static mut Z: u32 = 0;
+/// }
+/// ```
+///
+/// We'll generate code like this:
+///
+/// - `const_app`
+///
+/// ```
+/// const APP: () = {
+/// static mut X: MaybeUninit<UserDefinedStruct> = MaybeUninit::uninit();
+/// static mut Y: u64 = 0;
+/// static mut Z: u32 = 0;
+///
+/// impl<'a> Mutex for resources::X<'a> { .. }
+///
+/// impl<'a> Mutex for resources::Y<'a> { .. }
+///
+/// // but not for `Z` because it's not shared and thus requires no proxy
+/// };
+/// ```
+///
+/// - `mod_resources`
+///
+/// ```
+/// mod resources {
+/// pub struct X<'a> {
+/// priority: &'a Priority,
+/// }
+///
+/// impl<'a> X<'a> {
+/// pub unsafe fn new(priority: &'a Priority) -> Self {
+/// X { priority }
+/// }
+///
+/// pub unsafe fn priority(&self) -> &Priority {
+/// self.priority
+/// }
+/// }
+///
+/// // same thing for `Y`
+///
+/// // but not for `Z`
+/// }
+/// ```
+fn resources(
app: &App,
- late_resources: Option<Ident>,
-) -> proc_macro2::TokenStream {
- let mut items = vec![];
- let mut fields = vec![];
-
- let name = kind.ident();
- let priority = &ctxt.priority;
- let device = &app.args.device;
+ analysis: &Analysis,
+) -> (
+ // const_app
+ Vec<proc_macro2::TokenStream>,
+ // mod_resources
+ proc_macro2::TokenStream,
+) {
+ let mut const_app = vec![];
+ let mut mod_resources = vec![];
- let mut lt = None;
- match kind {
- Kind::Init => {
- if cfg!(feature = "timer-queue") {
- fields.push(quote!(
- /// System start time = `Instant(0 /* cycles */)`
- pub start: rtfm::Instant,
- ));
- }
+ for (name, res) in &app.resources {
+ let cfgs = &res.cfgs;
+ let attrs = &res.attrs;
+ let ty = &res.ty;
- fields.push(quote!(
- /// Core (Cortex-M) peripherals
- pub core: rtfm::Peripherals<'a>,
- /// Device specific peripherals
- pub device: #device::Peripherals,
+ if let Some(expr) = res.expr.as_ref() {
+ const_app.push(quote!(
+ #(#attrs)*
+ #(#cfgs)*
+ static mut #name: #ty = #expr;
+ ));
+ } else {
+ const_app.push(quote!(
+ #(#attrs)*
+ #(#cfgs)*
+ static mut #name: rtfm::export::MaybeUninit<#ty> =
+ rtfm::export::MaybeUninit::uninit();
));
- lt = Some(quote!('a));
- }
- Kind::Idle => {}
- Kind::Exception(_) | Kind::Interrupt(_) => {
- if cfg!(feature = "timer-queue") {
- fields.push(quote!(
- /// Time at which this handler started executing
- pub start: rtfm::Instant,
- ));
- }
- }
- Kind::Task(_) => {
- if cfg!(feature = "timer-queue") {
- fields.push(quote!(
- /// The time at which this task was scheduled to run
- pub scheduled: rtfm::Instant,
- ));
- }
}
- }
- if schedule {
- lt = Some(quote!('a));
-
- fields.push(quote!(
- /// Tasks that can be scheduled from this context
- pub schedule: Schedule<'a>,
- ));
-
- items.push(quote!(
- /// Tasks that can be scheduled from this context
- #[derive(Clone, Copy)]
- pub struct Schedule<'a> {
- #[doc(hidden)]
- pub #priority: &'a rtfm::export::Priority,
- }
- ));
- }
+ if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) {
+ let ptr = if res.expr.is_none() {
+ quote!(#name.as_mut_ptr())
+ } else {
+ quote!(&mut #name)
+ };
- if spawn {
- lt = Some(quote!('a));
+ mod_resources.push(quote!(
+ pub struct #name<'a> {
+ priority: &'a Priority,
+ }
- fields.push(quote!(
- /// Tasks that can be spawned from this context
- pub spawn: Spawn<'a>,
- ));
+ impl<'a> #name<'a> {
+ #[inline(always)]
+ pub unsafe fn new(priority: &'a Priority) -> Self {
+ #name { priority }
+ }
- if kind.is_idle() {
- items.push(quote!(
- /// Tasks that can be spawned from this context
- #[derive(Clone, Copy)]
- pub struct Spawn<'a> {
- #[doc(hidden)]
- pub #priority: &'a rtfm::export::Priority,
+ #[inline(always)]
+ pub unsafe fn priority(&self) -> &Priority {
+ self.priority
+ }
}
));
- } else {
- let baseline_field = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let baseline = &ctxt.baseline;
- quote!(
- // NOTE this field is visible so we use a shared reference to make it
- // immutable
- #[doc(hidden)]
- pub #baseline: &'a rtfm::Instant,
- )
- }
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
- items.push(quote!(
- /// Tasks that can be spawned from this context
- #[derive(Clone, Copy)]
- pub struct Spawn<'a> {
- #baseline_field
- #[doc(hidden)]
- pub #priority: &'a rtfm::export::Priority,
- }
+ const_app.push(impl_mutex(
+ app,
+ cfgs,
+ true,
+ name,
+ quote!(#ty),
+ *ceiling,
+ ptr,
));
}
}
- let mut root = None;
- if let Some(resources) = ctxt.resources.get(&kind) {
- lt = Some(quote!('a));
-
- root = Some(resources.decl.clone());
-
- let alias = &resources.alias;
- items.push(quote!(
- #[doc(inline)]
- pub use super::#alias as Resources;
- ));
-
- fields.push(quote!(
- /// Resources available in this context
- pub resources: Resources<'a>,
- ));
- };
+ let mod_resources = if mod_resources.is_empty() {
+ quote!()
+ } else {
+ quote!(mod resources {
+ use rtfm::export::Priority;
- let doc = match kind {
- Kind::Exception(_) => "Exception handler",
- Kind::Idle => "Idle loop",
- Kind::Init => "Initialization function",
- Kind::Interrupt(_) => "Interrupt handler",
- Kind::Task(_) => "Software task",
+ #(#mod_resources)*
+ })
};
- if let Some(late_resources) = late_resources {
- items.push(quote!(
- pub use super::#late_resources as LateResources;
- ));
- }
-
- quote!(
- #root
-
- #[doc = #doc]
- #[allow(non_snake_case)]
- pub mod #name {
- /// Variables injected into this context by the `app` attribute
- pub struct Context<#lt> {
- #(#fields)*
- }
-
- #(#items)*
- }
- )
+ (const_app, mod_resources)
}
-/// The prelude injects `resources`, `spawn`, `schedule` and `start` / `scheduled` (all values) into
-/// a function scope
-fn prelude(
- ctxt: &mut Context,
- kind: Kind,
- resources: &Idents,
- spawn: &Idents,
- schedule: &Idents,
+// For each exception we'll generate:
+//
+// - at the root of the crate:
+// - a ${name}Resources struct (maybe)
+// - a ${name}Locals struct
+//
+// - a module named after the exception, see the `module` function for more details
+//
+// - hidden in `const APP`
+// - the ${name}Resources constructor
+//
+// - the exception handler specified by the user
+fn exceptions(
app: &App,
- logical_prio: u8,
analysis: &Analysis,
-) -> proc_macro2::TokenStream {
- let mut items = vec![];
+) -> (
+ // const_app
+ Vec<proc_macro2::TokenStream>,
+ // exception_mods
+ Vec<proc_macro2::TokenStream>,
+ // exception_locals
+ Vec<proc_macro2::TokenStream>,
+ // exception_resources
+ Vec<proc_macro2::TokenStream>,
+ // user_exceptions
+ Vec<proc_macro2::TokenStream>,
+) {
+ let mut const_app = vec![];
+ let mut mods = vec![];
+ let mut locals_structs = vec![];
+ let mut resources_structs = vec![];
+ let mut user_code = vec![];
+
+ for (name, exception) in &app.exceptions {
+ let (let_instant, instant) = if cfg!(feature = "timer-queue") {
+ (
+ Some(quote!(let instant = rtfm::Instant::now();)),
+ Some(quote!(, instant)),
+ )
+ } else {
+ (None, None)
+ };
+ let priority = &exception.args.priority;
+ let symbol = exception.args.binds(name);
+ const_app.push(quote!(
+ #[allow(non_snake_case)]
+ #[no_mangle]
+ unsafe fn #symbol() {
+ const PRIORITY: u8 = #priority;
- let lt = if kind.runs_once() {
- quote!('static)
- } else {
- quote!('a)
- };
+ #let_instant
- let module = kind.ident();
-
- let priority = &ctxt.priority;
- if !resources.is_empty() {
- let mut defs = vec![];
- let mut exprs = vec![];
-
- // NOTE This field is just to avoid unused type parameter errors around `'a`
- defs.push(quote!(#[allow(dead_code)] pub #priority: &'a rtfm::export::Priority));
- exprs.push(parse_quote!(#priority));
-
- let mut may_call_lock = false;
- let mut needs_unsafe = false;
- for name in resources {
- let res = &app.resources[name];
- let cfgs = &res.cfgs;
-
- let initialized = res.expr.is_some();
- let singleton = res.singleton;
- let mut_ = res.mutability;
- let ty = &res.ty;
-
- if kind.is_init() {
- let mut force_mut = false;
- if !analysis.ownerships.contains_key(name) {
- // owned by Init
- if singleton {
- needs_unsafe = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: #name
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: <#name as owned_singleton::Singleton>::new()
- ));
- continue;
- } else {
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &'static #mut_ #ty
- ));
- }
- } else {
- // owned by someone else
- if singleton {
- needs_unsafe = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &'a mut #name
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: &mut <#name as owned_singleton::Singleton>::new()
- ));
- continue;
- } else {
- force_mut = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &'a mut #ty
- ));
- }
- }
+ rtfm::export::run(PRIORITY, || {
+ crate::#name(
+ #name::Locals::new(),
+ #name::Context::new(&rtfm::export::Priority::new(PRIORITY) #instant)
+ )
+ });
+ }
+ ));
- let alias = &ctxt.statics[name];
- // Resources assigned to init are always const initialized
- needs_unsafe = true;
- if force_mut {
- exprs.push(quote!(
- #(#cfgs)*
- #name: &mut #alias
- ));
- } else {
- exprs.push(quote!(
- #(#cfgs)*
- #name: &#mut_ #alias
- ));
- }
- } else {
- let ownership = &analysis.ownerships[name];
- let mut exclusive = false;
-
- if ownership.needs_lock(logical_prio) {
- may_call_lock = true;
- if singleton {
- if mut_.is_none() {
- needs_unsafe = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &'a #name
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: &<#name as owned_singleton::Singleton>::new()
- ));
- continue;
- } else {
- // Generate a resource proxy
- defs.push(quote!(
- #(#cfgs)*
- pub #name: resources::#name<'a>
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: resources::#name { #priority }
- ));
- continue;
- }
- } else {
- if mut_.is_none() {
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &'a #ty
- ));
- } else {
- // Generate a resource proxy
- defs.push(quote!(
- #(#cfgs)*
- pub #name: resources::#name<'a>
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: resources::#name { #priority }
- ));
- continue;
- }
- }
- } else {
- if singleton {
- if kind.runs_once() {
- needs_unsafe = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: #name
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: <#name as owned_singleton::Singleton>::new()
- ));
- } else {
- needs_unsafe = true;
- if ownership.is_owned() || mut_.is_none() {
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &'a #mut_ #name
- ));
- // XXX is randomness required?
- let alias = ctxt.ident_gen.mk_ident(None, true);
- items.push(quote!(
- #(#cfgs)*
- let #mut_ #alias = unsafe {
- <#name as owned_singleton::Singleton>::new()
- };
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: &#mut_ #alias
- ));
- } else {
- may_call_lock = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: rtfm::Exclusive<'a, #name>
- ));
- // XXX is randomness required?
- let alias = ctxt.ident_gen.mk_ident(None, true);
- items.push(quote!(
- #(#cfgs)*
- let #mut_ #alias = unsafe {
- <#name as owned_singleton::Singleton>::new()
- };
- ));
- exprs.push(quote!(
- #(#cfgs)*
- #name: rtfm::Exclusive(&mut #alias)
- ));
- }
- }
- continue;
- } else {
- if ownership.is_owned() || mut_.is_none() {
- defs.push(quote!(
- #(#cfgs)*
- pub #name: &#lt #mut_ #ty
- ));
- } else {
- exclusive = true;
- may_call_lock = true;
- defs.push(quote!(
- #(#cfgs)*
- pub #name: rtfm::Exclusive<#lt, #ty>
- ));
- }
- }
- }
+ let mut needs_lt = false;
+ if !exception.args.resources.is_empty() {
+ let (item, constructor) = resources_struct(
+ Kind::Exception(name.clone()),
+ exception.args.priority,
+ &mut needs_lt,
+ app,
+ analysis,
+ );
- let alias = &ctxt.statics[name];
- needs_unsafe = true;
- if initialized {
- if exclusive {
- exprs.push(quote!(
- #(#cfgs)*
- #name: rtfm::Exclusive(&mut #alias)
- ));
- } else {
- exprs.push(quote!(
- #(#cfgs)*
- #name: &#mut_ #alias
- ));
- }
- } else {
- let expr = if mut_.is_some() {
- quote!(&mut *#alias.as_mut_ptr())
- } else {
- quote!(&*#alias.as_ptr())
- };
+ resources_structs.push(item);
- if exclusive {
- exprs.push(quote!(
- #(#cfgs)*
- #name: rtfm::Exclusive(#expr)
- ));
- } else {
- exprs.push(quote!(
- #(#cfgs)*
- #name: #expr
- ));
- }
- }
- }
+ const_app.push(constructor);
}
- let alias = ctxt.ident_gen.mk_ident(None, false);
- let unsafety = if needs_unsafe {
- Some(quote!(unsafe))
+ mods.push(module(
+ Kind::Exception(name.clone()),
+ (!exception.args.resources.is_empty(), needs_lt),
+ !exception.args.schedule.is_empty(),
+ !exception.args.spawn.is_empty(),
+ false,
+ app,
+ ));
+
+ let attrs = &exception.attrs;
+ let context = &exception.context;
+ let (locals, lets) = locals(Kind::Exception(name.clone()), &exception.statics);
+ locals_structs.push(locals);
+ let use_u32ext = if cfg!(feature = "timer-queue") {
+ Some(quote!(
+ use rtfm::U32Ext as _;
+ ))
} else {
None
};
-
- let defs = &defs;
- let doc = format!("`{}::Resources`", kind.ident().to_string());
- let decl = quote!(
- #[doc = #doc]
+ let stmts = &exception.stmts;
+ user_code.push(quote!(
+ #(#attrs)*
#[allow(non_snake_case)]
- pub struct #alias<'a> { #(#defs,)* }
- );
- items.push(quote!(
- #[allow(unused_variables)]
- #[allow(unsafe_code)]
- #[allow(unused_mut)]
- let mut resources = #unsafety { #alias { #(#exprs,)* } };
- ));
+ fn #name(__locals: #name::Locals, #context: #name::Context) {
+ #use_u32ext
+ use rtfm::Mutex as _;
- ctxt.resources
- .insert(kind.clone(), Resources { alias, decl });
+ #(#lets;)*
- if may_call_lock {
- items.push(quote!(
- use rtfm::Mutex;
- ));
- }
- }
-
- if !spawn.is_empty() {
- if kind.is_idle() {
- items.push(quote!(
- #[allow(unused_variables)]
- let spawn = #module::Spawn { #priority };
- ));
- } else {
- let baseline_expr = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let baseline = &ctxt.baseline;
- quote!(#baseline)
- }
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
- items.push(quote!(
- #[allow(unused_variables)]
- let spawn = #module::Spawn { #priority, #baseline_expr };
- ));
- }
- }
-
- if !schedule.is_empty() {
- // Populate `schedule_fn`
- for task in schedule {
- if ctxt.schedule_fn.contains_key(task) {
- continue;
+ #(#stmts)*
}
-
- ctxt.schedule_fn
- .insert(task.clone(), ctxt.ident_gen.mk_ident(None, false));
- }
-
- items.push(quote!(
- #[allow(unused_imports)]
- use rtfm::U32Ext;
-
- #[allow(unused_variables)]
- let schedule = #module::Schedule { #priority };
));
}
- if items.is_empty() {
- quote!()
- } else {
- quote!(
- let ref #priority = unsafe { rtfm::export::Priority::new(#logical_prio) };
-
- #(#items)*
- )
- }
+ (
+ const_app,
+ mods,
+ locals_structs,
+ resources_structs,
+ user_code,
+ )
}
-fn idle(
- ctxt: &mut Context,
+// For each interrupt we'll generate:
+//
+// - at the root of the crate:
+// - a ${name}Resources struct (maybe)
+// - a ${name}Locals struct
+//
+// - a module named after the exception, see the `module` function for more details
+//
+// - hidden in `const APP`
+// - the ${name}Resources constructor
+//
+// - the interrupt handler specified by the user
+fn interrupts(
app: &App,
analysis: &Analysis,
-) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
- if let Some(idle) = app.idle.as_ref() {
- let attrs = &idle.attrs;
- let locals = mk_locals(&idle.statics, true);
- let stmts = &idle.stmts;
+) -> (
+ // const_app
+ Vec<proc_macro2::TokenStream>,
+ // interrupt_mods
+ Vec<proc_macro2::TokenStream>,
+ // interrupt_locals
+ Vec<proc_macro2::TokenStream>,
+ // interrupt_resources
+ Vec<proc_macro2::TokenStream>,
+ // user_exceptions
+ Vec<proc_macro2::TokenStream>,
+) {
+ let mut const_app = vec![];
+ let mut mods = vec![];
+ let mut locals_structs = vec![];
+ let mut resources_structs = vec![];
+ let mut user_code = vec![];
- let prelude = prelude(
- ctxt,
- Kind::Idle,
- &idle.args.resources,
- &idle.args.spawn,
- &idle.args.schedule,
- app,
- 0,
- analysis,
- );
-
- let module = module(
- ctxt,
- Kind::Idle,
- !idle.args.schedule.is_empty(),
- !idle.args.spawn.is_empty(),
- app,
- None,
- );
-
- let unsafety = &idle.unsafety;
- let idle = &ctxt.idle;
-
- (
- quote!(
- #module
-
- // unsafe trampoline to deter end-users from calling this non-reentrant function
- #(#attrs)*
- unsafe fn #idle() -> ! {
- #[inline(always)]
- #unsafety fn idle() -> ! {
- #(#locals)*
+ let device = &app.args.device;
+ for (name, interrupt) in &app.interrupts {
+ let (let_instant, instant) = if cfg!(feature = "timer-queue") {
+ (
+ Some(quote!(let instant = rtfm::Instant::now();)),
+ Some(quote!(, instant)),
+ )
+ } else {
+ (None, None)
+ };
+ let priority = &interrupt.args.priority;
+ let symbol = interrupt.args.binds(name);
+ const_app.push(quote!(
+ #[allow(non_snake_case)]
+ #[no_mangle]
+ unsafe fn #symbol() {
+ const PRIORITY: u8 = #priority;
- #prelude
+ #let_instant
- #(#stmts)*
- }
+ // check that this interrupt exists
+ let _ = #device::Interrupt::#symbol;
- idle()
- }
- ),
- quote!(#idle()),
- )
- } else {
- (
- quote!(),
- quote!(loop {
- rtfm::export::wfi();
- }),
- )
- }
-}
+ rtfm::export::run(PRIORITY, || {
+ crate::#name(
+ #name::Locals::new(),
+ #name::Context::new(&rtfm::export::Priority::new(PRIORITY) #instant)
+ )
+ });
+ }
+ ));
-fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
- app.exceptions
- .iter()
- .map(|(ident, exception)| {
- let attrs = &exception.attrs;
- let stmts = &exception.stmts;
-
- let kind = Kind::Exception(ident.clone());
- let prelude = prelude(
- ctxt,
- kind.clone(),
- &exception.args.resources,
- &exception.args.spawn,
- &exception.args.schedule,
+ let mut needs_lt = false;
+ if !interrupt.args.resources.is_empty() {
+ let (item, constructor) = resources_struct(
+ Kind::Interrupt(name.clone()),
+ interrupt.args.priority,
+ &mut needs_lt,
app,
- exception.args.priority,
analysis,
);
- let module = module(
- ctxt,
- kind,
- !exception.args.schedule.is_empty(),
- !exception.args.spawn.is_empty(),
- app,
- None,
- );
-
- #[cfg(feature = "timer-queue")]
- let baseline = &ctxt.baseline;
- let baseline_let = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(let ref #baseline = rtfm::Instant::now();),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
-
- let start_let = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(
- #[allow(unused_variables)]
- let start = *#baseline;
- ),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
-
- let locals = mk_locals(&exception.statics, false);
- let symbol = exception.args.binds(ident).to_string();
- let alias = ctxt.ident_gen.mk_ident(None, false);
- let unsafety = &exception.unsafety;
- quote!(
- #module
-
- // unsafe trampoline to deter end-users from calling this non-reentrant function
- #[export_name = #symbol]
- #(#attrs)*
- unsafe fn #alias() {
- #[inline(always)]
- #unsafety fn exception() {
- #(#locals)*
-
- #baseline_let
+ resources_structs.push(item);
- #prelude
-
- #start_let
-
- rtfm::export::run(move || {
- #(#stmts)*
- })
- }
-
- exception()
- }
- )
- })
- .collect()
-}
-
-fn interrupts(
- ctxt: &mut Context,
- app: &App,
- analysis: &Analysis,
-) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
- let mut root = vec![];
- let mut scoped = vec![];
-
- for (ident, interrupt) in &app.interrupts {
- let attrs = &interrupt.attrs;
- let stmts = &interrupt.stmts;
-
- let kind = Kind::Interrupt(ident.clone());
- let prelude = prelude(
- ctxt,
- kind.clone(),
- &interrupt.args.resources,
- &interrupt.args.spawn,
- &interrupt.args.schedule,
- app,
- interrupt.args.priority,
- analysis,
- );
+ const_app.push(constructor);
+ }
- root.push(module(
- ctxt,
- kind,
+ mods.push(module(
+ Kind::Interrupt(name.clone()),
+ (!interrupt.args.resources.is_empty(), needs_lt),
!interrupt.args.schedule.is_empty(),
!interrupt.args.spawn.is_empty(),
+ false,
app,
- None,
));
- #[cfg(feature = "timer-queue")]
- let baseline = &ctxt.baseline;
- let baseline_let = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(let ref #baseline = rtfm::Instant::now();),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
-
- let start_let = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(
- #[allow(unused_variables)]
- let start = *#baseline;
- ),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
+ let attrs = &interrupt.attrs;
+ let context = &interrupt.context;
+ let use_u32ext = if cfg!(feature = "timer-queue") {
+ Some(quote!(
+ use rtfm::U32Ext as _;
+ ))
+ } else {
+ None
};
-
- let locals = mk_locals(&interrupt.statics, false);
- let alias = ctxt.ident_gen.mk_ident(None, false);
- let symbol = interrupt.args.binds(ident).to_string();
- let unsafety = &interrupt.unsafety;
- scoped.push(quote!(
- // unsafe trampoline to deter end-users from calling this non-reentrant function
+ let (locals, lets) = locals(Kind::Interrupt(name.clone()), &interrupt.statics);
+ locals_structs.push(locals);
+ let stmts = &interrupt.stmts;
+ user_code.push(quote!(
#(#attrs)*
- #[export_name = #symbol]
- unsafe fn #alias() {
- #[inline(always)]
- #unsafety fn interrupt() {
- #(#locals)*
-
- #baseline_let
-
- #prelude
-
- #start_let
+ #[allow(non_snake_case)]
+ fn #name(__locals: #name::Locals, #context: #name::Context) {
+ #use_u32ext
+ use rtfm::Mutex as _;
- rtfm::export::run(move || {
- #(#stmts)*
- })
- }
+ #(#lets;)*
- interrupt()
+ #(#stmts)*
}
));
}
- (quote!(#(#root)*), quote!(#(#scoped)*))
+ (
+ const_app,
+ mods,
+ locals_structs,
+ resources_structs,
+ user_code,
+ )
}
-fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
- let mut items = vec![];
+// For each task we'll generate:
+//
+// - at the root of the crate:
+// - a ${name}Resources struct (maybe)
+// - a ${name}Locals struct
+//
+// - a module named after the task, see the `module` function for more details
+//
+// - hidden in `const APP`
+// - the ${name}Resources constructor
+// - an INPUTS buffer
+// - a free queue and a corresponding resource
+// - an INSTANTS buffer (if `timer-queue` is enabled)
+//
+// - the task handler specified by the user
+fn tasks(
+ app: &App,
+ analysis: &Analysis,
+) -> (
+ // const_app
+ Vec<proc_macro2::TokenStream>,
+ // task_mods
+ Vec<proc_macro2::TokenStream>,
+ // task_locals
+ Vec<proc_macro2::TokenStream>,
+ // task_resources
+ Vec<proc_macro2::TokenStream>,
+ // user_tasks
+ Vec<proc_macro2::TokenStream>,
+) {
+ let mut const_app = vec![];
+ let mut mods = vec![];
+ let mut locals_structs = vec![];
+ let mut resources_structs = vec![];
+ let mut user_code = vec![];
- // first pass to generate buffers (statics and resources) and spawn aliases
for (name, task) in &app.tasks {
- #[cfg(feature = "timer-queue")]
- let scheduleds_alias = ctxt.ident_gen.mk_ident(None, false);
- let free_alias = ctxt.ident_gen.mk_ident(None, false);
- let inputs_alias = ctxt.ident_gen.mk_ident(None, false);
- let task_alias = ctxt.ident_gen.mk_ident(Some(&name.to_string()), false);
-
let inputs = &task.inputs;
+ let (_, _, _, ty) = regroup_inputs(inputs);
- let ty = tuple_ty(inputs);
-
- let capacity = analysis.capacities[name];
- let capacity_lit = mk_capacity_literal(capacity);
- let capacity_ty = mk_typenum_capacity(capacity, true);
-
- let resource = mk_resource(
- ctxt,
- &[],
- &free_alias,
- quote!(rtfm::export::FreeQueue<#capacity_ty>),
- *analysis.free_queues.get(name).unwrap_or(&0),
- if cfg!(feature = "nightly") {
- quote!(&mut #free_alias)
- } else {
- quote!(#free_alias.get_mut())
- },
- app,
- None,
- );
+ let cap = analysis.capacities[name];
+ let cap_lit = mk_capacity_literal(cap);
+ let cap_ty = mk_typenum_capacity(cap, true);
- let scheduleds_static = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let scheduleds_symbol = format!("{}::SCHEDULED_TIMES::{}", name, scheduleds_alias);
+ let task_inputs = mk_inputs_ident(name);
+ let task_instants = mk_instants_ident(name);
+ let task_fq = mk_fq_ident(name);
- if cfg!(feature = "nightly") {
- let inits =
- (0..capacity).map(|_| quote!(rtfm::export::MaybeUninit::uninit()));
+ let elems = (0..cap)
+ .map(|_| quote!(rtfm::export::MaybeUninit::uninit()))
+ .collect::<Vec<_>>();
- quote!(
- #[doc = #scheduleds_symbol]
- static mut #scheduleds_alias:
- [rtfm::export::MaybeUninit<rtfm::Instant>; #capacity_lit] =
- [#(#inits),*];
- )
- } else {
- quote!(
- #[doc = #scheduleds_symbol]
- static mut #scheduleds_alias:
- rtfm::export::MaybeUninit<[rtfm::Instant; #capacity_lit]> =
- rtfm::export::MaybeUninit::uninit();
- )
- }
- }
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
+ if cfg!(feature = "timer-queue") {
+ let elems = elems.clone();
+ const_app.push(quote!(
+ /// Buffer that holds the instants associated to the inputs of a task
+ static mut #task_instants: [rtfm::export::MaybeUninit<rtfm::Instant>; #cap_lit] =
+ [#(#elems,)*];
+ ));
+ }
- let inputs_symbol = format!("{}::INPUTS::{}", name, inputs_alias);
- let free_symbol = format!("{}::FREE_QUEUE::{}", name, free_alias);
- if cfg!(feature = "nightly") {
- let inits = (0..capacity).map(|_| quote!(rtfm::export::MaybeUninit::uninit()));
+ const_app.push(quote!(
+ /// Buffer that holds the inputs of a task
+ static mut #task_inputs: [rtfm::export::MaybeUninit<#ty>; #cap_lit] =
+ [#(#elems,)*];
+ ));
- items.push(quote!(
- #[doc = #free_symbol]
- static mut #free_alias: rtfm::export::FreeQueue<#capacity_ty> = unsafe {
- rtfm::export::FreeQueue::new_sc()
+ let doc = "Queue version of a free-list that keeps track of empty slots in the previous buffer(s)";
+ let fq_ty = quote!(rtfm::export::FreeQueue<#cap_ty>);
+ let ptr = if cfg!(feature = "nightly") {
+ const_app.push(quote!(
+ #[doc = #doc]
+ static mut #task_fq: #fq_ty = unsafe {
+ rtfm::export::FreeQueue::u8_sc()
};
-
- #[doc = #inputs_symbol]
- static mut #inputs_alias: [rtfm::export::MaybeUninit<#ty>; #capacity_lit] =
- [#(#inits),*];
));
- } else {
- items.push(quote!(
- #[doc = #free_symbol]
- static mut #free_alias: rtfm::export::MaybeUninit<
- rtfm::export::FreeQueue<#capacity_ty>
- > = rtfm::export::MaybeUninit::uninit();
- #[doc = #inputs_symbol]
- static mut #inputs_alias: rtfm::export::MaybeUninit<[#ty; #capacity_lit]> =
+ quote!(&mut #task_fq)
+ } else {
+ const_app.push(quote!(
+ #[doc = #doc]
+ static mut #task_fq: rtfm::export::MaybeUninit<#fq_ty> =
rtfm::export::MaybeUninit::uninit();
-
));
- }
- items.push(quote!(
- #resource
+ quote!(#task_fq.as_mut_ptr())
+ };
- #scheduleds_static
- ));
+ if let Some(ceiling) = analysis.free_queues.get(name) {
+ const_app.push(quote!(struct #task_fq<'a> {
+ priority: &'a rtfm::export::Priority,
+ }));
- ctxt.tasks.insert(
- name.clone(),
- Task {
- alias: task_alias,
- free_queue: free_alias,
- inputs: inputs_alias,
- spawn_fn: ctxt.ident_gen.mk_ident(None, false),
-
- #[cfg(feature = "timer-queue")]
- scheduleds: scheduleds_alias,
- },
- );
- }
+ const_app.push(impl_mutex(app, &[], false, &task_fq, fq_ty, *ceiling, ptr));
+ }
- // second pass to generate the actual task function
- for (name, task) in &app.tasks {
- let inputs = &task.inputs;
- let locals = mk_locals(&task.statics, false);
- let stmts = &task.stmts;
- let unsafety = &task.unsafety;
+ let mut needs_lt = false;
+ if !task.args.resources.is_empty() {
+ let (item, constructor) = resources_struct(
+ Kind::Task(name.clone()),
+ task.args.priority,
+ &mut needs_lt,
+ app,
+ analysis,
+ );
- let scheduled_let = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let baseline = &ctxt.baseline;
- quote!(let scheduled = *#baseline;)
- }
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
+ resources_structs.push(item);
- let prelude = prelude(
- ctxt,
- Kind::Task(name.clone()),
- &task.args.resources,
- &task.args.spawn,
- &task.args.schedule,
- app,
- task.args.priority,
- analysis,
- );
+ const_app.push(constructor);
+ }
- items.push(module(
- ctxt,
+ mods.push(module(
Kind::Task(name.clone()),
+ (!task.args.resources.is_empty(), needs_lt),
!task.args.schedule.is_empty(),
!task.args.spawn.is_empty(),
+ false,
app,
- None,
));
let attrs = &task.attrs;
- let cfgs = &task.cfgs;
- let task_alias = &ctxt.tasks[name].alias;
- let (baseline, baseline_arg) = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let baseline = &ctxt.baseline;
- (quote!(#baseline,), quote!(#baseline: &rtfm::Instant,))
- }
- #[cfg(not(feature = "timer-queue"))]
- () => (quote!(), quote!()),
+ let use_u32ext = if cfg!(feature = "timer-queue") {
+ Some(quote!(
+ use rtfm::U32Ext as _;
+ ))
+ } else {
+ None
};
- let pats = tuple_pat(inputs);
- items.push(quote!(
- // unsafe trampoline to deter end-users from calling this non-reentrant function
+ let context = &task.context;
+ let stmts = &task.stmts;
+ let (locals_struct, lets) = locals(Kind::Task(name.clone()), &task.statics);
+ locals_structs.push(locals_struct);
+ user_code.push(quote!(
#(#attrs)*
- #(#cfgs)*
- unsafe fn #task_alias(#baseline_arg #(#inputs,)*) {
- #[inline(always)]
- #unsafety fn task(#baseline_arg #(#inputs,)*) {
- #(#locals)*
-
- #prelude
+ #[allow(non_snake_case)]
+ fn #name(__locals: #name::Locals, #context: #name::Context #(,#inputs)*) {
+ use rtfm::Mutex as _;
+ #use_u32ext
- #scheduled_let
+ #(#lets;)*
- #(#stmts)*
- }
-
- task(#baseline #pats)
+ #(#stmts)*
}
));
}
- quote!(#(#items)*)
+ (
+ const_app,
+ mods,
+ locals_structs,
+ resources_structs,
+ user_code,
+ )
}
-fn dispatchers(
- ctxt: &mut Context,
- app: &App,
- analysis: &Analysis,
-) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
- let mut data = vec![];
- let mut dispatchers = vec![];
+/// For each task dispatcher we'll generate
+///
+/// - A static variable that hold the ready queue (`RQ${priority}`) and a resource proxy for it
+/// - An enumeration of all the tasks dispatched by this dispatcher `T${priority}`
+/// - An interrupt handler that dispatches the tasks
+fn dispatchers(app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
+ let mut items = vec![];
let device = &app.args.device;
for (level, dispatcher) in &analysis.dispatchers {
- let ready_alias = ctxt.ident_gen.mk_ident(None, false);
- let enum_alias = ctxt.ident_gen.mk_ident(None, false);
- let capacity = mk_typenum_capacity(dispatcher.capacity, true);
+ let rq = mk_rq_ident(*level);
+ let t = mk_t_ident(*level);
+ let cap = mk_typenum_capacity(dispatcher.capacity, true);
+
+ let doc = format!(
+ "Queue of tasks ready to be dispatched at priority level {}",
+ level
+ );
+ let rq_ty = quote!(rtfm::export::ReadyQueue<#t, #cap>);
+ let ptr = if cfg!(feature = "nightly") {
+ items.push(quote!(
+ #[doc = #doc]
+ static mut #rq: #rq_ty = unsafe {
+ rtfm::export::ReadyQueue::u8_sc()
+ };
+ ));
+
+ quote!(&mut #rq)
+ } else {
+ items.push(quote!(
+ #[doc = #doc]
+ static mut #rq: rtfm::export::MaybeUninit<#rq_ty> =
+ rtfm::export::MaybeUninit::uninit();
+ ));
+
+ quote!(#rq.as_mut_ptr())
+ };
+
+ if let Some(ceiling) = analysis.ready_queues.get(&level) {
+ items.push(quote!(
+ struct #rq<'a> {
+ priority: &'a rtfm::export::Priority,
+ }
+ ));
+
+ items.push(impl_mutex(app, &[], false, &rq, rq_ty, *ceiling, ptr));
+ }
let variants = dispatcher
.tasks
.iter()
.map(|task| {
- let task_ = &app.tasks[task];
- let cfgs = &task_.cfgs;
+ let cfgs = &app.tasks[task].cfgs;
quote!(
#(#cfgs)*
@@ -1456,126 +743,100 @@ fn dispatchers(
)
})
.collect::<Vec<_>>();
- let symbol = format!("P{}::READY_QUEUE::{}", level, ready_alias);
- let e = quote!(rtfm::export);
- let ty = quote!(#e::ReadyQueue<#enum_alias, #capacity>);
- let ceiling = *analysis.ready_queues.get(&level).unwrap_or(&0);
- let resource = mk_resource(
- ctxt,
- &[],
- &ready_alias,
- ty.clone(),
- ceiling,
- if cfg!(feature = "nightly") {
- quote!(&mut #ready_alias)
- } else {
- quote!(#ready_alias.get_mut())
- },
- app,
- None,
- );
- if cfg!(feature = "nightly") {
- data.push(quote!(
- #[doc = #symbol]
- static mut #ready_alias: #ty = unsafe { #e::ReadyQueue::new_sc() };
- ));
- } else {
- data.push(quote!(
- #[doc = #symbol]
- static mut #ready_alias: #e::MaybeUninit<#ty> = #e::MaybeUninit::uninit();
- ));
- }
- data.push(quote!(
- #[allow(dead_code)]
+ let doc = format!(
+ "Software tasks to be dispatched at priority level {}",
+ level
+ );
+ items.push(quote!(
#[allow(non_camel_case_types)]
- enum #enum_alias { #(#variants,)* }
-
- #resource
+ #[derive(Clone, Copy)]
+ #[doc = #doc]
+ enum #t {
+ #(#variants,)*
+ }
));
let arms = dispatcher
.tasks
.iter()
- .map(|task| {
- let task_ = &ctxt.tasks[task];
- let inputs = &task_.inputs;
- let free = &task_.free_queue;
- let alias = &task_.alias;
-
- let task__ = &app.tasks[task];
- let pats = tuple_pat(&task__.inputs);
- let cfgs = &task__.cfgs;
-
- let baseline_let;
- let call;
- match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let scheduleds = &task_.scheduleds;
- let scheduled = if cfg!(feature = "nightly") {
- quote!(#scheduleds.get_unchecked(usize::from(index)).as_ptr())
- } else {
- quote!(#scheduleds.get_ref().get_unchecked(usize::from(index)))
- };
+ .map(|name| {
+ let task = &app.tasks[name];
+ let cfgs = &task.cfgs;
+ let (_, tupled, pats, _) = regroup_inputs(&task.inputs);
- baseline_let = quote!(
- let baseline = ptr::read(#scheduled);
- );
- call = quote!(#alias(&baseline, #pats));
- }
- #[cfg(not(feature = "timer-queue"))]
- () => {
- baseline_let = quote!();
- call = quote!(#alias(#pats));
- }
+ let inputs = mk_inputs_ident(name);
+ let fq = mk_fq_ident(name);
+
+ let input = quote!(#inputs.get_unchecked(usize::from(index)).read());
+ let fq = if cfg!(feature = "nightly") {
+ quote!(#fq)
+ } else {
+ quote!((*#fq.as_mut_ptr()))
};
- let (free_, input) = if cfg!(feature = "nightly") {
+ let (let_instant, _instant) = if cfg!(feature = "timer-queue") {
+ let instants = mk_instants_ident(name);
+ let instant = quote!(#instants.get_unchecked(usize::from(index)).read());
+
(
- quote!(#free),
- quote!(#inputs.get_unchecked(usize::from(index)).as_ptr()),
+ Some(quote!(let instant = #instant;)),
+ Some(quote!(, instant)),
)
} else {
- (
- quote!(#free.get_mut()),
- quote!(#inputs.get_ref().get_unchecked(usize::from(index))),
+ (None, None)
+ };
+
+ let call = {
+ let pats = pats.clone();
+
+ quote!(
+ #name(
+ #name::Locals::new(),
+ #name::Context::new(priority #_instant)
+ #(,#pats)*
+ )
)
};
quote!(
#(#cfgs)*
- #enum_alias::#task => {
- #baseline_let
- let input = ptr::read(#input);
- #free_.split().0.enqueue_unchecked(index);
- let (#pats) = input;
+ #t::#name => {
+ let #tupled = #input;
+ #let_instant
+ #fq.split().0.enqueue_unchecked(index);
+ let priority = &rtfm::export::Priority::new(PRIORITY);
#call
}
)
})
.collect::<Vec<_>>();
+ let doc = format!(
+ "interrupt handler used to dispatch tasks at priority {}",
+ level
+ );
let attrs = &dispatcher.attrs;
let interrupt = &dispatcher.interrupt;
- let symbol = interrupt.to_string();
- let alias = ctxt.ident_gen.mk_ident(None, false);
- let ready_alias_ = if cfg!(feature = "nightly") {
- quote!(#ready_alias)
+ let rq = if cfg!(feature = "nightly") {
+ quote!((&mut #rq))
} else {
- quote!(#ready_alias.get_mut())
+ quote!((*#rq.as_mut_ptr()))
};
- dispatchers.push(quote!(
+ items.push(quote!(
+ #[doc = #doc]
#(#attrs)*
- #[export_name = #symbol]
- unsafe fn #alias() {
- use core::ptr;
+ #[no_mangle]
+ #[allow(non_snake_case)]
+ unsafe fn #interrupt() {
+ /// The priority of this interrupt handler
+ const PRIORITY: u8 = #level;
// check that this interrupt exists
- let _ = #device::interrupt::#interrupt;
+ let _ = #device::Interrupt::#interrupt;
- rtfm::export::run(|| {
- while let Some((task, index)) = #ready_alias_.split().1.dequeue() {
+ rtfm::export::run(PRIORITY, || {
+ while let Some((task, index)) = #rq.split().1.dequeue() {
match task {
#(#arms)*
}
@@ -1583,269 +844,127 @@ fn dispatchers(
});
}
));
-
- ctxt.dispatchers.insert(
- *level,
- Dispatcher {
- ready_queue: ready_alias,
- enum_: enum_alias,
- },
- );
}
- (quote!(#(#data)*), quote!(#(#dispatchers)*))
+ items
}
-fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
+/// Generates all the `Spawn.$task` related code
+fn spawn(app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
let mut items = vec![];
- // Generate `spawn` functions
- let device = &app.args.device;
- let priority = &ctxt.priority;
- #[cfg(feature = "timer-queue")]
- let baseline = &ctxt.baseline;
- for (name, task) in &ctxt.tasks {
- let alias = &task.spawn_fn;
- let task_ = &app.tasks[name];
- let cfgs = &task_.cfgs;
- let free = &task.free_queue;
- let level = task_.args.priority;
- let dispatcher = &ctxt.dispatchers[&level];
- let ready = &dispatcher.ready_queue;
- let enum_ = &dispatcher.enum_;
- let dispatcher = &analysis.dispatchers[&level].interrupt;
- let inputs = &task.inputs;
- let args = &task_.inputs;
- let ty = tuple_ty(args);
- let pats = tuple_pat(args);
-
- let scheduleds_write = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- let scheduleds = &ctxt.tasks[name].scheduleds;
- if cfg!(feature = "nightly") {
- quote!(
- ptr::write(
- #scheduleds.get_unchecked_mut(usize::from(index)).as_mut_ptr(),
- #baseline,
- );
- )
- } else {
- quote!(
- ptr::write(
- #scheduleds.get_mut().get_unchecked_mut(usize::from(index)),
- #baseline,
- );
- )
- }
- }
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
-
- let baseline_arg = match () {
- #[cfg(feature = "timer-queue")]
- () => quote!(#baseline: rtfm::Instant,),
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
-
- let input = if cfg!(feature = "nightly") {
- quote!(#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr())
- } else {
- quote!(#inputs.get_mut().get_unchecked_mut(usize::from(index)))
- };
- items.push(quote!(
- #[inline(always)]
- #(#cfgs)*
- unsafe fn #alias(
- #baseline_arg
- #priority: &rtfm::export::Priority,
- #(#args,)*
- ) -> Result<(), #ty> {
- use core::ptr;
-
- use rtfm::Mutex;
-
- if let Some(index) = (#free { #priority }).lock(|f| f.split().1.dequeue()) {
- ptr::write(#input, (#pats));
- #scheduleds_write
-
- #ready { #priority }.lock(|rq| {
- rq.split().0.enqueue_unchecked((#enum_::#name, index))
- });
-
- rtfm::pend(#device::Interrupt::#dispatcher);
-
- Ok(())
- } else {
- Err((#pats))
- }
- }
- ))
- }
-
- // Generate `spawn` structs; these call the `spawn` functions generated above
- for (name, spawn) in app.spawn_callers() {
- if spawn.is_empty() {
+ let mut seen = BTreeSet::new();
+ for (spawner, spawnees) in app.spawn_callers() {
+ if spawnees.is_empty() {
continue;
}
- #[cfg(feature = "timer-queue")]
- let is_idle = name.to_string() == "idle";
-
let mut methods = vec![];
- for task in spawn {
- let task_ = &app.tasks[task];
- let alias = &ctxt.tasks[task].spawn_fn;
- let inputs = &task_.inputs;
- let cfgs = &task_.cfgs;
- let ty = tuple_ty(inputs);
- let pats = tuple_pat(inputs);
-
- let instant = match () {
- #[cfg(feature = "timer-queue")]
- () => {
- if is_idle {
- quote!(rtfm::Instant::now(),)
- } else {
- quote!(*self.#baseline,)
- }
- }
- #[cfg(not(feature = "timer-queue"))]
- () => quote!(),
- };
- methods.push(quote!(
- #[allow(unsafe_code)]
- #[inline]
- #(#cfgs)*
- pub fn #task(&self, #(#inputs,)*) -> Result<(), #ty> {
- unsafe { #alias(#instant &self.#priority, #pats) }
- }
- ));
- }
- items.push(quote!(
- impl<'a> #name::Spawn<'a> {
- #(#methods)*
- }
- ));
- }
+ let spawner_is_init = spawner == "init";
+ let spawner_is_idle = spawner == "idle";
+ for name in spawnees {
+ let spawnee = &app.tasks[name];
+ let cfgs = &spawnee.cfgs;
+ let (args, _, untupled, ty) = regroup_inputs(&spawnee.inputs);
- quote!(#(#items)*)
-}
+ if spawner_is_init {
+ // `init` uses a special spawn implementation; it doesn't use the `spawn_${name}`
+ // functions which are shared by other contexts
-#[cfg(feature = "timer-queue")]
-fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream {
- let mut items = vec![];
+ let body = mk_spawn_body(&spawner, &name, app, analysis);
- // Generate `schedule` functions
- let priority = &ctxt.priority;
- let timer_queue = &ctxt.timer_queue;
- for (task, alias) in &ctxt.schedule_fn {
- let task_ = &ctxt.tasks[task];
- let free = &task_.free_queue;
- let enum_ = &ctxt.schedule_enum;
- let inputs = &task_.inputs;
- let scheduleds = &task_.scheduleds;
- let task__ = &app.tasks[task];
- let args = &task__.inputs;
- let cfgs = &task__.cfgs;
- let ty = tuple_ty(args);
- let pats = tuple_pat(args);
-
- let input = if cfg!(feature = "nightly") {
- quote!(#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr())
- } else {
- quote!(#inputs.get_mut().get_unchecked_mut(usize::from(index)))
- };
+ let let_instant = if cfg!(feature = "timer-queue") {
+ Some(quote!(let instant = unsafe { rtfm::Instant::artificial(0) };))
+ } else {
+ None
+ };
+ methods.push(quote!(
+ #(#cfgs)*
+ fn #name(&self #(,#args)*) -> Result<(), #ty> {
+ #let_instant
+ #body
+ }
+ ));
+ } else {
+ let spawn = mk_spawn_ident(name);
- let scheduled = if cfg!(feature = "nightly") {
- quote!(#scheduleds.get_unchecked_mut(usize::from(index)).as_mut_ptr())
- } else {
- quote!(#scheduleds.get_mut().get_unchecked_mut(usize::from(index)))
- };
- items.push(quote!(
- #[inline(always)]
- #(#cfgs)*
- unsafe fn #alias(
- #priority: &rtfm::export::Priority,
- instant: rtfm::Instant,
- #(#args,)*
- ) -> Result<(), #ty> {
- use core::ptr;
-
- use rtfm::Mutex;
-
- if let Some(index) = (#free { #priority }).lock(|f| f.split().1.dequeue()) {
- ptr::write(#input, (#pats));
- ptr::write(#scheduled, instant);
-
- let nr = rtfm::export::NotReady {
- instant,
- index,
- task: #enum_::#task,
- };
+ if !seen.contains(name) {
+ // generate a `spawn_${name}` function
+ seen.insert(name);
- ({#timer_queue { #priority }}).lock(|tq| tq.enqueue_unchecked(nr));
+ let instant = if cfg!(feature = "timer-queue") {
+ Some(quote!(, instant: rtfm::Instant))
+ } else {
+ None
+ };
+ let body = mk_spawn_body(&spawner, &name, app, analysis);
+ let args = args.clone();
+ items.push(quote!(
+ #(#cfgs)*
+ unsafe fn #spawn(
+ priority: &rtfm::export::Priority
+ #instant
+ #(,#args)*
+ ) -> Result<(), #ty> {
+ #body
+ }
+ ));
+ }
- Ok(())
+ let (let_instant, instant) = if cfg!(feature = "timer-queue") {
+ (
+ Some(if spawner_is_idle {
+ quote!(let instant = rtfm::Instant::now();)
+ } else {
+ quote!(let instant = self.instant();)
+ }),
+ Some(quote!(, instant)),
+ )
} else {
- Err((#pats))
- }
+ (None, None)
+ };
+ methods.push(quote!(
+ #(#cfgs)*
+ #[inline(always)]
+ fn #name(&self #(,#args)*) -> Result<(), #ty> {
+ unsafe {
+ #let_instant
+ #spawn(self.priority() #instant #(,#untupled)*)
+ }
+ }
+ ));
}
- ))
- }
-
- // Generate `Schedule` structs; these call the `schedule` functions generated above
- for (name, schedule) in app.schedule_callers() {
- if schedule.is_empty() {
- continue;
- }
-
- debug_assert!(!schedule.is_empty());
-
- let mut methods = vec![];
- for task in schedule {
- let alias = &ctxt.schedule_fn[task];
- let task_ = &app.tasks[task];
- let inputs = &task_.inputs;
- let cfgs = &task_.cfgs;
- let ty = tuple_ty(inputs);
- let pats = tuple_pat(inputs);
-
- methods.push(quote!(
- #[inline]
- #(#cfgs)*
- pub fn #task(
- &self,
- instant: rtfm::Instant,
- #(#inputs,)*
- ) -> Result<(), #ty> {
- unsafe { #alias(&self.#priority, instant, #pats) }
- }
- ));
}
+ let lt = if spawner_is_init {
+ None
+ } else {
+ Some(quote!('a))
+ };
items.push(quote!(
- impl<'a> #name::Schedule<'a> {
+ impl<#lt> #spawner::Spawn<#lt> {
#(#methods)*
}
));
}
- quote!(#(#items)*)
+ items
}
-fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
+/// Generates code related to the timer queue, namely
+///
+/// - A static variable that holds the timer queue and a resource proxy for it
+/// - The system timer exception, which moves tasks from the timer queue into the ready queues
+fn timer_queue(app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
+ let mut items = vec![];
+
let tasks = &analysis.timer_queue.tasks;
if tasks.is_empty() {
- return quote!();
+ return items;
}
- let mut items = vec![];
-
let variants = tasks
.iter()
.map(|task| {
@@ -1856,389 +975,1485 @@ fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro
)
})
.collect::<Vec<_>>();
- let enum_ = &ctxt.schedule_enum;
+
items.push(quote!(
- #[allow(dead_code)]
+ /// `schedule`-dable tasks
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
- enum #enum_ { #(#variants,)* }
+ enum T {
+ #(#variants,)*
+ }
));
let cap = mk_typenum_capacity(analysis.timer_queue.capacity, false);
- let tq = &ctxt.timer_queue;
- let symbol = format!("TIMER_QUEUE::{}", tq);
- if cfg!(feature = "nightly") {
- items.push(quote!(
- #[doc = #symbol]
- static mut #tq: rtfm::export::MaybeUninit<rtfm::export::TimerQueue<#enum_, #cap>> =
- rtfm::export::MaybeUninit::uninit();
- ));
- } else {
- items.push(quote!(
- #[doc = #symbol]
- static mut #tq:
- rtfm::export::MaybeUninit<rtfm::export::TimerQueue<#enum_, #cap>> =
- rtfm::export::MaybeUninit::uninit();
- ));
- }
+ let ty = quote!(rtfm::export::TimerQueue<T, #cap>);
+ items.push(quote!(
+ /// The timer queue
+ static mut TQ: rtfm::export::MaybeUninit<#ty> = rtfm::export::MaybeUninit::uninit();
+ ));
+
+ items.push(quote!(
+ struct TQ<'a> {
+ priority: &'a rtfm::export::Priority,
+ }
+ ));
- items.push(mk_resource(
- ctxt,
+ items.push(impl_mutex(
+ app,
&[],
- tq,
- quote!(rtfm::export::TimerQueue<#enum_, #cap>),
+ false,
+ &Ident::new("TQ", Span::call_site()),
+ ty,
analysis.timer_queue.ceiling,
- quote!(&mut *#tq.as_mut_ptr()),
- app,
- None,
+ quote!(TQ.as_mut_ptr()),
));
- let priority = &ctxt.priority;
let device = &app.args.device;
let arms = tasks
.iter()
- .map(|task| {
- let task_ = &app.tasks[task];
- let level = task_.args.priority;
- let cfgs = &task_.cfgs;
- let dispatcher_ = &ctxt.dispatchers[&level];
- let tenum = &dispatcher_.enum_;
- let ready = &dispatcher_.ready_queue;
- let dispatcher = &analysis.dispatchers[&level].interrupt;
+ .map(|name| {
+ let task = &app.tasks[name];
+ let cfgs = &task.cfgs;
+ let priority = task.args.priority;
+ let rq = mk_rq_ident(priority);
+ let t = mk_t_ident(priority);
+ let dispatcher = &analysis.dispatchers[&priority].interrupt;
quote!(
#(#cfgs)*
- #enum_::#task => {
- (#ready { #priority }).lock(|rq| {
- rq.split().0.enqueue_unchecked((#tenum::#task, index))
+ T::#name => {
+ let priority = &rtfm::export::Priority::new(PRIORITY);
+ (#rq { priority }).lock(|rq| {
+ rq.split().0.enqueue_unchecked((#t::#name, index))
});
- rtfm::pend(#device::Interrupt::#dispatcher);
+ rtfm::pend(#device::Interrupt::#dispatcher)
}
)
})
.collect::<Vec<_>>();
- let logical_prio = analysis.timer_queue.priority;
- let alias = ctxt.ident_gen.mk_ident(None, false);
+ let priority = analysis.timer_queue.priority;
items.push(quote!(
- #[export_name = "SysTick"]
- #[doc(hidden)]
- unsafe fn #alias() {
- use rtfm::Mutex;
-
- let ref #priority = rtfm::export::Priority::new(#logical_prio);
-
- rtfm::export::run(|| {
- rtfm::export::sys_tick(#tq { #priority }, |task, index| {
+ /// The system timer
+ #[no_mangle]
+ unsafe fn SysTick() {
+ use rtfm::Mutex as _;
+
+ /// System timer priority
+ const PRIORITY: u8 = #priority;
+
+ rtfm::export::run(PRIORITY, || {
+ while let Some((task, index)) = (TQ {
+ // NOTE dynamic priority is always the static priority at this point
+ priority: &rtfm::export::Priority::new(PRIORITY),
+ })
+ // NOTE `inline(always)` produces faster and smaller code
+ .lock(#[inline(always)]
+ |tq| tq.dequeue())
+ {
match task {
#(#arms)*
}
- });
- })
+ }
+ });
}
));
- quote!(#(#items)*)
+ items
}
-fn pre_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
- let mut exprs = vec![];
+/// Generates all the `Schedule.$task` related code
+fn schedule(app: &App) -> Vec<proc_macro2::TokenStream> {
+ let mut items = vec![];
+ if !cfg!(feature = "timer-queue") {
+ return items;
+ }
- if !cfg!(feature = "nightly") {
- // these are `MaybeUninit` arrays
- for task in ctxt.tasks.values() {
- let inputs = &task.inputs;
- exprs.push(quote!(#inputs.write(core::mem::uninitialized());))
+ let mut seen = BTreeSet::new();
+ for (scheduler, schedulees) in app.schedule_callers() {
+ if schedulees.is_empty() {
+ continue;
}
- #[cfg(feature = "timer-queue")]
- for task in ctxt.tasks.values() {
- let scheduleds = &task.scheduleds;
- exprs.push(quote!(#scheduleds.write(core::mem::uninitialized());))
+ let mut methods = vec![];
+
+ let scheduler_is_init = scheduler == "init";
+ for name in schedulees {
+ let schedulee = &app.tasks[name];
+
+ let (args, _, untupled, ty) = regroup_inputs(&schedulee.inputs);
+
+ let cfgs = &schedulee.cfgs;
+
+ let schedule = mk_schedule_ident(name);
+ if scheduler_is_init {
+ let body = mk_schedule_body(&scheduler, name, app);
+
+ let args = args.clone();
+ methods.push(quote!(
+ #(#cfgs)*
+ fn #name(&self, instant: rtfm::Instant #(,#args)*) -> Result<(), #ty> {
+ #body
+ }
+ ));
+ } else {
+ if !seen.contains(name) {
+ seen.insert(name);
+
+ let body = mk_schedule_body(&scheduler, name, app);
+ let args = args.clone();
+
+ items.push(quote!(
+ #(#cfgs)*
+ fn #schedule(
+ priority: &rtfm::export::Priority,
+ instant: rtfm::Instant
+ #(,#args)*
+ ) -> Result<(), #ty> {
+ #body
+ }
+ ));
+ }
+
+ methods.push(quote!(
+ #(#cfgs)*
+ #[inline(always)]
+ fn #name(&self, instant: rtfm::Instant #(,#args)*) -> Result<(), #ty> {
+ let priority = unsafe { self.priority() };
+
+ #schedule(priority, instant #(,#untupled)*)
+ }
+ ));
+ }
}
- // these are `MaybeUninit` `ReadyQueue`s
- for dispatcher in ctxt.dispatchers.values() {
- let rq = &dispatcher.ready_queue;
- exprs.push(quote!(#rq.write(rtfm::export::ReadyQueue::new_sc());))
+ let lt = if scheduler_is_init {
+ None
+ } else {
+ Some(quote!('a))
+ };
+ items.push(quote!(
+ impl<#lt> #scheduler::Schedule<#lt> {
+ #(#methods)*
+ }
+ ));
+ }
+
+ items
+}
+
+/// Generates `Send` / `Sync` compile time checks
+fn assertions(app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
+ let mut stmts = vec![];
+
+ for ty in &analysis.assert_sync {
+ stmts.push(quote!(rtfm::export::assert_sync::<#ty>();));
+ }
+
+ for task in &analysis.tasks_assert_send {
+ let (_, _, _, ty) = regroup_inputs(&app.tasks[task].inputs);
+ stmts.push(quote!(rtfm::export::assert_send::<#ty>();));
+ }
+
+ // all late resources need to be `Send`
+ for ty in &analysis.resources_assert_send {
+ stmts.push(quote!(rtfm::export::assert_send::<#ty>();));
+ }
+
+ stmts
+}
+
+/// Generates code that we must run before `init` runs. See comments inside
+fn pre_init(app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
+ let mut stmts = vec![];
+
+ stmts.push(quote!(rtfm::export::interrupt::disable();));
+
+ // these won't be required once we have better `const fn` on stable (or const generics)
+ if !cfg!(feature = "nightly") {
+ // initialize `MaybeUninit` `ReadyQueue`s
+ for level in analysis.dispatchers.keys() {
+ let rq = mk_rq_ident(*level);
+ stmts.push(quote!(#rq.write(rtfm::export::ReadyQueue::u8_sc());))
}
- // these are `MaybeUninit` `FreeQueue`s
- for task in ctxt.tasks.values() {
- let fq = &task.free_queue;
- exprs.push(quote!(#fq.write(rtfm::export::FreeQueue::new_sc());))
+ // initialize `MaybeUninit` `FreeQueue`s
+ for name in app.tasks.keys() {
+ let fq = mk_fq_ident(name);
+
+ stmts.push(quote!(
+ let fq = #fq.write(rtfm::export::FreeQueue::u8_sc());
+ ));
+
+ // populate the `FreeQueue`s
+ let cap = analysis.capacities[name];
+ stmts.push(quote!(
+ for i in 0..#cap {
+ fq.enqueue_unchecked(i);
+ }
+ ));
+ }
+ } else {
+ // populate the `FreeQueue`s
+ for name in app.tasks.keys() {
+ let fq = mk_fq_ident(name);
+ let cap = analysis.capacities[name];
+
+ stmts.push(quote!(
+ for i in 0..#cap {
+ #fq.enqueue_unchecked(i);
+ }
+ ));
}
}
+ stmts.push(quote!(
+ let mut core = rtfm::export::Peripherals::steal();
+ ));
+
// Initialize the timer queue
if !analysis.timer_queue.tasks.is_empty() {
- let tq = &ctxt.timer_queue;
- exprs.push(quote!(#tq.write(rtfm::export::TimerQueue::new(p.SYST));));
- }
-
- // Populate the `FreeQueue`s
- for (name, task) in &ctxt.tasks {
- let fq = &task.free_queue;
- let fq_ = if cfg!(feature = "nightly") {
- quote!(#fq)
- } else {
- quote!(#fq.get_mut())
- };
- let capacity = analysis.capacities[name];
- exprs.push(quote!(
- for i in 0..#capacity {
- #fq_.enqueue_unchecked(i);
- }
- ))
+ stmts.push(quote!(TQ.write(rtfm::export::TimerQueue::new(core.SYST));));
}
+ // set interrupts priorities
let device = &app.args.device;
let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
for (handler, interrupt) in &app.interrupts {
let name = interrupt.args.binds(handler);
let priority = interrupt.args.priority;
- exprs.push(quote!(p.NVIC.enable(#device::Interrupt::#name);));
- // compile time assert that the priority is supported by the device
- exprs.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
+ stmts.push(quote!(core.NVIC.enable(#device::Interrupt::#name);));
- exprs.push(quote!(p.NVIC.set_priority(
- #device::Interrupt::#name,
- ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits),
- );));
+ // compile time assert that this priority is supported by the device
+ stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
+
+ stmts.push(quote!(
+ core.NVIC.set_priority(
+ #device::Interrupt::#name,
+ rtfm::export::logical2hw(#priority, #nvic_prio_bits),
+ );
+ ));
}
+ // set task dispatcher priorities
for (priority, dispatcher) in &analysis.dispatchers {
let name = &dispatcher.interrupt;
- exprs.push(quote!(p.NVIC.enable(#device::Interrupt::#name);));
- // compile time assert that the priority is supported by the device
- exprs.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
+ stmts.push(quote!(core.NVIC.enable(#device::Interrupt::#name);));
- exprs.push(quote!(p.NVIC.set_priority(
- #device::Interrupt::#name,
- ((1 << #nvic_prio_bits) - #priority) << (8 - #nvic_prio_bits),
- );));
+ // compile time assert that this priority is supported by the device
+ stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
+
+ stmts.push(quote!(
+ core.NVIC.set_priority(
+ #device::Interrupt::#name,
+ rtfm::export::logical2hw(#priority, #nvic_prio_bits),
+ );
+ ));
}
// Set the cycle count to 0 and disable it while `init` executes
if cfg!(feature = "timer-queue") {
- exprs.push(quote!(p.DWT.ctrl.modify(|r| r & !1);));
- exprs.push(quote!(p.DWT.cyccnt.write(0);));
+ stmts.push(quote!(core.DWT.ctrl.modify(|r| r & !1);));
+ stmts.push(quote!(core.DWT.cyccnt.write(0);));
}
- quote!(
- let mut p = rtfm::export::Peripherals::steal();
- #(#exprs)*
+ stmts
+}
+
+// This generates
+//
+// - at the root of the crate
+// - a initResources struct (maybe)
+// - a initLateResources struct (maybe)
+// - a initLocals struct
+//
+// - an `init` module that contains
+// - the `Context` struct
+// - a re-export of the initResources struct
+// - a re-export of the initLateResources struct
+// - a re-export of the initLocals struct
+// - the Spawn struct (maybe)
+// - the Schedule struct (maybe, if `timer-queue` is enabled)
+//
+// - hidden in `const APP`
+// - the initResources constructor
+//
+// - the user specified `init` function
+//
+// - a call to the user specified `init` function
+fn init(
+ app: &App,
+ analysis: &Analysis,
+) -> (
+ // const_app
+ Option<proc_macro2::TokenStream>,
+ // mod_init
+ proc_macro2::TokenStream,
+ // init_locals
+ proc_macro2::TokenStream,
+ // init_resources
+ Option<proc_macro2::TokenStream>,
+ // init_late_resources
+ Option<proc_macro2::TokenStream>,
+ // user_init
+ proc_macro2::TokenStream,
+ // call_init
+ proc_macro2::TokenStream,
+) {
+ let mut needs_lt = false;
+ let mut const_app = None;
+ let mut init_resources = None;
+ if !app.init.args.resources.is_empty() {
+ let (item, constructor) = resources_struct(Kind::Init, 0, &mut needs_lt, app, analysis);
+
+ init_resources = Some(item);
+ const_app = Some(constructor);
+ }
+
+ let core = if cfg!(feature = "timer-queue") {
+ quote!(rtfm::Peripherals {
+ CBP: core.CBP,
+ CPUID: core.CPUID,
+ DCB: &mut core.DCB,
+ FPB: core.FPB,
+ FPU: core.FPU,
+ ITM: core.ITM,
+ MPU: core.MPU,
+ SCB: &mut core.SCB,
+ TPIU: core.TPIU,
+ })
+ } else {
+ quote!(rtfm::Peripherals {
+ CBP: core.CBP,
+ CPUID: core.CPUID,
+ DCB: core.DCB,
+ DWT: core.DWT,
+ FPB: core.FPB,
+ FPU: core.FPU,
+ ITM: core.ITM,
+ MPU: core.MPU,
+ SCB: &mut core.SCB,
+ SYST: core.SYST,
+ TPIU: core.TPIU,
+ })
+ };
+
+ let call_init = quote!(let late = init(init::Locals::new(), init::Context::new(#core)););
+
+ let late_fields = app
+ .resources
+ .iter()
+ .filter_map(|(name, res)| {
+ if res.expr.is_none() {
+ let ty = &res.ty;
+
+ Some(quote!(pub #name: #ty))
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let attrs = &app.init.attrs;
+ let has_late_resources = !late_fields.is_empty();
+ let (ret, init_late_resources) = if has_late_resources {
+ (
+ Some(quote!(-> init::LateResources)),
+ Some(quote!(
+ /// Resources initialized at runtime
+ #[allow(non_snake_case)]
+ pub struct initLateResources {
+ #(#late_fields),*
+ }
+ )),
+ )
+ } else {
+ (None, None)
+ };
+ let context = &app.init.context;
+ let use_u32ext = if cfg!(feature = "timer-queue") {
+ Some(quote!(
+ use rtfm::U32Ext as _;
+ ))
+ } else {
+ None
+ };
+ let (locals_struct, lets) = locals(Kind::Init, &app.init.statics);
+ let stmts = &app.init.stmts;
+ let user_init = quote!(
+ #(#attrs)*
+ #[allow(non_snake_case)]
+ fn init(__locals: init::Locals, #context: init::Context) #ret {
+ #use_u32ext
+
+ #(#lets;)*
+
+ #(#stmts)*
+ }
+ );
+
+ let mod_init = module(
+ Kind::Init,
+ (!app.init.args.resources.is_empty(), needs_lt),
+ !app.init.args.schedule.is_empty(),
+ !app.init.args.spawn.is_empty(),
+ has_late_resources,
+ app,
+ );
+
+ (
+ const_app,
+ mod_init,
+ locals_struct,
+ init_resources,
+ init_late_resources,
+ user_init,
+ call_init,
)
}
-fn assertions(app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
- let mut items = vec![];
+/// Generates code that we must run after `init` returns. See comments inside
+fn post_init(app: &App, analysis: &Analysis) -> Vec<proc_macro2::TokenStream> {
+ let mut stmts = vec![];
- for ty in &analysis.assert_sync {
- items.push(quote!(rtfm::export::assert_sync::<#ty>()));
+ let device = &app.args.device;
+ let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
+
+ // initialize late resources
+ for (name, res) in &app.resources {
+ if res.expr.is_some() {
+ continue;
+ }
+
+ stmts.push(quote!(#name.write(late.#name);));
}
- for task in &analysis.tasks_assert_send {
- let ty = tuple_ty(&app.tasks[task].inputs);
- items.push(quote!(rtfm::export::assert_send::<#ty>()));
+ // set exception priorities
+ for (handler, exception) in &app.exceptions {
+ let name = exception.args.binds(handler);
+ let priority = exception.args.priority;
+
+ // compile time assert that this priority is supported by the device
+ stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
+
+ stmts.push(quote!(core.SCB.set_priority(
+ rtfm::export::SystemHandler::#name,
+ rtfm::export::logical2hw(#priority, #nvic_prio_bits),
+ );));
}
- // all late resources need to be `Send`
- for ty in &analysis.resources_assert_send {
- items.push(quote!(rtfm::export::assert_send::<#ty>()));
+ // set the system timer priority
+ if !analysis.timer_queue.tasks.is_empty() {
+ let priority = analysis.timer_queue.priority;
+
+ // compile time assert that this priority is supported by the device
+ stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
+
+ stmts.push(quote!(core.SCB.set_priority(
+ rtfm::export::SystemHandler::SysTick,
+ rtfm::export::logical2hw(#priority, #nvic_prio_bits),
+ );));
+ }
+
+ if app.idle.is_none() {
+ // Set SLEEPONEXIT bit to enter sleep mode when returning from ISR
+ stmts.push(quote!(core.SCB.scr.modify(|r| r | 1 << 1);));
+ }
+
+ // enable and start the system timer
+ if !analysis.timer_queue.tasks.is_empty() {
+ stmts.push(quote!((*TQ.as_mut_ptr())
+ .syst
+ .set_clock_source(rtfm::export::SystClkSource::Core);));
+ stmts.push(quote!((*TQ.as_mut_ptr()).syst.enable_counter();));
+ }
+
+ // enable the cycle counter
+ if cfg!(feature = "timer-queue") {
+ stmts.push(quote!(core.DCB.enable_trace();));
+ stmts.push(quote!(core.DWT.enable_cycle_counter();));
}
- quote!(#(#items;)*)
+ stmts.push(quote!(rtfm::export::interrupt::enable();));
+
+ stmts
}
-fn mk_resource(
- ctxt: &Context,
- cfgs: &[Attribute],
- struct_: &Ident,
- ty: proc_macro2::TokenStream,
- ceiling: u8,
- ptr: proc_macro2::TokenStream,
+// If the user specified `idle` this generates
+//
+// - at the root of the crate
+// - an idleResources struct (maybe)
+// - an idleLocals struct
+//
+// - an `init` module that contains
+// - the `Context` struct
+// - a re-export of the idleResources struct
+// - a re-export of the idleLocals struct
+// - the Spawn struct (maybe)
+// - the Schedule struct (maybe, if `timer-queue` is enabled)
+//
+// - hidden in `const APP`
+// - the idleResources constructor
+//
+// - the user specified `idle` function
+//
+// - a call to the user specified `idle` function
+//
+// Otherwise it uses `loop { WFI }` as `idle`
+fn idle(
app: &App,
- module: Option<&mut Vec<proc_macro2::TokenStream>>,
-) -> proc_macro2::TokenStream {
- let priority = &ctxt.priority;
- let device = &app.args.device;
+ analysis: &Analysis,
+) -> (
+ // const_app_idle
+ Option<proc_macro2::TokenStream>,
+ // mod_idle
+ Option<proc_macro2::TokenStream>,
+ // idle_locals
+ Option<proc_macro2::TokenStream>,
+ // idle_resources
+ Option<proc_macro2::TokenStream>,
+ // user_idle
+ Option<proc_macro2::TokenStream>,
+ // call_idle
+ proc_macro2::TokenStream,
+) {
+ if let Some(idle) = app.idle.as_ref() {
+ let mut needs_lt = false;
+ let mut const_app = None;
+ let mut idle_resources = None;
- let mut items = vec![];
+ if !idle.args.resources.is_empty() {
+ let (item, constructor) = resources_struct(Kind::Idle, 0, &mut needs_lt, app, analysis);
- let path = if let Some(module) = module {
- let doc = format!("`{}`", ty);
- module.push(quote!(
- #[allow(non_camel_case_types)]
- #[doc = #doc]
- #(#cfgs)*
- pub struct #struct_<'a> {
- #[doc(hidden)]
- pub #priority: &'a rtfm::export::Priority,
- }
+ idle_resources = Some(item);
+ const_app = Some(constructor);
+ }
+
+ let call_idle = quote!(idle(
+ idle::Locals::new(),
+ idle::Context::new(&rtfm::export::Priority::new(0))
));
- quote!(resources::#struct_)
+ let attrs = &idle.attrs;
+ let context = &idle.context;
+ let use_u32ext = if cfg!(feature = "timer-queue") {
+ Some(quote!(
+ use rtfm::U32Ext as _;
+ ))
+ } else {
+ None
+ };
+ let (idle_locals, lets) = locals(Kind::Idle, &idle.statics);
+ let stmts = &idle.stmts;
+ let user_idle = quote!(
+ #(#attrs)*
+ #[allow(non_snake_case)]
+ fn idle(__locals: idle::Locals, #context: idle::Context) -> ! {
+ #use_u32ext
+ use rtfm::Mutex as _;
+
+ #(#lets;)*
+
+ #(#stmts)*
+ }
+ );
+
+ let mod_idle = module(
+ Kind::Idle,
+ (!idle.args.resources.is_empty(), needs_lt),
+ !idle.args.schedule.is_empty(),
+ !idle.args.spawn.is_empty(),
+ false,
+ app,
+ );
+
+ (
+ const_app,
+ Some(mod_idle),
+ Some(idle_locals),
+ idle_resources,
+ Some(user_idle),
+ call_idle,
+ )
} else {
- items.push(quote!(
- #(#cfgs)*
- struct #struct_<'a> {
- #priority: &'a rtfm::export::Priority,
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ quote!(loop {
+ rtfm::export::wfi()
+ }),
+ )
+ }
+}
+
+/* Support functions */
+/// This function creates the `Resources` struct
+///
+/// It's a bit unfortunate but this struct has to be created in the root because it refers to types
+/// which may have been imported into the root.
+fn resources_struct(
+ kind: Kind,
+ priority: u8,
+ needs_lt: &mut bool,
+ app: &App,
+ analysis: &Analysis,
+) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
+ let mut lt = None;
+
+ let resources = match &kind {
+ Kind::Init => &app.init.args.resources,
+ Kind::Idle => &app.idle.as_ref().expect("UNREACHABLE").args.resources,
+ Kind::Interrupt(name) => &app.interrupts[name].args.resources,
+ Kind::Exception(name) => &app.exceptions[name].args.resources,
+ Kind::Task(name) => &app.tasks[name].args.resources,
+ };
+
+ let mut fields = vec![];
+ let mut values = vec![];
+ for name in resources {
+ let res = &app.resources[name];
+
+ let cfgs = &res.cfgs;
+ let mut_ = res.mutability;
+ let ty = &res.ty;
+
+ if kind.is_init() {
+ if !analysis.ownerships.contains_key(name) {
+ // owned by `init`
+ fields.push(quote!(
+ #(#cfgs)*
+ pub #name: &'static #mut_ #ty
+ ));
+
+ values.push(quote!(
+ #(#cfgs)*
+ #name: &#mut_ #name
+ ));
+ } else {
+ // owned by someone else
+ lt = Some(quote!('a));
+
+ fields.push(quote!(
+ #(#cfgs)*
+ pub #name: &'a mut #ty
+ ));
+
+ values.push(quote!(
+ #(#cfgs)*
+ #name: &mut #name
+ ));
+ }
+ } else {
+ let ownership = &analysis.ownerships[name];
+
+ let mut exclusive = false;
+ if ownership.needs_lock(priority) {
+ if mut_.is_none() {
+ lt = Some(quote!('a));
+
+ fields.push(quote!(
+ #(#cfgs)*
+ pub #name: &'a #ty
+ ));
+ } else {
+ // resource proxy
+ lt = Some(quote!('a));
+
+ fields.push(quote!(
+ #(#cfgs)*
+ pub #name: resources::#name<'a>
+ ));
+
+ values.push(quote!(
+ #(#cfgs)*
+ #name: resources::#name::new(priority)
+ ));
+
+ continue;
+ }
+ } else {
+ let lt = if kind.runs_once() {
+ quote!('static)
+ } else {
+ lt = Some(quote!('a));
+ quote!('a)
+ };
+
+ if ownership.is_owned() || mut_.is_none() {
+ fields.push(quote!(
+ #(#cfgs)*
+ pub #name: &#lt #mut_ #ty
+ ));
+ } else {
+ exclusive = true;
+
+ fields.push(quote!(
+ #(#cfgs)*
+ pub #name: rtfm::Exclusive<#lt, #ty>
+ ));
+ }
}
+
+ let is_late = res.expr.is_none();
+ if is_late {
+ let expr = if mut_.is_some() {
+ quote!(&mut *#name.as_mut_ptr())
+ } else {
+ quote!(&*#name.as_ptr())
+ };
+
+ if exclusive {
+ values.push(quote!(
+ #(#cfgs)*
+ #name: rtfm::Exclusive(#expr)
+ ));
+ } else {
+ values.push(quote!(
+ #(#cfgs)*
+ #name: #expr
+ ));
+ }
+ } else {
+ if exclusive {
+ values.push(quote!(
+ #(#cfgs)*
+ #name: rtfm::Exclusive(&mut #name)
+ ));
+ } else {
+ values.push(quote!(
+ #(#cfgs)*
+ #name: &#mut_ #name
+ ));
+ }
+ }
+ }
+ }
+
+ if lt.is_some() {
+ *needs_lt = true;
+
+ // the struct could end up empty due to `cfg` leading to an error due to `'a` being unused
+ fields.push(quote!(
+ #[doc(hidden)]
+ pub __marker__: core::marker::PhantomData<&'a ()>
));
- quote!(#struct_)
+ values.push(quote!(__marker__: core::marker::PhantomData))
+ }
+
+ let ident = kind.resources_ident();
+ let doc = format!("Resources {} has access to", ident);
+ let item = quote!(
+ #[allow(non_snake_case)]
+ #[doc = #doc]
+ pub struct #ident<#lt> {
+ #(#fields,)*
+ }
+ );
+ let arg = if kind.is_init() {
+ None
+ } else {
+ Some(quote!(priority: &#lt rtfm::export::Priority))
};
+ let constructor = quote!(
+ impl<#lt> #ident<#lt> {
+ #[inline(always)]
+ unsafe fn new(#arg) -> Self {
+ #ident {
+ #(#values,)*
+ }
+ }
+ }
+ );
+ (item, constructor)
+}
- items.push(quote!(
+/// Creates a `Mutex` implementation
+fn impl_mutex(
+ app: &App,
+ cfgs: &[Attribute],
+ resources_prefix: bool,
+ name: &Ident,
+ ty: proc_macro2::TokenStream,
+ ceiling: u8,
+ ptr: proc_macro2::TokenStream,
+) -> proc_macro2::TokenStream {
+ let path = if resources_prefix {
+ quote!(resources::#name)
+ } else {
+ quote!(#name)
+ };
+
+ let priority = if resources_prefix {
+ quote!(self.priority())
+ } else {
+ quote!(self.priority)
+ };
+
+ let device = &app.args.device;
+ quote!(
#(#cfgs)*
impl<'a> rtfm::Mutex for #path<'a> {
type T = #ty;
- #[inline]
- fn lock<R, F>(&mut self, f: F) -> R
- where
- F: FnOnce(&mut Self::T) -> R,
- {
+ #[inline(always)]
+ fn lock<R>(&mut self, f: impl FnOnce(&mut #ty) -> R) -> R {
+ /// Priority ceiling
+ const CEILING: u8 = #ceiling;
+
unsafe {
- rtfm::export::claim(
+ rtfm::export::lock(
#ptr,
- &self.#priority,
- #ceiling,
+ #priority,
+ CEILING,
#device::NVIC_PRIO_BITS,
f,
)
}
}
}
- ));
-
- quote!(#(#items)*)
+ )
}
-fn mk_capacity_literal(capacity: u8) -> LitInt {
- LitInt::new(u64::from(capacity), IntSuffix::None, Span::call_site())
-}
+/// Creates a `Locals` struct and related code. This returns
+///
+/// - `locals`
+///
+/// ```
+/// pub struct Locals<'a> {
+/// #[cfg(never)]
+/// pub X: &'a mut X,
+/// __marker__: PhantomData<&'a mut ()>,
+/// }
+/// ```
+///
+/// - `lt`
+///
+/// ```
+/// 'a
+/// ```
+///
+/// - `lets`
+///
+/// ```
+/// #[cfg(never)]
+/// let X = __locals.X
+/// ```
+fn locals(
+ kind: Kind,
+ statics: &BTreeMap<Ident, Static>,
+) -> (
+ // locals
+ proc_macro2::TokenStream,
+ // lets
+ Vec<proc_macro2::TokenStream>,
+) {
+ let runs_once = kind.runs_once();
+ let ident = kind.locals_ident();
-fn mk_typenum_capacity(capacity: u8, power_of_two: bool) -> proc_macro2::TokenStream {
- let capacity = if power_of_two {
- capacity
- .checked_next_power_of_two()
- .expect("capacity.next_power_of_two()")
- } else {
- capacity
- };
+ let mut lt = None;
+ let mut fields = vec![];
+ let mut lets = vec![];
+ let mut items = vec![];
+ let mut values = vec![];
+ for (name, static_) in statics {
+ let lt = if runs_once {
+ quote!('static)
+ } else {
+ lt = Some(quote!('a));
+ quote!('a)
+ };
- let ident = Ident::new(&format!("U{}", capacity), Span::call_site());
+ let cfgs = &static_.cfgs;
+ let expr = &static_.expr;
+ let ty = &static_.ty;
+ fields.push(quote!(
+ #(#cfgs)*
+ #name: &#lt mut #ty
+ ));
+ items.push(quote!(
+ #(#cfgs)*
+ static mut #name: #ty = #expr
+ ));
+ values.push(quote!(
+ #(#cfgs)*
+ #name: &mut #name
+ ));
+ lets.push(quote!(
+ #(#cfgs)*
+ let #name = __locals.#name
+ ));
+ }
- quote!(rtfm::export::consts::#ident)
-}
+ if lt.is_some() {
+ fields.push(quote!(__marker__: core::marker::PhantomData<&'a mut ()>));
+ values.push(quote!(__marker__: core::marker::PhantomData));
+ }
-struct IdentGenerator {
- call_count: u32,
- rng: rand::rngs::SmallRng,
+ let locals = quote!(
+ #[allow(non_snake_case)]
+ #[doc(hidden)]
+ pub struct #ident<#lt> {
+ #(#fields),*
+ }
+
+ impl<#lt> #ident<#lt> {
+ #[inline(always)]
+ unsafe fn new() -> Self {
+ #(#items;)*
+
+ #ident {
+ #(#values),*
+ }
+ }
+ }
+ );
+
+ (locals, lets)
}
-impl IdentGenerator {
- fn new() -> IdentGenerator {
- let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
+/// This function creates a module that contains
+//
+// - the Context struct
+// - a re-export of the ${name}Resources struct (maybe)
+// - a re-export of the ${name}LateResources struct (maybe)
+// - a re-export of the ${name}Locals struct
+// - the Spawn struct (maybe)
+// - the Schedule struct (maybe, if `timer-queue` is enabled)
+fn module(
+ kind: Kind,
+ resources: (/* has */ bool, /* 'a */ bool),
+ schedule: bool,
+ spawn: bool,
+ late_resources: bool,
+ app: &App,
+) -> proc_macro2::TokenStream {
+ let mut items = vec![];
+ let mut fields = vec![];
+ let mut values = vec![];
+
+ let name = kind.ident();
- let secs = elapsed.as_secs();
- let nanos = elapsed.subsec_nanos();
+ let mut needs_instant = false;
+ let mut lt = None;
+ match kind {
+ Kind::Init => {
+ if cfg!(feature = "timer-queue") {
+ fields.push(quote!(
+ /// System start time = `Instant(0 /* cycles */)`
+ pub start: rtfm::Instant
+ ));
- let mut seed: [u8; 16] = [0; 16];
+ values.push(quote!(start: rtfm::Instant::artificial(0)));
+ }
- for (i, v) in seed.iter_mut().take(8).enumerate() {
- *v = ((secs >> (i * 8)) & 0xFF) as u8
+ let device = &app.args.device;
+ fields.push(quote!(
+ /// Core (Cortex-M) peripherals
+ pub core: rtfm::Peripherals<'a>
+ ));
+ fields.push(quote!(
+ /// Device specific peripherals
+ pub device: #device::Peripherals
+ ));
+
+ values.push(quote!(core));
+ values.push(quote!(device: #device::Peripherals::steal()));
+ lt = Some(quote!('a));
}
- for (i, v) in seed.iter_mut().skip(8).take(4).enumerate() {
- *v = ((nanos >> (i * 8)) & 0xFF) as u8
+ Kind::Idle => {}
+
+ Kind::Exception(_) | Kind::Interrupt(_) => {
+ if cfg!(feature = "timer-queue") {
+ fields.push(quote!(
+ /// Time at which this handler started executing
+ pub start: rtfm::Instant
+ ));
+
+ values.push(quote!(start: instant));
+
+ needs_instant = true;
+ }
}
- let rng = rand::rngs::SmallRng::from_seed(seed);
+ Kind::Task(_) => {
+ if cfg!(feature = "timer-queue") {
+ fields.push(quote!(
+ /// The time at which this task was scheduled to run
+ pub scheduled: rtfm::Instant
+ ));
+
+ values.push(quote!(scheduled: instant));
- IdentGenerator { call_count: 0, rng }
+ needs_instant = true;
+ }
+ }
}
- fn mk_ident(&mut self, name: Option<&str>, random: bool) -> Ident {
- let s = if let Some(name) = name {
- format!("{}_", name)
+ let ident = kind.locals_ident();
+ items.push(quote!(
+ #[doc(inline)]
+ pub use super::#ident as Locals;
+ ));
+
+ if resources.0 {
+ let ident = kind.resources_ident();
+ let lt = if resources.1 {
+ lt = Some(quote!('a));
+ Some(quote!('a))
+ } else {
+ None
+ };
+
+ items.push(quote!(
+ #[doc(inline)]
+ pub use super::#ident as Resources;
+ ));
+
+ fields.push(quote!(
+ /// Resources this task has access to
+ pub resources: Resources<#lt>
+ ));
+
+ let priority = if kind.is_init() {
+ None
} else {
- "__rtfm_internal_".to_string()
+ Some(quote!(priority))
};
+ values.push(quote!(resources: Resources::new(#priority)));
+ }
+
+ if schedule {
+ let doc = "Tasks that can be `schedule`-d from this context";
+ if kind.is_init() {
+ items.push(quote!(
+ #[doc = #doc]
+ #[derive(Clone, Copy)]
+ pub struct Schedule {
+ _not_send: core::marker::PhantomData<*mut ()>,
+ }
+ ));
- let mut s = format!("{}{}", s, self.call_count);
- self.call_count += 1;
+ fields.push(quote!(
+ #[doc = #doc]
+ pub schedule: Schedule
+ ));
- if random {
- s.push('_');
+ values.push(quote!(
+ schedule: Schedule { _not_send: core::marker::PhantomData }
+ ));
+ } else {
+ lt = Some(quote!('a));
+
+ items.push(quote!(
+ #[doc = #doc]
+ #[derive(Clone, Copy)]
+ pub struct Schedule<'a> {
+ priority: &'a rtfm::export::Priority,
+ }
+
+ impl<'a> Schedule<'a> {
+ #[doc(hidden)]
+ #[inline(always)]
+ pub unsafe fn priority(&self) -> &rtfm::export::Priority {
+ &self.priority
+ }
+ }
+ ));
+
+ fields.push(quote!(
+ #[doc = #doc]
+ pub schedule: Schedule<'a>
+ ));
+
+ values.push(quote!(
+ schedule: Schedule { priority }
+ ));
+ }
+ }
+
+ if spawn {
+ let doc = "Tasks that can be `spawn`-ed from this context";
+ if kind.is_init() {
+ fields.push(quote!(
+ #[doc = #doc]
+ pub spawn: Spawn
+ ));
+
+ items.push(quote!(
+ #[doc = #doc]
+ #[derive(Clone, Copy)]
+ pub struct Spawn {
+ _not_send: core::marker::PhantomData<*mut ()>,
+ }
+ ));
+
+ values.push(quote!(spawn: Spawn { _not_send: core::marker::PhantomData }));
+ } else {
+ lt = Some(quote!('a));
+
+ fields.push(quote!(
+ #[doc = #doc]
+ pub spawn: Spawn<'a>
+ ));
- for i in 0..4 {
- if i == 0 || self.rng.gen() {
- s.push(('a' as u8 + self.rng.gen::<u8>() % 25) as char)
+ let mut instant_method = None;
+ if kind.is_idle() {
+ items.push(quote!(
+ #[doc = #doc]
+ #[derive(Clone, Copy)]
+ pub struct Spawn<'a> {
+ priority: &'a rtfm::export::Priority,
+ }
+ ));
+
+ values.push(quote!(spawn: Spawn { priority }));
+ } else {
+ let instant_field = if cfg!(feature = "timer-queue") {
+ needs_instant = true;
+ instant_method = Some(quote!(
+ pub unsafe fn instant(&self) -> rtfm::Instant {
+ self.instant
+ }
+ ));
+ Some(quote!(instant: rtfm::Instant,))
} else {
- s.push(('0' as u8 + self.rng.gen::<u8>() % 10) as char)
+ None
+ };
+
+ items.push(quote!(
+ /// Tasks that can be spawned from this context
+ #[derive(Clone, Copy)]
+ pub struct Spawn<'a> {
+ #instant_field
+ priority: &'a rtfm::export::Priority,
+ }
+ ));
+
+ let _instant = if needs_instant {
+ Some(quote!(, instant))
+ } else {
+ None
+ };
+ values.push(quote!(
+ spawn: Spawn { priority #_instant }
+ ));
+ }
+
+ items.push(quote!(
+ impl<'a> Spawn<'a> {
+ #[doc(hidden)]
+ #[inline(always)]
+ pub unsafe fn priority(&self) -> &rtfm::export::Priority {
+ self.priority
+ }
+
+ #instant_method
+ }
+ ));
+ }
+ }
+
+ if late_resources {
+ items.push(quote!(
+ #[doc(inline)]
+ pub use super::initLateResources as LateResources;
+ ));
+ }
+
+ let doc = match kind {
+ Kind::Exception(_) => "Hardware task (exception)",
+ Kind::Idle => "Idle loop",
+ Kind::Init => "Initialization function",
+ Kind::Interrupt(_) => "Hardware task (interrupt)",
+ Kind::Task(_) => "Software task",
+ };
+
+ let core = if kind.is_init() {
+ lt = Some(quote!('a));
+ Some(quote!(core: rtfm::Peripherals<'a>,))
+ } else {
+ None
+ };
+
+ let priority = if kind.is_init() {
+ None
+ } else {
+ Some(quote!(priority: &#lt rtfm::export::Priority))
+ };
+
+ let instant = if needs_instant {
+ Some(quote!(, instant: rtfm::Instant))
+ } else {
+ None
+ };
+ items.push(quote!(
+ /// Execution context
+ pub struct Context<#lt> {
+ #(#fields,)*
+ }
+
+ impl<#lt> Context<#lt> {
+ #[inline(always)]
+ pub unsafe fn new(#core #priority #instant) -> Self {
+ Context {
+ #(#values,)*
}
}
}
+ ));
- Ident::new(&s, Span::call_site())
+ if !items.is_empty() {
+ quote!(
+ #[allow(non_snake_case)]
+ #[doc = #doc]
+ pub mod #name {
+ #(#items)*
+ }
+ )
+ } else {
+ quote!()
}
}
-// `once = true` means that these locals will be called from a function that will run *once*
-fn mk_locals(locals: &BTreeMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream {
- let lt = if once { Some(quote!('static)) } else { None };
+/// Creates the body of `spawn_${name}`
+fn mk_spawn_body<'a>(
+ spawner: &Ident,
+ name: &Ident,
+ app: &'a App,
+ analysis: &Analysis,
+) -> proc_macro2::TokenStream {
+ let spawner_is_init = spawner == "init";
+ let device = &app.args.device;
- let locals = locals
- .iter()
- .map(|(name, static_)| {
- let attrs = &static_.attrs;
- let cfgs = &static_.cfgs;
- let expr = &static_.expr;
- let ident = name;
- let ty = &static_.ty;
+ let spawnee = &app.tasks[name];
+ let priority = spawnee.args.priority;
+ let dispatcher = &analysis.dispatchers[&priority].interrupt;
- quote!(
- #[allow(non_snake_case)]
- #(#cfgs)*
- let #ident: &#lt mut #ty = {
- #(#attrs)*
- #(#cfgs)*
- static mut #ident: #ty = #expr;
+ let (_, tupled, _, _) = regroup_inputs(&spawnee.inputs);
- unsafe { &mut #ident }
- };
+ let inputs = mk_inputs_ident(name);
+ let fq = mk_fq_ident(name);
+
+ let rq = mk_rq_ident(priority);
+ let t = mk_t_ident(priority);
+
+ let write_instant = if cfg!(feature = "timer-queue") {
+ let instants = mk_instants_ident(name);
+
+ Some(quote!(
+ #instants.get_unchecked_mut(usize::from(index)).write(instant);
+ ))
+ } else {
+ None
+ };
+
+ let (dequeue, enqueue) = if spawner_is_init {
+ // `init` has exclusive access to these queues so we can bypass the resources AND
+ // the consumer / producer split
+ if cfg!(feature = "nightly") {
+ (
+ quote!(#fq.dequeue()),
+ quote!(#rq.enqueue_unchecked((#t::#name, index));),
)
- })
- .collect::<Vec<_>>();
+ } else {
+ (
+ quote!((*#fq.as_mut_ptr()).dequeue()),
+ quote!((*#rq.as_mut_ptr()).enqueue_unchecked((#t::#name, index));),
+ )
+ }
+ } else {
+ (
+ quote!((#fq { priority }).lock(|fq| fq.split().1.dequeue())),
+ quote!((#rq { priority }).lock(|rq| {
+ rq.split().0.enqueue_unchecked((#t::#name, index))
+ });),
+ )
+ };
+
+ quote!(
+ unsafe {
+ use rtfm::Mutex as _;
- quote!(#(#locals)*)
+ let input = #tupled;
+ if let Some(index) = #dequeue {
+ #inputs.get_unchecked_mut(usize::from(index)).write(input);
+
+ #write_instant
+
+ #enqueue
+
+ rtfm::pend(#device::Interrupt::#dispatcher);
+
+ Ok(())
+ } else {
+ Err(input)
+ }
+ }
+ )
}
-fn tuple_pat(inputs: &[ArgCaptured]) -> proc_macro2::TokenStream {
- if inputs.len() == 1 {
- let pat = &inputs[0].pat;
- quote!(#pat)
+/// Creates the body of `schedule_${name}`
+fn mk_schedule_body<'a>(scheduler: &Ident, name: &Ident, app: &'a App) -> proc_macro2::TokenStream {
+ let scheduler_is_init = scheduler == "init";
+
+ let schedulee = &app.tasks[name];
+
+ let (_, tupled, _, _) = regroup_inputs(&schedulee.inputs);
+
+ let fq = mk_fq_ident(name);
+ let inputs = mk_inputs_ident(name);
+ let instants = mk_instants_ident(name);
+
+ let (dequeue, enqueue) = if scheduler_is_init {
+ // `init` has exclusive access to these queues so we can bypass the resources AND
+ // the consumer / producer split
+ let dequeue = if cfg!(feature = "nightly") {
+ quote!(#fq.dequeue())
+ } else {
+ quote!((*#fq.as_mut_ptr()).dequeue())
+ };
+
+ (dequeue, quote!((*TQ.as_mut_ptr()).enqueue_unchecked(nr);))
} else {
- let pats = inputs.iter().map(|i| &i.pat).collect::<Vec<_>>();
+ (
+ quote!((#fq { priority }).lock(|fq| fq.split().1.dequeue())),
+ quote!((TQ { priority }).lock(|tq| tq.enqueue_unchecked(nr));),
+ )
+ };
- quote!(#(#pats,)*)
- }
+ quote!(
+ unsafe {
+ use rtfm::Mutex as _;
+
+ let input = #tupled;
+ if let Some(index) = #dequeue {
+ #instants.get_unchecked_mut(usize::from(index)).write(instant);
+
+ #inputs.get_unchecked_mut(usize::from(index)).write(input);
+
+ let nr = rtfm::export::NotReady {
+ instant,
+ index,
+ task: T::#name,
+ };
+
+ #enqueue
+
+ Ok(())
+ } else {
+ Err(input)
+ }
+ }
+ )
+}
+
+/// `u8` -> (unsuffixed) `LitInt`
+fn mk_capacity_literal(capacity: u8) -> LitInt {
+ LitInt::new(u64::from(capacity), IntSuffix::None, Span::call_site())
}
-fn tuple_ty(inputs: &[ArgCaptured]) -> proc_macro2::TokenStream {
+/// e.g. `4u8` -> `U4`
+fn mk_typenum_capacity(capacity: u8, power_of_two: bool) -> proc_macro2::TokenStream {
+ let capacity = if power_of_two {
+ capacity
+ .checked_next_power_of_two()
+ .expect("capacity.next_power_of_two()")
+ } else {
+ capacity
+ };
+
+ let ident = Ident::new(&format!("U{}", capacity), Span::call_site());
+
+ quote!(rtfm::export::consts::#ident)
+}
+
+/// e.g. `foo` -> `foo_INPUTS`
+fn mk_inputs_ident(base: &Ident) -> Ident {
+ Ident::new(&format!("{}_INPUTS", base), Span::call_site())
+}
+
+/// e.g. `foo` -> `foo_INSTANTS`
+fn mk_instants_ident(base: &Ident) -> Ident {
+ Ident::new(&format!("{}_INSTANTS", base), Span::call_site())
+}
+
+/// e.g. `foo` -> `foo_FQ`
+fn mk_fq_ident(base: &Ident) -> Ident {
+ Ident::new(&format!("{}_FQ", base), Span::call_site())
+}
+
+/// e.g. `3` -> `RQ3`
+fn mk_rq_ident(level: u8) -> Ident {
+ Ident::new(&format!("RQ{}", level), Span::call_site())
+}
+
+/// e.g. `3` -> `T3`
+fn mk_t_ident(level: u8) -> Ident {
+ Ident::new(&format!("T{}", level), Span::call_site())
+}
+
+fn mk_spawn_ident(task: &Ident) -> Ident {
+ Ident::new(&format!("spawn_{}", task), Span::call_site())
+}
+
+fn mk_schedule_ident(task: &Ident) -> Ident {
+ Ident::new(&format!("schedule_{}", task), Span::call_site())
+}
+
+// Regroups a task inputs
+//
+// e.g. &[`input: Foo`], &[`mut x: i32`, `ref y: i64`]
+fn regroup_inputs(
+ inputs: &[ArgCaptured],
+) -> (
+ // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`]
+ Vec<proc_macro2::TokenStream>,
+ // tupled e.g. `_0`, `(_0, _1)`
+ proc_macro2::TokenStream,
+ // untupled e.g. &[`_0`], &[`_0`, `_1`]
+ Vec<proc_macro2::TokenStream>,
+ // ty e.g. `Foo`, `(i32, i64)`
+ proc_macro2::TokenStream,
+) {
if inputs.len() == 1 {
let ty = &inputs[0].ty;
- quote!(#ty)
+
+ (
+ vec![quote!(_0: #ty)],
+ quote!(_0),
+ vec![quote!(_0)],
+ quote!(#ty),
+ )
} else {
- let tys = inputs.iter().map(|i| &i.ty).collect::<Vec<_>>();
+ let mut args = vec![];
+ let mut pats = vec![];
+ let mut tys = vec![];
- quote!((#(#tys,)*))
+ for (i, input) in inputs.iter().enumerate() {
+ let i = Ident::new(&format!("_{}", i), Span::call_site());
+ let ty = &input.ty;
+
+ args.push(quote!(#i: #ty));
+
+ pats.push(quote!(#i));
+
+ tys.push(quote!(#ty));
+ }
+
+ let tupled = {
+ let pats = pats.clone();
+ quote!((#(#pats,)*))
+ };
+ let ty = quote!((#(#tys,)*));
+ (args, tupled, pats, ty)
}
}
@@ -2253,13 +2468,22 @@ enum Kind {
impl Kind {
fn ident(&self) -> Ident {
+ let span = Span::call_site();
match self {
- Kind::Init => Ident::new("init", Span::call_site()),
- Kind::Idle => Ident::new("idle", Span::call_site()),
+ Kind::Init => Ident::new("init", span),
+ Kind::Idle => Ident::new("idle", span),
Kind::Task(name) | Kind::Interrupt(name) | Kind::Exception(name) => name.clone(),
}
}
+ fn locals_ident(&self) -> Ident {
+ Ident::new(&format!("{}Locals", self.ident()), Span::call_site())
+ }
+
+ fn resources_ident(&self) -> Ident {
+ Ident::new(&format!("{}Resources", self.ident()), Span::call_site())
+ }
+
fn is_idle(&self) -> bool {
*self == Kind::Idle
}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index c8d9fee1..441d6b5e 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -288,9 +288,9 @@ mod syntax;
pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
// Parse
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(),
Ok(app) => app,
};
@@ -304,5 +304,5 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
let analysis = analyze::app(&app);
// Code generation
- codegen::app(&app, &analysis).into()
+ codegen::app(&input.ident, &app, &analysis).into()
}
diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs
index 228d9588..c6814d5f 100644
--- a/macros/src/syntax.rs
+++ b/macros/src/syntax.rs
@@ -11,8 +11,8 @@ use syn::{
spanned::Spanned,
token::Brace,
ArgCaptured, AttrStyle, Attribute, Expr, FnArg, ForeignItem, Ident, IntSuffix, Item, ItemFn,
- ItemForeignMod, ItemStatic, LitInt, Path, PathArguments, PathSegment, ReturnType, Stmt, Token,
- Type, TypeTuple, Visibility,
+ ItemForeignMod, ItemStatic, LitInt, Pat, Path, PathArguments, ReturnType, Stmt, Token, Type,
+ TypeTuple, Visibility,
};
pub struct AppArgs {
@@ -70,7 +70,7 @@ impl Parse for AppArgs {
pub struct Input {
_const_token: Token![const],
- _ident: Ident,
+ pub ident: Ident,
_colon_token: Token![:],
_ty: TypeTuple,
_eq_token: Token![=],
@@ -94,7 +94,7 @@ impl Parse for Input {
let content;
Ok(Input {
_const_token: input.parse()?,
- _ident: input.parse()?,
+ ident: input.parse()?,
_colon_token: input.parse()?,
_ty: input.parse()?,
_eq_token: input.parse()?,
@@ -435,7 +435,7 @@ pub type FreeInterrupts = BTreeMap<Ident, FreeInterrupt>;
pub struct Idle {
pub args: IdleArgs,
pub attrs: Vec<Attribute>,
- pub unsafety: Option<Token![unsafe]>,
+ pub context: Pat,
pub statics: BTreeMap<Ident, Static>,
pub stmts: Vec<Stmt>,
}
@@ -444,34 +444,29 @@ pub type IdleArgs = InitArgs;
impl Idle {
fn check(args: IdleArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature = item.vis == Visibility::Inherited
- && item.constness.is_none()
- && 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 valid_signature =
+ check_signature(&item) && item.decl.inputs.len() == 1 && is_bottom(&item.decl.output);
let span = item.span();
- if !valid_signature {
- return Err(parse::Error::new(
- span,
- "`idle` must have type signature `[unsafe] fn() -> !`",
- ));
+ if valid_signature {
+ if let Some((context, _)) = check_inputs(item.decl.inputs, "idle") {
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ return Ok(Idle {
+ args,
+ attrs: item.attrs,
+ context,
+ statics: Static::parse(statics)?,
+ stmts,
+ });
+ }
}
- let (statics, stmts) = extract_statics(item.block.stmts);
-
- Ok(Idle {
- args,
- attrs: item.attrs,
- unsafety: item.unsafety,
- statics: Static::parse(statics)?,
- stmts,
- })
+ Err(parse::Error::new(
+ span,
+ "`idle` must have type signature `fn(idle::Context) -> !`",
+ ))
}
}
@@ -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 args: InitArgs,
pub attrs: Vec<Attribute>,
- pub unsafety: Option<Token![unsafe]>,
pub statics: BTreeMap<Ident, Static>,
+ pub context: Pat,
pub stmts: Vec<Stmt>,
- // TODO remove in v0.5.x
- pub assigns: Vec<Assign>,
pub returns_late_resources: bool,
+ pub span: Span,
}
impl Init {
fn check(args: InitArgs, item: ItemFn) -> parse::Result<Self> {
- let mut valid_signature = item.vis == Visibility::Inherited
- && item.constness.is_none()
- && 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();
+ let mut valid_signature = check_signature(&item) && item.decl.inputs.len() == 1;
+
+ const DONT_CARE: bool = false;
let returns_late_resources = match &item.decl.output {
ReturnType::Default => false,
@@ -636,36 +618,25 @@ impl Init {
} else {
valid_signature = false;
- false // don't care
+ DONT_CARE
}
}
- Type::Path(p) => {
- let mut segments = p.path.segments.iter();
- 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)
- {
+ Type::Path(_) => {
+ if is_path(ty, &["init", "LateResources"]) {
// -> init::LateResources
true
} else {
valid_signature = false;
- false // don't care
+ DONT_CARE
}
}
_ => {
valid_signature = false;
- false // don't care
+ DONT_CARE
}
}
}
@@ -673,29 +644,26 @@ impl Init {
let span = item.span();
- if !valid_signature {
- return Err(parse::Error::new(
- span,
- "`init` must have type signature `[unsafe] fn() [-> init::LateResources]`",
- ));
+ if valid_signature {
+ if let Some((context, _)) = check_inputs(item.decl.inputs, "init") {
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ 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);
- let (stmts, assigns) = if returns_late_resources {
- (stmts, vec![])
- } else {
- extract_assignments(stmts)
- };
-
- Ok(Init {
- args,
- attrs: item.attrs,
- unsafety: item.unsafety,
- statics: Static::parse(statics)?,
- stmts,
- assigns,
- returns_late_resources,
- })
+ Err(parse::Error::new(
+ span,
+ "`init` must have type signature `fn(init::Context) [-> init::LateResources]`",
+ ))
}
}
@@ -725,8 +693,8 @@ impl Default for Args {
pub struct Exception {
pub args: ExceptionArgs,
pub attrs: Vec<Attribute>,
- pub unsafety: Option<Token![unsafe]>,
pub statics: BTreeMap<Ident, Static>,
+ pub context: Pat,
pub stmts: Vec<Stmt>,
}
@@ -770,61 +738,67 @@ impl Parse for ExceptionArgs {
impl Exception {
fn check(args: ExceptionArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature = item.vis == Visibility::Inherited
- && item.constness.is_none()
- && 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 {
- return Err(parse::Error::new(
- item.span(),
- "`exception` handlers must have type signature `[unsafe] fn()`",
- ));
- }
+ let valid_signature =
+ check_signature(&item) && item.decl.inputs.len() == 1 && is_unit(&item.decl.output);
- let span = item.ident.span();
- match &*args.binds.as_ref().unwrap_or(&item.ident).to_string() {
- "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
- | "DebugMonitor" | "PendSV" => {} // OK
- "SysTick" => {
- if cfg!(feature = "timer-queue") {
- return Err(parse::Error::new(
+ let span = item.span();
+
+ let name = item.ident.to_string();
+ if valid_signature {
+ if let Some((context, _)) = check_inputs(item.decl.inputs, &name) {
+ let span = item.ident.span();
+ match &*args
+ .binds
+ .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,
- "the `SysTick` exception can't be used because it's used by \
- the runtime when the `timer-queue` feature is enabled",
+ "only exceptions with configurable priority can be used as hardware tasks",
));
+ }
}
- }
- _ => {
- return Err(parse::Error::new(
- span,
- "only exceptions with configurable priority can be used as hardware tasks",
- ));
+
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ return Ok(Exception {
+ args,
+ attrs: item.attrs,
+ statics: Static::parse(statics)?,
+ context,
+ stmts,
+ });
}
}
- let (statics, stmts) = extract_statics(item.block.stmts);
-
- Ok(Exception {
- args,
- attrs: item.attrs,
- unsafety: item.unsafety,
- statics: Static::parse(statics)?,
- stmts,
- })
+ Err(parse::Error::new(
+ span,
+ &format!(
+ "this `exception` handler must have type signature `fn({}::Context)`",
+ name
+ ),
+ ))
}
}
pub struct Interrupt {
pub args: InterruptArgs,
pub attrs: Vec<Attribute>,
- pub unsafety: Option<Token![unsafe]>,
pub statics: BTreeMap<Ident, Static>,
+ pub context: Pat,
pub stmts: Vec<Stmt>,
}
@@ -832,49 +806,47 @@ pub type InterruptArgs = ExceptionArgs;
impl Interrupt {
fn check(args: InterruptArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature = item.vis == Visibility::Inherited
- && item.constness.is_none()
- && 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 valid_signature =
+ check_signature(&item) && item.decl.inputs.len() == 1 && is_unit(&item.decl.output);
let span = item.span();
- if !valid_signature {
- return Err(parse::Error::new(
- span,
- "`interrupt` handlers must have type signature `[unsafe] fn()`",
- ));
- }
+ let name = item.ident.to_string();
+ if valid_signature {
+ if let Some((context, _)) = check_inputs(item.decl.inputs, &name) {
+ 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() {
- "init" | "idle" | "resources" => {
- return Err(parse::Error::new(
- span,
- "`interrupt` handlers can NOT be named `idle`, `init` or `resources`",
- ));
+ let (statics, stmts) = extract_statics(item.block.stmts);
+
+ return Ok(Interrupt {
+ args,
+ attrs: item.attrs,
+ statics: Static::parse(statics)?,
+ context,
+ stmts,
+ });
}
- _ => {}
}
- let (statics, stmts) = extract_statics(item.block.stmts);
-
- Ok(Interrupt {
- args,
- attrs: item.attrs,
- unsafety: item.unsafety,
- statics: Static::parse(statics)?,
- stmts,
- })
+ Err(parse::Error::new(
+ span,
+ format!(
+ "this `interrupt` handler must have type signature `fn({}::Context)`",
+ name
+ ),
+ ))
}
}
pub struct Resource {
- pub singleton: bool,
pub cfgs: Vec<Attribute>,
pub attrs: Vec<Attribute>,
pub mutability: Option<Token![mut]>,
@@ -883,7 +855,7 @@ pub struct Resource {
}
impl Resource {
- fn check(mut item: ItemStatic) -> parse::Result<Resource> {
+ fn check(item: ItemStatic) -> parse::Result<Resource> {
if item.vis != Visibility::Inherited {
return Err(parse::Error::new(
item.span(),
@@ -896,19 +868,9 @@ impl Resource {
_ => 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);
Ok(Resource {
- singleton: pos.is_some(),
cfgs,
attrs,
mutability: item.mutability,
@@ -1177,66 +1139,61 @@ pub struct Task {
pub args: TaskArgs,
pub cfgs: Vec<Attribute>,
pub attrs: Vec<Attribute>,
- pub unsafety: Option<Token![unsafe]>,
pub inputs: Vec<ArgCaptured>,
+ pub context: Pat,
pub statics: BTreeMap<Ident, Static>,
pub stmts: Vec<Stmt>,
}
impl Task {
fn check(args: TaskArgs, item: ItemFn) -> parse::Result<Self> {
- let valid_signature = item.vis == Visibility::Inherited
- && item.constness.is_none()
- && 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 valid_signature =
+ check_signature(&item) && !item.decl.inputs.is_empty() && is_unit(&item.decl.output);
let span = item.span();
- if !valid_signature {
- return Err(parse::Error::new(
- span,
- "`task` handlers must have type signature `[unsafe] fn(..)`",
- ));
- }
+ let name = item.ident.to_string();
+ if valid_signature {
+ if let Some((context, rest)) = check_inputs(item.decl.inputs, &name) {
+ 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![];
- for input in item.decl.inputs {
- if let FnArg::Captured(capture) = input {
- inputs.push(capture);
- } else {
- return Err(parse::Error::new(
- span,
- "inputs must be named arguments (e.f. `foo: u32`) and not include `self`",
- ));
- }
- }
+ match &*name {
+ "init" | "idle" | "resources" => {
+ return Err(parse::Error::new(
+ span,
+ "`task` handlers can NOT be named `idle`, `init` or `resources`",
+ ));
+ }
+ _ => {}
+ }
- match &*item.ident.to_string() {
- "init" | "idle" | "resources" => {
- return Err(parse::Error::new(
- span,
- "`task` handlers can NOT be named `idle`, `init` or `resources`",
- ));
+ let (cfgs, attrs) = extract_cfgs(item.attrs);
+ return Ok(Task {
+ args,
+ cfgs,
+ attrs,
+ inputs,
+ context,
+ statics: Static::parse(statics)?,
+ stmts,
+ });
}
- _ => {}
}
- let (cfgs, attrs) = extract_cfgs(item.attrs);
- Ok(Task {
- args,
- cfgs,
- attrs,
- unsafety: item.unsafety,
- inputs,
- statics: Static::parse(statics)?,
- stmts,
- })
+ Err(parse::Error::new(
+ span,
+ &format!(
+ "this `task` handler must have type signature `fn({}::Context, ..)`",
+ name
+ ),
+ ))
}
}
@@ -1335,38 +1292,69 @@ fn extract_statics(stmts: Vec<Stmt>) -> (Statics, Vec<Stmt>) {
(statics, stmts)
}
-// TODO remove in v0.5.x
-fn extract_assignments(stmts: Vec<Stmt>) -> (Vec<Stmt>, Vec<Assign>) {
- let mut istmts = stmts.into_iter().rev();
-
- let mut assigns = vec![];
- let mut stmts = vec![];
- while let Some(stmt) = istmts.next() {
- match stmt {
- Stmt::Semi(Expr::Assign(assign), semi) => {
- if let Expr::Path(ref expr) = *assign.left {
- if expr.path.segments.len() == 1 {
- assigns.push(Assign {
- attrs: assign.attrs,
- left: expr.path.segments[0].ident.clone(),
- right: assign.right,
- });
- continue;
- }
- }
+// checks that the list of arguments has the form `#pat: #name::Context, (..)`
+//
+// 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();
+
+ match inputs.next() {
+ Some(FnArg::Captured(first)) => {
+ if is_path(&first.ty, &[name, "Context"]) {
+ let rest = inputs
+ .map(|arg| match arg {
+ FnArg::Captured(arg) => Ok(arg),
+ _ => Err(arg),
+ })
+ .collect::<Result<Vec<_>, _>>();
- stmts.push(Stmt::Semi(Expr::Assign(assign), semi));
- }
- _ => {
- stmts.push(stmt);
- break;
+ Some((first.pat, rest))
+ } else {
+ None
}
}
+
+ _ => 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()
+}
+
+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)
+ }
- (stmts.into_iter().rev().collect(), assigns)
+ _ => false,
+ }
}
fn is_bottom(ty: &ReturnType) -> bool {
diff --git a/src/export.rs b/src/export.rs
index cf7293b6..93a92fcf 100644
--- a/src/export.rs
+++ b/src/export.rs
@@ -1,7 +1,5 @@
//! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
-#[cfg(not(feature = "nightly"))]
-use core::ptr;
use core::{cell::Cell, u8};
#[cfg(armv7m)]
@@ -14,25 +12,31 @@ pub use heapless::consts;
use heapless::spsc::{Queue, SingleCore};
#[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 ReadyQueue<T, N> = Queue<(T, u8), N, usize, SingleCore>;
+pub type FreeQueue<N> = Queue<u8, N, u8, SingleCore>;
+pub type ReadyQueue<T, N> = Queue<(T, u8), N, u8, SingleCore>;
#[cfg(armv7m)]
#[inline(always)]
-pub fn run<F>(f: F)
+pub fn run<F>(priority: u8, f: F)
where
F: FnOnce(),
{
- let initial = basepri::read();
- f();
- unsafe { basepri::write(initial) }
+ if priority == 1 {
+ // if the priority of this interrupt is `1` then BASEPRI can only be `0`
+ f();
+ unsafe { basepri::write(0) }
+ } else {
+ let initial = basepri::read();
+ f();
+ unsafe { basepri::write(initial) }
+ }
}
#[cfg(not(armv7m))]
#[inline(always)]
-pub fn run<F>(f: F)
+pub fn run<F>(_priority: u8, f: F)
where
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)]
fn set(&self, value: u8) {
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> {
- // we newtype so the end-user doesn't need `#![feature(maybe_uninit)]` in their code
inner: core::mem::MaybeUninit<T>,
}
-#[cfg(feature = "nightly")]
impl<T> MaybeUninit<T> {
pub const fn uninit() -> Self {
MaybeUninit {
@@ -86,61 +89,12 @@ impl<T> MaybeUninit<T> {
self.inner.as_mut_ptr()
}
- pub fn write(&mut self, value: T) -> &mut T {
- 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 unsafe fn read(&self) -> T {
+ self.inner.read()
}
- 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)) }
+ pub fn write(&mut self, value: T) -> &mut T {
+ self.inner.write(value)
}
}
@@ -160,19 +114,16 @@ where
#[cfg(armv7m)]
#[inline(always)]
-pub unsafe fn claim<T, R, F>(
+pub unsafe fn lock<T, R>(
ptr: *mut T,
priority: &Priority,
ceiling: u8,
nvic_prio_bits: u8,
- f: F,
-) -> R
-where
- F: FnOnce(&mut T) -> R,
-{
+ f: impl FnOnce(&mut T) -> R,
+) -> R {
let current = priority.get();
- if priority.get() < ceiling {
+ if current < ceiling {
if ceiling == (1 << nvic_prio_bits) {
priority.set(u8::MAX);
let r = interrupt::free(|_| f(&mut *ptr));
@@ -193,19 +144,16 @@ where
#[cfg(not(armv7m))]
#[inline(always)]
-pub unsafe fn claim<T, R, F>(
+pub unsafe fn lock<T, R>(
ptr: *mut T,
priority: &Priority,
ceiling: u8,
_nvic_prio_bits: u8,
- f: F,
-) -> R
-where
- F: FnOnce(&mut T) -> R,
-{
+ f: impl FnOnce(&mut T) -> R,
+) -> R {
let current = priority.get();
- if priority.get() < ceiling {
+ if current < ceiling {
priority.set(u8::MAX);
let r = interrupt::free(|_| f(&mut *ptr));
priority.set(current);
@@ -215,8 +163,7 @@ where
}
}
-#[cfg(armv7m)]
#[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)
}
diff --git a/src/lib.rs b/src/lib.rs
index b0bf6689..acd8d433 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,8 @@
//! 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
//! library is `rtfm`.
//!
@@ -7,7 +10,7 @@
//!
//! 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
//! of the framework.
@@ -16,7 +19,7 @@
//!
//! # 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.
//!
//! # Semantic Versioning
@@ -36,12 +39,11 @@
//! [`Instant`]: struct.Instant.html
//! [`Duration`]: struct.Duration.html
//!
-//! - `nightly`. Enabling this opt-in feature makes RTFM internally use the unstable
-//! `core::mem::MaybeUninit` API and unstable `const_fn` language feature to reduce static memory
-//! usage, runtime overhead and initialization overhead. This feature requires a nightly compiler
-//! and may stop working at any time!
+//! - `nightly`. Enabling this opt-in feature makes RTFM internally use the unstable `const_fn`
+//! language feature to reduce static memory usage, runtime overhead and initialization overhead.
+//! This feature requires a nightly compiler and may stop working at any time!
-#![cfg_attr(feature = "nightly", feature(maybe_uninit))]
+#![feature(maybe_uninit)]
#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]
@@ -132,7 +134,7 @@ pub struct Instant(i32);
impl Instant {
/// IMPLEMENTATION DETAIL. DO NOT USE
#[doc(hidden)]
- pub fn artificial(timestamp: i32) -> Self {
+ pub unsafe fn artificial(timestamp: i32) -> Self {
Instant(timestamp)
}
@@ -290,9 +292,7 @@ pub trait Mutex {
type T;
/// Creates a critical section and grants temporary access to the protected data
- fn lock<R, F>(&mut self, f: F) -> R
- where
- F: FnOnce(&mut Self::T) -> R;
+ fn lock<R>(&mut self, f: impl FnOnce(&mut Self::T) -> R) -> R;
}
impl<'a, M> Mutex for &'a mut M
@@ -301,10 +301,7 @@ where
{
type T = M::T;
- fn lock<R, F>(&mut self, f: F) -> R
- where
- F: FnOnce(&mut Self::T) -> R,
- {
+ fn lock<R>(&mut self, f: impl FnOnce(&mut M::T) -> R) -> R {
(**self).lock(f)
}
}
@@ -317,10 +314,7 @@ pub struct Exclusive<'a, T>(pub &'a mut T);
impl<'a, T> Mutex for Exclusive<'a, T> {
type T = T;
- fn lock<R, F>(&mut self, f: F) -> R
- where
- F: FnOnce(&mut Self::T) -> R,
- {
+ fn lock<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
f(self.0)
}
}
diff --git a/src/tq.rs b/src/tq.rs
index 8d520518..8ca1bd3f 100644
--- a/src/tq.rs
+++ b/src/tq.rs
@@ -3,7 +3,7 @@ use core::cmp::{self, Ordering};
use cortex_m::peripheral::{SCB, SYST};
use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
-use crate::{Instant, Mutex};
+use crate::Instant;
pub struct TimerQueue<T, N>
where
@@ -43,11 +43,39 @@ where
}
// set SysTick pending
- (*SCB::ptr()).icsr.write(1 << 26);
+ SCB::set_pendst();
}
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>
@@ -87,49 +115,3 @@ where
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;
- }
- }
-}
diff --git a/tests/cfail/cfg-resources.rs b/tests/cfail/cfg-resources.rs
index dee1485b..5e20c4de 100644
--- a/tests/cfail/cfg-resources.rs
+++ b/tests/cfail/cfg-resources.rs
@@ -30,35 +30,35 @@ const APP: () = {
static S3: u32 = 0;
#[init(resources = [O1, O4, O5, O6, S3])]
- fn init() {
- resources.O1; //~ ERROR no field `O1`
- resources.O4; //~ ERROR no field `O4`
- resources.O5; //~ ERROR no field `O5`
- resources.O6; //~ ERROR no field `O6`
- resources.S3; //~ ERROR no field `S3`
+ fn init(c: init::Context) {
+ c.resources.O1; //~ ERROR no field `O1`
+ c.resources.O4; //~ ERROR no field `O4`
+ c.resources.O5; //~ ERROR no field `O5`
+ c.resources.O6; //~ ERROR no field `O6`
+ c.resources.S3; //~ ERROR no field `S3`
}
#[idle(resources = [O2, O4, S1, S3])]
- fn idle() -> ! {
- resources.O2; //~ ERROR no field `O2`
- resources.O4; //~ ERROR no field `O4`
- resources.S1; //~ ERROR no field `S1`
- resources.S3; //~ ERROR no field `S3`
+ fn idle(c: idle::Context) -> ! {
+ c.resources.O2; //~ ERROR no field `O2`
+ c.resources.O4; //~ ERROR no field `O4`
+ c.resources.S1; //~ ERROR no field `S1`
+ c.resources.S3; //~ ERROR no field `S3`
loop {}
}
#[interrupt(resources = [O3, S1, S2, S3])]
- fn UART0() {
- resources.O3; //~ ERROR no field `O3`
- resources.S1; //~ ERROR no field `S1`
- resources.S2; //~ ERROR no field `S2`
- resources.S3; //~ ERROR no field `S3`
+ fn UART0(c: UART0::Context) {
+ c.resources.O3; //~ ERROR no field `O3`
+ c.resources.S1; //~ ERROR no field `S1`
+ c.resources.S2; //~ ERROR no field `S2`
+ c.resources.S3; //~ ERROR no field `S3`
}
#[interrupt(resources = [S2, O5])]
- fn UART1() {
- resources.S2; //~ ERROR no field `S2`
- resources.O5; //~ ERROR no field `O5`
+ fn UART1(c: UART1::Context) {
+ c.resources.S2; //~ ERROR no field `S2`
+ c.resources.O5; //~ ERROR no field `O5`
}
};
diff --git a/tests/cfail/cfg-static.rs b/tests/cfail/cfg-static.rs
index 0d27e533..91465a1e 100644
--- a/tests/cfail/cfg-static.rs
+++ b/tests/cfail/cfg-static.rs
@@ -10,7 +10,7 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
+ fn init(_: init::Context) {
#[cfg(never)]
static mut FOO: u32 = 0;
@@ -18,7 +18,7 @@ const APP: () = {
}
#[idle]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
#[cfg(never)]
static mut FOO: u32 = 0;
@@ -28,7 +28,7 @@ const APP: () = {
}
#[exception]
- fn SVCall() {
+ fn SVCall(_: SVCall::Context) {
#[cfg(never)]
static mut FOO: u32 = 0;
@@ -36,7 +36,7 @@ const APP: () = {
}
#[interrupt]
- fn UART0() {
+ fn UART0(_: UART0::Context) {
#[cfg(never)]
static mut FOO: u32 = 0;
@@ -44,7 +44,7 @@ const APP: () = {
}
#[task]
- fn foo() {
+ fn foo(_: foo::Context) {
#[cfg(never)]
static mut FOO: u32 = 0;
diff --git a/tests/cfail/duplicate-args-2.rs b/tests/cfail/duplicate-args-2.rs
index 1a196e99..5bef79b5 100644
--- a/tests/cfail/duplicate-args-2.rs
+++ b/tests/cfail/duplicate-args-2.rs
@@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[task(
priority = 1,
priority = 2, //~ ERROR argument appears more than once
)]
- fn foo() {}
+ fn foo(_: foo::Context) {}
extern "C" {
fn UART0();
diff --git a/tests/cfail/duplicate-args.rs b/tests/cfail/duplicate-args.rs
index a946bae2..6938cd0d 100644
--- a/tests/cfail/duplicate-args.rs
+++ b/tests/cfail/duplicate-args.rs
@@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[task(
capacity = 1,
capacity = 2, //~ ERROR argument appears more than once
)]
- fn foo() {}
+ fn foo(_: foo::Context) {}
extern "C" {
fn UART0();
diff --git a/tests/cfail/early-return-2.rs b/tests/cfail/early-return-2.rs
deleted file mode 100644
index bf867e07..00000000
--- a/tests/cfail/early-return-2.rs
+++ /dev/null
@@ -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
- }
- }
-};
diff --git a/tests/cfail/early-return.rs b/tests/cfail/early-return.rs
deleted file mode 100644
index fb695aac..00000000
--- a/tests/cfail/early-return.rs
+++ /dev/null
@@ -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
- }
- }
-};
diff --git a/tests/cfail/exception-divergent.rs b/tests/cfail/exception-divergent.rs
index 692a57c7..3fe9a365 100644
--- a/tests/cfail/exception-divergent.rs
+++ b/tests/cfail/exception-divergent.rs
@@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[exception]
- fn SVCall() -> ! {
- //~^ ERROR `exception` handlers must have type signature `[unsafe] fn()`
+ fn SVCall(_: SVCall::Context) -> ! {
+ //~^ ERROR this `exception` handler must have type signature `fn(SVCall::Context)`
loop {}
}
};
diff --git a/tests/cfail/exception-input.rs b/tests/cfail/exception-input.rs
index cb0711ce..d1363fe5 100644
--- a/tests/cfail/exception-input.rs
+++ b/tests/cfail/exception-input.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[exception]
- fn SVCall(undef: u32) {
- //~^ ERROR `exception` handlers must have type signature `[unsafe] fn()`
+ fn SVCall(_: SVCall::Context, undef: u32) {
+ //~^ ERROR this `exception` handler must have type signature `fn(SVCall::Context)`
}
};
diff --git a/tests/cfail/exception-invalid.rs b/tests/cfail/exception-invalid.rs
index 0a7fb520..4bb8f1ec 100644
--- a/tests/cfail/exception-invalid.rs
+++ b/tests/cfail/exception-invalid.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[exception]
- fn NonMaskableInt() {
+ fn NonMaskableInt(_: NonMaskableInt::Context) {
//~^ ERROR only exceptions with configurable priority can be used as hardware tasks
}
};
diff --git a/tests/cfail/exception-output.rs b/tests/cfail/exception-output.rs
index 758dbdd7..8f672985 100644
--- a/tests/cfail/exception-output.rs
+++ b/tests/cfail/exception-output.rs
@@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[exception]
- fn SVCall() -> u32 {
- //~^ ERROR `exception` handlers must have type signature `[unsafe] fn()`
+ fn SVCall(_: SVCall::Context) -> u32 {
+ //~^ ERROR this `exception` handler must have type signature `fn(SVCall::Context)`
0
}
};
diff --git a/tests/cfail/exception-sys-tick.rs b/tests/cfail/exception-sys-tick.rs
index 69d73dbc..d5eae20b 100644
--- a/tests/cfail/exception-sys-tick.rs
+++ b/tests/cfail/exception-sys-tick.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[exception]
- fn SysTick() {
+ fn SysTick(_: SysTick::Context) {
//~^ ERROR the `SysTick` exception can't be used because it's used by the runtime
}
};
diff --git a/tests/cfail/idle-input.rs b/tests/cfail/idle-input.rs
index 5095977e..feb83e8b 100644
--- a/tests/cfail/idle-input.rs
+++ b/tests/cfail/idle-input.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[idle]
- fn idle(undef: u32) {
- //~^ ERROR `idle` must have type signature `[unsafe] fn() -> !`
+ fn idle(_: idle::Context, undef: u32) {
+ //~^ ERROR `idle` must have type signature `fn(idle::Context) -> !`
}
};
diff --git a/tests/cfail/idle-not-divergent.rs b/tests/cfail/idle-not-divergent.rs
index e90eff08..505fba14 100644
--- a/tests/cfail/idle-not-divergent.rs
+++ b/tests/cfail/idle-not-divergent.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[idle]
- fn idle() {
- //~^ ERROR `idle` must have type signature `[unsafe] fn() -> !`
+ fn idle(_: idle::Context) {
+ //~^ ERROR `idle` must have type signature `fn(idle::Context) -> !`
}
};
diff --git a/tests/cfail/init-divergent.rs b/tests/cfail/init-divergent.rs
index 54813d47..0e779ffc 100644
--- a/tests/cfail/init-divergent.rs
+++ b/tests/cfail/init-divergent.rs
@@ -10,8 +10,8 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() -> ! {
- //~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]`
+ fn init(_: init::Context) -> ! {
+ //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
loop {}
}
};
diff --git a/tests/cfail/init-input.rs b/tests/cfail/init-input.rs
index 3bf0cadf..9063efe3 100644
--- a/tests/cfail/init-input.rs
+++ b/tests/cfail/init-input.rs
@@ -10,7 +10,7 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init(undef: u32) {
- //~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]`
+ fn init(_: init::Context, undef: u32) {
+ //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
}
};
diff --git a/tests/cfail/init-not-send.rs b/tests/cfail/init-not-send.rs
index 3ac495f5..5a33fac7 100644
--- a/tests/cfail/init-not-send.rs
+++ b/tests/cfail/init-not-send.rs
@@ -1,6 +1,5 @@
//! This is equivalent to the `late-not-send` cfail test
-#![feature(extern_crate_item_prelude)] // ???
#![no_main]
#![no_std]
@@ -21,10 +20,10 @@ const APP: () = {
static mut X: Option<NotSend> = None;
#[init(resources = [X])]
- fn init() {
- *resources.X = Some(NotSend { _0: PhantomData })
+ fn init(c: init::Context) {
+ *c.resources.X = Some(NotSend { _0: PhantomData })
}
#[interrupt(resources = [X])]
- fn UART0() {}
+ fn UART0(_: UART0::Context) {}
};
diff --git a/tests/cfail/init-output.rs b/tests/cfail/init-output.rs
index 414a35a8..f88d5340 100644
--- a/tests/cfail/init-output.rs
+++ b/tests/cfail/init-output.rs
@@ -10,8 +10,8 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() -> u32 {
- //~^ ERROR `init` must have type signature `[unsafe] fn() [-> init::LateResources]`
+ fn init(_: init::Context) -> u32 {
+ //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
0
}
};
diff --git a/tests/cfail/insufficient-free-interrupts.rs b/tests/cfail/insufficient-free-interrupts.rs
index baa2582b..7148fbf3 100644
--- a/tests/cfail/insufficient-free-interrupts.rs
+++ b/tests/cfail/insufficient-free-interrupts.rs
@@ -10,8 +10,8 @@ use rtfm::app;
#[app(device = lm3s6965)] //~ ERROR 1 free interrupt (`extern { .. }`) is required
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[task]
- fn foo() {}
+ fn foo(_: foo::Context) {}
};
diff --git a/tests/cfail/interrupt-divergent.rs b/tests/cfail/interrupt-divergent.rs
index 4a015330..b67601ee 100644
--- a/tests/cfail/interrupt-divergent.rs
+++ b/tests/cfail/interrupt-divergent.rs
@@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[interrupt]
- fn UART0() -> ! {
- //~^ ERROR `interrupt` handlers must have type signature `[unsafe] fn()`
+ fn UART0(_: UART0::Context) -> ! {
+ //~^ ERROR this `interrupt` handler must have type signature `fn(UART0::Context)`
loop {}
}
};
diff --git a/tests/cfail/interrupt-input.rs b/tests/cfail/interrupt-input.rs
index d0240f4e..f11b2d39 100644
--- a/tests/cfail/interrupt-input.rs
+++ b/tests/cfail/interrupt-input.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[interrupt]
- fn UART0(undef: u32) {
- //~^ ERROR `interrupt` handlers must have type signature `[unsafe] fn()`
+ fn UART0(_: UART0::Context, undef: u32) {
+ //~^ ERROR this `interrupt` handler must have type signature `fn(UART0::Context)`
}
};
diff --git a/tests/cfail/interrupt-output.rs b/tests/cfail/interrupt-output.rs
index 37cd7c21..69e4957f 100644
--- a/tests/cfail/interrupt-output.rs
+++ b/tests/cfail/interrupt-output.rs
@@ -10,11 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[interrupt]
- fn UART0() -> u32 {
- //~^ ERROR `interrupt` handlers must have type signature `[unsafe] fn()`
+ fn UART0(_: UART0::Context) -> u32 {
+ //~^ ERROR this `interrupt` handler must have type signature `fn(UART0::Context)`
0
}
};
diff --git a/tests/cfail/late-assigned-to-init.rs b/tests/cfail/late-assigned-to-init.rs
index 70a361c1..00d6c8ce 100644
--- a/tests/cfail/late-assigned-to-init.rs
+++ b/tests/cfail/late-assigned-to-init.rs
@@ -12,5 +12,5 @@ const APP: () = {
static mut X: u32 = ();
#[init(resources = [X])] //~ ERROR late resources can NOT be assigned to `init`
- fn init() {}
+ fn init(_: init::Context) {}
};
diff --git a/tests/cfail/late-not-send.rs b/tests/cfail/late-not-send.rs
index eb3048d9..04a4af15 100644
--- a/tests/cfail/late-not-send.rs
+++ b/tests/cfail/late-not-send.rs
@@ -1,7 +1,6 @@
//! `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
-#![feature(extern_crate_item_prelude)] // ???
#![no_main]
#![no_std]
@@ -22,12 +21,12 @@ const APP: () = {
static mut X: NotSend = ();
#[init]
- fn init() -> init::LateResources {
+ fn init(_: init::Context) -> init::LateResources {
init::LateResources {
X: NotSend { _0: PhantomData },
}
}
#[interrupt(resources = [X])]
- fn UART0() {}
+ fn UART0(_: UART0::Context) {}
};
diff --git a/tests/cfail/needs-send.rs b/tests/cfail/needs-send.rs
index 7e3ca306..8dc9707f 100644
--- a/tests/cfail/needs-send.rs
+++ b/tests/cfail/needs-send.rs
@@ -1,4 +1,3 @@
-#![feature(extern_crate_item_prelude)] // ???
#![no_main]
#![no_std]
@@ -19,10 +18,10 @@ unsafe impl Sync for NotSend {}
#[app(device = lm3s6965)] //~ ERROR cannot be sent between threads safely
const APP: () = {
#[init(spawn = [foo])]
- fn init() {}
+ fn init(_: init::Context) {}
#[task]
- fn foo(_x: NotSend) {}
+ fn foo(_: foo::Context, _x: NotSend) {}
extern "C" {
fn UART0();
diff --git a/tests/cfail/needs-sync.rs b/tests/cfail/needs-sync.rs
index f25f91a2..6025e7d5 100644
--- a/tests/cfail/needs-sync.rs
+++ b/tests/cfail/needs-sync.rs
@@ -1,4 +1,3 @@
-#![feature(extern_crate_item_prelude)] // ???
#![no_main]
#![no_std]
@@ -21,13 +20,13 @@ const APP: () = {
static X: NotSync = NotSync { _0: PhantomData };
#[init(spawn = [foo])]
- fn init() {}
+ fn init(_: init::Context) {}
#[task(priority = 1, resources = [X])]
- fn foo() {}
+ fn foo(_: foo::Context) {}
#[task(priority = 2, resources = [X])]
- fn bar() {}
+ fn bar(_: bar::Context) {}
extern "C" {
fn UART0();
diff --git a/tests/cfail/priority-too-high.rs b/tests/cfail/priority-too-high.rs
index ec324014..817462a3 100644
--- a/tests/cfail/priority-too-high.rs
+++ b/tests/cfail/priority-too-high.rs
@@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)] //~ error evaluation of constant value failed
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
// OK, this is the maximum priority supported by the device
#[interrupt(priority = 8)]
- fn UART0() {}
+ fn UART0(_: UART0::Context) {}
// this value is too high!
#[interrupt(priority = 9)]
- fn UART1() {}
+ fn UART1(_: UART1::Context) {}
};
diff --git a/tests/cfail/priority-too-low.rs b/tests/cfail/priority-too-low.rs
index 6dcbfd63..361156df 100644
--- a/tests/cfail/priority-too-low.rs
+++ b/tests/cfail/priority-too-low.rs
@@ -10,13 +10,13 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
// OK, this is the minimum priority that tasks can have
#[interrupt(priority = 1)]
- fn UART0() {}
+ fn UART0(_: UART0::Context) {}
// this value is too low!
#[interrupt(priority = 0)] //~ error this literal must be in the range 1...255
- fn UART1() {}
+ fn UART1(_: UART1::Context) {}
};
diff --git a/tests/cfail/resource-not-declared.rs b/tests/cfail/resource-not-declared.rs
index f6d08a65..a37be42d 100644
--- a/tests/cfail/resource-not-declared.rs
+++ b/tests/cfail/resource-not-declared.rs
@@ -10,5 +10,5 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init(resources = [X])] //~ ERROR this resource has NOT been declared
- fn init() {}
+ fn init(_: init::Context) {}
};
diff --git a/tests/cfail/resource-pub.rs b/tests/cfail/resource-pub.rs
index 970fc6cc..3fb21f46 100644
--- a/tests/cfail/resource-pub.rs
+++ b/tests/cfail/resource-pub.rs
@@ -13,5 +13,5 @@ const APP: () = {
//~^ ERROR resources must have inherited / private visibility
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
};
diff --git a/tests/cfail/task-divergent.rs b/tests/cfail/task-divergent.rs
index 3822d754..577f0e06 100644
--- a/tests/cfail/task-divergent.rs
+++ b/tests/cfail/task-divergent.rs
@@ -5,16 +5,14 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::app;
-
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[task]
- fn foo() -> ! {
- //~^ ERROR `task` handlers must have type signature `[unsafe] fn(..)`
+ fn foo(_: foo::Context) -> ! {
+ //~^ ERROR this `task` handler must have type signature `fn(foo::Context, ..)`
loop {}
}
diff --git a/tests/cfail/task-idle.rs b/tests/cfail/task-idle.rs
index 62d927b9..963bf1ee 100644
--- a/tests/cfail/task-idle.rs
+++ b/tests/cfail/task-idle.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[task]
- fn idle() {
+ fn idle(_: idle::Context) {
//~^ ERROR `task` handlers can NOT be named `idle`, `init` or `resources`
}
diff --git a/tests/cfail/task-not-declared.rs b/tests/cfail/task-not-declared.rs
index 3e6d87c4..04309f59 100644
--- a/tests/cfail/task-not-declared.rs
+++ b/tests/cfail/task-not-declared.rs
@@ -10,5 +10,5 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [X])] //~ ERROR this task has NOT been declared
- fn init() {}
+ fn init(_: init::Context) {}
};
diff --git a/tests/cfail/unsafe-exception.rs b/tests/cfail/unsafe-exception.rs
new file mode 100644
index 00000000..353194a5
--- /dev/null
+++ b/tests/cfail/unsafe-exception.rs
@@ -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)`
+};
diff --git a/tests/cfail/unsafe-idle.rs b/tests/cfail/unsafe-idle.rs
new file mode 100644
index 00000000..fab1b0f1
--- /dev/null
+++ b/tests/cfail/unsafe-idle.rs
@@ -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 {}
+ }
+};
diff --git a/tests/cfail/late-uninit.rs b/tests/cfail/unsafe-init.rs
index 55122ed7..d8bb5605 100644
--- a/tests/cfail/late-uninit.rs
+++ b/tests/cfail/unsafe-init.rs
@@ -1,5 +1,3 @@
-// TODO remove in v0.5.x
-
#![no_main]
#![no_std]
@@ -11,8 +9,7 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
- static mut X: u32 = (); //~ ERROR late resources MUST be initialized at the end of `init`
-
#[init]
- fn init() {}
+ unsafe fn init(_: init::Context) {}
+ //~^ ERROR `init` must have type signature `fn(init::Context) [-> init::LateResources]`
};
diff --git a/tests/cfail/unsafe-interrupt.rs b/tests/cfail/unsafe-interrupt.rs
new file mode 100644
index 00000000..93225edf
--- /dev/null
+++ b/tests/cfail/unsafe-interrupt.rs
@@ -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)`
+};
diff --git a/tests/cfail/unsafe-task.rs b/tests/cfail/unsafe-task.rs
new file mode 100644
index 00000000..58c4d70c
--- /dev/null
+++ b/tests/cfail/unsafe-task.rs
@@ -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();
+ }
+};
diff --git a/tests/cfail/used-free-interrupt-2.rs b/tests/cfail/used-free-interrupt-2.rs
index 616d308d..ba9424fd 100644
--- a/tests/cfail/used-free-interrupt-2.rs
+++ b/tests/cfail/used-free-interrupt-2.rs
@@ -10,10 +10,10 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[interrupt(binds = UART0)] //~ ERROR free interrupts (`extern { .. }`) can't be used as interrupt handlers
- fn foo() {}
+ fn foo(_: foo::Context) {}
extern "C" {
fn UART0();
diff --git a/tests/cfail/used-free-interrupt.rs b/tests/cfail/used-free-interrupt.rs
index 78ae5407..1a56741b 100644
--- a/tests/cfail/used-free-interrupt.rs
+++ b/tests/cfail/used-free-interrupt.rs
@@ -10,10 +10,11 @@ use rtfm::app;
#[app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[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" {
fn UART0();
diff --git a/tests/cpass/binds.rs b/tests/cpass/binds.rs
index 7cb91741..897e083a 100644
--- a/tests/cpass/binds.rs
+++ b/tests/cpass/binds.rs
@@ -1,4 +1,6 @@
//! Check that `binds` works as advertised
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -6,18 +8,20 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::app;
-
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {}
+ fn init(_: init::Context) {}
#[exception(binds = SVCall)]
- fn foo() {}
+ fn foo(c: foo::Context) {
+ foo_trampoline(c)
+ }
#[interrupt(binds = UART0)]
- fn bar() {}
+ fn bar(c: bar::Context) {
+ bar_trampoline(c)
+ }
};
#[allow(dead_code)]
diff --git a/tests/cpass/cfg.rs b/tests/cpass/cfg.rs
index c91ab604..a0b6a870 100644
--- a/tests/cpass/cfg.rs
+++ b/tests/cpass/cfg.rs
@@ -6,24 +6,22 @@
#![no_std]
extern crate lm3s6965;
-extern crate panic_semihosting;
+extern crate panic_halt;
extern crate rtfm;
-use rtfm::app;
-
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[cfg(never)]
static mut FOO: u32 = 0;
#[init]
- fn init() {
+ fn init(_: init::Context) {
#[cfg(never)]
static mut BAR: u32 = 0;
}
#[idle]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
#[cfg(never)]
static mut BAR: u32 = 0;
@@ -31,20 +29,20 @@ const APP: () = {
}
#[task(resources = [FOO], schedule = [quux], spawn = [quux])]
- fn foo() {
+ fn foo(_: foo::Context) {
#[cfg(never)]
static mut BAR: u32 = 0;
}
#[task(priority = 3, resources = [FOO], schedule = [quux], spawn = [quux])]
- fn bar() {
+ fn bar(_: bar::Context) {
#[cfg(never)]
static mut BAR: u32 = 0;
}
#[cfg(never)]
#[task]
- fn quux() {}
+ fn quux(_: quux::Context) {}
extern "C" {
fn UART0();
diff --git a/tests/cpass/late-not-send.rs b/tests/cpass/late-not-send.rs
index 5b278ab5..0f690967 100644
--- a/tests/cpass/late-not-send.rs
+++ b/tests/cpass/late-not-send.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -7,20 +9,18 @@ extern crate rtfm;
use core::marker::PhantomData;
-use rtfm::app;
-
pub struct NotSend {
_0: PhantomData<*const ()>,
}
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static mut X: NotSend = ();
static mut Y: Option<NotSend> = None;
#[init(resources = [Y])]
- fn init() -> init::LateResources {
- *resources.Y = Some(NotSend { _0: PhantomData });
+ fn init(c: init::Context) -> init::LateResources {
+ *c.resources.Y = Some(NotSend { _0: PhantomData });
init::LateResources {
X: NotSend { _0: PhantomData },
@@ -28,7 +28,7 @@ const APP: () = {
}
#[idle(resources = [X, Y])]
- fn idle() -> ! {
+ fn idle(_: idle::Context) -> ! {
loop {}
}
};
diff --git a/tests/cpass/late-resource.rs b/tests/cpass/late-resource.rs
index 0dec4cbe..37dcf331 100644
--- a/tests/cpass/late-resource.rs
+++ b/tests/cpass/late-resource.rs
@@ -1,4 +1,6 @@
//! Runtime initialized resources
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -6,15 +8,13 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::app;
-
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static mut X: u32 = ();
static Y: u32 = ();
#[init]
- fn init() -> init::LateResources {
+ fn init(_: init::Context) -> init::LateResources {
init::LateResources { X: 0, Y: 1 }
}
};
diff --git a/tests/cpass/peripheral.rs b/tests/cpass/peripheral.rs
index 509a6be1..34352b84 100644
--- a/tests/cpass/peripheral.rs
+++ b/tests/cpass/peripheral.rs
@@ -1,4 +1,6 @@
//! Core and device peripherals
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -6,13 +8,11 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::app;
-
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init]
- fn init() {
- let _: rtfm::Peripherals = core;
- let _: lm3s6965::Peripherals = device;
+ fn init(c: init::Context) {
+ let _: rtfm::Peripherals = c.core;
+ let _: lm3s6965::Peripherals = c.device;
}
};
diff --git a/tests/cpass/resource.rs b/tests/cpass/resource.rs
index bb837393..4e92a032 100644
--- a/tests/cpass/resource.rs
+++ b/tests/cpass/resource.rs
@@ -1,5 +1,7 @@
//! Check code generation of resources
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -7,9 +9,9 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::{app, Exclusive};
+use rtfm::Exclusive;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
static mut O1: u32 = 0; // init
static mut O2: u32 = 0; // idle
@@ -23,57 +25,57 @@ const APP: () = {
static S3: u32 = 0;
#[init(resources = [O1, O4, O5, O6, S3])]
- fn init() {
+ fn init(c: init::Context) {
// 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
- let _: &'static u32 = resources.O6;
+ let _: &'static u32 = c.resources.O6;
// `init` has exclusive access to all resources
- let _: &mut u32 = resources.O4;
- let _: &mut u32 = resources.O5;
- let _: &mut u32 = resources.S3;
+ let _: &mut u32 = c.resources.O4;
+ let _: &mut u32 = c.resources.O5;
+ let _: &mut u32 = c.resources.S3;
}
#[idle(resources = [O2, O4, S1, S3])]
- fn idle() -> ! {
+ fn idle(mut c: idle::Context) -> ! {
// 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
- let _: &'static u32 = resources.O4;
+ let _: &'static u32 = c.resources.O4;
// shared with `idle` == `Mutex`
- resources.S1.lock(|_| {});
+ c.resources.S1.lock(|_| {});
// `&` if read-only
- let _: &u32 = resources.S3;
+ let _: &u32 = c.resources.S3;
loop {}
}
#[interrupt(resources = [O3, S1, S2, S3])]
- fn UART0() {
+ fn UART0(c: UART0::Context) {
// owned by interrupt == `&mut`
- let _: &mut u32 = resources.O3;
+ let _: &mut u32 = c.resources.O3;
// 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
- let _: Exclusive<u32> = resources.S2;
+ let _: Exclusive<u32> = c.resources.S2;
// `&` if read-only
- let _: &u32 = resources.S3;
+ let _: &u32 = c.resources.S3;
}
#[interrupt(resources = [S2, O5])]
- fn UART1() {
+ fn UART1(c: UART1::Context) {
// 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
- let _: Exclusive<u32> = resources.S2;
+ let _: Exclusive<u32> = c.resources.S2;
}
};
diff --git a/tests/cpass/schedule.rs b/tests/cpass/schedule.rs
index 0728d8b3..346f9124 100644
--- a/tests/cpass/schedule.rs
+++ b/tests/cpass/schedule.rs
@@ -1,3 +1,5 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -5,52 +7,52 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::{app, Instant};
+use rtfm::Instant;
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(schedule = [foo, bar, baz])]
- fn init() {
- let _: Result<(), ()> = schedule.foo(start + 10.cycles());
- let _: Result<(), u32> = schedule.bar(start + 20.cycles(), 0);
- let _: Result<(), (u32, u32)> = schedule.baz(start + 30.cycles(), 0, 1);
+ fn init(c: init::Context) {
+ let _: Result<(), ()> = c.schedule.foo(c.start + 10.cycles());
+ let _: Result<(), u32> = c.schedule.bar(c.start + 20.cycles(), 0);
+ let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 30.cycles(), 0, 1);
}
#[idle(schedule = [foo, bar, baz])]
- fn idle() -> ! {
- let _: Result<(), ()> = schedule.foo(Instant::now() + 40.cycles());
- let _: Result<(), u32> = schedule.bar(Instant::now() + 50.cycles(), 0);
- let _: Result<(), (u32, u32)> = schedule.baz(Instant::now() + 60.cycles(), 0, 1);
+ fn idle(c: idle::Context) -> ! {
+ let _: Result<(), ()> = c.schedule.foo(Instant::now() + 40.cycles());
+ let _: Result<(), u32> = c.schedule.bar(Instant::now() + 50.cycles(), 0);
+ let _: Result<(), (u32, u32)> = c.schedule.baz(Instant::now() + 60.cycles(), 0, 1);
loop {}
}
#[exception(schedule = [foo, bar, baz])]
- fn SVCall() {
- let _: Result<(), ()> = schedule.foo(start + 70.cycles());
- let _: Result<(), u32> = schedule.bar(start + 80.cycles(), 0);
- let _: Result<(), (u32, u32)> = schedule.baz(start + 90.cycles(), 0, 1);
+ fn SVCall(c: SVCall::Context) {
+ let _: Result<(), ()> = c.schedule.foo(c.start + 70.cycles());
+ let _: Result<(), u32> = c.schedule.bar(c.start + 80.cycles(), 0);
+ let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 90.cycles(), 0, 1);
}
#[interrupt(schedule = [foo, bar, baz])]
- fn UART0() {
- let _: Result<(), ()> = schedule.foo(start + 100.cycles());
- let _: Result<(), u32> = schedule.bar(start + 110.cycles(), 0);
- let _: Result<(), (u32, u32)> = schedule.baz(start + 120.cycles(), 0, 1);
+ fn UART0(c: UART0::Context) {
+ let _: Result<(), ()> = c.schedule.foo(c.start + 100.cycles());
+ let _: Result<(), u32> = c.schedule.bar(c.start + 110.cycles(), 0);
+ let _: Result<(), (u32, u32)> = c.schedule.baz(c.start + 120.cycles(), 0, 1);
}
#[task(schedule = [foo, bar, baz])]
- fn foo() {
- let _: Result<(), ()> = schedule.foo(scheduled + 130.cycles());
- let _: Result<(), u32> = schedule.bar(scheduled + 140.cycles(), 0);
- let _: Result<(), (u32, u32)> = schedule.baz(scheduled + 150.cycles(), 0, 1);
+ fn foo(c: foo::Context) {
+ let _: Result<(), ()> = c.schedule.foo(c.scheduled + 130.cycles());
+ let _: Result<(), u32> = c.schedule.bar(c.scheduled + 140.cycles(), 0);
+ let _: Result<(), (u32, u32)> = c.schedule.baz(c.scheduled + 150.cycles(), 0, 1);
}
#[task]
- fn bar(_x: u32) {}
+ fn bar(_: bar::Context, _x: u32) {}
#[task]
- fn baz(_x: u32, _y: u32) {}
+ fn baz(_: baz::Context, _x: u32, _y: u32) {}
extern "C" {
fn UART1();
diff --git a/tests/cpass/singleton.rs b/tests/cpass/singleton.rs
deleted file mode 100644
index d50f8523..00000000
--- a/tests/cpass/singleton.rs
+++ /dev/null
@@ -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;
- }
-};
diff --git a/tests/cpass/spawn.rs b/tests/cpass/spawn.rs
index 3df606a4..0a27c4f6 100644
--- a/tests/cpass/spawn.rs
+++ b/tests/cpass/spawn.rs
@@ -1,4 +1,6 @@
//! Check code generation of `spawn`
+#![deny(unsafe_code)]
+#![deny(warnings)]
#![no_main]
#![no_std]
@@ -6,52 +8,50 @@ extern crate lm3s6965;
extern crate panic_halt;
extern crate rtfm;
-use rtfm::app;
-
-#[app(device = lm3s6965)]
+#[rtfm::app(device = lm3s6965)]
const APP: () = {
#[init(spawn = [foo, bar, baz])]
- fn init() {
- let _: Result<(), ()> = spawn.foo();
- let _: Result<(), u32> = spawn.bar(0);
- let _: Result<(), (u32, u32)> = spawn.baz(0, 1);
+ fn init(c: init::Context) {
+ let _: Result<(), ()> = c.spawn.foo();
+ let _: Result<(), u32> = c.spawn.bar(0);
+ let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
}
#[idle(spawn = [foo, bar, baz])]
- fn idle() -> ! {
- let _: Result<(), ()> = spawn.foo();
- let _: Result<(), u32> = spawn.bar(0);
- let _: Result<(), (u32, u32)> = spawn.baz(0, 1);
+ fn idle(c: idle::Context) -> ! {
+ let _: Result<(), ()> = c.spawn.foo();
+ let _: Result<(), u32> = c.spawn.bar(0);
+ let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
loop {}
}
#[exception(spawn = [foo, bar, baz])]
- fn SVCall() {
- let _: Result<(), ()> = spawn.foo();
- let _: Result<(), u32> = spawn.bar(0);
- let _: Result<(), (u32, u32)> = spawn.baz(0, 1);
+ fn SVCall(c: SVCall::Context) {
+ let _: Result<(), ()> = c.spawn.foo();
+ let _: Result<(), u32> = c.spawn.bar(0);
+ let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
}
#[interrupt(spawn = [foo, bar, baz])]
- fn UART0() {
- let _: Result<(), ()> = spawn.foo();
- let _: Result<(), u32> = spawn.bar(0);
- let _: Result<(), (u32, u32)> = spawn.baz(0, 1);
+ fn UART0(c: UART0::Context) {
+ let _: Result<(), ()> = c.spawn.foo();
+ let _: Result<(), u32> = c.spawn.bar(0);
+ let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
}
#[task(spawn = [foo, bar, baz])]
- fn foo() {
- let _: Result<(), ()> = spawn.foo();
- let _: Result<(), u32> = spawn.bar(0);
- let _: Result<(), (u32, u32)> = spawn.baz(0, 1);
+ fn foo(c: foo::Context) {
+ let _: Result<(), ()> = c.spawn.foo();
+ let _: Result<(), u32> = c.spawn.bar(0);
+ let _: Result<(), (u32, u32)> = c.spawn.baz(0, 1);
}
#[task]
- fn bar(_x: u32) {}
+ fn bar(_: bar::Context, _x: u32) {}
#[task]
- fn baz(_x: u32, _y: u32) {}
+ fn baz(_: baz::Context, _x: u32, _y: u32) {}
extern "C" {
fn UART1();
diff --git a/tests/cpass/unsafe.rs b/tests/cpass/unsafe.rs
deleted file mode 100644
index b6996ad1..00000000
--- a/tests/cpass/unsafe.rs
+++ /dev/null
@@ -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();
- }
-};