2017-03-05 06:26:14 +01:00
|
|
|
//! Stack Resource Policy for Cortex-M processors
|
|
|
|
//!
|
|
|
|
//! NOTE ARMv6-M is not supported at the moment.
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
#![deny(missing_docs)]
|
|
|
|
#![deny(warnings)]
|
2017-03-05 06:26:14 +01:00
|
|
|
#![feature(asm)]
|
|
|
|
#![feature(const_fn)]
|
|
|
|
#![no_std]
|
|
|
|
|
|
|
|
// NOTE Only the 4 highest bits of the priority byte (BASEPRI / NVIC.IPR) are
|
|
|
|
// considered when determining priorities.
|
|
|
|
const PRIORITY_BITS: u8 = 4;
|
|
|
|
|
|
|
|
extern crate cortex_m;
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
use cortex_m::interrupt::CsCtxt;
|
|
|
|
use cortex_m::peripheral::Peripheral;
|
2017-03-05 06:26:14 +01:00
|
|
|
use cortex_m::register::{basepri, basepri_max};
|
|
|
|
|
|
|
|
use core::cell::UnsafeCell;
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
// XXX Do we need memory / instruction / compiler barriers here?
|
2017-03-05 06:26:14 +01:00
|
|
|
#[inline(always)]
|
2017-03-08 14:10:58 +01:00
|
|
|
unsafe fn claim<T, R, F>(f: F, res: *const T, ceiling: u8) -> R
|
|
|
|
where F: FnOnce(&T) -> R
|
|
|
|
{
|
|
|
|
let old_basepri = basepri::read();
|
|
|
|
basepri_max::write(ceiling);
|
|
|
|
let ret = f(&*res);
|
|
|
|
basepri::write(old_basepri);
|
|
|
|
ret
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
/// A peripheral as a resource
|
|
|
|
pub struct ResourceP<T>
|
2017-03-05 06:26:14 +01:00
|
|
|
where T: 'static
|
|
|
|
{
|
2017-03-08 14:10:58 +01:00
|
|
|
peripheral: Peripheral<T>,
|
|
|
|
// NOTE NVIC-style priority ceiling
|
2017-03-05 06:26:14 +01:00
|
|
|
ceiling: u8,
|
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
impl<T> ResourceP<T> {
|
|
|
|
/// Wraps a `peripheral` into a `Resource`
|
|
|
|
///
|
|
|
|
/// NOTE `ceiling` must be in the range `[1, 15]` (inclusive)
|
|
|
|
///
|
|
|
|
/// # Unsafety
|
|
|
|
///
|
2017-03-08 14:22:31 +01:00
|
|
|
/// - Must not create two resources that point to the same peripheral
|
|
|
|
/// - The `ceiling` must be picked to prevent two or more concurrent tasks
|
|
|
|
/// from claiming this resource (preemptively)
|
2017-03-08 14:10:58 +01:00
|
|
|
pub const unsafe fn new(p: Peripheral<T>, ceiling: u8) -> Self {
|
|
|
|
ResourceP {
|
|
|
|
peripheral: p,
|
|
|
|
// NOTE elements 1 and 2 of the tuple are a poor man's const context
|
|
|
|
// range checker
|
|
|
|
ceiling: (priority(ceiling), ceiling - 1, ceiling + 240).0,
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
/// Claims the resource, blocking tasks with priority lower than `ceiling`
|
|
|
|
pub fn claim<R, F>(&'static self, f: F) -> R
|
2017-03-05 06:26:14 +01:00
|
|
|
where F: FnOnce(&T) -> R
|
|
|
|
{
|
2017-03-08 14:10:58 +01:00
|
|
|
unsafe { claim(f, self.peripheral.get(), self.ceiling) }
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
/// Borrows the resource for the duration of a critical section
|
|
|
|
pub fn borrow<'a>(&'static self, _ctxt: &'a CsCtxt) -> &'a T {
|
|
|
|
unsafe { &*self.peripheral.get() }
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
unsafe impl<T> Sync for ResourceP<T> {}
|
|
|
|
|
|
|
|
/// A resource
|
2017-03-05 06:26:14 +01:00
|
|
|
pub struct Resource<T> {
|
2017-03-08 14:10:58 +01:00
|
|
|
// NOTE NVIC-style priority ceiling
|
2017-03-05 06:26:14 +01:00
|
|
|
ceiling: u8,
|
|
|
|
data: UnsafeCell<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Resource<T> {
|
2017-03-08 14:10:58 +01:00
|
|
|
/// Initializes a resource
|
|
|
|
///
|
|
|
|
/// NOTE `ceiling` must be in the range `[1, 15]`
|
2017-03-08 14:22:31 +01:00
|
|
|
///
|
|
|
|
/// # Unsafety
|
|
|
|
///
|
|
|
|
/// - The `ceiling` must be picked to prevent two or more concurrent tasks
|
|
|
|
/// from claiming this resource (preemptively)
|
|
|
|
pub const unsafe fn new(data: T, ceiling: u8) -> Self {
|
2017-03-05 06:26:14 +01:00
|
|
|
Resource {
|
2017-03-08 14:10:58 +01:00
|
|
|
// NOTE elements 1 and 2 of the tuple are a poor man's const context
|
|
|
|
// range checker
|
|
|
|
ceiling: (priority(ceiling), ceiling - 1, ceiling + 240).0,
|
2017-03-05 06:26:14 +01:00
|
|
|
data: UnsafeCell::new(data),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
/// Claims the resource, blocking tasks with priority lower than `ceiling`
|
|
|
|
pub fn claim<F, R>(&'static self, f: F) -> R
|
2017-03-05 06:26:14 +01:00
|
|
|
where F: FnOnce(&T) -> R
|
|
|
|
{
|
2017-03-08 14:10:58 +01:00
|
|
|
unsafe { claim(f, self.data.get(), self.ceiling) }
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-08 14:10:58 +01:00
|
|
|
/// Borrows the resource for the duration of a critical section
|
|
|
|
pub fn borrow<'cs>(&self, _ctxt: &'cs CsCtxt) -> &'cs T {
|
|
|
|
unsafe { &*self.data.get() }
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl<T> Sync for Resource<T> {}
|
|
|
|
|
|
|
|
/// 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)
|
2017-03-08 14:10:58 +01:00
|
|
|
///
|
|
|
|
/// NOTE `logical` must be in the range `[1, 15]` (inclusive)
|
2017-03-05 17:36:05 +01:00
|
|
|
pub const fn priority(logical: u8) -> u8 {
|
2017-03-08 14:10:58 +01:00
|
|
|
// NOTE elements 1 and 2 of the tuple are a poor man's const context range
|
|
|
|
// checker
|
|
|
|
(((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS),
|
|
|
|
logical - 1,
|
|
|
|
logical + 240)
|
|
|
|
.0
|
|
|
|
|
2017-03-05 06:26:14 +01:00
|
|
|
}
|