Skip to content

Commit

Permalink
Merge #205
Browse files Browse the repository at this point in the history
205: rtfm-syntax refactor + heterogeneous multi-core support r=japaric a=japaric

this PR implements RFCs #178, #198, #199, #200, #201, #203 (only the refactor
part), #204, #207, #211 and #212.

most cfail tests have been removed because the test suite of `rtfm-syntax`
already tests what was being tested here. The `rtfm-syntax` crate also has tests
for the analysis pass which we didn't have here -- that test suite contains a
regression test for #183.

the remaining cfail tests have been upgraded into UI test so we can more
thoroughly check / test the error message presented to the end user.

the cpass tests have been converted into plain examples

EDIT: I forgot, there are some examples of the multi-core API for the LPC541xx in [this repository](https://github.com/japaric/lpcxpresso54114)

people that would like to try out this API but have no hardware can try out the
x86_64 [Linux port] which also has multi-core support.

[Linux port]: https://github.com/japaric/linux-rtfm

closes #178 #198 #199 #200 #201 #203 #204 #207 #211 #212 
closes #163 
cc #209 (documents how to deal with errors)

Co-authored-by: Jorge Aparicio <jorge@japaric.io>
  • Loading branch information
bors[bot] and japaric committed Sep 15, 2019
2 parents fafeeb2 + 7aa270c commit 2fa07c8
Show file tree
Hide file tree
Showing 172 changed files with 5,569 additions and 6,418 deletions.
20 changes: 13 additions & 7 deletions .travis.yml
Expand Up @@ -3,22 +3,28 @@ language: rust
matrix:
include:
# NOTE used to build docs on successful merges to master
# - env: TARGET=x86_64-unknown-linux-gnu
- env: TARGET=x86_64-unknown-linux-gnu

# - env: TARGET=thumbv6m-none-eabi
# if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
# MSRV
- env: TARGET=thumbv7m-none-eabi
rust: 1.36.0
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)

- env: TARGET=thumbv6m-none-eabi
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)

# - env: TARGET=thumbv7m-none-eabi
# if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- env: TARGET=thumbv7m-none-eabi
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)

# compile-fail tests
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
# if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)

# heterogeneous multi-core support
- env: TARGET=thumbv6m-none-eabi
rust: nightly
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)

- env: TARGET=thumbv7m-none-eabi
rust: nightly
if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
Expand Down
58 changes: 36 additions & 22 deletions Cargo.toml
Expand Up @@ -12,61 +12,75 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rtfm"
readme = "README.md"
repository = "https://github.com/japaric/cortex-m-rtfm"
version = "0.5.0-alpha.1"
version = "0.5.0-beta.1"

[lib]
name = "rtfm"

[[example]]
name = "baseline"
required-features = ["timer-queue"]
required-features = ["__v7"]

[[example]]
name = "periodic"
required-features = ["timer-queue"]
required-features = ["__v7"]

[[example]]
name = "pool"
# this example doesn't need this feature but only works on ARMv7-M
# specifying the feature here avoids compiling this for ARMv6-M
required-features = ["timer-queue"]
required-features = ["__v7"]

[[example]]
name = "schedule"
required-features = ["timer-queue"]
required-features = ["__v7"]

[[example]]
name = "t-cfg"
required-features = ["__v7"]

[[example]]
name = "t-schedule"
required-features = ["__v7"]

[[example]]
name = "types"
required-features = ["timer-queue"]
required-features = ["__v7"]

[dependencies]
cortex-m = "0.5.8"
cortex-m-rt = "0.6.7"
cortex-m-rtfm-macros = { path = "macros", version = "0.5.0-alpha.1" }
heapless = "0.5.0-alpha.1"
cortex-m = "0.6.0"
cortex-m-rtfm-macros = { path = "macros" }
rtfm-core = { git = "https://github.com/japaric/rtfm-core" }
cortex-m-rt = "0.6.9"
heapless = "0.5.0"

[dependencies.microamp]
optional = true
version = "0.1.0-alpha.2"

[dev-dependencies]
cortex-m-semihosting = "0.3.2"
lm3s6965 = "0.1.3"
panic-halt = "0.2.0"
cortex-m-semihosting = "0.3.3"

[dev-dependencies.panic-semihosting]
features = ["exit"]
version = "0.5.1"

[features]
timer-queue = ["cortex-m-rtfm-macros/timer-queue"]
version = "0.5.2"

[target.x86_64-unknown-linux-gnu.dev-dependencies]
compiletest_rs = "0.3.21"
tempdir = "0.3.7"
compiletest_rs = "0.3.22"

[package.metadata.docs.rs]
features = ["timer-queue"]
[features]
heterogeneous = ["cortex-m-rtfm-macros/heterogeneous", "microamp"]
homogeneous = ["cortex-m-rtfm-macros/homogeneous"]
# used for testing this crate; do not use in applications
__v7 =[]

[profile.release]
codegen-units = 1
lto = true

[workspace]
members = ["macros"]
members = [
"heterogeneous",
"homogeneous",
"macros",
]
4 changes: 1 addition & 3 deletions README.md
Expand Up @@ -31,9 +31,7 @@ A concurrency framework for building real time systems.
- **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 supported**. The core features of RTFM are
supported on all Cortex-M devices. The timer queue is currently only supported
on ARMv7-M devices.
- **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
Expand Down
4 changes: 3 additions & 1 deletion book/en/src/SUMMARY.md
Expand Up @@ -4,7 +4,7 @@
- [RTFM by example](./by-example.md)
- [The `app` attribute](./by-example/app.md)
- [Resources](./by-example/resources.md)
- [Tasks](./by-example/tasks.md)
- [Software tasks](./by-example/tasks.md)
- [Timer queue](./by-example/timer-queue.md)
- [Types, Send and Sync](./by-example/types-send-sync.md)
- [Starting a new project](./by-example/new.md)
Expand All @@ -18,3 +18,5 @@
- [Ceiling analysis](./internals/ceilings.md)
- [Software tasks](./internals/tasks.md)
- [Timer queue](./internals/timer-queue.md)
- [Homogeneous multi-core support](./homogeneous.md)
- [Heterogeneous multi-core support](./heterogeneous.md)
85 changes: 66 additions & 19 deletions book/en/src/by-example/app.md
Expand Up @@ -10,8 +10,8 @@ All RTFM applications use the [`app`] attribute (`#[app(..)]`). This attribute
must be applied to a `const` item that contains items. The `app` attribute has
a mandatory `device` argument that takes a *path* as a value. This path must
point to a *peripheral access crate* (PAC) generated using [`svd2rust`]
**v0.14.x**. The `app` attribute will expand into a suitable entry point so it's
not required to use the [`cortex_m_rt::entry`] attribute.
**v0.14.x** or newer. 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
Expand All @@ -28,22 +28,23 @@ not required to use the [`cortex_m_rt::entry`] attribute.

Within the pseudo-module the `app` attribute expects to find an initialization
function marked with the `init` attribute. This function must have signature
`fn(init::Context) [-> init::LateResources]`.
`fn(init::Context) [-> init::LateResources]` (the return type is not always
required).

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 fields of `init::Context`. Not all Cortex-M peripherals are available
in `core` because the RTFM runtime takes ownership of some of them -- for more
details see the [`rtfm::Peripherals`] struct.
to Cortex-M and, optionally, device specific peripherals through the `core` and
`device` fields of `init::Context`.

`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.
The example below shows the types of the `core` and `device` fields and
showcases safe access to a `static mut` variable. The `device` field is only
available when the `peripherals` argument is set to `true` (it defaults to
`false`).

``` rust
{{#include ../../../../examples/init.rs}}
Expand All @@ -64,7 +65,7 @@ signature `fn(idle::Context) - > !`.

When present, the runtime will execute the `idle` task after `init`. Unlike
`init`, `idle` will run *with interrupts enabled* and it's not allowed to return
so it runs forever.
so it must run forever.

When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and
then sends the microcontroller to sleep after running `init`.
Expand All @@ -84,21 +85,67 @@ The example below shows that `idle` runs after `init`.
$ cargo run --example idle
{{#include ../../../../ci/expected/idle.run}}```

## `interrupt` / `exception`
## Hardware tasks

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.
To declare interrupt handlers the framework provides a `#[task]` attribute that
can be attached to functions. This attribute takes a `binds` argument whose
value is the name of the interrupt to which the handler will be bound to; the
function adornated with this attribute becomes the interrupt handler. Within the
framework these type of tasks are referred to as *hardware* tasks, because they
start executing in reaction to a hardware event.

The example below demonstrates the use of the `#[task]` attribute to declare an
interrupt handler. Like in the case of `#[init]` and `#[idle]` local `static
mut` variables are safe to use within a hardware task.

``` rust
{{#include ../../../../examples/interrupt.rs}}
{{#include ../../../../examples/hardware.rs}}
```

``` console
$ cargo run --example interrupt
{{#include ../../../../ci/expected/interrupt.run}}```
{{#include ../../../../ci/expected/hardware.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.
applications one can write using only the `cortex-m-rt` crate. From this point
we start introducing features unique to RTFM.

## Priorities

The static priority of each handler can be declared in the `task` attribute
using the `priority` argument. Tasks can have priorities in the range `1..=(1 <<
NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
crate. When the `priority` argument is omitted the priority is assumed to be
`1`. The `idle` task has a non-configurable static priority of `0`, the lowest
priority.

When several tasks are ready to be executed the one with *highest* static
priority will be executed first. Task prioritization can be observed in the
following scenario: an interrupt signal arrives during the execution of a low
priority task; the signal puts the higher priority task in the pending state.
The difference in priority results in the higher priority task preempting the
lower priority one: the execution of the lower priority task is suspended and
the higher priority task is executed to completion. Once the higher priority
task has terminated the lower priority task is resumed.

The following example showcases the priority based scheduling of tasks.

``` rust
{{#include ../../../../examples/preempt.rs}}
```

``` console
$ cargo run --example interrupt
{{#include ../../../../ci/expected/preempt.run}}```

Note that the task `gpiob` does *not* preempt task `gpioc` because its priority
is the *same* as `gpioc`'s. However, once `gpioc` terminates the execution of
task `gpiob` is prioritized over `gpioa`'s due to its higher priority. `gpioa`
is resumed only after `gpiob` terminates.

One more note about priorities: choosing a priority higher than what the device
supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to
limitations in the language the error message is currently far from helpful: it
will say something along the lines of "evaluation of constant value failed" and
the span of the error will *not* point out to the problematic interrupt value --
we are sorry about this!
3 changes: 1 addition & 2 deletions book/en/src/by-example/new.md
Expand Up @@ -36,8 +36,7 @@ $ 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.
3. Add the `cortex-m-rtfm` crate as a dependency.

``` console
$ cargo add cortex-m-rtfm --allow-prerelease
Expand Down

0 comments on commit 2fa07c8

Please sign in to comment.