add debug assertions to lock and get

This commit is contained in:
Jorge Aparicio 2017-04-03 16:18:26 -05:00
parent f3b73bcc12
commit 4c1b6c8293

View file

@ -12,7 +12,7 @@ extern crate cortex_m;
use cortex_m::ctxt::Context; use cortex_m::ctxt::Context;
use cortex_m::interrupt::CriticalSection; 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 cortex_m::register::{basepri, basepri_max};
use core::cell::UnsafeCell; use core::cell::UnsafeCell;
@ -23,6 +23,34 @@ use core::ptr;
// considered when determining priorities. // considered when determining priorities.
const PRIORITY_BITS: u8 = 4; 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? // XXX Do we need memory / instruction / compiler barriers here?
#[inline(always)] #[inline(always)]
unsafe fn lock<T, R, C, F>(f: F, res: *const T, ceiling: u8) -> R unsafe fn lock<T, R, C, F>(f: F, res: *const T, ceiling: u8) -> R
@ -30,6 +58,7 @@ where
C: Ceiling, C: Ceiling,
F: FnOnce(&T, C) -> R, F: FnOnce(&T, C) -> R,
{ {
lock_check(ceiling);
let old_basepri = basepri::read(); let old_basepri = basepri::read();
basepri_max::write(ceiling); basepri_max::write(ceiling);
let ret = f(&*res, ptr::read(0 as *const _)); let ret = f(&*res, ptr::read(0 as *const _));
@ -44,6 +73,7 @@ where
C: Ceiling, C: Ceiling,
F: FnOnce(&mut T, C) -> R, F: FnOnce(&mut T, C) -> R,
{ {
lock_check(ceiling);
let old_basepri = basepri::read(); let old_basepri = basepri::read();
basepri_max::write(ceiling); basepri_max::write(ceiling);
let ret = f(&mut *res, ptr::read(0 as *const _)); let ret = f(&mut *res, ptr::read(0 as *const _));
@ -131,7 +161,7 @@ where
F: FnOnce(&P, C) -> R, F: FnOnce(&P, C) -> R,
Ctxt: Context, 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 /// Mutably locks the resource, preventing tasks with priority lower than
@ -141,7 +171,7 @@ where
F: FnOnce(&mut P, C) -> R, F: FnOnce(&mut P, C) -> R,
Ctxt: Context, 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() } unsafe { &mut *self.data.get() }
} }
/// Returns a mutable pointer to the wrapped value /// Returns a mutable reference to the wrapped value
pub fn get(&self) -> *mut T { pub unsafe fn get(&self) -> &'static mut T {
self.data.get() 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` /// Locks the resource, preventing tasks with priority lower than `Ceiling`
@ -241,7 +305,7 @@ where
F: FnOnce(&T, C) -> R, F: FnOnce(&T, C) -> R,
Ctxt: Context, 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 /// Mutably locks the resource, preventing tasks with priority lower than
@ -251,7 +315,7 @@ where
F: FnOnce(&mut T, C) -> R, F: FnOnce(&mut T, C) -> R,
Ctxt: Context, 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<T> Resource<T, C0> {
unsafe impl<T, Ceiling> Sync for Resource<T, Ceiling> {} unsafe impl<T, Ceiling> Sync for Resource<T, Ceiling> {}
/// 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 /// Turns a `logical` priority into a NVIC-style priority
/// ///
/// With `logical` priorities, `2` has HIGHER priority than `1`. /// With `logical` priorities, `2` has HIGHER priority than `1`.
@ -298,86 +367,14 @@ pub struct C0 {
_0: (), _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 /// A real ceiling
// XXX this should be a "closed" trait // XXX this should be a "closed" trait
pub unsafe trait Ceiling { pub unsafe trait Ceiling {
/// Returns the ceiling as a number /// Returns the logical ceiling as a number
fn ceiling() -> u8; fn ceiling() -> u8;
/// Returns the HW ceiling as a number
fn hw_ceiling() -> u8;
} }
/// Usable as a ceiling /// Usable as a ceiling
@ -388,6 +385,45 @@ pub unsafe trait CeilingLike {}
// XXX this should be a "closed" trait // XXX this should be a "closed" trait
pub unsafe trait HigherThan<C> {} pub unsafe trait HigherThan<C> {}
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<C1> for C2 {} unsafe impl HigherThan<C1> for C2 {}
unsafe impl HigherThan<C1> for C3 {} unsafe impl HigherThan<C1> for C3 {}
unsafe impl HigherThan<C1> for C4 {} unsafe impl HigherThan<C1> for C4 {}
@ -507,124 +543,4 @@ unsafe impl HigherThan<C13> for C15 {}
unsafe impl HigherThan<C14> for C15 {} unsafe impl HigherThan<C14> 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 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 {}