mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-29 06:54:33 +01:00
compile time verified ceilings
This commit is contained in:
parent
6ac2625a75
commit
595404c5ff
8 changed files with 498 additions and 562 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
target
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
target/
|
||||||
|
|
22
Cargo.toml
22
Cargo.toml
|
@ -4,9 +4,21 @@ build = "build.rs"
|
||||||
name = "cortex-m-srp"
|
name = "cortex-m-srp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[build-dependencies]
|
||||||
cortex-m = "0.2.0"
|
quote = "0.3.15"
|
||||||
|
syn = "0.11.10"
|
||||||
|
|
||||||
[dependencies.vcell]
|
[dependencies]
|
||||||
features = ["const-fn"]
|
cortex-m = "0.2.2"
|
||||||
version = "0.1.0"
|
typenum = "1.7.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
compiletest_rs = "0.2.5"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Number of priority bits
|
||||||
|
P2 = []
|
||||||
|
P3 = []
|
||||||
|
P4 = []
|
||||||
|
P5 = []
|
||||||
|
default = ["P4"]
|
||||||
|
|
103
build.rs
103
build.rs
|
@ -1,9 +1,106 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quote;
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use syn::{Ident, IntTy, Lit};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let target = env::var("TARGET").unwrap();
|
let bits = if env::var_os("CARGO_FEATURE_P2").is_some() {
|
||||||
|
2
|
||||||
|
} else if env::var_os("CARGO_FEATURE_P3").is_some() {
|
||||||
|
3
|
||||||
|
} else if env::var_os("CARGO_FEATURE_P4").is_some() {
|
||||||
|
4
|
||||||
|
} else if env::var_os("CARGO_FEATURE_P5").is_some() {
|
||||||
|
5
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"Specify the number of priority bits through one of these Cargo \
|
||||||
|
features: P2, P3, P4 or P5"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if target == "thumbv6m-none-eabi" {
|
let n = Lit::Int(bits, IntTy::Unsuffixed);
|
||||||
println!("cargo:rustc-cfg=thumbv6m");
|
let mut tokens = vec![];
|
||||||
|
tokens.push(
|
||||||
|
quote! {
|
||||||
|
const PRIORITY_BITS: u8 = #n;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ceilings
|
||||||
|
for i in 1..(1 << bits) + 1 {
|
||||||
|
let c = Ident::new(format!("C{}", i));
|
||||||
|
let u = Ident::new(format!("U{}", i));
|
||||||
|
|
||||||
|
tokens.push(
|
||||||
|
quote! {
|
||||||
|
/// Ceiling
|
||||||
|
pub type #c = C<::typenum::#u>;
|
||||||
|
|
||||||
|
unsafe impl Ceiling for #c {}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Priorities
|
||||||
|
for i in 1..(1 << bits) + 1 {
|
||||||
|
let p = Ident::new(format!("P{}", i));
|
||||||
|
let u = Ident::new(format!("U{}", i));
|
||||||
|
|
||||||
|
tokens.push(
|
||||||
|
quote! {
|
||||||
|
/// Priority
|
||||||
|
pub type #p = P<::typenum::#u>;
|
||||||
|
|
||||||
|
unsafe impl Priority for #p {}
|
||||||
|
|
||||||
|
unsafe impl Level for ::typenum::#u {
|
||||||
|
fn hw() -> u8 {
|
||||||
|
logical2hw(::typenum::#u::to_u8())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThanOrEqual
|
||||||
|
for i in 1..(1 << bits) + 1 {
|
||||||
|
for j in 1..(i + 1) {
|
||||||
|
let i = Ident::new(format!("U{}", i));
|
||||||
|
let j = Ident::new(format!("U{}", j));
|
||||||
|
|
||||||
|
tokens.push(
|
||||||
|
quote! {
|
||||||
|
unsafe impl GreaterThanOrEqual<::typenum::#j> for
|
||||||
|
::typenum::#i {}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let u = Ident::new(format!("U{}", (1 << bits)));
|
||||||
|
tokens.push(quote! {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub type CMAX = C<::typenum::#u>;
|
||||||
|
|
||||||
|
/// Maximum priority level
|
||||||
|
pub type UMAX = ::typenum::#u;
|
||||||
|
});
|
||||||
|
|
||||||
|
let tokens = quote! {
|
||||||
|
#(#tokens)*
|
||||||
|
};
|
||||||
|
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let mut out = File::create(PathBuf::from(out_dir).join("prio.rs")).unwrap();
|
||||||
|
|
||||||
|
out.write_all(tokens.as_str().as_bytes()).unwrap();
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
//! Safe, run-time checked resources
|
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
|
|
||||||
use cortex_m::interrupt;
|
|
||||||
use cortex_m::register::{basepri, basepri_max};
|
|
||||||
use vcell::VolatileCell;
|
|
||||||
|
|
||||||
use Ceiling;
|
|
||||||
|
|
||||||
unsafe fn acquire(locked: &VolatileCell<bool>, ceiling: u8) -> u8 {
|
|
||||||
assert!(!locked.get(), "resource already locked");
|
|
||||||
let old_basepri = basepri::read();
|
|
||||||
basepri_max::write(ceiling);
|
|
||||||
locked.set(true);
|
|
||||||
old_basepri
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn release(locked: &VolatileCell<bool>, old_basepri: u8) {
|
|
||||||
locked.set(false);
|
|
||||||
basepri::write(old_basepri);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A totally safe `Resource` that panics on misuse
|
|
||||||
pub struct Resource<T, C> {
|
|
||||||
_marker: PhantomData<C>,
|
|
||||||
data: UnsafeCell<T>,
|
|
||||||
locked: VolatileCell<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, C> Resource<T, C>
|
|
||||||
where
|
|
||||||
C: Ceiling,
|
|
||||||
{
|
|
||||||
/// Creates a new `Resource` with ceiling `C`
|
|
||||||
pub const fn new(data: T) -> Resource<T, C> {
|
|
||||||
Resource {
|
|
||||||
_marker: PhantomData,
|
|
||||||
data: UnsafeCell::new(data),
|
|
||||||
locked: VolatileCell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Locks the resource, blocking tasks with priority equal or smaller than
|
|
||||||
/// the ceiling `C`
|
|
||||||
pub fn lock<R, F>(&'static self, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&T) -> R,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let old_basepri = acquire(&self.locked, C::hw_ceiling());
|
|
||||||
::compiler_barrier();
|
|
||||||
let ret = f(&*self.data.get());
|
|
||||||
::compiler_barrier();
|
|
||||||
release(&self.locked, old_basepri);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably locks the resource, blocking tasks with priority equal or
|
|
||||||
/// smaller than the ceiling `C`
|
|
||||||
pub fn lock_mut<R, F>(&'static self, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut T) -> R,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let old_basepri = acquire(&self.locked, C::hw_ceiling());
|
|
||||||
::compiler_barrier();
|
|
||||||
let ret = f(&mut *self.data.get());
|
|
||||||
::compiler_barrier();
|
|
||||||
release(&self.locked, old_basepri);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T, C> Sync for Resource<T, C> {}
|
|
775
src/lib.rs
775
src/lib.rs
|
@ -1,138 +1,26 @@
|
||||||
//! Stack Resource Policy for Cortex-M processors
|
|
||||||
//!
|
|
||||||
//! NOTE ARMv6-M is not fully supported at the moment.
|
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate cortex_m;
|
extern crate cortex_m;
|
||||||
extern crate vcell;
|
extern crate typenum;
|
||||||
|
|
||||||
pub mod checked;
|
|
||||||
|
|
||||||
use cortex_m::ctxt::Context;
|
|
||||||
use cortex_m::interrupt::{CriticalSection, Nr};
|
|
||||||
use cortex_m::peripheral::{Peripheral, NVIC};
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
use cortex_m::peripheral::SCB;
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
use cortex_m::register::{basepri, basepri_max};
|
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
#[cfg(not(thumbv6m))]
|
use core::cell::UnsafeCell;
|
||||||
use core::ptr;
|
|
||||||
|
|
||||||
#[cfg(not(thumbv6m))]
|
use cortex_m::interrupt::Nr;
|
||||||
// NOTE Only the 4 highest bits of the priority byte (BASEPRI / NVIC.IPR) are
|
use cortex_m::register::{basepri, basepri_max};
|
||||||
// considered when determining priorities.
|
use typenum::{Cmp, Equal, Greater, Less, Unsigned};
|
||||||
const PRIORITY_BITS: u8 = 4;
|
|
||||||
|
|
||||||
/// Logical task priority
|
pub use cortex_m::ctxt::Local;
|
||||||
#[cfg(not(thumbv6m))]
|
#[doc(hidden)]
|
||||||
unsafe fn task_priority() -> u8 {
|
pub use cortex_m::peripheral::NVIC;
|
||||||
// NOTE(safe) atomic read
|
#[doc(hidden)]
|
||||||
let nr = match (*SCB.get()).icsr.read() as u8 {
|
pub use cortex_m::interrupt::free;
|
||||||
n if n >= 16 => n - 16,
|
|
||||||
_ => panic!("not in a task"),
|
|
||||||
};
|
|
||||||
// NOTE(safe) atomic read
|
|
||||||
hardware((*NVIC.get()).ipr[nr as usize].read())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(debug_assertions, not(thumbv6m)))]
|
macro_rules! barrier {
|
||||||
unsafe fn get_check(logical_ceiling: u8) {
|
() => {
|
||||||
let task_priority = task_priority();
|
|
||||||
let system_ceiling = hardware(cortex_m::register::basepri::read());
|
|
||||||
let resource_ceiling = logical_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))]
|
|
||||||
unsafe fn get_check(_: u8) {}
|
|
||||||
|
|
||||||
#[cfg(all(debug_assertions, not(thumbv6m)))]
|
|
||||||
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(_: u8) {}
|
|
||||||
|
|
||||||
// XXX Do we need memory / instruction / compiler barriers here?
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn lock<T, R, C, F>(f: F, res: *const T, ceiling: u8) -> R
|
|
||||||
where
|
|
||||||
C: Ceiling,
|
|
||||||
F: FnOnce(&T, C) -> R,
|
|
||||||
{
|
|
||||||
lock_check(ceiling);
|
|
||||||
let old_basepri = basepri::read();
|
|
||||||
basepri_max::write(ceiling);
|
|
||||||
compiler_barrier();
|
|
||||||
let ret = f(&*res, ptr::read(0 as *const _));
|
|
||||||
compiler_barrier();
|
|
||||||
basepri::write(old_basepri);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX Do we need memory / instruction / compiler barriers here?
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn lock_mut<T, R, C, F>(f: F, res: *mut T, ceiling: u8) -> R
|
|
||||||
where
|
|
||||||
C: Ceiling,
|
|
||||||
F: FnOnce(&mut T, C) -> R,
|
|
||||||
{
|
|
||||||
lock_check(ceiling);
|
|
||||||
let old_basepri = basepri::read();
|
|
||||||
basepri_max::write(ceiling);
|
|
||||||
compiler_barrier();
|
|
||||||
let ret = f(&mut *res, ptr::read(0 as *const _));
|
|
||||||
compiler_barrier();
|
|
||||||
basepri::write(old_basepri);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compiler_barrier() {
|
|
||||||
unsafe {
|
|
||||||
asm!(""
|
asm!(""
|
||||||
:
|
:
|
||||||
:
|
:
|
||||||
|
@ -141,402 +29,339 @@ fn compiler_barrier() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A peripheral as a resource
|
|
||||||
pub struct ResourceP<P, Ceiling>
|
|
||||||
where
|
|
||||||
P: 'static,
|
|
||||||
{
|
|
||||||
_marker: PhantomData<Ceiling>,
|
|
||||||
peripheral: Peripheral<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, C> ResourceP<P, C>
|
|
||||||
where
|
|
||||||
C: CeilingLike,
|
|
||||||
{
|
|
||||||
/// Wraps a `peripheral` into a `Resource`
|
|
||||||
///
|
|
||||||
/// # Unsafety
|
|
||||||
///
|
|
||||||
/// - Must not create two resources that point to the same peripheral
|
|
||||||
/// - The ceiling, `C`, must be picked to prevent two or more tasks from
|
|
||||||
/// concurrently accessing the resource through preemption
|
|
||||||
pub const unsafe fn new(p: Peripheral<P>) -> Self {
|
|
||||||
ResourceP {
|
|
||||||
_marker: PhantomData,
|
|
||||||
peripheral: p,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Borrows the resource for the duration of `interrupt::free`
|
|
||||||
pub fn cs_borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs P {
|
|
||||||
unsafe { &*self.peripheral.get() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably borrows the resource for the duration of `interrupt::free`
|
|
||||||
pub fn cs_borrow_mut<'cs>(
|
|
||||||
&self,
|
|
||||||
_ctxt: &'cs mut CriticalSection,
|
|
||||||
) -> &'cs mut P {
|
|
||||||
unsafe { &mut *self.peripheral.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
impl<P, C> ResourceP<P, C>
|
|
||||||
where
|
|
||||||
C: Ceiling,
|
|
||||||
{
|
|
||||||
/// Borrows the resource without locking
|
|
||||||
///
|
|
||||||
/// NOTE The system ceiling must be higher than this resource ceiling
|
|
||||||
pub fn borrow<'l, SC>(&'static self, _system_ceiling: &'l SC) -> &'l P
|
|
||||||
where
|
|
||||||
SC: HigherThan<C>,
|
|
||||||
{
|
|
||||||
unsafe { &*self.peripheral.get() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an immutable reference to the inner data without locking
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// You must
|
|
||||||
///
|
|
||||||
/// - Preserve the "reference" rules. Don't create an immutable reference if
|
|
||||||
/// the current task already owns a mutable reference to the data.
|
|
||||||
///
|
|
||||||
/// - adhere to the Stack Resource Policy. You can
|
|
||||||
/// - Access the resource from the highest priority task.
|
|
||||||
/// - Access the resource from within a critical section that sets the
|
|
||||||
/// system ceiling to `C`.
|
|
||||||
pub unsafe fn get(&self) -> &'static P {
|
|
||||||
get_check(C::ceiling());
|
|
||||||
|
|
||||||
&*self.peripheral.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the inner data without locking
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// You must
|
|
||||||
///
|
|
||||||
/// - Preserve the "reference" rules. Don't create a mutable reference if
|
|
||||||
/// the current task already owns a reference to the data.
|
|
||||||
///
|
|
||||||
/// - adhere to the Stack Resource Policy. You can
|
|
||||||
/// - Access the resource from the highest priority task.
|
|
||||||
/// - Access the resource from within a critical section that sets the
|
|
||||||
/// system ceiling to `C`.
|
|
||||||
pub unsafe fn get_mut(&self) -> &'static mut P {
|
|
||||||
get_check(C::ceiling());
|
|
||||||
|
|
||||||
&mut *self.peripheral.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Locks the resource, preventing tasks with priority lower than `Ceiling`
|
|
||||||
/// from preempting the current task
|
|
||||||
pub fn lock<R, F, Ctxt>(&'static self, _ctxt: &Ctxt, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&P, C) -> R,
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { lock(f, self.peripheral.get(), C::hw_ceiling()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably locks the resource, preventing tasks with priority lower than
|
|
||||||
/// `Ceiling` from preempting the current task
|
|
||||||
pub fn lock_mut<R, F, Ctxt>(&'static self, _ctxt: &mut Ctxt, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut P, C) -> R,
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { lock_mut(f, self.peripheral.get(), C::hw_ceiling()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> ResourceP<P, C0> {
|
|
||||||
/// Borrows the resource without locking
|
|
||||||
pub fn borrow<'ctxt, Ctxt>(&'static self, _ctxt: &'ctxt Ctxt) -> &'ctxt P
|
|
||||||
where
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { &*self.peripheral.get() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably borrows the resource without locking
|
|
||||||
pub fn borrow_mut<'ctxt, Ctxt>(
|
|
||||||
&'static self,
|
|
||||||
_ctxt: &'ctxt mut Ctxt,
|
|
||||||
) -> &'ctxt mut P
|
|
||||||
where
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { &mut *self.peripheral.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<P, C> Sync for ResourceP<P, C> {}
|
|
||||||
|
|
||||||
/// A resource
|
/// A resource
|
||||||
pub struct Resource<T, Ceiling> {
|
pub struct Resource<T, CEILING> {
|
||||||
_marker: PhantomData<Ceiling>,
|
_ceiling: PhantomData<CEILING>,
|
||||||
data: UnsafeCell<T>,
|
data: UnsafeCell<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, C> Resource<T, C> {
|
impl<T, C> Resource<T, C> {
|
||||||
/// Initializes a resource
|
/// Creates a new resource with ceiling `C`
|
||||||
///
|
pub const fn new(data: T) -> Self
|
||||||
/// # Unsafety
|
|
||||||
///
|
|
||||||
/// - The ceiling, `C`, must be picked to prevent two or more tasks from
|
|
||||||
/// concurrently accessing the resource through preemption
|
|
||||||
pub const unsafe fn new(data: T) -> Self
|
|
||||||
where
|
where
|
||||||
C: CeilingLike,
|
C: Ceiling,
|
||||||
{
|
{
|
||||||
Resource {
|
Resource {
|
||||||
_marker: PhantomData,
|
_ceiling: PhantomData,
|
||||||
data: UnsafeCell::new(data),
|
data: UnsafeCell::new(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Borrows the resource for the duration of `interrupt::free`
|
impl<T, CEILING> Resource<T, C<CEILING>>
|
||||||
pub fn cs_borrow<'cs>(&self, _ctxt: &'cs CriticalSection) -> &'cs T {
|
where
|
||||||
|
C<CEILING>: 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<SCEILING>,
|
||||||
|
) -> &'cs T
|
||||||
|
where
|
||||||
|
SCEILING: GreaterThanOrEqual<CEILING>,
|
||||||
|
{
|
||||||
unsafe { &*self.data.get() }
|
unsafe { &*self.data.get() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutably borrows the resource for the duration of `interrupt::free`
|
/// Claims the resource at the task with highest priority
|
||||||
pub fn cs_borrow_mut<'cs>(
|
///
|
||||||
&self,
|
/// This operation is zero cost and doesn't impose any additional blocking
|
||||||
_ctxt: &'cs mut CriticalSection,
|
pub fn claim<'task, PRIORITY>(
|
||||||
) -> &'cs mut T {
|
&'static self,
|
||||||
unsafe { &mut *self.data.get() }
|
_priority: &'task P<PRIORITY>,
|
||||||
|
) -> &'task T
|
||||||
|
where
|
||||||
|
CEILING: Cmp<PRIORITY, Output = Equal>,
|
||||||
|
P<PRIORITY>: 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<R, PRIORITY, F>(
|
||||||
|
&'static self,
|
||||||
|
_priority: &P<PRIORITY>,
|
||||||
|
f: F,
|
||||||
|
) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&T, C<CEILING>) -> R,
|
||||||
|
C<CEILING>: Ceiling,
|
||||||
|
CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less>
|
||||||
|
+ Level,
|
||||||
|
P<PRIORITY>: Priority,
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(thumbv6m))]
|
unsafe impl<T, CEILING> Sync for Resource<T, CEILING>
|
||||||
impl<T, C> Resource<T, C>
|
where
|
||||||
|
CEILING: Ceiling,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hardware peripheral as a resource
|
||||||
|
pub struct Peripheral<P, CEILING>
|
||||||
|
where
|
||||||
|
P: 'static,
|
||||||
|
{
|
||||||
|
peripheral: cortex_m::peripheral::Peripheral<P>,
|
||||||
|
_ceiling: PhantomData<CEILING>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, C> Peripheral<P, C>
|
||||||
where
|
where
|
||||||
C: Ceiling,
|
C: Ceiling,
|
||||||
{
|
{
|
||||||
/// Borrows the resource without locking
|
/// Assigns a ceiling `C` to the `peripheral`
|
||||||
///
|
|
||||||
/// NOTE The system ceiling must be higher than this resource ceiling
|
|
||||||
pub fn borrow<'l, SC>(&'static self, _system_ceiling: &'l SC) -> &'l T
|
|
||||||
where
|
|
||||||
SC: HigherThan<C>,
|
|
||||||
{
|
|
||||||
unsafe { &*self.data.get() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an immutable reference to the inner data without locking
|
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// You must
|
/// You MUST not create two resources that point to the same peripheral
|
||||||
///
|
pub const unsafe fn new(peripheral: cortex_m::peripheral::Peripheral<P>,)
|
||||||
/// - Preserve the "reference" rules. Don't create an immutable reference if
|
-> Self {
|
||||||
/// the current task already owns a mutable reference to the data.
|
Peripheral {
|
||||||
///
|
_ceiling: PhantomData,
|
||||||
/// - adhere to the Stack Resource Policy. You can
|
peripheral: peripheral,
|
||||||
/// - Access the resource from the highest priority task.
|
}
|
||||||
/// - Access the resource from within a critical section that sets the
|
|
||||||
/// system ceiling to `C`.
|
|
||||||
pub unsafe fn get(&'static self) -> &'static T {
|
|
||||||
get_check(C::ceiling());
|
|
||||||
|
|
||||||
&*self.data.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the inner data without locking
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// You must
|
|
||||||
///
|
|
||||||
/// - Preserve the "reference" rules. Don't create a mutable reference if
|
|
||||||
/// the current task already owns a reference to the data.
|
|
||||||
///
|
|
||||||
/// - adhere to the Stack Resource Policy. You can
|
|
||||||
/// - Access the resource from the highest priority task.
|
|
||||||
/// - Access the resource from within a critical section that sets the
|
|
||||||
/// system ceiling to `C`.
|
|
||||||
pub unsafe fn get_mut(&'static self) -> &'static mut T {
|
|
||||||
get_check(C::ceiling());
|
|
||||||
|
|
||||||
&mut *self.data.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Locks the resource, preventing tasks with priority lower than `Ceiling`
|
|
||||||
/// from preempting the current task
|
|
||||||
pub fn lock<F, R, Ctxt>(&'static self, _ctxt: &Ctxt, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&T, C) -> R,
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { lock(f, self.data.get(), C::hw_ceiling()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably locks the resource, preventing tasks with priority lower than
|
|
||||||
/// `Ceiling` from preempting the current task
|
|
||||||
pub fn lock_mut<F, R, Ctxt>(&'static self, _ctxt: &mut Ctxt, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut T, C) -> R,
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { lock_mut(f, self.data.get(), C::hw_ceiling()) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Resource<T, C0> {
|
impl<Periph, CEILING> Peripheral<Periph, C<CEILING>>
|
||||||
/// Borrows the resource without locking
|
|
||||||
pub fn borrow<'ctxt, Ctxt>(&'static self, _ctxt: &'ctxt Ctxt) -> &'ctxt T
|
|
||||||
where
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { &*self.data.get() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutably borrows the resource without locking
|
|
||||||
pub fn borrow_mut<'ctxt, Ctxt>(
|
|
||||||
&'static self,
|
|
||||||
_ctxt: &'ctxt mut Ctxt,
|
|
||||||
) -> &'ctxt mut T
|
|
||||||
where
|
|
||||||
Ctxt: Context,
|
|
||||||
{
|
|
||||||
unsafe { &mut *self.data.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T, Ceiling> Sync for Resource<T, Ceiling> {}
|
|
||||||
|
|
||||||
/// Maps a hardware priority to a logical priority
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
fn hardware(priority: u8) -> u8 {
|
|
||||||
16 - (priority >> (8 - PRIORITY_BITS))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
///
|
|
||||||
/// NOTE Input `priority` must be in the range `[1, 16]` (inclusive)
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
pub fn logical(priority: u8) -> u8 {
|
|
||||||
assert!(priority >= 1 && priority <= 16);
|
|
||||||
|
|
||||||
((1 << PRIORITY_BITS) - priority) << (8 - PRIORITY_BITS)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Puts `interrupt` in the "to execute" queue
|
|
||||||
///
|
|
||||||
/// This function has no effect if the interrupt was already queued
|
|
||||||
pub fn queue<I>(interrupt: I)
|
|
||||||
where
|
where
|
||||||
I: Nr,
|
C<CEILING>: Ceiling,
|
||||||
{
|
{
|
||||||
unsafe {
|
/// See [Resource.borrow](./struct.Resource.html#method.borrow)
|
||||||
// NOTE(safe) atomic write
|
pub fn borrow<'cs, SCEILING>(
|
||||||
(*NVIC.get()).set_pending(interrupt)
|
&'static self,
|
||||||
|
_system_ceiling: &'cs C<SCEILING>,
|
||||||
|
) -> &'cs Periph
|
||||||
|
where
|
||||||
|
SCEILING: GreaterThanOrEqual<CEILING>,
|
||||||
|
{
|
||||||
|
unsafe { &*self.peripheral.get() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [Resource.claim](./struct.Resource.html#method.claim)
|
||||||
|
pub fn claim<'task, PRIORITY>(
|
||||||
|
&'static self,
|
||||||
|
_priority: &'task P<PRIORITY>,
|
||||||
|
) -> &'task Periph
|
||||||
|
where
|
||||||
|
CEILING: Cmp<PRIORITY, Output = Equal>,
|
||||||
|
P<PRIORITY>: Priority,
|
||||||
|
{
|
||||||
|
unsafe { &*self.peripheral.get() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [Resource.lock](./struct.Resource.html#method.lock)
|
||||||
|
pub fn lock<R, PRIORITY, F>(
|
||||||
|
&'static self,
|
||||||
|
_priority: &P<PRIORITY>,
|
||||||
|
f: F,
|
||||||
|
) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&Periph, C<CEILING>) -> R,
|
||||||
|
C<CEILING>: Ceiling,
|
||||||
|
CEILING: Cmp<PRIORITY, Output = Greater> + Cmp<UMAX, Output = Less>
|
||||||
|
+ Level,
|
||||||
|
P<PRIORITY>: Priority,
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fake ceiling, indicates that the resource is shared by cooperative tasks
|
unsafe impl<T, C> Sync for Peripheral<T, C>
|
||||||
pub struct C0 {
|
where
|
||||||
|
C: Ceiling,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests the execution of the task `task`
|
||||||
|
pub fn request<T>(task: T)
|
||||||
|
where
|
||||||
|
T: Nr,
|
||||||
|
{
|
||||||
|
let nvic = unsafe { &*NVIC.get() };
|
||||||
|
|
||||||
|
match () {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
() => {
|
||||||
|
let task = unsafe { core::ptr::read(&task) };
|
||||||
|
// NOTE(safe) atomic read
|
||||||
|
assert!(!nvic.is_pending(task),
|
||||||
|
"Task is already in the pending state");
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
() => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(safe) atomic write
|
||||||
|
nvic.set_pending(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type-level ceiling
|
||||||
|
pub struct C<T> {
|
||||||
_0: (),
|
_0: (),
|
||||||
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A real ceiling
|
/// A type-level priority
|
||||||
// XXX this should be a "closed" trait
|
pub struct P<T> {
|
||||||
#[cfg(not(thumbv6m))]
|
_0: (),
|
||||||
pub unsafe trait Ceiling {
|
_marker: PhantomData<T>,
|
||||||
/// Returns the logical ceiling as a number
|
|
||||||
fn ceiling() -> u8;
|
|
||||||
|
|
||||||
/// Returns the HW ceiling as a number
|
|
||||||
fn hw_ceiling() -> u8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Usable as a ceiling
|
impl<T> P<T>
|
||||||
// XXX this should be a "closed" trait
|
where
|
||||||
pub unsafe trait CeilingLike {}
|
T: Level,
|
||||||
|
{
|
||||||
|
pub fn hw() -> u8 {
|
||||||
|
T::hw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This ceiling is lower than `C`
|
/// A valid ceiling
|
||||||
// XXX this should be a "closed" trait
|
///
|
||||||
#[cfg(not(thumbv6m))]
|
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
|
||||||
pub unsafe trait HigherThan<C> {}
|
pub unsafe trait Ceiling {}
|
||||||
|
|
||||||
#[cfg(not(thumbv6m))]
|
/// Type-level `>=` operator
|
||||||
macro_rules! ceiling {
|
///
|
||||||
($ceiling:ident, $logical:expr) => {
|
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
|
||||||
/// Ceiling
|
pub unsafe trait GreaterThanOrEqual<RHS> {}
|
||||||
pub struct $ceiling {
|
|
||||||
_0: ()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl CeilingLike for $ceiling {}
|
/// Interrupt hardware level
|
||||||
|
///
|
||||||
|
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
|
||||||
|
pub unsafe trait Level {
|
||||||
|
fn hw() -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Ceiling for $ceiling {
|
/// A valid priority level
|
||||||
#[inline(always)]
|
///
|
||||||
fn ceiling() -> u8 {
|
/// DO NOT IMPLEMENT THIS TRAIT YOURSELF
|
||||||
$logical
|
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, $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
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
fn enable_tasks() {
|
||||||
fn hw_ceiling() -> u8 {
|
// NOTE(safe) this function runs in an interrupt free context
|
||||||
((1 << PRIORITY_BITS) - $logical) << (8 - PRIORITY_BITS)
|
let _nvic = unsafe { &*$crate::NVIC.get() };
|
||||||
|
|
||||||
|
$(
|
||||||
|
_nvic.enable(::$krate::interrupt::Interrupt::$Interrupt);
|
||||||
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn is_priority<P>()
|
||||||
|
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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(thumbv6m)]
|
include!(concat!(env!("OUT_DIR"), "/prio.rs"));
|
||||||
macro_rules! ceiling {
|
|
||||||
($($tt:tt)*) => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
#[cfg(not(thumbv6m))]
|
|
||||||
macro_rules! higher_than {
|
|
||||||
() => {};
|
|
||||||
($lowest:ident, $($higher:ident,)*) => {
|
|
||||||
$(
|
|
||||||
unsafe impl HigherThan<$lowest> for $higher {}
|
|
||||||
)*
|
|
||||||
|
|
||||||
higher_than!($($higher,)*);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(thumbv6m)]
|
|
||||||
macro_rules! higher_than {
|
|
||||||
($($tt:tt)*) => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
higher_than! {
|
|
||||||
C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl CeilingLike for C0 {}
|
|
||||||
|
|
16
tests/cfail.rs
Normal file
16
tests/cfail.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
extern crate compiletest_rs as compiletest;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use compiletest::common::Mode;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfail() {
|
||||||
|
let mut config = compiletest::default_config();
|
||||||
|
config.mode = Mode::CompileFail;
|
||||||
|
config.src_base = PathBuf::from(format!("tests/cfail"));
|
||||||
|
config.target_rustcflags =
|
||||||
|
Some("-L target/debug -L target/debug/deps ".to_string());
|
||||||
|
|
||||||
|
compiletest::run_tests(&config);
|
||||||
|
}
|
22
tests/cfail/borrow.rs
Normal file
22
tests/cfail/borrow.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
extern crate cortex_m_srp;
|
||||||
|
|
||||||
|
use cortex_m_srp::{C2, C3, C4, P1, Resource};
|
||||||
|
|
||||||
|
static R1: Resource<i32, C3> = Resource::new(0);
|
||||||
|
static R2: Resource<i32, C2> = Resource::new(0);
|
||||||
|
static R3: Resource<i32, C3> = Resource::new(0);
|
||||||
|
static R4: Resource<i32, C4> = Resource::new(0);
|
||||||
|
|
||||||
|
fn j1(prio: P1) {
|
||||||
|
R1.lock(&prio, |r1, c3| {
|
||||||
|
// CAN borrow a resource with ceiling C when the system ceiling SC > C
|
||||||
|
let r2 = R2.borrow(&c3);
|
||||||
|
|
||||||
|
// CAN borrow a resource with ceiling C when the system ceiling SC == C
|
||||||
|
let r3 = R3.borrow(&c3);
|
||||||
|
|
||||||
|
// CAN'T borrow a resource with ceiling C when the system ceiling SC < C
|
||||||
|
let r4 = R4.borrow(&c3);
|
||||||
|
//~^ error
|
||||||
|
});
|
||||||
|
}
|
41
tests/cfail/lock.rs
Normal file
41
tests/cfail/lock.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
extern crate cortex_m_srp;
|
||||||
|
|
||||||
|
use cortex_m_srp::{C16, C2, P1, P16, P2, P3, Resource};
|
||||||
|
|
||||||
|
static R1: Resource<i32, C2> = Resource::new(0);
|
||||||
|
|
||||||
|
// You CAN'T lock a resource with ceiling C from a task with priority P if P > C
|
||||||
|
fn j1(prio: P3) {
|
||||||
|
R1.lock(&prio, |_, _| {});
|
||||||
|
//~^ error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DON'T lock a resource with ceiling equal to the task priority.
|
||||||
|
// Instead use `claim`
|
||||||
|
fn j2(prio: P2) {
|
||||||
|
R1.lock(&prio, |_, _| {});
|
||||||
|
//~^ error
|
||||||
|
|
||||||
|
// OK
|
||||||
|
let r1 = R1.claim(&prio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You CAN lock a resource with ceiling C from a task with priority P if C > P
|
||||||
|
fn j3(prio: P1) {
|
||||||
|
// OK
|
||||||
|
R1.lock(&prio, |r1, _| {});
|
||||||
|
}
|
||||||
|
|
||||||
|
static R2: Resource<i32, C16> = Resource::new(0);
|
||||||
|
|
||||||
|
// Tasks with priority less than P16 can't lock a resource with ceiling C16
|
||||||
|
fn j4(prio: P1) {
|
||||||
|
R2.lock(&prio, |_, _| {});
|
||||||
|
//~^ error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only tasks with priority P16 can claim a resource with ceiling C16
|
||||||
|
fn j5(prio: P16) {
|
||||||
|
// OK
|
||||||
|
let r2 = R2.claim(&prio);
|
||||||
|
}
|
Loading…
Reference in a new issue