rtic/src/lib.rs

372 lines
9 KiB
Rust
Raw Normal View History

#![deny(warnings)]
2017-03-05 06:26:14 +01:00
#![feature(asm)]
#![feature(const_fn)]
#![no_std]
extern crate cortex_m;
2017-04-10 05:42:17 +02:00
extern crate typenum;
2017-03-05 06:26:14 +01:00
2017-03-10 05:59:50 +01:00
use core::marker::PhantomData;
2017-04-10 05:42:17 +02:00
use core::cell::UnsafeCell;
2017-04-10 05:42:17 +02:00
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};
2017-04-10 05:42:17 +02:00
#[doc(hidden)]
pub use cortex_m::peripheral::NVIC;
#[doc(hidden)]
pub use cortex_m::interrupt::free;
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
where
C: Ceiling,
{
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-10 05:42:17 +02:00
impl<T, CEILING> Resource<T, C<CEILING>>
where
2017-04-10 05:42:17 +02:00
C<CEILING>: Ceiling,
2017-03-10 05:59:50 +01:00
{
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
pub fn borrow<'cs, SCEILING>(
&'static self,
_system_ceiling: &'cs C<SCEILING>,
) -> &'cs T
where
2017-04-10 05:42:17 +02:00
SCEILING: GreaterThanOrEqual<CEILING>,
2017-03-05 06:26:14 +01:00
{
2017-04-10 05:42:17 +02:00
unsafe { &*self.data.get() }
2017-04-04 23:37:01 +02:00
}
2017-04-10 05:42:17 +02:00
/// Claims the resource at the task with highest priority
2017-04-04 23:37:01 +02:00
///
2017-04-10 05:42:17 +02:00
/// This operation is zero cost and doesn't impose any additional blocking
pub fn claim<'task, PRIORITY>(
&'static self,
2017-04-10 05:42:17 +02:00
_priority: &'task P<PRIORITY>,
) -> &'task T
where
2017-04-10 05:42:17 +02:00
CEILING: Cmp<PRIORITY, Output = Equal>,
P<PRIORITY>: Priority,
{
2017-04-10 05:42:17 +02:00
unsafe { &*self.data.get() }
}
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.
pub fn lock<R, PRIORITY, F>(
&'static self,
_priority: &P<PRIORITY>,
f: F,
) -> R
where
2017-04-10 05:42:17 +02:00
F: FnOnce(&T, C<CEILING>) -> R,
C<CEILING>: Ceiling,
CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less>
+ Level,
P<PRIORITY>: Priority,
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!();
let ret = f(
&*self.data.get(),
C {
_0: (),
_marker: PhantomData,
},
);
barrier!();
basepri::write(old_basepri);
ret
2017-03-05 06:26:14 +01:00
}
}
2017-04-10 05:42:17 +02:00
}
2017-04-10 05:42:17 +02:00
unsafe impl<T, CEILING> Sync for Resource<T, CEILING>
where
CEILING: Ceiling,
{
}
2017-04-10 05:42:17 +02:00
/// A hardware peripheral as a resource
pub struct Peripheral<P, CEILING>
where
P: 'static,
{
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>
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
pub const unsafe fn new(peripheral: cortex_m::peripheral::Peripheral<P>,)
-> Self {
Peripheral {
_ceiling: PhantomData,
peripheral: peripheral,
}
}
2017-04-10 05:42:17 +02:00
}
2017-04-10 05:42:17 +02:00
impl<Periph, CEILING> Peripheral<Periph, C<CEILING>>
where
C<CEILING>: Ceiling,
{
/// See [Resource.borrow](./struct.Resource.html#method.borrow)
pub fn borrow<'cs, SCEILING>(
&'static self,
_system_ceiling: &'cs C<SCEILING>,
) -> &'cs Periph
where
2017-04-10 05:42:17 +02:00
SCEILING: GreaterThanOrEqual<CEILING>,
{
2017-04-10 05:42:17 +02:00
unsafe { &*self.peripheral.get() }
}
2017-03-05 06:26:14 +01:00
2017-04-10 05:42:17 +02:00
/// See [Resource.claim](./struct.Resource.html#method.claim)
pub fn claim<'task, PRIORITY>(
&'static self,
_priority: &'task P<PRIORITY>,
) -> &'task Periph
where
2017-04-10 05:42:17 +02:00
CEILING: Cmp<PRIORITY, Output = Equal>,
P<PRIORITY>: Priority,
2017-03-10 05:59:50 +01:00
{
2017-04-10 05:42:17 +02:00
unsafe { &*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)
pub fn lock<R, PRIORITY, F>(
&'static self,
2017-04-10 05:42:17 +02:00
_priority: &P<PRIORITY>,
f: F,
) -> R
where
2017-04-10 05:42:17 +02:00
F: FnOnce(&Periph, C<CEILING>) -> R,
C<CEILING>: Ceiling,
CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less>
+ Level,
P<PRIORITY>: Priority,
{
2017-04-10 05:42:17 +02:00
unsafe {
let old_basepri = basepri::read();
basepri_max::write(<CEILING>::hw());
barrier!();
let ret = f(
&*self.peripheral.get(),
C {
_0: (),
_marker: PhantomData,
},
);
barrier!();
basepri::write(old_basepri);
ret
}
}
2017-03-05 06:26:14 +01:00
}
2017-04-10 05:42:17 +02:00
unsafe impl<T, C> Sync for Peripheral<T, C>
where
C: Ceiling,
{
}
/// Requests the execution of the task `task`
pub fn request<T, P>(_task: fn(T, P))
2017-04-10 05:42:17 +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> {
_0: (),
_marker: PhantomData<T>,
}
2017-04-10 05:42:17 +02:00
/// A type-level priority
pub struct P<T> {
_0: (),
_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-04 23:37:01 +02:00
where
2017-04-10 05:42:17 +02:00
T: Level,
2017-04-04 23:37:01 +02:00
{
2017-04-10 05:42:17 +02:00
pub fn hw() -> u8 {
T::hw()
2017-04-04 22:36:23 +02:00
}
}
2017-04-10 05:42:17 +02:00
/// 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<RHS> {}
/// Interrupt hardware level
///
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
pub unsafe trait Level {
fn hw() -> u8;
2017-03-10 05:59:50 +01:00
}
2017-04-10 05:42:17 +02:00
/// A valid priority level
///
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
pub unsafe trait Priority {}
2017-04-10 05:42:17 +02:00
fn logical2hw(logical: u8) -> u8 {
((1 << PRIORITY_BITS) - logical) << (8 - PRIORITY_BITS)
2017-03-10 05:59:50 +01:00
}
2017-04-10 05:42:17 +02:00
/// 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),)*
2017-04-10 05:42:17 +02:00
}) => {
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
}
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"));