diff --git a/src/lib.rs b/src/lib.rs index e06eee813f..898bccc47d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ extern crate cortex_m; use cortex_m::ctxt::Context; use cortex_m::interrupt::CriticalSection; -use cortex_m::peripheral::Peripheral; +use cortex_m::peripheral::{Peripheral, NVIC, SCB}; use cortex_m::register::{basepri, basepri_max}; use core::cell::UnsafeCell; @@ -23,6 +23,34 @@ use core::ptr; // considered when determining priorities. const PRIORITY_BITS: u8 = 4; +/// Logical task priority +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(debug_assertions)] +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, + ); + } +} + +#[cfg(not(debug_assertions))] +unsafe fn lock_check(_ceiling: u8) {} + // XXX Do we need memory / instruction / compiler barriers here? #[inline(always)] unsafe fn lock(f: F, res: *const T, ceiling: u8) -> R @@ -30,6 +58,7 @@ where C: Ceiling, F: FnOnce(&T, C) -> R, { + lock_check(ceiling); let old_basepri = basepri::read(); basepri_max::write(ceiling); let ret = f(&*res, ptr::read(0 as *const _)); @@ -44,6 +73,7 @@ where C: Ceiling, F: FnOnce(&mut T, C) -> R, { + lock_check(ceiling); let old_basepri = basepri::read(); basepri_max::write(ceiling); let ret = f(&mut *res, ptr::read(0 as *const _)); @@ -131,7 +161,7 @@ where F: FnOnce(&P, C) -> R, Ctxt: Context, { - unsafe { lock(f, self.peripheral.get(), C::ceiling()) } + unsafe { lock(f, self.peripheral.get(), C::hw_ceiling()) } } /// Mutably locks the resource, preventing tasks with priority lower than @@ -141,7 +171,7 @@ where F: FnOnce(&mut P, C) -> R, Ctxt: Context, { - unsafe { lock_mut(f, self.peripheral.get(), C::ceiling()) } + unsafe { lock_mut(f, self.peripheral.get(), C::hw_ceiling()) } } } @@ -229,9 +259,43 @@ where unsafe { &mut *self.data.get() } } - /// Returns a mutable pointer to the wrapped value - pub fn get(&self) -> *mut T { - self.data.get() + /// Returns a mutable reference to the wrapped value + pub unsafe fn get(&self) -> &'static mut T { + match () { + #[cfg(debug_assertions)] + () => { + let task_priority = task_priority(); + let system_ceiling = + hardware(cortex_m::register::basepri::read()); + let resource_ceiling = C::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))] + () => {} + } + + &mut *self.data.get() } /// Locks the resource, preventing tasks with priority lower than `Ceiling` @@ -241,7 +305,7 @@ where F: FnOnce(&T, C) -> R, Ctxt: Context, { - unsafe { lock(f, self.data.get(), C::ceiling()) } + unsafe { lock(f, self.data.get(), C::hw_ceiling()) } } /// Mutably locks the resource, preventing tasks with priority lower than @@ -251,7 +315,7 @@ where F: FnOnce(&mut T, C) -> R, Ctxt: Context, { - unsafe { lock_mut(f, self.data.get(), C::ceiling()) } + unsafe { lock_mut(f, self.data.get(), C::hw_ceiling()) } } } @@ -278,6 +342,11 @@ impl Resource { unsafe impl Sync for Resource {} +/// Maps a hardware priority to a logical priority +fn hardware(priority: u8) -> u8 { + 16 - (priority >> (8 - PRIORITY_BITS)) +} + /// Turns a `logical` priority into a NVIC-style priority /// /// With `logical` priorities, `2` has HIGHER priority than `1`. @@ -298,86 +367,14 @@ pub struct C0 { _0: (), } -/// Ceiling -pub struct C1 { - _0: (), -} - -/// Ceiling -pub struct C2 { - _0: (), -} - -/// Ceiling -pub struct C3 { - _0: (), -} - -/// Ceiling -pub struct C4 { - _0: (), -} - -/// Ceiling -pub struct C5 { - _0: (), -} - -/// Ceiling -pub struct C6 { - _0: (), -} - -/// Ceiling -pub struct C7 { - _0: (), -} - -/// Ceiling -pub struct C8 { - _0: (), -} - -/// Ceiling -pub struct C9 { - _0: (), -} - -/// Ceiling -pub struct C10 { - _0: (), -} - -/// Ceiling -pub struct C11 { - _0: (), -} - -/// Ceiling -pub struct C12 { - _0: (), -} - -/// Ceiling -pub struct C13 { - _0: (), -} - -/// Ceiling -pub struct C14 { - _0: (), -} - -/// Ceiling -pub struct C15 { - _0: (), -} - /// A real ceiling // XXX this should be a "closed" trait pub unsafe trait Ceiling { - /// Returns the ceiling as a number + /// Returns the logical ceiling as a number fn ceiling() -> u8; + + /// Returns the HW ceiling as a number + fn hw_ceiling() -> u8; } /// Usable as a ceiling @@ -388,6 +385,45 @@ pub unsafe trait CeilingLike {} // XXX this should be a "closed" trait pub unsafe trait HigherThan {} +macro_rules! ceiling { + ($ceiling:ident, $logical:expr) => { + /// Ceiling + pub struct $ceiling { + _0: () + } + + unsafe impl CeilingLike for $ceiling {} + + unsafe impl Ceiling for $ceiling { + #[inline(always)] + fn ceiling() -> u8 { + $logical + } + + #[inline(always)] + fn hw_ceiling() -> u8 { + ((1 << PRIORITY_BITS) - $logical) << (8 - PRIORITY_BITS) + } + } + } +} + +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); + unsafe impl HigherThan for C2 {} unsafe impl HigherThan for C3 {} unsafe impl HigherThan for C4 {} @@ -507,124 +543,4 @@ unsafe impl HigherThan for C15 {} unsafe impl HigherThan for C15 {} -unsafe impl Ceiling for C1 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 1) << 4 - } -} - -unsafe impl Ceiling for C2 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 2) << 4 - } -} - -unsafe impl Ceiling for C3 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 3) << 4 - } -} - -unsafe impl Ceiling for C4 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 4) << 4 - } -} - -unsafe impl Ceiling for C5 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 5) << 4 - } -} - -unsafe impl Ceiling for C6 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 6) << 4 - } -} - -unsafe impl Ceiling for C7 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 7) << 4 - } -} - -unsafe impl Ceiling for C8 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 8) << 4 - } -} - -unsafe impl Ceiling for C9 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 9) << 4 - } -} - -unsafe impl Ceiling for C10 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 10) << 4 - } -} - -unsafe impl Ceiling for C11 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 11) << 4 - } -} - -unsafe impl Ceiling for C12 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 12) << 4 - } -} - -unsafe impl Ceiling for C13 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 13) << 4 - } -} - -unsafe impl Ceiling for C14 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 14) << 4 - } -} - -unsafe impl Ceiling for C15 { - #[inline(always)] - fn ceiling() -> u8 { - ((1 << 4) - 15) << 4 - } -} - unsafe impl CeilingLike for C0 {} -unsafe impl CeilingLike for C1 {} -unsafe impl CeilingLike for C2 {} -unsafe impl CeilingLike for C3 {} -unsafe impl CeilingLike for C4 {} -unsafe impl CeilingLike for C5 {} -unsafe impl CeilingLike for C6 {} -unsafe impl CeilingLike for C7 {} -unsafe impl CeilingLike for C8 {} -unsafe impl CeilingLike for C9 {} -unsafe impl CeilingLike for C10 {} -unsafe impl CeilingLike for C11 {} -unsafe impl CeilingLike for C12 {} -unsafe impl CeilingLike for C13 {} -unsafe impl CeilingLike for C14 {} -unsafe impl CeilingLike for C15 {}