rtic/src/lib.rs

376 lines
9.7 KiB
Rust
Raw Normal View History

2017-04-14 07:33:07 +02:00
//! Stack Resource Policy
#![deny(missing_docs)]
#![deny(warnings)]
2017-03-05 06:26:14 +01:00
#![feature(asm)]
#![feature(const_fn)]
#![no_std]
extern crate cortex_m;
extern crate static_ref;
2017-04-10 05:42:17 +02:00
extern crate typenum;
2017-03-05 06:26:14 +01:00
2017-04-10 05:42:17 +02:00
use core::cell::UnsafeCell;
2017-04-12 22:27:48 +02:00
use core::marker::PhantomData;
use cortex_m::ctxt::Context;
2017-04-10 05:42:17 +02:00
use cortex_m::interrupt::Nr;
2017-04-12 06:15:05 +02:00
#[cfg(not(thumbv6m))]
2017-04-10 05:42:17 +02:00
use cortex_m::register::{basepri, basepri_max};
use static_ref::Ref;
2017-04-20 17:56:33 +02:00
use typenum::Unsigned;
2017-04-12 06:15:05 +02:00
#[cfg(not(thumbv6m))]
2017-04-20 17:56:33 +02:00
use typenum::{Cmp, Greater, Less};
pub use cortex_m::ctxt::Local;
2017-04-14 17:05:24 +02:00
pub use cortex_m::asm::{bkpt, wfi};
2017-04-10 05:42:17 +02:00
#[doc(hidden)]
pub use cortex_m::peripheral::NVIC;
2017-03-05 06:26:14 +01:00
2017-04-10 05:42:17 +02:00
macro_rules! barrier {
() => {
asm!(""
:
:
: "memory"
: "volatile");
}
}
2017-04-10 05:42:17 +02:00
/// A resource
pub struct Resource<T, CEILING> {
_ceiling: PhantomData<CEILING>,
data: UnsafeCell<T>,
2017-03-05 06:26:14 +01:00
}
2017-04-10 05:42:17 +02:00
impl<T, C> Resource<T, C> {
/// Creates a new resource with ceiling `C`
pub const fn new(data: T) -> Self
2017-04-17 18:59:56 +02:00
where
C: Ceiling,
2017-04-10 05:42:17 +02:00
{
Resource {
_ceiling: PhantomData,
data: UnsafeCell::new(data),
2017-03-05 06:26:14 +01:00
}
}
2017-03-10 05:59:50 +01:00
}
2017-03-05 06:26:14 +01:00
2017-04-14 07:33:07 +02:00
impl<T, CEILING> Resource<T, C<CEILING>> {
2017-04-10 05:42:17 +02:00
/// Borrows the resource for the duration of another resource's critical
/// section
///
2017-04-10 05:42:17 +02:00
/// This operation is zero cost and doesn't impose any additional blocking
2017-04-17 18:59:56 +02:00
pub fn borrow<'cs, PRIORITY, SCEILING>(
&'static self,
_priority: &P<PRIORITY>,
_system_ceiling: &'cs C<SCEILING>,
) -> Ref<'cs, T>
where
SCEILING: GreaterThanOrEqual<CEILING>,
CEILING: GreaterThanOrEqual<PRIORITY>,
2017-03-05 06:26:14 +01:00
{
unsafe { Ref::new(&*self.data.get()) }
2017-04-04 23:37:01 +02:00
}
2017-04-10 05:42:17 +02:00
/// Locks the resource for the duration of the critical section `f`
2017-03-08 14:22:31 +01:00
///
2017-04-10 05:42:17 +02:00
/// 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.
2017-04-12 22:27:48 +02:00
///
/// Within this critical section, resources with ceiling equal to or smaller
/// than `CEILING` can be borrowed at zero cost. See
/// [Resource.borrow](struct.Resource.html#method.borrow).
2017-04-12 06:15:05 +02:00
#[cfg(not(thumbv6m))]
pub fn lock<R, PRIORITY, F>(&'static self, _priority: &P<PRIORITY>, f: F) -> R
where F: FnOnce(Ref<T>, &C<CEILING>) -> R,
CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less> + Level
2017-03-10 05:59:50 +01:00
{
2017-04-10 05:42:17 +02:00
unsafe {
let old_basepri = basepri::read();
basepri_max::write(<CEILING>::hw());
barrier!();
2017-04-17 18:59:56 +02:00
let ret =
f(Ref::new(&*self.data.get()), &C { _marker: PhantomData });
2017-04-10 05:42:17 +02:00
barrier!();
basepri::write(old_basepri);
ret
2017-03-05 06:26:14 +01:00
}
}
2017-04-10 05:42:17 +02:00
}
2017-04-17 18:59:56 +02:00
unsafe impl<T, C> Sync for Resource<T, C>
where
C: Ceiling,
{
}
2017-04-10 05:42:17 +02:00
/// A hardware peripheral as a resource
pub struct Peripheral<P, CEILING>
2017-04-17 18:59:56 +02:00
where
P: 'static,
2017-04-10 05:42:17 +02:00
{
peripheral: cortex_m::peripheral::Peripheral<P>,
_ceiling: PhantomData<CEILING>,
2017-03-10 05:59:50 +01:00
}
2017-03-05 06:26:14 +01:00
2017-04-10 05:42:17 +02:00
impl<P, C> Peripheral<P, C>
2017-04-17 18:59:56 +02:00
where
C: Ceiling,
2017-03-10 05:59:50 +01:00
{
2017-04-10 05:42:17 +02:00
/// Assigns a ceiling `C` to the `peripheral`
2017-04-04 23:37:01 +02:00
///
/// # Safety
///
2017-04-10 05:42:17 +02:00
/// You MUST not create two resources that point to the same peripheral
2017-04-17 18:59:56 +02:00
pub const unsafe fn new(peripheral: cortex_m::peripheral::Peripheral<P>,)
-> Self {
2017-04-10 05:42:17 +02:00
Peripheral {
_ceiling: PhantomData,
peripheral: peripheral,
}
}
2017-04-10 05:42:17 +02:00
}
2017-04-14 07:33:07 +02:00
impl<Periph, CEILING> Peripheral<Periph, C<CEILING>> {
2017-04-10 05:42:17 +02:00
/// See [Resource.borrow](./struct.Resource.html#method.borrow)
2017-04-17 18:59:56 +02:00
pub fn borrow<'cs, PRIORITY, SCEILING>(
&'static self,
_priority: &P<PRIORITY>,
_system_ceiling: &'cs C<SCEILING>,
) -> Ref<'cs, Periph>
where
SCEILING: GreaterThanOrEqual<CEILING>,
CEILING: GreaterThanOrEqual<PRIORITY>,
{
unsafe { Ref::new(&*self.peripheral.get()) }
}
2017-03-05 06:26:14 +01:00
2017-04-10 05:42:17 +02:00
/// See [Resource.lock](./struct.Resource.html#method.lock)
2017-04-12 06:15:05 +02:00
#[cfg(not(thumbv6m))]
pub fn lock<R, PRIORITY, F>(&'static self, _priority: &P<PRIORITY>, f: F) -> R
where F: FnOnce(Ref<Periph>, &C<CEILING>) -> R,
CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less> + Level
{
2017-04-10 05:42:17 +02:00
unsafe {
let old_basepri = basepri::read();
basepri_max::write(<CEILING>::hw());
barrier!();
2017-04-17 18:59:56 +02:00
let ret = f(
Ref::new(&*self.peripheral.get()),
&C { _marker: PhantomData },
2017-04-17 18:59:56 +02:00
);
2017-04-10 05:42:17 +02:00
barrier!();
basepri::write(old_basepri);
ret
}
}
2017-03-05 06:26:14 +01:00
}
2017-04-17 18:59:56 +02:00
unsafe impl<T, C> Sync for Peripheral<T, C>
where
C: Ceiling,
{
}
2017-04-10 05:42:17 +02:00
/// A global critical section
///
/// No task can preempt this critical section
pub fn critical<R, F>(f: F) -> R
2017-04-17 18:59:56 +02:00
where
F: FnOnce(&CMAX) -> R,
{
let primask = ::cortex_m::register::primask::read();
::cortex_m::interrupt::disable();
let r = f(&C { _marker: PhantomData });
// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
if primask.is_active() {
::cortex_m::interrupt::enable();
}
r
}
2017-04-14 07:33:07 +02:00
/// Requests the execution of a `task`
pub fn request<T, P>(_task: fn(T, P))
2017-04-17 18:59:56 +02:00
where
T: Context + Nr,
P: Priority,
2017-04-10 05:42:17 +02:00
{
let nvic = unsafe { &*NVIC.get() };
match () {
#[cfg(debug_assertions)]
() => {
// NOTE(safe) zero sized type
let task = unsafe { core::ptr::read(0x0 as *const T) };
2017-04-10 05:42:17 +02:00
// NOTE(safe) atomic read
assert!(!nvic.is_pending(task),
"Task is already in the pending state");
}
#[cfg(not(debug_assertions))]
() => {}
}
2017-03-05 06:26:14 +01:00
// NOTE(safe) zero sized type
let task = unsafe { core::ptr::read(0x0 as *const T) };
2017-04-10 05:42:17 +02:00
// NOTE(safe) atomic write
nvic.set_pending(task);
}
2017-04-10 05:42:17 +02:00
/// A type-level ceiling
pub struct C<T> {
_marker: PhantomData<T>,
}
2017-04-10 05:42:17 +02:00
/// A type-level priority
pub struct P<T> {
_marker: PhantomData<T>,
2017-03-05 06:26:14 +01:00
}
2017-03-10 05:59:50 +01:00
2017-04-10 05:42:17 +02:00
impl<T> P<T>
2017-04-17 18:59:56 +02:00
where
T: Level,
2017-04-04 23:37:01 +02:00
{
2017-04-14 07:33:07 +02:00
#[doc(hidden)]
2017-04-10 05:42:17 +02:00
pub fn hw() -> u8 {
T::hw()
2017-04-04 22:36:23 +02:00
}
}
2017-04-14 07:33:07 +02:00
/// A valid resource ceiling
2017-04-10 05:42:17 +02:00
///
/// 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 {
2017-04-14 07:33:07 +02:00
/// Interrupt hardware level
2017-04-10 05:42:17 +02:00
fn hw() -> u8;
2017-03-10 05:59:50 +01:00
}
2017-04-14 07:33:07 +02:00
/// A valid task priority
2017-04-10 05:42:17 +02:00
///
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
pub unsafe trait Priority {}
/// Convert a logical priority to a shifted hardware prio
/// as used by the NVIC and basepri registers
/// Notice, wrapping causes a panic due to u8
pub fn logical2hw(logical: u8) -> u8 {
2017-04-10 05:42:17 +02:00
((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS)
2017-03-10 05:59:50 +01:00
}
/// Convert a shifted hardware prio to a logical priority
/// as used by the NVIC and basepri registers
/// Notice, wrapping causes a panic due to u8
pub fn hw2logical(hw: u8) -> u8 {
(1 << PRIORITY_BITS) - (hw >> (8 - PRIORITY_BITS))
}
2017-04-10 05:42:17 +02:00
/// Priority 0, the lowest priority
pub type P0 = P<::typenum::U0>;
/// Declares tasks
#[macro_export]
macro_rules! tasks {
($krate:ident, {
$($task:ident: ($Interrupt:ident, $P:ident),)*
2017-04-10 05:42:17 +02:00
}) => {
fn main() {
$crate::critical(|cmax| {
fn signature(_: fn($crate::P0, &$crate::CMAX)) {}
2017-04-14 07:39:12 +02:00
signature(init);
let p0 = unsafe { ::core::ptr::read(0x0 as *const _) };
init(p0, cmax);
2017-04-10 05:42:17 +02:00
set_priorities();
enable_tasks();
});
2017-04-14 07:39:12 +02:00
fn signature(_: fn($crate::P0) -> !) {}
signature(idle);
let p0 = unsafe { ::core::ptr::read(0x0 as *const _) };
idle(p0);
2017-04-10 05:42:17 +02:00
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 {
unsafe {
_nvic.set_priority
(::$krate::interrupt::Interrupt::$Interrupt,
hw,
);
}
2017-04-10 05:42:17 +02:00
}
}
)*
// TODO freeze the NVIC.IPR register using the MPU, if available
}
2017-04-10 05:42:17 +02:00
fn enable_tasks() {
// NOTE(safe) this function runs in an interrupt free context
let _nvic = unsafe { &*$crate::NVIC.get() };
2017-04-10 05:42:17 +02:00
$(
_nvic.enable(::$krate::interrupt::Interrupt::$Interrupt);
)*
}
2017-04-10 05:42:17 +02:00
#[allow(dead_code)]
fn is_priority<P>()
where
P: $crate::Priority,
{
}
2017-04-10 05:42:17 +02:00
#[allow(dead_code)]
#[link_section = ".rodata.interrupts"]
#[used]
static INTERRUPTS: ::$krate::interrupt::Handlers =
::$krate::interrupt::Handlers {
$(
$Interrupt: {
2017-04-10 05:42:17 +02:00
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
};
}
}
}
2017-04-10 05:42:17 +02:00
include!(concat!(env!("OUT_DIR"), "/prio.rs"));