From c7b9507a57f2ba28c18b15dd2719a1c56f74a302 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 20 Jul 2017 22:53:44 -0500 Subject: [PATCH] `Resource` trait, docs, examples and rtfm-syntax related changes --- .cargo/config | 7 + Cargo.toml | 14 +- Xargo.toml | 5 + examples/full-syntax.rs | 81 ++++++++++ examples/generics.rs | 69 ++++++++ examples/nested.rs | 125 ++++++++++++++ examples/one-task.rs | 91 +++++++++++ examples/preemption.rs | 70 ++++++++ examples/two-tasks.rs | 75 +++++++++ examples/zero-tasks.rs | 49 ++++++ gen-examples.sh | 55 +++++++ macros/src/trans.rs | 121 +++++++++----- memory.x | 6 + src/examples/_0_zero_tasks.rs | 53 ++++++ src/examples/_1_one_task.rs | 95 +++++++++++ src/examples/_2_two_tasks.rs | 79 +++++++++ src/examples/_3_preemption.rs | 74 +++++++++ src/examples/_4_nested.rs | 129 +++++++++++++++ src/examples/_5_generics.rs | 73 +++++++++ src/examples/_6_full_syntax.rs | 85 ++++++++++ src/examples/mod.rs | 9 ++ src/lib.rs | 288 ++++++++++++++++++--------------- 22 files changed, 1482 insertions(+), 171 deletions(-) create mode 100644 .cargo/config create mode 100644 Xargo.toml create mode 100644 examples/full-syntax.rs create mode 100644 examples/generics.rs create mode 100644 examples/nested.rs create mode 100644 examples/one-task.rs create mode 100644 examples/preemption.rs create mode 100644 examples/two-tasks.rs create mode 100644 examples/zero-tasks.rs create mode 100644 gen-examples.sh create mode 100644 memory.x create mode 100644 src/examples/_0_zero_tasks.rs create mode 100644 src/examples/_1_one_task.rs create mode 100644 src/examples/_2_two_tasks.rs create mode 100644 src/examples/_3_preemption.rs create mode 100644 src/examples/_4_nested.rs create mode 100644 src/examples/_5_generics.rs create mode 100644 src/examples/_6_full_syntax.rs create mode 100644 src/examples/mod.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000000..1642997efa --- /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 d36b81e965..e558f5fbb4 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 0000000000..bd7ffe0310 --- /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 0000000000..d14667813a --- /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 0000000000..335d159b6f --- /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(t: &mut Threshold, gpioa: &G, spi1: &S) +where + G: Resource, + S: Resource, +{ + 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 0000000000..d307634cd8 --- /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 0000000000..8cfe089c08 --- /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 0000000000..2ca6f951d0 --- /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 0000000000..eb74fa8f39 --- /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 0000000000..132016773a --- /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 0000000000..d053c95b49 --- /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 5262bc4113..b5e624d012 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) { 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) { 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( + fn claim( &self, t: &mut #krate::Threshold, f: F, @@ -373,16 +383,18 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { &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( + fn claim_mut( &mut self, t: &mut #krate::Threshold, f: F, @@ -393,45 +405,77 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { &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( + 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( &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( + &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) { } 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) { }); } 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) { 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 0000000000..534d478665 --- /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 0000000000..15231818bd --- /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 0000000000..33e8bf7f49 --- /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 0000000000..9eb61b184f --- /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 0000000000..b93ec0863c --- /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 0000000000..718710d77d --- /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 0000000000..a8f42cdff2 --- /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(t: &mut Threshold, gpioa: &G, spi1: &S) +//! where +//! G: Resource, +//! S: Resource, +//! { +//! 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 0000000000..9e93243676 --- /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 0000000000..e0be5a6f3f --- /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 6ee98d79f6..3b677380c6 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 [``] 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/ +//! [``]: 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}; -#[inline(always)] -unsafe fn claim( - data: T, +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; + + /// Mutable variant of `borrow` + fn borrow_mut<'cs>( + &'cs mut self, + cs: &'cs CriticalSection, + ) -> &'cs mut Static; + + /// 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(&self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&Static, &mut Threshold) -> R; + + /// Mutable variant of `claim` + fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&mut Static, &mut Threshold) -> R; +} + +impl Resource for Static { + type Data = T; + + fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static { + self + } + + fn borrow_mut<'cs>( + &'cs mut self, + _cs: &'cs CriticalSection, + ) -> &'cs mut Static { + self + } + + fn claim(&self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&Static, &mut Threshold) -> R, + { + f(self, t) + } + + fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&mut Static, &mut Threshold) -> R, + { + f(self, t) + } +} + +#[doc(hidden)] +pub unsafe fn claim( + data: *mut T, ceiling: u8, nvic_prio_bits: u8, t: &mut Threshold, @@ -30,10 +146,10 @@ unsafe fn claim( ) -> 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

-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

, -} - -impl

Peripheral

{ - pub const fn new(peripheral: cortex_m::peripheral::Peripheral

) -> 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( - &'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

Sync for Peripheral

-where - P: Send, -{ -} - -pub struct Resource { - // 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 { data: UnsafeCell, } -impl Resource { - pub const fn new(value: T) -> Self { - Resource { - data: UnsafeCell::new(value), +#[doc(hidden)] +impl Cell { + 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 { - Static::ref_(&*self.data.get()) - } - - #[inline(always)] - pub unsafe fn borrow_mut<'cs>( - &'static self, - _cs: &'cs CriticalSection, - ) -> &'cs mut Static { - Static::ref_mut(&mut *self.data.get()) - } - - #[inline(always)] - pub unsafe fn claim( - &'static self, - ceiling: u8, - nvic_prio_bits: u8, - t: &mut Threshold, - f: F, - ) -> R - where - F: FnOnce(&Static, &mut Threshold) -> R, - { - claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| { - Static::ref_(&*data.get()) - }) - } - - #[inline(always)] - pub unsafe fn claim_mut( - &'static self, - ceiling: u8, - nvic_prio_bits: u8, - t: &mut Threshold, - f: F, - ) -> R - where - F: FnOnce(&mut Static, &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 Sync for Resource +unsafe impl Sync for Cell 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(), ); }