aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorge Aparicio <japaricious@gmail.com>2017-04-09 22:42:17 -0500
committerJorge Aparicio <japaricious@gmail.com>2017-04-09 22:42:17 -0500
commit595404c5ffc37ce95ffb2b6999ab85e6818bfa50 (patch)
tree43688c0818f2af6fdb37ce2e3f29dd7f0ccb898b
parent6ac2625a75fc73b5cb846cd3d60422976f8946fc (diff)
compile time verified ceilings
-rw-r--r--.gitignore3
-rw-r--r--Cargo.toml20
-rw-r--r--build.rs103
-rw-r--r--src/checked.rs78
-rw-r--r--src/lib.rs721
-rw-r--r--tests/cfail.rs16
-rw-r--r--tests/cfail/borrow.rs22
-rw-r--r--tests/cfail/lock.rs41
8 files changed, 470 insertions, 534 deletions
diff --git a/.gitignore b/.gitignore
index a9d37c5..6dc3db1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
-target
+**/*.rs.bk
Cargo.lock
+target/
diff --git a/Cargo.toml b/Cargo.toml
index c51a72e..3bc0347 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,9 +4,21 @@ build = "build.rs"
name = "cortex-m-srp"
version = "0.1.0"
+[build-dependencies]
+quote = "0.3.15"
+syn = "0.11.10"
+
[dependencies]
-cortex-m = "0.2.0"
+cortex-m = "0.2.2"
+typenum = "1.7.0"
+
+[dev-dependencies]
+compiletest_rs = "0.2.5"
-[dependencies.vcell]
-features = ["const-fn"]
-version = "0.1.0" \ No newline at end of file
+[features]
+# Number of priority bits
+P2 = []
+P3 = []
+P4 = []
+P5 = []
+default = ["P4"]
diff --git a/build.rs b/build.rs
index 46bdb71..19625e5 100644
--- a/build.rs
+++ b/build.rs
@@ -1,9 +1,106 @@
+#[macro_use]
+extern crate quote;
+extern crate syn;
+
use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+
+use syn::{Ident, IntTy, Lit};
fn main() {
- let target = env::var("TARGET").unwrap();
+ let bits = if env::var_os("CARGO_FEATURE_P2").is_some() {
+ 2
+ } else if env::var_os("CARGO_FEATURE_P3").is_some() {
+ 3
+ } else if env::var_os("CARGO_FEATURE_P4").is_some() {
+ 4
+ } else if env::var_os("CARGO_FEATURE_P5").is_some() {
+ 5
+ } else {
+ panic!(
+ "Specify the number of priority bits through one of these Cargo \
+ features: P2, P3, P4 or P5"
+ );
+ };
+
+ let n = Lit::Int(bits, IntTy::Unsuffixed);
+ let mut tokens = vec![];
+ tokens.push(
+ quote! {
+ const PRIORITY_BITS: u8 = #n;
+ },
+ );
+
+ // Ceilings
+ for i in 1..(1 << bits) + 1 {
+ let c = Ident::new(format!("C{}", i));
+ let u = Ident::new(format!("U{}", i));
+
+ tokens.push(
+ quote! {
+ /// Ceiling
+ pub type #c = C<::typenum::#u>;
+
+ unsafe impl Ceiling for #c {}
+ },
+ );
+ }
+
+ // Priorities
+ for i in 1..(1 << bits) + 1 {
+ let p = Ident::new(format!("P{}", i));
+ let u = Ident::new(format!("U{}", i));
+
+ tokens.push(
+ quote! {
+ /// Priority
+ pub type #p = P<::typenum::#u>;
+
+ unsafe impl Priority for #p {}
- if target == "thumbv6m-none-eabi" {
- println!("cargo:rustc-cfg=thumbv6m");
+ unsafe impl Level for ::typenum::#u {
+ fn hw() -> u8 {
+ logical2hw(::typenum::#u::to_u8())
+ }
+ }
+ },
+ );
}
+
+ // GreaterThanOrEqual
+ for i in 1..(1 << bits) + 1 {
+ for j in 1..(i + 1) {
+ let i = Ident::new(format!("U{}", i));
+ let j = Ident::new(format!("U{}", j));
+
+ tokens.push(
+ quote! {
+ unsafe impl GreaterThanOrEqual<::typenum::#j> for
+ ::typenum::#i {}
+ },
+ );
+ }
+ }
+
+ let u = Ident::new(format!("U{}", (1 << bits)));
+ tokens.push(quote! {
+ #[doc(hidden)]
+ pub type CMAX = C<::typenum::#u>;
+
+ /// Maximum priority level
+ pub type UMAX = ::typenum::#u;
+ });
+
+ let tokens = quote! {
+ #(#tokens)*
+ };
+
+ let out_dir = env::var("OUT_DIR").unwrap();
+ let mut out = File::create(PathBuf::from(out_dir).join("prio.rs")).unwrap();
+
+ out.write_all(tokens.as_str().as_bytes()).unwrap();
+
+ println!("cargo:rerun-if-changed=build.rs");
}
diff --git a/src/checked.rs b/src/checked.rs
deleted file mode 100644
index ec4114d..0000000
--- a/src/checked.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-//! Safe, run-time checked resources
-
-use core::marker::PhantomData;
-use core::cell::UnsafeCell;
-
-use cortex_m::interrupt;
-use cortex_m::register::{basepri, basepri_max};
-use vcell::VolatileCell;
-
-use Ceiling;
-
-unsafe fn acquire(locked: &VolatileCell<bool>, ceiling: u8) -> u8 {
- assert!(!locked.get(), "resource already locked");
- let old_basepri = basepri::read();
- basepri_max::write(ceiling);
- locked.set(true);
- old_basepri
-}
-
-unsafe fn release(locked: &VolatileCell<bool>, old_basepri: u8) {
- locked.set(false);
- basepri::write(old_basepri);
-}
-
-/// A totally safe `Resource` that panics on misuse
-pub struct Resource<T, C> {
- _marker: PhantomData<C>,
- data: UnsafeCell<T>,
- locked: VolatileCell<bool>,
-}
-
-impl<T, C> Resource<T, C>
-where
- C: Ceiling,
-{
- /// Creates a new `Resource` with ceiling `C`
- pub const fn new(data: T) -> Resource<T, C> {
- Resource {
- _marker: PhantomData,
- data: UnsafeCell::new(data),
- locked: VolatileCell::new(false),
- }
- }
-
- /// Locks the resource, blocking tasks with priority equal or smaller than
- /// the ceiling `C`
- pub fn lock<R, F>(&'static self, f: F) -> R
- where
- F: FnOnce(&T) -> R,
- {
- unsafe {
- let old_basepri = acquire(&self.locked, C::hw_ceiling());
- ::compiler_barrier();
- let ret = f(&*self.data.get());
- ::compiler_barrier();
- release(&self.locked, old_basepri);
- ret
- }
- }
-
- /// Mutably locks the resource, blocking tasks with priority equal or
- /// smaller than the ceiling `C`
- pub fn lock_mut<R, F>(&'static self, f: F) -> R
- where
- F: FnOnce(&mut T) -> R,
- {
- unsafe {
- let old_basepri = acquire(&self.locked, C::hw_ceiling());
- ::compiler_barrier();
- let ret = f(&mut *self.data.get());
- ::compiler_barrier();
- release(&self.locked, old_basepri);
- ret
- }
- }
-}
-
-unsafe impl<T, C> Sync for Resource<T, C> {}
diff --git a/src/lib.rs b/src/lib.rs
index a7b6412..6e69684 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,138 +1,26 @@
-//! Stack Resource Policy for Cortex-M processors
-//!
-//! NOTE ARMv6-M is not fully supported at the moment.
-
-#![deny(missing_docs)]
#![deny(warnings)]
#![feature(asm)]
#![feature(const_fn)]
#![no_std]
extern crate cortex_m;
-extern crate vcell;
-
-pub mod checked;
-
-use cortex_m::ctxt::Context;
-use cortex_m::interrupt::{CriticalSection, Nr};
-use cortex_m::peripheral::{Peripheral, NVIC};
-#[cfg(not(thumbv6m))]
-use cortex_m::peripheral::SCB;
-#[cfg(not(thumbv6m))]
-use cortex_m::register::{basepri, basepri_max};
+extern crate typenum;
-use core::cell::UnsafeCell;
use core::marker::PhantomData;
-#[cfg(not(thumbv6m))]
-use core::ptr;
-
-#[cfg(not(thumbv6m))]
-// NOTE Only the 4 highest bits of the priority byte (BASEPRI / NVIC.IPR) are
-// considered when determining priorities.
-const PRIORITY_BITS: u8 = 4;
-
-/// Logical task priority
-#[cfg(not(thumbv6m))]
-unsafe fn task_priority() -> u8 {
- // NOTE(safe) atomic read
- let nr = match (*SCB.get()).icsr.read() as u8 {
- n if n >= 16 => n - 16,
- _ => panic!("not in a task"),
- };
- // NOTE(safe) atomic read
- hardware((*NVIC.get()).ipr[nr as usize].read())
-}
-
-#[cfg(all(debug_assertions, not(thumbv6m)))]
-unsafe fn get_check(logical_ceiling: u8) {
- let task_priority = task_priority();
- let system_ceiling = hardware(cortex_m::register::basepri::read());
- let resource_ceiling = logical_ceiling;
-
- if resource_ceiling < task_priority {
- panic!(
- "bad ceiling value. task priority = {}, \
- resource ceiling = {}",
- task_priority,
- resource_ceiling,
- );
- } else if resource_ceiling == task_priority {
- // OK: safe to access the resource without locking in the
- // task with highest priority
- } else if resource_ceiling <= system_ceiling {
- // OK: use within another resource critical section, where
- // the locked resource has higher or equal ceiling
- } else {
- panic!(
- "racy access to resource. \
- task priority = {}, \
- resource ceiling = {}, \
- system ceiling = {}",
- task_priority,
- resource_ceiling,
- system_ceiling,
- );
- }
-}
-
-#[cfg(not(debug_assertions))]
-unsafe fn get_check(_: u8) {}
-
-#[cfg(all(debug_assertions, not(thumbv6m)))]
-unsafe fn lock_check(ceiling: u8) {
- let ceiling = hardware(ceiling);
- let task_priority = task_priority();
-
- if task_priority > ceiling {
- panic!(
- "bad ceiling value. task_priority = {}, resource_ceiling = {}",
- task_priority,
- ceiling,
- );
- }
-}
+use core::cell::UnsafeCell;
-#[cfg(not(debug_assertions))]
-unsafe fn lock_check(_: u8) {}
+use cortex_m::interrupt::Nr;
+use cortex_m::register::{basepri, basepri_max};
+use typenum::{Cmp, Equal, Greater, Less, Unsigned};
-// XXX Do we need memory / instruction / compiler barriers here?
-#[cfg(not(thumbv6m))]
-#[inline(always)]
-unsafe fn lock<T, R, C, F>(f: F, res: *const T, ceiling: u8) -> R
-where
- C: Ceiling,
- F: FnOnce(&T, C) -> R,
-{
- lock_check(ceiling);
- let old_basepri = basepri::read();
- basepri_max::write(ceiling);
- compiler_barrier();
- let ret = f(&*res, ptr::read(0 as *const _));
- compiler_barrier();
- basepri::write(old_basepri);
- ret
-}
+pub use cortex_m::ctxt::Local;
+#[doc(hidden)]
+pub use cortex_m::peripheral::NVIC;
+#[doc(hidden)]
+pub use cortex_m::interrupt::free;
-// XXX Do we need memory / instruction / compiler barriers here?
-#[cfg(not(thumbv6m))]
-#[inline(always)]
-unsafe fn lock_mut<T, R, C, F>(f: F, res: *mut T, ceiling: u8) -> R
-where
- C: Ceiling,
- F: FnOnce(&mut T, C) -> R,
-{
- lock_check(ceiling);
- let old_basepri = basepri::read();
- basepri_max::write(ceiling);
- compiler_barrier();
- let ret = f(&mut *res, ptr::read(0 as *const _));
- compiler_barrier();
- basepri::write(old_basepri);
- ret
-}
-
-fn compiler_barrier() {
- unsafe {
+macro_rules! barrier {
+ () => {
asm!(""
:
:
@@ -141,402 +29,339 @@ fn compiler_barrier() {
}
}
-/// A peripheral as a resource
-pub struct ResourceP<P, Ceiling>
-where
- P: 'static,
-{
- _marker: PhantomData<Ceiling>,
- peripheral: Peripheral<P>,
+/// A resource
+pub struct Resource<T, CEILING> {
+ _ceiling: PhantomData<CEILING>,
+ data: UnsafeCell<T>,
}
-impl<P, C> ResourceP<P, C>
-where
- C: CeilingLike,
-{
- /// Wraps a `peripheral` into a `Resource`
- ///
- /// # Unsafety
- ///
- /// - Must not create two resources that point to the same peripheral
- /// - The ceiling, `C`, must be picked to prevent two or more tasks from
- /// concurrently accessing the resource through preemption
- pub const unsafe fn new(p: Peripheral<P>) -> Self {
- ResourceP {
- _marker: PhantomData,
- peripheral: p,
+impl<T, C> Resource<T, C> {
+ /// Creates a new resource with ceiling `C`
+ pub const fn new(data: T) -> Self
+ where
+ C: Ceiling,
+ {
+ Resource {
+ _ceiling: PhantomData,
+ data: UnsafeCell::new(data),
}
}
-
- /// Borrows the resource for the duration of `interrupt::free`
- pub fn cs_borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs P {
- unsafe { &*self.peripheral.get() }
- }
-
- /// Mutably borrows the resource for the duration of `interrupt::free`
- pub fn cs_borrow_mut<'cs>(
- &self,
- _ctxt: &'cs mut CriticalSection,
- ) -> &'cs mut P {
- unsafe { &mut *self.peripheral.get() }
- }
}
-#[cfg(not(thumbv6m))]
-impl<P, C> ResourceP<P, C>
+impl<T, CEILING> Resource<T, C<CEILING>>
where
- C: Ceiling,
+ C<CEILING>: Ceiling,
{
- /// Borrows the resource without locking
+ /// Borrows the resource for the duration of another resource's critical
+ /// section
///
- /// NOTE The system ceiling must be higher than this resource ceiling
- pub fn borrow<'l, SC>(&'static self, _system_ceiling: &'l SC) -> &'l P
+ /// This operation is zero cost and doesn't impose any additional blocking
+ pub fn borrow<'cs, SCEILING>(
+ &'static self,
+ _system_ceiling: &'cs C<SCEILING>,
+ ) -> &'cs T
where
- SC: HigherThan<C>,
+ SCEILING: GreaterThanOrEqual<CEILING>,
{
- unsafe { &*self.peripheral.get() }
- }
-
- /// Returns an immutable reference to the inner data without locking
- ///
- /// # Safety
- ///
- /// You must
- ///
- /// - Preserve the "reference" rules. Don't create an immutable reference if
- /// the current task already owns a mutable reference to the data.
- ///
- /// - adhere to the Stack Resource Policy. You can
- /// - Access the resource from the highest priority task.
- /// - Access the resource from within a critical section that sets the
- /// system ceiling to `C`.
- pub unsafe fn get(&self) -> &'static P {
- get_check(C::ceiling());
-
- &*self.peripheral.get()
+ unsafe { &*self.data.get() }
}
- /// Returns a mutable reference to the inner data without locking
- ///
- /// # Safety
- ///
- /// You must
- ///
- /// - Preserve the "reference" rules. Don't create a mutable reference if
- /// the current task already owns a reference to the data.
+ /// Claims the resource at the task with highest priority
///
- /// - adhere to the Stack Resource Policy. You can
- /// - Access the resource from the highest priority task.
- /// - Access the resource from within a critical section that sets the
- /// system ceiling to `C`.
- pub unsafe fn get_mut(&self) -> &'static mut P {
- get_check(C::ceiling());
-
- &mut *self.peripheral.get()
- }
-
- /// Locks the resource, preventing tasks with priority lower than `Ceiling`
- /// from preempting the current task
- pub fn lock<R, F, Ctxt>(&'static self, _ctxt: &Ctxt, f: F) -> R
- where
- F: FnOnce(&P, C) -> R,
- Ctxt: Context,
- {
- unsafe { lock(f, self.peripheral.get(), C::hw_ceiling()) }
- }
-
- /// Mutably locks the resource, preventing tasks with priority lower than
- /// `Ceiling` from preempting the current task
- pub fn lock_mut<R, F, Ctxt>(&'static self, _ctxt: &mut Ctxt, f: F) -> R
- where
- F: FnOnce(&mut P, C) -> R,
- Ctxt: Context,
- {
- unsafe { lock_mut(f, self.peripheral.get(), C::hw_ceiling()) }
- }
-}
-
-impl<P> ResourceP<P, C0> {
- /// Borrows the resource without locking
- pub fn borrow<'ctxt, Ctxt>(&'static self, _ctxt: &'ctxt Ctxt) -> &'ctxt P
- where
- Ctxt: Context,
- {
- unsafe { &*self.peripheral.get() }
- }
-
- /// Mutably borrows the resource without locking
- pub fn borrow_mut<'ctxt, Ctxt>(
+ /// This operation is zero cost and doesn't impose any additional blocking
+ pub fn claim<'task, PRIORITY>(
&'static self,
- _ctxt: &'ctxt mut Ctxt,
- ) -> &'ctxt mut P
+ _priority: &'task P<PRIORITY>,
+ ) -> &'task T
where
- Ctxt: Context,
+ CEILING: Cmp<PRIORITY, Output = Equal>,
+ P<PRIORITY>: Priority,
{
- unsafe { &mut *self.peripheral.get() }
+ unsafe { &*self.data.get() }
}
-}
-
-unsafe impl<P, C> Sync for ResourceP<P, C> {}
-/// A resource
-pub struct Resource<T, Ceiling> {
- _marker: PhantomData<Ceiling>,
- data: UnsafeCell<T>,
-}
-
-impl<T, C> Resource<T, C> {
- /// Initializes a resource
- ///
- /// # Unsafety
+ /// Locks the resource for the duration of the critical section `f`
///
- /// - The ceiling, `C`, must be picked to prevent two or more tasks from
- /// concurrently accessing the resource through preemption
- pub const unsafe fn new(data: T) -> Self
+ /// For the duration of the critical section, tasks whose priority level is
+ /// smaller than or equal to the resource `CEILING` will be prevented from
+ /// preempting the current task.
+ pub fn lock<R, PRIORITY, F>(
+ &'static self,
+ _priority: &P<PRIORITY>,
+ f: F,
+ ) -> R
where
- C: CeilingLike,
+ F: FnOnce(&T, C<CEILING>) -> R,
+ C<CEILING>: Ceiling,
+ CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less>
+ + Level,
+ P<PRIORITY>: Priority,
{
- Resource {
- _marker: PhantomData,
- data: UnsafeCell::new(data),
+ unsafe {
+ let old_basepri = basepri::read();
+ basepri_max::write(<CEILING>::hw());
+ barrier!();
+ let ret = f(
+ &*self.data.get(),
+ C {
+ _0: (),
+ _marker: PhantomData,
+ },
+ );
+ barrier!();
+ basepri::write(old_basepri);
+ ret
}
}
+}
- /// Borrows the resource for the duration of `interrupt::free`
- pub fn cs_borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs T {
- unsafe { &*self.data.get() }
- }
+unsafe impl<T, CEILING> Sync for Resource<T, CEILING>
+where
+ CEILING: Ceiling,
+{
+}
- /// Mutably borrows the resource for the duration of `interrupt::free`
- pub fn cs_borrow_mut<'cs>(
- &self,
- _ctxt: &'cs mut CriticalSection,
- ) -> &'cs mut T {
- unsafe { &mut *self.data.get() }
- }
+/// A hardware peripheral as a resource
+pub struct Peripheral<P, CEILING>
+where
+ P: 'static,
+{
+ peripheral: cortex_m::peripheral::Peripheral<P>,
+ _ceiling: PhantomData<CEILING>,
}
-#[cfg(not(thumbv6m))]
-impl<T, C> Resource<T, C>
+impl<P, C> Peripheral<P, C>
where
C: Ceiling,
{
- /// Borrows the resource without locking
- ///
- /// NOTE The system ceiling must be higher than this resource ceiling
- pub fn borrow<'l, SC>(&'static self, _system_ceiling: &'l SC) -> &'l T
- where
- SC: HigherThan<C>,
- {
- unsafe { &*self.data.get() }
- }
-
- /// Returns an immutable reference to the inner data without locking
- ///
- /// # Safety
- ///
- /// You must
- ///
- /// - Preserve the "reference" rules. Don't create an immutable reference if
- /// the current task already owns a mutable reference to the data.
- ///
- /// - adhere to the Stack Resource Policy. You can
- /// - Access the resource from the highest priority task.
- /// - Access the resource from within a critical section that sets the
- /// system ceiling to `C`.
- pub unsafe fn get(&'static self) -> &'static T {
- get_check(C::ceiling());
-
- &*self.data.get()
- }
-
- /// Returns a mutable reference to the inner data without locking
+ /// Assigns a ceiling `C` to the `peripheral`
///
/// # Safety
///
- /// You must
- ///
- /// - Preserve the "reference" rules. Don't create a mutable reference if
- /// the current task already owns a reference to the data.
- ///
- /// - adhere to the Stack Resource Policy. You can
- /// - Access the resource from the highest priority task.
- /// - Access the resource from within a critical section that sets the
- /// system ceiling to `C`.
- pub unsafe fn get_mut(&'static self) -> &'static mut T {
- get_check(C::ceiling());
-
- &mut *self.data.get()
- }
-
- /// Locks the resource, preventing tasks with priority lower than `Ceiling`
- /// from preempting the current task
- pub fn lock<F, R, Ctxt>(&'static self, _ctxt: &Ctxt, f: F) -> R
- where
- F: FnOnce(&T, C) -> R,
- Ctxt: Context,
- {
- unsafe { lock(f, self.data.get(), C::hw_ceiling()) }
+ /// You MUST not create two resources that point to the same peripheral
+ pub const unsafe fn new(peripheral: cortex_m::peripheral::Peripheral<P>,)
+ -> Self {
+ Peripheral {
+ _ceiling: PhantomData,
+ peripheral: peripheral,
+ }
}
+}
- /// Mutably locks the resource, preventing tasks with priority lower than
- /// `Ceiling` from preempting the current task
- pub fn lock_mut<F, R, Ctxt>(&'static self, _ctxt: &mut Ctxt, f: F) -> R
+impl<Periph, CEILING> Peripheral<Periph, C<CEILING>>
+where
+ C<CEILING>: Ceiling,
+{
+ /// See [Resource.borrow](./struct.Resource.html#method.borrow)
+ pub fn borrow<'cs, SCEILING>(
+ &'static self,
+ _system_ceiling: &'cs C<SCEILING>,
+ ) -> &'cs Periph
where
- F: FnOnce(&mut T, C) -> R,
- Ctxt: Context,
+ SCEILING: GreaterThanOrEqual<CEILING>,
{
- unsafe { lock_mut(f, self.data.get(), C::hw_ceiling()) }
+ unsafe { &*self.peripheral.get() }
}
-}
-impl<T> Resource<T, C0> {
- /// Borrows the resource without locking
- pub fn borrow<'ctxt, Ctxt>(&'static self, _ctxt: &'ctxt Ctxt) -> &'ctxt T
+ /// See [Resource.claim](./struct.Resource.html#method.claim)
+ pub fn claim<'task, PRIORITY>(
+ &'static self,
+ _priority: &'task P<PRIORITY>,
+ ) -> &'task Periph
where
- Ctxt: Context,
+ CEILING: Cmp<PRIORITY, Output = Equal>,
+ P<PRIORITY>: Priority,
{
- unsafe { &*self.data.get() }
+ unsafe { &*self.peripheral.get() }
}
- /// Mutably borrows the resource without locking
- pub fn borrow_mut<'ctxt, Ctxt>(
+ /// See [Resource.lock](./struct.Resource.html#method.lock)
+ pub fn lock<R, PRIORITY, F>(
&'static self,
- _ctxt: &'ctxt mut Ctxt,
- ) -> &'ctxt mut T
+ _priority: &P<PRIORITY>,
+ f: F,
+ ) -> R
where
- Ctxt: Context,
+ F: FnOnce(&Periph, C<CEILING>) -> R,
+ C<CEILING>: Ceiling,
+ CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less>
+ + Level,
+ P<PRIORITY>: Priority,
{
- unsafe { &mut *self.data.get() }
+ unsafe {
+ let old_basepri = basepri::read();
+ basepri_max::write(<CEILING>::hw());
+ barrier!();
+ let ret = f(
+ &*self.peripheral.get(),
+ C {
+ _0: (),
+ _marker: PhantomData,
+ },
+ );
+ barrier!();
+ basepri::write(old_basepri);
+ ret
+ }
}
}
-unsafe impl<T, Ceiling> Sync for Resource<T, Ceiling> {}
+unsafe impl<T, C> Sync for Peripheral<T, C>
+where
+ C: Ceiling,
+{
+}
+
+/// Requests the execution of the task `task`
+pub fn request<T>(task: T)
+where
+ T: Nr,
+{
+ let nvic = unsafe { &*NVIC.get() };
+
+ match () {
+ #[cfg(debug_assertions)]
+ () => {
+ let task = unsafe { core::ptr::read(&task) };
+ // NOTE(safe) atomic read
+ assert!(!nvic.is_pending(task),
+ "Task is already in the pending state");
+ }
+ #[cfg(not(debug_assertions))]
+ () => {}
+ }
-/// Maps a hardware priority to a logical priority
-#[cfg(not(thumbv6m))]
-fn hardware(priority: u8) -> u8 {
- 16 - (priority >> (8 - PRIORITY_BITS))
+ // NOTE(safe) atomic write
+ nvic.set_pending(task);
}
-/// Turns a `logical` priority into a NVIC-style priority
-///
-/// With `logical` priorities, `2` has HIGHER priority than `1`.
-///
-/// With NVIC priorities, `32` has LOWER priority than `16`. (Also, NVIC
-/// priorities encode the actual priority in the highest bits of a byte so
-/// priorities like `1` and `2` aren't actually different)
-///
-/// NOTE Input `priority` must be in the range `[1, 16]` (inclusive)
-#[cfg(not(thumbv6m))]
-pub fn logical(priority: u8) -> u8 {
- assert!(priority >= 1 && priority <= 16);
+/// A type-level ceiling
+pub struct C<T> {
+ _0: (),
+ _marker: PhantomData<T>,
+}
- ((1 << PRIORITY_BITS) - priority) << (8 - PRIORITY_BITS)
+/// A type-level priority
+pub struct P<T> {
+ _0: (),
+ _marker: PhantomData<T>,
}
-/// Puts `interrupt` in the "to execute" queue
-///
-/// This function has no effect if the interrupt was already queued
-pub fn queue<I>(interrupt: I)
+impl<T> P<T>
where
- I: Nr,
+ T: Level,
{
- unsafe {
- // NOTE(safe) atomic write
- (*NVIC.get()).set_pending(interrupt)
+ pub fn hw() -> u8 {
+ T::hw()
}
}
-/// Fake ceiling, indicates that the resource is shared by cooperative tasks
-pub struct C0 {
- _0: (),
+/// A valid ceiling
+///
+/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
+pub unsafe trait Ceiling {}
+
+/// Type-level `>=` operator
+///
+/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
+pub unsafe trait GreaterThanOrEqual<RHS> {}
+
+/// Interrupt hardware level
+///
+/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
+pub unsafe trait Level {
+ fn hw() -> u8;
}
-/// A real ceiling
-// XXX this should be a "closed" trait
-#[cfg(not(thumbv6m))]
-pub unsafe trait Ceiling {
- /// Returns the logical ceiling as a number
- fn ceiling() -> u8;
+/// A valid priority level
+///
+/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
+pub unsafe trait Priority {}
- /// Returns the HW ceiling as a number
- fn hw_ceiling() -> u8;
+fn logical2hw(logical: u8) -> u8 {
+ ((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS)
}
-/// Usable as a ceiling
-// XXX this should be a "closed" trait
-pub unsafe trait CeilingLike {}
-
-/// This ceiling is lower than `C`
-// XXX this should be a "closed" trait
-#[cfg(not(thumbv6m))]
-pub unsafe trait HigherThan<C> {}
-
-#[cfg(not(thumbv6m))]
-macro_rules! ceiling {
- ($ceiling:ident, $logical:expr) => {
- /// Ceiling
- pub struct $ceiling {
- _0: ()
- }
+/// Priority 0, the lowest priority
+pub type P0 = P<::typenum::U0>;
+
+unsafe impl Priority for P0 {}
+
+/// Declares tasks
+#[macro_export]
+macro_rules! tasks {
+ ($krate:ident, {
+ $($task:ident: ($interrupt:ident, $Interrupt:ident, $P:ident),)*
+ }) => {
+ fn main() {
+ $crate::free(|_| {
+ init(unsafe { ::core::ptr::read(0x0 as *const $crate::CMAX )});
+ set_priorities();
+ enable_tasks();
+ });
+
+ idle(unsafe { ::core::ptr::read(0x0 as *const P0) });
+
+ fn set_priorities() {
+ // NOTE(safe) this function runs in an interrupt free context
+ let _nvic = unsafe { &*$crate::NVIC.get() };
+
+ $(
+ {
+ let hw = $crate::$P::hw();
+ if hw != 0 {
+ _nvic.set_priority
+ (::$krate::interrupt::Interrupt::$Interrupt,
+ hw,
+ );
+ }
+ }
+ )*
+
+ // TODO freeze the NVIC.IPR register using the MPU, if available
+ }
- unsafe impl CeilingLike for $ceiling {}
+ fn enable_tasks() {
+ // NOTE(safe) this function runs in an interrupt free context
+ let _nvic = unsafe { &*$crate::NVIC.get() };
- unsafe impl Ceiling for $ceiling {
- #[inline(always)]
- fn ceiling() -> u8 {
- $logical
+ $(
+ _nvic.enable(::$krate::interrupt::Interrupt::$Interrupt);
+ )*
}
- #[inline(always)]
- fn hw_ceiling() -> u8 {
- ((1 << PRIORITY_BITS) - $logical) << (8 - PRIORITY_BITS)
+ #[allow(dead_code)]
+ fn is_priority<P>()
+ where
+ P: $crate::Priority,
+ {
}
+
+ #[allow(dead_code)]
+ #[link_section = ".rodata.interrupts"]
+ #[used]
+ static INTERRUPTS: ::$krate::interrupt::Handlers =
+ ::$krate::interrupt::Handlers {
+ $(
+ $interrupt: {
+ extern "C" fn $task(
+ task: ::$krate::interrupt::$Interrupt
+ ) {
+ is_priority::<$crate::$P>();
+ ::$task(
+ task, unsafe {
+ ::core::ptr::read(0x0 as *const $crate::$P)
+ }
+ )
+ }
+
+ $task
+ },
+ )*
+ ..::$krate::interrupt::DEFAULT_HANDLERS
+ };
}
}
}
-#[cfg(thumbv6m)]
-macro_rules! ceiling {
- ($($tt:tt)*) => {};
-}
-
-ceiling!(C1, 1);
-ceiling!(C2, 2);
-ceiling!(C3, 3);
-ceiling!(C4, 4);
-ceiling!(C5, 5);
-ceiling!(C6, 6);
-ceiling!(C7, 7);
-ceiling!(C8, 8);
-ceiling!(C9, 9);
-ceiling!(C10, 10);
-ceiling!(C11, 11);
-ceiling!(C12, 12);
-ceiling!(C13, 13);
-ceiling!(C14, 14);
-ceiling!(C15, 15);
-
-#[cfg(not(thumbv6m))]
-macro_rules! higher_than {
- () => {};
- ($lowest:ident, $($higher:ident,)*) => {
- $(
- unsafe impl HigherThan<$lowest> for $higher {}
- )*
-
- higher_than!($($higher,)*);
- };
-}
-
-#[cfg(thumbv6m)]
-macro_rules! higher_than {
- ($($tt:tt)*) => {};
-}
-
-higher_than! {
- C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15,
-}
-
-unsafe impl CeilingLike for C0 {}
+include!(concat!(env!("OUT_DIR"), "/prio.rs"));
diff --git a/tests/cfail.rs b/tests/cfail.rs
new file mode 100644
index 0000000..44c982c
--- /dev/null
+++ b/tests/cfail.rs
@@ -0,0 +1,16 @@
+extern crate compiletest_rs as compiletest;
+
+use std::path::PathBuf;
+
+use compiletest::common::Mode;
+
+#[test]
+fn cfail() {
+ let mut config = compiletest::default_config();
+ config.mode = Mode::CompileFail;
+ config.src_base = PathBuf::from(format!("tests/cfail"));
+ config.target_rustcflags =
+ Some("-L target/debug -L target/debug/deps ".to_string());
+
+ compiletest::run_tests(&config);
+}
diff --git a/tests/cfail/borrow.rs b/tests/cfail/borrow.rs
new file mode 100644
index 0000000..fc8638f
--- /dev/null
+++ b/tests/cfail/borrow.rs
@@ -0,0 +1,22 @@
+extern crate cortex_m_srp;
+
+use cortex_m_srp::{C2, C3, C4, P1, Resource};
+
+static R1: Resource<i32, C3> = Resource::new(0);
+static R2: Resource<i32, C2> = Resource::new(0);
+static R3: Resource<i32, C3> = Resource::new(0);
+static R4: Resource<i32, C4> = Resource::new(0);
+
+fn j1(prio: P1) {
+ R1.lock(&prio, |r1, c3| {
+ // CAN borrow a resource with ceiling C when the system ceiling SC > C
+ let r2 = R2.borrow(&c3);
+
+ // CAN borrow a resource with ceiling C when the system ceiling SC == C
+ let r3 = R3.borrow(&c3);
+
+ // CAN'T borrow a resource with ceiling C when the system ceiling SC < C
+ let r4 = R4.borrow(&c3);
+ //~^ error
+ });
+}
diff --git a/tests/cfail/lock.rs b/tests/cfail/lock.rs
new file mode 100644
index 0000000..6b1a411
--- /dev/null
+++ b/tests/cfail/lock.rs
@@ -0,0 +1,41 @@
+extern crate cortex_m_srp;
+
+use cortex_m_srp::{C16, C2, P1, P16, P2, P3, Resource};
+
+static R1: Resource<i32, C2> = Resource::new(0);
+
+// You CAN'T lock a resource with ceiling C from a task with priority P if P > C
+fn j1(prio: P3) {
+ R1.lock(&prio, |_, _| {});
+ //~^ error
+}
+
+// DON'T lock a resource with ceiling equal to the task priority.
+// Instead use `claim`
+fn j2(prio: P2) {
+ R1.lock(&prio, |_, _| {});
+ //~^ error
+
+ // OK
+ let r1 = R1.claim(&prio);
+}
+
+// You CAN lock a resource with ceiling C from a task with priority P if C > P
+fn j3(prio: P1) {
+ // OK
+ R1.lock(&prio, |r1, _| {});
+}
+
+static R2: Resource<i32, C16> = Resource::new(0);
+
+// Tasks with priority less than P16 can't lock a resource with ceiling C16
+fn j4(prio: P1) {
+ R2.lock(&prio, |_, _| {});
+ //~^ error
+}
+
+// Only tasks with priority P16 can claim a resource with ceiling C16
+fn j5(prio: P16) {
+ // OK
+ let r2 = R2.claim(&prio);
+}