aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config7
-rw-r--r--Cargo.toml14
-rw-r--r--Xargo.toml5
-rw-r--r--examples/full-syntax.rs81
-rw-r--r--examples/generics.rs69
-rw-r--r--examples/nested.rs125
-rw-r--r--examples/one-task.rs91
-rw-r--r--examples/preemption.rs70
-rw-r--r--examples/two-tasks.rs75
-rw-r--r--examples/zero-tasks.rs49
-rw-r--r--gen-examples.sh55
-rw-r--r--macros/src/trans.rs121
-rw-r--r--memory.x6
-rw-r--r--src/examples/_0_zero_tasks.rs53
-rw-r--r--src/examples/_1_one_task.rs95
-rw-r--r--src/examples/_2_two_tasks.rs79
-rw-r--r--src/examples/_3_preemption.rs74
-rw-r--r--src/examples/_4_nested.rs129
-rw-r--r--src/examples/_5_generics.rs73
-rw-r--r--src/examples/_6_full_syntax.rs85
-rw-r--r--src/examples/mod.rs9
-rw-r--r--src/lib.rs288
22 files changed, 1482 insertions, 171 deletions
diff --git a/.cargo/config b/.cargo/config
new file mode 100644
index 00000000..1642997e
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1,7 @@
+[target.thumbv7m-none-eabi]
+runner = 'arm-none-eabi-gdb'
+rustflags = [
+ "-C", "link-arg=-Tlink.x",
+ "-C", "linker=arm-none-eabi-ld",
+ "-Z", "linker-flavor=ld",
+]
diff --git a/Cargo.toml b/Cargo.toml
index d36b81e9..e558f5fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,16 @@ static-ref = "0.2.0"
[dependencies.cortex-m-rtfm-macros]
path = "macros"
-[dev-dependencies]
+[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
compiletest_rs = "0.2.8"
-stm32f103xx = "0.7.1"
+
+[dev-dependencies.cortex-m-rt]
+features = ["abort-on-panic"]
+version = "0.3.3"
+
+[dev-dependencies.stm32f103xx]
+features = ["rt"]
+version = "0.7.1"
+
+[profile.release]
+lto = true \ No newline at end of file
diff --git a/Xargo.toml b/Xargo.toml
new file mode 100644
index 00000000..bd7ffe03
--- /dev/null
+++ b/Xargo.toml
@@ -0,0 +1,5 @@
+[dependencies.core]
+stage = 0
+
+[dependencies.compiler_builtins]
+stage = 1 \ No newline at end of file
diff --git a/examples/full-syntax.rs b/examples/full-syntax.rs
new file mode 100644
index 00000000..d1466781
--- /dev/null
+++ b/examples/full-syntax.rs
@@ -0,0 +1,81 @@
+//! A showcase of the `app!` macro syntax
+
+#![deny(unsafe_code)]
+#![feature(const_fn)]
+#![feature(proc_macro)]
+#![no_std]
+
+#[macro_use(task)]
+extern crate cortex_m_rtfm as rtfm;
+extern crate stm32f103xx;
+
+use rtfm::{app, Resource, Threshold};
+
+app! {
+ device: stm32f103xx,
+
+ resources: {
+ static CO_OWNED: u32 = 0;
+ static OWNED: bool = false;
+ static SHARED: bool = false;
+ },
+
+ init: {
+ path: init_, // this is a path to the "init" function
+ },
+
+ idle: {
+ locals: {
+ static COUNTER: u32 = 0;
+ },
+ path: idle_, // this is a path to the "idle" function
+ resources: [OWNED, SHARED],
+ },
+
+ tasks: {
+ SYS_TICK: {
+ priority: 1,
+ resources: [CO_OWNED, SHARED],
+ },
+
+ TIM2: {
+ enabled: true,
+ priority: 1,
+ resources: [CO_OWNED],
+ },
+ },
+}
+
+fn init_(_p: init::Peripherals, _r: init::Resources) {}
+
+fn idle_(t: &mut Threshold, l: &mut idle::Locals, mut r: idle::Resources) -> ! {
+ loop {
+ *l.COUNTER += 1;
+
+ **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);
+ }
+ }
+}
+
+task!(SYS_TICK, sys_tick, Local {
+ static STATE: bool = true;
+});
+
+fn sys_tick(_t: &mut Threshold, l: &mut Local, r: SYS_TICK::Resources) {
+ *l.STATE = !*l.STATE;
+
+ **r.CO_OWNED += 1;
+}
+
+task!(TIM2, tim2);
+
+fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
+ **r.CO_OWNED += 1;
+}
diff --git a/examples/generics.rs b/examples/generics.rs
new file mode 100644
index 00000000..335d159b
--- /dev/null
+++ b/examples/generics.rs
@@ -0,0 +1,69 @@
+//! Working with resources in a generic fashion
+
+#![deny(unsafe_code)]
+#![feature(proc_macro)]
+#![no_std]
+
+#[macro_use(task)]
+extern crate cortex_m_rtfm as rtfm;
+extern crate stm32f103xx;
+
+use rtfm::{app, Resource, Threshold};
+use stm32f103xx::{SPI1, GPIOA};
+
+app! {
+ device: stm32f103xx,
+
+ tasks: {
+ EXTI0: {
+ enabled: true,
+ priority: 1,
+ resources: [GPIOA, SPI1],
+ },
+
+ EXTI1: {
+ enabled: true,
+ priority: 2,
+ resources: [GPIOA, SPI1],
+ },
+ },
+}
+
+fn init(_p: init::Peripherals) {}
+
+fn idle() -> ! {
+ loop {
+ rtfm::wfi();
+ }
+}
+
+// a generic function to use resources in any task (regardless of its priority)
+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
+ });
+}
+
+task!(EXTI0, exti0);
+
+// this task needs critical sections to access the resources
+fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
+ work(t, &r.GPIOA, &r.SPI1);
+}
+
+task!(EXTI1, exti1);
+
+// this task has direct access to the resources
+fn exti1(t: &mut Threshold, r: EXTI1::Resources) {
+ work(t, r.GPIOA, r.SPI1);
+}
diff --git a/examples/nested.rs b/examples/nested.rs
new file mode 100644
index 00000000..d307634c
--- /dev/null
+++ b/examples/nested.rs
@@ -0,0 +1,125 @@
+//! 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)]
+#![feature(const_fn)]
+#![feature(proc_macro)]
+#![no_std]
+
+#[macro_use(task)]
+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: {
+ enabled: true,
+ priority: 1,
+ resources: [LOW, HIGH],
+ },
+
+ EXTI1: {
+ enabled: true,
+ priority: 2,
+ resources: [LOW],
+ },
+
+ EXTI2: {
+ enabled: true,
+ priority: 3,
+ resources: [HIGH],
+ },
+ },
+}
+
+fn init(_p: init::Peripherals, _r: init::Resources) {}
+
+fn idle() -> ! {
+ // 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();
+ }
+}
+
+task!(EXTI0, exti0);
+
+fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
+ // because this task has a priority of 1 the preemption threshold is also 1
+
+ // A
+ 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
+ r.LOW.claim_mut(t, |_low, t| {
+ // this claim increases the preemption threshold to 2
+ // just high enough to not race with task `exti1` for access to the
+ // `LOW` resource
+
+ // C
+ 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
+
+ // E
+ rtfm::bkpt();
+
+ // claims can be nested
+ r.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);
+
+ // F
+ rtfm::bkpt();
+ });
+
+ // upon leaving the critical section the preemption threshold drops to 2
+ // and `exti2` immediately preempts this task
+ // ~> exti2
+ });
+
+ // once again the preemption threshold drops to 1
+ // now the pending `exti1` can preempt this task
+ // ~> exti1
+}
+
+task!(EXTI1, exti1);
+
+fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
+ // B, H
+ rtfm::bkpt();
+}
+
+task!(EXTI2, exti2);
+
+fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
+ // D, G
+ rtfm::bkpt();
+}
diff --git a/examples/one-task.rs b/examples/one-task.rs
new file mode 100644
index 00000000..8cfe089c
--- /dev/null
+++ b/examples/one-task.rs
@@ -0,0 +1,91 @@
+//! An application with one task
+
+#![deny(unsafe_code)]
+#![feature(const_fn)]
+#![feature(proc_macro)]
+#![no_std]
+
+extern crate cortex_m;
+#[macro_use(task)]
+extern crate cortex_m_rtfm as rtfm;
+extern crate stm32f103xx;
+
+use cortex_m::peripheral::SystClkSource;
+use rtfm::{app, Threshold};
+
+app! {
+ device: stm32f103xx,
+
+ // 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: {
+ // This is the priority of the task.
+ // 1 is the lowest priority a task can have.
+ // The maximum priority is determined by the number of priority bits
+ // the device has. This device has 4 priority bits so 16 is the
+ // maximum value.
+ priority: 1,
+
+ // These are the *resources* associated with this task
+ //
+ // The peripherals that the task needs can be listed here
+ resources: [GPIOC],
+ },
+ }
+}
+
+fn init(p: init::Peripherals) {
+ // power on GPIOC
+ p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
+
+ // configure PC13 as output
+ p.GPIOC.bsrr.write(|w| w.bs13().set());
+ p.GPIOC
+ .crh
+ .modify(|_, w| w.mode13().output().cnf13().push());
+
+ // configure the system timer to generate one interrupt every second
+ p.SYST.set_clock_source(SystClkSource::Core);
+ p.SYST.set_reload(8_000_000); // 1s
+ p.SYST.enable_interrupt();
+ p.SYST.enable_counter();
+}
+
+fn idle() -> ! {
+ loop {
+ rtfm::wfi();
+ }
+}
+
+// This binds the `sys_tick` handler to the `SYS_TICK` task
+//
+// This particular handler has local state associated to it. The value of the
+// `STATE` variable will be preserved across invocations of this handler
+task!(SYS_TICK, sys_tick, Locals {
+ static STATE: bool = false;
+});
+
+// This is the task handler of the SYS_TICK exception
+//
+// `t` is the preemption threshold token. We won't use it this time.
+// `l` is the data local to this task. The type here must match the one declared
+// in `task!`.
+// `r` is the resources this task has access to. `SYS_TICK::Resources` has one
+// field per resource declared in `app!`.
+fn sys_tick(_t: &mut Threshold, l: &mut Locals, r: SYS_TICK::Resources) {
+ // toggle state
+ *l.STATE = !*l.STATE;
+
+ if *l.STATE {
+ // set the pin PC13 high
+ r.GPIOC.bsrr.write(|w| w.bs13().set());
+ } else {
+ // set the pin PC13 low
+ r.GPIOC.bsrr.write(|w| w.br13().reset());
+ }
+}
diff --git a/examples/preemption.rs b/examples/preemption.rs
new file mode 100644
index 00000000..2ca6f951
--- /dev/null
+++ b/examples/preemption.rs
@@ -0,0 +1,70 @@
+//! Two tasks running at different priorities with access to the same resource
+
+#![deny(unsafe_code)]
+#![feature(const_fn)]
+#![feature(proc_macro)]
+#![no_std]
+
+#[macro_use(task)]
+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 task `SYS_TICK` has higher priority than `TIM2`
+ SYS_TICK: {
+ priority: 2,
+ resources: [COUNTER],
+ },
+
+ TIM2: {
+ enabled: true,
+ priority: 1,
+ resources: [COUNTER],
+ },
+ },
+}
+
+fn init(_p: init::Peripherals, _r: init::Resources) {
+ // ..
+}
+
+fn idle() -> ! {
+ loop {
+ rtfm::wfi();
+ }
+}
+
+task!(SYS_TICK, sys_tick);
+
+fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
+ // ..
+
+ // this task can't be preempted by `tim2` so it has direct access to the
+ // resource data
+ **r.COUNTER += 1;
+
+ // ..
+}
+
+task!(TIM2, tim2);
+
+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 data corruption or data loss
+ r.COUNTER.claim_mut(t, |counter, _t| { **counter += 1; });
+
+ // ..
+}
diff --git a/examples/two-tasks.rs b/examples/two-tasks.rs
new file mode 100644
index 00000000..eb74fa8f
--- /dev/null
+++ b/examples/two-tasks.rs
@@ -0,0 +1,75 @@
+//! Two tasks running at the same priority with access to the same resource
+
+#![deny(unsafe_code)]
+#![feature(const_fn)]
+#![feature(proc_macro)]
+#![no_std]
+
+#[macro_use(task)]
+extern crate cortex_m_rtfm as rtfm;
+extern crate stm32f103xx;
+
+use rtfm::{app, Threshold};
+
+app! {
+ device: stm32f103xx,
+
+ // Resources that are plain data, not peripherals
+ resources: {
+ // Declaration of resources looks like the declaration of `static`
+ // variables
+ static COUNTER: u64 = 0;
+ },
+
+ tasks: {
+ SYS_TICK: {
+ priority: 1,
+ // Both this task and TIM2 have access to the `COUNTER` resource
+ resources: [COUNTER],
+ },
+
+ // An interrupt as a task
+ TIM2: {
+ // For interrupts the `enabled` field must be specified. It
+ // indicates if the interrupt will be enabled or disabled once
+ // `idle` starts
+ enabled: true,
+ priority: 1,
+ resources: [COUNTER],
+ },
+ },
+}
+
+// when data resources are declared in the top `resources` field, `init` will
+// have full access to them
+fn init(_p: init::Peripherals, _r: init::Resources) {
+ // ..
+}
+
+fn idle() -> ! {
+ loop {
+ rtfm::wfi();
+ }
+}
+
+task!(SYS_TICK, sys_tick);
+
+// 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, r: SYS_TICK::Resources) {
+ // ..
+
+ **r.COUNTER += 1;
+
+ // ..
+}
+
+task!(TIM2, tim2);
+
+fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
+ // ..
+
+ **r.COUNTER += 1;
+
+ // ..
+}
diff --git a/examples/zero-tasks.rs b/examples/zero-tasks.rs
new file mode 100644
index 00000000..13201677
--- /dev/null
+++ b/examples/zero-tasks.rs
@@ -0,0 +1,49 @@
+//! Minimal example with zero tasks
+
+#![deny(unsafe_code)]
+#![feature(proc_macro)] // IMPORTANT always include this feature gate
+#![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 a 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.GPIOA;
+ p.RCC;
+ // ..
+
+ // You'll hit this breakpoint first
+ rtfm::bkpt();
+}
+
+// The idle loop.
+//
+// This runs afterwards 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() -> ! {
+ // And then this breakpoint
+ rtfm::bkpt();
+
+ loop {
+ // This puts the processor to sleep until there's a task to service
+ rtfm::wfi();
+ }
+}
diff --git a/gen-examples.sh b/gen-examples.sh
new file mode 100644
index 00000000..d053c95b
--- /dev/null
+++ b/gen-examples.sh
@@ -0,0 +1,55 @@
+# Converts the examples in the `examples` directory into documentation in the
+# `examples` module (`src/examples/*.rs`)
+
+set -ex
+
+main() {
+ local examples=(
+ zero-tasks
+ one-task
+ two-tasks
+ preemption
+ nested
+ generics
+ full-syntax
+ )
+
+ rm -rf src/examples
+
+ mkdir src/examples
+
+ cat >src/examples/mod.rs <<'EOF'
+//! Examples
+// Auto-generated. Do not modify.
+EOF
+
+ local i=0 out=
+ for ex in ${examples[@]}; do
+ name=_${i}_${ex//-/_}
+ out=src/examples/${name}.rs
+
+ echo "pub mod $name;" >> src/examples/mod.rs
+
+ grep '//!' examples/$ex.rs > $out
+ echo '//!' >> $out
+ echo '//! ```' >> $out
+ grep -v '//!' examples/$ex.rs | (
+ IFS=''
+
+ while read line; do
+ echo "//! $line" >> $out;
+ done
+ )
+ echo '//! ```' >> $out
+ echo '// Auto-generated. Do not modify.' >> $out
+
+
+ chmod -x $out
+
+ i=$(( i + 1 ))
+ done
+
+ chmod -x src/examples/mod.rs
+}
+
+main
diff --git a/macros/src/trans.rs b/macros/src/trans.rs
index 5262bc41..b5e624d0 100644
--- a/macros/src/trans.rs
+++ b/macros/src/trans.rs
@@ -64,15 +64,16 @@ fn idle(
let ty = &resource.ty;
lfields.push(quote! {
- pub #name: #ty,
+ pub #name: #krate::Static<#ty>,
});
lexprs.push(quote! {
- #name: #expr,
+ #name: unsafe { #krate::Static::new(#expr) },
});
}
mod_items.push(quote! {
+ #[allow(non_snake_case)]
pub struct Locals {
#(#lfields)*
}
@@ -114,19 +115,24 @@ fn idle(
let ty = &resource.ty;
rfields.push(quote! {
- pub #name: &'static mut #ty,
+ pub #name: &'static mut ::#krate::Static<#ty>,
});
rexprs.push(quote! {
- #name: &mut *#super_::#name.get(),
+ #name: #krate::Static::ref_mut(
+ &mut *#super_::#name.get(),
+ ),
});
} else {
rfields.push(quote! {
- pub #name: &'static mut ::#device::#name,
+ pub #name:
+ &'static mut ::#krate::Static<::#device::#name>,
});
rexprs.push(quote! {
- #name: &mut *::#device::#name.get(),
+ #name: ::krate::Static::ref_mut(
+ &mut *::#device::#name.get(),
+ ),
});
}
} else {
@@ -329,8 +335,8 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let ty = &resource.ty;
root.push(quote! {
- static #name: #krate::Resource<#ty> =
- #krate::Resource::new(#expr);
+ static #name: #krate::Cell<#ty> =
+ #krate::Cell::new(#expr);
});
} else {
// Peripheral
@@ -343,26 +349,30 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let ty = &resource.ty;
root.push(quote! {
- static #name: #krate::Resource<#ty> =
- #krate::Resource::new(#expr);
+ static #name: #krate::Cell<#ty> =
+ #krate::Cell::new(#expr);
});
impl_items.push(quote! {
- pub fn borrow<'cs>(
+ type Data = #ty;
+
+ fn borrow<'cs>(
&'cs self,
- cs: &'cs #krate::CriticalSection,
+ _cs: &'cs #krate::CriticalSection,
) -> &'cs #krate::Static<#ty> {
- unsafe { #name.borrow(cs) }
+ unsafe { #krate::Static::ref_(&*#name.get()) }
}
- pub fn borrow_mut<'cs>(
+ fn borrow_mut<'cs>(
&'cs mut self,
- cs: &'cs #krate::CriticalSection,
+ _cs: &'cs #krate::CriticalSection,
) -> &'cs mut #krate::Static<#ty> {
- unsafe { #name.borrow_mut(cs) }
+ unsafe {
+ #krate::Static::ref_mut(&mut *#name.get())
+ }
}
- pub fn claim<R, F>(
+ fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
@@ -373,16 +383,18 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
&mut #krate::Threshold) -> R
{
unsafe {
- #name.claim(
+ #krate::claim(
+ #name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
+ |data| #krate::Static::ref_(&*data),
)
}
}
- pub fn claim_mut<R, F>(
+ fn claim_mut<R, F>(
&mut self,
t: &mut #krate::Threshold,
f: F,
@@ -393,45 +405,77 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
&mut #krate::Threshold) -> R
{
unsafe {
- #name.claim_mut(
+ #krate::claim(
+ #name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
+ |data| #krate::Static::ref_mut(&mut *data),
)
}
}
});
} else {
- root.push(quote! {
- static #name: #krate::Peripheral<#device::#name> =
- #krate::Peripheral::new(#device::#name);
- });
-
impl_items.push(quote! {
- pub fn borrow<'cs>(
+ type Data = #device::#name;
+
+ fn borrow<'cs>(
&'cs self,
- cs: &'cs #krate::CriticalSection,
- ) -> &'cs #device::#name {
- unsafe { #name.borrow(cs) }
+ _cs: &'cs #krate::CriticalSection,
+ ) -> &'cs #krate::Static<#name> {
+ unsafe { #krate::Static::ref_(&*#name.get()) }
}
- pub fn claim<R, F>(
+ fn borrow_mut<'cs>(
+ &'cs mut self,
+ _cs: &'cs #krate::CriticalSection,
+ ) -> &'cs mut #krate::Static<#name> {
+ unsafe {
+ #krate::Static::ref_mut(&mut *#name.get())
+ }
+ }
+
+ fn claim<R, F>(
&self,
t: &mut #krate::Threshold,
f: F,
) -> R
where
F: FnOnce(
- &#device::#name,
+ &#krate::Static<#name>,
&mut #krate::Threshold) -> R
{
unsafe {
- #name.claim(
+ #krate::claim(
+ #device::#name.get(),
#ceiling,
#device::NVIC_PRIO_BITS,
t,
f,
+ |data| #krate::Static::ref_(&*data),
+ )
+ }
+ }
+
+ fn claim_mut<R, F>(
+ &mut self,
+ t: &mut #krate::Threshold,
+ f: F,
+ ) -> R
+ where
+ F: FnOnce(
+ &mut #krate::Static<#name>,
+ &mut #krate::Threshold) -> R
+ {
+ unsafe {
+ #krate::claim(
+ #device::#name.get(),
+ #ceiling,
+ #device::NVIC_PRIO_BITS,
+ t,
+ f,
+ |data| #krate::Static::ref_mut(&mut *data),
)
}
}
@@ -439,9 +483,8 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
}
impls.push(quote! {
- #[allow(dead_code)]
#[allow(unsafe_code)]
- impl _resource::#name {
+ impl #krate::Resource for _resource::#name {
#(#impl_items)*
}
});
@@ -512,11 +555,14 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
});
} else {
fields.push(quote! {
- pub #name: &'a mut ::#device::#name,
+ pub #name:
+ &'a mut ::#krate::Static<::#device::#name>,
});
exprs.push(quote! {
- #name: &mut *::#device::#name.get(),
+ #name: ::#krate::Static::ref_mut(
+ &mut *::#device::#name.get(),
+ ),
});
}
}
@@ -558,12 +604,13 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
let priority = task.priority;
root.push(quote!{
- #[allow(dead_code)]
#[allow(non_snake_case)]
#[allow(unsafe_code)]
mod #name {
#[deny(dead_code)]
pub const #name: u8 = #priority;
+
+ #[allow(dead_code)]
#[deny(const_err)]
const CHECK_PRIORITY: (u8, u8) = (
#priority - 1,
diff --git a/memory.x b/memory.x
new file mode 100644
index 00000000..534d4786
--- /dev/null
+++ b/memory.x
@@ -0,0 +1,6 @@
+/* STM32F103C8V6 */
+MEMORY
+{
+ FLASH : ORIGIN = 0x08000000, LENGTH = 64K
+ RAM : ORIGIN = 0x20000000, LENGTH = 20K
+}
diff --git a/src/examples/_0_zero_tasks.rs b/src/examples/_0_zero_tasks.rs
new file mode 100644
index 00000000..15231818
--- /dev/null
+++ b/src/examples/_0_zero_tasks.rs
@@ -0,0 +1,53 @@
+//! Minimal example with zero tasks
+//!
+//! ```
+//!
+//! #![deny(unsafe_code)]
+//! #![feature(proc_macro)] // IMPORTANT always include this feature gate
+//! #![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 a 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.GPIOA;
+//! p.RCC;
+//! // ..
+//!
+//! // You'll hit this breakpoint first
+//! rtfm::bkpt();
+//! }
+//!
+//! // The idle loop.
+//! //
+//! // This runs afterwards 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() -> ! {
+//! // And then this breakpoint
+//! rtfm::bkpt();
+//!
+//! loop {
+//! // This puts the processor to sleep until there's a task to service
+//! rtfm::wfi();
+//! }
+//! }
+//! ```
+// Auto-generated. Do not modify.
diff --git a/src/examples/_1_one_task.rs b/src/examples/_1_one_task.rs
new file mode 100644
index 00000000..33e8bf7f
--- /dev/null
+++ b/src/examples/_1_one_task.rs
@@ -0,0 +1,95 @@
+//! An application with one task
+//!
+//! ```
+//!
+//! #![deny(unsafe_code)]
+//! #![feature(const_fn)]
+//! #![feature(proc_macro)]
+//! #![no_std]
+//!
+//! extern crate cortex_m;
+//! #[macro_use(task)]
+//! extern crate cortex_m_rtfm as rtfm;
+//! extern crate stm32f103xx;
+//!
+//! use cortex_m::peripheral::SystClkSource;
+//! use rtfm::{app, Threshold};
+//!
+//! app! {
+//! device: stm32f103xx,
+//!
+//! // 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: {
+//! // This is the priority of the task.
+//! // 1 is the lowest priority a task can have.
+//! // The maximum priority is determined by the number of priority bits
+//! // the device has. This device has 4 priority bits so 16 is the
+//! // maximum value.
+//! priority: 1,
+//!
+//! // These are the *resources* associated with this task
+//! //
+//! // The peripherals that the task needs can be listed here
+//! resources: [GPIOC],
+//! },
+//! }
+//! }
+//!
+//! fn init(p: init::Peripherals) {
+//! // power on GPIOC
+//! p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled());
+//!
+//! // configure PC13 as output
+//! p.GPIOC.bsrr.write(|w| w.bs13().set());
+//! p.GPIOC
+//! .crh
+//! .modify(|_, w| w.mode13().output().cnf13().push());
+//!
+//! // configure the system timer to generate one interrupt every second
+//! p.SYST.set_clock_source(SystClkSource::Core);
+//! p.SYST.set_reload(8_000_000); // 1s
+//! p.SYST.enable_interrupt();
+//! p.SYST.enable_counter();
+//! }
+//!
+//! fn idle() -> ! {
+//! loop {
+//! rtfm::wfi();
+//! }
+//! }
+//!
+//! // This binds the `sys_tick` handler to the `SYS_TICK` task
+//! //
+//! // This particular handler has local state associated to it. The value of the
+//! // `STATE` variable will be preserved across invocations of this handler
+//! task!(SYS_TICK, sys_tick, Locals {
+//! static STATE: bool = false;
+//! });
+//!
+//! // This is the task handler of the SYS_TICK exception
+//! //
+//! // `t` is the preemption threshold token. We won't use it this time.
+//! // `l` is the data local to this task. The type here must match the one declared
+//! // in `task!`.
+//! // `r` is the resources this task has access to. `SYS_TICK::Resources` has one
+//! // field per resource declared in `app!`.
+//! fn sys_tick(_t: &mut Threshold, l: &mut Locals, r: SYS_TICK::Resources) {
+//! // toggle state
+//! *l.STATE = !*l.STATE;
+//!
+//! if *l.STATE {
+//! // set the pin PC13 high
+//! r.GPIOC.bsrr.write(|w| w.bs13().set());
+//! } else {
+//! // set the pin PC13 low
+//! r.GPIOC.bsrr.write(|w| w.br13().reset());
+//! }
+//! }
+//! ```
+// Auto-generated. Do not modify.
diff --git a/src/examples/_2_two_tasks.rs b/src/examples/_2_two_tasks.rs
new file mode 100644
index 00000000..9eb61b18
--- /dev/null
+++ b/src/examples/_2_two_tasks.rs
@@ -0,0 +1,79 @@
+//! Two tasks running at the same priority with access to the same resource
+//!
+//! ```
+//!
+//! #![deny(unsafe_code)]
+//! #![feature(const_fn)]
+//! #![feature(proc_macro)]
+//! #![no_std]
+//!
+//! #[macro_use(task)]
+//! extern crate cortex_m_rtfm as rtfm;
+//! extern crate stm32f103xx;
+//!
+//! use rtfm::{app, Threshold};
+//!
+//! app! {
+//! device: stm32f103xx,
+//!
+//! // Resources that are plain data, not peripherals
+//! resources: {
+//! // Declaration of resources looks like the declaration of `static`
+//! // variables
+//! static COUNTER: u64 = 0;
+//! },
+//!
+//! tasks: {
+//! SYS_TICK: {
+//! priority: 1,
+//! // Both this task and TIM2 have access to the `COUNTER` resource
+//! resources: [COUNTER],
+//! },
+//!
+//! // An interrupt as a task
+//! TIM2: {
+//! // For interrupts the `enabled` field must be specified. It
+//! // indicates if the interrupt will be enabled or disabled once
+//! // `idle` starts
+//! enabled: true,
+//! priority: 1,
+//! resources: [COUNTER],
+//! },
+//! },
+//! }
+//!
+//! // when data resources are declared in the top `resources` field, `init` will
+//! // have full access to them
+//! fn init(_p: init::Peripherals, _r: init::Resources) {
+//! // ..
+//! }
+//!
+//! fn idle() -> ! {
+//! loop {
+//! rtfm::wfi();
+//! }
+//! }
+//!
+//! task!(SYS_TICK, sys_tick);
+//!
+//! // 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, r: SYS_TICK::Resources) {
+//! // ..
+//!
+//! **r.COUNTER += 1;
+//!
+//! // ..
+//! }
+//!
+//! task!(TIM2, tim2);
+//!
+//! fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
+//! // ..
+//!
+//! **r.COUNTER += 1;
+//!
+//! // ..
+//! }
+//! ```
+// Auto-generated. Do not modify.
diff --git a/src/examples/_3_preemption.rs b/src/examples/_3_preemption.rs
new file mode 100644
index 00000000..b93ec086
--- /dev/null
+++ b/src/examples/_3_preemption.rs
@@ -0,0 +1,74 @@
+//! Two tasks running at different priorities with access to the same resource
+//!
+//! ```
+//!
+//! #![deny(unsafe_code)]
+//! #![feature(const_fn)]
+//! #![feature(proc_macro)]
+//! #![no_std]
+//!
+//! #[macro_use(task)]
+//! 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 task `SYS_TICK` has higher priority than `TIM2`
+//! SYS_TICK: {
+//! priority: 2,
+//! resources: [COUNTER],
+//! },
+//!
+//! TIM2: {
+//! enabled: true,
+//! priority: 1,
+//! resources: [COUNTER],
+//! },
+//! },
+//! }
+//!
+//! fn init(_p: init::Peripherals, _r: init::Resources) {
+//! // ..
+//! }
+//!
+//! fn idle() -> ! {
+//! loop {
+//! rtfm::wfi();
+//! }
+//! }
+//!
+//! task!(SYS_TICK, sys_tick);
+//!
+//! fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) {
+//! // ..
+//!
+//! // this task can't be preempted by `tim2` so it has direct access to the
+//! // resource data
+//! **r.COUNTER += 1;
+//!
+//! // ..
+//! }
+//!
+//! task!(TIM2, tim2);
+//!
+//! 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 data corruption or data loss
+//! r.COUNTER.claim_mut(t, |counter, _t| { **counter += 1; });
+//!
+//! // ..
+//! }
+//! ```
+// Auto-generated. Do not modify.
diff --git a/src/examples/_4_nested.rs b/src/examples/_4_nested.rs
new file mode 100644
index 00000000..718710d7
--- /dev/null
+++ b/src/examples/_4_nested.rs
@@ -0,0 +1,129 @@
+//! 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)]
+//! #![feature(const_fn)]
+//! #![feature(proc_macro)]
+//! #![no_std]
+//!
+//! #[macro_use(task)]
+//! 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: {
+//! enabled: true,
+//! priority: 1,
+//! resources: [LOW, HIGH],
+//! },
+//!
+//! EXTI1: {
+//! enabled: true,
+//! priority: 2,
+//! resources: [LOW],
+//! },
+//!
+//! EXTI2: {
+//! enabled: true,
+//! priority: 3,
+//! resources: [HIGH],
+//! },
+//! },
+//! }
+//!
+//! fn init(_p: init::Peripherals, _r: init::Resources) {}
+//!
+//! fn idle() -> ! {
+//! // 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();
+//! }
+//! }
+//!
+//! task!(EXTI0, exti0);
+//!
+//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
+//! // because this task has a priority of 1 the preemption threshold is also 1
+//!
+//! // A
+//! 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
+//! r.LOW.claim_mut(t, |_low, t| {
+//! // this claim increases the preemption threshold to 2
+//! // just high enough to not race with task `exti1` for access to the
+//! // `LOW` resource
+//!
+//! // C
+//! 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
+//!
+//! // E
+//! rtfm::bkpt();
+//!
+//! // claims can be nested
+//! r.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);
+//!
+//! // F
+//! rtfm::bkpt();
+//! });
+//!
+//! // upon leaving the critical section the preemption threshold drops to 2
+//! // and `exti2` immediately preempts this task
+//! // ~> exti2
+//! });
+//!
+//! // once again the preemption threshold drops to 1
+//! // now the pending `exti1` can preempt this task
+//! // ~> exti1
+//! }
+//!
+//! task!(EXTI1, exti1);
+//!
+//! fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) {
+//! // B, H
+//! rtfm::bkpt();
+//! }
+//!
+//! task!(EXTI2, exti2);
+//!
+//! fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {
+//! // D, G
+//! rtfm::bkpt();
+//! }
+//! ```
+// Auto-generated. Do not modify.
diff --git a/src/examples/_5_generics.rs b/src/examples/_5_generics.rs
new file mode 100644
index 00000000..a8f42cdf
--- /dev/null
+++ b/src/examples/_5_generics.rs
@@ -0,0 +1,73 @@
+//! Working with resources in a generic fashion
+//!
+//! ```
+//!
+//! #![deny(unsafe_code)]
+//! #![feature(proc_macro)]
+//! #![no_std]
+//!
+//! #[macro_use(task)]
+//! extern crate cortex_m_rtfm as rtfm;
+//! extern crate stm32f103xx;
+//!
+//! use rtfm::{app, Resource, Threshold};
+//! use stm32f103xx::{SPI1, GPIOA};
+//!
+//! app! {
+//! device: stm32f103xx,
+//!
+//! tasks: {
+//! EXTI0: {
+//! enabled: true,
+//! priority: 1,
+//! resources: [GPIOA, SPI1],
+//! },
+//!
+//! EXTI1: {
+//! enabled: true,
+//! priority: 2,
+//! resources: [GPIOA, SPI1],
+//! },
+//! },
+//! }
+//!
+//! fn init(_p: init::Peripherals) {}
+//!
+//! fn idle() -> ! {
+//! loop {
+//! rtfm::wfi();
+//! }
+//! }
+//!
+//! // a generic function to use resources in any task (regardless of its priority)
+//! 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
+//! });
+//! }
+//!
+//! task!(EXTI0, exti0);
+//!
+//! // this task needs critical sections to access the resources
+//! fn exti0(t: &mut Threshold, r: EXTI0::Resources) {
+//! work(t, &r.GPIOA, &r.SPI1);
+//! }
+//!
+//! task!(EXTI1, exti1);
+//!
+//! // 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.
diff --git a/src/examples/_6_full_syntax.rs b/src/examples/_6_full_syntax.rs
new file mode 100644
index 00000000..9e932436
--- /dev/null
+++ b/src/examples/_6_full_syntax.rs
@@ -0,0 +1,85 @@
+//! A showcase of the `app!` macro syntax
+//!
+//! ```
+//!
+//! #![deny(unsafe_code)]
+//! #![feature(const_fn)]
+//! #![feature(proc_macro)]
+//! #![no_std]
+//!
+//! #[macro_use(task)]
+//! extern crate cortex_m_rtfm as rtfm;
+//! extern crate stm32f103xx;
+//!
+//! use rtfm::{app, Resource, Threshold};
+//!
+//! app! {
+//! device: stm32f103xx,
+//!
+//! resources: {
+//! static CO_OWNED: u32 = 0;
+//! static OWNED: bool = false;
+//! static SHARED: bool = false;
+//! },
+//!
+//! init: {
+//! path: init_, // this is a path to the "init" function
+//! },
+//!
+//! idle: {
+//! locals: {
+//! static COUNTER: u32 = 0;
+//! },
+//! path: idle_, // this is a path to the "idle" function
+//! resources: [OWNED, SHARED],
+//! },
+//!
+//! tasks: {
+//! SYS_TICK: {
+//! priority: 1,
+//! resources: [CO_OWNED, SHARED],
+//! },
+//!
+//! TIM2: {
+//! enabled: true,
+//! priority: 1,
+//! resources: [CO_OWNED],
+//! },
+//! },
+//! }
+//!
+//! fn init_(_p: init::Peripherals, _r: init::Resources) {}
+//!
+//! fn idle_(t: &mut Threshold, l: &mut idle::Locals, mut r: idle::Resources) -> ! {
+//! loop {
+//! *l.COUNTER += 1;
+//!
+//! **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);
+//! }
+//! }
+//! }
+//!
+//! task!(SYS_TICK, sys_tick, Local {
+//! static STATE: bool = true;
+//! });
+//!
+//! fn sys_tick(_t: &mut Threshold, l: &mut Local, r: SYS_TICK::Resources) {
+//! *l.STATE = !*l.STATE;
+//!
+//! **r.CO_OWNED += 1;
+//! }
+//!
+//! task!(TIM2, tim2);
+//!
+//! fn tim2(_t: &mut Threshold, r: TIM2::Resources) {
+//! **r.CO_OWNED += 1;
+//! }
+//! ```
+// Auto-generated. Do not modify.
diff --git a/src/examples/mod.rs b/src/examples/mod.rs
new file mode 100644
index 00000000..e0be5a6f
--- /dev/null
+++ b/src/examples/mod.rs
@@ -0,0 +1,9 @@
+//! 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_generics;
+pub mod _6_full_syntax;
diff --git a/src/lib.rs b/src/lib.rs
index 6ee98d79..3b677380 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,57 @@
+//! Real Time For the Masses (RTFM), a framework for building concurrent
+//! applications, 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` function.
+//!
+//! - Task priorities must remain constant at runtime.
+//!
+//! # Dependencies
+//!
+//! - A device crate generated using [`svd2rust`] v0.11.x. The input SVD file
+//! *must* contain [`<cpu>`] information.
+//! - A `start` lang time: Vanilla `main` must be supported in binary crates.
+//! You can use the [`cortex-m-rt`] crate to fulfill the requirement
+//!
+//! [`svd2rust`]: https://docs.rs/svd2rust/0..0/svd2rust/
+//! [`<cpu>`]: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_cpu.html
+//! [`cortex-m-rt`]: https://docs.rs/cortex-m-rt/0.3.0/cortex_m_rt/
+//!
+//! # Examples
+//!
+//! In increasing grade of complexity: [examples](./examples/index.html)
+
+#![deny(missing_docs)]
+#![deny(warnings)]
#![feature(asm)]
#![feature(const_fn)]
#![feature(optin_builtin_traits)]
@@ -17,11 +71,73 @@ pub use cortex_m::interrupt::free as atomic;
pub use static_ref::Static;
use cortex_m::interrupt::Nr;
#[cfg(not(armv6m))]
-use cortex_m::register::{basepri_max, basepri};
+use cortex_m::register::{basepri, basepri_max};
+
+pub mod examples;
+
+/// A resource, a means to share data between tasks
+pub trait Resource {
+ /// The data protected by the resource
+ type Data;
+
+ /// Borrows the resource data for the duration of a *global* critical
+ /// section
+ fn borrow<'cs>(
+ &'cs self,
+ cs: &'cs CriticalSection,
+ ) -> &'cs Static<Self::Data>;
+
+ /// Mutable variant of `borrow`
+ fn borrow_mut<'cs>(
+ &'cs mut self,
+ cs: &'cs CriticalSection,
+ ) -> &'cs mut Static<Self::Data>;
+
+ /// Claims the resource data for the span of the closure `f`. For the
+ /// duration of the closure other tasks that may access the resource data
+ /// are prevented from preempting the current task.
+ fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R;
+
+ /// Mutable variant of `claim`
+ fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R;
+}
+
+impl<T> Resource for Static<T> {
+ type Data = T;
+
+ fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static<T> {
+ self
+ }
+
+ fn borrow_mut<'cs>(
+ &'cs mut self,
+ _cs: &'cs CriticalSection,
+ ) -> &'cs mut Static<T> {
+ self
+ }
-#[inline(always)]
-unsafe fn claim<T, U, R, F, G>(
- data: T,
+ fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R,
+ {
+ f(self, t)
+ }
+
+ fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
+ where
+ F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R,
+ {
+ f(self, t)
+ }
+}
+
+#[doc(hidden)]
+pub unsafe fn claim<T, U, R, F, G>(
+ data: *mut T,
ceiling: u8,
nvic_prio_bits: u8,
t: &mut Threshold,
@@ -30,10 +146,10 @@ unsafe fn claim<T, U, R, F, G>(
) -> R
where
F: FnOnce(U, &mut Threshold) -> R,
- G: FnOnce(T) -> U,
+ G: FnOnce(*mut T) -> U,
{
let max_priority = 1 << nvic_prio_bits;
- if ceiling > t.0 {
+ if ceiling > t.value {
match () {
#[cfg(armv6m)]
() => {
@@ -47,7 +163,7 @@ where
let old = basepri::read();
let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
basepri_max::write(hw);
- let ret = f(g(data), &mut Threshold(ceiling));
+ let ret = f(g(data), &mut Threshold::new(ceiling));
basepri::write(old);
ret
}
@@ -58,139 +174,44 @@ where
}
}
-pub struct Peripheral<P>
-where
- P: 'static,
-{
- // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
- // claims (the ceiling value gets loaded at runtime rather than inlined)
- // ceiling: u8,
- peripheral: cortex_m::peripheral::Peripheral<P>,
-}
-
-impl<P> Peripheral<P> {
- pub const fn new(peripheral: cortex_m::peripheral::Peripheral<P>) -> Self {
- Peripheral { peripheral }
- }
-
- #[inline(always)]
- pub unsafe fn borrow<'cs>(
- &'static self,
- _cs: &'cs CriticalSection,
- ) -> &'cs P {
- &*self.peripheral.get()
- }
-
- #[inline(always)]
- pub unsafe fn claim<R, F>(
- &'static self,
- ceiling: u8,
- nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
- ) -> R
- where
- F: FnOnce(&P, &mut Threshold) -> R,
- {
- claim(
- &self.peripheral,
- ceiling,
- nvic_prio_bits,
- t,
- f,
- |peripheral| &*peripheral.get(),
- )
- }
-
- pub fn get(&self) -> *mut P {
- self.peripheral.get()
- }
-}
-
-unsafe impl<P> Sync for Peripheral<P>
-where
- P: Send,
-{
-}
-
-pub struct Resource<T> {
- // FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
- // claims (the ceiling value gets loaded at runtime rather than inlined)
- // ceiling: u8,
+#[doc(hidden)]
+pub struct Cell<T> {
data: UnsafeCell<T>,
}
-impl<T> Resource<T> {
- pub const fn new(value: T) -> Self {
- Resource {
- data: UnsafeCell::new(value),
+#[doc(hidden)]
+impl<T> Cell<T> {
+ pub const fn new(data: T) -> Self {
+ Cell {
+ data: UnsafeCell::new(data),
}
}
- #[inline(always)]
- pub unsafe fn borrow<'cs>(
- &'static self,
- _cs: &'cs CriticalSection,
- ) -> &'cs Static<T> {
- Static::ref_(&*self.data.get())
- }
-
- #[inline(always)]
- pub unsafe fn borrow_mut<'cs>(
- &'static self,
- _cs: &'cs CriticalSection,
- ) -> &'cs mut Static<T> {
- Static::ref_mut(&mut *self.data.get())
- }
-
- #[inline(always)]
- pub unsafe fn claim<R, F>(
- &'static self,
- ceiling: u8,
- nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
- ) -> R
- where
- F: FnOnce(&Static<T>, &mut Threshold) -> R,
- {
- claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
- Static::ref_(&*data.get())
- })
- }
-
- #[inline(always)]
- pub unsafe fn claim_mut<R, F>(
- &'static self,
- ceiling: u8,
- nvic_prio_bits: u8,
- t: &mut Threshold,
- f: F,
- ) -> R
- where
- F: FnOnce(&mut Static<T>, &mut Threshold) -> R,
- {
- claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
- Static::ref_mut(&mut *data.get())
- })
- }
-
pub fn get(&self) -> *mut T {
self.data.get()
}
}
-unsafe impl<T> Sync for Resource<T>
+unsafe impl<T> Sync for Cell<T>
where
T: Send,
{
}
-pub struct Threshold(u8);
+/// Preemption threshold token
+///
+/// The preemption threshold indicates the priority a task must have to preempt
+/// the current context. For example a threshold of 2 indicates that only
+/// interrupts / exceptions with a priority of 3 or greater can preempt the
+/// current context
+pub struct Threshold {
+ value: u8,
+}
impl Threshold {
+ #[doc(hidden)]
pub unsafe fn new(value: u8) -> Self {
- Threshold(value)
+ Threshold { value }
}
}
@@ -206,14 +227,15 @@ where
nvic.set_pending(interrupt);
}
+/// Binds a task `$handler` to the interrupt / exception `$NAME`
#[macro_export]
macro_rules! task {
- ($NAME:ident, $body:path) => {
+ ($NAME:ident, $handler:path) => {
#[allow(non_snake_case)]
#[allow(unsafe_code)]
#[no_mangle]
pub unsafe extern "C" fn $NAME() {
- let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $body;
+ let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $handler;
f(
&mut $crate::Threshold::new(::$NAME::$NAME),
@@ -221,11 +243,13 @@ macro_rules! task {
);
}
};
- ($NAME:ident, $body:path, $local:ident {
- $($var:ident: $ty:ty = $expr:expr;)+
+
+ ($NAME:ident, $handler:path, $locals:ident {
+ $(static $var:ident: $ty:ty = $expr:expr;)+
}) => {
- struct $local {
- $($var: $ty,)+
+ #[allow(non_snake_case)]
+ struct $locals {
+ $($var: $crate::Static<$ty>,)+
}
#[allow(non_snake_case)]
@@ -234,17 +258,17 @@ macro_rules! task {
pub unsafe extern "C" fn $NAME() {
let f: fn(
&mut $crate::Threshold,
- &mut $local,
+ &mut $locals,
::$NAME::Resources,
- ) = $body;
+ ) = $handler;
- static mut LOCAL: $local = $local {
- $($var: $expr,)+
+ static mut LOCALS: $locals = $locals {
+ $($var: unsafe { $crate::Static::new($expr) },)+
};
f(
&mut $crate::Threshold::new(::$NAME::$NAME),
- &mut LOCAL,
+ &mut LOCALS,
::$NAME::Resources::new(),
);
}