mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
WIP
This commit is contained in:
parent
754f041ae0
commit
8723c6d45b
41 changed files with 1974 additions and 2851 deletions
|
@ -15,15 +15,15 @@ version = "0.3.2"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = "0.4.0"
|
cortex-m = "0.4.0"
|
||||||
cortex-m-rtfm-macros = { path = "macros", version = "0.3.1" }
|
cortex-m-rtfm-macros = { path = "macros", version = "0.3.1" }
|
||||||
heapless = "0.2.7"
|
heapless = "0.3.4"
|
||||||
rtfm-core = "0.2.0"
|
typenum = "1.10.0"
|
||||||
untagged-option = "0.1.1"
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
||||||
compiletest_rs = "0.3.5"
|
compiletest_rs = "0.3.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
panic-abort = "0.1.1"
|
panic-abort = "0.1.1"
|
||||||
|
panic-itm = "0.1.0"
|
||||||
|
|
||||||
[dev-dependencies.stm32f103xx]
|
[dev-dependencies.stm32f103xx]
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
|
|
8
build.rs
8
build.rs
|
@ -5,6 +5,14 @@ fn main() {
|
||||||
|
|
||||||
if target.starts_with("thumbv6m-") {
|
if target.starts_with("thumbv6m-") {
|
||||||
println!("cargo:rustc-cfg=armv6m");
|
println!("cargo:rustc-cfg=armv6m");
|
||||||
|
} else if target.starts_with("thumbv7m-") {
|
||||||
|
println!("cargo:rustc-cfg=armv7m");
|
||||||
|
} else if target.starts_with("thumbv7em-") {
|
||||||
|
println!("cargo:rustc-cfg=armv7m");
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.ends_with("-eabihf") {
|
||||||
|
println!("cargo:rustc-cfg=has_fpu");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
|
55
examples/async-after.rs
Normal file
55
examples/async-after.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
free_interrupts: [EXTI1],
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
exti0: {
|
||||||
|
interrupt: EXTI0,
|
||||||
|
async_after: [a],
|
||||||
|
},
|
||||||
|
|
||||||
|
a: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const S: u32 = 8_000_000;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
init::LateResources {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exti0(mut ctxt: exti0::Context) {
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 1 * S, ());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(ctxt: a::Context) {
|
||||||
|
asm::bkpt();
|
||||||
|
}
|
57
examples/async.rs
Normal file
57
examples/async.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
init: {
|
||||||
|
async: [a],
|
||||||
|
},
|
||||||
|
|
||||||
|
free_interrupts: [EXTI1],
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
exti0: {
|
||||||
|
interrupt: EXTI0,
|
||||||
|
async: [a],
|
||||||
|
},
|
||||||
|
|
||||||
|
a: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
init::LateResources {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exti0(mut ctxt: exti0::Context) {
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, ());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(ctxt: a::Context) {
|
||||||
|
asm::bkpt();
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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) {}
|
|
33
examples/empty.rs
Normal file
33
examples/empty.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
init::LateResources {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
//! A showcase of the `app!` macro syntax
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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,74 +0,0 @@
|
||||||
//! Working with resources in a generic fashion
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use rtfm::{app, Resource, Threshold};
|
|
||||||
use stm32f103xx::{SPI1, GPIOA};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
42
examples/interrupt.rs
Normal file
42
examples/interrupt.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
exti0: {
|
||||||
|
interrupt: EXTI0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
init::LateResources {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exti0(ctxt: exti0::Context) {}
|
|
@ -1,87 +0,0 @@
|
||||||
//! Demonstrates initialization of resources in `init`.
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +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)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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 { mut LOW, 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();
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
//! An application with one task
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
91
examples/periodic-payload.rs
Normal file
91
examples/periodic-payload.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// # -Os
|
||||||
|
// init
|
||||||
|
// a(bl=8000000, now=8000180, input=0)
|
||||||
|
// a(bl=16000000, now=16000180, input=1)
|
||||||
|
// a(bl=24000000, now=24000180, input=2)
|
||||||
|
//
|
||||||
|
// # -O3
|
||||||
|
// init
|
||||||
|
// a(bl=8000000, now=8000168, input=0)
|
||||||
|
// a(bl=16000000, now=16000168, input=1)
|
||||||
|
// a(bl=24000000, now=24000168, input=2)
|
||||||
|
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
resources: {
|
||||||
|
static ITM: ITM;
|
||||||
|
},
|
||||||
|
|
||||||
|
init: {
|
||||||
|
async_after: [a],
|
||||||
|
},
|
||||||
|
|
||||||
|
free_interrupts: [EXTI0],
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
a: {
|
||||||
|
async_after: [a],
|
||||||
|
input: u16,
|
||||||
|
resources: [ITM],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const MS: u32 = 8_000;
|
||||||
|
const S: u32 = 1_000 * MS;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
iprintln!(&mut ctxt.core.ITM.stim[0], "init");
|
||||||
|
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 1 * S, 0).ok();
|
||||||
|
|
||||||
|
init::LateResources { ITM: ctxt.core.ITM }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(mut ctxt: a::Context) {
|
||||||
|
let now = DWT::get_cycle_count();
|
||||||
|
let input = ctxt.input;
|
||||||
|
|
||||||
|
let bl = ctxt.baseline;
|
||||||
|
let itm = ctxt.resources.ITM;
|
||||||
|
iprintln!(
|
||||||
|
&mut itm.stim[0],
|
||||||
|
"a(bl={}, now={}, input={})",
|
||||||
|
bl,
|
||||||
|
now,
|
||||||
|
input
|
||||||
|
);
|
||||||
|
|
||||||
|
ctxt.async
|
||||||
|
.a
|
||||||
|
.post(&mut ctxt.threshold, 1 * S, input + 1)
|
||||||
|
.ok();
|
||||||
|
}
|
130
examples/periodic-preemption-payload.rs
Normal file
130
examples/periodic-preemption-payload.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
// # -Os
|
||||||
|
// a(bl=16000000, now=16000248, input=0)
|
||||||
|
// b(bl=24000000, now=24000251, input=0)
|
||||||
|
// a(bl=32000000, now=32000248, input=1)
|
||||||
|
// b(bl=48000000, now=48000283, input=1)
|
||||||
|
// a(bl=48000000, now=48002427, input=2)
|
||||||
|
// a(bl=64000000, now=64000248, input=3)
|
||||||
|
// b(bl=72000000, now=72000251, input=2)
|
||||||
|
// a(bl=80000000, now=80000248, input=4)
|
||||||
|
// b(bl=96000000, now=96000283, input=3)
|
||||||
|
// a(bl=96000000, now=96002427, input=5)
|
||||||
|
|
||||||
|
// # -O3
|
||||||
|
// init
|
||||||
|
// a(bl=16000000, now=16000231, input=0)
|
||||||
|
// b(bl=24000000, now=24000230, input=0)
|
||||||
|
// a(bl=32000000, now=32000231, input=1)
|
||||||
|
// b(bl=48000000, now=48000259, input=1)
|
||||||
|
// a(bl=48000000, now=48002397, input=2)
|
||||||
|
// a(bl=64000000, now=64000231, input=3)
|
||||||
|
// b(bl=72000000, now=72000230, input=2)
|
||||||
|
// a(bl=80000000, now=80000231, input=4)
|
||||||
|
// b(bl=96000000, now=96000259, input=3)
|
||||||
|
// a(bl=96000000, now=96002397, input=5)
|
||||||
|
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
resources: {
|
||||||
|
static ITM: ITM;
|
||||||
|
},
|
||||||
|
|
||||||
|
init: {
|
||||||
|
async_after: [a, b],
|
||||||
|
},
|
||||||
|
|
||||||
|
free_interrupts: [EXTI0, EXTI1],
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
a: {
|
||||||
|
async_after: [a],
|
||||||
|
input: u32,
|
||||||
|
resources: [ITM],
|
||||||
|
},
|
||||||
|
|
||||||
|
b: {
|
||||||
|
async_after: [b],
|
||||||
|
input: u32,
|
||||||
|
priority: 2,
|
||||||
|
resources: [ITM],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const MS: u32 = 8_000;
|
||||||
|
const S: u32 = 1_000 * MS;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
iprintln!(&mut ctxt.core.ITM.stim[0], "init");
|
||||||
|
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 2 * S, 0).ok();
|
||||||
|
ctxt.async.b.post(&mut ctxt.threshold, 3 * S, 0).ok();
|
||||||
|
|
||||||
|
init::LateResources { ITM: ctxt.core.ITM }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(mut ctxt: a::Context) {
|
||||||
|
let now = DWT::get_cycle_count();
|
||||||
|
|
||||||
|
let input = ctxt.input;
|
||||||
|
let bl = ctxt.baseline;
|
||||||
|
ctxt.resources.ITM.claim_mut(&mut ctxt.threshold, |itm, _| {
|
||||||
|
iprintln!(
|
||||||
|
&mut itm.stim[0],
|
||||||
|
"a(bl={}, now={}, input={})",
|
||||||
|
bl,
|
||||||
|
now,
|
||||||
|
input
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ctxt.async
|
||||||
|
.a
|
||||||
|
.post(&mut ctxt.threshold, 2 * S, input + 1)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b(mut ctxt: b::Context) {
|
||||||
|
let now = DWT::get_cycle_count();
|
||||||
|
|
||||||
|
let bl = ctxt.baseline;
|
||||||
|
let input = ctxt.input;
|
||||||
|
let t = &mut ctxt.threshold;
|
||||||
|
iprintln!(
|
||||||
|
&mut ctxt.resources.ITM.borrow_mut(t).stim[0],
|
||||||
|
"b(bl={}, now={}, input={})",
|
||||||
|
bl,
|
||||||
|
now,
|
||||||
|
input,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctxt.async.b.post(t, 3 * S, input + 1).ok();
|
||||||
|
}
|
117
examples/periodic-preemption.rs
Normal file
117
examples/periodic-preemption.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// # -Os
|
||||||
|
// init
|
||||||
|
// a(bl=16000000, now=16000249)
|
||||||
|
// b(bl=24000000, now=24000248)
|
||||||
|
// a(bl=32000000, now=32000249)
|
||||||
|
// b(bl=48000000, now=48000282)
|
||||||
|
// a(bl=48000000, now=48001731)
|
||||||
|
// a(bl=64000000, now=64000249)
|
||||||
|
// b(bl=72000000, now=72000248)
|
||||||
|
// a(bl=80000000, now=80000249)
|
||||||
|
// b(bl=96000000, now=96000282)
|
||||||
|
// a(bl=96000000, now=96001731)
|
||||||
|
|
||||||
|
// # -O3
|
||||||
|
// init
|
||||||
|
// a(bl=16000000, now=16000228)
|
||||||
|
// b(bl=24000000, now=24000231)
|
||||||
|
// a(bl=32000000, now=32000228)
|
||||||
|
// b(bl=48000000, now=48000257)
|
||||||
|
// a(bl=48000000, now=48001705)
|
||||||
|
// a(bl=64000000, now=64000228)
|
||||||
|
// b(bl=72000000, now=72000231)
|
||||||
|
// a(bl=80000000, now=80000228)
|
||||||
|
// b(bl=96000000, now=96000257)
|
||||||
|
// a(bl=96000000, now=96001705)
|
||||||
|
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
resources: {
|
||||||
|
static ITM: ITM;
|
||||||
|
},
|
||||||
|
|
||||||
|
init: {
|
||||||
|
async_after: [a, b],
|
||||||
|
},
|
||||||
|
|
||||||
|
free_interrupts: [EXTI0, EXTI1],
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
a: {
|
||||||
|
async_after: [a],
|
||||||
|
resources: [ITM],
|
||||||
|
},
|
||||||
|
|
||||||
|
b: {
|
||||||
|
async_after: [b],
|
||||||
|
priority: 2,
|
||||||
|
resources: [ITM],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const MS: u32 = 8_000;
|
||||||
|
const S: u32 = 1_000 * MS;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
iprintln!(&mut ctxt.core.ITM.stim[0], "init");
|
||||||
|
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 2 * S, ()).ok();
|
||||||
|
ctxt.async.b.post(&mut ctxt.threshold, 3 * S, ()).ok();
|
||||||
|
|
||||||
|
init::LateResources { ITM: ctxt.core.ITM }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(mut ctxt: a::Context) {
|
||||||
|
let now = DWT::get_cycle_count();
|
||||||
|
|
||||||
|
let bl = ctxt.baseline;
|
||||||
|
ctxt.resources.ITM.claim_mut(&mut ctxt.threshold, |itm, _| {
|
||||||
|
iprintln!(&mut itm.stim[0], "a(bl={}, now={})", bl, now);
|
||||||
|
});
|
||||||
|
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 2 * S, ()).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn b(mut ctxt: b::Context) {
|
||||||
|
let now = DWT::get_cycle_count();
|
||||||
|
|
||||||
|
let bl = ctxt.baseline;
|
||||||
|
let t = &mut ctxt.threshold;
|
||||||
|
iprintln!(
|
||||||
|
&mut ctxt.resources.ITM.borrow_mut(t).stim[0],
|
||||||
|
"b(bl={}, now={})",
|
||||||
|
bl,
|
||||||
|
now
|
||||||
|
);
|
||||||
|
|
||||||
|
ctxt.async.b.post(t, 3 * S, ()).ok();
|
||||||
|
}
|
77
examples/periodic.rs
Normal file
77
examples/periodic.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// # -Os
|
||||||
|
// init
|
||||||
|
// a(bl=8000000, now=8000180)
|
||||||
|
// a(bl=16000000, now=16000180)
|
||||||
|
//
|
||||||
|
// # -O3
|
||||||
|
// a(bl=8000000, now=8000168)
|
||||||
|
// a(bl=16000000, now=16000168)
|
||||||
|
|
||||||
|
#![allow(warnings)]
|
||||||
|
// #![deny(unsafe_code)]
|
||||||
|
// #![deny(warnings)]
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cortex_m;
|
||||||
|
extern crate cortex_m_rtfm as rtfm;
|
||||||
|
// extern crate panic_abort;
|
||||||
|
extern crate panic_itm;
|
||||||
|
extern crate stm32f103xx;
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::{DWT, ITM};
|
||||||
|
use rtfm::{app, Resource};
|
||||||
|
|
||||||
|
app! {
|
||||||
|
device: stm32f103xx,
|
||||||
|
|
||||||
|
resources: {
|
||||||
|
static ITM: ITM;
|
||||||
|
},
|
||||||
|
|
||||||
|
init: {
|
||||||
|
async_after: [a],
|
||||||
|
},
|
||||||
|
|
||||||
|
free_interrupts: [EXTI0],
|
||||||
|
|
||||||
|
tasks: {
|
||||||
|
a: {
|
||||||
|
async_after: [a],
|
||||||
|
resources: [ITM],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const MS: u32 = 8_000;
|
||||||
|
const S: u32 = 1_000 * MS;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn init(mut ctxt: init::Context) -> init::LateResources {
|
||||||
|
iprintln!(&mut ctxt.core.ITM.stim[0], "init");
|
||||||
|
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 1 * S, ()).ok();
|
||||||
|
|
||||||
|
init::LateResources { ITM: ctxt.core.ITM }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn idle(ctxt: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(mut ctxt: a::Context) {
|
||||||
|
let now = DWT::get_cycle_count();
|
||||||
|
|
||||||
|
let bl = ctxt.baseline;
|
||||||
|
let itm = ctxt.resources.ITM;
|
||||||
|
iprintln!(&mut itm.stim[0], "a(bl={}, now={})", bl, now);
|
||||||
|
|
||||||
|
ctxt.async.a.post(&mut ctxt.threshold, 1 * S, ()).ok();
|
||||||
|
}
|
|
@ -1,68 +0,0 @@
|
||||||
//! Two tasks running at *different* priorities with access to the same resource
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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;
|
|
||||||
});
|
|
||||||
|
|
||||||
// ..
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
//! Safe creation of `&'static mut` references
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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();
|
|
||||||
}
|
|
||||||
}
|
|
273
examples/tq.rs
273
examples/tq.rs
|
@ -1,273 +0,0 @@
|
||||||
// #![deny(unsafe_code)]
|
|
||||||
// #![deny(warnings)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate cortex_m;
|
|
||||||
extern crate cortex_m_rtfm as rtfm;
|
|
||||||
extern crate panic_abort;
|
|
||||||
extern crate stm32f103xx;
|
|
||||||
|
|
||||||
use core::cmp;
|
|
||||||
|
|
||||||
use cortex_m::peripheral::syst::SystClkSource;
|
|
||||||
use cortex_m::peripheral::ITM;
|
|
||||||
use rtfm::ll::{self, Consumer, FreeList, Instant, Node, Producer, RingBuffer, Slot, TaggedPayload,
|
|
||||||
TimerQueue};
|
|
||||||
use rtfm::{app, Resource, Threshold};
|
|
||||||
use stm32f103xx::Interrupt;
|
|
||||||
|
|
||||||
const ACAP: usize = 4;
|
|
||||||
|
|
||||||
const MS: u32 = 8_000;
|
|
||||||
|
|
||||||
app! {
|
|
||||||
device: stm32f103xx,
|
|
||||||
|
|
||||||
resources: {
|
|
||||||
/* timer queue */
|
|
||||||
static TQ: TimerQueue<Task, [TaggedPayload<Task>; ACAP]>;
|
|
||||||
|
|
||||||
/* a */
|
|
||||||
// payloads w/ after
|
|
||||||
static AN: [Node<i32>; ACAP] = unsafe { ll::uninitialized() };
|
|
||||||
static AFL: FreeList<i32> = FreeList::new();
|
|
||||||
|
|
||||||
/* exti0 */
|
|
||||||
static Q1: RingBuffer<TaggedPayload<Task1>, [TaggedPayload<Task1>; ACAP + 1], u8> =
|
|
||||||
RingBuffer::u8();
|
|
||||||
static Q1C: Consumer<'static, TaggedPayload<Task1>, [TaggedPayload<Task1>; ACAP + 1], u8>;
|
|
||||||
static Q1P: Producer<'static, TaggedPayload<Task1>, [TaggedPayload<Task1>; ACAP + 1], u8>;
|
|
||||||
},
|
|
||||||
|
|
||||||
init: {
|
|
||||||
resources: [AN, Q1],
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: {
|
|
||||||
EXTI1: {
|
|
||||||
path: exti1,
|
|
||||||
resources: [TQ, AFL],
|
|
||||||
priority: 1,
|
|
||||||
|
|
||||||
// async: [a],
|
|
||||||
},
|
|
||||||
|
|
||||||
// dispatch interrupt
|
|
||||||
EXTI0: {
|
|
||||||
path: exti0,
|
|
||||||
resources: [Q1C, AFL],
|
|
||||||
priority: 3,
|
|
||||||
},
|
|
||||||
|
|
||||||
// timer queue
|
|
||||||
SYS_TICK: {
|
|
||||||
path: sys_tick,
|
|
||||||
resources: [TQ, Q1P],
|
|
||||||
priority: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(mut p: ::init::Peripherals, r: init::Resources) -> init::LateResources {
|
|
||||||
// ..
|
|
||||||
|
|
||||||
/* executed after `init` end */
|
|
||||||
p.core.DWT.enable_cycle_counter();
|
|
||||||
unsafe { p.core.DWT.cyccnt.write(0) };
|
|
||||||
p.core.SYST.set_clock_source(SystClkSource::Core);
|
|
||||||
p.core.SYST.enable_counter();
|
|
||||||
p.core.SYST.disable_interrupt();
|
|
||||||
|
|
||||||
// populate the free list
|
|
||||||
for n in r.AN {
|
|
||||||
r.AFL.push(Slot::new(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (q1p, q1c) = r.Q1.split();
|
|
||||||
init::LateResources {
|
|
||||||
TQ: TimerQueue::new(p.core.SYST),
|
|
||||||
Q1C: q1c,
|
|
||||||
Q1P: q1p,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn idle() -> ! {
|
|
||||||
rtfm::set_pending(Interrupt::EXTI1);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
rtfm::wfi()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn a(_t: &mut Threshold, bl: Instant, payload: i32) {
|
|
||||||
unsafe {
|
|
||||||
iprintln!(
|
|
||||||
&mut (*ITM::ptr()).stim[0],
|
|
||||||
"a(now={:?}, bl={:?}, payload={})",
|
|
||||||
Instant::now(),
|
|
||||||
bl,
|
|
||||||
payload
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
|
|
||||||
/* expansion */
|
|
||||||
let bl = Instant::now();
|
|
||||||
let mut async = a::Async::new(bl, r.TQ, r.AFL);
|
|
||||||
/* end of expansion */
|
|
||||||
|
|
||||||
unsafe { iprintln!(&mut (*ITM::ptr()).stim[0], "EXTI0(bl={:?})", bl) }
|
|
||||||
async.a(t, 100 * MS, 0).unwrap();
|
|
||||||
async.a(t, 50 * MS, 1).unwrap();
|
|
||||||
async.a(t, 75 * MS, 2).unwrap();
|
|
||||||
async.a(t, 75 * MS + 1, 3).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* auto generated */
|
|
||||||
fn exti0(t: &mut Threshold, mut r: EXTI0::Resources) {
|
|
||||||
while let Some(payload) = r.Q1C.dequeue() {
|
|
||||||
match payload.tag() {
|
|
||||||
Task1::a => {
|
|
||||||
let (bl, payload, slot) = unsafe { payload.coerce() }.read();
|
|
||||||
|
|
||||||
r.AFL.claim_mut(t, |afl, _| afl.push(slot));
|
|
||||||
|
|
||||||
a(&mut unsafe { Threshold::new(3) }, bl, payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sys_tick(t: &mut Threshold, r: SYS_TICK::Resources) {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let SYS_TICK::Resources { mut Q1P, mut TQ } = r;
|
|
||||||
|
|
||||||
enum State<T>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
Payload(TaggedPayload<T>),
|
|
||||||
Baseline(Instant),
|
|
||||||
Done,
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let state = TQ.claim_mut(t, |tq, _| {
|
|
||||||
if let Some(bl) = tq.queue.peek().map(|p| p.baseline()) {
|
|
||||||
if Instant::now() >= bl {
|
|
||||||
// message ready
|
|
||||||
State::Payload(tq.queue.pop().unwrap())
|
|
||||||
} else {
|
|
||||||
// new timeout
|
|
||||||
State::Baseline(bl)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// empty queue
|
|
||||||
tq.syst.disable_interrupt();
|
|
||||||
State::Done
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match state {
|
|
||||||
State::Payload(p) => match p.tag() {
|
|
||||||
Task::a => {
|
|
||||||
Q1P.claim_mut(t, |q1p, _| q1p.enqueue_unchecked(p.retag(Task1::a)));
|
|
||||||
rtfm::set_pending(Interrupt::EXTI0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State::Baseline(bl) => {
|
|
||||||
const MAX: u32 = 0x00ffffff;
|
|
||||||
|
|
||||||
let diff = bl - Instant::now();
|
|
||||||
|
|
||||||
if diff < 0 {
|
|
||||||
// message became ready
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
TQ.claim_mut(t, |tq, _| {
|
|
||||||
tq.syst.set_reload(cmp::min(MAX, diff as u32));
|
|
||||||
tq.syst.clear_current();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Done => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tasks dispatched at a priority of 1
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Task1 {
|
|
||||||
a,
|
|
||||||
}
|
|
||||||
|
|
||||||
// All tasks
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Task {
|
|
||||||
a,
|
|
||||||
}
|
|
||||||
|
|
||||||
mod a {
|
|
||||||
use cortex_m::peripheral::SCB;
|
|
||||||
|
|
||||||
use rtfm::ll::Instant;
|
|
||||||
use rtfm::{Resource, Threshold};
|
|
||||||
use Task;
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub struct Async {
|
|
||||||
// inherited baseline
|
|
||||||
baseline: Instant,
|
|
||||||
TQ: ::EXTI1::TQ,
|
|
||||||
AFL: ::EXTI1::AFL,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Async {
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub fn new(bl: Instant, TQ: ::EXTI1::TQ, AFL: ::EXTI1::AFL) -> Self {
|
|
||||||
Async {
|
|
||||||
baseline: bl,
|
|
||||||
TQ,
|
|
||||||
AFL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn a(&mut self, t: &mut Threshold, after: u32, payload: i32) -> Result<(), i32> {
|
|
||||||
if let Some(slot) = self.AFL.claim_mut(t, |afl, _| afl.pop()) {
|
|
||||||
let baseline = self.baseline;
|
|
||||||
self.TQ.claim_mut(t, |tq, _| {
|
|
||||||
if tq.queue.capacity() == tq.queue.len() {
|
|
||||||
// full
|
|
||||||
Err(payload)
|
|
||||||
} else {
|
|
||||||
let bl = baseline + after;
|
|
||||||
if tq.queue
|
|
||||||
.peek()
|
|
||||||
.map(|head| bl < head.baseline())
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
tq.syst.enable_interrupt();
|
|
||||||
// Set SYST pending
|
|
||||||
unsafe { (*SCB::ptr()).icsr.write(1 << 26) }
|
|
||||||
}
|
|
||||||
|
|
||||||
tq.queue.push(slot.write(bl, payload).tag(Task::a)).ok();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
//! Two tasks running at the *same* priority with access to the same resource
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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;
|
|
||||||
|
|
||||||
// ..
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
//! Minimal example with zero tasks
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
// IMPORTANT always include this feature gate
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
#![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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,8 +13,10 @@ version = "0.3.1"
|
||||||
failure = "0.1.1"
|
failure = "0.1.1"
|
||||||
proc-macro2 = "0.3.6"
|
proc-macro2 = "0.3.6"
|
||||||
quote = "0.5.1"
|
quote = "0.5.1"
|
||||||
rtfm-syntax = "0.3.0"
|
# rtfm-syntax = "0.3.0"
|
||||||
|
rtfm-syntax = { git = "https://github.com/japaric/rtfm-syntax", branch = "tq" }
|
||||||
syn = "0.13.1"
|
syn = "0.13.1"
|
||||||
|
either = "1.5.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
|
@ -1,77 +1,260 @@
|
||||||
use std::cmp;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use syn::Ident;
|
use either::Either;
|
||||||
|
use syn::{Ident, Type};
|
||||||
|
use syntax::check::App;
|
||||||
|
|
||||||
use check::App;
|
pub fn app(app: &App) -> Context {
|
||||||
|
let mut async = HashSet::new();
|
||||||
|
let mut async_after = HashSet::new();
|
||||||
|
let mut dispatchers = HashMap::new();
|
||||||
|
let mut triggers = HashMap::new();
|
||||||
|
let mut tq = TimerQueue::new();
|
||||||
|
let mut free_interrupts = app.free_interrupts.iter().cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
pub type Ownerships = HashMap<Ident, Ownership>;
|
async.extend(&app.init.async);
|
||||||
|
async_after.extend(&app.init.async_after);
|
||||||
|
|
||||||
pub enum Ownership {
|
// compute dispatchers
|
||||||
/// Owned or co-owned by tasks that run at the same priority
|
for (name, task) in &app.tasks {
|
||||||
Owned { priority: u8 },
|
match task.interrupt_or_capacity {
|
||||||
/// Shared by tasks that run at different priorities.
|
Either::Left(interrupt) => {
|
||||||
///
|
triggers.insert(interrupt, (*name, task.priority));
|
||||||
/// `ceiling` is the maximum value across all the task priorities
|
|
||||||
Shared { ceiling: u8 },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ownership {
|
|
||||||
pub fn ceiling(&self) -> u8 {
|
|
||||||
match *self {
|
|
||||||
Ownership::Owned { priority } => priority,
|
|
||||||
Ownership::Shared { ceiling } => ceiling,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_owned(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Ownership::Owned { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn app(app: &App) -> Ownerships {
|
|
||||||
let mut ownerships = HashMap::new();
|
|
||||||
|
|
||||||
for resource in &app.idle.resources {
|
|
||||||
ownerships.insert(resource.clone(), Ownership::Owned { priority: 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
for task in app.tasks.values() {
|
|
||||||
for resource in task.resources.iter() {
|
|
||||||
if let Some(ownership) = ownerships.get_mut(resource) {
|
|
||||||
match *ownership {
|
|
||||||
Ownership::Owned { priority } => {
|
|
||||||
if priority == task.priority {
|
|
||||||
*ownership = Ownership::Owned { priority };
|
|
||||||
} else {
|
|
||||||
*ownership = Ownership::Shared {
|
|
||||||
ceiling: cmp::max(priority, task.priority),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ownership::Shared { ceiling } => {
|
|
||||||
if task.priority > ceiling {
|
|
||||||
*ownership = Ownership::Shared {
|
|
||||||
ceiling: task.priority,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
Either::Right(capacity) => {
|
||||||
|
let dispatcher = dispatchers.entry(task.priority).or_insert(Dispatcher::new(
|
||||||
|
free_interrupts.pop().expect("not enough free interrupts"),
|
||||||
|
));
|
||||||
|
dispatcher.tasks.push(*name);
|
||||||
|
dispatcher.capacity += capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ownerships.insert(
|
for task in &task.async {
|
||||||
resource.clone(),
|
async.insert(*task);
|
||||||
Ownership::Owned {
|
}
|
||||||
priority: task.priority,
|
|
||||||
},
|
for task in &task.async_after {
|
||||||
);
|
async_after.insert(*task);
|
||||||
|
|
||||||
|
// Timer queue
|
||||||
|
if let Entry::Vacant(entry) = tq.tasks.entry(*task) {
|
||||||
|
tq.capacity += app.tasks[task].interrupt_or_capacity.right().unwrap();
|
||||||
|
entry.insert(app.tasks[task].priority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ownerships
|
// The SysTick exception runs at the highest dispatcher priority
|
||||||
|
let sys_tick = dispatchers.keys().cloned().max().unwrap_or(1);
|
||||||
|
|
||||||
|
// compute ceilings
|
||||||
|
let mut ceilings = Ceilings::new(sys_tick);
|
||||||
|
|
||||||
|
// the SysTick interrupt contends for the timer queue (this has been accounted for in the
|
||||||
|
// `Ceilings` constructor) and for the producer end of all the dispatcher queues (__#N::Q)
|
||||||
|
for dispatch_priority in dispatchers.keys() {
|
||||||
|
ceilings
|
||||||
|
.dispatch_queues
|
||||||
|
.insert(*dispatch_priority, sys_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resources
|
||||||
|
for (priority, resource) in app.idle.resources.iter().map(|res| (0, res)).chain(
|
||||||
|
app.tasks
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(name, task)| task.resources.iter().map(move |res| (task.priority, res))),
|
||||||
|
) {
|
||||||
|
let ceiling = ceilings
|
||||||
|
.resources
|
||||||
|
.entry(*resource)
|
||||||
|
.or_insert(Ceiling::Owned(priority));
|
||||||
|
if priority > (*ceiling).into() {
|
||||||
|
*ceiling = Ceiling::Shared(priority);
|
||||||
|
} else if priority < (*ceiling).into() && ceiling.is_owned() {
|
||||||
|
*ceiling = Ceiling::Shared((*ceiling).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// async
|
||||||
|
for (caller_priority, task) in app.tasks
|
||||||
|
.values()
|
||||||
|
.flat_map(|caller| caller.async.iter().map(move |task| (caller.priority, task)))
|
||||||
|
{
|
||||||
|
// async callers contend for the consumer end of the task slot queue (#task::SQ) and ...
|
||||||
|
let ceiling = ceilings.slot_queues.entry(*task).or_insert(caller_priority);
|
||||||
|
|
||||||
|
if caller_priority > *ceiling {
|
||||||
|
*ceiling = caller_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. for the producer end of the dispatcher queue (__#dispatch_priority::Q)
|
||||||
|
let dispatch_priority = app.tasks[task].priority;
|
||||||
|
let ceiling = ceilings
|
||||||
|
.dispatch_queues
|
||||||
|
.entry(dispatch_priority)
|
||||||
|
.or_insert(dispatch_priority);
|
||||||
|
|
||||||
|
if caller_priority > *ceiling {
|
||||||
|
*ceiling = caller_priority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// async_after
|
||||||
|
for (caller_priority, task) in app.tasks.values().flat_map(|caller| {
|
||||||
|
caller
|
||||||
|
.async_after
|
||||||
|
.iter()
|
||||||
|
.map(move |task| (caller.priority, task))
|
||||||
|
}) {
|
||||||
|
// async_after callers contend for the consumer end of the task slot queue (#task::SQ) and
|
||||||
|
// ...
|
||||||
|
let ceiling = ceilings.slot_queues.entry(*task).or_insert(caller_priority);
|
||||||
|
|
||||||
|
if caller_priority > *ceiling {
|
||||||
|
*ceiling = caller_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. for the timer queue
|
||||||
|
if caller_priority > ceilings.timer_queue {
|
||||||
|
ceilings.timer_queue = caller_priority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context {
|
||||||
|
async,
|
||||||
|
async_after,
|
||||||
|
ceilings,
|
||||||
|
dispatchers,
|
||||||
|
sys_tick,
|
||||||
|
triggers,
|
||||||
|
timer_queue: tq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
// set of `async` tasks
|
||||||
|
pub async: HashSet<Ident>,
|
||||||
|
// set of `async_after` tasks
|
||||||
|
pub async_after: HashSet<Ident>,
|
||||||
|
pub ceilings: Ceilings,
|
||||||
|
// Priority:u8 -> Dispatcher
|
||||||
|
pub dispatchers: HashMap<u8, Dispatcher>,
|
||||||
|
// Interrupt:Ident -> Task:Ident
|
||||||
|
pub triggers: HashMap<Ident, (Ident, u8)>,
|
||||||
|
pub timer_queue: TimerQueue,
|
||||||
|
// priority of the SysTick exception
|
||||||
|
pub sys_tick: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TimerQueue {
|
||||||
|
// Task:Ident -> Priority:u8
|
||||||
|
tasks: HashMap<Ident, u8>,
|
||||||
|
capacity: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimerQueue {
|
||||||
|
fn new() -> Self {
|
||||||
|
TimerQueue {
|
||||||
|
tasks: HashMap::new(),
|
||||||
|
capacity: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capacity(&self) -> u8 {
|
||||||
|
self.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tasks(&self) -> &HashMap<Ident, u8> {
|
||||||
|
&self.tasks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Dispatcher {
|
||||||
|
capacity: u8,
|
||||||
|
interrupt: Ident,
|
||||||
|
tasks: Vec<Ident>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatcher {
|
||||||
|
fn new(interrupt: Ident) -> Self {
|
||||||
|
Dispatcher {
|
||||||
|
capacity: 0,
|
||||||
|
interrupt,
|
||||||
|
tasks: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capacity(&self) -> u8 {
|
||||||
|
self.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt(&self) -> Ident {
|
||||||
|
self.interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tasks(&self) -> &[Ident] {
|
||||||
|
&self.tasks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Ceilings {
|
||||||
|
dispatch_queues: HashMap<u8, u8>,
|
||||||
|
resources: HashMap<Ident, Ceiling>,
|
||||||
|
slot_queues: HashMap<Ident, u8>,
|
||||||
|
timer_queue: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ceilings {
|
||||||
|
fn new(sys_tick_priority: u8) -> Self {
|
||||||
|
Ceilings {
|
||||||
|
dispatch_queues: HashMap::new(),
|
||||||
|
resources: HashMap::new(),
|
||||||
|
slot_queues: HashMap::new(),
|
||||||
|
timer_queue: sys_tick_priority,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_queues(&self) -> &HashMap<u8, u8> {
|
||||||
|
&self.dispatch_queues
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resources(&self) -> &HashMap<Ident, Ceiling> {
|
||||||
|
&self.resources
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slot_queues(&self) -> &HashMap<Ident, u8> {
|
||||||
|
&self.slot_queues
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timer_queue(&self) -> u8 {
|
||||||
|
self.timer_queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Ceiling {
|
||||||
|
Owned(u8),
|
||||||
|
Shared(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ceiling {
|
||||||
|
pub fn is_owned(&self) -> bool {
|
||||||
|
if let Ceiling::Owned(..) = *self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ceiling> for u8 {
|
||||||
|
fn from(ceiling: Ceiling) -> u8 {
|
||||||
|
match ceiling {
|
||||||
|
Ceiling::Owned(prio) => prio,
|
||||||
|
Ceiling::Shared(ceil) => ceil,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use syntax::check::App;
|
||||||
|
use syntax::Result;
|
||||||
|
|
||||||
use syn::{Ident, Path};
|
pub fn app(_app: &App) -> Result<()> {
|
||||||
use syntax::check::{self, Idents, Idle, Init, Statics};
|
// TODO ???
|
||||||
use syntax::{self, Result};
|
Ok(())
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
pub device: Path,
|
|
||||||
pub idle: Idle,
|
|
||||||
pub init: Init,
|
|
||||||
pub resources: Statics,
|
|
||||||
pub tasks: Tasks,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Tasks = HashMap<Ident, Task>;
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub enum Exception {
|
|
||||||
PENDSV,
|
|
||||||
SVCALL,
|
|
||||||
SYS_TICK,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Exception {
|
|
||||||
pub fn from(s: &str) -> Option<Self> {
|
|
||||||
Some(match s {
|
|
||||||
"PENDSV" => Exception::PENDSV,
|
|
||||||
"SVCALL" => Exception::SVCALL,
|
|
||||||
"SYS_TICK" => Exception::SYS_TICK,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nr(&self) -> usize {
|
|
||||||
match *self {
|
|
||||||
Exception::PENDSV => 14,
|
|
||||||
Exception::SVCALL => 11,
|
|
||||||
Exception::SYS_TICK => 15,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Kind {
|
|
||||||
Exception(Exception),
|
|
||||||
Interrupt { enabled: bool },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Task {
|
|
||||||
pub kind: Kind,
|
|
||||||
pub path: Path,
|
|
||||||
pub priority: u8,
|
|
||||||
pub resources: Idents,
|
|
||||||
}
|
|
||||||
|
|
||||||
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.as_ref(), 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 {
|
|
||||||
kind,
|
|
||||||
path: task.path,
|
|
||||||
priority: task.priority.unwrap_or(1),
|
|
||||||
resources: task.resources,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Procedural macros of the `cortex-m-rtfm` crate
|
|
||||||
// #![deny(warnings)]
|
// #![deny(warnings)]
|
||||||
|
#![allow(warnings)]
|
||||||
#![feature(proc_macro)]
|
#![feature(proc_macro)]
|
||||||
#![recursion_limit = "128"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
@ -10,6 +10,7 @@ extern crate proc_macro2;
|
||||||
extern crate syn;
|
extern crate syn;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
extern crate either;
|
||||||
extern crate rtfm_syntax as syntax;
|
extern crate rtfm_syntax as syntax;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
@ -19,154 +20,6 @@ mod analyze;
|
||||||
mod check;
|
mod check;
|
||||||
mod trans;
|
mod trans;
|
||||||
|
|
||||||
/// The `app!` macro, a macro used to specify the tasks and resources of a RTFM application.
|
|
||||||
///
|
|
||||||
/// The contents of this macro uses a `key: value` syntax. All the possible keys are shown below:
|
|
||||||
///
|
|
||||||
/// ``` text
|
|
||||||
/// app! {
|
|
||||||
/// device: ..,
|
|
||||||
///
|
|
||||||
/// resources: { .. },
|
|
||||||
///
|
|
||||||
/// init: { .. },
|
|
||||||
///
|
|
||||||
/// idle: { .. },
|
|
||||||
///
|
|
||||||
/// tasks: { .. },
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # `device`
|
|
||||||
///
|
|
||||||
/// The value of this key is a Rust path, like `foo::bar::baz`, that must point to a *device crate*,
|
|
||||||
/// a crate generated using `svd2rust`.
|
|
||||||
///
|
|
||||||
/// # `resources`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a list of `static` variables. These variables are the data
|
|
||||||
/// that can be safely accessed, modified and shared by tasks.
|
|
||||||
///
|
|
||||||
/// ``` text
|
|
||||||
/// resources: {
|
|
||||||
/// static A: bool = false;
|
|
||||||
/// 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
|
|
||||||
/// initialized; these runtime initialized resources are also known as *late resources*.
|
|
||||||
///
|
|
||||||
/// If this key is omitted its value defaults to an empty list.
|
|
||||||
///
|
|
||||||
/// # `init`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
|
|
||||||
///
|
|
||||||
/// ``` text
|
|
||||||
/// init: {
|
|
||||||
/// path: ..,
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## `init.path`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the
|
|
||||||
/// initialization function.
|
|
||||||
///
|
|
||||||
/// If the key is omitted its value defaults to `init`.
|
|
||||||
///
|
|
||||||
/// ## `init.resources`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a set of resources the `init` function *owns*. The resources
|
|
||||||
/// in this list must be a subset of the resources listed in the top `resources` key. Note that some
|
|
||||||
/// restrictions apply:
|
|
||||||
///
|
|
||||||
/// - The resources in this list can't be late resources.
|
|
||||||
/// - The resources that appear in this list can't appear in other list like `idle.resources` or
|
|
||||||
/// `tasks.$TASK.resources`
|
|
||||||
///
|
|
||||||
/// If this key is omitted its value is assumed to be an empty list.
|
|
||||||
///
|
|
||||||
/// # `idle`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a set of key values. All the possible keys are shown below:
|
|
||||||
///
|
|
||||||
/// ``` text
|
|
||||||
/// idle: {
|
|
||||||
/// path: ..,
|
|
||||||
/// resources: [..],
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## `idle.path`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the idle
|
|
||||||
/// loop function.
|
|
||||||
///
|
|
||||||
/// If the key is omitted its value defaults to `idle`.
|
|
||||||
///
|
|
||||||
/// ## `idle.resources`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a list of resources the `idle` loop has access to. The
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// # `tasks`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a list of tasks. Each task itself is a set of key value pair.
|
|
||||||
/// The full syntax is shown below:
|
|
||||||
///
|
|
||||||
/// ``` text
|
|
||||||
/// tasks: {
|
|
||||||
/// $TASK: {
|
|
||||||
/// enabled: ..,
|
|
||||||
/// path: ..,
|
|
||||||
/// priority: ..,
|
|
||||||
/// resources: [..],
|
|
||||||
/// },
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// If this key is omitted its value is assumed to be an empty list.
|
|
||||||
///
|
|
||||||
/// ## `tasks.$TASK`
|
|
||||||
///
|
|
||||||
/// The key must be either a Cortex-M exception or a device specific interrupt. `PENDSV`, `SVCALL`,
|
|
||||||
/// `SYS_TICK` are considered as exceptions. All other names are assumed to be interrupts.
|
|
||||||
///
|
|
||||||
/// ## `tasks.$TASK.enabled`
|
|
||||||
///
|
|
||||||
/// This key is optional for interrupts and forbidden for exceptions. Its value must be a boolean
|
|
||||||
/// 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`.
|
|
||||||
///
|
|
||||||
/// ## `tasks.$TASK.path`
|
|
||||||
///
|
|
||||||
/// The value of this key is a Rust path, like `foo::bar::baz`, that points to the handler of this
|
|
||||||
/// task.
|
|
||||||
///
|
|
||||||
/// ## `tasks.$TASK.priority`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is an integer with type `u8` that specifies the priority of this
|
|
||||||
/// 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`.
|
|
||||||
///
|
|
||||||
/// ## `tasks.$TASK.resources`
|
|
||||||
///
|
|
||||||
/// This key is optional. Its value is a list of resources this task has access to. The 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.
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn app(ts: TokenStream) -> TokenStream {
|
pub fn app(ts: TokenStream) -> TokenStream {
|
||||||
match run(ts) {
|
match run(ts) {
|
||||||
|
@ -177,10 +30,10 @@ pub fn app(ts: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
fn run(ts: TokenStream) -> Result<TokenStream> {
|
fn run(ts: TokenStream) -> Result<TokenStream> {
|
||||||
let app = App::parse(ts)?.check()?;
|
let app = App::parse(ts)?.check()?;
|
||||||
let app = check::app(app)?;
|
check::app(&app)?;
|
||||||
|
|
||||||
let ownerships = analyze::app(&app);
|
let ctxt = analyze::app(&app);
|
||||||
let tokens = trans::app(&app, &ownerships);
|
let tokens = trans::app(&ctxt, &app);
|
||||||
|
|
||||||
Ok(tokens.into())
|
Ok(tokens.into())
|
||||||
}
|
}
|
||||||
|
|
1311
macros/src/trans.rs
1311
macros/src/trans.rs
File diff suppressed because it is too large
Load diff
|
@ -1,49 +0,0 @@
|
||||||
//! Minimal example with zero tasks
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! // IMPORTANT always include this feature gate
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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,101 +0,0 @@
|
||||||
//! An application with one task
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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,63 +0,0 @@
|
||||||
//! Two tasks running at the *same* priority with access to the same resource
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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,72 +0,0 @@
|
||||||
//! Two tasks running at *different* priorities with access to the same resource
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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,130 +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)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use stm32f103xx::Interrupt;
|
|
||||||
//! use rtfm::{app, Resource, Threshold};
|
|
||||||
//!
|
|
||||||
//! 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 { mut LOW, 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,91 +0,0 @@
|
||||||
//! Demonstrates initialization of resources in `init`.
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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,36 +0,0 @@
|
||||||
//! Safe creation of `&'static mut` references
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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,78 +0,0 @@
|
||||||
//! Working with resources in a generic fashion
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![no_std]
|
|
||||||
//!
|
|
||||||
//! extern crate cortex_m_rtfm as rtfm;
|
|
||||||
//! extern crate stm32f103xx;
|
|
||||||
//!
|
|
||||||
//! use rtfm::{app, Resource, Threshold};
|
|
||||||
//! use stm32f103xx::{SPI1, GPIOA};
|
|
||||||
//!
|
|
||||||
//! 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,88 +0,0 @@
|
||||||
//! A showcase of the `app!` macro syntax
|
|
||||||
//!
|
|
||||||
//! ```
|
|
||||||
//! #![deny(unsafe_code)]
|
|
||||||
//! #![deny(warnings)]
|
|
||||||
//! #![feature(proc_macro)]
|
|
||||||
//! #![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;
|
|
60
src/instant.rs
Normal file
60
src/instant.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use core::cmp::Ordering;
|
||||||
|
use core::ops;
|
||||||
|
|
||||||
|
use cortex_m::peripheral::DWT;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Instant(u32);
|
||||||
|
|
||||||
|
impl Into<u32> for Instant {
|
||||||
|
fn into(self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instant {
|
||||||
|
pub unsafe fn new(timestamp: u32) -> Self {
|
||||||
|
Instant(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now() -> Self {
|
||||||
|
Instant(DWT::get_cycle_count())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Instant {}
|
||||||
|
|
||||||
|
impl Ord for Instant {
|
||||||
|
fn cmp(&self, rhs: &Self) -> Ordering {
|
||||||
|
(self.0 as i32).wrapping_sub(rhs.0 as i32).cmp(&0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Instant {
|
||||||
|
fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
self.0.eq(&rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Instant {
|
||||||
|
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Add<u32> for Instant {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: u32) -> Self {
|
||||||
|
Instant(self.0.wrapping_add(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Sub for Instant {
|
||||||
|
type Output = i32;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> i32 {
|
||||||
|
(self.0 as i32).wrapping_sub(rhs.0 as i32)
|
||||||
|
}
|
||||||
|
}
|
233
src/lib.rs
233
src/lib.rs
|
@ -1,177 +1,110 @@
|
||||||
//! 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
|
|
||||||
//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren,
|
|
||||||
//! 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/
|
|
||||||
//! [ltu]: https://www.ltu.se/?l=en
|
|
||||||
//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
|
|
||||||
//! [references]: ./index.html#references
|
|
||||||
//!
|
|
||||||
//! # Features
|
|
||||||
//!
|
|
||||||
//! - **Event triggered tasks** as the unit of concurrency.
|
|
||||||
//! - 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
|
|
||||||
//!
|
|
||||||
//! - Tasks must run to completion. That's it, tasks can't contain endless
|
|
||||||
//! loops. However, you can run an endless event loop in the `idle` *loop*.
|
|
||||||
//!
|
|
||||||
//! - Task priorities must remain constant at runtime.
|
|
||||||
//!
|
|
||||||
//! # Dependencies
|
|
||||||
//!
|
|
||||||
//! The application crate must depend on a device crate generated using
|
|
||||||
//! [`svd2rust`] v0.12.x and the "rt" feature of that crate must be enabled. The
|
|
||||||
//! 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)]
|
||||||
|
#![allow(warnings)]
|
||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
#![feature(proc_macro)]
|
#![feature(proc_macro)]
|
||||||
#![feature(untagged_unions)]
|
#![feature(untagged_unions)]
|
||||||
#![feature(unsize)]
|
#![feature(never_type)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate cortex_m;
|
extern crate cortex_m;
|
||||||
extern crate cortex_m_rtfm_macros;
|
extern crate cortex_m_rtfm_macros;
|
||||||
extern crate heapless;
|
extern crate heapless;
|
||||||
extern crate rtfm_core;
|
extern crate typenum;
|
||||||
extern crate untagged_option;
|
|
||||||
|
|
||||||
use core::{mem, u8};
|
mod instant;
|
||||||
|
mod node;
|
||||||
|
mod resource;
|
||||||
|
mod tq;
|
||||||
|
|
||||||
pub use cortex_m::asm::{bkpt, wfi};
|
use core::mem;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use cortex_m::interrupt;
|
||||||
|
use cortex_m::interrupt::Nr;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use cortex_m::peripheral::syst::SystClkSource;
|
||||||
|
#[cfg(any(has_fpu, target_arch = "x86_64"))]
|
||||||
|
use cortex_m::peripheral::FPU;
|
||||||
|
use cortex_m::peripheral::{Peripherals, CPUID, DCB, DWT, MPU, NVIC, SCB, SYST};
|
||||||
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
|
use cortex_m::peripheral::{CBP, FPB, ITM, TPIU};
|
||||||
pub use cortex_m_rtfm_macros::app;
|
pub use cortex_m_rtfm_macros::app;
|
||||||
pub use rtfm_core::{Resource, Threshold};
|
use heapless::ring_buffer::RingBuffer;
|
||||||
#[doc(hidden)]
|
pub use typenum::consts::*;
|
||||||
pub use untagged_option::UntaggedOption;
|
pub use typenum::{Max, Maximum, Unsigned};
|
||||||
|
|
||||||
use cortex_m::interrupt::{self, Nr};
|
pub use instant::Instant;
|
||||||
use cortex_m::peripheral::NVIC;
|
pub use node::Node;
|
||||||
#[cfg(not(armv6m))]
|
use node::{Slot, TaggedPayload};
|
||||||
use cortex_m::register::basepri;
|
pub use resource::{Resource, Threshold};
|
||||||
|
pub use tq::{dispatch, TimerQueue};
|
||||||
|
|
||||||
pub mod examples;
|
pub type PayloadQueue<T, N> = RingBuffer<TaggedPayload<T>, N, u8>;
|
||||||
#[doc(hidden)]
|
pub type SlotQueue<T, N> = RingBuffer<Slot<T>, N, u8>;
|
||||||
pub mod ll;
|
pub type Ceiling<R> = <R as Resource>::Ceiling;
|
||||||
|
|
||||||
/// Executes the closure `f` in a preemption free context
|
pub struct Core {
|
||||||
///
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
/// During the execution of the closure no task can preempt the current task.
|
pub CBP: CBP,
|
||||||
pub fn atomic<R, F>(t: &mut Threshold, f: F) -> R
|
pub CPUID: CPUID,
|
||||||
where
|
pub DCB: DCB,
|
||||||
F: FnOnce(&mut Threshold) -> R,
|
// pub DWT: DWT,
|
||||||
{
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
if t.value() == u8::MAX {
|
pub FPB: FPB,
|
||||||
f(t)
|
#[cfg(any(has_fpu, target_arch = "x86_64"))]
|
||||||
} else {
|
pub FPU: FPU,
|
||||||
interrupt::disable();
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
let r = f(&mut unsafe { Threshold::max() });
|
pub ITM: ITM,
|
||||||
unsafe { interrupt::enable() };
|
pub MPU: MPU,
|
||||||
r
|
pub SCB: SCB,
|
||||||
|
// pub SYST: SYST,
|
||||||
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
|
pub TPIU: TPIU,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Core {
|
||||||
|
pub unsafe fn steal() -> (Core, DWT, NVIC, SYST) {
|
||||||
|
let p = Peripherals::steal();
|
||||||
|
|
||||||
|
(
|
||||||
|
Core {
|
||||||
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
|
CBP: p.CBP,
|
||||||
|
CPUID: p.CPUID,
|
||||||
|
DCB: p.DCB,
|
||||||
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
|
FPB: p.FPB,
|
||||||
|
#[cfg(any(has_fpu, target_arch = "x86_64"))]
|
||||||
|
FPU: p.FPU,
|
||||||
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
|
ITM: p.ITM,
|
||||||
|
MPU: p.MPU,
|
||||||
|
SCB: p.SCB,
|
||||||
|
#[cfg(any(armv7m, target_arch = "x86_64"))]
|
||||||
|
TPIU: p.TPIU,
|
||||||
|
},
|
||||||
|
p.DWT,
|
||||||
|
p.NVIC,
|
||||||
|
p.SYST,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub unsafe fn claim<T, R, F>(
|
pub const unsafe fn uninitialized<T>() -> T {
|
||||||
data: T,
|
#[allow(unions_with_drop_fields)]
|
||||||
ceiling: u8,
|
union U<T> {
|
||||||
_nvic_prio_bits: u8,
|
some: T,
|
||||||
t: &mut Threshold,
|
none: (),
|
||||||
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))]
|
|
||||||
() => {
|
|
||||||
let max_priority = 1 << _nvic_prio_bits;
|
|
||||||
|
|
||||||
if ceiling == max_priority {
|
|
||||||
atomic(t, |t| f(data, t))
|
|
||||||
} else {
|
|
||||||
let old = basepri::read();
|
|
||||||
let hw = (max_priority - ceiling) << (8 - _nvic_prio_bits);
|
|
||||||
basepri::write(hw);
|
|
||||||
let ret = f(data, &mut Threshold::new(ceiling));
|
|
||||||
basepri::write(old);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f(data, t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
U { none: () }.some
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets an interrupt, that is a task, as pending
|
pub unsafe fn set_pending<I>(interrupt: I)
|
||||||
///
|
|
||||||
/// If the task priority is high enough the task will be serviced immediately,
|
|
||||||
/// otherwise it will be serviced at some point after the current task ends.
|
|
||||||
pub fn set_pending<I>(interrupt: I)
|
|
||||||
where
|
where
|
||||||
I: Nr,
|
I: Nr,
|
||||||
{
|
{
|
||||||
// NOTE(safe) atomic write
|
mem::transmute::<(), NVIC>(()).set_pending(interrupt)
|
||||||
let mut nvic: NVIC = unsafe { mem::transmute(()) };
|
|
||||||
nvic.set_pending(interrupt);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::marker::Unsize;
|
|
||||||
use core::ops;
|
|
||||||
use core::{mem, ptr};
|
use core::{mem, ptr};
|
||||||
|
|
||||||
use cortex_m::peripheral::{DWT, SYST};
|
use instant::Instant;
|
||||||
use heapless::binary_heap::{BinaryHeap, Min};
|
|
||||||
pub use heapless::ring_buffer::{Consumer, Producer, RingBuffer};
|
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Node<T>
|
pub struct Node<T>
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
baseline: Instant,
|
baseline: Instant,
|
||||||
next: Option<Slot<T>>,
|
|
||||||
payload: T,
|
payload: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +33,7 @@ impl<T> PartialOrd for Node<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct Slot<T>
|
pub struct Slot<T>
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
|
@ -45,10 +42,6 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Slot<T> {
|
impl<T> Slot<T> {
|
||||||
pub fn new(node: &'static mut Node<T>) -> Self {
|
|
||||||
Slot { node }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self, bl: Instant, data: T) -> Payload<T> {
|
pub fn write(self, bl: Instant, data: T) -> Payload<T> {
|
||||||
self.node.baseline = bl;
|
self.node.baseline = bl;
|
||||||
unsafe { ptr::write(&mut self.node.payload, data) }
|
unsafe { ptr::write(&mut self.node.payload, data) }
|
||||||
|
@ -56,31 +49,13 @@ impl<T> Slot<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FreeList<T>
|
impl<T> Into<Slot<T>> for &'static mut Node<T> {
|
||||||
where
|
fn into(self) -> Slot<T> {
|
||||||
T: 'static,
|
Slot { node: self }
|
||||||
{
|
|
||||||
head: Option<Slot<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FreeList<T> {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
FreeList { head: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Option<Slot<T>> {
|
|
||||||
self.head.take().map(|head| {
|
|
||||||
self.head = head.node.next.take();
|
|
||||||
head
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, slot: Slot<T>) {
|
|
||||||
slot.node.next = self.head.take();
|
|
||||||
self.head = Some(slot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct Payload<T>
|
pub struct Payload<T>
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
|
@ -105,6 +80,7 @@ impl<T> Payload<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct TaggedPayload<A>
|
pub struct TaggedPayload<A>
|
||||||
where
|
where
|
||||||
A: Copy,
|
A: Copy,
|
||||||
|
@ -172,80 +148,3 @@ where
|
||||||
Some(self.cmp(rhs))
|
Some(self.cmp(rhs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TimerQueue<T, A>
|
|
||||||
where
|
|
||||||
A: Unsize<[TaggedPayload<T>]>,
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
pub syst: SYST,
|
|
||||||
pub queue: BinaryHeap<TaggedPayload<T>, A, Min>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, A> TimerQueue<T, A>
|
|
||||||
where
|
|
||||||
A: Unsize<[TaggedPayload<T>]>,
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
pub const fn new(syst: SYST) -> Self {
|
|
||||||
TimerQueue {
|
|
||||||
syst,
|
|
||||||
queue: BinaryHeap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Instant(u32);
|
|
||||||
|
|
||||||
impl Instant {
|
|
||||||
pub fn now() -> Self {
|
|
||||||
Instant(DWT::get_cycle_count())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Instant {}
|
|
||||||
|
|
||||||
impl Ord for Instant {
|
|
||||||
fn cmp(&self, rhs: &Self) -> Ordering {
|
|
||||||
(self.0 as i32).wrapping_sub(rhs.0 as i32).cmp(&0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Instant {
|
|
||||||
fn eq(&self, rhs: &Self) -> bool {
|
|
||||||
self.0.eq(&rhs.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Instant {
|
|
||||||
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(rhs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Add<u32> for Instant {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(self, rhs: u32) -> Self {
|
|
||||||
Instant(self.0.wrapping_add(rhs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Sub for Instant {
|
|
||||||
type Output = i32;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> i32 {
|
|
||||||
(self.0 as i32).wrapping_sub(rhs.0 as i32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const unsafe fn uninitialized<T>() -> T {
|
|
||||||
#[allow(unions_with_drop_fields)]
|
|
||||||
union U<T> {
|
|
||||||
some: T,
|
|
||||||
none: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
U { none: () }.some
|
|
||||||
}
|
|
88
src/resource.rs
Normal file
88
src/resource.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
#[cfg(not(armv6m))]
|
||||||
|
use cortex_m::register::basepri;
|
||||||
|
|
||||||
|
use typenum::{Max, Maximum, Unsigned};
|
||||||
|
|
||||||
|
pub struct Threshold<N>
|
||||||
|
where
|
||||||
|
N: Unsigned,
|
||||||
|
{
|
||||||
|
_not_send_or_sync: PhantomData<*const ()>,
|
||||||
|
_n: PhantomData<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> Threshold<N>
|
||||||
|
where
|
||||||
|
N: Unsigned,
|
||||||
|
{
|
||||||
|
pub unsafe fn new() -> Self {
|
||||||
|
Threshold {
|
||||||
|
_not_send_or_sync: PhantomData,
|
||||||
|
_n: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe trait Resource {
|
||||||
|
#[doc(hidden)]
|
||||||
|
const NVIC_PRIO_BITS: u8;
|
||||||
|
type Ceiling: Unsigned;
|
||||||
|
type Data: 'static + Send;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
unsafe fn get() -> &'static mut Self::Data;
|
||||||
|
|
||||||
|
fn borrow<'cs>(&'cs self, _t: &'cs Threshold<Self::Ceiling>) -> &'cs Self::Data {
|
||||||
|
unsafe { Self::get() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn borrow_mut<'cs>(&'cs mut self, _t: &'cs Threshold<Self::Ceiling>) -> &'cs mut Self::Data {
|
||||||
|
unsafe { Self::get() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claim<'cs, R, F, P>(&self, _t: &mut Threshold<P>, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&Self::Data, &mut Threshold<Maximum<P, Self::Ceiling>>) -> R,
|
||||||
|
P: Max<Self::Ceiling> + Unsigned,
|
||||||
|
Maximum<P, Self::Ceiling>: Unsigned,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
if P::to_u8() >= Self::Ceiling::to_u8() {
|
||||||
|
f(Self::get(), &mut Threshold::new())
|
||||||
|
} else {
|
||||||
|
let max = 1 << Self::NVIC_PRIO_BITS;
|
||||||
|
let new = (max - Self::Ceiling::to_u8()) << (8 - Self::NVIC_PRIO_BITS);
|
||||||
|
|
||||||
|
let old = basepri::read();
|
||||||
|
basepri::write(new);
|
||||||
|
let r = f(Self::get(), &mut Threshold::new());
|
||||||
|
basepri::write(old);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claim_mut<'cs, R, F, P>(&mut self, _t: &mut Threshold<P>, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self::Data, &mut Threshold<Maximum<P, Self::Ceiling>>) -> R,
|
||||||
|
P: Max<Self::Ceiling> + Unsigned,
|
||||||
|
Maximum<P, Self::Ceiling>: Unsigned,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
if P::to_u8() >= Self::Ceiling::to_u8() {
|
||||||
|
f(Self::get(), &mut Threshold::new())
|
||||||
|
} else {
|
||||||
|
let max = 1 << Self::NVIC_PRIO_BITS;
|
||||||
|
let new = (max - Self::Ceiling::to_u8()) << (8 - Self::NVIC_PRIO_BITS);
|
||||||
|
|
||||||
|
let old = basepri::read();
|
||||||
|
basepri::write(new);
|
||||||
|
let r = f(Self::get(), &mut Threshold::new());
|
||||||
|
basepri::write(old);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
src/tq.rs
Normal file
109
src/tq.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use core::cmp;
|
||||||
|
|
||||||
|
use cortex_m::peripheral::{SCB, SYST};
|
||||||
|
use heapless::binary_heap::{BinaryHeap, Min};
|
||||||
|
use heapless::ArrayLength;
|
||||||
|
use typenum::{Max, Maximum, Unsigned};
|
||||||
|
|
||||||
|
use instant::Instant;
|
||||||
|
use node::{Slot, TaggedPayload};
|
||||||
|
use resource::{Resource, Threshold};
|
||||||
|
|
||||||
|
enum State<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
Payload(TaggedPayload<T>),
|
||||||
|
Baseline(Instant),
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct TimerQueue<T, N>
|
||||||
|
where
|
||||||
|
N: ArrayLength<TaggedPayload<T>>,
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub syst: SYST,
|
||||||
|
pub queue: BinaryHeap<TaggedPayload<T>, N, Min>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, N> TimerQueue<T, N>
|
||||||
|
where
|
||||||
|
N: ArrayLength<TaggedPayload<T>>,
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
pub const fn new(syst: SYST) -> Self {
|
||||||
|
TimerQueue {
|
||||||
|
syst,
|
||||||
|
queue: BinaryHeap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn enqueue(&mut self, bl: Instant, tp: TaggedPayload<T>) {
|
||||||
|
if self.queue
|
||||||
|
.peek()
|
||||||
|
.map(|head| bl < head.baseline())
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
|
self.syst.enable_interrupt();
|
||||||
|
// set SysTick pending
|
||||||
|
unsafe { (*SCB::ptr()).icsr.write(1 << 26) }
|
||||||
|
}
|
||||||
|
|
||||||
|
self.queue.push_unchecked(tp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch<T, TQ, N, F, P>(t: &mut Threshold<P>, tq: &mut TQ, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut Threshold<P>, TaggedPayload<T>),
|
||||||
|
Maximum<P, TQ::Ceiling>: Unsigned,
|
||||||
|
N: 'static + ArrayLength<TaggedPayload<T>>,
|
||||||
|
P: Unsigned + Max<TQ::Ceiling>,
|
||||||
|
T: 'static + Copy + Send,
|
||||||
|
TQ: Resource<Data = TimerQueue<T, N>>,
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
let state = tq.claim_mut(t, |tq, _| {
|
||||||
|
if let Some(bl) = tq.queue.peek().map(|p| p.baseline()) {
|
||||||
|
if Instant::now() >= bl {
|
||||||
|
// message ready
|
||||||
|
State::Payload(unsafe { tq.queue.pop_unchecked() })
|
||||||
|
} else {
|
||||||
|
// set a new timeout
|
||||||
|
State::Baseline(bl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// empty queue
|
||||||
|
tq.syst.disable_interrupt();
|
||||||
|
State::Done
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match state {
|
||||||
|
State::Payload(p) => f(t, p),
|
||||||
|
State::Baseline(bl) => {
|
||||||
|
const MAX: u32 = 0x00ffffff;
|
||||||
|
|
||||||
|
let diff = bl - Instant::now();
|
||||||
|
|
||||||
|
if diff < 0 {
|
||||||
|
// message became ready
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
tq.claim_mut(t, |tq, _| {
|
||||||
|
tq.syst.set_reload(cmp::min(MAX, diff as u32));
|
||||||
|
// start counting from the new reload
|
||||||
|
tq.syst.clear_current();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Done => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue