mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-26 03:49:34 +01:00
Merge #97
97: v0.4.0 r=japaric a=japaric closes #32 closes #33 Co-authored-by: Jorge Aparicio <jorge@japaric.io>
This commit is contained in:
commit
777765e522
154 changed files with 7540 additions and 3271 deletions
|
@ -1,31 +1,13 @@
|
||||||
[target.thumbv6m-none-eabi]
|
[target.thumbv6m-none-eabi]
|
||||||
runner = 'arm-none-eabi-gdb'
|
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=-Tlink.x",
|
|
||||||
"-C", "linker=true",
|
|
||||||
"-Z", "linker-flavor=ld",
|
|
||||||
]
|
|
||||||
|
|
||||||
[target.thumbv7m-none-eabi]
|
[target.thumbv7m-none-eabi]
|
||||||
runner = 'arm-none-eabi-gdb'
|
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
|
||||||
|
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C", "link-arg=-Tlink.x",
|
"-C", "link-arg=-Tlink.x",
|
||||||
"-C", "linker=arm-none-eabi-ld",
|
|
||||||
"-Z", "linker-flavor=ld",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.thumbv7em-none-eabi]
|
[build]
|
||||||
runner = 'arm-none-eabi-gdb'
|
target = "thumbv7m-none-eabi"
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=-Tlink.x",
|
|
||||||
"-C", "linker=arm-none-eabi-ld",
|
|
||||||
"-Z", "linker-flavor=ld",
|
|
||||||
]
|
|
||||||
|
|
||||||
[target.thumbv7em-none-eabihf]
|
|
||||||
runner = 'arm-none-eabi-gdb'
|
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=-Tlink.x",
|
|
||||||
"-C", "linker=arm-none-eabi-ld",
|
|
||||||
"-Z", "linker-flavor=ld",
|
|
||||||
]
|
|
6
.gdbinit
6
.gdbinit
|
@ -1,6 +0,0 @@
|
||||||
target remote :3333
|
|
||||||
|
|
||||||
monitor arm semihosting enable
|
|
||||||
|
|
||||||
load
|
|
||||||
step
|
|
1
.github/bors.toml
vendored
1
.github/bors.toml
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
delete_merged_branches = true
|
||||||
status = [
|
status = [
|
||||||
"continuous-integration/travis-ci/push",
|
"continuous-integration/travis-ci/push",
|
||||||
]
|
]
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
*.org
|
|
||||||
.#*
|
.#*
|
||||||
.gdb_history
|
.gdb_history
|
||||||
|
/book/book
|
||||||
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
target/
|
|
||||||
|
|
34
.travis.yml
34
.travis.yml
|
@ -2,38 +2,48 @@ language: rust
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
# NOTE used to build docs on successful merges to master
|
||||||
|
- env: TARGET=x86_64-unknown-linux-gnu
|
||||||
|
rust: beta
|
||||||
|
|
||||||
|
- env: TARGET=thumbv6m-none-eabi
|
||||||
|
rust: beta
|
||||||
|
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
||||||
|
|
||||||
|
- env: TARGET=thumbv7m-none-eabi
|
||||||
|
rust: beta
|
||||||
|
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
||||||
|
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
- env: TARGET=x86_64-unknown-linux-gnu
|
||||||
rust: nightly
|
rust: nightly
|
||||||
|
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
||||||
|
|
||||||
- env: TARGET=thumbv6m-none-eabi
|
- env: TARGET=thumbv6m-none-eabi
|
||||||
rust: nightly
|
rust: nightly
|
||||||
if: branch != master
|
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
||||||
|
|
||||||
- env: TARGET=thumbv7m-none-eabi
|
- env: TARGET=thumbv7m-none-eabi
|
||||||
rust: nightly
|
rust: nightly
|
||||||
if: branch != master
|
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
|
||||||
|
|
||||||
- env: TARGET=thumbv7em-none-eabi
|
|
||||||
rust: nightly
|
|
||||||
if: branch != master
|
|
||||||
|
|
||||||
- env: TARGET=thumbv7em-none-eabihf
|
|
||||||
rust: nightly
|
|
||||||
if: branch != master
|
|
||||||
|
|
||||||
before_install: set -e
|
before_install: set -e
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- bash ci/install.sh
|
- bash ci/install.sh
|
||||||
- export PATH="$PATH:$PWD/gcc/bin"
|
- export PATH="$PATH:$PWD/qemu"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- bash ci/script.sh
|
- bash ci/script.sh
|
||||||
|
|
||||||
|
after_script: set +e
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash ci/after-success.sh
|
- bash ci/after-success.sh
|
||||||
|
|
||||||
after_script: set +e
|
cache: cache
|
||||||
|
|
||||||
|
before_cache:
|
||||||
|
- chmod -R a+r $HOME/.cargo;
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
|
|
63
Cargo.toml
63
Cargo.toml
|
@ -4,33 +4,62 @@ authors = [
|
||||||
"Per Lindgren <per.lindgren@ltu.se>",
|
"Per Lindgren <per.lindgren@ltu.se>",
|
||||||
]
|
]
|
||||||
categories = ["concurrency", "embedded", "no-std"]
|
categories = ["concurrency", "embedded", "no-std"]
|
||||||
description = "Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers"
|
description = "Real Time For the Masses (RTFM): a concurrency framework for building real time systems"
|
||||||
documentation = "https://japaric.github.io/cortex-m-rtfm/cortex_m_rtfm/"
|
documentation = "https://japaric.github.io/cortex-m-rtfm/book/"
|
||||||
|
edition = "2018"
|
||||||
keywords = ["arm", "cortex-m"]
|
keywords = ["arm", "cortex-m"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
name = "cortex-m-rtfm"
|
name = "cortex-m-rtfm"
|
||||||
|
readme = "README.md"
|
||||||
repository = "https://github.com/japaric/cortex-m-rtfm"
|
repository = "https://github.com/japaric/cortex-m-rtfm"
|
||||||
version = "0.3.4"
|
version = "0.4.0-beta.1"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "rtfm"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "baseline"
|
||||||
|
required-features = ["timer-queue"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "periodic"
|
||||||
|
required-features = ["timer-queue"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "schedule"
|
||||||
|
required-features = ["timer-queue"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "types"
|
||||||
|
required-features = ["timer-queue"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.4.0"
|
cortex-m = "0.5.8"
|
||||||
cortex-m-rtfm-macros = { path = "macros", version = "0.3.2" }
|
cortex-m-rt = "0.6.5"
|
||||||
rtfm-core = "0.2.0"
|
cortex-m-rtfm-macros = { path = "macros", version = "0.4.0-beta.1" }
|
||||||
untagged-option = "0.1.1"
|
heapless = "0.4.0"
|
||||||
|
owned-singleton = "0.1.0"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
[dev-dependencies]
|
||||||
compiletest_rs = "0.3.5"
|
alloc-singleton = "0.1.0"
|
||||||
|
cortex-m-semihosting = "0.3.1"
|
||||||
|
lm3s6965 = "0.1.3"
|
||||||
|
panic-halt = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies.cortex-m-rt]
|
[dev-dependencies.panic-semihosting]
|
||||||
features = ["abort-on-panic"]
|
features = ["exit"]
|
||||||
version = "0.3.9"
|
version = "0.5.1"
|
||||||
|
|
||||||
[dev-dependencies.stm32f103xx]
|
|
||||||
features = ["rt"]
|
|
||||||
version = "0.8.0"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
cm7-r0p1 = ["cortex-m/cm7-r0p1"]
|
timer-queue = ["cortex-m-rtfm-macros/timer-queue"]
|
||||||
|
|
||||||
|
[target.x86_64-unknown-linux-gnu.dev-dependencies]
|
||||||
|
compiletest_rs = "0.3.16"
|
||||||
|
tempdir = "0.3.7"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["macros"]
|
428
LICENSE-CC-BY-SA
Normal file
428
LICENSE-CC-BY-SA
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
Attribution-ShareAlike 4.0 International
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||||
|
does not provide legal services or legal advice. Distribution of
|
||||||
|
Creative Commons public licenses does not create a lawyer-client or
|
||||||
|
other relationship. Creative Commons makes its licenses and related
|
||||||
|
information available on an "as-is" basis. Creative Commons gives no
|
||||||
|
warranties regarding its licenses, any material licensed under their
|
||||||
|
terms and conditions, or any related information. Creative Commons
|
||||||
|
disclaims all liability for damages resulting from their use to the
|
||||||
|
fullest extent possible.
|
||||||
|
|
||||||
|
Using Creative Commons Public Licenses
|
||||||
|
|
||||||
|
Creative Commons public licenses provide a standard set of terms and
|
||||||
|
conditions that creators and other rights holders may use to share
|
||||||
|
original works of authorship and other material subject to copyright
|
||||||
|
and certain other rights specified in the public license below. The
|
||||||
|
following considerations are for informational purposes only, are not
|
||||||
|
exhaustive, and do not form part of our licenses.
|
||||||
|
|
||||||
|
Considerations for licensors: Our public licenses are
|
||||||
|
intended for use by those authorized to give the public
|
||||||
|
permission to use material in ways otherwise restricted by
|
||||||
|
copyright and certain other rights. Our licenses are
|
||||||
|
irrevocable. Licensors should read and understand the terms
|
||||||
|
and conditions of the license they choose before applying it.
|
||||||
|
Licensors should also secure all rights necessary before
|
||||||
|
applying our licenses so that the public can reuse the
|
||||||
|
material as expected. Licensors should clearly mark any
|
||||||
|
material not subject to the license. This includes other CC-
|
||||||
|
licensed material, or material used under an exception or
|
||||||
|
limitation to copyright. More considerations for licensors:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensors
|
||||||
|
|
||||||
|
Considerations for the public: By using one of our public
|
||||||
|
licenses, a licensor grants the public permission to use the
|
||||||
|
licensed material under specified terms and conditions. If
|
||||||
|
the licensor's permission is not necessary for any reason--for
|
||||||
|
example, because of any applicable exception or limitation to
|
||||||
|
copyright--then that use is not regulated by the license. Our
|
||||||
|
licenses grant only permissions under copyright and certain
|
||||||
|
other rights that a licensor has authority to grant. Use of
|
||||||
|
the licensed material may still be restricted for other
|
||||||
|
reasons, including because others have copyright or other
|
||||||
|
rights in the material. A licensor may make special requests,
|
||||||
|
such as asking that all changes be marked or described.
|
||||||
|
Although not required by our licenses, you are encouraged to
|
||||||
|
respect those requests where reasonable. More considerations
|
||||||
|
for the public:
|
||||||
|
wiki.creativecommons.org/Considerations_for_licensees
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||||
|
License
|
||||||
|
|
||||||
|
By exercising the Licensed Rights (defined below), You accept and agree
|
||||||
|
to be bound by the terms and conditions of this Creative Commons
|
||||||
|
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||||
|
License"). To the extent this Public License may be interpreted as a
|
||||||
|
contract, You are granted the Licensed Rights in consideration of Your
|
||||||
|
acceptance of these terms and conditions, and the Licensor grants You
|
||||||
|
such rights in consideration of benefits the Licensor receives from
|
||||||
|
making the Licensed Material available under these terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
|
||||||
|
Section 1 -- Definitions.
|
||||||
|
|
||||||
|
a. Adapted Material means material subject to Copyright and Similar
|
||||||
|
Rights that is derived from or based upon the Licensed Material
|
||||||
|
and in which the Licensed Material is translated, altered,
|
||||||
|
arranged, transformed, or otherwise modified in a manner requiring
|
||||||
|
permission under the Copyright and Similar Rights held by the
|
||||||
|
Licensor. For purposes of this Public License, where the Licensed
|
||||||
|
Material is a musical work, performance, or sound recording,
|
||||||
|
Adapted Material is always produced where the Licensed Material is
|
||||||
|
synched in timed relation with a moving image.
|
||||||
|
|
||||||
|
b. Adapter's License means the license You apply to Your Copyright
|
||||||
|
and Similar Rights in Your contributions to Adapted Material in
|
||||||
|
accordance with the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
c. BY-SA Compatible License means a license listed at
|
||||||
|
creativecommons.org/compatiblelicenses, approved by Creative
|
||||||
|
Commons as essentially the equivalent of this Public License.
|
||||||
|
|
||||||
|
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||||
|
closely related to copyright including, without limitation,
|
||||||
|
performance, broadcast, sound recording, and Sui Generis Database
|
||||||
|
Rights, without regard to how the rights are labeled or
|
||||||
|
categorized. For purposes of this Public License, the rights
|
||||||
|
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||||
|
Rights.
|
||||||
|
|
||||||
|
e. Effective Technological Measures means those measures that, in the
|
||||||
|
absence of proper authority, may not be circumvented under laws
|
||||||
|
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||||
|
Treaty adopted on December 20, 1996, and/or similar international
|
||||||
|
agreements.
|
||||||
|
|
||||||
|
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||||
|
any other exception or limitation to Copyright and Similar Rights
|
||||||
|
that applies to Your use of the Licensed Material.
|
||||||
|
|
||||||
|
g. License Elements means the license attributes listed in the name
|
||||||
|
of a Creative Commons Public License. The License Elements of this
|
||||||
|
Public License are Attribution and ShareAlike.
|
||||||
|
|
||||||
|
h. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
i. Licensed Rights means the rights granted to You subject to the
|
||||||
|
terms and conditions of this Public License, which are limited to
|
||||||
|
all Copyright and Similar Rights that apply to Your use of the
|
||||||
|
Licensed Material and that the Licensor has authority to license.
|
||||||
|
|
||||||
|
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
k. Share means to provide material to the public by any means or
|
||||||
|
process that requires permission under the Licensed Rights, such
|
||||||
|
as reproduction, public display, public performance, distribution,
|
||||||
|
dissemination, communication, or importation, and to make material
|
||||||
|
available to the public including in ways that members of the
|
||||||
|
public may access the material from a place and at a time
|
||||||
|
individually chosen by them.
|
||||||
|
|
||||||
|
l. Sui Generis Database Rights means rights other than copyright
|
||||||
|
resulting from Directive 96/9/EC of the European Parliament and of
|
||||||
|
the Council of 11 March 1996 on the legal protection of databases,
|
||||||
|
as amended and/or succeeded, as well as other essentially
|
||||||
|
equivalent rights anywhere in the world.
|
||||||
|
|
||||||
|
m. You means the individual or entity exercising the Licensed Rights
|
||||||
|
under this Public License. Your has a corresponding meaning.
|
||||||
|
|
||||||
|
|
||||||
|
Section 2 -- Scope.
|
||||||
|
|
||||||
|
a. License grant.
|
||||||
|
|
||||||
|
1. Subject to the terms and conditions of this Public License,
|
||||||
|
the Licensor hereby grants You a worldwide, royalty-free,
|
||||||
|
non-sublicensable, non-exclusive, irrevocable license to
|
||||||
|
exercise the Licensed Rights in the Licensed Material to:
|
||||||
|
|
||||||
|
a. reproduce and Share the Licensed Material, in whole or
|
||||||
|
in part; and
|
||||||
|
|
||||||
|
b. produce, reproduce, and Share Adapted Material.
|
||||||
|
|
||||||
|
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||||
|
Exceptions and Limitations apply to Your use, this Public
|
||||||
|
License does not apply, and You do not need to comply with
|
||||||
|
its terms and conditions.
|
||||||
|
|
||||||
|
3. Term. The term of this Public License is specified in Section
|
||||||
|
6(a).
|
||||||
|
|
||||||
|
4. Media and formats; technical modifications allowed. The
|
||||||
|
Licensor authorizes You to exercise the Licensed Rights in
|
||||||
|
all media and formats whether now known or hereafter created,
|
||||||
|
and to make technical modifications necessary to do so. The
|
||||||
|
Licensor waives and/or agrees not to assert any right or
|
||||||
|
authority to forbid You from making technical modifications
|
||||||
|
necessary to exercise the Licensed Rights, including
|
||||||
|
technical modifications necessary to circumvent Effective
|
||||||
|
Technological Measures. For purposes of this Public License,
|
||||||
|
simply making modifications authorized by this Section 2(a)
|
||||||
|
(4) never produces Adapted Material.
|
||||||
|
|
||||||
|
5. Downstream recipients.
|
||||||
|
|
||||||
|
a. Offer from the Licensor -- Licensed Material. Every
|
||||||
|
recipient of the Licensed Material automatically
|
||||||
|
receives an offer from the Licensor to exercise the
|
||||||
|
Licensed Rights under the terms and conditions of this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
b. Additional offer from the Licensor -- Adapted Material.
|
||||||
|
Every recipient of Adapted Material from You
|
||||||
|
automatically receives an offer from the Licensor to
|
||||||
|
exercise the Licensed Rights in the Adapted Material
|
||||||
|
under the conditions of the Adapter's License You apply.
|
||||||
|
|
||||||
|
c. No downstream restrictions. You may not offer or impose
|
||||||
|
any additional or different terms or conditions on, or
|
||||||
|
apply any Effective Technological Measures to, the
|
||||||
|
Licensed Material if doing so restricts exercise of the
|
||||||
|
Licensed Rights by any recipient of the Licensed
|
||||||
|
Material.
|
||||||
|
|
||||||
|
6. No endorsement. Nothing in this Public License constitutes or
|
||||||
|
may be construed as permission to assert or imply that You
|
||||||
|
are, or that Your use of the Licensed Material is, connected
|
||||||
|
with, or sponsored, endorsed, or granted official status by,
|
||||||
|
the Licensor or others designated to receive attribution as
|
||||||
|
provided in Section 3(a)(1)(A)(i).
|
||||||
|
|
||||||
|
b. Other rights.
|
||||||
|
|
||||||
|
1. Moral rights, such as the right of integrity, are not
|
||||||
|
licensed under this Public License, nor are publicity,
|
||||||
|
privacy, and/or other similar personality rights; however, to
|
||||||
|
the extent possible, the Licensor waives and/or agrees not to
|
||||||
|
assert any such rights held by the Licensor to the limited
|
||||||
|
extent necessary to allow You to exercise the Licensed
|
||||||
|
Rights, but not otherwise.
|
||||||
|
|
||||||
|
2. Patent and trademark rights are not licensed under this
|
||||||
|
Public License.
|
||||||
|
|
||||||
|
3. To the extent possible, the Licensor waives any right to
|
||||||
|
collect royalties from You for the exercise of the Licensed
|
||||||
|
Rights, whether directly or through a collecting society
|
||||||
|
under any voluntary or waivable statutory or compulsory
|
||||||
|
licensing scheme. In all other cases the Licensor expressly
|
||||||
|
reserves any right to collect such royalties.
|
||||||
|
|
||||||
|
|
||||||
|
Section 3 -- License Conditions.
|
||||||
|
|
||||||
|
Your exercise of the Licensed Rights is expressly made subject to the
|
||||||
|
following conditions.
|
||||||
|
|
||||||
|
a. Attribution.
|
||||||
|
|
||||||
|
1. If You Share the Licensed Material (including in modified
|
||||||
|
form), You must:
|
||||||
|
|
||||||
|
a. retain the following if it is supplied by the Licensor
|
||||||
|
with the Licensed Material:
|
||||||
|
|
||||||
|
i. identification of the creator(s) of the Licensed
|
||||||
|
Material and any others designated to receive
|
||||||
|
attribution, in any reasonable manner requested by
|
||||||
|
the Licensor (including by pseudonym if
|
||||||
|
designated);
|
||||||
|
|
||||||
|
ii. a copyright notice;
|
||||||
|
|
||||||
|
iii. a notice that refers to this Public License;
|
||||||
|
|
||||||
|
iv. a notice that refers to the disclaimer of
|
||||||
|
warranties;
|
||||||
|
|
||||||
|
v. a URI or hyperlink to the Licensed Material to the
|
||||||
|
extent reasonably practicable;
|
||||||
|
|
||||||
|
b. indicate if You modified the Licensed Material and
|
||||||
|
retain an indication of any previous modifications; and
|
||||||
|
|
||||||
|
c. indicate the Licensed Material is licensed under this
|
||||||
|
Public License, and include the text of, or the URI or
|
||||||
|
hyperlink to, this Public License.
|
||||||
|
|
||||||
|
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||||
|
reasonable manner based on the medium, means, and context in
|
||||||
|
which You Share the Licensed Material. For example, it may be
|
||||||
|
reasonable to satisfy the conditions by providing a URI or
|
||||||
|
hyperlink to a resource that includes the required
|
||||||
|
information.
|
||||||
|
|
||||||
|
3. If requested by the Licensor, You must remove any of the
|
||||||
|
information required by Section 3(a)(1)(A) to the extent
|
||||||
|
reasonably practicable.
|
||||||
|
|
||||||
|
b. ShareAlike.
|
||||||
|
|
||||||
|
In addition to the conditions in Section 3(a), if You Share
|
||||||
|
Adapted Material You produce, the following conditions also apply.
|
||||||
|
|
||||||
|
1. The Adapter's License You apply must be a Creative Commons
|
||||||
|
license with the same License Elements, this version or
|
||||||
|
later, or a BY-SA Compatible License.
|
||||||
|
|
||||||
|
2. You must include the text of, or the URI or hyperlink to, the
|
||||||
|
Adapter's License You apply. You may satisfy this condition
|
||||||
|
in any reasonable manner based on the medium, means, and
|
||||||
|
context in which You Share Adapted Material.
|
||||||
|
|
||||||
|
3. You may not offer or impose any additional or different terms
|
||||||
|
or conditions on, or apply any Effective Technological
|
||||||
|
Measures to, Adapted Material that restrict exercise of the
|
||||||
|
rights granted under the Adapter's License You apply.
|
||||||
|
|
||||||
|
|
||||||
|
Section 4 -- Sui Generis Database Rights.
|
||||||
|
|
||||||
|
Where the Licensed Rights include Sui Generis Database Rights that
|
||||||
|
apply to Your use of the Licensed Material:
|
||||||
|
|
||||||
|
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||||
|
to extract, reuse, reproduce, and Share all or a substantial
|
||||||
|
portion of the contents of the database;
|
||||||
|
|
||||||
|
b. if You include all or a substantial portion of the database
|
||||||
|
contents in a database in which You have Sui Generis Database
|
||||||
|
Rights, then the database in which You have Sui Generis Database
|
||||||
|
Rights (but not its individual contents) is Adapted Material,
|
||||||
|
|
||||||
|
including for purposes of Section 3(b); and
|
||||||
|
c. You must comply with the conditions in Section 3(a) if You Share
|
||||||
|
all or a substantial portion of the contents of the database.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 4 supplements and does not
|
||||||
|
replace Your obligations under this Public License where the Licensed
|
||||||
|
Rights include other Copyright and Similar Rights.
|
||||||
|
|
||||||
|
|
||||||
|
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||||
|
|
||||||
|
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||||
|
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||||
|
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||||
|
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||||
|
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||||
|
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||||
|
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||||
|
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||||
|
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||||
|
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||||
|
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||||
|
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||||
|
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||||
|
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||||
|
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||||
|
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||||
|
|
||||||
|
c. The disclaimer of warranties and limitation of liability provided
|
||||||
|
above shall be interpreted in a manner that, to the extent
|
||||||
|
possible, most closely approximates an absolute disclaimer and
|
||||||
|
waiver of all liability.
|
||||||
|
|
||||||
|
|
||||||
|
Section 6 -- Term and Termination.
|
||||||
|
|
||||||
|
a. This Public License applies for the term of the Copyright and
|
||||||
|
Similar Rights licensed here. However, if You fail to comply with
|
||||||
|
this Public License, then Your rights under this Public License
|
||||||
|
terminate automatically.
|
||||||
|
|
||||||
|
b. Where Your right to use the Licensed Material has terminated under
|
||||||
|
Section 6(a), it reinstates:
|
||||||
|
|
||||||
|
1. automatically as of the date the violation is cured, provided
|
||||||
|
it is cured within 30 days of Your discovery of the
|
||||||
|
violation; or
|
||||||
|
|
||||||
|
2. upon express reinstatement by the Licensor.
|
||||||
|
|
||||||
|
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||||
|
right the Licensor may have to seek remedies for Your violations
|
||||||
|
of this Public License.
|
||||||
|
|
||||||
|
c. For the avoidance of doubt, the Licensor may also offer the
|
||||||
|
Licensed Material under separate terms or conditions or stop
|
||||||
|
distributing the Licensed Material at any time; however, doing so
|
||||||
|
will not terminate this Public License.
|
||||||
|
|
||||||
|
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 7 -- Other Terms and Conditions.
|
||||||
|
|
||||||
|
a. The Licensor shall not be bound by any additional or different
|
||||||
|
terms or conditions communicated by You unless expressly agreed.
|
||||||
|
|
||||||
|
b. Any arrangements, understandings, or agreements regarding the
|
||||||
|
Licensed Material not stated herein are separate from and
|
||||||
|
independent of the terms and conditions of this Public License.
|
||||||
|
|
||||||
|
|
||||||
|
Section 8 -- Interpretation.
|
||||||
|
|
||||||
|
a. For the avoidance of doubt, this Public License does not, and
|
||||||
|
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||||
|
conditions on any use of the Licensed Material that could lawfully
|
||||||
|
be made without permission under this Public License.
|
||||||
|
|
||||||
|
b. To the extent possible, if any provision of this Public License is
|
||||||
|
deemed unenforceable, it shall be automatically reformed to the
|
||||||
|
minimum extent necessary to make it enforceable. If the provision
|
||||||
|
cannot be reformed, it shall be severed from this Public License
|
||||||
|
without affecting the enforceability of the remaining terms and
|
||||||
|
conditions.
|
||||||
|
|
||||||
|
c. No term or condition of this Public License will be waived and no
|
||||||
|
failure to comply consented to unless expressly agreed to by the
|
||||||
|
Licensor.
|
||||||
|
|
||||||
|
d. Nothing in this Public License constitutes or may be interpreted
|
||||||
|
as a limitation upon, or waiver of, any privileges and immunities
|
||||||
|
that apply to the Licensor or You, including from the legal
|
||||||
|
processes of any jurisdiction or authority.
|
||||||
|
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Creative Commons is not a party to its public
|
||||||
|
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||||
|
its public licenses to material it publishes and in those instances
|
||||||
|
will be considered the “Licensor.” The text of the Creative Commons
|
||||||
|
public licenses is dedicated to the public domain under the CC0 Public
|
||||||
|
Domain Dedication. Except for the limited purpose of indicating that
|
||||||
|
material is shared under a Creative Commons public license or as
|
||||||
|
otherwise permitted by the Creative Commons policies published at
|
||||||
|
creativecommons.org/policies, Creative Commons does not authorize the
|
||||||
|
use of the trademark "Creative Commons" or any other trademark or logo
|
||||||
|
of Creative Commons without its prior written consent including,
|
||||||
|
without limitation, in connection with any unauthorized modifications
|
||||||
|
to any of its public licenses or any other arrangements,
|
||||||
|
understandings, or agreements concerning use of licensed material. For
|
||||||
|
the avoidance of doubt, this paragraph does not form part of the
|
||||||
|
public licenses.
|
||||||
|
|
||||||
|
Creative Commons may be contacted at creativecommons.org.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2017 Jorge Aparicio
|
Copyright (c) 2017-2018 Jorge Aparicio
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
|
|
101
README.md
101
README.md
|
@ -1,25 +1,104 @@
|
||||||
[![crates.io](https://img.shields.io/crates/v/cortex-m-rtfm.svg)](https://crates.io/crates/cortex-m-rtfm)
|
# Real Time For the Masses
|
||||||
[![crates.io](https://img.shields.io/crates/d/cortex-m-rtfm.svg)](https://crates.io/crates/cortex-m-rtfm)
|
|
||||||
|
|
||||||
# `cortex-m-rtfm`
|
A concurrency framework for building real time systems.
|
||||||
|
|
||||||
> Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers
|
**IMPORTANT** This crate is currently in pre-release (beta) state . We reserve
|
||||||
|
the right to make breaking changes in the syntax or to patch memory safety holes
|
||||||
|
before the v0.4.0 release, which is planned for 2018-12-07. When v0.4.0 is
|
||||||
|
released *all the pre-releases will be yanked*. If you run into a panic message
|
||||||
|
or an unhelpful error message (e.g. misleading span), or if something doesn't
|
||||||
|
behave the way you expect please open [an issue]!
|
||||||
|
|
||||||
# [Documentation](https://japaric.github.io/cortex-m-rtfm/cortex_m_rtfm/)
|
[an issue]: https://github.com/japaric/cortex-m-rtfm/issues
|
||||||
|
|
||||||
# License
|
## Features
|
||||||
|
|
||||||
Licensed under either of
|
- **Tasks** as the unit of concurrency [^1]. Tasks can be *event triggered*
|
||||||
|
(fired in response to asynchronous stimuli) or spawned by the application on
|
||||||
|
demand.
|
||||||
|
|
||||||
|
- **Message passing** between tasks. Specifically, messages can be passed to
|
||||||
|
software tasks at spawn time.
|
||||||
|
|
||||||
|
- **A timer queue** [^2]. Software tasks can be scheduled to run at some time
|
||||||
|
in the future. This feature can be used to implement periodic tasks.
|
||||||
|
|
||||||
|
- Support for prioritization of tasks and, thus, **preemptive multitasking**.
|
||||||
|
|
||||||
|
- **Efficient and data race free memory sharing** through fine grained *priority
|
||||||
|
based* critical sections [^1].
|
||||||
|
|
||||||
|
- **Deadlock free execution** guaranteed at compile time. This is an stronger
|
||||||
|
guarantee than what's provided by [the standard `Mutex`
|
||||||
|
abstraction][std-mutex].
|
||||||
|
|
||||||
|
[std-mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
|
||||||
|
|
||||||
|
- **Minimal scheduling overhead**. The task scheduler has minimal software
|
||||||
|
footprint; the hardware does the bulk of the scheduling.
|
||||||
|
|
||||||
|
- **Highly efficient memory usage**: All the tasks share a single call stack and
|
||||||
|
there's no hard dependency on a dynamic memory allocator.
|
||||||
|
|
||||||
|
- **All Cortex-M devices are fully supported**.
|
||||||
|
|
||||||
|
- This task model is amenable to known WCET (Worst Case Execution Time) analysis
|
||||||
|
and scheduling analysis techniques. (Though we haven't yet developed Rust
|
||||||
|
friendly tooling for that.)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Rust 1.31.0+
|
||||||
|
|
||||||
|
- Applications must be written using the 2018 edition.
|
||||||
|
|
||||||
|
## [User documentation](https://japaric.github.io/cortex-m-rtfm/book/index.html)
|
||||||
|
|
||||||
|
## [API reference](https://japaric.github.io/cortex-m-rtfm/api/rtfm/index.html)
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
This crate is based on [the RTFM language][rtfm-lang] created by the Embedded
|
||||||
|
Systems group at [Luleå University of Technology][ltu], led by [Prof. Per
|
||||||
|
Lindgren][per].
|
||||||
|
|
||||||
|
[rtfm-lang]: http://www.rtfm-lang.org/
|
||||||
|
[ltu]: https://www.ltu.se/?l=en
|
||||||
|
[per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
[^1]: Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P.
|
||||||
|
(2013, June). Real-time for the masses, step 1: Programming API and static
|
||||||
|
priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013
|
||||||
|
8th IEEE International Symposium on (pp. 110-113). IEEE.
|
||||||
|
|
||||||
|
[^2]: Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho,
|
||||||
|
L. M. (2016). Abstract timers and their implementation onto the arm cortex-m
|
||||||
|
family of mcus. ACM SIGBED Review, 13(1), 48-53.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
All source code (including code snippets) is licensed under either of
|
||||||
|
|
||||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
http://www.apache.org/licenses/LICENSE-2.0)
|
[https://www.apache.org/licenses/LICENSE-2.0][L1])
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||||
|
[https://opensource.org/licenses/MIT][L2])
|
||||||
|
|
||||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
[L1]: https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
[L2]: https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
## Contribution
|
The written prose contained within the book is licensed under the terms of the
|
||||||
|
Creative Commons CC-BY-SA v4.0 license ([LICENSE-CC-BY-SA](LICENSE-CC-BY-SA) or
|
||||||
|
[https://creativecommons.org/licenses/by-sa/4.0/legalcode][L3]).
|
||||||
|
|
||||||
|
[L3]: https://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||||
|
|
||||||
|
### Contribution
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||||
dual licensed as above, without any additional terms or conditions.
|
licensed as above, without any additional terms or conditions.
|
||||||
|
|
5
book/book.toml
Normal file
5
book/book.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[book]
|
||||||
|
authors = ["Jorge Aparicio"]
|
||||||
|
multilingual = false
|
||||||
|
src = "src"
|
||||||
|
title = "Real Time For the Masses"
|
16
book/src/SUMMARY.md
Normal file
16
book/src/SUMMARY.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
[Preface](./preface.md)
|
||||||
|
- [RTFM by example](./by-example.md)
|
||||||
|
- [The `app` attribute](./by-example/app.md)
|
||||||
|
- [Resources](./by-example/resources.md)
|
||||||
|
- [Tasks](./by-example/tasks.md)
|
||||||
|
- [Timer queue](./by-example/timer-queue.md)
|
||||||
|
- [Singletons](./by-example/singletons.md)
|
||||||
|
- [Types, Send and Sync](./by-example/types-send-sync.md)
|
||||||
|
- [Starting a new project](./by-example/new.md)
|
||||||
|
- [Tips & tricks](./by-example/tips.md)
|
||||||
|
- [Under the hood](./internals.md)
|
||||||
|
- [Ceiling analysis](./internals/ceilings.md)
|
||||||
|
- [Task dispatcher](./internals/tasks.md)
|
||||||
|
- [Timer queue](./internals/timer-queue.md)
|
16
book/src/by-example.md
Normal file
16
book/src/by-example.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# RTFM by example
|
||||||
|
|
||||||
|
This part of the book introduces the Real Time For the Masses (RTFM) framework
|
||||||
|
to new users by walking them through examples of increasing complexity.
|
||||||
|
|
||||||
|
All examples in this part of the book can be found in the GitHub [repository] of
|
||||||
|
the project, and most of the examples can be run on QEMU so no special hardware
|
||||||
|
is required to follow along.
|
||||||
|
|
||||||
|
[repository]: https://github.com/japaric/cortex-m-rtfm
|
||||||
|
|
||||||
|
To run the examples on your laptop / PC you'll need the `qemu-system-arm`
|
||||||
|
program. Check [the embedded Rust book] for instructions on how to set up an
|
||||||
|
embedded development environment that includes QEMU.
|
||||||
|
|
||||||
|
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html
|
105
book/src/by-example/app.md
Normal file
105
book/src/by-example/app.md
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
# The `app` attribute
|
||||||
|
|
||||||
|
This is the smallest possible RTFM application:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/smallest.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
All RTFM applications use the [`app`] attribute (`#[app(..)]`). This attribute
|
||||||
|
must be applied to a `const` item that contains items. The `app` attribute has
|
||||||
|
a mandatory `device` argument that takes a *path* as a value. This path must
|
||||||
|
point to a *device* crate generated using [`svd2rust`] **v0.14.x**. The `app`
|
||||||
|
attribute will expand into a suitable entry point so it's not required to use
|
||||||
|
the [`cortex_m_rt::entry`] attribute.
|
||||||
|
|
||||||
|
[`app`]: ../../api/cortex_m_rtfm_macros/attr.app.html
|
||||||
|
[`svd2rust`]: https://crates.io/crates/svd2rust
|
||||||
|
[`cortex_m_rt::entry`]: ../../api/cortex_m_rt_macros/attr.entry.html
|
||||||
|
|
||||||
|
> **ASIDE**: Some of you may be wondering why we are using a `const` item as a
|
||||||
|
> module and not a proper `mod` item. The reason is that using attributes on
|
||||||
|
> modules requires a feature gate, which requires a nightly toolchain. To make
|
||||||
|
> RTFM work on stable we use the `const` item instead. When more parts of macros
|
||||||
|
> 1.2 are stabilized we'll move from a `const` item to a `mod` item and
|
||||||
|
> eventually to a crate level attribute (`#![app]`).
|
||||||
|
|
||||||
|
## `init`
|
||||||
|
|
||||||
|
Within the pseudo-module the `app` attribute expects to find an initialization
|
||||||
|
function marked with the `init` attribute. This function must have signature
|
||||||
|
`[unsafe] fn()`.
|
||||||
|
|
||||||
|
This initialization function will be the first part of the application to run.
|
||||||
|
The `init` function will run *with interrupts disabled* and has exclusive access
|
||||||
|
to Cortex-M and device specific peripherals through the `core` and `device`
|
||||||
|
variables, which are injected in the scope of `init` by the `app` attribute. Not
|
||||||
|
all Cortex-M peripherals are available in `core` because the RTFM runtime takes
|
||||||
|
ownership of some of them -- for more details see the [`rtfm::Peripherals`]
|
||||||
|
struct.
|
||||||
|
|
||||||
|
`static mut` variables declared at the beginning of `init` will be transformed
|
||||||
|
into `&'static mut` references that are safe to access.
|
||||||
|
|
||||||
|
[`rtfm::Peripherals`]: ../../api/rtfm/struct.Peripherals.html
|
||||||
|
|
||||||
|
The example below shows the types of the `core` and `device` variables and
|
||||||
|
showcases safe access to a `static mut` variable.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/init.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running the example will print `init` to the console and then exit the QEMU
|
||||||
|
process.
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example init
|
||||||
|
{{#include ../../../ci/expected/init.run}}```
|
||||||
|
|
||||||
|
## `idle`
|
||||||
|
|
||||||
|
A function marked with the `idle` attribute can optionally appear in the
|
||||||
|
pseudo-module. This function is used as the special *idle task* and must have
|
||||||
|
signature `[unsafe] fn() - > !`.
|
||||||
|
|
||||||
|
When present, the runtime will execute the `idle` task after `init`. Unlike
|
||||||
|
`init`, `idle` will run *with interrupts enabled* and it's not allowed to return
|
||||||
|
so it runs forever.
|
||||||
|
|
||||||
|
When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and
|
||||||
|
then sends the microcontroller to sleep after running `init`.
|
||||||
|
|
||||||
|
[SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
|
||||||
|
|
||||||
|
Like in `init`, `static mut` variables will be transformed into `&'static mut`
|
||||||
|
references that are safe to access.
|
||||||
|
|
||||||
|
The example below shows that `idle` runs after `init`.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/idle.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example idle
|
||||||
|
{{#include ../../../ci/expected/idle.run}}```
|
||||||
|
|
||||||
|
## `interrupt` / `exception`
|
||||||
|
|
||||||
|
Just like you would do with the `cortex-m-rt` crate you can use the `interrupt`
|
||||||
|
and `exception` attributes within the `app` pseudo-module to declare interrupt
|
||||||
|
and exception handlers. In RTFM, we refer to interrupt and exception handlers as
|
||||||
|
*hardware* tasks.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/interrupt.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example interrupt
|
||||||
|
{{#include ../../../ci/expected/interrupt.run}}```
|
||||||
|
|
||||||
|
So far all the RTFM applications we have seen look no different that the
|
||||||
|
applications one can write using only the `cortex-m-rt` crate. In the next
|
||||||
|
section we start introducing features unique to RTFM.
|
67
book/src/by-example/new.md
Normal file
67
book/src/by-example/new.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# Starting a new project
|
||||||
|
|
||||||
|
Now that you have learned about the main features of the RTFM framework you can
|
||||||
|
try it out on your hardware by following these instructions.
|
||||||
|
|
||||||
|
1. Instantiate the [`cortex-m-quickstart`] template.
|
||||||
|
|
||||||
|
[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart#cortex-m-quickstart
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ # for example using `cargo-generate`
|
||||||
|
$ cargo generate \
|
||||||
|
--git https://github.com/rust-embedded/cortex-m-quickstart \
|
||||||
|
--name app
|
||||||
|
|
||||||
|
$ # follow the rest of the instructions
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add a device crate that was generated using [`svd2rust`] **v0.14.x**, or a
|
||||||
|
board support crate that depends on one such device crate as a dependency.
|
||||||
|
Make sure that the `rt` feature of the crate is enabled.
|
||||||
|
|
||||||
|
[`svd2rust`]: https://crates.io/crates/svd2rust
|
||||||
|
|
||||||
|
In this example, I'll use the [`lm3s6965`] device crate. This device crate
|
||||||
|
doesn't have an `rt` Cargo feature; that feature is always enabled.
|
||||||
|
|
||||||
|
[`lm3s6965`]: https://crates.io/crates/lm3s6965
|
||||||
|
|
||||||
|
This device crate provides a linker script with the memory layout of the target
|
||||||
|
device so `memory.x` and `build.rs` need to be removed.
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo add lm3s6965 --vers 0.1.3
|
||||||
|
|
||||||
|
$ rm memory.x build.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Add the `cortex-m-rtfm` crate as a dependency and, if you need it, enable the
|
||||||
|
`timer-queue` feature.
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo add cortex-m-rtfm --allow-prerelease --upgrade=none
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Write your RTFM application.
|
||||||
|
|
||||||
|
Here I'll use the `init` example from the `cortex-m-rtfm` crate.
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ curl \
|
||||||
|
-L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0-beta.1/examples/init.rs \
|
||||||
|
> src/main.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
That example depends on the `panic-semihosting` crate:
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo add panic-semihosting
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Build it, flash it and run it.
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ # NOTE: I have uncommented the `runner` option in `.cargo/config`
|
||||||
|
$ cargo run
|
||||||
|
{{#include ../../../ci/expected/init.run}}```
|
119
book/src/by-example/resources.md
Normal file
119
book/src/by-example/resources.md
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
One of the limitations of the attributes provided by the `cortex-m-rt` crate is
|
||||||
|
that sharing data (or peripherals) between interrupts, or between an interrupt
|
||||||
|
and the `entry` function, requires a `cortex_m::interrupt::Mutex`, which
|
||||||
|
*always* requires disabling *all* interrupts to access the data. Disabling all
|
||||||
|
the interrupts is not always required for memory safety but the compiler doesn't
|
||||||
|
have enough information to optimize the access to the shared data.
|
||||||
|
|
||||||
|
The `app` attribute has a full view of the application thus it can optimize
|
||||||
|
access to `static` variables. In RTFM we refer to the `static` variables
|
||||||
|
declared inside the `app` pseudo-module as *resources*. To access a resource the
|
||||||
|
context (`init`, `idle`, `interrupt` or `exception`) must first declare the
|
||||||
|
resource in the `resources` argument of its attribute.
|
||||||
|
|
||||||
|
In the example below two interrupt handlers access the same resource. No `Mutex`
|
||||||
|
is required in this case because the two handlers run at the same priority and
|
||||||
|
no preemption is possible. The `SHARED` resource can only be accessed by these
|
||||||
|
two handlers.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/resource.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example resource
|
||||||
|
{{#include ../../../ci/expected/resource.run}}```
|
||||||
|
|
||||||
|
## Priorities
|
||||||
|
|
||||||
|
The priority of each handler can be declared in the `interrupt` and `exception`
|
||||||
|
attributes. It's not possible to set the priority in any other way because the
|
||||||
|
runtime takes ownership of the `NVIC` peripheral; it's also not possible to
|
||||||
|
change the priority of a handler / task at runtime. Thanks to this restriction
|
||||||
|
the framework has knowledge about the *static* priorities of all interrupt and
|
||||||
|
exception handlers.
|
||||||
|
|
||||||
|
Interrupts and exceptions can have priorities in the range `1..=(1 <<
|
||||||
|
NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
|
||||||
|
crate. The `idle` task has a priority of `0`, the lowest priority.
|
||||||
|
|
||||||
|
Resources that are shared between handlers that run at different priorities
|
||||||
|
require critical sections for memory safety. The framework ensures that critical
|
||||||
|
sections are used but *only where required*: for example, no critical section is
|
||||||
|
required by the highest priority handler that has access to the resource.
|
||||||
|
|
||||||
|
The critical section API provided by the RTFM framework (see [`Mutex`]) is
|
||||||
|
based on dynamic priorities rather than on disabling interrupts. The consequence
|
||||||
|
is that these critical sections will prevent *some* handlers, including all the
|
||||||
|
ones that contend for the resource, from *starting* but will let higher priority
|
||||||
|
handlers, that don't contend for the resource, run.
|
||||||
|
|
||||||
|
[`Mutex`]: ../../api/rtfm/trait.Mutex.html
|
||||||
|
|
||||||
|
In the example below we have three interrupt handlers with priorities ranging
|
||||||
|
from one to three. The two handlers with the lower priorities contend for the
|
||||||
|
`SHARED` resource. The lowest priority handler needs to [`lock`] the
|
||||||
|
`SHARED` resource to access its data, whereas the mid priority handler can
|
||||||
|
directly access its data. The highest priority handler is free to preempt
|
||||||
|
the critical section created by the lowest priority handler.
|
||||||
|
|
||||||
|
[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/lock.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example lock
|
||||||
|
{{#include ../../../ci/expected/lock.run}}```
|
||||||
|
|
||||||
|
## Late resources
|
||||||
|
|
||||||
|
Unlike normal `static` variables, which need to be assigned an initial value
|
||||||
|
when declared, resources can be initialized at runtime. We refer to these
|
||||||
|
runtime initialized resources as *late resources*. Late resources are useful for
|
||||||
|
*moving* (as in transferring ownership) peripherals initialized in `init` into
|
||||||
|
interrupt and exception handlers.
|
||||||
|
|
||||||
|
Late resources are declared like normal resources but that are given an initial
|
||||||
|
value of `()` (the unit value). Late resources must be initialized at the end of
|
||||||
|
the `init` function using plain assignments (e.g. `FOO = 1`).
|
||||||
|
|
||||||
|
The example below uses late resources to stablish a lockless, one-way channel
|
||||||
|
between the `UART0` interrupt handler and the `idle` function. A single producer
|
||||||
|
single consumer [`Queue`] is used as the channel. The queue is split into
|
||||||
|
consumer and producer end points in `init` and then each end point is stored
|
||||||
|
in a different resource; `UART0` owns the producer resource and `idle` owns
|
||||||
|
the consumer resource.
|
||||||
|
|
||||||
|
[`Queue`]: ../../api/heapless/spsc/struct.Queue.html
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/late.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example late
|
||||||
|
{{#include ../../../ci/expected/late.run}}```
|
||||||
|
|
||||||
|
## `static` resources
|
||||||
|
|
||||||
|
`static` variables can also be used as resources. Tasks can only get `&`
|
||||||
|
(shared) references to these resources but locks are never required to access
|
||||||
|
their data. You can think of `static` resources as plain `static` variables that
|
||||||
|
can be initialized at runtime and have better scoping rules: you can control
|
||||||
|
which tasks can access the variable, instead of the variable being visible to
|
||||||
|
all the functions in the scope it was declared in.
|
||||||
|
|
||||||
|
In the example below a key is loaded (or created) at runtime and then used from
|
||||||
|
two tasks that run at different priorities.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/static.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example static
|
||||||
|
{{#include ../../../ci/expected/static.run}}```
|
26
book/src/by-example/singletons.md
Normal file
26
book/src/by-example/singletons.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Singletons
|
||||||
|
|
||||||
|
The `app` attribute is aware of [`owned-singleton`] crate and its [`Singleton`]
|
||||||
|
attribute. When this attribute is applied to one of the resources the runtime
|
||||||
|
will perform the `unsafe` initialization of the singleton for you, ensuring that
|
||||||
|
only a single instance of the singleton is ever created.
|
||||||
|
|
||||||
|
[`owned-singleton`]: ../../api/owned_singleton/index.html
|
||||||
|
[`Singleton`]: ../../api/owned_singleton_macros/attr.Singleton.html
|
||||||
|
|
||||||
|
Note that when using the `Singleton` attribute you'll need to have the
|
||||||
|
`owned_singleton` in your dependencies.
|
||||||
|
|
||||||
|
Below is an example that uses the `Singleton` attribute on a chunk of memory
|
||||||
|
and then uses the singleton instance as a fixed-size memory pool using one of
|
||||||
|
the [`alloc-singleton`] abstractions.
|
||||||
|
|
||||||
|
[`alloc-singleton`]: https://crates.io/crates/alloc-singleton
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/singleton.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example singleton
|
||||||
|
{{#include ../../../ci/expected/singleton.run}}```
|
63
book/src/by-example/tasks.md
Normal file
63
book/src/by-example/tasks.md
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# Software tasks
|
||||||
|
|
||||||
|
RTFM treats interrupt and exception handlers as *hardware* tasks. Hardware tasks
|
||||||
|
are invoked by the hardware in response to events, like pressing a button. RTFM
|
||||||
|
also supports *software* tasks which can be spawned by the software from any
|
||||||
|
execution context.
|
||||||
|
|
||||||
|
Software tasks can also be assigned priorities and are dispatched from interrupt
|
||||||
|
handlers. RTFM requires that free interrupts are declared in an `extern` block
|
||||||
|
when using software tasks; these free interrupts will be used to dispatch the
|
||||||
|
software tasks. An advantage of software tasks over hardware tasks is that many
|
||||||
|
tasks can be mapped to a single interrupt handler.
|
||||||
|
|
||||||
|
Software tasks are declared by applying the `task` attribute to functions. To be
|
||||||
|
able to spawn a software task the name of the task must appear in the `spawn`
|
||||||
|
argument of the context attribute (`init`, `idle`, `interrupt`, etc.).
|
||||||
|
|
||||||
|
The example below showcases three software tasks that run at 2 different
|
||||||
|
priorities. The three tasks map to 2 interrupts handlers.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/task.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example task
|
||||||
|
{{#include ../../../ci/expected/task.run}}```
|
||||||
|
|
||||||
|
## Message passing
|
||||||
|
|
||||||
|
The other advantage of software tasks is that messages can be passed to these
|
||||||
|
tasks when spawning them. The type of the message payload must be specified in
|
||||||
|
the signature of the task handler.
|
||||||
|
|
||||||
|
The example below showcases three tasks, two of them expect a message.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/message.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example message
|
||||||
|
{{#include ../../../ci/expected/message.run}}```
|
||||||
|
|
||||||
|
## Capacity
|
||||||
|
|
||||||
|
Task dispatchers do *not* use any dynamic memory allocation. The memory required
|
||||||
|
to store messages is statically reserved. The framework will reserve enough
|
||||||
|
space for every context to be able to spawn each task at most once. This is a
|
||||||
|
sensible default but the "inbox" capacity of each task can be controlled using
|
||||||
|
the `capacicy` argument of the `task` attribute.
|
||||||
|
|
||||||
|
The example below sets the capacity of the software task `foo` to 4. If the
|
||||||
|
capacity is not specified then the second `spawn.foo` call in `UART0` would
|
||||||
|
fail.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/capacity.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example capacity
|
||||||
|
{{#include ../../../ci/expected/capacity.run}}```
|
89
book/src/by-example/timer-queue.md
Normal file
89
book/src/by-example/timer-queue.md
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
# Timer queue
|
||||||
|
|
||||||
|
When the `timer-queue` feature is enabled the RTFM framework includes a *global
|
||||||
|
timer queue* that applications can use to *schedule* software tasks to run some
|
||||||
|
time in the future.
|
||||||
|
|
||||||
|
To be able to schedule a software task the name of the task must appear in the
|
||||||
|
`schedule` argument of the context attribute. When scheduling a task the
|
||||||
|
[`Instant`] at which the task should be executed must be passed as the first
|
||||||
|
argument of the `schedule` invocation.
|
||||||
|
|
||||||
|
[`Instant`]: ../../api/rtfm/struct.Instant.html
|
||||||
|
|
||||||
|
The RTFM runtime includes a monotonic, non-decreasing, 32-bit timer which can be
|
||||||
|
queried using the `Instant::now` constructor. A [`Duration`] can be added to
|
||||||
|
`Instant::now()` to obtain an `Instant` into the future. The monotonic timer is
|
||||||
|
disabled while `init` runs so `Instant::now()` always returns the value
|
||||||
|
`Instant(0 /* clock cycles */)`; the timer is enabled right before the
|
||||||
|
interrupts are re-enabled and `idle` is executed.
|
||||||
|
|
||||||
|
[`Duration`]: ../../api/rtfm/struct.Duration.html
|
||||||
|
|
||||||
|
The example below schedules two tasks from `init`: `foo` and `bar`. `foo` is
|
||||||
|
scheduled to run 8 million clock cycles in the future. Next, `bar` is scheduled
|
||||||
|
to run 4 million clock cycles in the future. `bar` runs before `foo` since it
|
||||||
|
was scheduled to run first.
|
||||||
|
|
||||||
|
> **IMPORTANT**: The examples that use the `schedule` API or the `Instant`
|
||||||
|
> abstraction will **not** properly work on QEMU because the Cortex-M cycle
|
||||||
|
> counter functionality has not been implemented in `qemu-system-arm`.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/schedule.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running the program on real hardware produces the following output in the console:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
{{#include ../../../ci/expected/schedule.run}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Periodic tasks
|
||||||
|
|
||||||
|
Software tasks have access to the `Instant` at which they were scheduled to run
|
||||||
|
through the `scheduled` variable. This information and the `schedule` API can be
|
||||||
|
used to implement periodic tasks as shown in the example below.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/periodic.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the output produced by the example. Note that there is zero drift /
|
||||||
|
jitter even though `schedule.foo` was invoked at the *end* of `foo`. Using
|
||||||
|
`Instant::now` instead of `scheduled` would have resulted in drift / jitter.
|
||||||
|
|
||||||
|
``` text
|
||||||
|
{{#include ../../../ci/expected/periodic.run}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Baseline
|
||||||
|
|
||||||
|
For the tasks scheduled from `init` we have exact information about their
|
||||||
|
`scheduled` time. For hardware tasks there's no `scheduled` time because these
|
||||||
|
tasks are asynchronous in nature. For hardware task the runtime provides a
|
||||||
|
`start` time, which indicates the time at which the task handler started
|
||||||
|
executing.
|
||||||
|
|
||||||
|
Note that `start` is **not** equal to the arrival time of the event that fired
|
||||||
|
the task. Depending on the priority of the task and the load of the system the
|
||||||
|
`start` time could be very far off from the event arrival time.
|
||||||
|
|
||||||
|
What do you think will be the value of `scheduled` for software tasks that are
|
||||||
|
*spawned* instead of scheduled? The answer is that spawned tasks inherit the
|
||||||
|
*baseline* time of the context that spawned it. The baseline of hardware tasks
|
||||||
|
is `start`, the baseline of software tasks is `scheduled` and the baseline of
|
||||||
|
`init` is `start = Instant(0)`. `idle` doesn't really have a baseline but tasks
|
||||||
|
spawned from it will use `Instant::now()` as their baseline time.
|
||||||
|
|
||||||
|
The example below showcases the different meanings of the *baseline*.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/baseline.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running the program on real hardware produces the following output in the console:
|
||||||
|
|
||||||
|
``` text
|
||||||
|
{{#include ../../../ci/expected/baseline.run}}
|
||||||
|
```
|
43
book/src/by-example/tips.md
Normal file
43
book/src/by-example/tips.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Tips & tricks
|
||||||
|
|
||||||
|
## Running tasks from RAM
|
||||||
|
|
||||||
|
The main goal of moving the specification of RTFM applications to attributes in
|
||||||
|
RTFM v0.4.x was to allow inter-operation with other attributes. For example, the
|
||||||
|
`link_section` attribute can be applied to tasks to place them in RAM; this can
|
||||||
|
improve performance in some cases.
|
||||||
|
|
||||||
|
> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle`
|
||||||
|
> attributes are very powerful but also easy to misuse. Incorrectly using any of
|
||||||
|
> these attributes can cause undefined behavior; you should always prefer to use
|
||||||
|
> safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and
|
||||||
|
> `exception` attributes.
|
||||||
|
>
|
||||||
|
> In the particular case of RAM functions there's no
|
||||||
|
> safe abstraction for it in `cortex-m-rt` v0.6.5 but there's an [RFC] for
|
||||||
|
> adding a `ramfunc` attribute in a future release.
|
||||||
|
|
||||||
|
[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100
|
||||||
|
|
||||||
|
The example below shows how to place the higher priority task, `bar`, in RAM.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/ramfunc.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running this program produces the expected output.
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo run --example ramfunc
|
||||||
|
{{#include ../../../ci/expected/ramfunc.run}}```
|
||||||
|
|
||||||
|
One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM
|
||||||
|
(`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`).
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo nm --example ramfunc --release | grep ' foo::'
|
||||||
|
{{#include ../../../ci/expected/ramfunc.grep.foo}}```
|
||||||
|
|
||||||
|
``` console
|
||||||
|
$ cargo nm --example ramfunc --release | grep ' bar::'
|
||||||
|
{{#include ../../../ci/expected/ramfunc.grep.bar}}```
|
60
book/src/by-example/types-send-sync.md
Normal file
60
book/src/by-example/types-send-sync.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# Types, Send and Sync
|
||||||
|
|
||||||
|
The `app` attribute injects a context, a collection of variables, into every
|
||||||
|
function. All these variables have predictable, non-anonymous types so you can
|
||||||
|
write plain functions that take them as arguments.
|
||||||
|
|
||||||
|
The API reference specifies how these types are generated from the input. You
|
||||||
|
can also generate documentation for you binary crate (`cargo doc --bin <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.
|
||||||
|
|
||||||
|
The example below shows the different types generates by the `app` attribute.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/types.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `Send`
|
||||||
|
|
||||||
|
[`Send`] is a marker trait for "types that can be transferred across thread
|
||||||
|
boundaries", according to its definition in `core`. In the context of RTFM the
|
||||||
|
`Send` trait is only required where it's possible to transfer a value between
|
||||||
|
tasks that run at *different* priorities. This occurs in a few places: in message
|
||||||
|
passing, in shared `static mut` resources and in the initialization of late
|
||||||
|
resources.
|
||||||
|
|
||||||
|
[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
|
||||||
|
|
||||||
|
The `app` attribute will enforce that `Send` is implemented where required so
|
||||||
|
you don't need to worry much about it. It's more important to know where you do
|
||||||
|
*not* need the `Send` trait: on types that are transferred between tasks that
|
||||||
|
run at the *same* priority. This occurs in two places: in message passing and in
|
||||||
|
shared `static mut` resources.
|
||||||
|
|
||||||
|
The example below shows where a type that doesn't implement `Send` can be used.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/not-send.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `Sync`
|
||||||
|
|
||||||
|
Similarly, [`Sync`] is a marker trait for "types for which it is safe to share
|
||||||
|
references between threads", according to its definition in `core`. In the
|
||||||
|
context of RTFM the `Sync` trait is only required where it's possible for two,
|
||||||
|
or more, tasks that run at different priority to hold a shared reference to a
|
||||||
|
resource. This only occurs with shared `static` resources.
|
||||||
|
|
||||||
|
[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
|
||||||
|
|
||||||
|
The `app` attribute will enforce that `Sync` is implemented where required but
|
||||||
|
it's important to know where the `Sync` bound is not required: in `static`
|
||||||
|
resources shared between tasks that run at the *same* priority.
|
||||||
|
|
||||||
|
The example below shows where a type that doesn't implement `Sync` can be used.
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
{{#include ../../../examples/not-sync.rs}}
|
||||||
|
```
|
6
book/src/internals.md
Normal file
6
book/src/internals.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Under the hood
|
||||||
|
|
||||||
|
This section describes the internals of the RTFM framework at a *high level*.
|
||||||
|
Low level details like the parsing and code generation done by the procedural
|
||||||
|
macro (`#[app]`) will not be explained here. The focus will be the analysis of
|
||||||
|
the user specification and the data structures used by the runtime.
|
3
book/src/internals/ceilings.md
Normal file
3
book/src/internals/ceilings.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Ceiling analysis
|
||||||
|
|
||||||
|
**TODO**
|
3
book/src/internals/tasks.md
Normal file
3
book/src/internals/tasks.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Task dispatcher
|
||||||
|
|
||||||
|
**TODO**
|
3
book/src/internals/timer-queue.md
Normal file
3
book/src/internals/timer-queue.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Timer queue
|
||||||
|
|
||||||
|
**TODO**
|
12
book/src/preface.md
Normal file
12
book/src/preface.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<h1 align="center">Real Time For the Masses</h1>
|
||||||
|
|
||||||
|
<p align="center">A concurrency framework for building real time systems</p>
|
||||||
|
|
||||||
|
# Preface
|
||||||
|
|
||||||
|
This book contains user level documentation for the Real Time For the Masses
|
||||||
|
(RTFM) framework. The API reference can be found [here](../api/rtfm/index.html).
|
||||||
|
|
||||||
|
{{#include ../../README.md:5:53}}
|
||||||
|
|
||||||
|
{{#include ../../README.md:59:}}
|
4
build.rs
4
build.rs
|
@ -3,8 +3,8 @@ use std::env;
|
||||||
fn main() {
|
fn main() {
|
||||||
let target = env::var("TARGET").unwrap();
|
let target = env::var("TARGET").unwrap();
|
||||||
|
|
||||||
if target.starts_with("thumbv6m-") {
|
if target.starts_with("thumbv7m") | target.starts_with("thumbv7em") {
|
||||||
println!("cargo:rustc-cfg=armv6m");
|
println!("cargo:rustc-cfg=armv7m")
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
cargo doc
|
rm -f .cargo/config
|
||||||
|
cargo doc --features timer-queue
|
||||||
|
( cd book && mdbook build )
|
||||||
|
|
||||||
|
local td=$(mktemp -d)
|
||||||
|
cp -r target/doc $td/api
|
||||||
|
cp -r book/book $td/
|
||||||
|
cp LICENSE-* $td/book/
|
||||||
|
|
||||||
mkdir ghp-import
|
mkdir ghp-import
|
||||||
|
|
||||||
curl -Ls https://github.com/davisp/ghp-import/archive/master.tar.gz |
|
curl -Ls https://github.com/davisp/ghp-import/archive/master.tar.gz |
|
||||||
tar --strip-components 1 -C ghp-import -xz
|
tar --strip-components 1 -C ghp-import -xz
|
||||||
|
|
||||||
./ghp-import/ghp_import.py target/doc
|
./ghp-import/ghp_import.py $td
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
git push -fq https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git gh-pages && echo OK
|
git push -fq https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git gh-pages && echo OK
|
||||||
|
|
||||||
|
rm -rf $td
|
||||||
}
|
}
|
||||||
|
|
||||||
# only publish on successful merges to master
|
if [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ]; then
|
||||||
if [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && [ $TARGET = x86_64-unknown-linux-gnu ]; then
|
|
||||||
main
|
main
|
||||||
fi
|
fi
|
||||||
|
|
4
ci/expected/baseline.run
Normal file
4
ci/expected/baseline.run
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
init(baseline = Instant(0))
|
||||||
|
foo(baseline = Instant(0))
|
||||||
|
UART0(baseline = Instant(904))
|
||||||
|
foo(baseline = Instant(904))
|
5
ci/expected/capacity.run
Normal file
5
ci/expected/capacity.run
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
foo(0)
|
||||||
|
foo(1)
|
||||||
|
foo(2)
|
||||||
|
foo(3)
|
||||||
|
bar
|
2
ci/expected/idle.run
Normal file
2
ci/expected/idle.run
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
init
|
||||||
|
idle
|
1
ci/expected/init.run
Normal file
1
ci/expected/init.run
Normal file
|
@ -0,0 +1 @@
|
||||||
|
init
|
4
ci/expected/interrupt.run
Normal file
4
ci/expected/interrupt.run
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
init
|
||||||
|
UART0 called 1 time
|
||||||
|
idle
|
||||||
|
UART0 called 2 times
|
1
ci/expected/late.run
Normal file
1
ci/expected/late.run
Normal file
|
@ -0,0 +1 @@
|
||||||
|
received message: 42
|
5
ci/expected/lock.run
Normal file
5
ci/expected/lock.run
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
A
|
||||||
|
B - SHARED = 1
|
||||||
|
C
|
||||||
|
D - SHARED = 2
|
||||||
|
E
|
6
ci/expected/message.run
Normal file
6
ci/expected/message.run
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
foo
|
||||||
|
bar(0)
|
||||||
|
baz(1, 2)
|
||||||
|
foo
|
||||||
|
bar(1)
|
||||||
|
baz(2, 3)
|
0
ci/expected/not-send.run
Normal file
0
ci/expected/not-send.run
Normal file
0
ci/expected/not-sync.run
Normal file
0
ci/expected/not-sync.run
Normal file
3
ci/expected/periodic.run
Normal file
3
ci/expected/periodic.run
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
foo(scheduled = Instant(8000000), now = Instant(8000196))
|
||||||
|
foo(scheduled = Instant(16000000), now = Instant(16000196))
|
||||||
|
foo(scheduled = Instant(24000000), now = Instant(24000196))
|
3
ci/expected/ramfunc.grep.bar
Normal file
3
ci/expected/ramfunc.grep.bar
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
20000100 B bar::FREE_QUEUE::lk14244m263eivix
|
||||||
|
200000dc B bar::INPUTS::mi89534s44r1mnj1
|
||||||
|
20000000 T bar::ns9009yhw2dc2y25
|
3
ci/expected/ramfunc.grep.foo
Normal file
3
ci/expected/ramfunc.grep.foo
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
20000100 B foo::FREE_QUEUE::ujkptet2nfdw5t20
|
||||||
|
200000dc B foo::INPUTS::thvubs85b91dg365
|
||||||
|
000002c6 T foo::sidaht420cg1mcm8
|
1
ci/expected/ramfunc.run
Normal file
1
ci/expected/ramfunc.run
Normal file
|
@ -0,0 +1 @@
|
||||||
|
foo
|
2
ci/expected/resource.run
Normal file
2
ci/expected/resource.run
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
UART0: SHARED = 1
|
||||||
|
UART1: SHARED = 2
|
3
ci/expected/schedule.run
Normal file
3
ci/expected/schedule.run
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
init @ Instant(0)
|
||||||
|
bar @ Instant(4000236)
|
||||||
|
foo @ Instant(8000173)
|
2
ci/expected/singleton.run
Normal file
2
ci/expected/singleton.run
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bar(2)
|
||||||
|
foo(1)
|
2
ci/expected/static.run
Normal file
2
ci/expected/static.run
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
UART1(KEY = 0xdeadbeef)
|
||||||
|
UART0(KEY = 0xdeadbeef)
|
3
ci/expected/task.run
Normal file
3
ci/expected/task.run
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
foo
|
||||||
|
baz
|
||||||
|
bar
|
0
ci/expected/types.run
Normal file
0
ci/expected/types.run
Normal file
|
@ -5,9 +5,11 @@ main() {
|
||||||
rustup target add $TARGET
|
rustup target add $TARGET
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir gcc
|
mkdir qemu
|
||||||
|
curl -L https://github.com/japaric/qemu-bin/raw/master/14.04/qemu-system-arm-2.12.0 > qemu/qemu-system-arm
|
||||||
curl -L https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2?revision=bc2c96c0-14b5-4bb4-9f18-bceb4050fee7?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,7-2018-q2-update | tar --strip-components=1 -C gcc -xj
|
chmod +x qemu/qemu-system-arm
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
if [ $TRAVIS_BRANCH != master ] || [ $TRAVIS_PULL_REQUEST != false ]; then
|
||||||
|
main
|
||||||
|
fi
|
||||||
|
|
96
ci/script.sh
96
ci/script.sh
|
@ -1,23 +1,95 @@
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
if [ $TARGET = x86_64-unknown-linux-gnu ]; then
|
local T=$TARGET
|
||||||
cargo build
|
|
||||||
cargo test --test cfail
|
if [ $T = x86_64-unknown-linux-gnu ]; then
|
||||||
|
# compile-fail and compile-pass tests
|
||||||
|
if [ $TRAVIS_RUST_VERSION = nightly ]; then
|
||||||
|
# TODO how to run a subset of these tests when timer-queue is disabled?
|
||||||
|
cargo test --features timer-queue --test compiletest --target $T
|
||||||
|
fi
|
||||||
|
|
||||||
|
cargo check --target $T
|
||||||
|
cargo check --features timer-queue --target $T
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case $TARGET in
|
cargo check --target $T --examples
|
||||||
thumbv7em-none-eabi*)
|
cargo check --features timer-queue --target $T --examples
|
||||||
cargo check --target $TARGET --features cm7-r0p1
|
|
||||||
cargo check --target $TARGET --features cm7-r0p1 --examples
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
cargo check --target $TARGET
|
# run-pass tests
|
||||||
cargo check --target $TARGET --examples
|
case $T in
|
||||||
|
thumbv6m-none-eabi | thumbv7m-none-eabi)
|
||||||
|
local exs=(
|
||||||
|
idle
|
||||||
|
init
|
||||||
|
interrupt
|
||||||
|
|
||||||
|
resource
|
||||||
|
lock
|
||||||
|
late
|
||||||
|
static
|
||||||
|
|
||||||
|
task
|
||||||
|
message
|
||||||
|
capacity
|
||||||
|
|
||||||
|
singleton
|
||||||
|
|
||||||
|
types
|
||||||
|
not-send
|
||||||
|
not-sync
|
||||||
|
|
||||||
|
ramfunc
|
||||||
|
)
|
||||||
|
|
||||||
|
for ex in ${exs[@]}; do
|
||||||
|
if [ $ex = ramfunc ] && [ $T = thumbv6m-none-eabi ]; then
|
||||||
|
# LLD doesn't support this at the moment
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $ex != types ]; then
|
||||||
|
cargo run --example $ex --target $T | \
|
||||||
|
diff -u ci/expected/$ex.run -
|
||||||
|
|
||||||
|
cargo run --example $ex --target $T --release | \
|
||||||
|
diff -u ci/expected/$ex.run -
|
||||||
|
fi
|
||||||
|
|
||||||
|
cargo run --features timer-queue --example $ex --target $T | \
|
||||||
|
diff -u ci/expected/$ex.run -
|
||||||
|
|
||||||
|
cargo run --features timer-queue --example $ex --target $T --release | \
|
||||||
|
diff -u ci/expected/$ex.run -
|
||||||
|
done
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $TRAVIS_BRANCH != master ]; then
|
# fake Travis variables to be able to run this on a local machine
|
||||||
|
if [ -z ${TRAVIS_BRANCH-} ]; then
|
||||||
|
TRAVIS_BRANCH=auto
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z ${TRAVIS_PULL_REQUEST-} ]; then
|
||||||
|
TRAVIS_PULL_REQUEST=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z ${TRAVIS_RUST_VERSION-} ]; then
|
||||||
|
case $(rustc -V) in
|
||||||
|
*nightly*)
|
||||||
|
TRAVIS_RUST_VERSION=nightly
|
||||||
|
;;
|
||||||
|
*beta*)
|
||||||
|
TRAVIS_RUST_VERSION=beta
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
TRAVIS_RUST_VERSION=stable
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $TRAVIS_BRANCH != master ] || [ $TRAVIS_PULL_REQUEST != false ]; then
|
||||||
main
|
main
|
||||||
fi
|
fi
|
||||||
|
|
61
examples/baseline.rs
Normal file
61
examples/baseline.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! examples/baseline.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: does NOT properly work on QEMU
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(spawn = [foo])]
|
||||||
|
fn init() {
|
||||||
|
println!("init(baseline = {:?})", start);
|
||||||
|
|
||||||
|
// `foo` inherits the baseline of `init`: `Instant(0)`
|
||||||
|
spawn.foo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(schedule = [foo])]
|
||||||
|
fn foo() {
|
||||||
|
static mut ONCE: bool = true;
|
||||||
|
|
||||||
|
println!("foo(baseline = {:?})", scheduled);
|
||||||
|
|
||||||
|
if *ONCE {
|
||||||
|
*ONCE = false;
|
||||||
|
|
||||||
|
rtfm::pend(Interrupt::UART0);
|
||||||
|
} else {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt(spawn = [foo])]
|
||||||
|
fn UART0() {
|
||||||
|
println!("UART0(baseline = {:?})", start);
|
||||||
|
|
||||||
|
// `foo` inherits the baseline of `UART0`: its `start` time
|
||||||
|
spawn.foo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
57
examples/capacity.rs
Normal file
57
examples/capacity.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//! examples/capacity.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(spawn = [foo])]
|
||||||
|
fn init() {
|
||||||
|
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();
|
||||||
|
|
||||||
|
spawn.bar().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(capacity = 4)]
|
||||||
|
fn foo(x: u32) {
|
||||||
|
println!("foo({})", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn bar() {
|
||||||
|
println!("bar");
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt handlers used to dispatch software tasks
|
||||||
|
extern "C" {
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,49 +0,0 @@
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Threshold};
|
|
||||||
|
|
||||||
pub struct Foo;
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static CO_OWNED: Foo = Foo;
|
|
||||||
static ON: Foo = Foo;
|
|
||||||
static OWNED: Foo = Foo;
|
|
||||||
static SHARED: Foo = Foo;
|
|
||||||
},
|
|
||||||
|
|
||||||
idle: {
|
|
||||||
resources: [OWNED, SHARED],
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
SYS_TICK: {
|
|
||||||
path: sys_tick,
|
|
||||||
resources: [CO_OWNED, ON, SHARED],
|
|
||||||
},
|
|
||||||
|
|
||||||
TIM2: {
|
|
||||||
enabled: false,
|
|
||||||
path: tim2,
|
|
||||||
priority: 1,
|
|
||||||
resources: [CO_OWNED],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(_p: ::init::Peripherals, _r: ::init::Resources) {}
|
|
||||||
|
|
||||||
fn idle(_t: &mut Threshold, _r: ::idle::Resources) -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sys_tick(_t: &mut Threshold, _r: SYS_TICK::Resources) {}
|
|
||||||
|
|
||||||
fn tim2(_t: &mut Threshold, _r: TIM2::Resources) {}
|
|
|
@ -1,83 +0,0 @@
|
||||||
//! A showcase of the `app!` macro syntax
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Threshold};
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static CO_OWNED: u32 = 0;
|
|
||||||
static ON: bool = false;
|
|
||||||
static OWNED: bool = false;
|
|
||||||
static SHARED: bool = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
init: {
|
|
||||||
// This is the path to the `init` function
|
|
||||||
//
|
|
||||||
// `init` doesn't necessarily has to be in the root of the crate
|
|
||||||
path: main::init,
|
|
||||||
},
|
|
||||||
|
|
||||||
idle: {
|
|
||||||
// This is a path to the `idle` function
|
|
||||||
//
|
|
||||||
// `idle` doesn't necessarily has to be in the root of the crate
|
|
||||||
path: main::idle,
|
|
||||||
resources: [OWNED, SHARED],
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
SYS_TICK: {
|
|
||||||
path: sys_tick,
|
|
||||||
// If omitted priority is assumed to be 1
|
|
||||||
// priority: 1,
|
|
||||||
resources: [CO_OWNED, ON, SHARED],
|
|
||||||
},
|
|
||||||
|
|
||||||
TIM2: {
|
|
||||||
// Tasks are enabled, between `init` and `idle`, by default but they
|
|
||||||
// can start disabled if `false` is specified here
|
|
||||||
enabled: false,
|
|
||||||
path: tim2,
|
|
||||||
priority: 1,
|
|
||||||
resources: [CO_OWNED],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mod main {
|
|
||||||
use rtfm::{self, Resource, Threshold};
|
|
||||||
|
|
||||||
pub fn init(_p: ::init::Peripherals, _r: ::init::Resources) {}
|
|
||||||
|
|
||||||
pub fn idle(t: &mut Threshold, mut r: ::idle::Resources) -> ! {
|
|
||||||
loop {
|
|
||||||
*r.OWNED = !*r.OWNED;
|
|
||||||
|
|
||||||
if *r.OWNED {
|
|
||||||
if r.SHARED.claim(t, |shared, _| *shared) {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r.SHARED.claim_mut(t, |shared, _| *shared = !*shared);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
*r.ON = !*r.ON;
|
|
||||||
|
|
||||||
*r.CO_OWNED += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) {
|
|
||||||
*r.CO_OWNED += 1;
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
//! Working with resources in a generic fashion
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Resource, Threshold};
|
|
||||||
use stm32f103xx::{GPIOA, SPI1};
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static GPIOA: GPIOA;
|
|
||||||
static SPI1: SPI1;
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
EXTI0: {
|
|
||||||
path: exti0,
|
|
||||||
priority: 1,
|
|
||||||
resources: [GPIOA, SPI1],
|
|
||||||
},
|
|
||||||
|
|
||||||
EXTI1: {
|
|
||||||
path: exti1,
|
|
||||||
priority: 2,
|
|
||||||
resources: [GPIOA, SPI1],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(p: init::Peripherals) -> init::LateResources {
|
|
||||||
init::LateResources {
|
|
||||||
GPIOA: p.device.GPIOA,
|
|
||||||
SPI1: p.device.SPI1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle() -> ! {
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A generic function that uses some resources
|
|
||||||
fn work<G, S>(t: &mut Threshold, gpioa: &G, spi1: &S)
|
|
||||||
where
|
|
||||||
G: Resource<Data = GPIOA>,
|
|
||||||
S: Resource<Data = SPI1>,
|
|
||||||
{
|
|
||||||
gpioa.claim(t, |_gpioa, t| {
|
|
||||||
// drive NSS low
|
|
||||||
|
|
||||||
spi1.claim(t, |_spi1, _| {
|
|
||||||
// transfer data
|
|
||||||
});
|
|
||||||
|
|
||||||
// drive NSS high
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// This task needs critical sections to access the resources
|
|
||||||
fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
|
|
||||||
work(t, &r.GPIOA, &r.SPI1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This task has direct access to the resources
|
|
||||||
fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
|
|
||||||
work(t, &r.GPIOA, &r.SPI1);
|
|
||||||
}
|
|
43
examples/idle.rs
Normal file
43
examples/idle.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//! examples/idle.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
println!("init");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[idle]
|
||||||
|
fn idle() -> ! {
|
||||||
|
static mut X: u32 = 0;
|
||||||
|
|
||||||
|
// Safe access to local `static mut` variable
|
||||||
|
let _x: &'static mut u32 = X;
|
||||||
|
|
||||||
|
println!("idle");
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
};
|
44
examples/init.rs
Normal file
44
examples/init.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//! examples/init.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
// NOTE: This convenience macro will appear in all the other examples and
|
||||||
|
// will always look the same
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
static mut X: u32 = 0;
|
||||||
|
|
||||||
|
// Cortex-M peripherals
|
||||||
|
let _core: rtfm::Peripherals = core;
|
||||||
|
|
||||||
|
// Device specific peripherals
|
||||||
|
let _device: lm3s6965::Peripherals = device;
|
||||||
|
|
||||||
|
// Safe access to local `static mut` variable
|
||||||
|
let _x: &'static mut u32 = X;
|
||||||
|
|
||||||
|
println!("init");
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
};
|
61
examples/interrupt.rs
Normal file
61
examples/interrupt.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! examples/interrupt.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
// Pends the UART0 interrupt but its handler won't run until *after*
|
||||||
|
// `init` returns because interrupts are disabled
|
||||||
|
rtfm::pend(Interrupt::UART0);
|
||||||
|
|
||||||
|
println!("init");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[idle]
|
||||||
|
fn idle() -> ! {
|
||||||
|
// interrupts are enabled again; the `UART0` handler runs at this point
|
||||||
|
|
||||||
|
println!("idle");
|
||||||
|
|
||||||
|
rtfm::pend(Interrupt::UART0);
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn UART0() {
|
||||||
|
static mut TIMES: u32 = 0;
|
||||||
|
|
||||||
|
// Safe access to local `static mut` variable
|
||||||
|
*TIMES += 1;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"UART0 called {} time{}",
|
||||||
|
*TIMES,
|
||||||
|
if *TIMES > 1 { "s" } else { "" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,86 +0,0 @@
|
||||||
//! Demonstrates initialization of resources in `init`.
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Threshold};
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
// Usually, resources are initialized with a constant initializer:
|
|
||||||
static ON: bool = false;
|
|
||||||
|
|
||||||
// However, there are cases where this is not possible or not desired.
|
|
||||||
// For example, there may not be a sensible value to use, or the type may
|
|
||||||
// not be constructible in a constant (like `Vec`).
|
|
||||||
//
|
|
||||||
// While it is possible to use an `Option` in some cases, that requires
|
|
||||||
// you to properly initialize it and `.unwrap()` it at every use. It
|
|
||||||
// also consumes more memory.
|
|
||||||
//
|
|
||||||
// To solve this, it is possible to defer initialization of resources to
|
|
||||||
// `init` by omitting the initializer. Doing that will require `init` to
|
|
||||||
// return the values of all "late" resources.
|
|
||||||
static IP_ADDRESS: u32;
|
|
||||||
|
|
||||||
// PORT is used by 2 tasks, making it a shared resource. This just tests
|
|
||||||
// another internal code path and is not important for the example.
|
|
||||||
static PORT: u16;
|
|
||||||
},
|
|
||||||
|
|
||||||
idle: {
|
|
||||||
// Test that late resources can be used in idle
|
|
||||||
resources: [IP_ADDRESS],
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
SYS_TICK: {
|
|
||||||
priority: 1,
|
|
||||||
path: sys_tick,
|
|
||||||
resources: [IP_ADDRESS, PORT, ON],
|
|
||||||
},
|
|
||||||
|
|
||||||
EXTI0: {
|
|
||||||
priority: 2,
|
|
||||||
path: exti0,
|
|
||||||
resources: [PORT],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The signature of `init` is now required to have a specific return type.
|
|
||||||
fn init(_p: init::Peripherals, _r: init::Resources) -> init::LateResources {
|
|
||||||
// `init::Resources` does not contain `IP_ADDRESS`, since it is not yet
|
|
||||||
// initialized.
|
|
||||||
//_r.IP_ADDRESS; // doesn't compile
|
|
||||||
|
|
||||||
// ...obtain value for IP_ADDRESS from EEPROM/DHCP...
|
|
||||||
let ip_address = 0x7f000001;
|
|
||||||
|
|
||||||
init::LateResources {
|
|
||||||
// This struct will contain fields for all resources with omitted
|
|
||||||
// initializers.
|
|
||||||
IP_ADDRESS: ip_address,
|
|
||||||
PORT: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
|
|
||||||
// Other tasks can access late resources like any other, since they are
|
|
||||||
// guaranteed to be initialized when tasks are run.
|
|
||||||
|
|
||||||
r.IP_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exti0(_t: &mut Threshold, _r: EXTI0::Resources) {}
|
|
||||||
|
|
||||||
fn idle(_t: &mut Threshold, _r: idle::Resources) -> ! {
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
65
examples/late.rs
Normal file
65
examples/late.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
//! examples/late.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use heapless::{
|
||||||
|
consts::*,
|
||||||
|
spsc::{Consumer, Producer, Queue},
|
||||||
|
};
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
// Late resources
|
||||||
|
static mut P: Producer<'static, u32, U4> = ();
|
||||||
|
static mut C: Consumer<'static, u32, U4> = ();
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
// NOTE: we use `Option` here to work around the lack of
|
||||||
|
// a stable `const` constructor
|
||||||
|
static mut Q: Option<Queue<u32, U4>> = None;
|
||||||
|
|
||||||
|
*Q = Some(Queue::new());
|
||||||
|
let (p, c) = Q.as_mut().unwrap().split();
|
||||||
|
|
||||||
|
// Initialization of late resources
|
||||||
|
P = p;
|
||||||
|
C = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[idle(resources = [C])]
|
||||||
|
fn idle() -> ! {
|
||||||
|
loop {
|
||||||
|
if let Some(byte) = resources.C.dequeue() {
|
||||||
|
println!("received message: {}", byte);
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
} else {
|
||||||
|
rtfm::pend(Interrupt::UART0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt(resources = [P])]
|
||||||
|
fn UART0() {
|
||||||
|
resources.P.enqueue(42).unwrap();
|
||||||
|
}
|
||||||
|
};
|
71
examples/lock.rs
Normal file
71
examples/lock.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
//! examples/lock.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
static mut SHARED: u32 = 0;
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
rtfm::pend(Interrupt::GPIOA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// when omitted priority is assumed to be `1`
|
||||||
|
#[interrupt(resources = [SHARED])]
|
||||||
|
fn GPIOA() {
|
||||||
|
println!("A");
|
||||||
|
|
||||||
|
// the lower priority task requires a critical section to access the data
|
||||||
|
resources.SHARED.lock(|shared| {
|
||||||
|
// data can only be modified within this critical section (closure)
|
||||||
|
*shared += 1;
|
||||||
|
|
||||||
|
// GPIOB will *not* run right now due to the critical section
|
||||||
|
rtfm::pend(Interrupt::GPIOB);
|
||||||
|
|
||||||
|
println!("B - SHARED = {}", *shared);
|
||||||
|
|
||||||
|
// GPIOC does not contend for `SHARED` so it's allowed to run now
|
||||||
|
rtfm::pend(Interrupt::GPIOC);
|
||||||
|
});
|
||||||
|
|
||||||
|
// critical section is over: GPIOB can now start
|
||||||
|
|
||||||
|
println!("E");
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt(priority = 2, resources = [SHARED])]
|
||||||
|
fn GPIOB() {
|
||||||
|
// the higher priority task does *not* need a critical section
|
||||||
|
*resources.SHARED += 1;
|
||||||
|
|
||||||
|
println!("D - SHARED = {}", *resources.SHARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt(priority = 3)]
|
||||||
|
fn GPIOC() {
|
||||||
|
println!("C");
|
||||||
|
}
|
||||||
|
};
|
61
examples/message.rs
Normal file
61
examples/message.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! examples/message.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(spawn = [foo])]
|
||||||
|
fn init() {
|
||||||
|
spawn.foo(/* no message */).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(spawn = [bar])]
|
||||||
|
fn foo() {
|
||||||
|
static mut COUNT: u32 = 0;
|
||||||
|
|
||||||
|
println!("foo");
|
||||||
|
|
||||||
|
spawn.bar(*COUNT).unwrap();
|
||||||
|
*COUNT += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(spawn = [baz])]
|
||||||
|
fn bar(x: u32) {
|
||||||
|
println!("bar({})", x);
|
||||||
|
|
||||||
|
spawn.baz(x + 1, x + 2).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(spawn = [foo])]
|
||||||
|
fn baz(x: u32, y: u32) {
|
||||||
|
println!("baz({}, {})", x, y);
|
||||||
|
|
||||||
|
if x + y > 4 {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn.foo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,128 +0,0 @@
|
||||||
//! Nesting claims and how the preemption threshold works
|
|
||||||
//!
|
|
||||||
//! If you run this program you'll hit the breakpoints as indicated by the
|
|
||||||
//! letters in the comments: A, then B, then C, etc.
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Resource, Threshold};
|
|
||||||
use stm32f103xx::Interrupt;
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static LOW: u64 = 0;
|
|
||||||
static HIGH: u64 = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
EXTI0: {
|
|
||||||
path: exti0,
|
|
||||||
priority: 1,
|
|
||||||
resources: [LOW, HIGH],
|
|
||||||
},
|
|
||||||
|
|
||||||
EXTI1: {
|
|
||||||
path: exti1,
|
|
||||||
priority: 2,
|
|
||||||
resources: [LOW],
|
|
||||||
},
|
|
||||||
|
|
||||||
EXTI2: {
|
|
||||||
path: exti2,
|
|
||||||
priority: 3,
|
|
||||||
resources: [HIGH],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(_p: init::Peripherals, _r: init::Resources) {}
|
|
||||||
|
|
||||||
fn idle() -> ! {
|
|
||||||
// A
|
|
||||||
rtfm::bkpt();
|
|
||||||
|
|
||||||
// Sets task `exti0` as pending
|
|
||||||
//
|
|
||||||
// Because `exti0` has higher priority than `idle` it will be executed
|
|
||||||
// immediately
|
|
||||||
rtfm::set_pending(Interrupt::EXTI0); // ~> exti0
|
|
||||||
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn exti0(
|
|
||||||
t: &mut Threshold,
|
|
||||||
EXTI0::Resources {
|
|
||||||
LOW: mut low,
|
|
||||||
HIGH: mut high,
|
|
||||||
}: EXTI0::Resources,
|
|
||||||
) {
|
|
||||||
// Because this task has a priority of 1 the preemption threshold `t` also
|
|
||||||
// starts at 1
|
|
||||||
|
|
||||||
// B
|
|
||||||
rtfm::bkpt();
|
|
||||||
|
|
||||||
// Because `exti1` has higher priority than `exti0` it can preempt it
|
|
||||||
rtfm::set_pending(Interrupt::EXTI1); // ~> exti1
|
|
||||||
|
|
||||||
// A claim creates a critical section
|
|
||||||
low.claim_mut(t, |_low, t| {
|
|
||||||
// This claim increases the preemption threshold to 2
|
|
||||||
//
|
|
||||||
// 2 is just high enough to not race with task `exti1` for access to the
|
|
||||||
// `LOW` resource
|
|
||||||
|
|
||||||
// D
|
|
||||||
rtfm::bkpt();
|
|
||||||
|
|
||||||
// Now `exti1` can't preempt this task because its priority is equal to
|
|
||||||
// the current preemption threshold
|
|
||||||
rtfm::set_pending(Interrupt::EXTI1);
|
|
||||||
|
|
||||||
// But `exti2` can, because its priority is higher than the current
|
|
||||||
// preemption threshold
|
|
||||||
rtfm::set_pending(Interrupt::EXTI2); // ~> exti2
|
|
||||||
|
|
||||||
// F
|
|
||||||
rtfm::bkpt();
|
|
||||||
|
|
||||||
// Claims can be nested
|
|
||||||
high.claim_mut(t, |_high, _| {
|
|
||||||
// This claim increases the preemption threshold to 3
|
|
||||||
|
|
||||||
// Now `exti2` can't preempt this task
|
|
||||||
rtfm::set_pending(Interrupt::EXTI2);
|
|
||||||
|
|
||||||
// G
|
|
||||||
rtfm::bkpt();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Upon leaving the critical section the preemption threshold drops back
|
|
||||||
// to 2 and `exti2` immediately preempts this task
|
|
||||||
// ~> exti2
|
|
||||||
});
|
|
||||||
|
|
||||||
// Once again the preemption threshold drops but this time to 1. Now the
|
|
||||||
// pending `exti1` task can preempt this task
|
|
||||||
// ~> exti1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
|
|
||||||
// C, I
|
|
||||||
rtfm::bkpt();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
|
|
||||||
// E, H
|
|
||||||
rtfm::bkpt();
|
|
||||||
}
|
|
58
examples/not-send.rs
Normal file
58
examples/not-send.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//! `examples/not-send.rs`
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_halt;
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
pub struct NotSend {
|
||||||
|
_0: PhantomData<*const ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
static mut SHARED: Option<NotSend> = None;
|
||||||
|
|
||||||
|
#[init(spawn = [baz, quux])]
|
||||||
|
fn init() {
|
||||||
|
spawn.baz().unwrap();
|
||||||
|
spawn.quux().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(spawn = [bar])]
|
||||||
|
fn foo() {
|
||||||
|
// scenario 1: message passed to task that runs at the same priority
|
||||||
|
spawn.bar(NotSend { _0: PhantomData }).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn bar(_x: NotSend) {
|
||||||
|
// scenario 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(priority = 2, resources = [SHARED])]
|
||||||
|
fn baz() {
|
||||||
|
// scenario 2: resource shared between tasks that run at the same priority
|
||||||
|
*resources.SHARED = Some(NotSend { _0: PhantomData });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(priority = 2, resources = [SHARED])]
|
||||||
|
fn quux() {
|
||||||
|
// scenario 2
|
||||||
|
let _not_send = resources.SHARED.take().unwrap();
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
41
examples/not-sync.rs
Normal file
41
examples/not-sync.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//! `examples/not-sync.rs`
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
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)]
|
||||||
|
const APP: () = {
|
||||||
|
static SHARED: NotSync = NotSync { _0: PhantomData };
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(resources = [SHARED])]
|
||||||
|
fn foo() {
|
||||||
|
let _: &NotSync = resources.SHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(resources = [SHARED])]
|
||||||
|
fn bar() {
|
||||||
|
let _: &NotSync = resources.SHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,96 +0,0 @@
|
||||||
//! An application with one task
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m;
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use cortex_m::peripheral::syst::SystClkSource;
|
|
||||||
use rtfm::{app, Threshold};
|
|
||||||
use stm32f103xx::GPIOC;
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
// Here data resources are declared
|
|
||||||
//
|
|
||||||
// Data resources are static variables that are safe to share across tasks
|
|
||||||
resources: {
|
|
||||||
// Declaration of resources looks exactly like declaration of static
|
|
||||||
// variables
|
|
||||||
static ON: bool = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Here tasks are declared
|
|
||||||
//
|
|
||||||
// Each task corresponds to an interrupt or an exception. Every time the
|
|
||||||
// interrupt or exception becomes *pending* the corresponding task handler
|
|
||||||
// will be executed.
|
|
||||||
tasks: {
|
|
||||||
// Here we declare that we'll use the SYS_TICK exception as a task
|
|
||||||
SYS_TICK: {
|
|
||||||
// Path to the task handler
|
|
||||||
path: sys_tick,
|
|
||||||
|
|
||||||
// These are the resources this task has access to.
|
|
||||||
//
|
|
||||||
// The resources listed here must also appear in `app.resources`
|
|
||||||
resources: [ON],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(mut p: init::Peripherals, r: init::Resources) {
|
|
||||||
// `init` can modify all the `resources` declared in `app!`
|
|
||||||
r.ON;
|
|
||||||
|
|
||||||
// power on GPIOC
|
|
||||||
p.device.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
|
|
||||||
|
|
||||||
// configure PC13 as output
|
|
||||||
p.device.GPIOC.bsrr.write(|w| w.bs13().set());
|
|
||||||
p.device
|
|
||||||
.GPIOC
|
|
||||||
.crh
|
|
||||||
.modify(|_, w| w.mode13().output().cnf13().push());
|
|
||||||
|
|
||||||
// configure the system timer to generate one interrupt every second
|
|
||||||
p.core.SYST.set_clock_source(SystClkSource::Core);
|
|
||||||
p.core.SYST.set_reload(8_000_000); // 1s
|
|
||||||
p.core.SYST.enable_interrupt();
|
|
||||||
p.core.SYST.enable_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle() -> ! {
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the task handler of the SYS_TICK exception
|
|
||||||
//
|
|
||||||
// `_t` is the preemption threshold token. We won't use it in this program.
|
|
||||||
//
|
|
||||||
// `r` is the set of resources this task has access to. `SYS_TICK::Resources`
|
|
||||||
// has one field per resource declared in `app!`.
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
// toggle state
|
|
||||||
*r.ON = !*r.ON;
|
|
||||||
|
|
||||||
if *r.ON {
|
|
||||||
// set the pin PC13 high
|
|
||||||
// NOTE(unsafe) atomic write to a stateless register
|
|
||||||
unsafe {
|
|
||||||
(*GPIOC::ptr()).bsrr.write(|w| w.bs13().set());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// set the pin PC13 low
|
|
||||||
// NOTE(unsafe) atomic write to a stateless register
|
|
||||||
unsafe {
|
|
||||||
(*GPIOC::ptr()).bsrr.write(|w| w.br13().reset());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
43
examples/periodic.rs
Normal file
43
examples/periodic.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//! examples/periodic.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use rtfm::{app, Instant};
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const PERIOD: u32 = 8_000_000;
|
||||||
|
|
||||||
|
// NOTE: does NOT work on QEMU!
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(schedule = [foo])]
|
||||||
|
fn init() {
|
||||||
|
schedule.foo(Instant::now() + PERIOD.cycles()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(schedule = [foo])]
|
||||||
|
fn foo() {
|
||||||
|
let now = Instant::now();
|
||||||
|
println!("foo(scheduled = {:?}, now = {:?})", scheduled, now);
|
||||||
|
|
||||||
|
schedule.foo(scheduled + PERIOD.cycles()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,67 +0,0 @@
|
||||||
//! Two tasks running at *different* priorities with access to the same resource
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Resource, Threshold};
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static COUNTER: u64 = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
// The `SYS_TICK` task has higher priority than `TIM2`
|
|
||||||
SYS_TICK: {
|
|
||||||
path: sys_tick,
|
|
||||||
priority: 2,
|
|
||||||
resources: [COUNTER],
|
|
||||||
},
|
|
||||||
|
|
||||||
TIM2: {
|
|
||||||
path: tim2,
|
|
||||||
priority: 1,
|
|
||||||
resources: [COUNTER],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(_p: init::Peripherals, _r: init::Resources) {
|
|
||||||
// ..
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle() -> ! {
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
// ..
|
|
||||||
|
|
||||||
// This task can't be preempted by `tim2` so it has direct access to the
|
|
||||||
// resource data
|
|
||||||
*r.COUNTER += 1;
|
|
||||||
|
|
||||||
// ..
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tim2(t: &mut Threshold, mut r: TIM2::Resources) {
|
|
||||||
// ..
|
|
||||||
|
|
||||||
// As this task runs at lower priority it needs a critical section to
|
|
||||||
// prevent `sys_tick` from preempting it while it modifies this resource
|
|
||||||
// data. The critical section is required to prevent data races which can
|
|
||||||
// lead to undefined behavior.
|
|
||||||
r.COUNTER.claim_mut(t, |counter, _t| {
|
|
||||||
// `claim_mut` creates a critical section
|
|
||||||
*counter += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// ..
|
|
||||||
}
|
|
53
examples/ramfunc.rs
Normal file
53
examples/ramfunc.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//! examples/ramfunc.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(spawn = [bar])]
|
||||||
|
fn init() {
|
||||||
|
spawn.bar().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
#[task]
|
||||||
|
fn foo() {
|
||||||
|
println!("foo");
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// run this task from RAM
|
||||||
|
#[inline(never)]
|
||||||
|
#[link_section = ".data.bar"]
|
||||||
|
#[task(priority = 2, spawn = [foo])]
|
||||||
|
fn bar() {
|
||||||
|
spawn.foo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
|
||||||
|
// run the task dispatcher from RAM
|
||||||
|
#[link_section = ".data.UART1"]
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
60
examples/resource.rs
Normal file
60
examples/resource.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//! examples/resource.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
// A resource
|
||||||
|
static mut SHARED: u32 = 0;
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
rtfm::pend(Interrupt::UART0);
|
||||||
|
rtfm::pend(Interrupt::UART1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[idle]
|
||||||
|
fn idle() -> ! {
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
|
||||||
|
// error: `SHARED` can't be accessed from this context
|
||||||
|
// SHARED += 1;
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `SHARED` can be access from this context
|
||||||
|
#[interrupt(resources = [SHARED])]
|
||||||
|
fn UART0() {
|
||||||
|
*resources.SHARED += 1;
|
||||||
|
|
||||||
|
println!("UART0: SHARED = {}", resources.SHARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `SHARED` can be access from this context
|
||||||
|
#[interrupt(resources = [SHARED])]
|
||||||
|
fn UART1() {
|
||||||
|
*resources.SHARED += 1;
|
||||||
|
|
||||||
|
println!("UART1: SHARED = {}", resources.SHARED);
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,31 +0,0 @@
|
||||||
//! Safe creation of `&'static mut` references
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::app;
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static BUFFER: [u8; 16] = [0; 16];
|
|
||||||
},
|
|
||||||
|
|
||||||
init: {
|
|
||||||
resources: [BUFFER],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(_p: init::Peripherals, r: init::Resources) {
|
|
||||||
let _buf: &'static mut [u8; 16] = r.BUFFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle() -> ! {
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
51
examples/schedule.rs
Normal file
51
examples/schedule.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
//! examples/schedule.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use rtfm::{app, Instant};
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: does NOT work on QEMU!
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(schedule = [foo, bar])]
|
||||||
|
fn init() {
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
println!("init @ {:?}", now);
|
||||||
|
|
||||||
|
// Schedule `foo` to run 8e6 cycles (clock cycles) in the future
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn foo() {
|
||||||
|
println!("foo @ {:?}", Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn bar() {
|
||||||
|
println!("bar @ {:?}", Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
}
|
||||||
|
};
|
69
examples/singleton.rs
Normal file
69
examples/singleton.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//! 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;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[Singleton(Send)]
|
||||||
|
static mut M: [u32; 2] = [0; 2];
|
||||||
|
|
||||||
|
static mut P: Pool<M> = ();
|
||||||
|
|
||||||
|
#[init(resources = [M])]
|
||||||
|
fn init() {
|
||||||
|
rtfm::pend(Interrupt::I2C0);
|
||||||
|
|
||||||
|
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>) {
|
||||||
|
println!("foo({})", x);
|
||||||
|
|
||||||
|
resources.P.lock(|p| p.dealloc(x));
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(priority = 2, resources = [P])]
|
||||||
|
fn bar(x: Box<M>) {
|
||||||
|
println!("bar({})", x);
|
||||||
|
|
||||||
|
resources.P.dealloc(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
17
examples/smallest.rs
Normal file
17
examples/smallest.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
//! examples/smallest.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
// panic-handler crate
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init]
|
||||||
|
fn init() {}
|
||||||
|
};
|
47
examples/static.rs
Normal file
47
examples/static.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//! examples/static.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use lm3s6965::Interrupt;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
static KEY: u32 = ();
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init() {
|
||||||
|
rtfm::pend(Interrupt::UART0);
|
||||||
|
rtfm::pend(Interrupt::UART1);
|
||||||
|
|
||||||
|
KEY = 0xdeadbeef;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt(resources = [KEY])]
|
||||||
|
fn UART0() {
|
||||||
|
println!("UART0(KEY = {:#x})", resources.KEY);
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt(priority = 2, resources = [KEY])]
|
||||||
|
fn UART1() {
|
||||||
|
println!("UART1(KEY = {:#x})", resources.KEY);
|
||||||
|
}
|
||||||
|
};
|
61
examples/task.rs
Normal file
61
examples/task.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! examples/task.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::app;
|
||||||
|
|
||||||
|
macro_rules! println {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() {
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
writeln!(stdout, $($tt)*).ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[app(device = lm3s6965)]
|
||||||
|
const APP: () = {
|
||||||
|
#[init(spawn = [foo])]
|
||||||
|
fn init() {
|
||||||
|
spawn.foo().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(spawn = [bar, baz])]
|
||||||
|
fn foo() {
|
||||||
|
println!("foo");
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// spawns `baz` onto the task scheduler
|
||||||
|
// `baz` has higher priority than `foo` so it immediately preempts `foo`
|
||||||
|
spawn.baz().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
fn bar() {
|
||||||
|
println!("bar");
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(priority = 2)]
|
||||||
|
fn baz() {
|
||||||
|
println!("baz");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt handlers used to dispatch software tasks
|
||||||
|
extern "C" {
|
||||||
|
fn UART0();
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,58 +0,0 @@
|
||||||
//! Two tasks running at the *same* priority with access to the same resource
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Threshold};
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
static COUNTER: u64 = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Both SYS_TICK and TIM2 have access to the `COUNTER` data
|
|
||||||
tasks: {
|
|
||||||
SYS_TICK: {
|
|
||||||
path: sys_tick,
|
|
||||||
resources: [COUNTER],
|
|
||||||
},
|
|
||||||
|
|
||||||
TIM2: {
|
|
||||||
path: tim2,
|
|
||||||
resources: [COUNTER],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(_p: init::Peripherals, _r: init::Resources) {
|
|
||||||
// ..
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle() -> ! {
|
|
||||||
loop {
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// As both tasks are running at the same priority one can't preempt the other.
|
|
||||||
// Thus both tasks have direct access to the resource
|
|
||||||
fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
// ..
|
|
||||||
|
|
||||||
*r.COUNTER += 1;
|
|
||||||
|
|
||||||
// ..
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) {
|
|
||||||
// ..
|
|
||||||
|
|
||||||
*r.COUNTER += 1;
|
|
||||||
|
|
||||||
// ..
|
|
||||||
}
|
|
54
examples/types.rs
Normal file
54
examples/types.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//! examples/types.rs
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![deny(warnings)]
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate panic_semihosting;
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
use rtfm::{app, Instant};
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
|
||||||
|
debug::exit(debug::EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[exception(schedule = [foo], spawn = [foo])]
|
||||||
|
fn SVCall() {
|
||||||
|
let _: Instant = start;
|
||||||
|
let _: SVCall::Schedule = schedule;
|
||||||
|
let _: SVCall::Spawn = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])]
|
||||||
|
fn foo() {
|
||||||
|
let _: Instant = scheduled;
|
||||||
|
let _: foo::Resources = resources;
|
||||||
|
let _: foo::Schedule = schedule;
|
||||||
|
let _: foo::Spawn = spawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn UART1();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,43 +0,0 @@
|
||||||
//! Minimal example with zero tasks
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm; // IMPORTANT always do this rename
|
|
||||||
extern crate stm32f103xx; // the device crate
|
|
||||||
|
|
||||||
// import the procedural macro
|
|
||||||
use rtfm::app;
|
|
||||||
|
|
||||||
// This macro call indicates that this is a RTFM application
|
|
||||||
//
|
|
||||||
// This macro will expand to a `main` function so you don't need to supply
|
|
||||||
// `main` yourself.
|
|
||||||
app! {
|
|
||||||
// this is the path to the device crate
|
|
||||||
device: stm32f103xx,
|
|
||||||
}
|
|
||||||
|
|
||||||
// The initialization phase.
|
|
||||||
//
|
|
||||||
// This runs first and within a *global* critical section. Nothing can preempt
|
|
||||||
// this function.
|
|
||||||
fn init(p: init::Peripherals) {
|
|
||||||
// This function has access to all the peripherals of the device
|
|
||||||
p.core.SYST;
|
|
||||||
p.device.GPIOA;
|
|
||||||
p.device.RCC;
|
|
||||||
// ..
|
|
||||||
}
|
|
||||||
|
|
||||||
// The idle loop.
|
|
||||||
//
|
|
||||||
// This runs after `init` and has a priority of 0. All tasks can preempt this
|
|
||||||
// function. This function can never return so it must contain some sort of
|
|
||||||
// endless loop.
|
|
||||||
fn idle() -> ! {
|
|
||||||
loop {
|
|
||||||
// This puts the processor to sleep until there's a task to service
|
|
||||||
rtfm::wfi();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
# Converts the examples in the `examples` directory into documentation in the
|
|
||||||
# `examples` module (`src/examples/*.rs`)
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
main() {
|
|
||||||
local examples=(
|
|
||||||
zero-tasks
|
|
||||||
one-task
|
|
||||||
two-tasks
|
|
||||||
preemption
|
|
||||||
nested
|
|
||||||
late-resources
|
|
||||||
safe-static-mut-ref
|
|
||||||
generics
|
|
||||||
full-syntax
|
|
||||||
)
|
|
||||||
|
|
||||||
rm -rf src/examples
|
|
||||||
|
|
||||||
mkdir src/examples
|
|
||||||
|
|
||||||
cat >src/examples/mod.rs <<'EOF'
|
|
||||||
//! Examples
|
|
||||||
// Auto-generated. Do not modify.
|
|
||||||
EOF
|
|
||||||
|
|
||||||
local i=0 out=
|
|
||||||
for ex in ${examples[@]}; do
|
|
||||||
name=_${i}_${ex//-/_}
|
|
||||||
out=src/examples/${name}.rs
|
|
||||||
|
|
||||||
echo "pub mod $name;" >> src/examples/mod.rs
|
|
||||||
|
|
||||||
grep '//!' examples/$ex.rs > $out
|
|
||||||
echo '//!' >> $out
|
|
||||||
echo '//! ```' >> $out
|
|
||||||
grep -v '//!' examples/$ex.rs | (
|
|
||||||
IFS=''
|
|
||||||
|
|
||||||
while read line; do
|
|
||||||
echo "//! $line" >> $out;
|
|
||||||
done
|
|
||||||
)
|
|
||||||
echo '//! ```' >> $out
|
|
||||||
echo '// Auto-generated. Do not modify.' >> $out
|
|
||||||
|
|
||||||
|
|
||||||
chmod -x $out
|
|
||||||
|
|
||||||
i=$(( i + 1 ))
|
|
||||||
done
|
|
||||||
|
|
||||||
chmod -x src/examples/mod.rs
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
|
@ -2,19 +2,28 @@
|
||||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||||
categories = ["concurrency", "embedded", "no-std"]
|
categories = ["concurrency", "embedded", "no-std"]
|
||||||
description = "Procedural macros of the cortex-m-rtfm crate"
|
description = "Procedural macros of the cortex-m-rtfm crate"
|
||||||
documentation = "https://docs.rs/cortex-m-rtfm-macros"
|
documentation = "https://japaric.github.io/cortex-m-rtfm/api/cortex_m_rtfm"
|
||||||
keywords = ["arm", "cortex-m"]
|
keywords = ["arm", "cortex-m"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
name = "cortex-m-rtfm-macros"
|
name = "cortex-m-rtfm-macros"
|
||||||
|
readme = "../README.md"
|
||||||
repository = "https://github.com/japaric/cortex-m-rtfm"
|
repository = "https://github.com/japaric/cortex-m-rtfm"
|
||||||
version = "0.3.2"
|
version = "0.4.0-beta.1"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
failure = "0.1.1"
|
|
||||||
proc-macro2 = "0.4.6"
|
|
||||||
quote = "0.6.3"
|
|
||||||
rtfm-syntax = "0.3.4"
|
|
||||||
syn = "0.14.2"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "0.6.8"
|
||||||
|
proc-macro2 = "0.4.20"
|
||||||
|
|
||||||
|
[dependencies.syn]
|
||||||
|
features = ["extra-traits", "full"]
|
||||||
|
version = "0.15.6"
|
||||||
|
|
||||||
|
[dependencies.rand]
|
||||||
|
default-features = false
|
||||||
|
version = "0.5.5"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
timer-queue = []
|
|
@ -1,77 +1,243 @@
|
||||||
use std::cmp;
|
use std::{
|
||||||
use std::collections::HashMap;
|
cmp,
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
};
|
||||||
|
|
||||||
use syn::Ident;
|
use syn::{Attribute, Ident, Type};
|
||||||
|
|
||||||
use check::App;
|
use syntax::{App, Idents};
|
||||||
|
|
||||||
pub type Ownerships = HashMap<Ident, Ownership>;
|
pub type Ownerships = HashMap<Ident, Ownership>;
|
||||||
|
|
||||||
|
pub struct Analysis {
|
||||||
|
/// Capacities of free queues
|
||||||
|
pub capacities: Capacities,
|
||||||
|
pub dispatchers: Dispatchers,
|
||||||
|
// Ceilings of free queues
|
||||||
|
pub free_queues: HashMap<Ident, u8>,
|
||||||
|
pub resources_assert_send: HashSet<Box<Type>>,
|
||||||
|
pub tasks_assert_send: HashSet<Ident>,
|
||||||
|
/// Types of RO resources that need to be Sync
|
||||||
|
pub assert_sync: HashSet<Box<Type>>,
|
||||||
|
// Resource ownership
|
||||||
|
pub ownerships: Ownerships,
|
||||||
|
// Ceilings of ready queues
|
||||||
|
pub ready_queues: HashMap<u8, u8>,
|
||||||
|
pub timer_queue: TimerQueue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum Ownership {
|
pub enum Ownership {
|
||||||
/// Owned or co-owned by tasks that run at the same priority
|
// NOTE priorities and ceilings are "logical" (0 = lowest priority, 255 = highest priority)
|
||||||
Owned { priority: u8 },
|
Owned { priority: u8 },
|
||||||
/// Shared by tasks that run at different priorities.
|
|
||||||
///
|
|
||||||
/// `ceiling` is the maximum value across all the task priorities
|
|
||||||
Shared { ceiling: u8 },
|
Shared { ceiling: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ownership {
|
impl Ownership {
|
||||||
pub fn ceiling(&self) -> u8 {
|
pub fn needs_lock(&self, priority: u8) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Ownership::Owned { priority } => priority,
|
Ownership::Owned { .. } => false,
|
||||||
Ownership::Shared { ceiling } => ceiling,
|
Ownership::Shared { ceiling } => {
|
||||||
}
|
debug_assert!(ceiling >= priority);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_owned(&self) -> bool {
|
priority < ceiling
|
||||||
match *self {
|
}
|
||||||
Ownership::Owned { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app(app: &App) -> Ownerships {
|
pub struct Dispatcher {
|
||||||
let mut ownerships = HashMap::new();
|
/// Attributes to apply to the dispatcher
|
||||||
|
pub attrs: Vec<Attribute>,
|
||||||
|
pub interrupt: Ident,
|
||||||
|
/// Tasks dispatched at this priority level
|
||||||
|
pub tasks: Vec<Ident>,
|
||||||
|
// Queue capacity
|
||||||
|
pub capacity: u8,
|
||||||
|
}
|
||||||
|
|
||||||
for resource in &app.idle.resources {
|
/// Priority -> Dispatcher
|
||||||
ownerships.insert(resource.clone(), Ownership::Owned { priority: 0 });
|
pub type Dispatchers = HashMap<u8, Dispatcher>;
|
||||||
}
|
|
||||||
|
|
||||||
for task in app.tasks.values() {
|
pub type Capacities = HashMap<Ident, u8>;
|
||||||
for resource in task.resources.iter() {
|
|
||||||
if let Some(ownership) = ownerships.get_mut(resource) {
|
pub fn app(app: &App) -> Analysis {
|
||||||
match *ownership {
|
// Ceiling analysis of R/W resource and Sync analysis of RO resources
|
||||||
Ownership::Owned { priority } => {
|
// (Resource shared by tasks that run at different priorities need to be `Sync`)
|
||||||
if priority == task.priority {
|
let mut ownerships = Ownerships::new();
|
||||||
*ownership = Ownership::Owned { priority };
|
let mut resources_assert_send = HashSet::new();
|
||||||
} else {
|
let mut tasks_assert_send = HashSet::new();
|
||||||
*ownership = Ownership::Shared {
|
let mut assert_sync = HashSet::new();
|
||||||
ceiling: cmp::max(priority, task.priority),
|
|
||||||
};
|
for (priority, res) in app.resource_accesses() {
|
||||||
}
|
if let Some(ownership) = ownerships.get_mut(res) {
|
||||||
}
|
match *ownership {
|
||||||
Ownership::Shared { ceiling } => {
|
Ownership::Owned { priority: ceiling } | Ownership::Shared { ceiling } => {
|
||||||
if task.priority > ceiling {
|
if priority != ceiling {
|
||||||
*ownership = Ownership::Shared {
|
*ownership = Ownership::Shared {
|
||||||
ceiling: task.priority,
|
ceiling: cmp::max(ceiling, priority),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let res = &app.resources[res];
|
||||||
|
if res.mutability.is_none() {
|
||||||
|
assert_sync.insert(res.ty.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ownerships.insert(
|
continue;
|
||||||
resource.clone(),
|
}
|
||||||
Ownership::Owned {
|
|
||||||
priority: task.priority,
|
ownerships.insert(res.clone(), Ownership::Owned { priority });
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
// Compute sizes of free queues
|
||||||
|
// We assume at most one message per `spawn` / `schedule`
|
||||||
|
let mut capacities: Capacities = app.tasks.keys().map(|task| (task.clone(), 0)).collect();
|
||||||
|
for (_, task) in app.spawn_calls().chain(app.schedule_calls()) {
|
||||||
|
*capacities.get_mut(task).expect("BUG: capacities.get_mut") += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override computed capacities if user specified a capacity in `#[task]`
|
||||||
|
for (name, task) in &app.tasks {
|
||||||
|
if let Some(cap) = task.args.capacity {
|
||||||
|
*capacities.get_mut(name).expect("BUG: capacities.get_mut") = cap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ownerships
|
// Compute the size of the timer queue
|
||||||
|
// Compute the priority of the timer queue, which matches the priority of the highest
|
||||||
|
// `schedule`-able task
|
||||||
|
let mut tq_capacity = 0;
|
||||||
|
let mut tq_priority = 1;
|
||||||
|
let mut tq_tasks = Idents::new();
|
||||||
|
for (_, task) in app.schedule_calls() {
|
||||||
|
tq_capacity += capacities[task];
|
||||||
|
tq_priority = cmp::max(tq_priority, app.tasks[task].args.priority);
|
||||||
|
tq_tasks.insert(task.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute dispatchers capacities
|
||||||
|
// Determine which tasks are dispatched by which dispatcher
|
||||||
|
// Compute the timer queue priority which matches the priority of the highest priority
|
||||||
|
// dispatcher
|
||||||
|
let mut dispatchers = Dispatchers::new();
|
||||||
|
let mut free_interrupts = app.free_interrupts.iter();
|
||||||
|
let mut tasks = app.tasks.iter().collect::<Vec<_>>();
|
||||||
|
tasks.sort_by(|l, r| l.1.args.priority.cmp(&r.1.args.priority));
|
||||||
|
for (name, task) in tasks {
|
||||||
|
let dispatcher = dispatchers.entry(task.args.priority).or_insert_with(|| {
|
||||||
|
let (name, fi) = free_interrupts
|
||||||
|
.next()
|
||||||
|
.expect("BUG: not enough free_interrupts");
|
||||||
|
|
||||||
|
Dispatcher {
|
||||||
|
attrs: fi.attrs.clone(),
|
||||||
|
capacity: 0,
|
||||||
|
interrupt: name.clone(),
|
||||||
|
tasks: vec![],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatcher.capacity += capacities[name];
|
||||||
|
dispatcher.tasks.push(name.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// All messages sent from `init` need to be `Send`
|
||||||
|
for task in app.init.args.spawn.iter().chain(&app.init.args.schedule) {
|
||||||
|
tasks_assert_send.insert(task.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// All late resources need to be `Send`, unless they are owned by `idle`
|
||||||
|
for (name, res) in &app.resources {
|
||||||
|
let owned_by_idle = Ownership::Owned { priority: 0 };
|
||||||
|
if res.expr.is_none()
|
||||||
|
&& ownerships
|
||||||
|
.get(name)
|
||||||
|
.map(|ship| *ship != owned_by_idle)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
resources_assert_send.insert(res.ty.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All resources shared with init need to be `Send`, unless they are owned by `idle`
|
||||||
|
// This is equivalent to late initialization (e.g. `static mut LATE: Option<T> = None`)
|
||||||
|
for name in &app.init.args.resources {
|
||||||
|
let owned_by_idle = Ownership::Owned { priority: 0 };
|
||||||
|
if ownerships
|
||||||
|
.get(name)
|
||||||
|
.map(|ship| *ship != owned_by_idle)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
resources_assert_send.insert(app.resources[name].ty.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ceiling analysis of free queues (consumer end point) -- first pass
|
||||||
|
// Ceiling analysis of ready queues (producer end point)
|
||||||
|
// 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();
|
||||||
|
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");
|
||||||
|
*c = cmp::max(*c, priority);
|
||||||
|
|
||||||
|
let c = ready_queues
|
||||||
|
.get_mut(&app.tasks[task].args.priority)
|
||||||
|
.expect("BUG: ready_queues.get_mut");
|
||||||
|
*c = cmp::max(*c, priority);
|
||||||
|
|
||||||
|
// Send is required when sending messages from a task whose priority doesn't match the
|
||||||
|
// priority of the receiving task
|
||||||
|
if app.tasks[task].args.priority != priority {
|
||||||
|
tasks_assert_send.insert(task.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// spawns from `init` are excluded from the ceiling analysis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
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");
|
||||||
|
*c = cmp::max(*c, priority);
|
||||||
|
|
||||||
|
// Users of `schedule` contend for the timer queu
|
||||||
|
tq_ceiling = cmp::max(tq_ceiling, priority);
|
||||||
|
} else {
|
||||||
|
// spawns from `init` are excluded from the ceiling analysis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Analysis {
|
||||||
|
capacities,
|
||||||
|
dispatchers,
|
||||||
|
free_queues,
|
||||||
|
tasks_assert_send,
|
||||||
|
resources_assert_send,
|
||||||
|
assert_sync,
|
||||||
|
ownerships,
|
||||||
|
ready_queues,
|
||||||
|
timer_queue: TimerQueue {
|
||||||
|
capacity: tq_capacity,
|
||||||
|
ceiling: tq_ceiling,
|
||||||
|
priority: tq_priority,
|
||||||
|
tasks: tq_tasks,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TimerQueue {
|
||||||
|
pub capacity: u8,
|
||||||
|
pub ceiling: u8,
|
||||||
|
pub priority: u8,
|
||||||
|
pub tasks: Idents,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,115 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashSet, iter};
|
||||||
|
|
||||||
use syn::{Ident, Path};
|
use proc_macro2::Span;
|
||||||
use syntax::check::{self, Idents, Idle, Init, Statics};
|
use syn::parse;
|
||||||
use syntax::{self, Result};
|
|
||||||
|
|
||||||
pub struct App {
|
use syntax::App;
|
||||||
pub device: Path,
|
|
||||||
pub idle: Idle,
|
|
||||||
pub init: Init,
|
|
||||||
pub resources: Statics,
|
|
||||||
pub tasks: Tasks,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Tasks = HashMap<Ident, Task>;
|
pub fn app(app: &App) -> parse::Result<()> {
|
||||||
|
// Check that all referenced resources have been declared
|
||||||
|
for res in app
|
||||||
|
.idle
|
||||||
|
.as_ref()
|
||||||
|
.map(|idle| -> Box<Iterator<Item = _>> { Box::new(idle.args.resources.iter()) })
|
||||||
|
.unwrap_or_else(|| Box::new(iter::empty()))
|
||||||
|
.chain(&app.init.args.resources)
|
||||||
|
.chain(app.exceptions.values().flat_map(|e| &e.args.resources))
|
||||||
|
.chain(app.interrupts.values().flat_map(|i| &i.args.resources))
|
||||||
|
.chain(app.tasks.values().flat_map(|t| &t.args.resources))
|
||||||
|
{
|
||||||
|
if !app.resources.contains_key(res) {
|
||||||
|
return Err(parse::Error::new(
|
||||||
|
res.span(),
|
||||||
|
"this resource has NOT been declared",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
// Check that late resources have not been assigned to `init`
|
||||||
pub enum Exception {
|
for res in &app.init.args.resources {
|
||||||
PENDSV,
|
if app.resources.get(res).unwrap().expr.is_none() {
|
||||||
SVCALL,
|
return Err(parse::Error::new(
|
||||||
SYS_TICK,
|
res.span(),
|
||||||
}
|
"late resources can NOT be assigned to `init`",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Exception {
|
// Check that all late resources have been initialized in `#[init]`
|
||||||
pub fn from(s: &str) -> Option<Self> {
|
for res in app
|
||||||
Some(match s {
|
.resources
|
||||||
"PENDSV" => Exception::PENDSV,
|
.iter()
|
||||||
"SVCALL" => Exception::SVCALL,
|
.filter_map(|(name, res)| if res.expr.is_none() { Some(name) } else { None })
|
||||||
"SYS_TICK" => Exception::SYS_TICK,
|
{
|
||||||
_ => return None,
|
if app.init.assigns.iter().all(|assign| assign.left != *res) {
|
||||||
|
return Err(parse::Error::new(
|
||||||
|
res.span(),
|
||||||
|
"late resources MUST be initialized at the end of `init`",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all referenced tasks have been declared
|
||||||
|
for task in app
|
||||||
|
.idle
|
||||||
|
.as_ref()
|
||||||
|
.map(|idle| -> Box<Iterator<Item = _>> {
|
||||||
|
Box::new(idle.args.schedule.iter().chain(&idle.args.spawn))
|
||||||
})
|
})
|
||||||
}
|
.unwrap_or_else(|| Box::new(iter::empty()))
|
||||||
|
.chain(&app.init.args.schedule)
|
||||||
pub fn nr(&self) -> usize {
|
.chain(&app.init.args.spawn)
|
||||||
match *self {
|
.chain(
|
||||||
Exception::PENDSV => 14,
|
app.exceptions
|
||||||
Exception::SVCALL => 11,
|
.values()
|
||||||
Exception::SYS_TICK => 15,
|
.flat_map(|e| e.args.schedule.iter().chain(&e.args.spawn)),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
app.interrupts
|
||||||
|
.values()
|
||||||
|
.flat_map(|i| i.args.schedule.iter().chain(&i.args.spawn)),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
app.tasks
|
||||||
|
.values()
|
||||||
|
.flat_map(|t| t.args.schedule.iter().chain(&t.args.spawn)),
|
||||||
|
) {
|
||||||
|
if !app.tasks.contains_key(task) {
|
||||||
|
return Err(parse::Error::new(
|
||||||
|
task.span(),
|
||||||
|
"this task has NOT been declared",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Kind {
|
// Check that there are enough free interrupts to dispatch all tasks
|
||||||
Exception(Exception),
|
let ndispatchers = app
|
||||||
Interrupt { enabled: bool },
|
.tasks
|
||||||
}
|
.values()
|
||||||
|
.map(|t| t.args.priority)
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.len();
|
||||||
|
if ndispatchers > app.free_interrupts.len() {
|
||||||
|
return Err(parse::Error::new(
|
||||||
|
Span::call_site(),
|
||||||
|
&*format!(
|
||||||
|
"{} free interrupt{} (`extern {{ .. }}`) {} required to dispatch all soft tasks",
|
||||||
|
ndispatchers,
|
||||||
|
if ndispatchers > 1 { "s" } else { "" },
|
||||||
|
if ndispatchers > 1 { "are" } else { "is" },
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Task {
|
// Check that free interrupts are not being used
|
||||||
pub kind: Kind,
|
for int in app.interrupts.keys() {
|
||||||
pub path: Path,
|
if app.free_interrupts.contains_key(int) {
|
||||||
pub priority: u8,
|
return Err(parse::Error::new(
|
||||||
pub resources: Idents,
|
int.span(),
|
||||||
}
|
"free interrupts (`extern { .. }`) can't be used as interrupt handlers",
|
||||||
|
));
|
||||||
pub fn app(app: check::App) -> Result<App> {
|
|
||||||
let app = App {
|
|
||||||
device: app.device,
|
|
||||||
idle: app.idle,
|
|
||||||
init: app.init,
|
|
||||||
resources: app.resources,
|
|
||||||
tasks: app.tasks
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
let v = ::check::task(&k.to_string(), v)?;
|
|
||||||
|
|
||||||
Ok((k, v))
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn task(name: &str, task: syntax::check::Task) -> Result<Task> {
|
|
||||||
let kind = match Exception::from(name) {
|
|
||||||
Some(e) => {
|
|
||||||
ensure!(
|
|
||||||
task.enabled.is_none(),
|
|
||||||
"`enabled` field is not valid for exceptions"
|
|
||||||
);
|
|
||||||
|
|
||||||
Kind::Exception(e)
|
|
||||||
}
|
}
|
||||||
None => Kind::Interrupt {
|
}
|
||||||
enabled: task.enabled.unwrap_or(true),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Task {
|
Ok(())
|
||||||
kind,
|
|
||||||
path: task.path,
|
|
||||||
priority: task.priority.unwrap_or(1),
|
|
||||||
resources: task.resources,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
1815
macros/src/codegen.rs
Normal file
1815
macros/src/codegen.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,185 +1,312 @@
|
||||||
//! Procedural macros of the `cortex-m-rtfm` crate
|
|
||||||
// #![deny(warnings)]
|
// #![deny(warnings)]
|
||||||
#![recursion_limit = "128"]
|
#![recursion_limit = "128"]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate failure;
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
extern crate proc_macro2;
|
||||||
extern crate syn;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
extern crate rtfm_syntax as syntax;
|
extern crate rand;
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syntax::{App, Result};
|
use syn::parse_macro_input;
|
||||||
|
|
||||||
mod analyze;
|
mod analyze;
|
||||||
mod check;
|
mod check;
|
||||||
mod trans;
|
mod codegen;
|
||||||
|
mod syntax;
|
||||||
|
|
||||||
/// The `app!` macro, a macro used to specify the tasks and resources of a RTFM application.
|
/// Attribute used to declare a RTFM application
|
||||||
///
|
///
|
||||||
/// The contents of this macro uses a `key: value` syntax. All the possible keys are shown below:
|
/// This attribute must be applied to a `const` item of type `()`. The `const` item is effectively
|
||||||
|
/// used as a `mod` item: its value must be a block that contains items commonly found in modules,
|
||||||
|
/// like functions and `static` variables.
|
||||||
///
|
///
|
||||||
/// ``` text
|
/// The `app` attribute has one mandatory argument:
|
||||||
/// app! {
|
|
||||||
/// device: ..,
|
|
||||||
///
|
///
|
||||||
/// resources: { .. },
|
/// - `device = <path>`. The path must point to a device crate generated using [`svd2rust`]
|
||||||
|
/// **v0.14.x**.
|
||||||
///
|
///
|
||||||
/// init: { .. },
|
/// [`svd2rust`]: https://crates.io/crates/svd2rust
|
||||||
///
|
///
|
||||||
/// idle: { .. },
|
/// The items allowed in the block value of the `const` item are specified below:
|
||||||
///
|
///
|
||||||
/// tasks: { .. },
|
/// # 1. `static [mut]` variables
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// # `device`
|
/// These variables are used as *resources*. Resources can be owned by tasks or shared between them.
|
||||||
|
/// Tasks can get `&mut` (exclusives) references to `static mut` resources, but only `&` (shared)
|
||||||
|
/// references to `static` resources. Lower priority tasks will need a [`lock`] to get a `&mut`
|
||||||
|
/// reference to a `static mut` resource shared with higher priority tasks.
|
||||||
///
|
///
|
||||||
/// The value of this key is a Rust path, like `foo::bar::baz`, that must point to a *device crate*,
|
/// [`lock`]: ../rtfm/trait.Mutex.html#method.lock
|
||||||
/// a crate generated using `svd2rust`.
|
|
||||||
///
|
///
|
||||||
/// # `resources`
|
/// `static mut` resources that are shared by tasks that run at *different* priorities need to
|
||||||
|
/// implement the [`Send`] trait. Similarly, `static` resources that are shared by tasks that run at
|
||||||
|
/// *different* priorities need to implement the [`Sync`] trait.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a list of `static` variables. These variables are the data
|
/// [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
|
||||||
/// that can be safely accessed, modified and shared by tasks.
|
/// [`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
|
||||||
///
|
///
|
||||||
/// ``` text
|
/// Resources can be initialized at runtime by assigning them `()` (the unit value) as their initial
|
||||||
/// resources: {
|
/// value in their declaration. These "late" resources need to be initialized an the end of the
|
||||||
/// static A: bool = false;
|
/// `init` function.
|
||||||
/// static B: i32 = 0;
|
|
||||||
/// static C: [u8; 16] = [0; 16];
|
|
||||||
/// static D: Thing = Thing::new(..);
|
|
||||||
/// static E: Thing;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// The initial value of a resource can be omitted. This means that the resource will be runtime
|
/// The `app` attribute will inject a `resources` module in the root of the crate. This module
|
||||||
/// initialized; these runtime initialized resources are also known as *late resources*.
|
/// contains proxy `struct`s that implement the [`Mutex`] trait. The `struct` are named after the
|
||||||
|
/// `static mut` resources. For example, `static mut FOO: u32 = 0` will map to a `resources::FOO`
|
||||||
|
/// `struct` that implements the `Mutex<Data = u32>` trait.
|
||||||
///
|
///
|
||||||
/// If this key is omitted its value defaults to an empty list.
|
/// [`Mutex`]: ../rtfm/trait.Mutex.html
|
||||||
///
|
///
|
||||||
/// # `init`
|
/// # 2. `fn`
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
|
/// Functions must contain *one* of the following attributes: `init`, `idle`, `interrupt`,
|
||||||
|
/// `exception` or `task`. The attribute defines the role of the function in the application.
|
||||||
///
|
///
|
||||||
/// ``` text
|
/// ## a. `#[init]`
|
||||||
/// init: {
|
|
||||||
/// path: ..,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// ## `init.path`
|
/// This attribute indicates that the function is to be used as the *initialization function*. There
|
||||||
|
/// must be exactly one instance of the `init` attribute inside the `app` pseudo-module. The
|
||||||
|
/// signature of the `init` function must be `[unsafe] fn ()`.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the
|
/// The `init` function runs after memory (RAM) is initialized and runs with interrupts disabled.
|
||||||
/// initialization function.
|
/// Interrupts are re-enabled after `init` returns.
|
||||||
///
|
///
|
||||||
/// If the key is omitted its value defaults to `init`.
|
/// The `init` attribute accepts the following optional arguments:
|
||||||
///
|
///
|
||||||
/// ## `init.resources`
|
/// - `resources = [RESOURCE_A, RESOURCE_B, ..]`. This is the list of resources this function has
|
||||||
|
/// access to.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a set of resources the `init` function *owns*. The resources
|
/// - `schedule = [task_a, task_b, ..]`. This is the list of *software* tasks that this function can
|
||||||
/// in this list must be a subset of the resources listed in the top `resources` key. Note that some
|
/// schedule to run in the future. *IMPORTANT*: This argument is accepted only if the `timer-queue`
|
||||||
/// restrictions apply:
|
/// feature has been enabled.
|
||||||
///
|
///
|
||||||
/// - The resources in this list can't be late resources.
|
/// - `spawn = [task_a, task_b, ..]`. This is the list of *software* tasks that this function can
|
||||||
/// - The resources that appear in this list can't appear in other list like `idle.resources` or
|
/// immediately spawn.
|
||||||
/// `tasks.$TASK.resources`
|
|
||||||
///
|
///
|
||||||
/// If this key is omitted its value is assumed to be an empty list.
|
/// The `app` attribute will injected a *context* into this function that comprises the following
|
||||||
|
/// variables:
|
||||||
///
|
///
|
||||||
/// # `idle`
|
/// - `core: rtfm::Peripherals`. Exclusive access to core peripherals. See [`rtfm::Peripherals`] for
|
||||||
|
/// more details.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
|
/// [`rtfm::Peripherals`]: ../rtfm/struct.Peripherals.html
|
||||||
///
|
///
|
||||||
/// ``` text
|
/// - `device: <device-path>::Peripherals`. Exclusive access to device-specific peripherals.
|
||||||
/// idle: {
|
/// `<device-path>` is the path to the device crate declared in the top `app` attribute.
|
||||||
/// path: ..,
|
|
||||||
/// resources: [..],
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// ## `idle.path`
|
/// - `start: rtfm::Instant`. The `start` time of the system: `Instant(0 /* cycles */)`. **NOTE**:
|
||||||
|
/// only present if the `timer-queue` feature is enabled.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the idle
|
/// - `resources: _`. An opaque `struct` that contains all the resources assigned to this function.
|
||||||
/// loop function.
|
/// The resource maybe appear by value (`impl Singleton`), by references (`&[mut]`) or by proxy
|
||||||
|
/// (`impl Mutex`).
|
||||||
///
|
///
|
||||||
/// If the key is omitted its value defaults to `idle`.
|
/// - `schedule: init::Schedule`. A `struct` that can be used to schedule *software* tasks.
|
||||||
|
/// **NOTE**: only present if the `timer-queue` feature is enabled.
|
||||||
///
|
///
|
||||||
/// ## `idle.resources`
|
/// - `spawn: init::Spawn`. A `struct` that can be used to spawn *software* tasks.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a list of resources the `idle` loop has access to. The
|
/// Other properties / constraints:
|
||||||
/// resources in this list must be a subset of the resources listed in the top `resources` key.
|
|
||||||
///
|
///
|
||||||
/// If omitted its value defaults to an empty list.
|
/// - The `init` function can **not** be called from software.
|
||||||
///
|
///
|
||||||
/// # `tasks`
|
/// - The `static mut` variables declared at the beginning of this function will be transformed into
|
||||||
|
/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
|
||||||
|
/// become `FOO: &'static mut u32`.
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a list of tasks. Each task itself is a set of key value pair.
|
/// - Assignments (e.g. `FOO = 0`) at the end of this function can be used to initialize *late*
|
||||||
/// The full syntax is shown below:
|
/// resources.
|
||||||
///
|
///
|
||||||
/// ``` text
|
/// ## b. `#[idle]`
|
||||||
/// tasks: {
|
|
||||||
/// $TASK: {
|
|
||||||
/// enabled: ..,
|
|
||||||
/// path: ..,
|
|
||||||
/// priority: ..,
|
|
||||||
/// resources: [..],
|
|
||||||
/// },
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// If this key is omitted its value is assumed to be an empty list.
|
/// This attribute indicates that the function is to be used as the *idle task*. There can be at
|
||||||
|
/// most once instance of the `idle` attribute inside the `app` pseudo-module. The signature of the
|
||||||
|
/// `idle` function must be `fn() -> !`.
|
||||||
///
|
///
|
||||||
/// ## `tasks.$TASK`
|
/// The `idle` task is a special task that always runs in the background. The `idle` task runs at
|
||||||
|
/// the lowest priority of `0`. If the `idle` task is not defined then the runtime sets the
|
||||||
|
/// [SLEEPONEXIT] bit after executing `init`.
|
||||||
///
|
///
|
||||||
/// The key must be either a Cortex-M exception or a device specific interrupt. `PENDSV`, `SVCALL`,
|
/// [SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
|
||||||
/// `SYS_TICK` are considered as exceptions. All other names are assumed to be interrupts.
|
|
||||||
///
|
///
|
||||||
/// ## `tasks.$TASK.enabled`
|
/// The `idle` attribute accepts the following optional arguments:
|
||||||
///
|
///
|
||||||
/// This key is optional for interrupts and forbidden for exceptions. Its value must be a boolean
|
/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
|
||||||
/// and indicates whether the interrupt will be enabled (`true`) or disabled (`false`) after `init`
|
|
||||||
/// ends and before `idle` starts.
|
|
||||||
///
|
///
|
||||||
/// If this key is omitted its value defaults to `true`.
|
/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
|
||||||
///
|
///
|
||||||
/// ## `tasks.$TASK.path`
|
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
|
||||||
///
|
///
|
||||||
/// The value of this key is a Rust path, like `foo::bar::baz`, that points to the handler of this
|
/// The `app` attribute will injected a *context* into this function that comprises the following
|
||||||
/// task.
|
/// variables:
|
||||||
///
|
///
|
||||||
/// ## `tasks.$TASK.priority`
|
/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is an integer with type `u8` that specifies the priority of this
|
/// - `schedule: idle::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
|
||||||
/// task. The minimum valid priority is 1. The maximum valid priority depends on the number of the
|
|
||||||
/// NVIC priority bits the device has; if the device has 4 priority bits the maximum allowed value
|
|
||||||
/// would be 16.
|
|
||||||
///
|
///
|
||||||
/// If this key is omitted its value defaults to `1`.
|
/// - `spawn: idle::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
|
||||||
///
|
///
|
||||||
/// ## `tasks.$TASK.resources`
|
/// Other properties / constraints:
|
||||||
///
|
///
|
||||||
/// This key is optional. Its value is a list of resources this task has access to. The resources in
|
/// - The `idle` function can **not** be called from software.
|
||||||
/// this list must be a subset of the resources listed in the top `resources` key.
|
|
||||||
///
|
///
|
||||||
/// If omitted its value defaults to an empty list.
|
/// - The `static mut` variables declared at the beginning of this function will be transformed into
|
||||||
#[proc_macro]
|
/// `&'static mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
|
||||||
pub fn app(ts: TokenStream) -> TokenStream {
|
/// become `FOO: &'static mut u32`.
|
||||||
match run(ts) {
|
///
|
||||||
Err(e) => panic!("error: {}", e),
|
/// ## c. `#[exception]`
|
||||||
Ok(ts) => ts,
|
///
|
||||||
|
/// This attribute indicates that the function is to be used as an *exception handler*, a type of
|
||||||
|
/// hardware task. The signature of `exception` handlers must be `[unsafe] fn()`.
|
||||||
|
///
|
||||||
|
/// The name of the function must match one of the Cortex-M exceptions that has [configurable
|
||||||
|
/// priority][system-handler].
|
||||||
|
///
|
||||||
|
/// [system-handler]: ../cortex_m/peripheral/scb/enum.SystemHandler.html
|
||||||
|
///
|
||||||
|
/// The `exception` attribute accepts the following optional arguments.
|
||||||
|
///
|
||||||
|
/// - `priority = <integer>`. This is the static priority of the exception handler. The value must
|
||||||
|
/// be in the range `1..=(1 << <device-path>::NVIC_PRIO_BITS)` where `<device-path>` is the path to
|
||||||
|
/// the device crate declared in the top `app` attribute. If this argument is omitted the priority
|
||||||
|
/// is assumed to be 1.
|
||||||
|
///
|
||||||
|
/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
|
||||||
|
///
|
||||||
|
/// The `app` attribute will injected a *context* into this function that comprises the following
|
||||||
|
/// variables:
|
||||||
|
///
|
||||||
|
/// - `start: rtfm::Instant`. The time at which this handler started executing. **NOTE**: only
|
||||||
|
/// present if the `timer-queue` feature is enabled.
|
||||||
|
///
|
||||||
|
/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `schedule: <exception-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `spawn: <exception-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
|
||||||
|
///
|
||||||
|
/// Other properties / constraints:
|
||||||
|
///
|
||||||
|
/// - `exception` handlers can **not** be called from software.
|
||||||
|
///
|
||||||
|
/// - The `static mut` variables declared at the beginning of this function will be transformed into
|
||||||
|
/// `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
|
||||||
|
/// become `FOO: &mut u32`.
|
||||||
|
///
|
||||||
|
/// ## d. `#[interrupt]`
|
||||||
|
///
|
||||||
|
/// This attribute indicates that the function is to be used as an *interrupt handler*, a type of
|
||||||
|
/// hardware task. The signature of `interrupt` handlers must be `[unsafe] fn()`.
|
||||||
|
///
|
||||||
|
/// The name of the function must match one of the device specific interrupts. See your device crate
|
||||||
|
/// documentation (`Interrupt` enum) for more details.
|
||||||
|
///
|
||||||
|
/// The `interrupt` attribute accepts the following optional arguments.
|
||||||
|
///
|
||||||
|
/// - `priority = (..)`. Same meaning / function as [`#[exception].priority`](#b-exception).
|
||||||
|
///
|
||||||
|
/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
|
||||||
|
///
|
||||||
|
/// The `app` attribute will injected a *context* into this function that comprises the following
|
||||||
|
/// variables:
|
||||||
|
///
|
||||||
|
/// - `start: rtfm::Instant`. Same meaning / function as [`exception.start`](#b-exception).
|
||||||
|
///
|
||||||
|
/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `schedule: <interrupt-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `spawn: <interrupt-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
|
||||||
|
///
|
||||||
|
/// Other properties / constraints:
|
||||||
|
///
|
||||||
|
/// - `interrupt` handlers can **not** be called from software, but they can be [`pend`]-ed by the
|
||||||
|
/// software from any context.
|
||||||
|
///
|
||||||
|
/// [`pend`]: ../rtfm/fn.pend.html
|
||||||
|
///
|
||||||
|
/// - The `static mut` variables declared at the beginning of this function will be transformed into
|
||||||
|
/// `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
|
||||||
|
/// become `FOO: &mut u32`.
|
||||||
|
///
|
||||||
|
/// ## e. `#[task]`
|
||||||
|
///
|
||||||
|
/// This attribute indicates that the function is to be used as a *software task*. The signature of
|
||||||
|
/// software `task`s must be `[unsafe] fn(<inputs>)`.
|
||||||
|
///
|
||||||
|
/// The `task` attribute accepts the following optional arguments.
|
||||||
|
///
|
||||||
|
/// - `capacity = <integer>`. The maximum number of instances of this task that can be queued onto
|
||||||
|
/// the task scheduler for execution. The value must be in the range `1..=255`. If the `capacity`
|
||||||
|
/// argument is omitted then the capacity will be inferred.
|
||||||
|
///
|
||||||
|
/// - `priority = <integer>`. Same meaning / function as [`#[exception].priority`](#b-exception).
|
||||||
|
///
|
||||||
|
/// - `resources = (..)`. Same meaning / function as [`#[init].resources`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `schedule = (..)`. Same meaning / function as [`#[init].schedule`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `spawn = (..)`. Same meaning / function as [`#[init].spawn`](#a-init).
|
||||||
|
///
|
||||||
|
/// The `app` attribute will injected a *context* into this function that comprises the following
|
||||||
|
/// variables:
|
||||||
|
///
|
||||||
|
/// - `scheduled: rtfm::Instant`. The time at which this task was scheduled to run. **NOTE**: Only
|
||||||
|
/// present if `timer-queue` is enabled.
|
||||||
|
///
|
||||||
|
/// - `resources: _`. Same meaning / function as [`init.resources`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `schedule: <interrupt-name>::Schedule`. Same meaning / function as [`init.schedule`](#a-init).
|
||||||
|
///
|
||||||
|
/// - `spawn: <interrupt-name>::Spawn`. Same meaning / function as [`init.spawn`](#a-init).
|
||||||
|
///
|
||||||
|
/// Other properties / constraints:
|
||||||
|
///
|
||||||
|
/// - Software `task`s can **not** be called from software, but they can be `spawn`-ed and
|
||||||
|
/// `schedule`-d by the software from any context.
|
||||||
|
///
|
||||||
|
/// - The `static mut` variables declared at the beginning of this function will be transformed into
|
||||||
|
/// `&mut` references that are safe to access. For example, `static mut FOO: u32 = 0` will
|
||||||
|
/// become `FOO: &mut u32`.
|
||||||
|
///
|
||||||
|
/// # 3. `extern` block
|
||||||
|
///
|
||||||
|
/// This `extern` block contains a list of interrupts which are *not* used by the application as
|
||||||
|
/// hardware tasks. These interrupts will be used to dispatch software tasks. Each interrupt will be
|
||||||
|
/// used to dispatch *multiple* software tasks *at the same priority level*.
|
||||||
|
///
|
||||||
|
/// This `extern` block must only contain functions with signature `fn ()`. The names of these
|
||||||
|
/// functions must match the names of the target device interrupts.
|
||||||
|
///
|
||||||
|
/// Importantly, attributes can be applied to the functions inside this block. These attributes will
|
||||||
|
/// be forwarded to the interrupt handlers generated by the `app` attribute.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
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 app = match syntax::App::parse(items, args) {
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
Ok(app) => app,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check the specification
|
||||||
|
if let Err(e) = check::app(&app) {
|
||||||
|
return e.to_compile_error().into();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Ceiling analysis
|
||||||
fn run(ts: TokenStream) -> Result<TokenStream> {
|
let analysis = analyze::app(&app);
|
||||||
let app = App::parse(ts)?.check()?;
|
|
||||||
let app = check::app(app)?;
|
// Code generation
|
||||||
|
codegen::app(&app, &analysis)
|
||||||
let ownerships = analyze::app(&app);
|
|
||||||
let tokens = trans::app(&app, &ownerships);
|
|
||||||
|
|
||||||
Ok(tokens.into())
|
|
||||||
}
|
}
|
||||||
|
|
1235
macros/src/syntax.rs
Normal file
1235
macros/src/syntax.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,631 +0,0 @@
|
||||||
use proc_macro2::{TokenStream, Span};
|
|
||||||
use syn::{Ident, LitStr};
|
|
||||||
|
|
||||||
use analyze::Ownerships;
|
|
||||||
use check::{App, Kind};
|
|
||||||
|
|
||||||
fn krate() -> Ident {
|
|
||||||
Ident::new("rtfm", Span::call_site())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn app(app: &App, ownerships: &Ownerships) -> TokenStream {
|
|
||||||
let mut root = vec![];
|
|
||||||
let mut main = vec![quote!(#![allow(path_statements)])];
|
|
||||||
|
|
||||||
::trans::tasks(app, ownerships, &mut root, &mut main);
|
|
||||||
::trans::init(app, &mut main, &mut root);
|
|
||||||
::trans::idle(app, ownerships, &mut main, &mut root);
|
|
||||||
::trans::resources(app, ownerships, &mut root);
|
|
||||||
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
fn main() {
|
|
||||||
#(#main)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote!(#(#root)*)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec<TokenStream>, root: &mut Vec<TokenStream>) {
|
|
||||||
let krate = krate();
|
|
||||||
|
|
||||||
let mut mod_items = vec![];
|
|
||||||
let mut tys = vec![];
|
|
||||||
let mut exprs = vec![];
|
|
||||||
|
|
||||||
if !app.idle.resources.is_empty() {
|
|
||||||
tys.push(quote!(&mut #krate::Threshold));
|
|
||||||
exprs.push(quote!(unsafe { &mut #krate::Threshold::new(0) }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !app.idle.resources.is_empty() {
|
|
||||||
let mut needs_reexport = false;
|
|
||||||
for name in &app.idle.resources {
|
|
||||||
if ownerships[name].is_owned() {
|
|
||||||
if app.resources.get(name).is_some() {
|
|
||||||
needs_reexport = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let super_ = if needs_reexport {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Ident::new("super", Span::call_site()))
|
|
||||||
};
|
|
||||||
let mut rexprs = vec![];
|
|
||||||
let mut rfields = vec![];
|
|
||||||
for name in &app.idle.resources {
|
|
||||||
if ownerships[name].is_owned() {
|
|
||||||
let resource = app.resources.get(name).expect(&format!(
|
|
||||||
"BUG: resource {} assigned to `idle` has no definition",
|
|
||||||
name
|
|
||||||
));
|
|
||||||
let ty = &resource.ty;
|
|
||||||
|
|
||||||
rfields.push(quote! {
|
|
||||||
pub #name: &'static mut #ty,
|
|
||||||
});
|
|
||||||
|
|
||||||
let _name = Ident::new(&name.to_string(), Span::call_site());
|
|
||||||
rexprs.push(if resource.expr.is_some() {
|
|
||||||
quote! {
|
|
||||||
#name: &mut #super_::#_name,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
#name: #super_::#_name.as_mut(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
rfields.push(quote! {
|
|
||||||
pub #name: ::idle::#name,
|
|
||||||
});
|
|
||||||
|
|
||||||
rexprs.push(quote! {
|
|
||||||
#name: ::idle::#name { _0: ::core::marker::PhantomData },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if needs_reexport {
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct _idleResources {
|
|
||||||
#(#rfields)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mod_items.push(quote! {
|
|
||||||
pub use ::_idleResources as Resources;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
mod_items.push(quote! {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct Resources {
|
|
||||||
#(#rfields)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
mod_items.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
impl Resources {
|
|
||||||
pub unsafe fn new() -> Self {
|
|
||||||
Resources {
|
|
||||||
#(#rexprs)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tys.push(quote!(idle::Resources));
|
|
||||||
exprs.push(quote!(unsafe { idle::Resources::new() }));
|
|
||||||
}
|
|
||||||
|
|
||||||
let device = &app.device;
|
|
||||||
for name in &app.idle.resources {
|
|
||||||
let ceiling = ownerships[name].ceiling();
|
|
||||||
|
|
||||||
// owned resource
|
|
||||||
if ceiling == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _name = Ident::new(&name.to_string(), Span::call_site());
|
|
||||||
let resource = app.resources
|
|
||||||
.get(name)
|
|
||||||
.expect(&format!("BUG: resource {} has no definition", name));
|
|
||||||
|
|
||||||
let ty = &resource.ty;
|
|
||||||
let _static = if resource.expr.is_some() {
|
|
||||||
quote!(#_name)
|
|
||||||
} else {
|
|
||||||
quote!(#_name.some)
|
|
||||||
};
|
|
||||||
|
|
||||||
mod_items.push(quote! {
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub struct #name { _0: ::core::marker::PhantomData<*const ()> }
|
|
||||||
});
|
|
||||||
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe impl #krate::Resource for idle::#name {
|
|
||||||
type Data = #ty;
|
|
||||||
|
|
||||||
fn borrow<'cs>(&'cs self, t: &'cs Threshold) -> &'cs Self::Data {
|
|
||||||
assert!(t.value() >= #ceiling);
|
|
||||||
|
|
||||||
unsafe { &#_static }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn borrow_mut<'cs>(
|
|
||||||
&'cs mut self,
|
|
||||||
t: &'cs Threshold,
|
|
||||||
) -> &'cs mut Self::Data {
|
|
||||||
assert!(t.value() >= #ceiling);
|
|
||||||
|
|
||||||
unsafe { &mut #_static }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&Self::Data, &mut Threshold) -> R
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
#krate::claim(
|
|
||||||
&#_static,
|
|
||||||
#ceiling,
|
|
||||||
#device::NVIC_PRIO_BITS,
|
|
||||||
t,
|
|
||||||
f,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self::Data, &mut Threshold) -> R
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
#krate::claim(
|
|
||||||
&mut #_static,
|
|
||||||
#ceiling,
|
|
||||||
#device::NVIC_PRIO_BITS,
|
|
||||||
t,
|
|
||||||
f,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mod_items.is_empty() {
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
mod idle {
|
|
||||||
#(#mod_items)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let idle = &app.idle.path;
|
|
||||||
main.push(quote! {
|
|
||||||
// type check
|
|
||||||
let idle: fn(#(#tys),*) -> ! = #idle;
|
|
||||||
|
|
||||||
idle(#(#exprs),*);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(app: &App, main: &mut Vec<TokenStream>, root: &mut Vec<TokenStream>) {
|
|
||||||
let device = &app.device;
|
|
||||||
let krate = krate();
|
|
||||||
|
|
||||||
let mut tys = vec![quote!(init::Peripherals)];
|
|
||||||
let mut exprs = vec![
|
|
||||||
quote!{
|
|
||||||
init::Peripherals {
|
|
||||||
core: ::#device::CorePeripherals::steal(),
|
|
||||||
device: ::#device::Peripherals::steal(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let mut ret = None;
|
|
||||||
let mut mod_items = vec![];
|
|
||||||
|
|
||||||
let (init_resources, late_resources): (Vec<_>, Vec<_>) = app.resources
|
|
||||||
.iter()
|
|
||||||
.partition(|&(_, res)| res.expr.is_some());
|
|
||||||
|
|
||||||
if !init_resources.is_empty() {
|
|
||||||
let mut fields = vec![];
|
|
||||||
let mut lifetime = None;
|
|
||||||
let mut rexprs = vec![];
|
|
||||||
|
|
||||||
for (name, resource) in init_resources {
|
|
||||||
let ty = &resource.ty;
|
|
||||||
|
|
||||||
if app.init.resources.contains(name) {
|
|
||||||
fields.push(quote! {
|
|
||||||
pub #name: &'static mut #ty,
|
|
||||||
});
|
|
||||||
|
|
||||||
let expr = &resource.expr;
|
|
||||||
rexprs.push(quote!(#name: {
|
|
||||||
static mut #name: #ty = #expr;
|
|
||||||
&mut #name
|
|
||||||
},));
|
|
||||||
} else {
|
|
||||||
let _name = Ident::new(&name.to_string(), Span::call_site());
|
|
||||||
lifetime = Some(quote!('a));
|
|
||||||
|
|
||||||
fields.push(quote! {
|
|
||||||
pub #name: &'a mut #ty,
|
|
||||||
});
|
|
||||||
|
|
||||||
rexprs.push(quote! {
|
|
||||||
#name: &mut ::#_name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct _initResources<#lifetime> {
|
|
||||||
#(#fields)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mod_items.push(quote! {
|
|
||||||
pub use ::_initResources as Resources;
|
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
impl<#lifetime> Resources<#lifetime> {
|
|
||||||
pub unsafe fn new() -> Self {
|
|
||||||
Resources {
|
|
||||||
#(#rexprs)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tys.push(quote!(init::Resources));
|
|
||||||
exprs.push(quote!(init::Resources::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialization statements for late resources
|
|
||||||
let mut late_resource_init = vec![];
|
|
||||||
|
|
||||||
if !late_resources.is_empty() {
|
|
||||||
// `init` must initialize and return resources
|
|
||||||
|
|
||||||
let mut fields = vec![];
|
|
||||||
|
|
||||||
for (name, resource) in late_resources {
|
|
||||||
let _name = Ident::new(&name.to_string(), Span::call_site());
|
|
||||||
|
|
||||||
let ty = &resource.ty;
|
|
||||||
|
|
||||||
fields.push(quote! {
|
|
||||||
pub #name: #ty,
|
|
||||||
});
|
|
||||||
|
|
||||||
late_resource_init.push(quote! {
|
|
||||||
#_name = #krate::UntaggedOption { some: _late_resources.#name };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct _initLateResources {
|
|
||||||
#(#fields)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mod_items.push(quote! {
|
|
||||||
pub use ::_initLateResources as LateResources;
|
|
||||||
});
|
|
||||||
|
|
||||||
// `init` must return the initialized resources
|
|
||||||
ret = Some(quote!( -> ::init::LateResources));
|
|
||||||
}
|
|
||||||
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
mod init {
|
|
||||||
pub struct Peripherals {
|
|
||||||
pub core: ::#device::CorePeripherals,
|
|
||||||
pub device: ::#device::Peripherals,
|
|
||||||
}
|
|
||||||
|
|
||||||
#(#mod_items)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut exceptions = vec![];
|
|
||||||
let mut interrupts = vec![];
|
|
||||||
for (name, task) in &app.tasks {
|
|
||||||
match task.kind {
|
|
||||||
Kind::Exception(ref e) => {
|
|
||||||
if exceptions.is_empty() {
|
|
||||||
exceptions.push(quote! {
|
|
||||||
let scb = &*#device::SCB::ptr();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let nr = e.nr();
|
|
||||||
let priority = task.priority;
|
|
||||||
exceptions.push(quote! {
|
|
||||||
let prio_bits = #device::NVIC_PRIO_BITS;
|
|
||||||
let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits);
|
|
||||||
scb.shpr[#nr - 4].write(hw);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Kind::Interrupt { enabled } => {
|
|
||||||
// Interrupt. These are enabled / disabled through the NVIC
|
|
||||||
if interrupts.is_empty() {
|
|
||||||
interrupts.push(quote! {
|
|
||||||
use #device::Interrupt;
|
|
||||||
|
|
||||||
let mut nvic: #device::NVIC = core::mem::transmute(());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let priority = task.priority;
|
|
||||||
interrupts.push(quote! {
|
|
||||||
let prio_bits = #device::NVIC_PRIO_BITS;
|
|
||||||
let hw = ((1 << prio_bits) - #priority) << (8 - prio_bits);
|
|
||||||
nvic.set_priority(Interrupt::#name, hw);
|
|
||||||
});
|
|
||||||
|
|
||||||
if enabled {
|
|
||||||
interrupts.push(quote! {
|
|
||||||
nvic.enable(Interrupt::#name);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
interrupts.push(quote! {
|
|
||||||
nvic.disable(Interrupt::#name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let init = &app.init.path;
|
|
||||||
main.push(quote! {
|
|
||||||
// type check
|
|
||||||
let init: fn(#(#tys,)*) #ret = #init;
|
|
||||||
|
|
||||||
#krate::atomic(unsafe { &mut #krate::Threshold::new(0) }, |_t| unsafe {
|
|
||||||
let _late_resources = init(#(#exprs,)*);
|
|
||||||
#(#late_resource_init)*
|
|
||||||
|
|
||||||
#(#exceptions)*
|
|
||||||
#(#interrupts)*
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<TokenStream>) {
|
|
||||||
let krate = krate();
|
|
||||||
|
|
||||||
for name in ownerships.keys() {
|
|
||||||
let _name = Ident::new(&name.to_string(), Span::call_site());
|
|
||||||
|
|
||||||
// Declare the static that holds the resource
|
|
||||||
let resource = app.resources
|
|
||||||
.get(name)
|
|
||||||
.expect(&format!("BUG: resource {} has no definition", name));
|
|
||||||
|
|
||||||
let expr = &resource.expr;
|
|
||||||
let ty = &resource.ty;
|
|
||||||
|
|
||||||
root.push(match *expr {
|
|
||||||
Some(ref expr) => quote! {
|
|
||||||
static mut #_name: #ty = #expr;
|
|
||||||
},
|
|
||||||
None => quote! {
|
|
||||||
// Resource initialized in `init`
|
|
||||||
static mut #_name: #krate::UntaggedOption<#ty> =
|
|
||||||
#krate::UntaggedOption { none: () };
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<TokenStream>, main: &mut Vec<TokenStream>) {
|
|
||||||
let device = &app.device;
|
|
||||||
let krate = krate();
|
|
||||||
|
|
||||||
for (tname, task) in &app.tasks {
|
|
||||||
let mut exprs = vec![];
|
|
||||||
let mut fields = vec![];
|
|
||||||
let mut items = vec![];
|
|
||||||
|
|
||||||
let has_resources = !task.resources.is_empty();
|
|
||||||
|
|
||||||
if has_resources {
|
|
||||||
for rname in &task.resources {
|
|
||||||
let ceiling = ownerships[rname].ceiling();
|
|
||||||
let _rname = Ident::new(&rname.to_string(), Span::call_site());
|
|
||||||
let resource = app.resources
|
|
||||||
.get(rname)
|
|
||||||
.expect(&format!("BUG: resource {} has no definition", rname));
|
|
||||||
|
|
||||||
let ty = &resource.ty;
|
|
||||||
let _static = if resource.expr.is_some() {
|
|
||||||
quote!(#_rname)
|
|
||||||
} else {
|
|
||||||
quote!(#_rname.some)
|
|
||||||
};
|
|
||||||
|
|
||||||
items.push(quote! {
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub struct #rname { _0: PhantomData<*const ()> }
|
|
||||||
});
|
|
||||||
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe impl #krate::Resource for #tname::#rname {
|
|
||||||
type Data = #ty;
|
|
||||||
|
|
||||||
fn borrow<'cs>(&'cs self, t: &'cs Threshold) -> &'cs Self::Data {
|
|
||||||
assert!(t.value() >= #ceiling);
|
|
||||||
|
|
||||||
unsafe { &#_static }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn borrow_mut<'cs>(
|
|
||||||
&'cs mut self,
|
|
||||||
t: &'cs Threshold,
|
|
||||||
) -> &'cs mut Self::Data {
|
|
||||||
assert!(t.value() >= #ceiling);
|
|
||||||
|
|
||||||
unsafe { &mut #_static }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&Self::Data, &mut Threshold) -> R
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
#krate::claim(
|
|
||||||
&#_static,
|
|
||||||
#ceiling,
|
|
||||||
#device::NVIC_PRIO_BITS,
|
|
||||||
t,
|
|
||||||
f,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self::Data, &mut Threshold) -> R
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
#krate::claim(
|
|
||||||
&mut #_static,
|
|
||||||
#ceiling,
|
|
||||||
#device::NVIC_PRIO_BITS,
|
|
||||||
t,
|
|
||||||
f,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if ceiling <= task.priority {
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
impl core::ops::Deref for #tname::#rname {
|
|
||||||
type Target = #ty;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { &#_static }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
impl core::ops::DerefMut for #tname::#rname {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe { &mut #_static }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.push(quote! {
|
|
||||||
pub #rname: #rname,
|
|
||||||
});
|
|
||||||
|
|
||||||
exprs.push(quote! {
|
|
||||||
#rname: #rname { _0: PhantomData },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push(quote! {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct Resources {
|
|
||||||
#(#fields)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
items.push(quote! {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
impl Resources {
|
|
||||||
pub unsafe fn new() -> Self {
|
|
||||||
Resources {
|
|
||||||
#(#exprs)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tys = vec![];
|
|
||||||
let mut exprs = vec![];
|
|
||||||
|
|
||||||
let priority = task.priority;
|
|
||||||
if has_resources {
|
|
||||||
tys.push(quote!(&mut #krate::Threshold));
|
|
||||||
exprs.push(quote! {
|
|
||||||
&mut if #priority == 1 << #device::NVIC_PRIO_BITS {
|
|
||||||
#krate::Threshold::new(::core::u8::MAX)
|
|
||||||
} else {
|
|
||||||
#krate::Threshold::new(#priority)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_resources {
|
|
||||||
tys.push(quote!(#tname::Resources));
|
|
||||||
exprs.push(quote!(#tname::Resources::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = &task.path;
|
|
||||||
let _tname = Ident::new(&tname.to_string(), Span::call_site());
|
|
||||||
let export_name = LitStr::new(&tname.to_string(), Span::call_site());
|
|
||||||
root.push(quote! {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
#[export_name = #export_name]
|
|
||||||
pub unsafe extern "C" fn #_tname() {
|
|
||||||
let f: fn(#(#tys,)*) = #path;
|
|
||||||
|
|
||||||
f(#(#exprs,)*)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
root.push(quote!{
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
mod #tname {
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[deny(const_err)]
|
|
||||||
pub const CHECK_PRIORITY: (u8, u8) = (
|
|
||||||
#priority - 1,
|
|
||||||
(1 << ::#device::NVIC_PRIO_BITS) - #priority,
|
|
||||||
);
|
|
||||||
|
|
||||||
#(#items)*
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// after miri landed (?) rustc won't analyze `const` items unless they are used so we force
|
|
||||||
// evaluation with this path statement
|
|
||||||
main.push(quote!(#tname::CHECK_PRIORITY;));
|
|
||||||
}
|
|
||||||
}
|
|
6
memory.x
6
memory.x
|
@ -1,6 +0,0 @@
|
||||||
/* STM32F103C8V6 */
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
|
||||||
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
//! Minimal example with zero tasks
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm; // IMPORTANT always do this rename
|
|
||||||
//! extern crate stm32f103xx; // the device crate
|
|
||||||
//!
|
|
||||||
//! // import the procedural macro
|
|
||||||
//! use rtfm::app;
|
|
||||||
//!
|
|
||||||
//! // This macro call indicates that this is a RTFM application
|
|
||||||
//! //
|
|
||||||
//! // This macro will expand to a `main` function so you don't need to supply
|
|
||||||
//! // `main` yourself.
|
|
||||||
//! app! {
|
|
||||||
//! // this is the path to the device crate
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // The initialization phase.
|
|
||||||
//! //
|
|
||||||
//! // This runs first and within a *global* critical section. Nothing can preempt
|
|
||||||
//! // this function.
|
|
||||||
//! fn init(p: init::Peripherals) {
|
|
||||||
//! // This function has access to all the peripherals of the device
|
|
||||||
//! p.core.SYST;
|
|
||||||
//! p.device.GPIOA;
|
|
||||||
//! p.device.RCC;
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // The idle loop.
|
|
||||||
//! //
|
|
||||||
//! // This runs after `init` and has a priority of 0. All tasks can preempt this
|
|
||||||
//! // function. This function can never return so it must contain some sort of
|
|
||||||
//! // endless loop.
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! // This puts the processor to sleep until there's a task to service
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,100 +0,0 @@
|
||||||
//! An application with one task
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m;
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use cortex_m::peripheral::syst::SystClkSource;
|
|
||||||
//! use rtfm::{app, Threshold};
|
|
||||||
//! use stm32f103xx::GPIOC;
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! // Here data resources are declared
|
|
||||||
//! //
|
|
||||||
//! // Data resources are static variables that are safe to share across tasks
|
|
||||||
//! resources: {
|
|
||||||
//! // Declaration of resources looks exactly like declaration of static
|
|
||||||
//! // variables
|
|
||||||
//! static ON: bool = false;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! // Here tasks are declared
|
|
||||||
//! //
|
|
||||||
//! // Each task corresponds to an interrupt or an exception. Every time the
|
|
||||||
//! // interrupt or exception becomes *pending* the corresponding task handler
|
|
||||||
//! // will be executed.
|
|
||||||
//! tasks: {
|
|
||||||
//! // Here we declare that we'll use the SYS_TICK exception as a task
|
|
||||||
//! SYS_TICK: {
|
|
||||||
//! // Path to the task handler
|
|
||||||
//! path: sys_tick,
|
|
||||||
//!
|
|
||||||
//! // These are the resources this task has access to.
|
|
||||||
//! //
|
|
||||||
//! // The resources listed here must also appear in `app.resources`
|
|
||||||
//! resources: [ON],
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn init(mut p: init::Peripherals, r: init::Resources) {
|
|
||||||
//! // `init` can modify all the `resources` declared in `app!`
|
|
||||||
//! r.ON;
|
|
||||||
//!
|
|
||||||
//! // power on GPIOC
|
|
||||||
//! p.device.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
|
|
||||||
//!
|
|
||||||
//! // configure PC13 as output
|
|
||||||
//! p.device.GPIOC.bsrr.write(|w| w.bs13().set());
|
|
||||||
//! p.device
|
|
||||||
//! .GPIOC
|
|
||||||
//! .crh
|
|
||||||
//! .modify(|_, w| w.mode13().output().cnf13().push());
|
|
||||||
//!
|
|
||||||
//! // configure the system timer to generate one interrupt every second
|
|
||||||
//! p.core.SYST.set_clock_source(SystClkSource::Core);
|
|
||||||
//! p.core.SYST.set_reload(8_000_000); // 1s
|
|
||||||
//! p.core.SYST.enable_interrupt();
|
|
||||||
//! p.core.SYST.enable_counter();
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // This is the task handler of the SYS_TICK exception
|
|
||||||
//! //
|
|
||||||
//! // `_t` is the preemption threshold token. We won't use it in this program.
|
|
||||||
//! //
|
|
||||||
//! // `r` is the set of resources this task has access to. `SYS_TICK::Resources`
|
|
||||||
//! // has one field per resource declared in `app!`.
|
|
||||||
//! #[allow(unsafe_code)]
|
|
||||||
//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
//! // toggle state
|
|
||||||
//! *r.ON = !*r.ON;
|
|
||||||
//!
|
|
||||||
//! if *r.ON {
|
|
||||||
//! // set the pin PC13 high
|
|
||||||
//! // NOTE(unsafe) atomic write to a stateless register
|
|
||||||
//! unsafe {
|
|
||||||
//! (*GPIOC::ptr()).bsrr.write(|w| w.bs13().set());
|
|
||||||
//! }
|
|
||||||
//! } else {
|
|
||||||
//! // set the pin PC13 low
|
|
||||||
//! // NOTE(unsafe) atomic write to a stateless register
|
|
||||||
//! unsafe {
|
|
||||||
//! (*GPIOC::ptr()).bsrr.write(|w| w.br13().reset());
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,62 +0,0 @@
|
||||||
//! Two tasks running at the *same* priority with access to the same resource
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Threshold};
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! static COUNTER: u64 = 0;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! // Both SYS_TICK and TIM2 have access to the `COUNTER` data
|
|
||||||
//! tasks: {
|
|
||||||
//! SYS_TICK: {
|
|
||||||
//! path: sys_tick,
|
|
||||||
//! resources: [COUNTER],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! TIM2: {
|
|
||||||
//! path: tim2,
|
|
||||||
//! resources: [COUNTER],
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn init(_p: init::Peripherals, _r: init::Resources) {
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // As both tasks are running at the same priority one can't preempt the other.
|
|
||||||
//! // Thus both tasks have direct access to the resource
|
|
||||||
//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
//! // ..
|
|
||||||
//!
|
|
||||||
//! *r.COUNTER += 1;
|
|
||||||
//!
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) {
|
|
||||||
//! // ..
|
|
||||||
//!
|
|
||||||
//! *r.COUNTER += 1;
|
|
||||||
//!
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,71 +0,0 @@
|
||||||
//! Two tasks running at *different* priorities with access to the same resource
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Resource, Threshold};
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! static COUNTER: u64 = 0;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! tasks: {
|
|
||||||
//! // The `SYS_TICK` task has higher priority than `TIM2`
|
|
||||||
//! SYS_TICK: {
|
|
||||||
//! path: sys_tick,
|
|
||||||
//! priority: 2,
|
|
||||||
//! resources: [COUNTER],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! TIM2: {
|
|
||||||
//! path: tim2,
|
|
||||||
//! priority: 1,
|
|
||||||
//! resources: [COUNTER],
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn init(_p: init::Peripherals, _r: init::Resources) {
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
//! // ..
|
|
||||||
//!
|
|
||||||
//! // This task can't be preempted by `tim2` so it has direct access to the
|
|
||||||
//! // resource data
|
|
||||||
//! *r.COUNTER += 1;
|
|
||||||
//!
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn tim2(t: &mut Threshold, mut r: TIM2::Resources) {
|
|
||||||
//! // ..
|
|
||||||
//!
|
|
||||||
//! // As this task runs at lower priority it needs a critical section to
|
|
||||||
//! // prevent `sys_tick` from preempting it while it modifies this resource
|
|
||||||
//! // data. The critical section is required to prevent data races which can
|
|
||||||
//! // lead to undefined behavior.
|
|
||||||
//! r.COUNTER.claim_mut(t, |counter, _t| {
|
|
||||||
//! // `claim_mut` creates a critical section
|
|
||||||
//! *counter += 1;
|
|
||||||
//! });
|
|
||||||
//!
|
|
||||||
//! // ..
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,132 +0,0 @@
|
||||||
//! Nesting claims and how the preemption threshold works
|
|
||||||
//!
|
|
||||||
//! If you run this program you'll hit the breakpoints as indicated by the
|
|
||||||
//! letters in the comments: A, then B, then C, etc.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Resource, Threshold};
|
|
||||||
//! use stm32f103xx::Interrupt;
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! static LOW: u64 = 0;
|
|
||||||
//! static HIGH: u64 = 0;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! tasks: {
|
|
||||||
//! EXTI0: {
|
|
||||||
//! path: exti0,
|
|
||||||
//! priority: 1,
|
|
||||||
//! resources: [LOW, HIGH],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! EXTI1: {
|
|
||||||
//! path: exti1,
|
|
||||||
//! priority: 2,
|
|
||||||
//! resources: [LOW],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! EXTI2: {
|
|
||||||
//! path: exti2,
|
|
||||||
//! priority: 3,
|
|
||||||
//! resources: [HIGH],
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn init(_p: init::Peripherals, _r: init::Resources) {}
|
|
||||||
//!
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! // A
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//!
|
|
||||||
//! // Sets task `exti0` as pending
|
|
||||||
//! //
|
|
||||||
//! // Because `exti0` has higher priority than `idle` it will be executed
|
|
||||||
//! // immediately
|
|
||||||
//! rtfm::set_pending(Interrupt::EXTI0); // ~> exti0
|
|
||||||
//!
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[allow(non_snake_case)]
|
|
||||||
//! fn exti0(
|
|
||||||
//! t: &mut Threshold,
|
|
||||||
//! EXTI0::Resources {
|
|
||||||
//! LOW: mut low,
|
|
||||||
//! HIGH: mut high,
|
|
||||||
//! }: EXTI0::Resources,
|
|
||||||
//! ) {
|
|
||||||
//! // Because this task has a priority of 1 the preemption threshold `t` also
|
|
||||||
//! // starts at 1
|
|
||||||
//!
|
|
||||||
//! // B
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//!
|
|
||||||
//! // Because `exti1` has higher priority than `exti0` it can preempt it
|
|
||||||
//! rtfm::set_pending(Interrupt::EXTI1); // ~> exti1
|
|
||||||
//!
|
|
||||||
//! // A claim creates a critical section
|
|
||||||
//! low.claim_mut(t, |_low, t| {
|
|
||||||
//! // This claim increases the preemption threshold to 2
|
|
||||||
//! //
|
|
||||||
//! // 2 is just high enough to not race with task `exti1` for access to the
|
|
||||||
//! // `LOW` resource
|
|
||||||
//!
|
|
||||||
//! // D
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//!
|
|
||||||
//! // Now `exti1` can't preempt this task because its priority is equal to
|
|
||||||
//! // the current preemption threshold
|
|
||||||
//! rtfm::set_pending(Interrupt::EXTI1);
|
|
||||||
//!
|
|
||||||
//! // But `exti2` can, because its priority is higher than the current
|
|
||||||
//! // preemption threshold
|
|
||||||
//! rtfm::set_pending(Interrupt::EXTI2); // ~> exti2
|
|
||||||
//!
|
|
||||||
//! // F
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//!
|
|
||||||
//! // Claims can be nested
|
|
||||||
//! high.claim_mut(t, |_high, _| {
|
|
||||||
//! // This claim increases the preemption threshold to 3
|
|
||||||
//!
|
|
||||||
//! // Now `exti2` can't preempt this task
|
|
||||||
//! rtfm::set_pending(Interrupt::EXTI2);
|
|
||||||
//!
|
|
||||||
//! // G
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//! });
|
|
||||||
//!
|
|
||||||
//! // Upon leaving the critical section the preemption threshold drops back
|
|
||||||
//! // to 2 and `exti2` immediately preempts this task
|
|
||||||
//! // ~> exti2
|
|
||||||
//! });
|
|
||||||
//!
|
|
||||||
//! // Once again the preemption threshold drops but this time to 1. Now the
|
|
||||||
//! // pending `exti1` task can preempt this task
|
|
||||||
//! // ~> exti1
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
|
|
||||||
//! // C, I
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
|
|
||||||
//! // E, H
|
|
||||||
//! rtfm::bkpt();
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,90 +0,0 @@
|
||||||
//! Demonstrates initialization of resources in `init`.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Threshold};
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! // Usually, resources are initialized with a constant initializer:
|
|
||||||
//! static ON: bool = false;
|
|
||||||
//!
|
|
||||||
//! // However, there are cases where this is not possible or not desired.
|
|
||||||
//! // For example, there may not be a sensible value to use, or the type may
|
|
||||||
//! // not be constructible in a constant (like `Vec`).
|
|
||||||
//! //
|
|
||||||
//! // While it is possible to use an `Option` in some cases, that requires
|
|
||||||
//! // you to properly initialize it and `.unwrap()` it at every use. It
|
|
||||||
//! // also consumes more memory.
|
|
||||||
//! //
|
|
||||||
//! // To solve this, it is possible to defer initialization of resources to
|
|
||||||
//! // `init` by omitting the initializer. Doing that will require `init` to
|
|
||||||
//! // return the values of all "late" resources.
|
|
||||||
//! static IP_ADDRESS: u32;
|
|
||||||
//!
|
|
||||||
//! // PORT is used by 2 tasks, making it a shared resource. This just tests
|
|
||||||
//! // another internal code path and is not important for the example.
|
|
||||||
//! static PORT: u16;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! idle: {
|
|
||||||
//! // Test that late resources can be used in idle
|
|
||||||
//! resources: [IP_ADDRESS],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! tasks: {
|
|
||||||
//! SYS_TICK: {
|
|
||||||
//! priority: 1,
|
|
||||||
//! path: sys_tick,
|
|
||||||
//! resources: [IP_ADDRESS, PORT, ON],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! EXTI0: {
|
|
||||||
//! priority: 2,
|
|
||||||
//! path: exti0,
|
|
||||||
//! resources: [PORT],
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // The signature of `init` is now required to have a specific return type.
|
|
||||||
//! fn init(_p: init::Peripherals, _r: init::Resources) -> init::LateResources {
|
|
||||||
//! // `init::Resources` does not contain `IP_ADDRESS`, since it is not yet
|
|
||||||
//! // initialized.
|
|
||||||
//! //_r.IP_ADDRESS; // doesn't compile
|
|
||||||
//!
|
|
||||||
//! // ...obtain value for IP_ADDRESS from EEPROM/DHCP...
|
|
||||||
//! let ip_address = 0x7f000001;
|
|
||||||
//!
|
|
||||||
//! init::LateResources {
|
|
||||||
//! // This struct will contain fields for all resources with omitted
|
|
||||||
//! // initializers.
|
|
||||||
//! IP_ADDRESS: ip_address,
|
|
||||||
//! PORT: 0,
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
|
|
||||||
//! // Other tasks can access late resources like any other, since they are
|
|
||||||
//! // guaranteed to be initialized when tasks are run.
|
|
||||||
//!
|
|
||||||
//! r.IP_ADDRESS;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn exti0(_t: &mut Threshold, _r: EXTI0::Resources) {}
|
|
||||||
//!
|
|
||||||
//! fn idle(_t: &mut Threshold, _r: idle::Resources) -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,35 +0,0 @@
|
||||||
//! Safe creation of `&'static mut` references
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::app;
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! static BUFFER: [u8; 16] = [0; 16];
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! init: {
|
|
||||||
//! resources: [BUFFER],
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn init(_p: init::Peripherals, r: init::Resources) {
|
|
||||||
//! let _buf: &'static mut [u8; 16] = r.BUFFER;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,77 +0,0 @@
|
||||||
//! Working with resources in a generic fashion
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Resource, Threshold};
|
|
||||||
//! use stm32f103xx::{GPIOA, SPI1};
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! static GPIOA: GPIOA;
|
|
||||||
//! static SPI1: SPI1;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! tasks: {
|
|
||||||
//! EXTI0: {
|
|
||||||
//! path: exti0,
|
|
||||||
//! priority: 1,
|
|
||||||
//! resources: [GPIOA, SPI1],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! EXTI1: {
|
|
||||||
//! path: exti1,
|
|
||||||
//! priority: 2,
|
|
||||||
//! resources: [GPIOA, SPI1],
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn init(p: init::Peripherals) -> init::LateResources {
|
|
||||||
//! init::LateResources {
|
|
||||||
//! GPIOA: p.device.GPIOA,
|
|
||||||
//! SPI1: p.device.SPI1,
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn idle() -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // A generic function that uses some resources
|
|
||||||
//! fn work<G, S>(t: &mut Threshold, gpioa: &G, spi1: &S)
|
|
||||||
//! where
|
|
||||||
//! G: Resource<Data = GPIOA>,
|
|
||||||
//! S: Resource<Data = SPI1>,
|
|
||||||
//! {
|
|
||||||
//! gpioa.claim(t, |_gpioa, t| {
|
|
||||||
//! // drive NSS low
|
|
||||||
//!
|
|
||||||
//! spi1.claim(t, |_spi1, _| {
|
|
||||||
//! // transfer data
|
|
||||||
//! });
|
|
||||||
//!
|
|
||||||
//! // drive NSS high
|
|
||||||
//! });
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // This task needs critical sections to access the resources
|
|
||||||
//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
|
|
||||||
//! work(t, &r.GPIOA, &r.SPI1);
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! // This task has direct access to the resources
|
|
||||||
//! fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
|
|
||||||
//! work(t, &r.GPIOA, &r.SPI1);
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,87 +0,0 @@
|
||||||
//! A showcase of the `app!` macro syntax
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Threshold};
|
|
||||||
//!
|
|
||||||
//! app! {
|
|
||||||
//! device: stm32f103xx,
|
|
||||||
//!
|
|
||||||
//! resources: {
|
|
||||||
//! static CO_OWNED: u32 = 0;
|
|
||||||
//! static ON: bool = false;
|
|
||||||
//! static OWNED: bool = false;
|
|
||||||
//! static SHARED: bool = false;
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! init: {
|
|
||||||
//! // This is the path to the `init` function
|
|
||||||
//! //
|
|
||||||
//! // `init` doesn't necessarily has to be in the root of the crate
|
|
||||||
//! path: main::init,
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! idle: {
|
|
||||||
//! // This is a path to the `idle` function
|
|
||||||
//! //
|
|
||||||
//! // `idle` doesn't necessarily has to be in the root of the crate
|
|
||||||
//! path: main::idle,
|
|
||||||
//! resources: [OWNED, SHARED],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! tasks: {
|
|
||||||
//! SYS_TICK: {
|
|
||||||
//! path: sys_tick,
|
|
||||||
//! // If omitted priority is assumed to be 1
|
|
||||||
//! // priority: 1,
|
|
||||||
//! resources: [CO_OWNED, ON, SHARED],
|
|
||||||
//! },
|
|
||||||
//!
|
|
||||||
//! TIM2: {
|
|
||||||
//! // Tasks are enabled, between `init` and `idle`, by default but they
|
|
||||||
//! // can start disabled if `false` is specified here
|
|
||||||
//! enabled: false,
|
|
||||||
//! path: tim2,
|
|
||||||
//! priority: 1,
|
|
||||||
//! resources: [CO_OWNED],
|
|
||||||
//! },
|
|
||||||
//! },
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! mod main {
|
|
||||||
//! use rtfm::{self, Resource, Threshold};
|
|
||||||
//!
|
|
||||||
//! pub fn init(_p: ::init::Peripherals, _r: ::init::Resources) {}
|
|
||||||
//!
|
|
||||||
//! pub fn idle(t: &mut Threshold, mut r: ::idle::Resources) -> ! {
|
|
||||||
//! loop {
|
|
||||||
//! *r.OWNED = !*r.OWNED;
|
|
||||||
//!
|
|
||||||
//! if *r.OWNED {
|
|
||||||
//! if r.SHARED.claim(t, |shared, _| *shared) {
|
|
||||||
//! rtfm::wfi();
|
|
||||||
//! }
|
|
||||||
//! } else {
|
|
||||||
//! r.SHARED.claim_mut(t, |shared, _| *shared = !*shared);
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
|
|
||||||
//! *r.ON = !*r.ON;
|
|
||||||
//!
|
|
||||||
//! *r.CO_OWNED += 1;
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) {
|
|
||||||
//! *r.CO_OWNED += 1;
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
// Auto-generated. Do not modify.
|
|
|
@ -1,11 +0,0 @@
|
||||||
//! Examples
|
|
||||||
// Auto-generated. Do not modify.
|
|
||||||
pub mod _0_zero_tasks;
|
|
||||||
pub mod _1_one_task;
|
|
||||||
pub mod _2_two_tasks;
|
|
||||||
pub mod _3_preemption;
|
|
||||||
pub mod _4_nested;
|
|
||||||
pub mod _5_late_resources;
|
|
||||||
pub mod _6_safe_static_mut_ref;
|
|
||||||
pub mod _7_generics;
|
|
||||||
pub mod _8_full_syntax;
|
|
84
src/export.rs
Normal file
84
src/export.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/// IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
|
||||||
|
use core::{hint, ptr};
|
||||||
|
|
||||||
|
#[cfg(armv7m)]
|
||||||
|
use cortex_m::register::basepri;
|
||||||
|
pub use cortex_m::{
|
||||||
|
asm::wfi, interrupt, peripheral::scb::SystemHandler, peripheral::syst::SystClkSource,
|
||||||
|
peripheral::Peripherals,
|
||||||
|
};
|
||||||
|
pub use cortex_m_rt::{entry, exception};
|
||||||
|
pub use heapless::consts;
|
||||||
|
use heapless::spsc::Queue;
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
pub use crate::tq::{isr as sys_tick, NotReady, TimerQueue};
|
||||||
|
|
||||||
|
pub type FreeQueue<N> = Queue<u8, N>;
|
||||||
|
pub type ReadyQueue<T, N> = Queue<(T, u8), N>;
|
||||||
|
|
||||||
|
#[cfg(armv7m)]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run<F>(f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(),
|
||||||
|
{
|
||||||
|
let initial = basepri::read();
|
||||||
|
f();
|
||||||
|
unsafe { basepri::write(initial) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(armv7m))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run<F>(f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(),
|
||||||
|
{
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(MaybeUninit) Until core::mem::MaybeUninit is stabilized we use our own (inefficient)
|
||||||
|
// implementation
|
||||||
|
pub struct MaybeUninit<T> {
|
||||||
|
value: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MaybeUninit<T> {
|
||||||
|
pub const fn uninitialized() -> Self {
|
||||||
|
MaybeUninit { value: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_ref(&self) -> &T {
|
||||||
|
if let Some(x) = self.value.as_ref() {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
hint::unreachable_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_mut(&mut self) -> &mut T {
|
||||||
|
if let Some(x) = self.value.as_mut() {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
hint::unreachable_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, val: T) {
|
||||||
|
unsafe { ptr::write(&mut self.value, Some(val)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn assert_send<T>()
|
||||||
|
where
|
||||||
|
T: Send,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn assert_sync<T>()
|
||||||
|
where
|
||||||
|
T: Sync,
|
||||||
|
{
|
||||||
|
}
|
440
src/lib.rs
440
src/lib.rs
|
@ -1,170 +1,342 @@
|
||||||
//! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers
|
//! Real Time For the Masses (RTFM) framework for ARM Cortex-M microcontrollers
|
||||||
//!
|
//!
|
||||||
//! This crate is based on [the RTFM framework] created by the Embedded Systems
|
//! **IMPORTANT**: This crate is published as [`cortex-m-rtfm`] on crates.io but the name of the
|
||||||
//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren,
|
//! library is `rtfm`.
|
||||||
//! and uses a simplified version of the Stack Resource Policy as scheduling
|
|
||||||
//! policy (check the [references] for details).
|
|
||||||
//!
|
//!
|
||||||
//! [the RTFM framework]: http://www.rtfm-lang.org/
|
//! [`cortex-m-rtfm`]: https://crates.io/crates/cortex-m-rtfm
|
||||||
//! [ltu]: https://www.ltu.se/?l=en
|
|
||||||
//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
|
|
||||||
//! [references]: ./index.html#references
|
|
||||||
//!
|
//!
|
||||||
//! # Features
|
//! The user level documentation can be found [here].
|
||||||
//!
|
//!
|
||||||
//! - **Event triggered tasks** as the unit of concurrency.
|
//! [here]: ../../book/index.html
|
||||||
//! - Support for prioritization of tasks and, thus, **preemptive
|
|
||||||
//! multitasking**.
|
|
||||||
//! - **Efficient and data race free memory sharing** through fine grained *non
|
|
||||||
//! global* critical sections.
|
|
||||||
//! - **Deadlock free execution** guaranteed at compile time.
|
|
||||||
//! - **Minimal scheduling overhead** as the scheduler has no "software
|
|
||||||
//! component": the hardware does all the scheduling.
|
|
||||||
//! - **Highly efficient memory usage**: All the tasks share a single call stack
|
|
||||||
//! and there's no hard dependency on a dynamic memory allocator.
|
|
||||||
//! - **All Cortex M devices are fully supported**.
|
|
||||||
//! - This task model is amenable to known WCET (Worst Case Execution Time)
|
|
||||||
//! analysis and scheduling analysis techniques. (Though we haven't yet
|
|
||||||
//! developed Rust friendly tooling for that.)
|
|
||||||
//!
|
//!
|
||||||
//! # Constraints
|
//! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component
|
||||||
|
//! of the framework.
|
||||||
//!
|
//!
|
||||||
//! - Tasks must run to completion. That's it, tasks can't contain endless
|
//! [`#[app]`]: ../cortex_m_rtfm_macros/attr.app.html
|
||||||
//! loops. However, you can run an endless event loop in the `idle` *loop*.
|
|
||||||
//!
|
//!
|
||||||
//! - Task priorities must remain constant at runtime.
|
//! # Cargo features
|
||||||
//!
|
//!
|
||||||
//! # Dependencies
|
//! - `timer-queue`. This opt-in feature enables the `schedule` API which can be used to schedule
|
||||||
|
//! tasks to run in the future. Also see [`Instant`] and [`Duration`].
|
||||||
//!
|
//!
|
||||||
//! The application crate must depend on a device crate generated using
|
//! [`Instant`]: struct.Instant.html
|
||||||
//! [`svd2rust`] v0.12.x and the "rt" feature of that crate must be enabled. The
|
//! [`Duration`]: struct.Duration.html
|
||||||
//! SVD file used to generate the device crate *must* contain [`<cpu>`]
|
|
||||||
//! information.
|
|
||||||
//!
|
|
||||||
//! [`svd2rust`]: https://docs.rs/svd2rust/0.12.0/svd2rust/
|
|
||||||
//! [`<cpu>`]: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_cpu.html
|
|
||||||
//!
|
|
||||||
//! # `app!`
|
|
||||||
//!
|
|
||||||
//! The `app!` macro is documented [here].
|
|
||||||
//!
|
|
||||||
//! [here]: https://docs.rs/cortex-m-rtfm-macros/0.3.0/cortex_m_rtfm_macros/fn.app.html
|
|
||||||
//!
|
|
||||||
//! # Important: Cortex-M7 devices
|
|
||||||
//!
|
|
||||||
//! If targeting a Cortex-M7 device with revision r0p1 then you MUST enable the `cm7-r0p1` Cargo
|
|
||||||
//! feature of this crate or the `Resource.claim` and `Resource.claim_mut` methods WILL misbehave.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! In increasing grade of complexity. See the [examples](./examples/index.html)
|
|
||||||
//! module.
|
|
||||||
//!
|
|
||||||
//! # References
|
|
||||||
//!
|
|
||||||
//! - Baker, T. P. (1991). Stack-based scheduling of realtime processes.
|
|
||||||
//! *Real-Time Systems*, 3(1), 67-99.
|
|
||||||
//!
|
|
||||||
//! > The original Stack Resource Policy paper. [PDF][srp].
|
|
||||||
//!
|
|
||||||
//! [srp]: http://www.cs.fsu.edu/~baker/papers/mstacks3.pdf
|
|
||||||
//!
|
|
||||||
//! - Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P.
|
|
||||||
//! (2013, June). Real-time for the masses, step 1: Programming API and static
|
|
||||||
//! priority SRP kernel primitives. In Industrial Embedded Systems (SIES),
|
|
||||||
//! 2013 8th IEEE International Symposium on (pp. 110-113). IEEE.
|
|
||||||
//!
|
|
||||||
//! > A description of the RTFM task and resource model. [PDF][rtfm]
|
|
||||||
//!
|
|
||||||
//! [rtfm]: http://www.diva-portal.org/smash/get/diva2:1005680/FULLTEXT01.pdf
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate cortex_m;
|
use core::{cell::Cell, u8};
|
||||||
extern crate cortex_m_rtfm_macros;
|
#[cfg(feature = "timer-queue")]
|
||||||
extern crate rtfm_core;
|
use core::{cmp::Ordering, ops};
|
||||||
extern crate untagged_option;
|
|
||||||
|
|
||||||
use core::{mem, u8};
|
#[cfg(not(feature = "timer-queue"))]
|
||||||
|
use cortex_m::peripheral::SYST;
|
||||||
pub use cortex_m::asm::{bkpt, wfi};
|
#[cfg(armv7m)]
|
||||||
pub use cortex_m_rtfm_macros::app;
|
|
||||||
pub use rtfm_core::{Resource, Threshold};
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use untagged_option::UntaggedOption;
|
|
||||||
|
|
||||||
use cortex_m::interrupt::{self, Nr};
|
|
||||||
use cortex_m::peripheral::NVIC;
|
|
||||||
#[cfg(not(armv6m))]
|
|
||||||
use cortex_m::register::basepri;
|
use cortex_m::register::basepri;
|
||||||
|
use cortex_m::{
|
||||||
|
interrupt::{self, Nr},
|
||||||
|
peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU},
|
||||||
|
};
|
||||||
|
pub use cortex_m_rtfm_macros::app;
|
||||||
|
|
||||||
pub mod examples;
|
#[doc(hidden)]
|
||||||
|
pub mod export;
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
mod tq;
|
||||||
|
|
||||||
/// Executes the closure `f` in a preemption free context
|
/// Core peripherals
|
||||||
///
|
///
|
||||||
/// During the execution of the closure no task can preempt the current task.
|
/// This is `cortex_m::Peripherals` minus the peripherals that the RTFM runtime uses
|
||||||
pub fn atomic<R, F>(t: &mut Threshold, f: F) -> R
|
///
|
||||||
where
|
/// - The `NVIC` field is never present.
|
||||||
F: FnOnce(&mut Threshold) -> R,
|
/// - When the `timer-queue` feature is enabled the following fields are *not* present: `DWT` and
|
||||||
{
|
/// `SYST`.
|
||||||
if t.value() == u8::MAX {
|
#[allow(non_snake_case)]
|
||||||
f(t)
|
pub struct Peripherals<'a> {
|
||||||
} else {
|
/// Cache and branch predictor maintenance operations (not present on Cortex-M0 variants)
|
||||||
interrupt::disable();
|
pub CBP: CBP,
|
||||||
let r = f(&mut unsafe { Threshold::max() });
|
|
||||||
unsafe { interrupt::enable() };
|
/// CPUID
|
||||||
r
|
pub CPUID: CPUID,
|
||||||
|
|
||||||
|
/// Debug Control Block (by value if the `timer-queue` feature is disabled)
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
pub DCB: &'a mut DCB,
|
||||||
|
|
||||||
|
/// Debug Control Block (borrowed if the `timer-queue` feature is enabled)
|
||||||
|
#[cfg(not(feature = "timer-queue"))]
|
||||||
|
pub DCB: DCB,
|
||||||
|
|
||||||
|
/// Data Watchpoint and Trace unit (not present if the `timer-queue` feature is enabled)
|
||||||
|
#[cfg(not(feature = "timer-queue"))]
|
||||||
|
pub DWT: DWT,
|
||||||
|
|
||||||
|
/// Flash Patch and Breakpoint unit (not present on Cortex-M0 variants)
|
||||||
|
pub FPB: FPB,
|
||||||
|
|
||||||
|
/// Floating Point Unit (only present on `thumbv7em-none-eabihf`)
|
||||||
|
pub FPU: FPU,
|
||||||
|
|
||||||
|
/// Instrumentation Trace Macrocell (not present on Cortex-M0 variants)
|
||||||
|
pub ITM: ITM,
|
||||||
|
|
||||||
|
/// Memory Protection Unit
|
||||||
|
pub MPU: MPU,
|
||||||
|
|
||||||
|
// Nested Vector Interrupt Controller
|
||||||
|
// pub NVIC: NVIC,
|
||||||
|
/// System Control Block
|
||||||
|
pub SCB: &'a mut SCB,
|
||||||
|
|
||||||
|
/// SysTick: System Timer (not present if the `timer-queue` is enabled)
|
||||||
|
#[cfg(not(feature = "timer-queue"))]
|
||||||
|
pub SYST: SYST,
|
||||||
|
|
||||||
|
/// Trace Port Interface Unit (not present on Cortex-M0 variants)
|
||||||
|
pub TPIU: TPIU,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A measurement of a monotonically nondecreasing clock. Opaque and useful only with `Duration`
|
||||||
|
///
|
||||||
|
/// This data type is only available when the `timer-queue` feature is enabled
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
pub struct Instant(i32);
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl Instant {
|
||||||
|
/// IMPLEMENTATION DETAIL. DO NOT USE
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn artificial(timestamp: i32) -> Self {
|
||||||
|
Instant(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an instant corresponding to "now"
|
||||||
|
pub fn now() -> Self {
|
||||||
|
Instant(DWT::get_cycle_count() as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed since this instant was created.
|
||||||
|
pub fn elapsed(&self) -> Duration {
|
||||||
|
Instant::now() - *self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of time elapsed from another instant to this one.
|
||||||
|
pub fn duration_since(&self, earlier: Instant) -> Duration {
|
||||||
|
let diff = self.0 - earlier.0;
|
||||||
|
assert!(diff >= 0, "second instant is later than self");
|
||||||
|
Duration(diff as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[cfg(feature = "timer-queue")]
|
||||||
#[doc(hidden)]
|
impl ops::AddAssign<Duration> for Instant {
|
||||||
pub unsafe fn claim<T, R, F>(
|
fn add_assign(&mut self, dur: Duration) {
|
||||||
data: T,
|
debug_assert!(dur.0 < (1 << 31));
|
||||||
ceiling: u8,
|
self.0 = self.0.wrapping_add(dur.0 as i32);
|
||||||
_nvic_prio_bits: u8,
|
}
|
||||||
t: &mut Threshold,
|
}
|
||||||
f: F,
|
|
||||||
) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(T, &mut Threshold) -> R,
|
|
||||||
{
|
|
||||||
if ceiling > t.value() {
|
|
||||||
match () {
|
|
||||||
#[cfg(armv6m)]
|
|
||||||
() => atomic(t, |t| f(data, t)),
|
|
||||||
|
|
||||||
#[cfg(not(armv6m))]
|
#[cfg(feature = "timer-queue")]
|
||||||
() => {
|
impl ops::Add<Duration> for Instant {
|
||||||
let max_priority = 1 << _nvic_prio_bits;
|
type Output = Self;
|
||||||
|
|
||||||
if ceiling == max_priority {
|
fn add(mut self, dur: Duration) -> Self {
|
||||||
atomic(t, |t| f(data, t))
|
self += dur;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::SubAssign<Duration> for Instant {
|
||||||
|
fn sub_assign(&mut self, dur: Duration) {
|
||||||
|
// XXX should this be a non-debug assertion?
|
||||||
|
debug_assert!(dur.0 < (1 << 31));
|
||||||
|
self.0 = self.0.wrapping_sub(dur.0 as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::Sub<Duration> for Instant {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(mut self, dur: Duration) -> Self {
|
||||||
|
self -= dur;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::Sub<Instant> for Instant {
|
||||||
|
type Output = Duration;
|
||||||
|
|
||||||
|
fn sub(self, other: Instant) -> Duration {
|
||||||
|
self.duration_since(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl Ord for Instant {
|
||||||
|
fn cmp(&self, rhs: &Self) -> Ordering {
|
||||||
|
self.0.wrapping_sub(rhs.0).cmp(&0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl PartialOrd for Instant {
|
||||||
|
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `Duration` type to represent a span of time.
|
||||||
|
///
|
||||||
|
/// This data type is only available when the `timer-queue` feature is enabled
|
||||||
|
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
pub struct Duration(u32);
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::AddAssign for Duration {
|
||||||
|
fn add_assign(&mut self, dur: Duration) {
|
||||||
|
self.0 += dur.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::Add<Duration> for Duration {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Duration(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::SubAssign for Duration {
|
||||||
|
fn sub_assign(&mut self, rhs: Duration) {
|
||||||
|
self.0 -= rhs.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl ops::Sub<Duration> for Duration {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self {
|
||||||
|
Duration(self.0 - rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the `cycles` method to the `u32` type
|
||||||
|
///
|
||||||
|
/// This trait is only available when the `timer-queue` feature is enabled
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
pub trait U32Ext {
|
||||||
|
/// Converts the `u32` value into clock cycles
|
||||||
|
fn cycles(self) -> Duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timer-queue")]
|
||||||
|
impl U32Ext for u32 {
|
||||||
|
fn cycles(self) -> Duration {
|
||||||
|
Duration(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Memory safe access to shared resources
|
||||||
|
///
|
||||||
|
/// In RTFM, locks are implemented as critical sections that prevent other tasks from *starting*.
|
||||||
|
/// These critical sections are implemented by temporarily increasing the dynamic priority (see
|
||||||
|
/// [BASEPRI]) of the current context.
|
||||||
|
///
|
||||||
|
/// [BASEPRI]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100701/latest/special-purpose-mask-registers
|
||||||
|
pub unsafe trait Mutex {
|
||||||
|
/// IMPLEMENTATION DETAIL. DO NOT USE THIS CONSTANT
|
||||||
|
#[doc(hidden)]
|
||||||
|
const CEILING: u8;
|
||||||
|
|
||||||
|
/// IMPLEMENTATION DETAIL. DO NOT USE THIS CONSTANT
|
||||||
|
#[doc(hidden)]
|
||||||
|
const NVIC_PRIO_BITS: u8;
|
||||||
|
|
||||||
|
/// Data protected by the mutex
|
||||||
|
type Data: Send;
|
||||||
|
|
||||||
|
/// IMPLEMENTATION DETAIL. DO NOT USE THIS METHOD
|
||||||
|
#[doc(hidden)]
|
||||||
|
unsafe fn priority(&self) -> &Cell<u8>;
|
||||||
|
|
||||||
|
/// IMPLEMENTATION DETAIL. DO NOT USE THIS METHOD
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn ptr(&self) -> *mut Self::Data;
|
||||||
|
|
||||||
|
/// Creates a critical section and grants temporary access to the protected data
|
||||||
|
#[inline(always)]
|
||||||
|
#[cfg(armv7m)]
|
||||||
|
fn lock<R, F>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Data) -> R,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let current = self.priority().get();
|
||||||
|
|
||||||
|
if self.priority().get() < Self::CEILING {
|
||||||
|
if Self::CEILING == (1 << Self::NVIC_PRIO_BITS) {
|
||||||
|
self.priority().set(u8::MAX);
|
||||||
|
let r = interrupt::free(|_| f(&mut *self.ptr()));
|
||||||
|
self.priority().set(current);
|
||||||
|
r
|
||||||
} else {
|
} else {
|
||||||
let old = basepri::read();
|
self.priority().set(Self::CEILING);
|
||||||
let hw = (max_priority - ceiling) << (8 - _nvic_prio_bits);
|
basepri::write(logical2hw(Self::CEILING, Self::NVIC_PRIO_BITS));
|
||||||
basepri::write(hw);
|
let r = f(&mut *self.ptr());
|
||||||
let ret = f(data, &mut Threshold::new(ceiling));
|
basepri::write(logical2hw(current, Self::NVIC_PRIO_BITS));
|
||||||
basepri::write(old);
|
self.priority().set(current);
|
||||||
ret
|
r
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
f(&mut *self.ptr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a critical section and grants temporary access to the protected data
|
||||||
|
#[cfg(not(armv7m))]
|
||||||
|
fn lock<R, F>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Data) -> R,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let current = self.priority().get();
|
||||||
|
|
||||||
|
if self.priority().get() < Self::CEILING {
|
||||||
|
self.priority().set(u8::MAX);
|
||||||
|
let r = interrupt::free(|_| f(&mut *self.ptr()));
|
||||||
|
self.priority().set(current);
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
f(&mut *self.ptr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
f(data, t)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets an interrupt, that is a task, as pending
|
#[cfg(armv7m)]
|
||||||
|
#[inline]
|
||||||
|
fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
|
||||||
|
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the given `interrupt` as pending
|
||||||
///
|
///
|
||||||
/// If the task priority is high enough the task will be serviced immediately,
|
/// This is a convenience function around
|
||||||
/// otherwise it will be serviced at some point after the current task ends.
|
/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend)
|
||||||
pub fn set_pending<I>(interrupt: I)
|
pub fn pend<I>(interrupt: I)
|
||||||
where
|
where
|
||||||
I: Nr,
|
I: Nr,
|
||||||
{
|
{
|
||||||
// NOTE(safe) atomic write
|
NVIC::pend(interrupt)
|
||||||
let mut nvic: NVIC = unsafe { mem::transmute(()) };
|
|
||||||
nvic.set_pending(interrupt);
|
|
||||||
}
|
}
|
||||||
|
|
135
src/tq.rs
Normal file
135
src/tq.rs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
use core::cmp::{self, Ordering};
|
||||||
|
|
||||||
|
use cortex_m::peripheral::{SCB, SYST};
|
||||||
|
use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
|
||||||
|
|
||||||
|
use crate::{Instant, Mutex};
|
||||||
|
|
||||||
|
pub struct TimerQueue<T, N>
|
||||||
|
where
|
||||||
|
N: ArrayLength<NotReady<T>>,
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub syst: SYST,
|
||||||
|
pub queue: BinaryHeap<NotReady<T>, N, Min>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, N> TimerQueue<T, N>
|
||||||
|
where
|
||||||
|
N: ArrayLength<NotReady<T>>,
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub fn new(syst: SYST) -> Self {
|
||||||
|
TimerQueue {
|
||||||
|
syst,
|
||||||
|
queue: BinaryHeap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady<T>) {
|
||||||
|
let mut is_empty = true;
|
||||||
|
if self
|
||||||
|
.queue
|
||||||
|
.peek()
|
||||||
|
.map(|head| {
|
||||||
|
is_empty = false;
|
||||||
|
nr.instant < head.instant
|
||||||
|
})
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
if is_empty {
|
||||||
|
self.syst.enable_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set SysTick pending
|
||||||
|
(*SCB::ptr()).icsr.write(1 << 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.queue.push_unchecked(nr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NotReady<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub index: u8,
|
||||||
|
pub instant: Instant,
|
||||||
|
pub task: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Eq for NotReady<T> where T: Copy {}
|
||||||
|
|
||||||
|
impl<T> Ord for NotReady<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.instant.cmp(&other.instant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PartialEq for NotReady<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.instant == other.instant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PartialOrd for NotReady<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(&other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn isr<TQ, T, N, F>(mut tq: TQ, mut f: F)
|
||||||
|
where
|
||||||
|
TQ: Mutex<Data = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue