#![deny(warnings)] #![feature(asm)] #![feature(const_fn)] #![no_std] extern crate cortex_m; extern crate typenum; use core::marker::PhantomData; use core::cell::UnsafeCell; use cortex_m::interrupt::Nr; use cortex_m::register::{basepri, basepri_max}; use typenum::{Cmp, Equal, Greater, Less, Unsigned}; pub use cortex_m::ctxt::{Context, Local}; #[doc(hidden)] pub use cortex_m::peripheral::NVIC; #[doc(hidden)] pub use cortex_m::interrupt::free; macro_rules! barrier { () => { asm!("" : : : "memory" : "volatile"); } } /// A resource pub struct Resource { _ceiling: PhantomData, data: UnsafeCell, } impl Resource { /// Creates a new resource with ceiling `C` pub const fn new(data: T) -> Self where C: Ceiling, { Resource { _ceiling: PhantomData, data: UnsafeCell::new(data), } } } impl Resource> where C: Ceiling, { /// Borrows the resource for the duration of another resource's critical /// section /// /// This operation is zero cost and doesn't impose any additional blocking pub fn borrow<'cs, SCEILING>( &'static self, _system_ceiling: &'cs C, ) -> &'cs T where SCEILING: GreaterThanOrEqual, { unsafe { &*self.data.get() } } /// Claims the resource at the task with highest priority /// /// This operation is zero cost and doesn't impose any additional blocking pub fn claim<'task, PRIORITY>( &'static self, _priority: &'task P, ) -> &'task T where CEILING: Cmp, P: Priority, { unsafe { &*self.data.get() } } /// Locks the resource for the duration of the critical section `f` /// /// 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( &'static self, _priority: &P, f: F, ) -> R where F: FnOnce(&T, C) -> R, C: Ceiling, CEILING: Cmp + Cmp + Level, P: Priority, { unsafe { let old_basepri = basepri::read(); basepri_max::write(::hw()); barrier!(); let ret = f( &*self.data.get(), C { _0: (), _marker: PhantomData, }, ); barrier!(); basepri::write(old_basepri); ret } } } unsafe impl Sync for Resource where CEILING: Ceiling, { } /// A hardware peripheral as a resource pub struct Peripheral where P: 'static, { peripheral: cortex_m::peripheral::Peripheral

, _ceiling: PhantomData, } impl Peripheral where C: Ceiling, { /// Assigns a ceiling `C` to the `peripheral` /// /// # Safety /// /// You MUST not create two resources that point to the same peripheral pub const unsafe fn new(peripheral: cortex_m::peripheral::Peripheral

,) -> Self { Peripheral { _ceiling: PhantomData, peripheral: peripheral, } } } impl Peripheral> where C: Ceiling, { /// See [Resource.borrow](./struct.Resource.html#method.borrow) pub fn borrow<'cs, SCEILING>( &'static self, _system_ceiling: &'cs C, ) -> &'cs Periph where SCEILING: GreaterThanOrEqual, { unsafe { &*self.peripheral.get() } } /// See [Resource.claim](./struct.Resource.html#method.claim) pub fn claim<'task, PRIORITY>( &'static self, _priority: &'task P, ) -> &'task Periph where CEILING: Cmp, P: Priority, { unsafe { &*self.peripheral.get() } } /// See [Resource.lock](./struct.Resource.html#method.lock) pub fn lock( &'static self, _priority: &P, f: F, ) -> R where F: FnOnce(&Periph, C) -> R, C: Ceiling, CEILING: Cmp + Cmp + Level, P: Priority, { unsafe { let old_basepri = basepri::read(); basepri_max::write(::hw()); barrier!(); let ret = f( &*self.peripheral.get(), C { _0: (), _marker: PhantomData, }, ); barrier!(); basepri::write(old_basepri); ret } } } unsafe impl Sync for Peripheral where C: Ceiling, { } /// Requests the execution of the task `task` pub fn request(_task: fn(T, P)) where T: Context + Nr, P: Priority, { let nvic = unsafe { &*NVIC.get() }; match () { #[cfg(debug_assertions)] () => { // NOTE(safe) zero sized type let task = unsafe { core::ptr::read(0x0 as *const T) }; // NOTE(safe) atomic read assert!(!nvic.is_pending(task), "Task is already in the pending state"); } #[cfg(not(debug_assertions))] () => {} } // NOTE(safe) zero sized type let task = unsafe { core::ptr::read(0x0 as *const T) }; // NOTE(safe) atomic write nvic.set_pending(task); } /// A type-level ceiling pub struct C { _0: (), _marker: PhantomData, } /// A type-level priority pub struct P { _0: (), _marker: PhantomData, } impl P where T: Level, { pub fn hw() -> u8 { T::hw() } } /// 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 {} /// Interrupt hardware level /// /// DO NOT IMPLEMENT THIS TRAIT YOURSELF pub unsafe trait Level { fn hw() -> u8; } /// A valid priority level /// /// DO NOT IMPLEMENT THIS TRAIT YOURSELF pub unsafe trait Priority {} fn logical2hw(logical: u8) -> u8 { ((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS) } /// 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, $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 } fn enable_tasks() { // NOTE(safe) this function runs in an interrupt free context let _nvic = unsafe { &*$crate::NVIC.get() }; $( _nvic.enable(::$krate::interrupt::Interrupt::$Interrupt); )* } #[allow(dead_code)] fn is_priority

() 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 }; } } } include!(concat!(env!("OUT_DIR"), "/prio.rs"));